Loading services/core/java/com/android/server/backup/PeopleBackupHelper.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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); } } services/core/java/com/android/server/people/PeopleServiceInternal.java +10 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } services/people/java/com/android/server/people/PeopleService.java +6 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading services/people/java/com/android/server/people/data/ConversationInfo.java +55 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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. */ Loading services/people/java/com/android/server/people/data/ConversationStore.java +51 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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<>(); Loading Loading @@ -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 Loading
services/core/java/com/android/server/backup/PeopleBackupHelper.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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); } }
services/core/java/com/android/server/people/PeopleServiceInternal.java +10 −8 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); }
services/people/java/com/android/server/people/PeopleService.java +6 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading
services/people/java/com/android/server/people/data/ConversationInfo.java +55 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) Loading Loading @@ -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. */ Loading
services/people/java/com/android/server/people/data/ConversationStore.java +51 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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<>(); Loading Loading @@ -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