Loading services/core/java/com/android/server/pm/ArchiveManager.java +90 −10 Original line number Diff line number Diff line Loading @@ -16,14 +16,28 @@ package com.android.server.pm; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.IntentSender; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; import com.android.internal.annotations.GuardedBy; import com.android.server.pm.pkg.ArchiveState; import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo; import com.android.server.pm.pkg.PackageStateInternal; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** Loading @@ -35,37 +49,103 @@ import java.util.Objects; */ final class ArchiveManager { private final Context mContext; private final PackageManagerService mPm; ArchiveManager(PackageManagerService mPm) { @Nullable private LauncherApps mLauncherApps; ArchiveManager(Context context, PackageManagerService mPm) { this.mContext = context; this.mPm = mPm; } void archiveApp( @NonNull String packageName, @NonNull String callerPackageName, int userId, @NonNull UserHandle user, @NonNull IntentSender intentSender) throws PackageManager.NameNotFoundException { Objects.requireNonNull(packageName); Objects.requireNonNull(callerPackageName); Objects.requireNonNull(user); Objects.requireNonNull(intentSender); Computer snapshot = mPm.snapshotComputer(); int callingUid = Binder.getCallingUid(); int userId = user.getIdentifier(); String callingPackageName = snapshot.getNameForUid(callingUid); snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "archiveApp"); snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "archiveApp"); verifyCaller(callerPackageName, callingPackageName); PackageStateInternal ps = snapshot.getPackageStateInternal(packageName); PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user); verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource()); List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList( ps.getPackageName(), new UserHandle(userId)); // TODO(b/291569242) Verify that this list is not empty and return failure with intentsender storeArchiveState(ps, mainActivities, userId); // TODO(b/278553670) Add special strings for the delete dialog mPm.mInstallerService.uninstall( new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), callerPackageName, DELETE_KEEP_DATA, intentSender, userId); } @NonNull private static PackageStateInternal getPackageState(String packageName, Computer snapshot, int callingUid, UserHandle user) throws PackageManager.NameNotFoundException { PackageStateInternal ps = snapshot.getPackageStateFiltered(packageName, callingUid, user.getIdentifier()); if (ps == null) { throw new PackageManager.NameNotFoundException( TextUtils.formatSimple("Package %s not found.", packageName)); } return ps; } private LauncherApps getLauncherApps() { if (mLauncherApps == null) { mLauncherApps = mContext.getSystemService(LauncherApps.class); } return mLauncherApps; } verifyInstallOwnership(packageName, callingPackageName, ps); private void storeArchiveState(PackageStateInternal ps, List<LauncherActivityInfo> mainActivities, int userId) throws PackageManager.NameNotFoundException { List<ArchiveActivityInfo> activityInfos = new ArrayList<>(); for (int i = 0; i < mainActivities.size(); i++) { // TODO(b/278553670) Extract and store launcher icons ArchiveActivityInfo activityInfo = new ArchiveActivityInfo( mainActivities.get(i).getLabel().toString(), Path.of("/TODO"), null); activityInfos.add(activityInfo); } // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there InstallSource installSource = ps.getInstallSource(); String installerPackageName = installSource.mUpdateOwnerPackageName != null ? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName; // TODO(b/278553670) Complete implementations throw new UnsupportedOperationException("Method not implemented."); synchronized (mPm.mLock) { getPackageSetting(ps.getPackageName(), userId).modifyUserState(userId).setArchiveState( new ArchiveState(activityInfos, installerPackageName)); } } @NonNull @GuardedBy("mPm.mLock") private PackageSetting getPackageSetting(String packageName, int userId) throws PackageManager.NameNotFoundException { PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); if (ps == null || !ps.getUserStateOrDefault(userId).isInstalled()) { throw new PackageManager.NameNotFoundException( TextUtils.formatSimple("Package %s not found.", packageName)); } return ps; } private static void verifyCaller(String callerPackageName, String callingPackageName) { Loading @@ -80,14 +160,14 @@ final class ArchiveManager { } private static void verifyInstallOwnership(String packageName, String callingPackageName, PackageStateInternal ps) { if (!TextUtils.equals(ps.getInstallSource().mInstallerPackageName, InstallSource installSource) { if (!TextUtils.equals(installSource.mInstallerPackageName, callingPackageName)) { throw new SecurityException( TextUtils.formatSimple("Caller is not the installer of record for %s.", packageName)); } String updateOwnerPackageName = ps.getInstallSource().mUpdateOwnerPackageName; String updateOwnerPackageName = installSource.mUpdateOwnerPackageName; if (updateOwnerPackageName != null && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) { throw new SecurityException( Loading services/tests/servicestests/src/com/android/server/pm/ArchiveManagerTest.java→services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java +250 −0 Original line number Diff line number Diff line Loading @@ -16,28 +16,48 @@ package com.android.server.pm; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.IntentSender; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.os.Binder; import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.pm.pkg.ArchiveState; import com.android.server.pm.pkg.PackageStateInternal; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) Loading @@ -45,13 +65,24 @@ public class ArchiveManagerTest { private static final String PACKAGE = "com.example"; private static final String CALLER_PACKAGE = "com.vending"; private static final int USER_ID = 1; @Mock private IntentSender mIntentSender; @Mock private PackageManagerService mPm; @Mock private Computer mComputer; @Mock private PackageStateInternal mPackageState; private InstallSource mInstallSource = @Rule public final MockSystemRule mMockSystem = new MockSystemRule(); @Mock private IntentSender mIntentSender; @Mock private Computer mComputer; @Mock private Context mContext; @Mock private LauncherApps mLauncherApps; @Mock private PackageInstallerService mInstallerService; @Mock private PackageStateInternal mPackageState; private final InstallSource mInstallSource = InstallSource.create( CALLER_PACKAGE, CALLER_PACKAGE, Loading @@ -61,25 +92,48 @@ public class ArchiveManagerTest { /* installerAttributionTag= */ null, /* packageSource= */ 0); private final List<LauncherActivityInfo> mLauncherActivityInfos = createLauncherActivities(); private PackageSetting mPackageSetting; private PackageManagerService mPm; private ArchiveManager mArchiveManager; private int mCallingUid; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mArchiveManager = new ArchiveManager(mPm); mCallingUid = Binder.getCallingUid(); when(mPm.snapshotComputer()).thenReturn(mComputer); when(mComputer.getNameForUid(eq(mCallingUid))).thenReturn(CALLER_PACKAGE); when(mComputer.getPackageStateInternal(eq(PACKAGE))).thenReturn(mPackageState); mMockSystem.system().stageNominalSystemState(); when(mMockSystem.mocks().getInjector().getPackageInstallerService()).thenReturn( mInstallerService); mPm = spy(new PackageManagerService(mMockSystem.mocks().getInjector(), /* factoryTest= */false, MockSystem.Companion.getDEFAULT_VERSION_INFO().fingerprint, /* isEngBuild= */ false, /* isUserDebugBuild= */false, Build.VERSION_CODES.CUR_DEVELOPMENT, Build.VERSION.INCREMENTAL)); when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn( mPackageState); when(mPackageState.getPackageName()).thenReturn(PACKAGE); when(mPackageState.getInstallSource()).thenReturn(mInstallSource); mPackageSetting = createBasicPackageSetting(); when(mMockSystem.mocks().getSettings().getPackageLPr(eq(PACKAGE))).thenReturn( mPackageSetting); when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn( mLauncherActivityInfos); doReturn(mComputer).when(mPm).snapshotComputer(); when(mComputer.getNameForUid(eq(Binder.getCallingUid()))).thenReturn(CALLER_PACKAGE); mArchiveManager = new ArchiveManager(mContext, mPm); } @Test public void archiveApp_callerPackageNameIncorrect() { Exception e = assertThrows( SecurityException.class, () -> mArchiveManager.archiveApp(PACKAGE, "different", USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, "different", UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo( String.format( Loading @@ -91,11 +145,25 @@ public class ArchiveManagerTest { @Test public void archiveApp_packageNotInstalled() { when(mComputer.getPackageStateInternal(eq(PACKAGE))).thenReturn(null); when(mMockSystem.mocks().getSettings().getPackageLPr(eq(PACKAGE))).thenReturn( null); Exception e = assertThrows( PackageManager.NameNotFoundException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE)); } @Test public void archiveApp_packageNotInstalledForUser() { mPackageSetting.modifyUserState(UserHandle.CURRENT.getIdentifier()).setInstalled(false); Exception e = assertThrows( PackageManager.NameNotFoundException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE)); } Loading @@ -115,7 +183,8 @@ public class ArchiveManagerTest { Exception e = assertThrows( SecurityException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, Process.myUserHandle(), mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo( String.format("Caller is not the installer of record for %s.", PACKAGE)); Loading @@ -136,9 +205,46 @@ public class ArchiveManagerTest { Exception e = assertThrows( SecurityException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo( String.format("Caller is not the update owner for %s.", PACKAGE)); } @Test public void archiveApp_success() throws PackageManager.NameNotFoundException { List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>(); for (LauncherActivityInfo mainActivity : createLauncherActivities()) { // TODO(b/278553670) Extract and store launcher icons ArchiveState.ArchiveActivityInfo activityInfo = new ArchiveState.ArchiveActivityInfo( mainActivity.getLabel().toString(), Path.of("/TODO"), null); activityInfos.add(activityInfo); } mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender); verify(mInstallerService).uninstall( eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)), eq(CALLER_PACKAGE), eq(DELETE_KEEP_DATA), eq(mIntentSender), eq(UserHandle.CURRENT.getIdentifier())); assertThat(mPackageSetting.readUserState( UserHandle.CURRENT.getIdentifier()).getArchiveState()).isEqualTo( new ArchiveState(activityInfos, CALLER_PACKAGE)); } private static List<LauncherActivityInfo> createLauncherActivities() { LauncherActivityInfo activity1 = mock(LauncherActivityInfo.class); when(activity1.getLabel()).thenReturn("activity1"); LauncherActivityInfo activity2 = mock(LauncherActivityInfo.class); when(activity2.getLabel()).thenReturn("activity2"); return List.of(activity1, activity2); } private PackageSetting createBasicPackageSetting() { return new PackageSettingBuilder() .setName(PACKAGE).setCodePath("/data/app/" + PACKAGE + "-randompath") .setInstallState(UserHandle.CURRENT.getIdentifier(), /* installed= */ true) .build(); } } Loading
services/core/java/com/android/server/pm/ArchiveManager.java +90 −10 Original line number Diff line number Diff line Loading @@ -16,14 +16,28 @@ package com.android.server.pm; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.IntentSender; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; import com.android.internal.annotations.GuardedBy; import com.android.server.pm.pkg.ArchiveState; import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo; import com.android.server.pm.pkg.PackageStateInternal; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** Loading @@ -35,37 +49,103 @@ import java.util.Objects; */ final class ArchiveManager { private final Context mContext; private final PackageManagerService mPm; ArchiveManager(PackageManagerService mPm) { @Nullable private LauncherApps mLauncherApps; ArchiveManager(Context context, PackageManagerService mPm) { this.mContext = context; this.mPm = mPm; } void archiveApp( @NonNull String packageName, @NonNull String callerPackageName, int userId, @NonNull UserHandle user, @NonNull IntentSender intentSender) throws PackageManager.NameNotFoundException { Objects.requireNonNull(packageName); Objects.requireNonNull(callerPackageName); Objects.requireNonNull(user); Objects.requireNonNull(intentSender); Computer snapshot = mPm.snapshotComputer(); int callingUid = Binder.getCallingUid(); int userId = user.getIdentifier(); String callingPackageName = snapshot.getNameForUid(callingUid); snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "archiveApp"); snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "archiveApp"); verifyCaller(callerPackageName, callingPackageName); PackageStateInternal ps = snapshot.getPackageStateInternal(packageName); PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user); verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource()); List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList( ps.getPackageName(), new UserHandle(userId)); // TODO(b/291569242) Verify that this list is not empty and return failure with intentsender storeArchiveState(ps, mainActivities, userId); // TODO(b/278553670) Add special strings for the delete dialog mPm.mInstallerService.uninstall( new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), callerPackageName, DELETE_KEEP_DATA, intentSender, userId); } @NonNull private static PackageStateInternal getPackageState(String packageName, Computer snapshot, int callingUid, UserHandle user) throws PackageManager.NameNotFoundException { PackageStateInternal ps = snapshot.getPackageStateFiltered(packageName, callingUid, user.getIdentifier()); if (ps == null) { throw new PackageManager.NameNotFoundException( TextUtils.formatSimple("Package %s not found.", packageName)); } return ps; } private LauncherApps getLauncherApps() { if (mLauncherApps == null) { mLauncherApps = mContext.getSystemService(LauncherApps.class); } return mLauncherApps; } verifyInstallOwnership(packageName, callingPackageName, ps); private void storeArchiveState(PackageStateInternal ps, List<LauncherActivityInfo> mainActivities, int userId) throws PackageManager.NameNotFoundException { List<ArchiveActivityInfo> activityInfos = new ArrayList<>(); for (int i = 0; i < mainActivities.size(); i++) { // TODO(b/278553670) Extract and store launcher icons ArchiveActivityInfo activityInfo = new ArchiveActivityInfo( mainActivities.get(i).getLabel().toString(), Path.of("/TODO"), null); activityInfos.add(activityInfo); } // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there InstallSource installSource = ps.getInstallSource(); String installerPackageName = installSource.mUpdateOwnerPackageName != null ? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName; // TODO(b/278553670) Complete implementations throw new UnsupportedOperationException("Method not implemented."); synchronized (mPm.mLock) { getPackageSetting(ps.getPackageName(), userId).modifyUserState(userId).setArchiveState( new ArchiveState(activityInfos, installerPackageName)); } } @NonNull @GuardedBy("mPm.mLock") private PackageSetting getPackageSetting(String packageName, int userId) throws PackageManager.NameNotFoundException { PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); if (ps == null || !ps.getUserStateOrDefault(userId).isInstalled()) { throw new PackageManager.NameNotFoundException( TextUtils.formatSimple("Package %s not found.", packageName)); } return ps; } private static void verifyCaller(String callerPackageName, String callingPackageName) { Loading @@ -80,14 +160,14 @@ final class ArchiveManager { } private static void verifyInstallOwnership(String packageName, String callingPackageName, PackageStateInternal ps) { if (!TextUtils.equals(ps.getInstallSource().mInstallerPackageName, InstallSource installSource) { if (!TextUtils.equals(installSource.mInstallerPackageName, callingPackageName)) { throw new SecurityException( TextUtils.formatSimple("Caller is not the installer of record for %s.", packageName)); } String updateOwnerPackageName = ps.getInstallSource().mUpdateOwnerPackageName; String updateOwnerPackageName = installSource.mUpdateOwnerPackageName; if (updateOwnerPackageName != null && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) { throw new SecurityException( Loading
services/tests/servicestests/src/com/android/server/pm/ArchiveManagerTest.java→services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java +250 −0 Original line number Diff line number Diff line Loading @@ -16,28 +16,48 @@ package com.android.server.pm; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.IntentSender; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.os.Binder; import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.pm.pkg.ArchiveState; import com.android.server.pm.pkg.PackageStateInternal; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) Loading @@ -45,13 +65,24 @@ public class ArchiveManagerTest { private static final String PACKAGE = "com.example"; private static final String CALLER_PACKAGE = "com.vending"; private static final int USER_ID = 1; @Mock private IntentSender mIntentSender; @Mock private PackageManagerService mPm; @Mock private Computer mComputer; @Mock private PackageStateInternal mPackageState; private InstallSource mInstallSource = @Rule public final MockSystemRule mMockSystem = new MockSystemRule(); @Mock private IntentSender mIntentSender; @Mock private Computer mComputer; @Mock private Context mContext; @Mock private LauncherApps mLauncherApps; @Mock private PackageInstallerService mInstallerService; @Mock private PackageStateInternal mPackageState; private final InstallSource mInstallSource = InstallSource.create( CALLER_PACKAGE, CALLER_PACKAGE, Loading @@ -61,25 +92,48 @@ public class ArchiveManagerTest { /* installerAttributionTag= */ null, /* packageSource= */ 0); private final List<LauncherActivityInfo> mLauncherActivityInfos = createLauncherActivities(); private PackageSetting mPackageSetting; private PackageManagerService mPm; private ArchiveManager mArchiveManager; private int mCallingUid; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mArchiveManager = new ArchiveManager(mPm); mCallingUid = Binder.getCallingUid(); when(mPm.snapshotComputer()).thenReturn(mComputer); when(mComputer.getNameForUid(eq(mCallingUid))).thenReturn(CALLER_PACKAGE); when(mComputer.getPackageStateInternal(eq(PACKAGE))).thenReturn(mPackageState); mMockSystem.system().stageNominalSystemState(); when(mMockSystem.mocks().getInjector().getPackageInstallerService()).thenReturn( mInstallerService); mPm = spy(new PackageManagerService(mMockSystem.mocks().getInjector(), /* factoryTest= */false, MockSystem.Companion.getDEFAULT_VERSION_INFO().fingerprint, /* isEngBuild= */ false, /* isUserDebugBuild= */false, Build.VERSION_CODES.CUR_DEVELOPMENT, Build.VERSION.INCREMENTAL)); when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn( mPackageState); when(mPackageState.getPackageName()).thenReturn(PACKAGE); when(mPackageState.getInstallSource()).thenReturn(mInstallSource); mPackageSetting = createBasicPackageSetting(); when(mMockSystem.mocks().getSettings().getPackageLPr(eq(PACKAGE))).thenReturn( mPackageSetting); when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn( mLauncherActivityInfos); doReturn(mComputer).when(mPm).snapshotComputer(); when(mComputer.getNameForUid(eq(Binder.getCallingUid()))).thenReturn(CALLER_PACKAGE); mArchiveManager = new ArchiveManager(mContext, mPm); } @Test public void archiveApp_callerPackageNameIncorrect() { Exception e = assertThrows( SecurityException.class, () -> mArchiveManager.archiveApp(PACKAGE, "different", USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, "different", UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo( String.format( Loading @@ -91,11 +145,25 @@ public class ArchiveManagerTest { @Test public void archiveApp_packageNotInstalled() { when(mComputer.getPackageStateInternal(eq(PACKAGE))).thenReturn(null); when(mMockSystem.mocks().getSettings().getPackageLPr(eq(PACKAGE))).thenReturn( null); Exception e = assertThrows( PackageManager.NameNotFoundException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE)); } @Test public void archiveApp_packageNotInstalledForUser() { mPackageSetting.modifyUserState(UserHandle.CURRENT.getIdentifier()).setInstalled(false); Exception e = assertThrows( PackageManager.NameNotFoundException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE)); } Loading @@ -115,7 +183,8 @@ public class ArchiveManagerTest { Exception e = assertThrows( SecurityException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, Process.myUserHandle(), mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo( String.format("Caller is not the installer of record for %s.", PACKAGE)); Loading @@ -136,9 +205,46 @@ public class ArchiveManagerTest { Exception e = assertThrows( SecurityException.class, () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, USER_ID, mIntentSender) () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo( String.format("Caller is not the update owner for %s.", PACKAGE)); } @Test public void archiveApp_success() throws PackageManager.NameNotFoundException { List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>(); for (LauncherActivityInfo mainActivity : createLauncherActivities()) { // TODO(b/278553670) Extract and store launcher icons ArchiveState.ArchiveActivityInfo activityInfo = new ArchiveState.ArchiveActivityInfo( mainActivity.getLabel().toString(), Path.of("/TODO"), null); activityInfos.add(activityInfo); } mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender); verify(mInstallerService).uninstall( eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)), eq(CALLER_PACKAGE), eq(DELETE_KEEP_DATA), eq(mIntentSender), eq(UserHandle.CURRENT.getIdentifier())); assertThat(mPackageSetting.readUserState( UserHandle.CURRENT.getIdentifier()).getArchiveState()).isEqualTo( new ArchiveState(activityInfos, CALLER_PACKAGE)); } private static List<LauncherActivityInfo> createLauncherActivities() { LauncherActivityInfo activity1 = mock(LauncherActivityInfo.class); when(activity1.getLabel()).thenReturn("activity1"); LauncherActivityInfo activity2 = mock(LauncherActivityInfo.class); when(activity2.getLabel()).thenReturn("activity2"); return List.of(activity1, activity2); } private PackageSetting createBasicPackageSetting() { return new PackageSettingBuilder() .setName(PACKAGE).setCodePath("/data/app/" + PACKAGE + "-randompath") .setInstallState(UserHandle.CURRENT.getIdentifier(), /* installed= */ true) .build(); } }