Loading core/api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -3916,6 +3916,7 @@ package android.content.pm { method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); core/java/android/app/ApplicationPackageManager.java +10 −0 Original line number Diff line number Diff line Loading @@ -2585,6 +2585,16 @@ public class ApplicationPackageManager extends PackageManager { return installSourceInfo; } @Override public boolean isAppArchivable(String packageName) throws NameNotFoundException { try { Objects.requireNonNull(packageName); return mPM.isAppArchivable(packageName, new UserHandle(getUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public int getMoveStatus(int moveId) { try { Loading core/java/android/content/pm/IPackageManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -838,4 +838,6 @@ interface IPackageManager { ArchivedPackageParcel getArchivedPackage(in String packageName, int userId); Bitmap getArchivedAppIcon(String packageName, in UserHandle user); boolean isAppArchivable(String packageName, in UserHandle user); } core/java/android/content/pm/PackageManager.java +14 −0 Original line number Diff line number Diff line Loading @@ -8909,6 +8909,20 @@ public abstract class PackageManager { throw new UnsupportedOperationException("getInstallSourceInfo not implemented"); } /** * Returns true if an app is archivable. * * @throws NameNotFoundException if the given package name is not available to the caller. * @see PackageInstaller#requestArchive(String, IntentSender) * * @hide */ @SystemApi @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING) public boolean isAppArchivable(@NonNull String packageName) throws NameNotFoundException { throw new UnsupportedOperationException("isAppArchivable not implemented"); } /** * Attempts to clear the user data directory of an application. * Since this may take a little while, the result will Loading services/core/java/com/android/server/pm/PackageArchiver.java +66 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap; import static android.content.pm.ArchivedActivityInfo.drawableToBitmap; Loading Loading @@ -106,6 +107,9 @@ public class PackageArchiver { @Nullable private LauncherApps mLauncherApps; @Nullable private AppOpsManager mAppOpsManager; PackageArchiver(Context context, PackageManagerService mPm) { this.mContext = context; this.mPm = mPm; Loading Loading @@ -178,6 +182,8 @@ public class PackageArchiver { Binder.getCallingUid(), userId); String responsibleInstallerPackage = getResponsibleInstallerPackage(ps); verifyInstaller(responsibleInstallerPackage, userId); verifyOptOutStatus(packageName, UserHandle.getUid(userId, UserHandle.getUid(userId, ps.getAppId()))); List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(), userId); Loading Loading @@ -308,6 +314,59 @@ public class PackageArchiver { return intentReceivers != null && !intentReceivers.getList().isEmpty(); } /** * Returns true if the app is archivable. */ // TODO(b/299299569) Exclude system apps public boolean isAppArchivable(@NonNull String packageName, @NonNull UserHandle user) { Objects.requireNonNull(packageName); Objects.requireNonNull(user); Computer snapshot = mPm.snapshotComputer(); int userId = user.getIdentifier(); int binderUid = Binder.getCallingUid(); snapshot.enforceCrossUserPermission(binderUid, userId, true, true, "isAppArchivable"); PackageStateInternal ps; try { ps = getPackageState(packageName, mPm.snapshotComputer(), Binder.getCallingUid(), userId); } catch (PackageManager.NameNotFoundException e) { throw new ParcelableException(e); } if (isAppOptedOutOfArchiving(packageName, ps.getAppId())) { return false; } try { verifyInstaller(getResponsibleInstallerPackage(ps), userId); getLauncherActivityInfos(packageName, userId); } catch (PackageManager.NameNotFoundException e) { return false; } return true; } /** * Returns true if user has opted the app out of archiving through system settings. */ // TODO(b/304256918) Switch this to a separate OP code for archiving. private boolean isAppOptedOutOfArchiving(String packageName, int uid) { return Binder.withCleanCallingIdentity(() -> getAppOpsManager().checkOp(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, packageName) == MODE_IGNORED); } private void verifyOptOutStatus(String packageName, int uid) throws PackageManager.NameNotFoundException { if (isAppOptedOutOfArchiving(packageName, uid)) { throw new PackageManager.NameNotFoundException( TextUtils.formatSimple("The app %s is opted out of archiving.", packageName)); } } void requestUnarchive( @NonNull String packageName, @NonNull String callerPackageName, Loading Loading @@ -510,6 +569,13 @@ public class PackageArchiver { return mLauncherApps; } private AppOpsManager getAppOpsManager() { if (mAppOpsManager == null) { mAppOpsManager = mContext.getSystemService(AppOpsManager.class); } return mAppOpsManager; } private void storeArchiveState(String packageName, ArchiveState archiveState, int userId) throws PackageManager.NameNotFoundException { synchronized (mPm.mLock) { Loading Loading
core/api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -3916,6 +3916,7 @@ package android.content.pm { method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @Deprecated public abstract int installExistingPackage(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @Deprecated public abstract int installExistingPackage(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @FlaggedApi("android.content.pm.archiving") public boolean isAppArchivable(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, int, android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(@NonNull android.content.Intent, @NonNull android.content.pm.PackageManager.ResolveInfoFlags, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle);
core/java/android/app/ApplicationPackageManager.java +10 −0 Original line number Diff line number Diff line Loading @@ -2585,6 +2585,16 @@ public class ApplicationPackageManager extends PackageManager { return installSourceInfo; } @Override public boolean isAppArchivable(String packageName) throws NameNotFoundException { try { Objects.requireNonNull(packageName); return mPM.isAppArchivable(packageName, new UserHandle(getUserId())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override public int getMoveStatus(int moveId) { try { Loading
core/java/android/content/pm/IPackageManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -838,4 +838,6 @@ interface IPackageManager { ArchivedPackageParcel getArchivedPackage(in String packageName, int userId); Bitmap getArchivedAppIcon(String packageName, in UserHandle user); boolean isAppArchivable(String packageName, in UserHandle user); }
core/java/android/content/pm/PackageManager.java +14 −0 Original line number Diff line number Diff line Loading @@ -8909,6 +8909,20 @@ public abstract class PackageManager { throw new UnsupportedOperationException("getInstallSourceInfo not implemented"); } /** * Returns true if an app is archivable. * * @throws NameNotFoundException if the given package name is not available to the caller. * @see PackageInstaller#requestArchive(String, IntentSender) * * @hide */ @SystemApi @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING) public boolean isAppArchivable(@NonNull String packageName) throws NameNotFoundException { throw new UnsupportedOperationException("isAppArchivable not implemented"); } /** * Attempts to clear the user data directory of an application. * Since this may take a little while, the result will Loading
services/core/java/com/android/server/pm/PackageArchiver.java +66 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap; import static android.content.pm.ArchivedActivityInfo.drawableToBitmap; Loading Loading @@ -106,6 +107,9 @@ public class PackageArchiver { @Nullable private LauncherApps mLauncherApps; @Nullable private AppOpsManager mAppOpsManager; PackageArchiver(Context context, PackageManagerService mPm) { this.mContext = context; this.mPm = mPm; Loading Loading @@ -178,6 +182,8 @@ public class PackageArchiver { Binder.getCallingUid(), userId); String responsibleInstallerPackage = getResponsibleInstallerPackage(ps); verifyInstaller(responsibleInstallerPackage, userId); verifyOptOutStatus(packageName, UserHandle.getUid(userId, UserHandle.getUid(userId, ps.getAppId()))); List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps.getPackageName(), userId); Loading Loading @@ -308,6 +314,59 @@ public class PackageArchiver { return intentReceivers != null && !intentReceivers.getList().isEmpty(); } /** * Returns true if the app is archivable. */ // TODO(b/299299569) Exclude system apps public boolean isAppArchivable(@NonNull String packageName, @NonNull UserHandle user) { Objects.requireNonNull(packageName); Objects.requireNonNull(user); Computer snapshot = mPm.snapshotComputer(); int userId = user.getIdentifier(); int binderUid = Binder.getCallingUid(); snapshot.enforceCrossUserPermission(binderUid, userId, true, true, "isAppArchivable"); PackageStateInternal ps; try { ps = getPackageState(packageName, mPm.snapshotComputer(), Binder.getCallingUid(), userId); } catch (PackageManager.NameNotFoundException e) { throw new ParcelableException(e); } if (isAppOptedOutOfArchiving(packageName, ps.getAppId())) { return false; } try { verifyInstaller(getResponsibleInstallerPackage(ps), userId); getLauncherActivityInfos(packageName, userId); } catch (PackageManager.NameNotFoundException e) { return false; } return true; } /** * Returns true if user has opted the app out of archiving through system settings. */ // TODO(b/304256918) Switch this to a separate OP code for archiving. private boolean isAppOptedOutOfArchiving(String packageName, int uid) { return Binder.withCleanCallingIdentity(() -> getAppOpsManager().checkOp(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, packageName) == MODE_IGNORED); } private void verifyOptOutStatus(String packageName, int uid) throws PackageManager.NameNotFoundException { if (isAppOptedOutOfArchiving(packageName, uid)) { throw new PackageManager.NameNotFoundException( TextUtils.formatSimple("The app %s is opted out of archiving.", packageName)); } } void requestUnarchive( @NonNull String packageName, @NonNull String callerPackageName, Loading Loading @@ -510,6 +569,13 @@ public class PackageArchiver { return mLauncherApps; } private AppOpsManager getAppOpsManager() { if (mAppOpsManager == null) { mAppOpsManager = mContext.getSystemService(AppOpsManager.class); } return mAppOpsManager; } private void storeArchiveState(String packageName, ArchiveState archiveState, int userId) throws PackageManager.NameNotFoundException { synchronized (mPm.mLock) { Loading