Loading services/core/java/com/android/server/pm/PackageArchiver.java +36 −8 Original line number Diff line number Diff line Loading @@ -676,23 +676,51 @@ public class PackageArchiver { PackageStateInternal ps; try { ps = getPackageState(packageName, snapshot, callingUid, userId); snapshot.enforceCrossUserPermission(callingUid, userId, true, false, "getArchivedAppIcon"); verifyArchived(ps, userId); } catch (PackageManager.NameNotFoundException e) { throw new ParcelableException(e); Slog.e(TAG, TextUtils.formatSimple("Package %s couldn't be found.", packageName), e); return null; } List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault( userId).getArchiveState().getActivityInfos(); if (activityInfos.size() == 0) { ArchiveState archiveState = getAnyArchiveState(ps, userId); if (archiveState == null || archiveState.getActivityInfos().size() == 0) { return null; } // TODO(b/298452477) Handle monochrome icons. // In the rare case the archived app defined more than two launcher activities, we choose // the first one arbitrarily. return includeCloudOverlay(decodeIcon(activityInfos.get(0))); return includeCloudOverlay(decodeIcon(archiveState.getActivityInfos().get(0))); } /** * This method first checks the ArchiveState for the provided userId and then tries to fallback * to other users if the current user is not archived. * * <p> This fallback behaviour is required for archived apps to fit into the multi-user world * where APKs are shared across users. E.g. current ways of fetching icons for apps that are * only installed on the work profile also work when executed on the personal profile if you're * using {@link PackageManager#MATCH_UNINSTALLED_PACKAGES}. Resource fetching from APKs is for * the most part userId-agnostic, which we need to mimic here in order for existing methods * like {@link PackageManager#getApplicationIcon} to continue working. * * @return {@link ArchiveState} for {@code userId} if present. If not present, false back to an * arbitrary userId. If no user is archived, returns null. */ @Nullable private ArchiveState getAnyArchiveState(PackageStateInternal ps, int userId) { PackageUserStateInternal userState = ps.getUserStateOrDefault(userId); if (isArchived(userState)) { return userState.getArchiveState(); } for (int i = 0; i < ps.getUserStates().size(); i++) { userState = ps.getUserStates().valueAt(i); if (isArchived(userState)) { return userState.getArchiveState(); } } return null; } @VisibleForTesting Loading services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +4 −12 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.os.ParcelableException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading Loading @@ -174,6 +175,7 @@ public class PackageArchiverTest { mUserState = new PackageUserStateImpl().setInstalled(true); mPackageSetting.setUserState(mUserId, mUserState); when(mPackageState.getUserStateOrDefault(eq(mUserId))).thenReturn(mUserState); when(mPackageState.getUserStates()).thenReturn(new SparseArray<>()); when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); when(mContext.getSystemService(AppOpsManager.class)).thenReturn( Loading Loading @@ -551,22 +553,12 @@ public class PackageArchiverTest { when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn( null); Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("Package %s not found.", PACKAGE)); assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test public void getArchivedAppIcon_notArchived() { Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("Package %s is not currently archived.", PACKAGE)); assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test Loading Loading
services/core/java/com/android/server/pm/PackageArchiver.java +36 −8 Original line number Diff line number Diff line Loading @@ -676,23 +676,51 @@ public class PackageArchiver { PackageStateInternal ps; try { ps = getPackageState(packageName, snapshot, callingUid, userId); snapshot.enforceCrossUserPermission(callingUid, userId, true, false, "getArchivedAppIcon"); verifyArchived(ps, userId); } catch (PackageManager.NameNotFoundException e) { throw new ParcelableException(e); Slog.e(TAG, TextUtils.formatSimple("Package %s couldn't be found.", packageName), e); return null; } List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault( userId).getArchiveState().getActivityInfos(); if (activityInfos.size() == 0) { ArchiveState archiveState = getAnyArchiveState(ps, userId); if (archiveState == null || archiveState.getActivityInfos().size() == 0) { return null; } // TODO(b/298452477) Handle monochrome icons. // In the rare case the archived app defined more than two launcher activities, we choose // the first one arbitrarily. return includeCloudOverlay(decodeIcon(activityInfos.get(0))); return includeCloudOverlay(decodeIcon(archiveState.getActivityInfos().get(0))); } /** * This method first checks the ArchiveState for the provided userId and then tries to fallback * to other users if the current user is not archived. * * <p> This fallback behaviour is required for archived apps to fit into the multi-user world * where APKs are shared across users. E.g. current ways of fetching icons for apps that are * only installed on the work profile also work when executed on the personal profile if you're * using {@link PackageManager#MATCH_UNINSTALLED_PACKAGES}. Resource fetching from APKs is for * the most part userId-agnostic, which we need to mimic here in order for existing methods * like {@link PackageManager#getApplicationIcon} to continue working. * * @return {@link ArchiveState} for {@code userId} if present. If not present, false back to an * arbitrary userId. If no user is archived, returns null. */ @Nullable private ArchiveState getAnyArchiveState(PackageStateInternal ps, int userId) { PackageUserStateInternal userState = ps.getUserStateOrDefault(userId); if (isArchived(userState)) { return userState.getArchiveState(); } for (int i = 0; i < ps.getUserStates().size(); i++) { userState = ps.getUserStates().valueAt(i); if (isArchived(userState)) { return userState.getArchiveState(); } } return null; } @VisibleForTesting Loading
services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +4 −12 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.os.ParcelableException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; Loading Loading @@ -174,6 +175,7 @@ public class PackageArchiverTest { mUserState = new PackageUserStateImpl().setInstalled(true); mPackageSetting.setUserState(mUserId, mUserState); when(mPackageState.getUserStateOrDefault(eq(mUserId))).thenReturn(mUserState); when(mPackageState.getUserStates()).thenReturn(new SparseArray<>()); when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); when(mContext.getSystemService(AppOpsManager.class)).thenReturn( Loading Loading @@ -551,22 +553,12 @@ public class PackageArchiverTest { when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn( null); Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("Package %s not found.", PACKAGE)); assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test public void getArchivedAppIcon_notArchived() { Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("Package %s is not currently archived.", PACKAGE)); assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test Loading