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

Commit 2d3bdff3 authored by Jakob Schneider's avatar Jakob Schneider Committed by Android (Google) Code Review
Browse files

Merge "Add API to check if an app is archiveable." into main

parents 16952a94 86582358
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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);
+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.ArchivedActivityInfo.bytesFromBitmap;
import static android.content.pm.ArchivedActivityInfo.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