Loading services/core/java/com/android/server/pm/PackageArchiverService.java +154 −13 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE; import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; Loading @@ -24,6 +25,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.content.Context; Loading @@ -33,24 +35,37 @@ import android.content.pm.IPackageArchiverService; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageArchiver; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.ParcelableException; import android.os.SELinux; import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.pkg.ArchiveState; import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageUserStateInternal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; /** * Responsible archiving apps and returning information about archived apps. Loading @@ -61,6 +76,8 @@ import java.util.Objects; */ public class PackageArchiverService extends IPackageArchiverService.Stub { private static final String TAG = "PackageArchiverService"; /** * The maximum time granted for an app store to start a foreground service when unarchival * is requested. Loading @@ -68,6 +85,8 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { // TODO(b/297358628) Make this configurable through a flag. private static final int DEFAULT_UNARCHIVE_FOREGROUND_TIMEOUT_MS = 120 * 1000; private static final String ARCHIVE_ICONS_DIR = "package_archiver"; private final Context mContext; private final PackageManagerService mPm; Loading Loading @@ -97,25 +116,44 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { snapshot.enforceCrossUserPermission(binderUid, userId, true, true, "archiveApp"); verifyCaller(providedUid, binderUid); ArchiveState archiveState; CompletableFuture<ArchiveState> archiveStateFuture; try { archiveState = createArchiveState(packageName, userId); archiveStateFuture = createArchiveState(packageName, userId); } catch (PackageManager.NameNotFoundException e) { Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s", packageName, e.getMessage())); throw new ParcelableException(e); } archiveStateFuture .thenAccept( archiveState -> { // TODO(b/282952870) Should be reverted if uninstall fails/cancels try { storeArchiveState(packageName, archiveState, userId); } catch (PackageManager.NameNotFoundException e) { throw new ParcelableException(e); sendFailureStatus(intentSender, packageName, e.getMessage()); return; } // 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); new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), callerPackageName, DELETE_KEEP_DATA, intentSender, userId, binderUid); }) .exceptionally( e -> { sendFailureStatus(intentSender, packageName, e.getMessage()); return null; }); } /** * Creates archived state for the package and user. */ public ArchiveState createArchiveState(String packageName, int userId) public CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId) throws PackageManager.NameNotFoundException { PackageStateInternal ps = getPackageState(packageName, mPm.snapshotComputer(), Binder.getCallingUid(), userId); Loading @@ -123,16 +161,56 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { verifyInstaller(responsibleInstallerPackage); List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps, userId); final CompletableFuture<ArchiveState> archiveState = new CompletableFuture<>(); mPm.mHandler.post(() -> { try { archiveState.complete( createArchiveStateInternal(packageName, userId, mainActivities, responsibleInstallerPackage)); } catch (IOException e) { archiveState.completeExceptionally(e); } }); return archiveState; } private ArchiveState createArchiveStateInternal(String packageName, int userId, List<LauncherActivityInfo> mainActivities, String installerPackage) throws IOException { List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(); for (int i = 0; i < mainActivities.size(); i++) { // TODO(b/278553670) Extract and store launcher icons LauncherActivityInfo mainActivity = mainActivities.get(i); Path iconPath = storeIcon(packageName, mainActivity, userId); ArchiveActivityInfo activityInfo = new ArchiveActivityInfo( mainActivities.get(i).getLabel().toString(), Path.of("/TODO"), null); mainActivity.getLabel().toString(), iconPath, null); archiveActivityInfos.add(activityInfo); } return new ArchiveState(archiveActivityInfos, responsibleInstallerPackage); return new ArchiveState(archiveActivityInfos, installerPackage); } // TODO(b/298452477) Handle monochrome icons. @VisibleForTesting Path storeIcon(String packageName, LauncherActivityInfo mainActivity, @UserIdInt int userId) throws IOException { int iconResourceId = mainActivity.getActivityInfo().getIconResource(); if (iconResourceId == 0) { // The app doesn't define an icon. No need to store anything. return null; } File iconsDir = createIconsDir(userId); File iconFile = new File(iconsDir, packageName + "-" + mainActivity.getName() + ".png"); Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0)); try (FileOutputStream out = new FileOutputStream(iconFile)) { // Note: Quality is ignored for PNGs. if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) { throw new IOException(TextUtils.formatSimple("Failure to store icon file %s", iconFile.getName())); } out.flush(); } return iconFile.toPath(); } private void verifyInstaller(String installerPackage) Loading Loading @@ -313,6 +391,29 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { return ps; } private void sendFailureStatus(IntentSender statusReceiver, String packageName, String message) { Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s", packageName, message)); final Intent fillIn = new Intent(); fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName); fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, message); try { final BroadcastOptions options = BroadcastOptions.makeBasic(); options.setPendingIntentBackgroundActivityStartMode( MODE_BACKGROUND_ACTIVITY_START_DENIED); statusReceiver.sendIntent(mContext, 0, fillIn, /* onFinished= */ null, /* handler= */ null, /* requiredPermission= */ null, options.toBundle()); } catch (IntentSender.SendIntentException e) { Slog.e( TAG, TextUtils.formatSimple("Failed to send failure status for %s with message %s", packageName, message), e); } } private static void verifyCaller(int providedUid, int binderUid) { if (providedUid != binderUid) { throw new SecurityException( Loading @@ -323,4 +424,44 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { binderUid)); } } private File createIconsDir(@UserIdInt int userId) throws IOException { File iconsDir = getIconsDir(userId); if (!iconsDir.isDirectory()) { iconsDir.delete(); iconsDir.mkdirs(); if (!iconsDir.isDirectory()) { throw new IOException("Unable to create directory " + iconsDir); } } SELinux.restorecon(iconsDir); return iconsDir; } private File getIconsDir(int userId) { return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR); } private static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap; if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { // Needed for drawables that are just a single color. bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); } else { bitmap = Bitmap.createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } } services/core/java/com/android/server/pm/PackageInstallerService.java +12 −2 Original line number Diff line number Diff line Loading @@ -1240,8 +1240,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, IntentSender statusReceiver, int userId) { uninstall( versionedPackage, callerPackageName, flags, statusReceiver, userId, Binder.getCallingUid()); } void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, IntentSender statusReceiver, int userId, int callingUid) { final Computer snapshot = mPm.snapshotComputer(); final int callingUid = Binder.getCallingUid(); snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) { mAppOps.checkPackage(callingUid, callerPackageName); Loading @@ -1257,7 +1267,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, statusReceiver, versionedPackage.getPackageName(), canSilentlyInstallPackage, userId); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) if (mContext.checkCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) { // Sweet, call straight through! mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags); Loading services/core/java/com/android/server/pm/PackageSetting.java +10 −12 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.content.pm.overlay.OverlayPaths; import android.os.UserHandle; import android.os.incremental.IncrementalManager; import android.service.pm.PackageProto; import android.service.pm.PackageProto.UserInfoProto.ArchiveState.ArchiveActivityInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; Loading Loading @@ -1161,18 +1162,15 @@ public class PackageSetting extends SettingBase implements PackageStateInternal for (ArchiveState.ArchiveActivityInfo activityInfo : archiveState.getActivityInfos()) { long activityInfoToken = proto.start( PackageProto.UserInfoProto.ArchiveState.ACTIVITY_INFOS); proto.write(PackageProto.UserInfoProto.ArchiveState.ArchiveActivityInfo.TITLE, activityInfo.getTitle()); proto.write( PackageProto.UserInfoProto.ArchiveState.ArchiveActivityInfo.ICON_BITMAP_PATH, proto.write(ArchiveActivityInfo.TITLE, activityInfo.getTitle()); if (activityInfo.getIconBitmap() != null) { proto.write(ArchiveActivityInfo.ICON_BITMAP_PATH, activityInfo.getIconBitmap().toAbsolutePath().toString()); proto.write( PackageProto .UserInfoProto .ArchiveState .ArchiveActivityInfo .MONOCHROME_ICON_BITMAP_PATH, } if (activityInfo.getMonochromeIconBitmap() != null) { proto.write(ArchiveActivityInfo.MONOCHROME_ICON_BITMAP_PATH, activityInfo.getMonochromeIconBitmap().toAbsolutePath().toString()); } proto.end(activityInfoToken); } Loading services/core/java/com/android/server/pm/Settings.java +7 −4 Original line number Diff line number Diff line Loading @@ -2062,8 +2062,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (tagName.equals(TAG_ARCHIVE_ACTIVITY_INFO)) { String title = parser.getAttributeValue(null, ATTR_ARCHIVE_ACTIVITY_TITLE); Path iconPath = Path.of(parser.getAttributeValue(null, ATTR_ARCHIVE_ICON_PATH)); String iconAttribute = parser.getAttributeValue(null, ATTR_ARCHIVE_ICON_PATH); Path iconPath = iconAttribute == null ? null : Path.of(iconAttribute); String monochromeAttribute = parser.getAttributeValue(null, ATTR_ARCHIVE_MONOCHROME_ICON_PATH); Path monochromeIconPath = monochromeAttribute == null ? null : Path.of( Loading Loading @@ -2447,8 +2448,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()); if (activityInfo.getIconBitmap() != null) { serializer.attribute(null, ATTR_ARCHIVE_ICON_PATH, activityInfo.getIconBitmap().toAbsolutePath().toString()); } if (activityInfo.getMonochromeIconBitmap() != null) { serializer.attribute(null, ATTR_ARCHIVE_MONOCHROME_ICON_PATH, activityInfo.getMonochromeIconBitmap().toAbsolutePath().toString()); Loading services/core/java/com/android/server/pm/pkg/ArchiveState.java +14 −11 Original line number Diff line number Diff line Loading @@ -56,8 +56,11 @@ public class ArchiveState { @NonNull private final String mTitle; /** The path to the stored icon of the activity in the app's locale. */ @NonNull /** * 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). */ @Nullable private final Path mIconBitmap; /** See {@link #mIconBitmap}. Only set if the app defined a monochrome icon. */ Loading Loading @@ -85,21 +88,20 @@ public class ArchiveState { * @param title * Corresponds to the activity's android:label in the app's locale. * @param iconBitmap * The path to the stored icon of the activity in the app's locale. * 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). * @param monochromeIconBitmap * See {@link #mIconBitmap}. Only set if the app defined a monochrome icon. */ @DataClass.Generated.Member public ArchiveActivityInfo( @NonNull String title, @NonNull Path iconBitmap, @Nullable Path iconBitmap, @Nullable Path monochromeIconBitmap) { this.mTitle = title; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mTitle); this.mIconBitmap = iconBitmap; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mIconBitmap); this.mMonochromeIconBitmap = monochromeIconBitmap; // onConstructed(); // You can define this method to get a callback Loading @@ -114,10 +116,11 @@ public class ArchiveState { } /** * The path to the stored icon of the activity in the app's locale. * 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). */ @DataClass.Generated.Member public @NonNull Path getIconBitmap() { public @Nullable Path getIconBitmap() { return mIconBitmap; } Loading Loading @@ -174,10 +177,10 @@ public class ArchiveState { } @DataClass.Generated( time = 1689169065133L, 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.NonNull 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.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() {} Loading Loading @@ -292,7 +295,7 @@ public class ArchiveState { } @DataClass.Generated( time = 1689169065144L, time = 1693590309027L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/ArchiveState.java", inputSignatures = "private final @android.annotation.NonNull java.util.List<com.android.server.pm.pkg.ArchiveActivityInfo> mActivityInfos\nprivate final @android.annotation.NonNull java.lang.String mInstallerTitle\nclass ArchiveState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)") Loading Loading
services/core/java/com/android/server/pm/PackageArchiverService.java +154 −13 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE; import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; Loading @@ -24,6 +25,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.content.Context; Loading @@ -33,24 +35,37 @@ import android.content.pm.IPackageArchiverService; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageArchiver; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.ParcelableException; import android.os.SELinux; import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.pkg.ArchiveState; import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageUserStateInternal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; /** * Responsible archiving apps and returning information about archived apps. Loading @@ -61,6 +76,8 @@ import java.util.Objects; */ public class PackageArchiverService extends IPackageArchiverService.Stub { private static final String TAG = "PackageArchiverService"; /** * The maximum time granted for an app store to start a foreground service when unarchival * is requested. Loading @@ -68,6 +85,8 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { // TODO(b/297358628) Make this configurable through a flag. private static final int DEFAULT_UNARCHIVE_FOREGROUND_TIMEOUT_MS = 120 * 1000; private static final String ARCHIVE_ICONS_DIR = "package_archiver"; private final Context mContext; private final PackageManagerService mPm; Loading Loading @@ -97,25 +116,44 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { snapshot.enforceCrossUserPermission(binderUid, userId, true, true, "archiveApp"); verifyCaller(providedUid, binderUid); ArchiveState archiveState; CompletableFuture<ArchiveState> archiveStateFuture; try { archiveState = createArchiveState(packageName, userId); archiveStateFuture = createArchiveState(packageName, userId); } catch (PackageManager.NameNotFoundException e) { Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s", packageName, e.getMessage())); throw new ParcelableException(e); } archiveStateFuture .thenAccept( archiveState -> { // TODO(b/282952870) Should be reverted if uninstall fails/cancels try { storeArchiveState(packageName, archiveState, userId); } catch (PackageManager.NameNotFoundException e) { throw new ParcelableException(e); sendFailureStatus(intentSender, packageName, e.getMessage()); return; } // 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); new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), callerPackageName, DELETE_KEEP_DATA, intentSender, userId, binderUid); }) .exceptionally( e -> { sendFailureStatus(intentSender, packageName, e.getMessage()); return null; }); } /** * Creates archived state for the package and user. */ public ArchiveState createArchiveState(String packageName, int userId) public CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId) throws PackageManager.NameNotFoundException { PackageStateInternal ps = getPackageState(packageName, mPm.snapshotComputer(), Binder.getCallingUid(), userId); Loading @@ -123,16 +161,56 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { verifyInstaller(responsibleInstallerPackage); List<LauncherActivityInfo> mainActivities = getLauncherActivityInfos(ps, userId); final CompletableFuture<ArchiveState> archiveState = new CompletableFuture<>(); mPm.mHandler.post(() -> { try { archiveState.complete( createArchiveStateInternal(packageName, userId, mainActivities, responsibleInstallerPackage)); } catch (IOException e) { archiveState.completeExceptionally(e); } }); return archiveState; } private ArchiveState createArchiveStateInternal(String packageName, int userId, List<LauncherActivityInfo> mainActivities, String installerPackage) throws IOException { List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(); for (int i = 0; i < mainActivities.size(); i++) { // TODO(b/278553670) Extract and store launcher icons LauncherActivityInfo mainActivity = mainActivities.get(i); Path iconPath = storeIcon(packageName, mainActivity, userId); ArchiveActivityInfo activityInfo = new ArchiveActivityInfo( mainActivities.get(i).getLabel().toString(), Path.of("/TODO"), null); mainActivity.getLabel().toString(), iconPath, null); archiveActivityInfos.add(activityInfo); } return new ArchiveState(archiveActivityInfos, responsibleInstallerPackage); return new ArchiveState(archiveActivityInfos, installerPackage); } // TODO(b/298452477) Handle monochrome icons. @VisibleForTesting Path storeIcon(String packageName, LauncherActivityInfo mainActivity, @UserIdInt int userId) throws IOException { int iconResourceId = mainActivity.getActivityInfo().getIconResource(); if (iconResourceId == 0) { // The app doesn't define an icon. No need to store anything. return null; } File iconsDir = createIconsDir(userId); File iconFile = new File(iconsDir, packageName + "-" + mainActivity.getName() + ".png"); Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0)); try (FileOutputStream out = new FileOutputStream(iconFile)) { // Note: Quality is ignored for PNGs. if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) { throw new IOException(TextUtils.formatSimple("Failure to store icon file %s", iconFile.getName())); } out.flush(); } return iconFile.toPath(); } private void verifyInstaller(String installerPackage) Loading Loading @@ -313,6 +391,29 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { return ps; } private void sendFailureStatus(IntentSender statusReceiver, String packageName, String message) { Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s", packageName, message)); final Intent fillIn = new Intent(); fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName); fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, message); try { final BroadcastOptions options = BroadcastOptions.makeBasic(); options.setPendingIntentBackgroundActivityStartMode( MODE_BACKGROUND_ACTIVITY_START_DENIED); statusReceiver.sendIntent(mContext, 0, fillIn, /* onFinished= */ null, /* handler= */ null, /* requiredPermission= */ null, options.toBundle()); } catch (IntentSender.SendIntentException e) { Slog.e( TAG, TextUtils.formatSimple("Failed to send failure status for %s with message %s", packageName, message), e); } } private static void verifyCaller(int providedUid, int binderUid) { if (providedUid != binderUid) { throw new SecurityException( Loading @@ -323,4 +424,44 @@ public class PackageArchiverService extends IPackageArchiverService.Stub { binderUid)); } } private File createIconsDir(@UserIdInt int userId) throws IOException { File iconsDir = getIconsDir(userId); if (!iconsDir.isDirectory()) { iconsDir.delete(); iconsDir.mkdirs(); if (!iconsDir.isDirectory()) { throw new IOException("Unable to create directory " + iconsDir); } } SELinux.restorecon(iconsDir); return iconsDir; } private File getIconsDir(int userId) { return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR); } private static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap; if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { // Needed for drawables that are just a single color. bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); } else { bitmap = Bitmap.createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } }
services/core/java/com/android/server/pm/PackageInstallerService.java +12 −2 Original line number Diff line number Diff line Loading @@ -1240,8 +1240,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, IntentSender statusReceiver, int userId) { uninstall( versionedPackage, callerPackageName, flags, statusReceiver, userId, Binder.getCallingUid()); } void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, IntentSender statusReceiver, int userId, int callingUid) { final Computer snapshot = mPm.snapshotComputer(); final int callingUid = Binder.getCallingUid(); snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) { mAppOps.checkPackage(callingUid, callerPackageName); Loading @@ -1257,7 +1267,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, statusReceiver, versionedPackage.getPackageName(), canSilentlyInstallPackage, userId); if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) if (mContext.checkCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) { // Sweet, call straight through! mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags); Loading
services/core/java/com/android/server/pm/PackageSetting.java +10 −12 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.content.pm.overlay.OverlayPaths; import android.os.UserHandle; import android.os.incremental.IncrementalManager; import android.service.pm.PackageProto; import android.service.pm.PackageProto.UserInfoProto.ArchiveState.ArchiveActivityInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; Loading Loading @@ -1161,18 +1162,15 @@ public class PackageSetting extends SettingBase implements PackageStateInternal for (ArchiveState.ArchiveActivityInfo activityInfo : archiveState.getActivityInfos()) { long activityInfoToken = proto.start( PackageProto.UserInfoProto.ArchiveState.ACTIVITY_INFOS); proto.write(PackageProto.UserInfoProto.ArchiveState.ArchiveActivityInfo.TITLE, activityInfo.getTitle()); proto.write( PackageProto.UserInfoProto.ArchiveState.ArchiveActivityInfo.ICON_BITMAP_PATH, proto.write(ArchiveActivityInfo.TITLE, activityInfo.getTitle()); if (activityInfo.getIconBitmap() != null) { proto.write(ArchiveActivityInfo.ICON_BITMAP_PATH, activityInfo.getIconBitmap().toAbsolutePath().toString()); proto.write( PackageProto .UserInfoProto .ArchiveState .ArchiveActivityInfo .MONOCHROME_ICON_BITMAP_PATH, } if (activityInfo.getMonochromeIconBitmap() != null) { proto.write(ArchiveActivityInfo.MONOCHROME_ICON_BITMAP_PATH, activityInfo.getMonochromeIconBitmap().toAbsolutePath().toString()); } proto.end(activityInfoToken); } Loading
services/core/java/com/android/server/pm/Settings.java +7 −4 Original line number Diff line number Diff line Loading @@ -2062,8 +2062,9 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (tagName.equals(TAG_ARCHIVE_ACTIVITY_INFO)) { String title = parser.getAttributeValue(null, ATTR_ARCHIVE_ACTIVITY_TITLE); Path iconPath = Path.of(parser.getAttributeValue(null, ATTR_ARCHIVE_ICON_PATH)); String iconAttribute = parser.getAttributeValue(null, ATTR_ARCHIVE_ICON_PATH); Path iconPath = iconAttribute == null ? null : Path.of(iconAttribute); String monochromeAttribute = parser.getAttributeValue(null, ATTR_ARCHIVE_MONOCHROME_ICON_PATH); Path monochromeIconPath = monochromeAttribute == null ? null : Path.of( Loading Loading @@ -2447,8 +2448,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()); if (activityInfo.getIconBitmap() != null) { serializer.attribute(null, ATTR_ARCHIVE_ICON_PATH, activityInfo.getIconBitmap().toAbsolutePath().toString()); } if (activityInfo.getMonochromeIconBitmap() != null) { serializer.attribute(null, ATTR_ARCHIVE_MONOCHROME_ICON_PATH, activityInfo.getMonochromeIconBitmap().toAbsolutePath().toString()); Loading
services/core/java/com/android/server/pm/pkg/ArchiveState.java +14 −11 Original line number Diff line number Diff line Loading @@ -56,8 +56,11 @@ public class ArchiveState { @NonNull private final String mTitle; /** The path to the stored icon of the activity in the app's locale. */ @NonNull /** * 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). */ @Nullable private final Path mIconBitmap; /** See {@link #mIconBitmap}. Only set if the app defined a monochrome icon. */ Loading Loading @@ -85,21 +88,20 @@ public class ArchiveState { * @param title * Corresponds to the activity's android:label in the app's locale. * @param iconBitmap * The path to the stored icon of the activity in the app's locale. * 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). * @param monochromeIconBitmap * See {@link #mIconBitmap}. Only set if the app defined a monochrome icon. */ @DataClass.Generated.Member public ArchiveActivityInfo( @NonNull String title, @NonNull Path iconBitmap, @Nullable Path iconBitmap, @Nullable Path monochromeIconBitmap) { this.mTitle = title; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mTitle); this.mIconBitmap = iconBitmap; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mIconBitmap); this.mMonochromeIconBitmap = monochromeIconBitmap; // onConstructed(); // You can define this method to get a callback Loading @@ -114,10 +116,11 @@ public class ArchiveState { } /** * The path to the stored icon of the activity in the app's locale. * 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). */ @DataClass.Generated.Member public @NonNull Path getIconBitmap() { public @Nullable Path getIconBitmap() { return mIconBitmap; } Loading Loading @@ -174,10 +177,10 @@ public class ArchiveState { } @DataClass.Generated( time = 1689169065133L, 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.NonNull 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.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() {} Loading Loading @@ -292,7 +295,7 @@ public class ArchiveState { } @DataClass.Generated( time = 1689169065144L, time = 1693590309027L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/ArchiveState.java", inputSignatures = "private final @android.annotation.NonNull java.util.List<com.android.server.pm.pkg.ArchiveActivityInfo> mActivityInfos\nprivate final @android.annotation.NonNull java.lang.String mInstallerTitle\nclass ArchiveState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true)") Loading