Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8839aaee authored by Trung Lam's avatar Trung Lam Committed by Android (Google) Code Review
Browse files

Merge "Implement backup and restoration of conversation infos." into rvc-dev

parents 28a0b857 b213fca0
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ class PeopleBackupHelper extends BlobBackupHelper {
        if (DEBUG) {
            Slog.d(TAG, "Handling backup of " + key);
        }
        return ps.backupConversationInfos(mUserId);
        return ps.getBackupPayload(mUserId);
    }

    @Override
@@ -63,6 +63,6 @@ class PeopleBackupHelper extends BlobBackupHelper {
        if (DEBUG) {
            Slog.d(TAG, "Handling restore of " + key);
        }
        ps.restoreConversationInfos(mUserId, key, payload);
        ps.restore(mUserId, payload);
    }
}
+10 −8
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.people;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.CancellationSignal;
import android.service.appprediction.IPredictionService;
@@ -34,16 +35,17 @@ public abstract class PeopleServiceInternal extends IPredictionService.Stub {
            @NonNull CancellationSignal signal);

    /**
     * The number conversation infos will be dynamic, based on the currently installed apps on the
     * device. All of which should be combined into a single blob to be backed up.
     * Returns a backup payload that contains conversation infos. The number conversation infos will
     * be dynamic, based on the currently installed apps on the device. All of which should be
     * combined into a single blob to be backed up.
     */
    public abstract byte[] backupConversationInfos(@UserIdInt int userId);
    @Nullable
    public abstract byte[] getBackupPayload(@UserIdInt int userId);

    /**
     * Multiple conversation infos may exist in the restore payload, child classes are required to
     * manage the restoration based on how individual conversation infos were originally combined
     * during backup.
     * Restores conversation infos stored in payload blob. Multiple conversation infos may exist in
     * the restore payload, child classes are required to manage the restoration based on how
     * individual conversation infos were originally combined during backup.
     */
    public abstract void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
            @NonNull byte[] payload);
    public abstract void restore(@UserIdInt int userId, @NonNull byte[] payload);
}
+6 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.people;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
@@ -145,14 +146,15 @@ public class PeopleService extends SystemService {
            mDataManager.pruneDataForUser(userId, signal);
        }

        @Nullable
        @Override
        public byte[] backupConversationInfos(@UserIdInt int userId) {
            return new byte[0];
        public byte[] getBackupPayload(@UserIdInt int userId) {
            return mDataManager.getBackupPayload(userId);
        }

        @Override
        public void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
                @NonNull byte[] payload) {
        public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
            mDataManager.restore(userId, payload);
        }

        @VisibleForTesting
+55 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.LocusIdProto;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutInfo.ShortcutFlags;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -31,6 +32,10 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.util.Preconditions;
import com.android.server.people.ConversationInfoProto;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -280,6 +285,25 @@ public class ConversationInfo {
        }
    }

    @Nullable
    byte[] getBackupPayload() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(baos);
        try {
            out.writeUTF(mShortcutId);
            out.writeUTF(mLocusId != null ? mLocusId.getId() : "");
            out.writeUTF(mContactUri != null ? mContactUri.toString() : "");
            out.writeUTF(mNotificationChannelId != null ? mNotificationChannelId : "");
            out.writeInt(mShortcutFlags);
            out.writeInt(mConversationFlags);
            out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : "");
        } catch (IOException e) {
            Slog.e(TAG, "Failed to write fields to backup payload.", e);
            return null;
        }
        return baos.toByteArray();
    }

    /** Reads from {@link ProtoInputStream} and constructs a {@link ConversationInfo}. */
    @NonNull
    static ConversationInfo readFromProto(@NonNull ProtoInputStream protoInputStream)
@@ -331,6 +355,37 @@ public class ConversationInfo {
        return builder.build();
    }

    @Nullable
    static ConversationInfo readFromBackupPayload(@NonNull byte[] payload) {
        ConversationInfo.Builder builder = new ConversationInfo.Builder();
        DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
        try {
            builder.setShortcutId(in.readUTF());
            String locusId = in.readUTF();
            if (!TextUtils.isEmpty(locusId)) {
                builder.setLocusId(new LocusId(locusId));
            }
            String contactUri = in.readUTF();
            if (!TextUtils.isEmpty(contactUri)) {
                builder.setContactUri(Uri.parse(contactUri));
            }
            String notificationChannelId = in.readUTF();
            if (!TextUtils.isEmpty(notificationChannelId)) {
                builder.setNotificationChannelId(notificationChannelId);
            }
            builder.setShortcutFlags(in.readInt());
            builder.setConversationFlags(in.readInt());
            String contactPhoneNumber = in.readUTF();
            if (!TextUtils.isEmpty(contactPhoneNumber)) {
                builder.setContactPhoneNumber(contactPhoneNumber);
            }
        } catch (IOException e) {
            Slog.e(TAG, "Failed to read conversation info fields from backup payload.", e);
            return null;
        }
        return builder.build();
    }

    /**
     * Builder class for {@link ConversationInfo} objects.
     */
+51 −0
Original line number Diff line number Diff line
@@ -31,6 +31,10 @@ import com.android.server.people.ConversationInfosProto;

import com.google.android.collect.Lists;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -48,6 +52,8 @@ class ConversationStore {

    private static final String CONVERSATIONS_FILE_NAME = "conversations";

    private static final int CONVERSATION_INFOS_END_TOKEN = -1;

    // Shortcut ID -> Conversation Info
    @GuardedBy("this")
    private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
@@ -195,6 +201,51 @@ class ConversationStore {
        mConversationInfosProtoDiskReadWriter.deleteConversationsFile();
    }

    @Nullable
    synchronized byte[] getBackupPayload() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream conversationInfosOut = new DataOutputStream(baos);
        for (ConversationInfo conversationInfo : mConversationInfoMap.values()) {
            byte[] backupPayload = conversationInfo.getBackupPayload();
            if (backupPayload == null) {
                continue;
            }
            try {
                conversationInfosOut.writeInt(backupPayload.length);
                conversationInfosOut.write(backupPayload);
            } catch (IOException e) {
                Slog.e(TAG, "Failed to write conversation info to backup payload.", e);
                return null;
            }
        }
        try {
            conversationInfosOut.writeInt(CONVERSATION_INFOS_END_TOKEN);
        } catch (IOException e) {
            Slog.e(TAG, "Failed to write conversation infos end token to backup payload.", e);
            return null;
        }
        return baos.toByteArray();
    }

    synchronized void restore(@NonNull byte[] payload) {
        DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
        try {
            for (int conversationInfoSize = in.readInt();
                    conversationInfoSize != CONVERSATION_INFOS_END_TOKEN;
                    conversationInfoSize = in.readInt()) {
                byte[] conversationInfoPayload = new byte[conversationInfoSize];
                in.readFully(conversationInfoPayload, 0, conversationInfoSize);
                ConversationInfo conversationInfo = ConversationInfo.readFromBackupPayload(
                        conversationInfoPayload);
                if (conversationInfo != null) {
                    addOrUpdate(conversationInfo);
                }
            }
        } catch (IOException e) {
            Slog.e(TAG, "Failed to read conversation info from payload.", e);
        }
    }

    @MainThread
    private synchronized void updateConversationsInMemory(
            @NonNull ConversationInfo conversationInfo) {
Loading