diff --git a/core/java/android/content/pm/ArchivedActivityParcel.aidl b/core/java/android/content/pm/ArchivedActivityParcel.aidl index 7ab7ed1cc5df571edfb55c99cf112d1cbd4a357b..74953fff40d87b2ff7eb241a85148c83ffeaa358 100644 --- a/core/java/android/content/pm/ArchivedActivityParcel.aidl +++ b/core/java/android/content/pm/ArchivedActivityParcel.aidl @@ -16,9 +16,12 @@ package android.content.pm; +import android.content.ComponentName; + /** @hide */ parcelable ArchivedActivityParcel { String title; + ComponentName originalComponentName; // PNG compressed bitmaps. byte[] iconBitmap; byte[] monochromeIconBitmap; diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto index eb14db070f9f32a459d4173b3c81689c30a368ab..068f4dd07ccb6d1bfa460fc0c53314d6d2731631 100644 --- a/core/proto/android/service/package.proto +++ b/core/proto/android/service/package.proto @@ -115,6 +115,9 @@ message PackageProto { // Only set if the app defined a monochrome icon. optional string monochrome_icon_bitmap_path = 3; + + // The component name of the original activity (pre-archival). + optional string original_component_name = 4; } /** Information about main activities. */ diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index f59188e9fd93eaefb94bf095a13d3e5ddc281d83..0fb1f7a0780cee0d6ad1aab8e48b931f7336df6d 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -196,8 +196,12 @@ public class PackageArchiver { for (int i = 0, size = mainActivities.length; i < size; ++i) { var mainActivity = mainActivities[i]; Path iconPath = storeIconForParcel(packageName, mainActivity, userId, i); - ArchiveActivityInfo activityInfo = new ArchiveActivityInfo( - mainActivity.title, iconPath, null); + ArchiveActivityInfo activityInfo = + new ArchiveActivityInfo( + mainActivity.title, + mainActivity.originalComponentName, + iconPath, + null); archiveActivityInfos.add(activityInfo); } @@ -215,8 +219,12 @@ public class PackageArchiver { for (int i = 0, size = mainActivities.size(); i < size; i++) { LauncherActivityInfo mainActivity = mainActivities.get(i); Path iconPath = storeIcon(packageName, mainActivity, userId, i); - ArchiveActivityInfo activityInfo = new ArchiveActivityInfo( - mainActivity.getLabel().toString(), iconPath, null); + ArchiveActivityInfo activityInfo = + new ArchiveActivityInfo( + mainActivity.getLabel().toString(), + mainActivity.getComponentName(), + iconPath, + null); archiveActivityInfos.add(activityInfo); } @@ -593,6 +601,7 @@ public class PackageArchiver { } var archivedActivity = new ArchivedActivityParcel(); archivedActivity.title = info.getTitle(); + archivedActivity.originalComponentName = info.getOriginalComponentName(); archivedActivity.iconBitmap = bytesFromBitmapFile(info.getIconBitmap()); archivedActivity.monochromeIconBitmap = bytesFromBitmapFile( info.getMonochromeIconBitmap()); @@ -624,6 +633,7 @@ public class PackageArchiver { } var archivedActivity = new ArchivedActivityParcel(); archivedActivity.title = info.getLabel().toString(); + archivedActivity.originalComponentName = info.getComponentName(); archivedActivity.iconBitmap = info.getActivityInfo().getIconResource() == 0 ? null : bytesFromBitmap( drawableToBitmap(info.getIcon(/* density= */ 0))); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 9f4e86d527cecd7301ecd6aeb86f84f6de2693c8..3ca933a66656d90f6bac58d3e0cff7800037e871 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -1228,6 +1228,9 @@ public class PackageSetting extends SettingBase implements PackageStateInternal long activityInfoToken = proto.start( PackageProto.UserInfoProto.ArchiveState.ACTIVITY_INFOS); proto.write(ArchiveActivityInfo.TITLE, activityInfo.getTitle()); + proto.write( + ArchiveActivityInfo.ORIGINAL_COMPONENT_NAME, + activityInfo.getOriginalComponentName().flattenToString()); if (activityInfo.getIconBitmap() != null) { proto.write(ArchiveActivityInfo.ICON_BITMAP_PATH, activityInfo.getIconBitmap().toAbsolutePath().toString()); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 397a841db6049b6208155448f45f8df6ec538a05..e726d91c30a0e0090dc9946ab30f6a20f6f4f3e6 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -368,6 +368,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile private static final String ATTR_VALUE = "value"; private static final String ATTR_FIRST_INSTALL_TIME = "first-install-time"; private static final String ATTR_ARCHIVE_ACTIVITY_TITLE = "activity-title"; + private static final String ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME = "original-component-name"; private static final String ATTR_ARCHIVE_INSTALLER_TITLE = "installer-title"; private static final String ATTR_ARCHIVE_ICON_PATH = "icon-path"; private static final String ATTR_ARCHIVE_MONOCHROME_ICON_PATH = "monochrome-icon-path"; @@ -2079,6 +2080,8 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (tagName.equals(TAG_ARCHIVE_ACTIVITY_INFO)) { String title = parser.getAttributeValue(null, ATTR_ARCHIVE_ACTIVITY_TITLE); + String originalComponentName = + parser.getAttributeValue(null, ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME); String iconAttribute = parser.getAttributeValue(null, ATTR_ARCHIVE_ICON_PATH); Path iconPath = iconAttribute == null ? null : Path.of(iconAttribute); @@ -2087,17 +2090,27 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile Path monochromeIconPath = monochromeAttribute == null ? null : Path.of( monochromeAttribute); - if (title == null || iconPath == null) { - Slog.wtf(TAG, - TextUtils.formatSimple("Missing attributes in tag %s. %s: %s, %s: %s", - TAG_ARCHIVE_ACTIVITY_INFO, ATTR_ARCHIVE_ACTIVITY_TITLE, title, + if (title == null || originalComponentName == null || iconPath == null) { + Slog.wtf( + TAG, + TextUtils.formatSimple( + "Missing attributes in tag %s. %s: %s, %s: %s, %s: %s", + TAG_ARCHIVE_ACTIVITY_INFO, + ATTR_ARCHIVE_ACTIVITY_TITLE, + title, + ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME, + originalComponentName, ATTR_ARCHIVE_ICON_PATH, iconPath)); continue; } activityInfos.add( - new ArchiveState.ArchiveActivityInfo(title, iconPath, monochromeIconPath)); + new ArchiveState.ArchiveActivityInfo( + title, + ComponentName.unflattenFromString(originalComponentName), + iconPath, + monochromeIconPath)); } } return activityInfos; @@ -2469,6 +2482,10 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile for (ArchiveState.ArchiveActivityInfo activityInfo : archiveState.getActivityInfos()) { serializer.startTag(null, TAG_ARCHIVE_ACTIVITY_INFO); serializer.attribute(null, ATTR_ARCHIVE_ACTIVITY_TITLE, activityInfo.getTitle()); + serializer.attribute( + null, + ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME, + activityInfo.getOriginalComponentName().flattenToString()); if (activityInfo.getIconBitmap() != null) { serializer.attribute(null, ATTR_ARCHIVE_ICON_PATH, activityInfo.getIconBitmap().toAbsolutePath().toString()); diff --git a/services/core/java/com/android/server/pm/pkg/ArchiveState.java b/services/core/java/com/android/server/pm/pkg/ArchiveState.java index 4916a4a6d72a278ddb57c9b19b1ac21edd21ed79..1e40d44bd4caef3c6b3b8145dd6b61d78a3574bf 100644 --- a/services/core/java/com/android/server/pm/pkg/ArchiveState.java +++ b/services/core/java/com/android/server/pm/pkg/ArchiveState.java @@ -16,9 +16,11 @@ package com.android.server.pm.pkg; +import android.content.ComponentName; import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.util.AnnotationValidations; import com.android.internal.util.DataClass; import java.nio.file.Path; @@ -56,6 +58,10 @@ public class ArchiveState { @NonNull private final String mTitle; + /** The component name of the original activity (pre-archival). */ + @NonNull + private final ComponentName mOriginalComponentName; + /** * The path to the stored icon of the activity in the app's locale. Null if the app does * not define any icon (default icon would be shown on the launcher). @@ -96,11 +102,13 @@ public class ArchiveState { @DataClass.Generated.Member public ArchiveActivityInfo( @NonNull String title, + @NonNull ComponentName originalComponentName, @Nullable Path iconBitmap, @Nullable Path monochromeIconBitmap) { this.mTitle = title; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTitle); + this.mOriginalComponentName = originalComponentName; + AnnotationValidations.validate(NonNull.class, null, mTitle); + AnnotationValidations.validate(NonNull.class, null, mOriginalComponentName); this.mIconBitmap = iconBitmap; this.mMonochromeIconBitmap = monochromeIconBitmap; @@ -115,6 +123,14 @@ public class ArchiveState { return mTitle; } + /** + * The component name of the original activity (pre-archival). + */ + @DataClass.Generated.Member + public @NonNull ComponentName getOriginalComponentName() { + return mOriginalComponentName; + } + /** * The path to the stored icon of the activity in the app's locale. Null if the app does * not define any icon (default icon would be shown on the launcher). @@ -140,6 +156,7 @@ public class ArchiveState { return "ArchiveActivityInfo { " + "title = " + mTitle + ", " + + "originalComponentName = " + mOriginalComponentName + ", " + "iconBitmap = " + mIconBitmap + ", " + "monochromeIconBitmap = " + mMonochromeIconBitmap + " }"; @@ -159,6 +176,7 @@ public class ArchiveState { //noinspection PointlessBooleanExpression return true && java.util.Objects.equals(mTitle, that.mTitle) + && java.util.Objects.equals(mOriginalComponentName, that.mOriginalComponentName) && java.util.Objects.equals(mIconBitmap, that.mIconBitmap) && java.util.Objects.equals(mMonochromeIconBitmap, that.mMonochromeIconBitmap); } @@ -171,6 +189,7 @@ public class ArchiveState { int _hash = 1; _hash = 31 * _hash + java.util.Objects.hashCode(mTitle); + _hash = 31* _hash + java.util.Objects.hashCode(mOriginalComponentName); _hash = 31 * _hash + java.util.Objects.hashCode(mIconBitmap); _hash = 31 * _hash + java.util.Objects.hashCode(mMonochromeIconBitmap); return _hash; @@ -180,7 +199,8 @@ public class ArchiveState { time = 1693590309015L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/ArchiveState.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mTitle\nprivate final @android.annotation.Nullable java.nio.file.Path mIconBitmap\nprivate final @android.annotation.Nullable java.nio.file.Path mMonochromeIconBitmap\nclass ArchiveActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)") + inputSignatures = + "private final @android.annotation.NonNull java.lang.String mTitle\nprivate final @android.annotation.NonNull android.content.ComponentName mOriginalComponentName\nprivate final @android.annotation.Nullable java.nio.file.Path mIconBitmap\nprivate final @android.annotation.Nullable java.nio.file.Path mMonochromeIconBitmap\nclass ArchiveActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)") @Deprecated private void __metadata() {} @@ -224,11 +244,9 @@ public class ArchiveState { @NonNull List<ArchiveActivityInfo> activityInfos, @NonNull String installerTitle) { this.mActivityInfos = activityInfos; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mActivityInfos); + AnnotationValidations.validate(NonNull.class, null, mActivityInfos); this.mInstallerTitle = installerTitle; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mInstallerTitle); + AnnotationValidations.validate(NonNull.class, null, mInstallerTitle); // onConstructed(); // You can define this method to get a callback } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java index 952cfc48b5835b8be7e6ef9fd8cf0ee1a3c08114..cbedcaf97358379793f6214f4e398859db32ed2c 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -43,6 +43,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.PropertyInvalidatedCache; +import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.SuspendDialogInfo; @@ -873,12 +874,20 @@ public class PackageManagerSettingsTests { .setUid(packageSetting.getAppId()) .hideAsFinal()); - ArchiveState archiveState = new ArchiveState( - List.of(new ArchiveState.ArchiveActivityInfo("title1", Path.of("/path1"), - Path.of("/monochromePath1")), - new ArchiveState.ArchiveActivityInfo("title2", Path.of("/path2"), - Path.of("/monochromePath2"))), - "installerTitle"); + ArchiveState archiveState = + new ArchiveState( + List.of( + new ArchiveState.ArchiveActivityInfo( + "title1", + new ComponentName("pkg1", "class1"), + Path.of("/path1"), + Path.of("/monochromePath1")), + new ArchiveState.ArchiveActivityInfo( + "title2", + new ComponentName("pkg2", "class2"), + Path.of("/path2"), + Path.of("/monochromePath2"))), + "installerTitle"); packageSetting.modifyUserState(UserHandle.SYSTEM.getIdentifier()).setArchiveState( archiveState); settings.mPackages.put(PACKAGE_NAME_1, packageSetting); diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java index 58ae7406580e6f4c10e861ca0ccafe73a5bbf8d5..87a297b0e86fc6940a3da0b1a5d4cb5162d75fe7 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageUserStateTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.SuspendDialogInfo; import android.content.pm.overlay.OverlayPaths; @@ -192,8 +193,8 @@ public class PackageUserStateTest { return new SuspendParams(dialogInfo, appExtras, launcherExtras); } - private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey, - String sValue, String dKey, double dValue) { + private static PersistableBundle createPersistableBundle( + String lKey, long lValue, String sKey, String sValue, String dKey, double dValue) { final PersistableBundle result = new PersistableBundle(3); if (lKey != null) { result.putLong("com.unit_test." + lKey, lValue); @@ -320,6 +321,7 @@ public class PackageUserStateTest { assertEquals(0L, state.getLastPackageUsageTimeInMills()[i]); } } + private static void assertLastPackageUsageSet( PackageStateUnserialized state, int reason, long value) throws Exception { for (int i = state.getLastPackageUsageTimeInMills().length - 1; i >= 0; --i) { @@ -330,6 +332,7 @@ public class PackageUserStateTest { } } } + @Test public void testPackageUseReasons() throws Exception { PackageSetting packageSetting = Mockito.mock(PackageSetting.class); @@ -377,6 +380,7 @@ public class PackageUserStateTest { assertTrue(testState.setOverlayPaths(new OverlayPaths.Builder().build())); assertFalse(testState.setOverlayPaths(null)); } + @Test public void testSharedLibOverlayPaths() { final PackageUserStateImpl testState = new PackageUserStateImpl(); @@ -401,8 +405,12 @@ public class PackageUserStateTest { @Test public void archiveState() { PackageUserStateImpl packageUserState = new PackageUserStateImpl(); - ArchiveState.ArchiveActivityInfo archiveActivityInfo = new ArchiveState.ArchiveActivityInfo( - "appTitle", Path.of("/path1"), Path.of("/path2")); + ArchiveState.ArchiveActivityInfo archiveActivityInfo = + new ArchiveState.ArchiveActivityInfo( + "appTitle", + new ComponentName("pkg", "class"), + Path.of("/path1"), + Path.of("/path2")); ArchiveState archiveState = new ArchiveState(List.of(archiveActivityInfo), "installerTitle"); packageUserState.setArchiveState(archiveState); 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 eb50556821eb1b2ece9cac1b017805b622aaaaaa..610ea903767e5730b43b24e08983d0cb5237ecdb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; @@ -427,6 +428,7 @@ public class PackageArchiverTest { for (LauncherActivityInfo mainActivity : createLauncherActivities()) { ArchiveState.ArchiveActivityInfo activityInfo = new ArchiveState.ArchiveActivityInfo( mainActivity.getLabel().toString(), + mainActivity.getComponentName(), ICON_PATH, null); activityInfos.add(activityInfo); } @@ -437,9 +439,11 @@ public class PackageArchiverTest { ActivityInfo activityInfo = mock(ActivityInfo.class); LauncherActivityInfo activity1 = mock(LauncherActivityInfo.class); when(activity1.getLabel()).thenReturn("activity1"); + when(activity1.getComponentName()).thenReturn(new ComponentName("pkg1", "class1")); when(activity1.getActivityInfo()).thenReturn(activityInfo); LauncherActivityInfo activity2 = mock(LauncherActivityInfo.class); when(activity2.getLabel()).thenReturn("activity2"); + when(activity2.getComponentName()).thenReturn(new ComponentName("pkg2", "class2")); when(activity2.getActivityInfo()).thenReturn(activityInfo); return List.of(activity1, activity2); }