Merge "Implement backup and restoration of conversation infos." into rvc-dev am: 8839aaee6d am: 350f6f8f9b
Change-Id: Ia6841a2a264f975e479bc0127747d571f6957f14
This commit is contained in:
@@ -50,7 +50,7 @@ class PeopleBackupHelper extends BlobBackupHelper {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Handling backup of " + key);
|
Slog.d(TAG, "Handling backup of " + key);
|
||||||
}
|
}
|
||||||
return ps.backupConversationInfos(mUserId);
|
return ps.getBackupPayload(mUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,6 +63,6 @@ class PeopleBackupHelper extends BlobBackupHelper {
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.d(TAG, "Handling restore of " + key);
|
Slog.d(TAG, "Handling restore of " + key);
|
||||||
}
|
}
|
||||||
ps.restoreConversationInfos(mUserId, key, payload);
|
ps.restore(mUserId, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server.people;
|
package com.android.server.people;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
import android.service.appprediction.IPredictionService;
|
import android.service.appprediction.IPredictionService;
|
||||||
@@ -34,16 +35,17 @@ public abstract class PeopleServiceInternal extends IPredictionService.Stub {
|
|||||||
@NonNull CancellationSignal signal);
|
@NonNull CancellationSignal signal);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number conversation infos will be dynamic, based on the currently installed apps on the
|
* Returns a backup payload that contains conversation infos. The number conversation infos will
|
||||||
* device. All of which should be combined into a single blob to be backed up.
|
* 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
|
* Restores conversation infos stored in payload blob. Multiple conversation infos may exist in
|
||||||
* manage the restoration based on how individual conversation infos were originally combined
|
* the restore payload, child classes are required to manage the restoration based on how
|
||||||
* during backup.
|
* individual conversation infos were originally combined during backup.
|
||||||
*/
|
*/
|
||||||
public abstract void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
|
public abstract void restore(@UserIdInt int userId, @NonNull byte[] payload);
|
||||||
@NonNull byte[] payload);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server.people;
|
package com.android.server.people;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
import android.app.prediction.AppPredictionContext;
|
import android.app.prediction.AppPredictionContext;
|
||||||
import android.app.prediction.AppPredictionSessionId;
|
import android.app.prediction.AppPredictionSessionId;
|
||||||
@@ -145,14 +146,15 @@ public class PeopleService extends SystemService {
|
|||||||
mDataManager.pruneDataForUser(userId, signal);
|
mDataManager.pruneDataForUser(userId, signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public byte[] backupConversationInfos(@UserIdInt int userId) {
|
public byte[] getBackupPayload(@UserIdInt int userId) {
|
||||||
return new byte[0];
|
return mDataManager.getBackupPayload(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
|
public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
|
||||||
@NonNull byte[] payload) {
|
mDataManager.restore(userId, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.content.LocusIdProto;
|
|||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
import android.content.pm.ShortcutInfo.ShortcutFlags;
|
import android.content.pm.ShortcutInfo.ShortcutFlags;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.proto.ProtoInputStream;
|
import android.util.proto.ProtoInputStream;
|
||||||
import android.util.proto.ProtoOutputStream;
|
import android.util.proto.ProtoOutputStream;
|
||||||
@@ -31,6 +32,10 @@ import android.util.proto.ProtoOutputStream;
|
|||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
import com.android.server.people.ConversationInfoProto;
|
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.io.IOException;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
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}. */
|
/** Reads from {@link ProtoInputStream} and constructs a {@link ConversationInfo}. */
|
||||||
@NonNull
|
@NonNull
|
||||||
static ConversationInfo readFromProto(@NonNull ProtoInputStream protoInputStream)
|
static ConversationInfo readFromProto(@NonNull ProtoInputStream protoInputStream)
|
||||||
@@ -331,6 +355,37 @@ public class ConversationInfo {
|
|||||||
return builder.build();
|
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.
|
* Builder class for {@link ConversationInfo} objects.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ import com.android.server.people.ConversationInfosProto;
|
|||||||
|
|
||||||
import com.google.android.collect.Lists;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -48,6 +52,8 @@ class ConversationStore {
|
|||||||
|
|
||||||
private static final String CONVERSATIONS_FILE_NAME = "conversations";
|
private static final String CONVERSATIONS_FILE_NAME = "conversations";
|
||||||
|
|
||||||
|
private static final int CONVERSATION_INFOS_END_TOKEN = -1;
|
||||||
|
|
||||||
// Shortcut ID -> Conversation Info
|
// Shortcut ID -> Conversation Info
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
|
private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
|
||||||
@@ -195,6 +201,51 @@ class ConversationStore {
|
|||||||
mConversationInfosProtoDiskReadWriter.deleteConversationsFile();
|
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
|
@MainThread
|
||||||
private synchronized void updateConversationsInMemory(
|
private synchronized void updateConversationsInMemory(
|
||||||
@NonNull ConversationInfo conversationInfo) {
|
@NonNull ConversationInfo conversationInfo) {
|
||||||
|
|||||||
@@ -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) {
|
private int mimeTypeToShareEventType(String mimeType) {
|
||||||
if (mimeType.startsWith("text/")) {
|
if (mimeType.startsWith("text/")) {
|
||||||
return Event.TYPE_SHARE_TEXT;
|
return Event.TYPE_SHARE_TEXT;
|
||||||
|
|||||||
@@ -22,8 +22,14 @@ import android.annotation.UserIdInt;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArrayMap;
|
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.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -31,6 +37,10 @@ import java.util.function.Consumer;
|
|||||||
/** The data associated with a user profile. */
|
/** The data associated with a user profile. */
|
||||||
class UserData {
|
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 @UserIdInt int mUserId;
|
||||||
|
|
||||||
private final File mPerUserPeopleDataDir;
|
private final File mPerUserPeopleDataDir;
|
||||||
@@ -125,6 +135,48 @@ class UserData {
|
|||||||
return mDefaultSmsApp != null ? getPackageData(mDefaultSmsApp) : null;
|
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) {
|
private PackageData createPackageData(String packageName) {
|
||||||
return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
|
return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
|
||||||
mScheduledExecutorService, mPerUserPeopleDataDir);
|
mScheduledExecutorService, mPerUserPeopleDataDir);
|
||||||
|
|||||||
@@ -284,6 +284,30 @@ public final class ConversationStoreTest {
|
|||||||
assertEquals(in2, out2);
|
assertEquals(in2, out2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBackupAndRestore() {
|
||||||
|
ConversationInfo in1 = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
|
||||||
|
PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
|
||||||
|
ConversationInfo in2 = buildConversationInfo(SHORTCUT_ID_2, LOCUS_ID_2, CONTACT_URI_2,
|
||||||
|
PHONE_NUMBER_2, NOTIFICATION_CHANNEL_ID_2);
|
||||||
|
mConversationStore.addOrUpdate(in1);
|
||||||
|
mConversationStore.addOrUpdate(in2);
|
||||||
|
|
||||||
|
byte[] backupPayload = mConversationStore.getBackupPayload();
|
||||||
|
assertNotNull(backupPayload);
|
||||||
|
|
||||||
|
ConversationStore conversationStore = new ConversationStore(mFile,
|
||||||
|
mMockScheduledExecutorService);
|
||||||
|
ConversationInfo out1 = conversationStore.getConversation(SHORTCUT_ID);
|
||||||
|
assertNull(out1);
|
||||||
|
|
||||||
|
conversationStore.restore(backupPayload);
|
||||||
|
out1 = conversationStore.getConversation(SHORTCUT_ID);
|
||||||
|
ConversationInfo out2 = conversationStore.getConversation(SHORTCUT_ID_2);
|
||||||
|
assertEquals(in1, out1);
|
||||||
|
assertEquals(in2, out2);
|
||||||
|
}
|
||||||
|
|
||||||
private void resetConversationStore() {
|
private void resetConversationStore() {
|
||||||
mFile.mkdir();
|
mFile.mkdir();
|
||||||
mMockScheduledExecutorService = new MockScheduledExecutorService();
|
mMockScheduledExecutorService = new MockScheduledExecutorService();
|
||||||
|
|||||||
@@ -653,6 +653,33 @@ public final class DataManagerTest {
|
|||||||
assertTrue(activeTimeSlots.isEmpty());
|
assertTrue(activeTimeSlots.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBackupAndRestoration()
|
||||||
|
throws IntentFilter.MalformedMimeTypeException {
|
||||||
|
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||||
|
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
|
||||||
|
null);
|
||||||
|
AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
|
||||||
|
.build();
|
||||||
|
AppTargetEvent appTargetEvent =
|
||||||
|
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
|
||||||
|
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
|
||||||
|
.build();
|
||||||
|
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
|
||||||
|
|
||||||
|
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
|
||||||
|
byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
|
||||||
|
|
||||||
|
DataManager dataManager = new DataManager(mContext, mInjector);
|
||||||
|
dataManager.onUserUnlocked(USER_ID_PRIMARY);
|
||||||
|
dataManager.restore(USER_ID_PRIMARY, payload);
|
||||||
|
ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
|
||||||
|
.getConversationStore()
|
||||||
|
.getConversation(TEST_SHORTCUT_ID);
|
||||||
|
assertNotNull(conversationInfo);
|
||||||
|
assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
|
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
|
||||||
LocalServices.removeServiceForTest(clazz);
|
LocalServices.removeServiceForTest(clazz);
|
||||||
LocalServices.addService(clazz, mock);
|
LocalServices.addService(clazz, mock);
|
||||||
|
|||||||
Reference in New Issue
Block a user