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

Commit 469b5101 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

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

Merge "Implement backup and restoration of conversation infos." into rvc-dev am: 8839aaee am: 350f6f8f

Change-Id: Ia6841a2a264f975e479bc0127747d571f6957f14
parents 2fe4cde3 350f6f8f
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