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

Commit 021b57ab authored by Suprabh Shukla's avatar Suprabh Shukla
Browse files

APIs to suspend packages with SUSPEND_APPS permission

Changed the existing hidden api setPackagesSuspendedAsUser to a system
api setPackagesSuspended that can be called by apps with either
MANAGE_USERS or SUSPEND_APPS permission. Additionally, the suspending
app can now specify optional extra information meant to be used by the
suspended apps and the launcher to deal with this state.

The following other APIs are added:
 - isPackageSuspended(): Apps can query whether they are in a suspended
 state
 - @SystemApi getPackageSuspendedAppExtras(String): Apps with permission
 SUSPEND_APPS can get the appExtras passed to PM when suspending the
 app.
 - @SystemApi setPackageSuspendedAppExtras(String, PersistableBundle):
 Apps with permission SUSPEND_APPS can update app extras for a
 suspended package.
 - getPackageSuspendedAppExtras(): Apps can call to get the appExtras
 passed in to PM when they were suspended.

Test: Can be run via:
atest com.android.server.pm.PackageManagerSettingsTests
atest com.android.server.pm.PackageUserStateTest
atest com.android.server.pm.SuspendPackagesTest

Bug: 74336673
Change-Id: I3b9ed2c8478b34ee2e8986f5f5fddb2839d102e3
parent 5d9617c4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -11160,6 +11160,7 @@ package android.content.pm {
    method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
    method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
    method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
    method public android.os.PersistableBundle getSuspendedPackageAppExtras();
    method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
    method public abstract java.lang.String[] getSystemSharedLibraryNames();
    method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -11173,6 +11174,7 @@ package android.content.pm {
    method public abstract boolean hasSystemFeature(java.lang.String, int);
    method public abstract boolean isInstantApp();
    method public abstract boolean isInstantApp(java.lang.String);
    method public boolean isPackageSuspended();
    method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
    method public abstract boolean isSafeMode();
    method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
+5 −0
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ package android {
    field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
    field public static final java.lang.String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
    field public static final java.lang.String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
    field public static final java.lang.String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
    field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
    field public static final java.lang.String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
    field public static final java.lang.String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
@@ -1007,15 +1008,19 @@ package android.content.pm {
    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
    method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
    method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
    method public android.os.PersistableBundle getSuspendedPackageAppExtras(java.lang.String);
    method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
    method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
    method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
    method public boolean isPackageSuspended(java.lang.String);
    method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
    method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
    method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
    method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
    method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
    method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
    method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
    method public void setSuspendedPackageAppExtras(java.lang.String, android.os.PersistableBundle);
    method public abstract void setUpdateAvailable(java.lang.String, boolean);
    method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
    method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
+41 −3
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -2144,15 +2145,41 @@ public class ApplicationPackageManager extends PackageManager {
    }

    @Override
    public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
            int userId) {
    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
            PersistableBundle appExtras, PersistableBundle launcherExtras,
            String dialogMessage) {
        // TODO (b/75332201): Pass in the dialogMessage and use it in the interceptor dialog
        try {
            return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
                    launcherExtras, mContext.getOpPackageName(), mContext.getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    @Override
    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
        try {
            return mPM.setPackagesSuspendedAsUser(packageNames, suspended, userId);
            return mPM.getPackageSuspendedAppExtras(packageName, mContext.getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    @Override
    public PersistableBundle getSuspendedPackageAppExtras() {
        return getSuspendedPackageAppExtras(mContext.getOpPackageName());
    }

    @Override
    public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras) {
        try {
            mPM.setSuspendedPackageAppExtras(packageName, appExtras, mContext.getUserId());
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

    @Override
    public boolean isPackageSuspendedForUser(String packageName, int userId) {
        try {
@@ -2162,6 +2189,17 @@ public class ApplicationPackageManager extends PackageManager {
        }
    }

    /** @hide */
    @Override
    public boolean isPackageSuspended(String packageName) {
        return isPackageSuspendedForUser(packageName, mContext.getUserId());
    }

    @Override
    public boolean isPackageSuspended() {
        return isPackageSuspendedForUser(mContext.getOpPackageName(), mContext.getUserId());
    }

    /** @hide */
    @Override
    public void setApplicationCategoryHint(String packageName, int categoryHint) {
+10 −2
Original line number Diff line number Diff line
@@ -50,8 +50,8 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.IArtManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.content.IntentSender;

/**
@@ -272,9 +272,17 @@ interface IPackageManager {

    void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);

    String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, int userId);
    String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
            in PersistableBundle launcherExtras, in PersistableBundle appExtras,
            String callingPackage, int userId);

    boolean isPackageSuspendedForUser(String packageName, int userId);

    PersistableBundle getPackageSuspendedAppExtras(String pacakgeName, int userId);

    void setSuspendedPackageAppExtras(String packageName, in PersistableBundle appExtras,
            int userId);

    /**
     * Backup/restore support - only the system uid may use these.
     */
+111 −9
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -5504,28 +5505,49 @@ public abstract class PackageManager {
    /**
     * Puts the package in a suspended state, where attempts at starting activities are denied.
     *
     * <p>It doesn't remove the data or the actual package file. The application notifications
     * will be hidden, the application will not show up in recents, will not be able to show
     * toasts or dialogs or ring the device.
     * <p>It doesn't remove the data or the actual package file. The application's notifications
     * will be hidden, any of the it's started activities will be stopped and it will not be able to
     * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a
     * system dialog with the given {@code dialogMessage} will be shown instead.</p>
     *
     * <p>The package must already be installed. If the package is uninstalled while suspended
     * the package will no longer be suspended.
     * the package will no longer be suspended. </p>
     *
     * <p>Optionally, the suspending app can provide extra information in the form of
     * {@link PersistableBundle} objects to be shared with the apps being suspended and the
     * launcher to support customization that they might need to handle the suspended state. </p>
     *
     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
     * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
     *
     * @param packageNames The names of the packages to set the suspended status.
     * @param suspended If set to {@code true} than the packages will be suspended, if set to
     * {@code false} the packages will be unsuspended.
     * @param userId The user id.
     * {@code false}, the packages will be unsuspended.
     * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
     *                  which will be shared with the apps being suspended. Ignored if
     *                  {@code suspended} is false.
     * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
     *                       provide which will be shared with the launcher. Ignored if
     *                       {@code suspended} is false.
     * @param dialogMessage The message to be displayed to the user, when they try to launch a
     *                      suspended app.
     *
     * @return an array of package names for which the suspended status is not set as requested in
     * this method.
     *
     * @hide
     */
    public abstract String[] setPackagesSuspendedAsUser(
            String[] packageNames, boolean suspended, @UserIdInt int userId);
    @SystemApi
    @RequiresPermission(anyOf = {Manifest.permission.SUSPEND_APPS,
            Manifest.permission.MANAGE_USERS})
    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
            String dialogMessage) {
        throw new UnsupportedOperationException("setPackagesSuspended not implemented");
    }

    /**
     * @see #setPackageSuspendedAsUser(String, boolean, int)
     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
     * @param packageName The name of the package to get the suspended status of.
     * @param userId The user id.
     * @return {@code true} if the package is suspended or {@code false} if the package is not
@@ -5534,6 +5556,86 @@ public abstract class PackageManager {
     */
    public abstract boolean isPackageSuspendedForUser(String packageName, int userId);

    /**
     * Query if an app is currently suspended.
     *
     * @return {@code true} if the given package is suspended, {@code false} otherwise
     *
     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
     * @hide
     */
    @SystemApi
    public boolean isPackageSuspended(String packageName) {
        throw new UnsupportedOperationException("isPackageSuspended not implemented");
    }

    /**
     * Apps can query this to know if they have been suspended.
     *
     * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
     *
     * @see #getSuspendedPackageAppExtras()
     */
    public boolean isPackageSuspended() {
        throw new UnsupportedOperationException("isPackageSuspended not implemented");
    }

    /**
     * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
     * package was suspended.
     *
     * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
     * api.</p>
     *
     * @param packageName The package to retrieve extras for.
     * @return The {@code appExtras} for the suspended package.
     *
     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
    }

    /**
     * Set the app extras for a suspended package. This method can be used to update the appExtras
     * for a package that was earlier suspended using
     * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
     * String)}
     * Does nothing if the given package is not already in a suspended state.
     *
     * @param packageName The package for which the appExtras need to be updated
     * @param appExtras The new appExtras for the given package
     *
     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
    public void setSuspendedPackageAppExtras(String packageName,
            @Nullable PersistableBundle appExtras) {
        throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
    }

    /**
     * Returns any extra information supplied as {@code appExtras} to the system when the calling
     * app was suspended.
     *
     * <p> Note: This just returns whatever {@link PersistableBundle} was passed to the system via
     * {@code setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
     * String)} when suspending the package, <em> which might be {@code null}. </em></p>
     *
     * @return A {@link PersistableBundle} containing the extras for the app, or {@code null} if the
     * package is not currently suspended.
     * @see #isPackageSuspended()
     */
    public @Nullable PersistableBundle getSuspendedPackageAppExtras() {
        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
    }

    /**
     * Provide a hint of what the {@link ApplicationInfo#category} value should
     * be for the given package.
Loading