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

Commit a04b9ab4 authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Improve AppOpsManager documentation

... focusing on public APIs.

Test: one docs changes
Bug: 136505050
Change-Id: I90215a638a3c380fcffde2b9209c4386f8fd70ea
parent 47f42fed
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -4354,7 +4354,7 @@ package android.app {
  public class AppOpsManager {
    method @Deprecated public int checkOp(@NonNull String, int, @NonNull String);
    method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String);
    method public void checkPackage(int, @NonNull String);
    method @Deprecated public void checkPackage(int, @NonNull String);
    method @Deprecated public void finishOp(@NonNull String, int, @NonNull String);
    method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String);
    method public boolean isOpActive(@NonNull String, int, @NonNull String);
@@ -4367,7 +4367,7 @@ package android.app {
    method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @NonNull String);
    method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int);
    method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String);
    method public static String permissionToOp(String);
    method @Nullable public static String permissionToOp(@NonNull String);
    method public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector);
    method @Deprecated public int startOp(@NonNull String, int, @NonNull String);
    method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
+2 −2
Original line number Diff line number Diff line
@@ -370,8 +370,8 @@ package android.app {
    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]);
    method public static int opToDefaultMode(@NonNull String);
    method @Nullable public static String opToPermission(@NonNull String);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
    field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
    field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
    field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
+2 −2
Original line number Diff line number Diff line
@@ -178,8 +178,8 @@ package android.app {
    method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetHistoryParameters();
    method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
    method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
    method public static int strOpToOp(@NonNull String);
    field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
    field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
+107 −60
Original line number Diff line number Diff line
@@ -90,15 +90,77 @@ import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * AppOps are mappings of [package/uid, op-name] -> [mode]. The list of existing appops is defined
 * by the system and cannot be amended by apps. Only system apps can change appop-modes.
 *
 * <p>Beside a mode the system tracks when an op was {@link #noteOp noted}. The tracked data can
 * only be read by system components.
 *
 * <p>Installed apps can usually only listen to changes and events on their own ops. E.g.
 * {@link AppOpsCollector} allows to get a callback each time an app called {@link #noteOp} or
 * {@link #startOp} for an op belonging to the app.
 * App-ops are used for two purposes: Access control and tracking.
 *
 * <p>App-ops cover a wide variety of functionality from helping with runtime permissions access
 * control and tracking to battery consumption tracking.
 *
 * <h2>Access control</h2>
 *
 * <p>App-ops can either be controlled for each uid or for each package. Which one is used depends
 * on the API provider maintaining this app-op. For any security or privacy related app-op the
 * provider needs to control the app-op for per uid as all security and privacy is based on uid in
 * Android.
 *
 * <p>To control access the app-op can be set to a mode to:
 * <dl>
 *     <dt>{@link #MODE_DEFAULT}
 *     <dd>Default behavior, might differ from app-op or app-op
 *     <dt>{@link #MODE_ALLOWED}
 *     <dd>Allow the access
 *     <dt>{@link #MODE_IGNORED}
 *     <dd>Don't allow the access, i.e. don't perform the requested action or return no or dummy
 *     data
 *     <dt>{@link #MODE_ERRORED}
 *     <dd>Throw a {@link SecurityException} on access. This can be suppressed by using a
 *     {@code ...noThrow} method to check the mode
 * </dl>
 *
 * <p>API providers need to check the mode returned by {@link #noteOp} if they are are allowing
 * access to operations gated by the app-op. {@link #unsafeCheckOp} should be used to check the
 * mode if no access is granted. E.g. this can be used for displaying app-op state in the UI or
 * when checking the state before later calling {@link #noteOp} anyway.
 *
 * <p>If an operation refers to a time span (e.g. a audio-recording session) the API provider
 * should use {@link #startOp} and {@link #finishOp} instead of {@link #noteOp}.
 *
 * <h3>Runtime permissions and app-ops</h3>
 *
 * <p>Each platform defined runtime permission (beside background modifiers) has an associated app
 * op which is used for tracking but also to allow for silent failures. I.e. if the runtime
 * permission is denied the caller gets a {@link SecurityException}, but if the permission is
 * granted and the app-op is {@link #MODE_IGNORED} then the callers gets dummy behavior, e.g.
 * location callbacks would not happen.
 *
 * <h3>App-op permissions</h3>
 *
 * <p>App-ops permissions are platform defined permissions that can be overridden. The security
 * check for app-op permissions should by {@link #MODE_DEFAULT default} check the permission grant
 * state. If the app-op state is set to {@link #MODE_ALLOWED} or {@link #MODE_IGNORED} the app-op
 * state should be checked instead of the permission grant state.
 *
 * <p>This functionality allows to grant access by default to apps fulfilling the requirements for
 * a certain permission level. Still the behavior can be overridden when needed.
 *
 * <h2>Tracking</h2>
 *
 * <p>App-ops track many important events, including all accesses to runtime permission protected
 * APIs. This is done by tracking when an app-op was {@link #noteOp noted} or
 * {@link #startOp started}. The tracked data can only be read by system components.
 *
 * <p><b>Only {@link #noteOp}/{@link #startOp} are tracked; {@link #unsafeCheckOp} is not tracked.
 * Hence it is important to eventually call {@link #noteOp} or {@link #startOp} when providing
 * access to protected operations or data.</b>
 *
 * <p>Some apps are forwarding access to other apps. E.g. an app might get the location from the
 * system's location provider and then send the location further to a 3rd app. In this case the
 * app passing on the data needs to call {@link #noteProxyOp} to signal the access proxying. This
 * might also make sense inside of a single app if the access is forwarded between two features of
 * the app.
 *
 * <p>An app can register an {@link AppOpsCollector} to get informed about what accesses the
 * system is tracking for it. As each runtime permission has an associated app-op this API is
 * particularly useful for an app that want to find unexpected private data accesses.
 */
@SystemService(Context.APP_OPS_SERVICE)
public class AppOpsManager {
@@ -114,28 +176,6 @@ public class AppOpsManager {
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    public static final long CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE = 148180766L;

    /**
     * <p>App ops allows callers to:</p>
     *
     * <ul>
     * <li> Note when operations are happening, and find out if they are allowed for the current
     * caller.</li>
     * <li> Disallow specific apps from doing specific operations.</li>
     * <li> Collect all of the current information about operations that have been executed or
     * are not being allowed.</li>
     * <li> Monitor for changes in whether an operation is allowed.</li>
     * </ul>
     *
     * <p>Each operation is identified by a single integer; these integers are a fixed set of
     * operations, enumerated by the OP_* constants.
     *
     * <p></p>When checking operations, the result is a "mode" integer indicating the current
     * setting for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute
     * the operation but fake its behavior enough so that the caller doesn't crash),
     * MODE_ERRORED (throw a SecurityException back to the caller; the normal operation calls
     * will do this for you).
     */

    final Context mContext;

    @UnsupportedAppUsage
@@ -1024,7 +1064,7 @@ public class AppOpsManager {
    /** Required to draw on top of other apps. */
    public static final String OPSTR_SYSTEM_ALERT_WINDOW
            = "android:system_alert_window";
    /** Required to write/modify/update system settingss. */
    /** Required to write/modify/update system settings. */
    public static final String OPSTR_WRITE_SETTINGS
            = "android:write_settings";
    /** @hide Get device accounts. */
@@ -6042,7 +6082,7 @@ public class AppOpsManager {
     */
    public interface OnOpActiveChangedListener {
        /**
         * Called when the active state of an app op changes.
         * Called when the active state of an app-op changes.
         *
         * @param op The operation that changed.
         * @param packageName The package performing the operation.
@@ -6333,7 +6373,7 @@ public class AppOpsManager {
    @SystemApi
    @TestApi
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setUidMode(String appOp, int uid, @Mode int mode) {
    public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
        try {
            mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
        } catch (RemoteException e) {
@@ -6388,7 +6428,8 @@ public class AppOpsManager {
    @TestApi
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
    public void setMode(String op, int uid, String packageName, @Mode int mode) {
    public void setMode(@NonNull String op, int uid, @Nullable String packageName,
            @Mode int mode) {
        try {
            mService.setMode(strOpToOp(op), uid, packageName, mode);
        } catch (RemoteException e) {
@@ -6431,16 +6472,17 @@ public class AppOpsManager {
    }

    /**
     * Gets the app op name associated with a given permission.
     * The app op name is one of the public constants defined
     * Gets the app-op name associated with a given permission.
     *
     * <p>The app-op name is one of the public constants defined
     * in this class such as {@link #OPSTR_COARSE_LOCATION}.
     * This API is intended to be used for mapping runtime
     * permissions to the corresponding app op.
     * permissions to the corresponding app-op.
     *
     * @param permission The permission.
     * @return The app op associated with the permission or null.
     * @return The app-op associated with the permission or {@code null}.
     */
    public static String permissionToOp(String permission) {
    public static @Nullable String permissionToOp(@NonNull String permission) {
        final Integer opCode = sPermToOp.get(permission);
        if (opCode == null) {
            return null;
@@ -6558,7 +6600,7 @@ public class AppOpsManager {
    }

    /**
     * Start watching for changes to the active state of app ops. An app op may be
     * Start watching for changes to the active state of app-ops. An app-op may be
     * long running and it has a clear start and stop delimiters. If an op is being
     * started or stopped by any package you will get a callback. To change the
     * watched ops for a registered callback you need to unregister and register it
@@ -6617,7 +6659,7 @@ public class AppOpsManager {
    }

    /**
     * Stop watching for changes to the active state of an app op. An app op may be
     * Stop watching for changes to the active state of an app-op. An app-op may be
     * long running and it has a clear start and stop delimiters. Unregistering a
     * non-registered callback has no effect.
     *
@@ -6848,7 +6890,8 @@ public class AppOpsManager {
     * @param op The operation to note.  One of the OPSTR_* constants.
     * @param uid The user id of the application attempting to perform the operation.
     * @param packageName The name of the application attempting to perform the operation.
     * @param featureId The feature in the app or {@code null} for default feature
     * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
     * null} for default feature
     * @param message A message describing the reason the op was noted
     *
     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -6923,6 +6966,8 @@ public class AppOpsManager {
     * @param op The operation to note.  One of the OPSTR_* constants.
     * @param uid The user id of the application attempting to perform the operation.
     * @param packageName The name of the application attempting to perform the operation.
     * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
     * null} for default feature
     * @param message A message describing the reason the op was noted
     *
     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -6930,8 +6975,8 @@ public class AppOpsManager {
     * causing the app to crash).
     */
    public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
            @Nullable String feature, @Nullable String message) {
        return noteOpNoThrow(strOpToOp(op), uid, packageName, feature, message);
            @Nullable String featureId, @Nullable String message) {
        return noteOpNoThrow(strOpToOp(op), uid, packageName, featureId, message);
    }

    /**
@@ -7200,11 +7245,9 @@ public class AppOpsManager {
    }

    /**
     * Do a quick check to validate if a package name belongs to a UID.
     *
     * @throws SecurityException if the package name doesn't belong to the given
     *             UID, or if ownership cannot be verified.
     * @deprecated Use {@link PackageManager#getPackageUid} instead
     */
    @Deprecated
    public void checkPackage(int uid, @NonNull String packageName) {
        try {
            if (mService.checkPackage(uid, packageName) != MODE_ALLOWED) {
@@ -7312,7 +7355,8 @@ public class AppOpsManager {
     * @param op The operation to start.  One of the OPSTR_* constants.
     * @param uid The user id of the application attempting to perform the operation.
     * @param packageName The name of the application attempting to perform the operation.
     * @param featureId The feature in the app or {@code null} for default feature
     * @param featureId The {@link Context#createFeatureContext feature} in the package or {@code
     * null} for default feature
     * @param message Description why op was started
     *
     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -7503,7 +7547,8 @@ public class AppOpsManager {
    }

    /**
     * Checks whether the given op for a package is active.
     * Checks whether the given op for a package is active, i.e. did someone call {@link #startOp}
     * without {@link #finishOp} yet.
     * <p>
     * If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
     * permission you can query only for your UID.
@@ -7828,18 +7873,19 @@ public class AppOpsManager {
    }

    /**
     * Callback an app can choose to {@link #setNotedAppOpsCollector register} to monitor it's noted
     * appops. I.e. each time any app calls {@link #noteOp} or {@link #startOp} one of the callback
     * methods of this object is called.
     * Callback an app can {@link #setNotedAppOpsCollector register} to monitor the app-ops the
     * system has tracked for it. I.e. each time any app calls {@link #noteOp} or {@link #startOp}
     * one of the callback methods of this object is called.
     *
     * <p><b>Only appops related to dangerous permissions are collected.</b>
     * <p><b>There will be a callback for all app-ops related to runtime permissions, but not
     * necessarily for all other app-ops.
     *
     * <pre>
     * setNotedAppOpsCollector(new AppOpsCollector() {
     *     ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>();
     *
     *     private synchronized void addAccess(String op, String accessLocation) {
     *         // Ops are often noted when permission protected APIs were called.
     *         // Ops are often noted when runtime permission protected APIs were called.
     *         // In this case permissionToOp() allows to resolve the permission<->op
     *         opsNotedForThisProcess.add(new Pair(accessType, accessLocation));
     *     }
@@ -7881,20 +7927,21 @@ public class AppOpsManager {
        }

        /**
         * Called when an app-op was noted for this package inside of a two-way binder-call.
         * Called when an app-op was {@link #noteOp noted} for this package inside of a synchronous
         * API call, i.e. a API call that returned data or waited until the action was performed.
         *
         * <p>Called on the calling thread just after executing the binder-call. This allows
         * the app to e.g. collect stack traces to figure out where the access came from.
         * <p>Called on the calling thread before the API returns. This allows the app to e.g.
         * collect stack traces to figure out where the access came from.
         *
         * @param op The op noted
         */
        public abstract void onNoted(@NonNull SyncNotedAppOp op);

        /**
         * Called when this app noted an app-op for its own package.
         * Called when this app noted an app-op for its own package,
         *
         * <p>Called on the thread the noted the op. This allows the app to e.g. collect stack
         * traces to figure out where the access came from.
         * <p>This is very similar to {@link #onNoted} only that the tracking was not caused by the
         * API provider in a separate process, but by one in the app's own process.
         *
         * @param op The op noted
         */