summaryrefslogtreecommitdiff
path: root/services/people
diff options
context:
space:
mode:
authorTrung Lam <lamtrung@google.com>2020-02-24 16:51:37 -0800
committerTrung Lam <lamtrung@google.com>2020-02-25 14:32:25 -0800
commitb213fca0ed9770a3011a69c7f5ae2208e2ab6f47 (patch)
treeee9e792abd0388119102922d008247e099a27ffa /services/people
parent3ef69f7e7fd309db03af6047f58a4fed3ce44c51 (diff)
Implement backup and restoration of conversation infos.
Change-Id: I2cc08c54b714b4ab65762ae261cf8a50d7f258fd Test: Built and tested on device. Bug: 147512341
Diffstat (limited to 'services/people')
-rw-r--r--services/people/java/com/android/server/people/PeopleService.java10
-rw-r--r--services/people/java/com/android/server/people/data/ConversationInfo.java55
-rw-r--r--services/people/java/com/android/server/people/data/ConversationStore.java51
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java19
-rw-r--r--services/people/java/com/android/server/people/data/UserData.java52
5 files changed, 183 insertions, 4 deletions
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 2499614a3738..20302990cae7 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -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
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 41bc3611e085..dc3fa2a048f6 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -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.
*/
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 89c4972c8ef4..2f2a95cb0a6c 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -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) {
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 3a34c6a6be95..e4ce6bafff31 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -334,6 +334,25 @@ public class DataManager {
});
}
+ /** Retrieves a backup payload blob for specified user id. */
+ @Nullable
+ public byte[] getBackupPayload(@UserIdInt int userId) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return null;
+ }
+ return userData.getBackupPayload();
+ }
+
+ /** Attempts to restore data for the specified user id. */
+ public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return;
+ }
+ userData.restore(payload);
+ }
+
private int mimeTypeToShareEventType(String mimeType) {
if (mimeType.startsWith("text/")) {
return Event.TYPE_SHARE_TEXT;
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index 0f8b91bfa2b1..ed8c595ab42a 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -22,8 +22,14 @@ import android.annotation.UserIdInt;
import android.os.Environment;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.Slog;
+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.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
@@ -31,6 +37,10 @@ import java.util.function.Consumer;
/** The data associated with a user profile. */
class UserData {
+ private static final String TAG = UserData.class.getSimpleName();
+
+ private static final int CONVERSATIONS_END_TOKEN = -1;
+
private final @UserIdInt int mUserId;
private final File mPerUserPeopleDataDir;
@@ -125,6 +135,48 @@ class UserData {
return mDefaultSmsApp != null ? getPackageData(mDefaultSmsApp) : null;
}
+ @Nullable
+ byte[] getBackupPayload() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(baos);
+ for (PackageData packageData : mPackageDataMap.values()) {
+ try {
+ byte[] conversationsBackupPayload =
+ packageData.getConversationStore().getBackupPayload();
+ out.writeInt(conversationsBackupPayload.length);
+ out.write(conversationsBackupPayload);
+ out.writeUTF(packageData.getPackageName());
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversations to backup payload.", e);
+ return null;
+ }
+ }
+ try {
+ out.writeInt(CONVERSATIONS_END_TOKEN);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversations end token to backup payload.", e);
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
+ void restore(@NonNull byte[] payload) {
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
+ try {
+ for (int conversationsPayloadSize = in.readInt();
+ conversationsPayloadSize != CONVERSATIONS_END_TOKEN;
+ conversationsPayloadSize = in.readInt()) {
+ byte[] conversationsPayload = new byte[conversationsPayloadSize];
+ in.readFully(conversationsPayload, 0, conversationsPayloadSize);
+ String packageName = in.readUTF();
+ getOrCreatePackageData(packageName).getConversationStore().restore(
+ conversationsPayload);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to restore conversations from backup payload.", e);
+ }
+ }
+
private PackageData createPackageData(String packageName) {
return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
mScheduledExecutorService, mPerUserPeopleDataDir);