1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.model; 18 19 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; 20 import static android.os.Process.myUserHandle; 21 22 import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME; 23 import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME; 24 import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb; 25 import static com.android.launcher3.provider.LauncherDbUtils.dropTable; 26 import static com.android.launcher3.provider.LauncherDbUtils.tableExists; 27 import static com.android.launcher3.util.LauncherModelHelper.APP_ICON; 28 import static com.android.launcher3.util.LauncherModelHelper.NO__ICON; 29 import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT; 30 import static com.android.launcher3.util.ReflectionHelpers.getField; 31 import static com.android.launcher3.util.ReflectionHelpers.setField; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertTrue; 35 import static org.mockito.ArgumentMatchers.eq; 36 import static org.mockito.Mockito.doReturn; 37 import static org.mockito.Mockito.spy; 38 39 import android.app.backup.BackupManager; 40 import android.content.pm.PackageInstaller; 41 import android.database.Cursor; 42 import android.database.sqlite.SQLiteDatabase; 43 import android.os.UserHandle; 44 import android.util.ArrayMap; 45 import android.util.LongSparseArray; 46 47 import androidx.test.ext.junit.runners.AndroidJUnit4; 48 import androidx.test.filters.SmallTest; 49 50 import com.android.launcher3.InvariantDeviceProfile; 51 import com.android.launcher3.pm.UserCache; 52 import com.android.launcher3.provider.RestoreDbTask; 53 import com.android.launcher3.util.LauncherModelHelper; 54 import com.android.launcher3.util.SafeCloseable; 55 56 import org.junit.After; 57 import org.junit.Before; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 61 /** 62 * Tests to verify backup and restore flow. 63 */ 64 @SmallTest 65 @RunWith(AndroidJUnit4.class) 66 public class BackupRestoreTest { 67 68 private static final int PER_USER_RANGE = 200000; 69 70 71 private long mCurrentMyProfileId; 72 private long mOldMyProfileId; 73 74 private long mCurrentWorkProfileId; 75 private long mOldWorkProfileId; 76 77 private BackupManager mBackupManager; 78 private LauncherModelHelper mModelHelper; 79 private SQLiteDatabase mDb; 80 private InvariantDeviceProfile mIdp; 81 82 private UserHandle mWorkUserHandle; 83 84 private SafeCloseable mUserChangeListener; 85 86 @Before setUp()87 public void setUp() { 88 mModelHelper = new LauncherModelHelper(); 89 90 mCurrentMyProfileId = mModelHelper.defaultProfileId; 91 mOldMyProfileId = mCurrentMyProfileId + 1; 92 mCurrentWorkProfileId = mOldMyProfileId + 1; 93 mOldWorkProfileId = mCurrentWorkProfileId + 1; 94 95 mWorkUserHandle = UserHandle.getUserHandleForUid(PER_USER_RANGE); 96 mUserChangeListener = UserCache.INSTANCE.get(mModelHelper.sandboxContext) 97 .addUserChangeListener(() -> { }); 98 99 setupUserManager(); 100 setupBackupManager(); 101 RestoreDbTask.setPending(mModelHelper.sandboxContext); 102 mDb = mModelHelper.provider.getDb(); 103 mIdp = InvariantDeviceProfile.INSTANCE.get(mModelHelper.sandboxContext); 104 105 } 106 107 @After tearDown()108 public void tearDown() { 109 mUserChangeListener.close(); 110 mModelHelper.destroy(); 111 } 112 setupUserManager()113 private void setupUserManager() { 114 UserCache cache = UserCache.INSTANCE.get(mModelHelper.sandboxContext); 115 synchronized (cache) { 116 LongSparseArray<UserHandle> users = getField(cache, "mUsers"); 117 users.clear(); 118 users.put(mCurrentMyProfileId, myUserHandle()); 119 users.put(mCurrentWorkProfileId, mWorkUserHandle); 120 121 ArrayMap<UserHandle, Long> userMap = getField(cache, "mUserToSerialMap"); 122 userMap.clear(); 123 userMap.put(myUserHandle(), mCurrentMyProfileId); 124 userMap.put(mWorkUserHandle, mCurrentWorkProfileId); 125 } 126 } 127 setupBackupManager()128 private void setupBackupManager() { 129 mBackupManager = spy(new BackupManager(mModelHelper.sandboxContext)); 130 doReturn(myUserHandle()).when(mBackupManager) 131 .getUserForAncestralSerialNumber(eq(mOldMyProfileId)); 132 doReturn(mWorkUserHandle).when(mBackupManager) 133 .getUserForAncestralSerialNumber(eq(mOldWorkProfileId)); 134 } 135 136 @Test testOnCreateDbIfNotExists_CreatesBackup()137 public void testOnCreateDbIfNotExists_CreatesBackup() { 138 assertTrue(tableExists(mDb, BACKUP_TABLE_NAME)); 139 } 140 141 @Test testOnRestoreSessionWithValidCondition_PerformsRestore()142 public void testOnRestoreSessionWithValidCondition_PerformsRestore() throws Exception { 143 setupBackup(); 144 verifyTableIsFilled(BACKUP_TABLE_NAME, false); 145 verifyTableIsEmpty(TABLE_NAME); 146 createRestoreSession(); 147 verifyTableIsFilled(TABLE_NAME, true); 148 } 149 setupBackup()150 private void setupBackup() { 151 createTableUsingOldProfileId(); 152 // setup grid for main user on first screen 153 mModelHelper.createGrid(new int[][][]{{ 154 { APP_ICON, APP_ICON, SHORTCUT, SHORTCUT}, 155 { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON}, 156 { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT}, 157 { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON}, 158 }}, 1, mOldMyProfileId); 159 // setup grid for work profile on second screen 160 mModelHelper.createGrid(new int[][][]{{ 161 { NO__ICON, APP_ICON, SHORTCUT, SHORTCUT}, 162 { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON}, 163 { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT}, 164 { APP_ICON, SHORTCUT, SHORTCUT, NO__ICON}, 165 }}, 2, mOldWorkProfileId); 166 // simulates the creation of backup upon restore 167 new GridBackupTable(mModelHelper.sandboxContext, mDb, mIdp.numDatabaseHotseatIcons, 168 mIdp.numColumns, mIdp.numRows).doBackup( 169 mOldMyProfileId, GridBackupTable.OPTION_REQUIRES_SANITIZATION); 170 // reset favorites table 171 createTableUsingOldProfileId(); 172 } 173 verifyTableIsEmpty(String tableName)174 private void verifyTableIsEmpty(String tableName) { 175 assertEquals(0, getCount(mDb, "SELECT * FROM " + tableName)); 176 } 177 verifyTableIsFilled(String tableName, boolean sanitized)178 private void verifyTableIsFilled(String tableName, boolean sanitized) { 179 assertEquals(sanitized ? 12 : 13, getCount(mDb, 180 "SELECT * FROM " + tableName + " WHERE profileId = " 181 + (sanitized ? mCurrentMyProfileId : mOldMyProfileId))); 182 assertEquals(10, getCount(mDb, "SELECT * FROM " + tableName + " WHERE profileId = " 183 + (sanitized ? mCurrentWorkProfileId : mOldWorkProfileId))); 184 } 185 createTableUsingOldProfileId()186 private void createTableUsingOldProfileId() { 187 // simulates the creation of favorites table on old device 188 dropTable(mDb, TABLE_NAME); 189 addTableToDb(mDb, mOldMyProfileId, false); 190 } 191 createRestoreSession()192 private void createRestoreSession() throws Exception { 193 final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 194 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 195 final PackageInstaller installer = mModelHelper.sandboxContext.getPackageManager() 196 .getPackageInstaller(); 197 final int sessionId = installer.createSession(params); 198 final PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId); 199 setField(info, "installReason", INSTALL_REASON_DEVICE_RESTORE); 200 // TODO: (b/148410677) we should verify the following call instead 201 // InstallSessionHelper.INSTANCE.get(getContext()).restoreDbIfApplicable(info); 202 RestoreDbTask.restoreIfPossible(mModelHelper.sandboxContext, 203 mModelHelper.provider.getHelper(), mBackupManager); 204 } 205 getCount(SQLiteDatabase db, String sql)206 private static int getCount(SQLiteDatabase db, String sql) { 207 try (Cursor c = db.rawQuery(sql, null)) { 208 return c.getCount(); 209 } 210 } 211 } 212