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

Commit 2eebf929 authored by Svet Ganov's avatar Svet Ganov
Browse files

Switch media fw permissions checks to AttributionSource

Attribution source is the abstraction to capture the data
flows for private data across apps. Checking permissions
for an attribution source does this for all apps in the
chain that would receive the data as well as the relevant
app ops are checked/noted/started as needed.

Teach speech recognition service about attribution
chains. If an implementation does nothing the OS
would enforce permisisons and do blame as always.
This apporach leads to double blaming and doesn't
support attribition chains where app calls into
the default recognizer which calls into the on
device recognizer (this nests recursively). If the
implementer takes advantage of the attribution chain
mechanims the permissions for the entire chain are
checked at mic access time and all apps are blamed
only once.

Fixed a few bugs around finishing ops for attribution
chains. Also ensured that any app death in a started
attribution chain would lead to finishing the op for
this app

bug: 158792096

Test: (added tests for speech reco)
      atest CtsMediaTestCases
      atest CtsPermissionTestCases
      atest CtsPermission2TestCases
      atest CtsPermission3TestCases
      atest CtsPermission4TestCases
      atest CtsPermission5TestCases
      atest CtsAppOpsTestCases
      atest CtsAppOps2TestCases

Merged-In: Ic92c7adc14bd2d135ac13b96f17a1b393dd562e4

Change-Id: Ic92c7adc14bd2d135ac13b96f17a1b393dd562e4
parent a6377c98
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -201,6 +201,11 @@ package android.app {
    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 public static int strOpToOp(@NonNull String);
    field public static final int ATTRIBUTION_CHAIN_ID_NONE = -1; // 0xffffffff
    field public static final int ATTRIBUTION_FLAGS_NONE = 0; // 0x0
    field public static final int ATTRIBUTION_FLAG_ACCESSOR = 1; // 0x1
    field public static final int ATTRIBUTION_FLAG_INTERMEDIARY = 2; // 0x2
    field public static final int ATTRIBUTION_FLAG_RECEIVER = 4; // 0x4
    field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
    field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
    field public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2; // 0x2
@@ -230,6 +235,10 @@ package android.app {
    method public void offsetBeginAndEndTime(long);
  }

  public static interface AppOpsManager.OnOpActiveChangedListener {
    method public default void onOpActiveChanged(@NonNull String, int, @NonNull String, @Nullable String, boolean, int, int);
  }

  public class BroadcastOptions {
    ctor public BroadcastOptions(@NonNull android.os.Bundle);
    method public int getMaxManifestReceiverApiLevel();
@@ -669,7 +678,7 @@ package android.content {

  public final class AttributionSource implements android.os.Parcelable {
    ctor public AttributionSource(int, @Nullable String, @Nullable String);
    ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable android.content.AttributionSource);
    ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
    ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
  }

@@ -2041,7 +2050,7 @@ package android.permission {
  public final class PermissionManager {
    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
    method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
    method public void registerAttributionSource(@NonNull android.content.AttributionSource);
  }

}
+156 −21
Original line number Diff line number Diff line
@@ -708,6 +708,62 @@ public class AppOpsManager {
        }
    }

    /**
     * Attribution chain flag: specifies that this is the accessor. When
     * an app A accesses the data that is then passed to app B that is then
     * passed to C, we call app A accessor, app B intermediary, and app C
     * receiver. If A accesses the data for itself, then it is the accessor
     * and the receiver.
     * @hide
     */
    @TestApi
    public static final int ATTRIBUTION_FLAG_ACCESSOR = 0x1;

    /**
     * Attribution chain flag: specifies that this is the intermediary. When
     * an app A accesses the data that is then passed to app B that is then
     * passed to C, we call app A accessor, app B intermediary, and app C
     * receiver. If A accesses the data for itself, then it is the accessor
     * and the receiver.
     * @hide
     */
    @TestApi
    public static final int ATTRIBUTION_FLAG_INTERMEDIARY = 0x2;

    /**
     * Attribution chain flag: specifies that this is the receiver. When
     * an app A accesses the data that is then passed to app B that is then
     * passed to C, we call app A accessor, app B intermediary, and app C
     * receiver. If A accesses the data for itself, then it is the accessor
     * and the receiver.
     * @hide
     */
    @TestApi
    public static final int ATTRIBUTION_FLAG_RECEIVER = 0x4;

    /**
     * No attribution flags.
     * @hide
     */
    @TestApi
    public static final int ATTRIBUTION_FLAGS_NONE = 0x0;

    /**
     * No attribution chain id.
     * @hide
     */
    @TestApi
    public static final int ATTRIBUTION_CHAIN_ID_NONE = -1;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
            ATTRIBUTION_FLAG_ACCESSOR,
            ATTRIBUTION_FLAG_INTERMEDIARY,
            ATTRIBUTION_FLAG_RECEIVER
    })
    public @interface AttributionFlags {}

    // These constants are redefined here to work around a metalava limitation/bug where
    // @IntDef is not able to see @hide symbols when they are hidden via package hiding:
    // frameworks/base/core/java/com/android/internal/package.html
@@ -7073,6 +7129,25 @@ public class AppOpsManager {
         */
        void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
                boolean active);

        /**
         * Called when the active state of an app-op changes.
         *
         * @param op The operation that changed.
         * @param uid The UID performing the operation.
         * @param packageName The package performing the operation.
         * @param attributionTag The operation's attribution tag.
         * @param active Whether the operation became active or inactive.
         * @param attributionFlags the attribution flags for this operation.
         * @param attributionChainId the unique id of the attribution chain this op is a part of.
         * @hide
         */
        @TestApi
        default void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
                @Nullable String attributionTag, boolean active, @AttributionFlags
                int attributionFlags, int attributionChainId) {
            onOpActiveChanged(op, uid, packageName, active);
        }
    }

    /**
@@ -7694,14 +7769,17 @@ public class AppOpsManager {
            }
            cb = new IAppOpsActiveCallback.Stub() {
                @Override
                public void opActiveChanged(int op, int uid, String packageName, boolean active) {
                public void opActiveChanged(int op, int uid, String packageName,
                        String attributionTag, boolean active, @AttributionFlags
                        int attributionFlags, int attributionChainId) {
                    executor.execute(() -> {
                        if (callback instanceof OnOpActiveChangedInternalListener) {
                            ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
                                    uid, packageName, active);
                        }
                        if (sOpToString[op] != null) {
                            callback.onOpActiveChanged(sOpToString[op], uid, packageName, active);
                            callback.onOpActiveChanged(sOpToString[op], uid, packageName,
                                    attributionTag, active, attributionFlags, attributionChainId);
                        }
                    });
                }
@@ -8179,8 +8257,9 @@ public class AppOpsManager {
    public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
            @Nullable String proxiedAttributionTag, @Nullable String message) {
        return noteProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
                new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)),
                message, /*skipProxyOperation*/ false);
                new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag,
                        mContext.getAttributionSource().getToken())), message,
                        /*skipProxyOperation*/ false);
    }

    /**
@@ -8265,8 +8344,8 @@ public class AppOpsManager {
            int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
        return noteProxyOpNoThrow(strOpToOp(op), new AttributionSource(
                mContext.getAttributionSource(), new AttributionSource(proxiedUid,
                        proxiedPackageName, proxiedAttributionTag)), message,
                        /*skipProxyOperation*/ false);
                        proxiedPackageName, proxiedAttributionTag, mContext.getAttributionSource()
                        .getToken())), message,/*skipProxyOperation*/ false);
    }

    /**
@@ -8602,6 +8681,29 @@ public class AppOpsManager {
     */
    public int startOpNoThrow(int op, int uid, @NonNull String packageName,
            boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message) {
        return startOpNoThrow(mContext.getAttributionSource().getToken(), op, uid, packageName,
                startIfModeDefault, attributionTag, message);
    }

    /**
     * @see #startOpNoThrow(String, int, String, String, String)
     *
     * @hide
     */
    public int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
            boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message) {
        return startOpNoThrow(token, op, uid, packageName, startIfModeDefault, attributionTag,
                message, ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_CHAIN_ID_NONE);
    }

    /**
     * @see #startOpNoThrow(String, int, String, String, String)
     *
     * @hide
     */
    public int startOpNoThrow(@NonNull IBinder token, int op, int uid, @NonNull String packageName,
            boolean startIfModeDefault, @Nullable String attributionTag, @Nullable String message,
            @AttributionFlags int attributionFlags, int attributionChainId) {
        try {
            collectNoteOpCallsForValidation(op);
            int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
@@ -8614,9 +8716,9 @@ public class AppOpsManager {
                }
            }

            SyncNotedAppOp syncOp = mService.startOperation(getClientId(), op, uid, packageName,
            SyncNotedAppOp syncOp = mService.startOperation(token, op, uid, packageName,
                    attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
                    shouldCollectMessage);
                    shouldCollectMessage, attributionFlags, attributionChainId);

            if (syncOp.getOpMode() == MODE_ALLOWED) {
                if (collectionMode == COLLECT_SELF) {
@@ -8653,8 +8755,9 @@ public class AppOpsManager {
    public int startProxyOp(@NonNull String op, int proxiedUid, @NonNull String proxiedPackageName,
            @Nullable String proxiedAttributionTag, @Nullable String message) {
        return startProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
                new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)),
                message, /*skipProxyOperation*/ false);
                new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag,
                        mContext.getAttributionSource().getToken())), message,
                        /*skipProxyOperation*/ false);
    }

    /**
@@ -8700,7 +8803,8 @@ public class AppOpsManager {
            @Nullable String message) {
        return startProxyOpNoThrow(AppOpsManager.strOpToOp(op), new AttributionSource(
                mContext.getAttributionSource(), new AttributionSource(proxiedUid,
                        proxiedPackageName, proxiedAttributionTag)), message,
                        proxiedPackageName, proxiedAttributionTag,
                        mContext.getAttributionSource().getToken())), message,
                        /*skipProxyOperation*/ false);
    }

@@ -8715,6 +8819,23 @@ public class AppOpsManager {
     */
    public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
            @Nullable String message, boolean skipProxyOperation) {
        return startProxyOpNoThrow(op, attributionSource, message, skipProxyOperation,
                ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_CHAIN_ID_NONE);
    }

    /**
     * Like {@link #startProxyOp(String, AttributionSource, String)} but instead
     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED} and
     * the checks is for the attribution chain specified by the {@link AttributionSource}.
     *
     * @see #startProxyOp(String, AttributionSource, String)
     *
     * @hide
     */
    public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource,
            @Nullable String message, boolean skipProxyOperation, @AttributionFlags
            int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags,
            int attributionChainId) {
        try {
            collectNoteOpCallsForValidation(op);
            int collectionMode = getNotedOpCollectionMode(
@@ -8729,9 +8850,10 @@ public class AppOpsManager {
                }
            }

            SyncNotedAppOp syncOp = mService.startProxyOperation(getClientId(), op,
            SyncNotedAppOp syncOp = mService.startProxyOperation(op,
                    attributionSource, false, collectionMode == COLLECT_ASYNC, message,
                    shouldCollectMessage, skipProxyOperation);
                    shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
                    proxiedAttributionFlags, attributionChainId);

            if (syncOp.getOpMode() == MODE_ALLOWED) {
                if (collectionMode == COLLECT_SELF) {
@@ -8795,8 +8917,18 @@ public class AppOpsManager {
     */
    public void finishOp(int op, int uid, @NonNull String packageName,
            @Nullable String attributionTag) {
        finishOp(mContext.getAttributionSource().getToken(), op, uid, packageName, attributionTag);
    }

    /**
     * @see #finishOp(String, int, String, String)
     *
     * @hide
     */
    public void finishOp(IBinder token, int op, int uid, @NonNull String packageName,
            @Nullable String attributionTag) {
        try {
            mService.finishOperation(getClientId(), op, uid, packageName, attributionTag);
            mService.finishOperation(token, op, uid, packageName, attributionTag);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -8817,23 +8949,26 @@ public class AppOpsManager {
    public void finishProxyOp(@NonNull String op, int proxiedUid,
            @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) {
        finishProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
                new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag)));
                new AttributionSource(proxiedUid, proxiedPackageName,  proxiedAttributionTag,
                        mContext.getAttributionSource().getToken())), /*skipProxyOperation*/ false);
    }

    /**
     * Report that an application is no longer performing an operation that had previously
     * been started with {@link #startProxyOp(String, AttributionSource, String)}. There is no
     * validation of input or result; the parameters supplied here must be the exact same ones
     * previously passed in when starting the operation.
     * been started with {@link #startProxyOp(String, AttributionSource, String, boolean)}. There
     * is no validation of input or result; the parameters supplied here must be the exact same
     * ones previously passed in when starting the operation.
     *
     * @param op The operation which was started
     * @param attributionSource The permission identity for which to finish
     * @param skipProxyOperation Whether to skip the proxy finish.
     *
     * @hide
     */
    public void finishProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource) {
    public void finishProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource,
            boolean skipProxyOperation) {
        try {
            mService.finishProxyOperation(getClientId(), strOpToOp(op), attributionSource);
            mService.finishProxyOperation(strOpToOp(op), attributionSource, skipProxyOperation);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+23 −17
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app;

import android.app.AppOpsManager.AttributionFlags;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.AttributionSource;
@@ -24,13 +25,13 @@ import android.util.SparseArray;
import android.util.SparseIntArray;

import com.android.internal.app.IAppOpsCallback;
import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;

/**
 * App ops service local interface.
@@ -52,8 +53,8 @@ public abstract class AppOpsManagerInternal {
         * @return The app op check result.
         */
        int checkOperation(int code, int uid, String packageName, @Nullable String attributionTag,
                boolean raw,
                QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl);
                boolean raw, QuintFunction<Integer, Integer, String, String, Boolean, Integer>
                superImpl);

        /**
         * Allows overriding check audio operation behavior.
@@ -116,20 +117,22 @@ public abstract class AppOpsManagerInternal {
         * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
         * @param message The message in the async noted op
         * @param shouldCollectMessage whether to collect messages
         * @param attributionFlags the attribution flags for this operation.
         * @param attributionChainId the unique id of the attribution chain this op is a part of.
         * @param superImpl The super implementation.
         * @return The app op note result.
         */
        SyncNotedAppOp startOperation(IBinder token, int code, int uid,
                @Nullable String packageName, @Nullable String attributionTag,
                boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
                @Nullable String message, boolean shouldCollectMessage, @NonNull NonaFunction<
                        IBinder, Integer, Integer, String, String, Boolean, Boolean, String,
                        Boolean, SyncNotedAppOp> superImpl);
                @Nullable String message, boolean shouldCollectMessage,
                @AttributionFlags int attributionFlags, int attributionChainId,
                @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
                        Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl);

        /**
         * Allows overriding start proxy operation behavior.
         *
         * @param token The client state.
         * @param code The op code to start.
         * @param attributionSource The permission identity of the caller.
         * @param startIfModeDefault Whether to start the op of the mode is default.
@@ -137,26 +140,29 @@ public abstract class AppOpsManagerInternal {
         * @param message The message in the async noted op
         * @param shouldCollectMessage whether to collect messages
         * @param skipProxyOperation Whether to skip the proxy portion of the operation
         * @param proxyAttributionFlags The attribution flags for the proxy.
         * @param proxiedAttributionFlags The attribution flags for the proxied.
         * @oaram attributionChainId The id of the attribution chain this operation is a part of.
         * @param superImpl The super implementation.
         * @return The app op note result.
         */
        SyncNotedAppOp startProxyOperation(IBinder token, int code,
                @NonNull AttributionSource attributionSource, boolean startIfModeDefault,
                boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
                boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer,
                        AttributionSource, Boolean, Boolean, String, Boolean, Boolean,
        SyncNotedAppOp startProxyOperation(int code, @NonNull AttributionSource attributionSource,
                boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
                boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags
                int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags,
                int attributionChainId, @NonNull DecFunction<Integer, AttributionSource, Boolean,
                        Boolean, String, Boolean, Boolean, Integer, Integer, Integer,
                        SyncNotedAppOp> superImpl);

        /**
         * Allows overriding finish proxy op.
         *
         * @param clientId Client state token.
         * @param code The op code to finish.
         * @param attributionSource The permission identity of the caller.
         */
        void finishProxyOperation(IBinder clientId, int code,
                @NonNull AttributionSource attributionSource,
                @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl);
        void finishProxyOperation(int code, @NonNull AttributionSource attributionSource,
                boolean skipProxyOperation,
                @NonNull TriFunction<Integer, AttributionSource, Boolean, Void> superImpl);
    }

    /**
+1 −7
Original line number Diff line number Diff line
@@ -3157,13 +3157,7 @@ class ContextImpl extends Context {
        // If we want to access protected data on behalf of another app we need to
        // tell the OS that we opt in to participate in the attribution chain.
        if (nextAttributionSource != null) {
            // If an app happened to stub the internal OS for testing the registration method
            // can return null. In this case we keep the current untrusted attribution source.
            final AttributionSource registeredAttributionSource = getSystemService(
                    PermissionManager.class).registerAttributionSource(attributionSource);
            if (registeredAttributionSource != null) {
                return registeredAttributionSource;
            }
            getSystemService(PermissionManager.class).registerAttributionSource(attributionSource);
        }
        return attributionSource;
    }
+9 −0
Original line number Diff line number Diff line
@@ -186,6 +186,7 @@ import android.os.incremental.IIncrementalService;
import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.permission.LegacyPermissionManager;
import android.permission.PermissionCheckerManager;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.print.IPrintManager;
@@ -1334,6 +1335,14 @@ public final class SystemServiceRegistry {
                                ctx.getMainThreadHandler());
                    }});

        registerService(Context.PERMISSION_CHECKER_SERVICE, PermissionCheckerManager.class,
                new CachedServiceFetcher<PermissionCheckerManager>() {
                    @Override
                    public PermissionCheckerManager createService(ContextImpl ctx)
                            throws ServiceNotFoundException {
                        return new PermissionCheckerManager(ctx.getOuterContext());
                    }});

        registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class,
                new CachedServiceFetcher<DynamicSystemManager>() {
                    @Override
Loading