diff options
| author | 2023-12-13 11:02:10 +0000 | |
|---|---|---|
| committer | 2023-12-13 11:02:10 +0000 | |
| commit | 861eaf127d41ba79635f144b01ac331c6a20294f (patch) | |
| tree | 0e113566aede8f76fc4b0f200b4a6b22743fddc0 | |
| parent | 53d81cb3c4a89f4d7f74669629030c5499169f9f (diff) | |
| parent | 2cbb8d4c15b4ccca318966873aa6124cb80a9fdf (diff) | |
Merge "Bugfix: Return an app launcher icon for archived apps across profiles." into main
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageArchiver.java | 44 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java | 16 |
2 files changed, 40 insertions, 20 deletions
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index e3bab3f243c3..40e66c980592 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -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 diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java index 2332988cc832..73d300d5a4c6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java @@ -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; @@ -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( @@ -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 |