Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86582358 authored by Jakob Schneider's avatar Jakob Schneider
Browse files

Add API to check if an app is archiveable.

Bug: 307771241
Test: PackageInstallerArchiveTest.java

Change-Id: I94164f379950391a1ec7dd8d80929fe5ddedc0b0
parent befcaa72
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3914,6 +3914,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);
+10 −0
Original line number Diff line number Diff line
@@ -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 {
+2 −0
Original line number Diff line number Diff line
@@ -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);
}
+14 −0
Original line number Diff line number Diff line
@@ -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
+66 −0
Original line number Diff line number Diff line
@@ -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.ArchivedActivity.bytesFromBitmap;
import static android.content.pm.ArchivedActivity.drawableToBitmap;
@@ -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;
@@ -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);
@@ -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,
@@ -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