summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/test-current.txt13
-rw-r--r--core/java/android/app/AppOpsManager.java177
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java40
-rw-r--r--core/java/android/app/ContextImpl.java8
-rw-r--r--core/java/android/app/SystemServiceRegistry.java9
-rw-r--r--core/java/android/content/AttributionSource.java131
-rw-r--r--core/java/android/content/ContentProvider.java13
-rw-r--r--core/java/android/content/Context.java8
-rw-r--r--core/java/android/content/PermissionChecker.java139
-rw-r--r--core/java/android/permission/IPermissionManager.aidl2
-rw-r--r--core/java/android/permission/PermissionCheckerManager.java186
-rw-r--r--core/java/android/permission/PermissionManager.java5
-rw-r--r--core/java/android/speech/RecognitionService.java125
-rw-r--r--core/java/com/android/internal/app/IAppOpsActiveCallback.aidl3
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl11
-rw-r--r--core/jni/Android.bp3
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_media_AudioRecord.cpp20
-rw-r--r--core/jni/android_media_AudioTrack.cpp12
-rw-r--r--core/jni/permission_utils.cpp71
-rw-r--r--core/jni/permission_utils.h27
-rw-r--r--media/java/android/media/AudioRecord.java85
-rw-r--r--media/java/android/media/MediaPlayer.java18
-rw-r--r--media/java/android/media/MediaRecorder.java24
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java18
-rw-r--r--media/java/android/media/audiofx/Visualizer.java17
-rw-r--r--media/java/android/media/permission/PermissionUtil.java18
-rw-r--r--media/jni/Android.bp2
-rw-r--r--media/jni/android_media_MediaPlayer.cpp12
-rw-r--r--media/jni/android_media_MediaRecorder.cpp14
-rw-r--r--media/jni/audioeffect/Android.bp5
-rw-r--r--media/jni/audioeffect/Visualizer.cpp6
-rw-r--r--media/jni/audioeffect/Visualizer.h8
-rw-r--r--media/jni/audioeffect/android_media_AudioEffect.cpp16
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp16
-rw-r--r--media/jni/soundpool/Android.bp2
-rw-r--r--media/jni/soundpool/Stream.cpp11
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java57
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java347
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java286
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java20
45 files changed, 1342 insertions, 698 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0b70a49a5ba0..15696b011095 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -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);
}
@@ -2056,7 +2065,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);
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 02520afea147..ea42d957b151 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -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,8 +8803,9 @@ public class AppOpsManager {
@Nullable String message) {
return startProxyOpNoThrow(AppOpsManager.strOpToOp(op), new AttributionSource(
mContext.getAttributionSource(), new AttributionSource(proxiedUid,
- proxiedPackageName, proxiedAttributionTag)), message,
- /*skipProxyOperation*/ false);
+ 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();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 2de0ddb17646..a757e32d0d75 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -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);
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index daef8b1eae08..16b6ea5bcf42 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -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;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 91dad2a1f13c..871d48b07a20 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -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
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 7ab731f15ad2..1dda6374a474 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.ActivityThread;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -94,14 +95,22 @@ public final class AttributionSource implements Parcelable {
@TestApi
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag) {
- this(uid, packageName, attributionTag, /*next*/ null);
+ this(uid, packageName, attributionTag, new Binder());
}
/** @hide */
@TestApi
public AttributionSource(int uid, @Nullable String packageName,
- @Nullable String attributionTag, @Nullable AttributionSource next) {
- this(uid, packageName, attributionTag, /*renouncedPermissions*/ null, next);
+ @Nullable String attributionTag, @NonNull IBinder token) {
+ this(uid, packageName, attributionTag, token, /*renouncedPermissions*/ null,
+ /*next*/ null);
+ }
+
+ /** @hide */
+ public AttributionSource(int uid, @Nullable String packageName,
+ @Nullable String attributionTag, @NonNull IBinder token,
+ @Nullable AttributionSource next) {
+ this(uid, packageName, attributionTag, token, /*renouncedPermissions*/ null, next);
}
/** @hide */
@@ -109,28 +118,32 @@ public final class AttributionSource implements Parcelable {
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable Set<String> renouncedPermissions,
@Nullable AttributionSource next) {
- this(uid, packageName, attributionTag, /*token*/ null, (renouncedPermissions != null)
+ this(uid, packageName, attributionTag, (renouncedPermissions != null)
? renouncedPermissions.toArray(new String[0]) : null, next);
}
/** @hide */
- public AttributionSource(@NonNull AttributionSource current,
- @Nullable AttributionSource next) {
+ public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) {
this(current.getUid(), current.getPackageName(), current.getAttributionTag(),
- /*token*/ null, /*renouncedPermissions*/ null, next);
+ current.getToken(), current.mAttributionSourceState.renouncedPermissions, next);
}
AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
- @Nullable IBinder token, @Nullable String[] renouncedPermissions,
+ @Nullable String[] renouncedPermissions, @Nullable AttributionSource next) {
+ this(uid, packageName, attributionTag, new Binder(), renouncedPermissions, next);
+ }
+
+ AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
+ @NonNull IBinder token, @Nullable String[] renouncedPermissions,
@Nullable AttributionSource next) {
mAttributionSourceState = new AttributionSourceState();
mAttributionSourceState.uid = uid;
+ mAttributionSourceState.token = token;
mAttributionSourceState.packageName = packageName;
mAttributionSourceState.attributionTag = attributionTag;
- mAttributionSourceState.token = token;
mAttributionSourceState.renouncedPermissions = renouncedPermissions;
mAttributionSourceState.next = (next != null) ? new AttributionSourceState[]
- {next.mAttributionSourceState} : null;
+ {next.mAttributionSourceState} : new AttributionSourceState[0];
}
AttributionSource(@NonNull Parcel in) {
@@ -145,18 +158,12 @@ public final class AttributionSource implements Parcelable {
/** @hide */
public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, next);
- }
-
- /** @hide */
- public AttributionSource withToken(@Nullable IBinder token) {
- return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
- token, mAttributionSourceState.renouncedPermissions, getNext());
+ mAttributionSourceState.renouncedPermissions, next);
}
/** @hide */
public AttributionSource withPackageName(@Nullable String packageName) {
- return new AttributionSource(getUid(), packageName, getAttributionTag(), getToken(),
+ return new AttributionSource(getUid(), packageName, getAttributionTag(),
mAttributionSourceState.renouncedPermissions, getNext());
}
@@ -165,6 +172,45 @@ public final class AttributionSource implements Parcelable {
return mAttributionSourceState;
}
+ /** @hide */
+ public @NonNull ScopedParcelState asScopedParcelState() {
+ return new ScopedParcelState(this);
+ }
+
+ /** @hide */
+ public static AttributionSource myAttributionSource() {
+ return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(),
+ /*attributionTag*/ null, (String[]) /*renouncedPermissions*/ null, /*next*/ null);
+ }
+
+ /**
+ * This is a scoped object that exposes the content of an attribution source
+ * as a parcel. This is useful when passing one to native and avoid custom
+ * conversion logic from Java to native state that needs to be kept in sync
+ * as attribution source evolves. This way we use the same logic for passing
+ * to native as the ones for passing in an IPC - in both cases this is the
+ * same auto generated code.
+ *
+ * @hide
+ */
+ public static class ScopedParcelState implements AutoCloseable {
+ private final Parcel mParcel;
+
+ public @NonNull Parcel getParcel() {
+ return mParcel;
+ }
+
+ public ScopedParcelState(AttributionSource attributionSource) {
+ mParcel = Parcel.obtain();
+ attributionSource.writeToParcel(mParcel, 0);
+ mParcel.setDataPosition(0);
+ }
+
+ public void close() {
+ mParcel.recycle();
+ }
+ }
+
/**
* If you are handling an IPC and you don't trust the caller you need to validate
* whether the attribution source is one for the calling app to prevent the caller
@@ -209,7 +255,8 @@ public final class AttributionSource implements Parcelable {
"attributionTag = " + mAttributionSourceState.attributionTag + ", " +
"token = " + mAttributionSourceState.token + ", " +
"next = " + (mAttributionSourceState.next != null
- ? mAttributionSourceState.next[0]: null) +
+ && mAttributionSourceState.next.length > 0
+ ? mAttributionSourceState.next[0] : null) +
" }";
}
return super.toString();
@@ -221,7 +268,8 @@ public final class AttributionSource implements Parcelable {
* @hide
*/
public int getNextUid() {
- if (mAttributionSourceState.next != null) {
+ if (mAttributionSourceState.next != null
+ && mAttributionSourceState.next.length > 0) {
return mAttributionSourceState.next[0].uid;
}
return Process.INVALID_UID;
@@ -233,26 +281,42 @@ public final class AttributionSource implements Parcelable {
* @hide
*/
public @Nullable String getNextPackageName() {
- if (mAttributionSourceState.next != null) {
+ if (mAttributionSourceState.next != null
+ && mAttributionSourceState.next.length > 0) {
return mAttributionSourceState.next[0].packageName;
}
return null;
}
/**
- * @return The nexxt package's attribution tag that would receive
+ * @return The next package's attribution tag that would receive
* the permission protected data.
*
* @hide
*/
public @Nullable String getNextAttributionTag() {
- if (mAttributionSourceState.next != null) {
+ if (mAttributionSourceState.next != null
+ && mAttributionSourceState.next.length > 0) {
return mAttributionSourceState.next[0].attributionTag;
}
return null;
}
/**
+ * @return The next package's token that would receive
+ * the permission protected data.
+ *
+ * @hide
+ */
+ public @Nullable IBinder getNextToken() {
+ if (mAttributionSourceState.next != null
+ && mAttributionSourceState.next.length > 0) {
+ return mAttributionSourceState.next[0].token;
+ }
+ return null;
+ }
+
+ /**
* Checks whether this attribution source can be trusted. That is whether
* the app it refers to created it and provided to the attribution chain.
*
@@ -311,7 +375,7 @@ public final class AttributionSource implements Parcelable {
*
* @hide
*/
- public @Nullable IBinder getToken() {
+ public @NonNull IBinder getToken() {
return mAttributionSourceState.token;
}
@@ -319,7 +383,8 @@ public final class AttributionSource implements Parcelable {
* The next app to receive the permission protected data.
*/
public @Nullable AttributionSource getNext() {
- if (mNextCached == null && mAttributionSourceState.next != null) {
+ if (mNextCached == null && mAttributionSourceState.next != null
+ && mAttributionSourceState.next.length > 0) {
mNextCached = new AttributionSource(mAttributionSourceState.next[0]);
}
return mNextCached;
@@ -442,7 +507,7 @@ public final class AttributionSource implements Parcelable {
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x10;
+ mBuilderFieldsSet |= 0x8;
mAttributionSourceState.renouncedPermissions = (value != null)
? value.toArray(new String[0]) : null;
return this;
@@ -453,9 +518,9 @@ public final class AttributionSource implements Parcelable {
*/
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x20;
+ mBuilderFieldsSet |= 0x10;
mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
- {value.mAttributionSourceState} : null;
+ {value.mAttributionSourceState} : mAttributionSourceState.next;
return this;
}
@@ -471,14 +536,16 @@ public final class AttributionSource implements Parcelable {
mAttributionSourceState.attributionTag = null;
}
if ((mBuilderFieldsSet & 0x8) == 0) {
- mAttributionSourceState.token = null;
- }
- if ((mBuilderFieldsSet & 0x10) == 0) {
mAttributionSourceState.renouncedPermissions = null;
}
- if ((mBuilderFieldsSet & 0x20) == 0) {
+ if ((mBuilderFieldsSet & 0x10) == 0) {
mAttributionSourceState.next = null;
}
+ mAttributionSourceState.token = new Binder();
+ if (mAttributionSourceState.next == null) {
+ // The NDK aidl backend doesn't support null parcelable arrays.
+ mAttributionSourceState.next = new AttributionSourceState[0];
+ }
return new AttributionSource(mAttributionSourceState);
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 88686a309ee4..dc29c5e25f3c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -49,6 +49,7 @@ import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.permission.PermissionCheckerManager;
import android.text.TextUtils;
import android.util.Log;
@@ -670,7 +671,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
}
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
private void enforceFilePermission(@NonNull AttributionSource attributionSource,
Uri uri, String mode)
throws FileNotFoundException, SecurityException {
@@ -687,7 +688,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
}
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
private int enforceReadPermission(@NonNull AttributionSource attributionSource, Uri uri)
throws SecurityException {
final int result = enforceReadPermissionInner(uri, attributionSource);
@@ -705,7 +706,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
return PermissionChecker.PERMISSION_GRANTED;
}
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
private int enforceWritePermission(@NonNull AttributionSource attributionSource, Uri uri)
throws SecurityException {
final int result = enforceWritePermissionInner(uri, attributionSource);
@@ -738,7 +739,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
* Verify that calling app holds both the given permission and any app-op
* associated with that permission.
*/
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
private int checkPermission(String permission,
@NonNull AttributionSource attributionSource) {
if (Binder.getCallingPid() == Process.myPid()) {
@@ -753,7 +754,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/** {@hide} */
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
protected int enforceReadPermissionInner(Uri uri,
@NonNull AttributionSource attributionSource) throws SecurityException {
final Context context = getContext();
@@ -836,7 +837,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/** {@hide} */
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
protected int enforceWritePermissionInner(Uri uri,
@NonNull AttributionSource attributionSource) throws SecurityException {
final Context context = getContext();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fc676cf5b9c2..1872a98555e7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4839,6 +4839,14 @@ public abstract class Context {
public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
/**
+ * Official published name of the (internal) permission checker service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String PERMISSION_CHECKER_SERVICE = "permission_checker";
+
+ /**
* Use with {@link #getSystemService(String) to retrieve an
* {@link android.apphibernation.AppHibernationManager}} for
* communicating with the hibernation service.
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 66b74f25bb2a..8d3452ec2c60 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -16,19 +16,14 @@
package android.content;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.os.Binder;
-import android.os.IBinder;
import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.permission.IPermissionChecker;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import android.permission.PermissionCheckerManager;
+import android.permission.PermissionCheckerManager.PermissionResult;
/**
* This class provides permission check APIs that verify both the
@@ -75,7 +70,7 @@ public final class PermissionChecker {
*
* @hide
*/
- public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED;
+ public static final int PERMISSION_GRANTED = PermissionCheckerManager.PERMISSION_GRANTED;
/**
* The permission is denied. Applicable only to runtime and app op permissions.
@@ -89,7 +84,8 @@ public final class PermissionChecker {
*
* @hide
*/
- public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED;
+ public static final int PERMISSION_SOFT_DENIED =
+ PermissionCheckerManager.PERMISSION_SOFT_DENIED;
/**
* The permission is denied.
@@ -103,18 +99,12 @@ public final class PermissionChecker {
*
* @hide
*/
- public static final int PERMISSION_HARD_DENIED = IPermissionChecker.PERMISSION_HARD_DENIED;
+ public static final int PERMISSION_HARD_DENIED =
+ PermissionCheckerManager.PERMISSION_HARD_DENIED;
/** Constant when the PID for which we check permissions is unknown. */
public static final int PID_UNKNOWN = -1;
- /** @hide */
- @IntDef({PERMISSION_GRANTED,
- PERMISSION_SOFT_DENIED,
- PERMISSION_HARD_DENIED})
- @Retention(RetentionPolicy.SOURCE)
- public @interface PermissionResult {}
-
private static volatile IPermissionChecker sService;
private PermissionChecker() {
@@ -157,7 +147,7 @@ public final class PermissionChecker {
*
* @see #checkPermissionForPreflight(Context, String, int, int, String)
*/
- @PermissionResult
+ @PermissionCheckerManager.PermissionResult
public static int checkPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, int pid, int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable String message, boolean startDataDelivery) {
@@ -321,19 +311,13 @@ public final class PermissionChecker {
message, startDataDelivery, /*fromDatasource*/ false);
}
+ @SuppressWarnings("ConstantConditions")
private static int checkPermissionForDataDeliveryCommon(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean startDataDelivery, boolean fromDatasource) {
- // If the check failed in the middle of the chain, finish any started op.
- try {
- final int result = getPermissionCheckerService().checkPermission(permission,
- attributionSource.asState(), message, true /*forDataDelivery*/,
- startDataDelivery, fromDatasource);
- return result;
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- return PERMISSION_HARD_DENIED;
+ return context.getSystemService(PermissionCheckerManager.class).checkPermission(permission,
+ attributionSource.asState(), message, true /*forDataDelivery*/, startDataDelivery,
+ fromDatasource, AppOpsManager.OP_NONE);
}
/**
@@ -365,17 +349,13 @@ public final class PermissionChecker {
* @see #checkPermissionForPreflight(Context, String, AttributionSource)
*/
@PermissionResult
+ @SuppressWarnings("ConstantConditions")
public static int checkPermissionAndStartDataDelivery(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- try {
- return getPermissionCheckerService().checkPermission(permission,
- attributionSource.asState(), message, true /*forDataDelivery*/,
- /*startDataDelivery*/ true, /*fromDatasource*/ false);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- return PERMISSION_HARD_DENIED;
+ return context.getSystemService(PermissionCheckerManager.class).checkPermission(
+ permission, attributionSource.asState(), message, true /*forDataDelivery*/,
+ /*startDataDelivery*/ true, /*fromDatasource*/ false, AppOpsManager.OP_NONE);
}
/**
@@ -404,17 +384,13 @@ public final class PermissionChecker {
* @see #finishDataDelivery(Context, String, AttributionSource)
*/
@PermissionResult
+ @SuppressWarnings("ConstantConditions")
public static int startOpForDataDelivery(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- try {
- return getPermissionCheckerService().checkOp(
- AppOpsManager.strOpToOp(opName), attributionSource.asState(), message,
- true /*forDataDelivery*/, true /*startDataDelivery*/);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- return PERMISSION_HARD_DENIED;
+ return context.getSystemService(PermissionCheckerManager.class).checkOp(
+ AppOpsManager.strOpToOp(opName), attributionSource.asState(), message,
+ true /*forDataDelivery*/, true /*startDataDelivery*/);
}
/**
@@ -428,13 +404,32 @@ public final class PermissionChecker {
* @see #startOpForDataDelivery(Context, String, AttributionSource, String)
* @see #checkPermissionAndStartDataDelivery(Context, String, AttributionSource, String)
*/
+ @SuppressWarnings("ConstantConditions")
public static void finishDataDelivery(@NonNull Context context, @NonNull String op,
@NonNull AttributionSource attributionSource) {
- try {
- getPermissionCheckerService().finishDataDelivery(op, attributionSource.asState());
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
+ context.getSystemService(PermissionCheckerManager.class).finishDataDelivery(
+ AppOpsManager.strOpToOp(op), attributionSource.asState(),
+ /*fromDatasource*/ false);
+ }
+
+ /**
+ * Finishes an ongoing op for data access chain described by the given {@link
+ * AttributionSource}. Call this method if you are the datasource which would
+ * not finish an op for your attribution source as it was not started.
+ *
+ * @param context Context for accessing resources.
+ * @param op The op to finish.
+ * @param attributionSource The identity for which finish op.
+ *
+ * @see #startOpForDataDelivery(Context, String, AttributionSource, String)
+ * @see #checkPermissionAndStartDataDelivery(Context, String, AttributionSource, String)
+ */
+ @SuppressWarnings("ConstantConditions")
+ public static void finishDataDeliveryFromDatasource(@NonNull Context context,
+ @NonNull String op, @NonNull AttributionSource attributionSource) {
+ context.getSystemService(PermissionCheckerManager.class).finishDataDelivery(
+ AppOpsManager.strOpToOp(op), attributionSource.asState(),
+ /*fromDatasource*/ true);
}
/**
@@ -466,17 +461,13 @@ public final class PermissionChecker {
* @see #checkOpForDataDelivery(Context, String, AttributionSource, String)
*/
@PermissionResult
+ @SuppressWarnings("ConstantConditions")
public static int checkOpForPreflight(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- try {
- return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName),
- attributionSource.asState(), message, false /*forDataDelivery*/,
- false /*startDataDelivery*/);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- return PERMISSION_HARD_DENIED;
+ return context.getSystemService(PermissionCheckerManager.class).checkOp(
+ AppOpsManager.strOpToOp(opName), attributionSource.asState(), message,
+ false /*forDataDelivery*/, false /*startDataDelivery*/);
}
/**
@@ -505,17 +496,13 @@ public final class PermissionChecker {
* @see #checkOpForPreflight(Context, String, AttributionSource, String)
*/
@PermissionResult
+ @SuppressWarnings("ConstantConditions")
public static int checkOpForDataDelivery(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- try {
- return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName),
- attributionSource.asState(), message, true /*forDataDelivery*/,
- false /*startDataDelivery*/);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- return PERMISSION_HARD_DENIED;
+ return context.getSystemService(PermissionCheckerManager.class).checkOp(
+ AppOpsManager.strOpToOp(opName), attributionSource.asState(), message,
+ true /*forDataDelivery*/, false /*startDataDelivery*/);
}
/**
@@ -584,16 +571,13 @@ public final class PermissionChecker {
* String, boolean)
*/
@PermissionResult
+ @SuppressWarnings("ConstantConditions")
public static int checkPermissionForPreflight(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource) {
- try {
- return getPermissionCheckerService().checkPermission(permission,
- attributionSource.asState(), null /*message*/, false /*forDataDelivery*/,
- /*startDataDelivery*/ false, /*fromDatasource*/ false);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- return PERMISSION_HARD_DENIED;
+ return context.getSystemService(PermissionCheckerManager.class)
+ .checkPermission(permission, attributionSource.asState(), null /*message*/,
+ false /*forDataDelivery*/, /*startDataDelivery*/ false, /*fromDatasource*/ false,
+ AppOpsManager.OP_NONE);
}
/**
@@ -827,13 +811,4 @@ public final class PermissionChecker {
return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
Binder.getCallingUid(), packageName);
}
-
- private static @NonNull IPermissionChecker getPermissionCheckerService() {
- // Race is fine, we may end up looking up the same instance twice, no big deal.
- if (sService == null) {
- final IBinder service = ServiceManager.getService("permission_checker");
- sService = IPermissionChecker.Stub.asInterface(service);
- }
- return sService;
- }
}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index ef075e1efbff..9ab69552f1a0 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -87,7 +87,7 @@ interface IPermissionManager {
boolean isAutoRevokeExempted(String packageName, int userId);
- AttributionSource registerAttributionSource(in AttributionSource source);
+ void registerAttributionSource(in AttributionSource source);
boolean isRegisteredAttributionSource(in AttributionSource source);
}
diff --git a/core/java/android/permission/PermissionCheckerManager.java b/core/java/android/permission/PermissionCheckerManager.java
new file mode 100644
index 000000000000..7523816250b0
--- /dev/null
+++ b/core/java/android/permission/PermissionCheckerManager.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.AttributionSourceState;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Manager for checking runtime and app op permissions. This is a temporary
+ * class and we may fold its function in the PermissionManager once the
+ * permission re-architecture starts falling into place. The main benefit
+ * of this class is to allow context level caching.
+ *
+ * @hide
+ */
+public class PermissionCheckerManager {
+
+ /**
+ * The permission is granted.
+ */
+ public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED;
+
+ /**
+ * The permission is denied. Applicable only to runtime and app op permissions.
+ *
+ * <p>Returned when:
+ * <ul>
+ * <li>the runtime permission is granted, but the corresponding app op is denied
+ * for runtime permissions.</li>
+ * <li>the app ops is ignored for app op permissions.</li>
+ * </ul>
+ */
+ public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED;
+
+ /**
+ * The permission is denied.
+ *
+ * <p>Returned when:
+ * <ul>
+ * <li>the permission is denied for non app op permissions.</li>
+ * <li>the app op is denied or app op is {@link AppOpsManager#MODE_DEFAULT}
+ * and permission is denied.</li>
+ * </ul>
+ */
+ public static final int PERMISSION_HARD_DENIED = IPermissionChecker.PERMISSION_HARD_DENIED;
+
+ /** @hide */
+ @IntDef({PERMISSION_GRANTED,
+ PERMISSION_SOFT_DENIED,
+ PERMISSION_HARD_DENIED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionResult {}
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final IPermissionChecker mService;
+
+ @NonNull
+ private final PackageManager mPackageManager;
+
+ public PermissionCheckerManager(@NonNull Context context)
+ throws ServiceManager.ServiceNotFoundException {
+ mContext = context;
+ mService = IPermissionChecker.Stub.asInterface(ServiceManager.getServiceOrThrow(
+ Context.PERMISSION_CHECKER_SERVICE));
+ mPackageManager = context.getPackageManager();
+ }
+
+ /**
+ * Checks a permission by validating the entire attribution source chain. If the
+ * permission is associated with an app op the op is also noted/started for the
+ * entire attribution chain.
+ *
+ * @param permission The permission
+ * @param attributionSource The attribution chain to check.
+ * @param message Message associated with the permission if permission has an app op
+ * @param forDataDelivery Whether the check is for delivering data if permission has an app op
+ * @param startDataDelivery Whether to start data delivery (start op) if permission has
+ * an app op
+ * @param fromDatasource Whether the check is by a datasource (skip checks for the
+ * first attribution source in the chain as this is the datasource)
+ * @param attributedOp Alternative app op to attribute
+ * @return The permission check result.
+ */
+ @PermissionResult
+ public int checkPermission(@NonNull String permission,
+ @NonNull AttributionSourceState attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource,
+ int attributedOp) {
+ Objects.requireNonNull(permission);
+ Objects.requireNonNull(attributionSource);
+ // Fast path for non-runtime, non-op permissions where the attribution chain has
+ // length one. This is the majority of the cases and we want these to be fast by
+ // hitting the local in process permission cache.
+ if (AppOpsManager.permissionToOpCode(permission) == AppOpsManager.OP_NONE) {
+ if (fromDatasource) {
+ if (attributionSource.next != null && attributionSource.next.length > 0) {
+ return mContext.checkPermission(permission, attributionSource.next[0].pid,
+ attributionSource.next[0].uid) == PackageManager.PERMISSION_GRANTED
+ ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+ }
+ } else {
+ return (mContext.checkPermission(permission, attributionSource.pid,
+ attributionSource.uid) == PackageManager.PERMISSION_GRANTED)
+ ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+ }
+ }
+ try {
+ return mService.checkPermission(permission, attributionSource, message, forDataDelivery,
+ startDataDelivery, fromDatasource, attributedOp);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
+ }
+
+ /**
+ * Finishes an app op by validating the entire attribution source chain.
+ *
+ * @param op The op to finish.
+ * @param attributionSource The attribution chain to finish.
+ * @param fromDatasource Whether the finish is by a datasource (skip finish for the
+ * first attribution source in the chain as this is the datasource)
+ */
+ public void finishDataDelivery(int op, @NonNull AttributionSourceState attributionSource,
+ boolean fromDatasource) {
+ Objects.requireNonNull(attributionSource);
+ try {
+ mService.finishDataDelivery(op, attributionSource, fromDatasource);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Checks an app op by validating the entire attribution source chain. The op is
+ * also noted/started for the entire attribution chain.
+ *
+ * @param op The op to check.
+ * @param attributionSource The attribution chain to check.
+ * @param message Message associated with the permission if permission has an app op
+ * @param forDataDelivery Whether the check is for delivering data if permission has an app op
+ * @param startDataDelivery Whether to start data delivery (start op) if permission has
+ * an app op
+ * @return The op check result.
+ */
+ @PermissionResult
+ public int checkOp(int op, @NonNull AttributionSourceState attributionSource,
+ @Nullable String message, boolean forDataDelivery, boolean startDataDelivery) {
+ Objects.requireNonNull(attributionSource);
+ try {
+ return mService.checkOp(op, attributionSource, message, forDataDelivery,
+ startDataDelivery);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
+ }
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index d490e7a7b454..f3cc35b32223 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1156,13 +1156,12 @@ public final class PermissionManager {
* @hide
*/
@TestApi
- public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
+ public void registerAttributionSource(@NonNull AttributionSource source) {
try {
- return mPermissionManager.registerAttributionSource(source);
+ mPermissionManager.registerAttributionSource(source);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
- return null;
}
/**
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index bb48757988e9..b9ff5e7be86a 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -65,22 +65,20 @@ public abstract class RecognitionService extends Service {
private static final String TAG = "RecognitionService";
/** Debugging flag */
- private static final boolean DBG = true;
-
- private static final String RECORD_AUDIO_APP_OP =
- AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO);
- private static final int RECORD_AUDIO_APP_OP_CODE =
- AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO);
+ private static final boolean DBG = false;
/** Binder of the recognition service */
private RecognitionServiceBinder mBinder = new RecognitionServiceBinder(this);
/**
* The current callback of an application that invoked the
+ *
* {@link RecognitionService#onStartListening(Intent, Callback)} method
*/
private Callback mCurrentCallback = null;
+ private boolean mStartedDataDelivery;
+
private static final int MSG_START_LISTENING = 1;
private static final int MSG_STOP_LISTENING = 2;
@@ -120,6 +118,11 @@ public abstract class RecognitionService extends Service {
mCurrentCallback = new Callback(listener, attributionSource);
RecognitionService.this.onStartListening(intent, mCurrentCallback);
+ if (!checkPermissionAndStartDataDelivery()) {
+ listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
+ Log.i(TAG, "caller doesn't have permission:"
+ + Manifest.permission.RECORD_AUDIO);
+ }
} else {
listener.onError(SpeechRecognizer.ERROR_RECOGNIZER_BUSY);
Log.i(TAG, "concurrent startListening received - ignoring this call");
@@ -152,13 +155,15 @@ public abstract class RecognitionService extends Service {
Log.w(TAG, "cancel called by client who did not call startListening - ignoring");
} else { // the correct state
RecognitionService.this.onCancel(mCurrentCallback);
- mCurrentCallback = null;
+ dispatchClearCallback();
if (DBG) Log.d(TAG, "canceling - setting mCurrentCallback to null");
}
}
private void dispatchClearCallback() {
+ finishDataDelivery();
mCurrentCallback = null;
+ mStartedDataDelivery = false;
}
private class StartListeningArgs {
@@ -177,7 +182,30 @@ public abstract class RecognitionService extends Service {
/**
* Notifies the service that it should start listening for speech.
- *
+ *
+ * <p> If you are recognizing speech from the microphone, in this callback you
+ * should create an attribution context for the caller such that when you access
+ * the mic the caller would be properly blamed (and their permission checked in
+ * the process) for accessing the microphone and that you served as a proxy for
+ * this sensitive data (and your permissions would be checked in the process).
+ * You should also open the mic in this callback via the attribution context
+ * and close the mic before returning the recognized result. If you don't do
+ * that then the caller would be blamed and you as being a proxy as well as you
+ * would get one more blame on yourself when you open the microphone.
+ *
+ * <pre>
+ * Context attributionContext = context.createContext(new ContextParams.Builder()
+ * .setNextAttributionSource(callback.getCallingAttributionSource())
+ * .build());
+ *
+ * AudioRecord recorder = AudioRecord.Builder()
+ * .setContext(attributionContext);
+ * . . .
+ * .build();
+ *
+ * recorder.startRecording()
+ * </pre>
+ *
* @param recognizerIntent contains parameters for the recognition to be performed. The intent
* may also contain optional extras, see {@link RecognizerIntent}. If these values are
* not set explicitly, default values should be used by the recognizer.
@@ -335,57 +363,13 @@ public abstract class RecognitionService extends Service {
return mCallingAttributionSource;
}
- boolean maybeStartAttribution() {
- if (DBG) {
- Log.i(TAG, "Starting attribution");
- }
-
- if (DBG && isProxyingRecordAudioToCaller()) {
- Log.i(TAG, "Proxying already in progress, not starting the attribution");
- }
-
- if (!isProxyingRecordAudioToCaller()) {
+ @NonNull Context getAttributionContextForCaller() {
+ if (mAttributionContext == null) {
mAttributionContext = createContext(new ContextParams.Builder()
.setNextAttributionSource(mCallingAttributionSource)
.build());
-
- final int result = PermissionChecker.checkPermissionAndStartDataDelivery(
- RecognitionService.this,
- Manifest.permission.RECORD_AUDIO,
- mAttributionContext.getAttributionSource(),
- /*message*/ null);
-
- return result == PermissionChecker.PERMISSION_GRANTED;
- }
- return false;
- }
-
- void maybeFinishAttribution() {
- if (DBG) {
- Log.i(TAG, "Finishing attribution");
- }
-
- if (DBG && !isProxyingRecordAudioToCaller()) {
- Log.i(TAG, "Not proxying currently, not finishing the attribution");
- }
-
- if (isProxyingRecordAudioToCaller()) {
- PermissionChecker.finishDataDelivery(
- RecognitionService.this,
- RECORD_AUDIO_APP_OP,
- mAttributionContext.getAttributionSource());
-
- mAttributionContext = null;
}
- }
-
- private boolean isProxyingRecordAudioToCaller() {
- final AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
- return appOpsManager.isProxying(
- RECORD_AUDIO_APP_OP_CODE,
- getAttributionTag(),
- mCallingAttributionSource.getUid(),
- mCallingAttributionSource.getPackageName());
+ return mAttributionContext;
}
}
@@ -435,4 +419,35 @@ public abstract class RecognitionService extends Service {
mServiceRef.clear();
}
}
+
+ private boolean checkPermissionAndStartDataDelivery() {
+ if (isPerformingDataDelivery()) {
+ return true;
+ }
+ if (PermissionChecker.checkPermissionAndStartDataDelivery(
+ RecognitionService.this, Manifest.permission.RECORD_AUDIO,
+ mCurrentCallback.getAttributionContextForCaller().getAttributionSource(),
+ /*message*/ null) == PermissionChecker.PERMISSION_GRANTED) {
+ mStartedDataDelivery = true;
+ }
+ return mStartedDataDelivery;
+ }
+
+ void finishDataDelivery() {
+ if (mStartedDataDelivery) {
+ mStartedDataDelivery = false;
+ final String op = AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO);
+ PermissionChecker.finishDataDelivery(RecognitionService.this, op,
+ mCurrentCallback.getAttributionContextForCaller().getAttributionSource());
+ }
+ }
+
+ @SuppressWarnings("ConstantCondition")
+ private boolean isPerformingDataDelivery() {
+ final int op = AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO);
+ final AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
+ return appOpsManager.isProxying(op, getAttributionTag(),
+ mCurrentCallback.getCallingAttributionSource().getUid(),
+ mCurrentCallback.getCallingAttributionSource().getPackageName());
+ }
}
diff --git a/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl b/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl
index 510af770d126..ae6ad326e7b1 100644
--- a/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl
@@ -18,5 +18,6 @@ package com.android.internal.app;
// Iterface to observe op active changes
oneway interface IAppOpsActiveCallback {
- void opActiveChanged(int op, int uid, String packageName, boolean active);
+ void opActiveChanged(int op, int uid, String packageName, String attributionTag,
+ boolean active, int attributionFlags, int attributionChainId);
}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index c112d09d40e4..9ad457200700 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -41,7 +41,8 @@ interface IAppOpsService {
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
SyncNotedAppOp startOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ int attributionFlags, int attributionChainId);
@UnsupportedAppUsage
void finishOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag);
@@ -57,10 +58,12 @@ interface IAppOpsService {
SyncNotedAppOp noteProxyOperation(int code, in AttributionSource attributionSource,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
boolean skipProxyOperation);
- SyncNotedAppOp startProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource,
+ SyncNotedAppOp startProxyOperation(int code, in AttributionSource attributionSource,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage, boolean skipProxyOperation);
- void finishProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource);
+ boolean shouldCollectMessage, boolean skipProxyOperation, int proxyAttributionFlags,
+ int proxiedAttributionFlags, int attributionChainId);
+ void finishProxyOperation(int code, in AttributionSource attributionSource,
+ boolean skipProxyOperation);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 1f805c94d51c..323b401185c9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -221,7 +221,6 @@ cc_library_shared {
"fd_utils.cpp",
"android_hardware_input_InputWindowHandle.cpp",
"android_hardware_input_InputApplicationHandle.cpp",
- "permission_utils.cpp",
],
static_libs: [
@@ -240,7 +239,6 @@ cc_library_shared {
"audioflinger-aidl-cpp",
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
- "media_permission-aidl-cpp",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
@@ -257,6 +255,7 @@ cc_library_shared {
"libgraphicsenv",
"libgui",
"libmediandk",
+ "libpermission",
"libsensor",
"libinput",
"libcamera_client",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f76cccb65973..de6a2b52a025 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -98,7 +98,6 @@ extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
extern int register_android_media_ToneGenerator(JNIEnv *env);
extern int register_android_media_midi(JNIEnv *env);
-extern int register_android_media_permission_Identity(JNIEnv* env);
namespace android {
@@ -1589,7 +1588,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_RemoteDisplay),
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_media_midi),
- REG_JNI(register_android_media_permission_Identity),
REG_JNI(register_android_opengl_classes),
REG_JNI(register_android_server_NetworkManagementSocketTagger),
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 83dc1e0bb0d9..bce4ed78eda1 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -22,13 +22,15 @@
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
-#include "permission_utils.h"
#include <utils/Log.h>
#include <media/AudioRecord.h>
#include <media/MicrophoneInfo.h>
#include <vector>
+#include <android/content/AttributionSourceState.h>
+#include <android_os_Parcel.h>
+
#include <nativehelper/ScopedUtfChars.h>
#include "android_media_AudioFormat.h"
@@ -38,10 +40,8 @@
#include "android_media_MicrophoneInfo.h"
#include "android_media_AudioAttributes.h"
-// ----------------------------------------------------------------------------
-using android::media::permission::convertIdentity;
-using android::media::permission::Identity;
+// ----------------------------------------------------------------------------
using namespace android;
@@ -189,7 +189,7 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w
jobject jaa, jintArray jSampleRate, jint channelMask,
jint channelIndexMask, jint audioFormat,
jint buffSizeInBytes, jintArray jSession,
- jobject jIdentity, jlong nativeRecordInJavaObj,
+ jobject jAttributionSource, jlong nativeRecordInJavaObj,
jint sharedAudioHistoryMs) {
//ALOGV(">> Entering android_media_AudioRecord_setup");
//ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
@@ -260,14 +260,18 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w
size_t bytesPerSample = audio_bytes_per_sample(format);
if (buffSizeInBytes == 0) {
- ALOGE("Error creating AudioRecord: frameCount is 0.");
+ ALOGE("Error creating AudioRecord: frameCount is 0.");
return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
}
size_t frameSize = channelCount * bytesPerSample;
size_t frameCount = buffSizeInBytes / frameSize;
// create an uninitialized AudioRecord object
- lpRecorder = new AudioRecord(convertIdentity(env, jIdentity));
+ Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
+ android::content::AttributionSourceState attributionSource;
+ attributionSource.readFromParcel(parcel);
+
+ lpRecorder = new AudioRecord(attributionSource);
// read the AudioAttributes values
auto paa = JNIAudioAttributeHelper::makeUnique();
@@ -912,7 +916,7 @@ static const JNINativeMethod gMethods[] = {
{"native_start", "(II)I", (void *)android_media_AudioRecord_start},
{"native_stop", "()V", (void *)android_media_AudioRecord_stop},
{"native_setup",
- "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/media/permission/Identity;JI)I",
+ "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/os/Parcel;JI)I",
(void *)android_media_AudioRecord_setup},
{"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
{"native_release", "()V", (void *)android_media_AudioRecord_release},
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index de5df202ba4e..73d2d8d949cd 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -48,7 +48,6 @@
using namespace android;
using ::android::media::VolumeShaper;
-using ::android::media::permission::Identity;
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/AudioTrack";
@@ -330,9 +329,10 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
// create the native AudioTrack object
ScopedUtfChars opPackageNameStr(env, opPackageName);
// TODO b/182469354: make consistent with AudioRecord
- Identity identity = Identity();
- identity.packageName = std::string(opPackageNameStr.c_str());
- lpTrack = new AudioTrack(identity);
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = std::string(opPackageNameStr.c_str());
+ attributionSource.token = sp<BBinder>::make();
+ lpTrack = new AudioTrack(attributionSource);
// read the AudioAttributes values
auto paa = JNIAudioAttributeHelper::makeUnique();
@@ -395,7 +395,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
: AudioTrack::TRANSFER_SYNC,
(offload || encapsulationMode) ? &offloadInfo : NULL,
- Identity(), // default uid, pid values
+ AttributionSourceState(), // default uid, pid values
paa.get());
break;
@@ -421,7 +421,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we
sessionId, // audio session ID
AudioTrack::TRANSFER_SHARED,
NULL, // default offloadInfo
- Identity(), // default uid, pid values
+ AttributionSourceState(), // default uid, pid values
paa.get());
break;
diff --git a/core/jni/permission_utils.cpp b/core/jni/permission_utils.cpp
deleted file mode 100644
index 2b7ef9999491..000000000000
--- a/core/jni/permission_utils.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "permission_utils.h"
-#include "core_jni_helpers.h"
-
-static struct {
- jfieldID fieldUid; // Identity.uid
- jfieldID fieldPid; // Identity.pid
- jfieldID fieldPackageName; // Identity.packageName
- jfieldID fieldAttributionTag; // Identity.attributionTag
-} javaIdentityFields;
-
-static const JNINativeMethod method_table[] = {
- // no static methods, currently
-};
-
-int register_android_media_permission_Identity(JNIEnv* env) {
- jclass identityClass = android::FindClassOrDie(env, "android/media/permission/Identity");
- javaIdentityFields.fieldUid = android::GetFieldIDOrDie(env, identityClass, "uid", "I");
- javaIdentityFields.fieldPid = android::GetFieldIDOrDie(env, identityClass, "pid", "I");
- javaIdentityFields.fieldPackageName =
- android::GetFieldIDOrDie(env, identityClass, "packageName", "Ljava/lang/String;");
- javaIdentityFields.fieldAttributionTag =
- android::GetFieldIDOrDie(env, identityClass, "attributionTag", "Ljava/lang/String;");
-
- return android::RegisterMethodsOrDie(env, "android/media/permission/Identity", method_table,
- NELEM(method_table));
-}
-
-namespace android::media::permission {
-
-Identity convertIdentity(JNIEnv* env, const jobject& jIdentity) {
- Identity identity;
-
- identity.uid = env->GetIntField(jIdentity, javaIdentityFields.fieldUid);
- identity.pid = env->GetIntField(jIdentity, javaIdentityFields.fieldPid);
-
- jstring packageNameStr = static_cast<jstring>(
- env->GetObjectField(jIdentity, javaIdentityFields.fieldPackageName));
- if (packageNameStr == nullptr) {
- identity.packageName = std::nullopt;
- } else {
- identity.packageName = std::string(ScopedUtfChars(env, packageNameStr).c_str());
- }
-
- jstring attributionTagStr = static_cast<jstring>(
- env->GetObjectField(jIdentity, javaIdentityFields.fieldAttributionTag));
- if (attributionTagStr == nullptr) {
- identity.attributionTag = std::nullopt;
- } else {
- identity.attributionTag = std::string(ScopedUtfChars(env, attributionTagStr).c_str());
- }
-
- return identity;
-}
-
-} // namespace android::media::permission
diff --git a/core/jni/permission_utils.h b/core/jni/permission_utils.h
deleted file mode 100644
index d625bb6ba30a..000000000000
--- a/core/jni/permission_utils.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android/media/permission/Identity.h>
-#include <jni.h>
-
-namespace android::media::permission {
-
-Identity convertIdentity(JNIEnv* env, const jobject& jIdentity);
-}
-
-int register_android_media_permission_Identity(JNIEnv* env);
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 0d44a8580ef9..7c6ae28bdd30 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,8 +16,6 @@
package android.media;
-import static android.media.permission.PermissionUtil.myIdentity;
-
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -29,12 +27,13 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
+import android.content.AttributionSource.ScopedParcelState;
import android.content.Context;
import android.media.MediaRecorder.Source;
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioPolicy;
import android.media.metrics.LogSessionId;
-import android.media.permission.Identity;
import android.media.projection.MediaProjection;
import android.os.Binder;
import android.os.Build;
@@ -42,6 +41,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -381,7 +381,8 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
* {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction
* time. See also {@link AudioManager#generateAudioSessionId()} to obtain a session ID before
* construction.
- * @param context An optional context to pull an attribution tag from.
+ * @param context An optional context on whose behalf the recoding is performed.
+ *
* @throws IllegalArgumentException
*/
private AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
@@ -449,10 +450,11 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
audioBuffSizeCheck(bufferSizeInBytes);
- Identity identity = myIdentity(context);
- if (identity.packageName == null) {
+ AttributionSource attributionSource = (context != null)
+ ? context.getAttributionSource() : AttributionSource.myAttributionSource();
+ if (attributionSource.getPackageName() == null) {
// Command line utility
- identity.packageName = "uid:" + Binder.getCallingUid();
+ attributionSource = attributionSource.withPackageName("uid:" + Binder.getCallingUid());
}
int[] sampleRate = new int[] {mSampleRate};
@@ -461,14 +463,15 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
//TODO: update native initialization when information about hardware init failure
// due to capture device already open is available.
- int initResult = native_setup(new WeakReference<AudioRecord>(this),
- mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask,
- mAudioFormat, mNativeBufferSizeInBytes,
- session, identity, 0 /*nativeRecordInJavaObj*/,
- maxSharedAudioHistoryMs);
- if (initResult != SUCCESS) {
- loge("Error code "+initResult+" when initializing native AudioRecord object.");
- return; // with mState == STATE_UNINITIALIZED
+ try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
+ int initResult = native_setup(new WeakReference<AudioRecord>(this), mAudioAttributes,
+ sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
+ mNativeBufferSizeInBytes, session, attributionSourceState.getParcel(),
+ 0 /*nativeRecordInJavaObj*/, maxSharedAudioHistoryMs);
+ if (initResult != SUCCESS) {
+ loge("Error code " + initResult + " when initializing native AudioRecord object.");
+ return; // with mState == STATE_UNINITIALIZED
+ }
}
mSampleRate = sampleRate[0];
@@ -512,23 +515,27 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
*/
/* package */ void deferred_connect(long nativeRecordInJavaObj) {
if (mState != STATE_INITIALIZED) {
- int[] session = { 0 };
- int[] rates = { 0 };
+ int[] session = {0};
+ int[] rates = {0};
//TODO: update native initialization when information about hardware init failure
// due to capture device already open is available.
// Note that for this native_setup, we are providing an already created/initialized
// *Native* AudioRecord, so the attributes parameters to native_setup() are ignored.
- int initResult = native_setup(new WeakReference<AudioRecord>(this),
- null /*mAudioAttributes*/,
- rates /*mSampleRates*/,
- 0 /*mChannelMask*/,
- 0 /*mChannelIndexMask*/,
- 0 /*mAudioFormat*/,
- 0 /*mNativeBufferSizeInBytes*/,
- session,
- myIdentity(null),
- nativeRecordInJavaObj,
- 0);
+ final int initResult;
+ try (ScopedParcelState attributionSourceState = AttributionSource.myAttributionSource()
+ .asScopedParcelState()) {
+ initResult = native_setup(new WeakReference<>(this),
+ null /*mAudioAttributes*/,
+ rates /*mSampleRates*/,
+ 0 /*mChannelMask*/,
+ 0 /*mChannelIndexMask*/,
+ 0 /*mAudioFormat*/,
+ 0 /*mNativeBufferSizeInBytes*/,
+ session,
+ attributionSourceState.getParcel(),
+ nativeRecordInJavaObj,
+ 0);
+ }
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing native AudioRecord object.");
return; // with mState == STATE_UNINITIALIZED
@@ -620,8 +627,8 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
/**
* Sets the context the record belongs to. This context will be used to pull information,
- * such as attribution tags, which will be associated with the AudioRecord. However, the
- * context itself will not be retained by the AudioRecord.
+ * such as {@link android.content.AttributionSource}, which will be associated with
+ * the AudioRecord. However, the context itself will not be retained by the AudioRecord.
* @param context a non-null {@link Context} instance
* @return the same Builder instance.
*/
@@ -2216,7 +2223,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
//--------------------
/**
- * @deprecated Use native_setup that takes an Identity object
+ * @deprecated Use native_setup that takes an {@link AttributionSource} object
* @return
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
@@ -2227,18 +2234,20 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
int buffSizeInBytes, int[] sessionId, String opPackageName,
long nativeRecordInJavaObj) {
- Identity identity = myIdentity(null);
- identity.packageName = opPackageName;
-
- return native_setup(audiorecordThis, attributes, sampleRate, channelMask, channelIndexMask,
- audioFormat, buffSizeInBytes, sessionId, identity, nativeRecordInJavaObj, 0);
+ AttributionSource attributionSource = AttributionSource.myAttributionSource()
+ .withPackageName(opPackageName);
+ try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
+ return native_setup(audiorecordThis, attributes, sampleRate, channelMask,
+ channelIndexMask, audioFormat, buffSizeInBytes, sessionId,
+ attributionSourceState.getParcel(), nativeRecordInJavaObj, 0);
+ }
}
private native int native_setup(Object audiorecordThis,
Object /*AudioAttributes*/ attributes,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
- int buffSizeInBytes, int[] sessionId, Identity identity, long nativeRecordInJavaObj,
- int maxSharedAudioHistoryMs);
+ int buffSizeInBytes, int[] sessionId, @NonNull Parcel attributionSource,
+ long nativeRecordInJavaObj, int maxSharedAudioHistoryMs);
// TODO remove: implementation calls directly into implementation of native_release()
private native void native_finalize();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index db3e8b0bf888..ccd830a37d89 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -18,7 +18,6 @@ package android.media;
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.media.permission.PermissionUtil.myIdentity;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -27,6 +26,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
+import android.content.AttributionSource.ScopedParcelState;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -34,7 +35,6 @@ import android.content.res.AssetFileDescriptor;
import android.graphics.SurfaceTexture;
import android.media.SubtitleController.Anchor;
import android.media.SubtitleTrack.RenderingWidget;
-import android.media.permission.Identity;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -55,7 +55,6 @@ import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -684,14 +683,18 @@ public class MediaPlayer extends PlayerBase
mTimeProvider = new TimeProvider(this);
mOpenSubtitleSources = new Vector<InputStream>();
- Identity identity = myIdentity(null);
+ AttributionSource attributionSource = AttributionSource.myAttributionSource();
// set the package name to empty if it was null
- identity.packageName = TextUtils.emptyIfNull(identity.packageName);
+ if (attributionSource.getPackageName() == null) {
+ attributionSource = attributionSource.withPackageName("");
+ }
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
- native_setup(new WeakReference<MediaPlayer>(this), identity);
+ try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
+ native_setup(new WeakReference<MediaPlayer>(this), attributionSourceState.getParcel());
+ }
baseRegisterPlayer(sessionId);
}
@@ -2474,7 +2477,8 @@ public class MediaPlayer extends PlayerBase
private native final int native_setMetadataFilter(Parcel request);
private static native final void native_init();
- private native void native_setup(Object mediaplayerThis, @NonNull Identity identity);
+ private native void native_setup(Object mediaplayerThis,
+ @NonNull Parcel attributionSource);
private native final void native_finalize();
/**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index c63e64af37cd..da18a77a497e 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,8 +16,6 @@
package android.media;
-import static android.media.permission.PermissionUtil.myIdentity;
-
import android.annotation.CallbackExecutor;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -27,14 +25,16 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
+import android.content.AttributionSource.ScopedParcelState;
import android.content.Context;
import android.hardware.Camera;
import android.media.metrics.LogSessionId;
-import android.media.permission.Identity;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.util.ArrayMap;
import android.util.Log;
@@ -163,8 +163,11 @@ public class MediaRecorder implements AudioRouting,
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
- native_setup(new WeakReference<MediaRecorder>(this),
- ActivityThread.currentPackageName(), myIdentity(context));
+ try (ScopedParcelState attributionSourceState = context.getAttributionSource()
+ .asScopedParcelState()) {
+ native_setup(new WeakReference<>(this), ActivityThread.currentPackageName(),
+ attributionSourceState.getParcel());
+ }
}
/**
@@ -1898,14 +1901,15 @@ public class MediaRecorder implements AudioRouting,
publicAlternatives = "{@link MediaRecorder}")
private void native_setup(Object mediarecorderThis,
String clientName, String opPackageName) throws IllegalStateException {
- Identity identity = myIdentity(null);
- identity.packageName = opPackageName;
-
- native_setup(mediarecorderThis, clientName, identity);
+ AttributionSource attributionSource = AttributionSource.myAttributionSource()
+ .withPackageName(opPackageName);
+ try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
+ native_setup(mediarecorderThis, clientName, attributionSourceState.getParcel());
+ }
}
private native void native_setup(Object mediarecorderThis,
- String clientName, Identity identity)
+ String clientName, @NonNull Parcel attributionSource)
throws IllegalStateException;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index fd3c4057ad21..70bb9608f1c2 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -16,8 +16,6 @@
package android.media.audiofx;
-import static android.media.permission.PermissionUtil.myIdentity;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -26,10 +24,11 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
+import android.content.AttributionSource.ScopedParcelState;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioSystem;
-import android.media.permission.Identity;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -518,10 +517,13 @@ public class AudioEffect {
// native initialization
// TODO b/182469354: Make consistent with AudioRecord
- int initResult = native_setup(new WeakReference<AudioEffect>(this),
- type.toString(), uuid.toString(), priority, audioSession,
- deviceType, deviceAddress,
- id, desc, myIdentity(null), probe);
+ int initResult;
+ try (ScopedParcelState attributionSourceState = AttributionSource.myAttributionSource()
+ .asScopedParcelState()) {
+ initResult = native_setup(new WeakReference<>(this), type.toString(), uuid.toString(),
+ priority, audioSession, deviceType, deviceAddress, id, desc,
+ attributionSourceState.getParcel(), probe);
+ }
if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
Log.e(TAG, "Error code " + initResult
+ " when initializing AudioEffect.");
@@ -1388,7 +1390,7 @@ public class AudioEffect {
private native final int native_setup(Object audioeffect_this, String type,
String uuid, int priority, int audioSession,
int deviceType, String deviceAddress, int[] id, Object[] desc,
- Identity identity, boolean probe);
+ @NonNull Parcel attributionSource, boolean probe);
private native final void native_finalize();
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 58c9e650bb90..3349277ede3b 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -16,12 +16,13 @@
package android.media.audiofx;
-import static android.media.permission.PermissionUtil.myIdentity;
-
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
-import android.media.permission.Identity;
+import android.content.AttributionSource;
+import android.content.AttributionSource.ScopedParcelState;
import android.os.Handler;
import android.os.Looper;
+import android.os.Parcel;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -222,8 +223,12 @@ public class Visualizer {
// native initialization
// TODO b/182469354: make consistent with AudioRecord
- int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id,
- myIdentity(null));
+ int result;
+ try (ScopedParcelState attributionSourceState = AttributionSource.myAttributionSource()
+ .asScopedParcelState()) {
+ result = native_setup(new WeakReference<>(this), audioSession, id,
+ attributionSourceState.getParcel());
+ }
if (result != SUCCESS && result != ALREADY_EXISTS) {
Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
switch (result) {
@@ -690,7 +695,7 @@ public class Visualizer {
private native final int native_setup(Object audioeffect_this,
int audioSession,
int[] id,
- Identity identity);
+ @NonNull Parcel attributionSource);
@GuardedBy("mStateLock")
private native final void native_finalize();
diff --git a/media/java/android/media/permission/PermissionUtil.java b/media/java/android/media/permission/PermissionUtil.java
index 92fe8820570c..b08d111db89c 100644
--- a/media/java/android/media/permission/PermissionUtil.java
+++ b/media/java/android/media/permission/PermissionUtil.java
@@ -51,24 +51,6 @@ import java.util.Objects;
* @hide
*/
public class PermissionUtil {
- /**
- * Create an identity for the current process and the passed context.
- *
- * @param context The process the identity is for. If {@code null}, the process's default
- * identity is chosen.
- * @return The identity for the current process and context
- */
- public static @NonNull Identity myIdentity(@Nullable Context context) {
- Identity identity = new Identity();
-
- identity.pid = Process.myPid();
- identity.uid = Process.myUid();
- identity.packageName = context != null ? context.getOpPackageName()
- : ActivityThread.currentOpPackageName();
- identity.attributionTag = context != null ? context.getAttributionTag() : null;
-
- return identity;
- }
/**
* Authenticate an originator, where the binder call is coming from a middleman.
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index d49790e3ecd3..bc73f6ad1ad2 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -51,6 +51,7 @@ cc_library_shared {
shared_libs: [
"audioclient-types-aidl-cpp",
"av-types-aidl-cpp",
+ "framework-permission-aidl-cpp",
"libandroid_runtime",
"libaudioclient",
"libnativehelper",
@@ -85,7 +86,6 @@ cc_library_shared {
"android.hardware.drm@1.4",
"android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
- "media_permission-aidl-cpp",
],
header_libs: [
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index a3607597f05e..2636ab227646 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -17,7 +17,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer-JNI"
-#include "permission_utils.h"
#include "utils/Log.h"
#include <media/mediaplayer.h>
@@ -80,8 +79,6 @@ static StateExceptionFields gStateExceptionFields;
using namespace android;
using media::VolumeShaper;
-using media::permission::Identity;
-using media::permission::convertIdentity;
// ----------------------------------------------------------------------------
@@ -949,11 +946,14 @@ android_media_MediaPlayer_native_init(JNIEnv *env)
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jobject jIdentity)
+ jobject jAttributionSource)
{
ALOGV("native_setup");
- sp<MediaPlayer> mp = new MediaPlayer(convertIdentity(env, jIdentity));
+ Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
+ android::content::AttributionSourceState attributionSource;
+ attributionSource.readFromParcel(parcel);
+ sp<MediaPlayer> mp = new MediaPlayer(attributionSource);
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
@@ -1409,7 +1409,7 @@ static const JNINativeMethod gMethods[] = {
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
- {"native_setup", "(Ljava/lang/Object;Landroid/media/permission/Identity;)V",(void *)android_media_MediaPlayer_native_setup},
+ {"native_setup", "(Ljava/lang/Object;Landroid/os/Parcel;)V",(void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
{"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 66411233216f..7ef0f7709408 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -24,7 +24,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaRecorderJNI"
-#include "permission_utils.h"
#include <utils/Log.h>
#include <gui/Surface.h>
@@ -46,13 +45,13 @@
#include <system/audio.h>
#include <android_runtime/android_view_Surface.h>
+#include <android/content/AttributionSourceState.h>
+#include <android_os_Parcel.h>
// ----------------------------------------------------------------------------
using namespace android;
-using android::media::permission::convertIdentity;
-
// ----------------------------------------------------------------------------
// helper function to extract a native Camera object from a Camera Java object
@@ -620,11 +619,14 @@ android_media_MediaRecorder_native_init(JNIEnv *env)
static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jstring packageName, jobject jIdentity)
+ jstring packageName, jobject jAttributionSource)
{
ALOGV("setup");
- sp<MediaRecorder> mr = new MediaRecorder(convertIdentity(env, jIdentity));
+ Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
+ android::content::AttributionSourceState attributionSource;
+ attributionSource.readFromParcel(parcel);
+ sp<MediaRecorder> mr = new MediaRecorder(attributionSource);
if (mr == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
@@ -871,7 +873,7 @@ static const JNINativeMethod gMethods[] = {
{"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
{"native_init", "()V", (void *)android_media_MediaRecorder_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Landroid/media/permission/Identity;)V",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Landroid/os/Parcel;)V",
(void *)android_media_MediaRecorder_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize},
{"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index bfed983c8bce..2ddfacf3884a 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -19,6 +19,7 @@ cc_library_shared {
],
shared_libs: [
+ "framework-permission-aidl-cpp",
"liblog",
"libcutils",
"libutils",
@@ -27,11 +28,11 @@ cc_library_shared {
"libaudioclient",
"libaudioutils",
"libaudiofoundation",
- "media_permission-aidl-cpp",
+ "libbinder"
],
export_shared_lib_headers: [
- "media_permission-aidl-cpp",
+ "framework-permission-aidl-cpp",
],
version_script: "exports.lds",
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 8a52456849f0..84a8d5122470 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -28,14 +28,16 @@
#include <cutils/bitops.h>
#include <utils/Thread.h>
+#include <android/content/AttributionSourceState.h>
+
#include "Visualizer.h"
namespace android {
// ---------------------------------------------------------------------------
-Visualizer::Visualizer (const Identity& identity)
- : AudioEffect(identity)
+Visualizer::Visualizer (const android::content::AttributionSourceState& attributionSource)
+ : AudioEffect(attributionSource)
{
}
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index 3ee91f0f8b1e..aa07ce8055ae 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -20,9 +20,7 @@
#include <media/AudioEffect.h>
#include <system/audio_effects/effect_visualizer.h>
#include <utils/Thread.h>
-#include "android/media/permission/Identity.h"
-
-using namespace android::media::permission;
+#include "android/content/AttributionSourceState.h"
/**
* The Visualizer class enables application to retrieve part of the currently playing audio for
@@ -68,9 +66,9 @@ public:
/* Constructor.
* See AudioEffect constructor for details on parameters.
*/
- explicit Visualizer(const Identity& identity);
+ explicit Visualizer(const android::content::AttributionSourceState& attributionSource);
- ~Visualizer();
+ ~Visualizer();
/**
* Initialize an uninitialized Visualizer.
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 953b7e01c983..3a8decdad18f 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -25,7 +25,9 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include "media/AudioEffect.h"
-#include "permission_utils.h"
+
+#include <android/content/AttributionSourceState.h>
+#include <android_os_Parcel.h>
#include <nativehelper/ScopedUtfChars.h>
@@ -35,8 +37,6 @@
using namespace android;
-using media::permission::convertIdentity;
-
#define AUDIOEFFECT_SUCCESS 0
#define AUDIOEFFECT_ERROR (-1)
#define AUDIOEFFECT_ERROR_ALREADY_EXISTS (-2)
@@ -273,7 +273,7 @@ static jint
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jstring type, jstring uuid, jint priority, jint sessionId,
jint deviceType, jstring deviceAddress,
- jintArray jId, jobjectArray javadesc, jobject jIdentity, jboolean probe)
+ jintArray jId, jobjectArray javadesc, jobject jAttributionSource, jboolean probe)
{
ALOGV("android_media_AudioEffect_native_setup");
AudioEffectJniStorage* lpJniStorage = NULL;
@@ -285,6 +285,8 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
effect_descriptor_t desc;
jobject jdesc;
AudioDeviceTypeAddr device;
+ AttributionSourceState attributionSource;
+ Parcel* parcel = NULL;
setAudioEffect(env, thiz, 0);
@@ -338,7 +340,9 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t
}
// create the native AudioEffect object
- lpAudioEffect = new AudioEffect(convertIdentity(env, jIdentity));
+ parcel = parcelForJavaObject(env, jAttributionSource);
+ attributionSource.readFromParcel(parcel);
+ lpAudioEffect = new AudioEffect(attributionSource);
if (lpAudioEffect == 0) {
ALOGE("Error creating AudioEffect");
goto setup_failure;
@@ -774,7 +778,7 @@ android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz _
// Dalvik VM type signatures
static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Landroid/media/permission/Identity;Z)I",
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Landroid/os/Parcel;Z)I",
(void *)android_media_AudioEffect_native_setup},
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 439715cbb811..b30f00fdf7de 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -25,12 +25,16 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
#include "Visualizer.h"
-#include "permission_utils.h"
#include <nativehelper/ScopedUtfChars.h>
+#include <android/content/AttributionSourceState.h>
+#include <android_os_Parcel.h>
+
using namespace android;
+using content::AttributionSourceState;
+
#define VISUALIZER_SUCCESS 0
#define VISUALIZER_ERROR (-1)
#define VISUALIZER_ERROR_ALREADY_EXISTS (-2)
@@ -348,13 +352,15 @@ static void android_media_visualizer_effect_callback(int32_t event,
static jint
android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jint sessionId, jintArray jId, jobject jIdentity)
+ jint sessionId, jintArray jId, jobject jAttributionSource)
{
ALOGV("android_media_visualizer_native_setup");
VisualizerJniStorage* lpJniStorage = NULL;
int lStatus = VISUALIZER_ERROR_NO_MEMORY;
sp<Visualizer> lpVisualizer;
jint* nId = NULL;
+ AttributionSourceState attributionSource;
+ Parcel* parcel = nullptr;
setVisualizer(env, thiz, 0);
@@ -381,7 +387,9 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
}
// create the native Visualizer object
- lpVisualizer = new Visualizer(convertIdentity(env, jIdentity));
+ parcel = parcelForJavaObject(env, jAttributionSource);
+ attributionSource.readFromParcel(parcel);
+ lpVisualizer = sp<Visualizer>::make(attributionSource);
if (lpVisualizer == 0) {
ALOGE("Error creating Visualizer");
goto setup_failure;
@@ -678,7 +686,7 @@ android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean
// Dalvik VM type signatures
static const JNINativeMethod gMethods[] = {
{"native_init", "()V", (void *)android_media_visualizer_native_init},
- {"native_setup", "(Ljava/lang/Object;I[ILandroid/media/permission/Identity;)I",
+ {"native_setup", "(Ljava/lang/Object;I[ILandroid/os/Parcel;)I",
(void *)android_media_visualizer_native_setup},
{"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
{"native_release", "()V", (void *)android_media_visualizer_native_release},
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
index 4227cd8cbb29..ee473f5deb15 100644
--- a/media/jni/soundpool/Android.bp
+++ b/media/jni/soundpool/Android.bp
@@ -126,6 +126,7 @@ cc_library_shared {
],
shared_libs: [
+ "framework-permission-aidl-cpp",
"libaudioutils",
"liblog",
"libcutils",
@@ -135,7 +136,6 @@ cc_library_shared {
"libaudioclient",
"libmediandk",
"libbinder",
- "media_permission-aidl-cpp",
],
cflags: [
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 95fe000bd2c2..bbbef3853b23 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -17,7 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool::Stream"
#include <utils/Log.h>
-#include<android/media/permission/Identity.h>
+#include <android/content/AttributionSourceState.h>
#include "Stream.h"
@@ -25,8 +25,6 @@
namespace android::soundpool {
-using media::permission::Identity;
-
Stream::~Stream()
{
ALOGV("%s(%p)", __func__, this);
@@ -330,15 +328,16 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
// do not create a new audio track if current track is compatible with sound parameters
- Identity identity = Identity();
- identity.packageName = mStreamManager->getOpPackageName();
+ android::content::AttributionSourceState attributionSource;
+ attributionSource.packageName = mStreamManager->getOpPackageName();
+ attributionSource.token = sp<BBinder>::make();
// TODO b/182469354 make consistent with AudioRecord, add util for native source
newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
staticCallback, userData,
0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
AudioTrack::TRANSFER_DEFAULT,
- nullptr /*offloadInfo*/, identity,
+ nullptr /*offloadInfo*/, attributionSource,
mStreamManager->getAttributes(),
false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
// Set caller name so it can be logged in destructor.
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 562f8f449833..84a24d86d84c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -806,7 +806,8 @@ public final class ActiveServices {
}
mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
- true, false, null, false);
+ true, false, null, false, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -1875,7 +1876,8 @@ public final class ActiveServices {
mAm.mAppOpsService.startOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
- null, true, false, "", false);
+ null, true, false, "", false, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b518490d3adb..172c3f7dc7c2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -156,6 +156,7 @@ import android.app.ActivityThread;
import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributionFlags;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
@@ -346,6 +347,7 @@ import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
+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;
@@ -353,6 +355,7 @@ 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;
import com.android.server.AlarmManagerInternal;
import com.android.server.DeviceIdleInternal;
import com.android.server.DisplayThread;
@@ -1833,7 +1836,9 @@ public class ActivityManagerService extends IActivityManager.Stub
final int[] cameraOp = {AppOpsManager.OP_CAMERA};
mAppOpsService.startWatchingActive(cameraOp, 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) {
cameraActiveChanged(uid, active);
}
});
@@ -16851,7 +16856,7 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
return superImpl.apply(code, new AttributionSource(shellUid,
"com.android.shell", attributionSource.getAttributionTag(),
- attributionSource.getNext()),
+ attributionSource.getToken(), attributionSource.getNext()),
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
skiProxyOperation);
} finally {
@@ -16867,8 +16872,9 @@ public class ActivityManagerService extends IActivityManager.Stub
@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) {
+ @AttributionFlags int attributionFlags, int attributionChainId,
+ @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
+ Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
@@ -16876,57 +16882,62 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
return superImpl.apply(token, code, shellUid, "com.android.shell",
attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage);
+ shouldCollectMessage, attributionFlags, attributionChainId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ attributionFlags, attributionChainId);
}
@Override
- public SyncNotedAppOp startProxyOperation(IBinder token, int code,
+ public SyncNotedAppOp startProxyOperation(int code,
@NonNull AttributionSource attributionSource, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProsyOperation, @NonNull OctFunction<IBinder, Integer,
- AttributionSource, Boolean, Boolean, String, Boolean, Boolean,
- SyncNotedAppOp> superImpl) {
+ boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
+ @AttributionFlags int proxiedAttributionFlags, int attributionChainId,
+ @NonNull DecFunction<Integer, AttributionSource, Boolean, Boolean, String, Boolean,
+ Boolean, Integer, Integer, Integer, SyncNotedAppOp> superImpl) {
if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(
attributionSource.getUid()), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(token, code, new AttributionSource(shellUid,
- "com.android.shell", attributionSource.getAttributionTag(),
- attributionSource.getNext()), startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- skipProsyOperation);
+ return superImpl.apply(code, new AttributionSource(shellUid,
+ "com.android.shell", attributionSource.getAttributionTag(),
+ attributionSource.getToken(), attributionSource.getNext()),
+ startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
+ proxiedAttributionFlags, attributionChainId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(token, code, attributionSource, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProsyOperation);
+ return superImpl.apply(code, attributionSource, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation,
+ proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
}
@Override
- public void finishProxyOperation(IBinder clientId, int code,
- @NonNull AttributionSource attributionSource,
- @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
+ public void finishProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean skipProxyOperation, @NonNull TriFunction<Integer, AttributionSource,
+ Boolean, Void> superImpl) {
if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(
attributionSource.getUid()), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- superImpl.apply(clientId, code, new AttributionSource(shellUid,
+ superImpl.apply(code, new AttributionSource(shellUid,
"com.android.shell", attributionSource.getAttributionTag(),
- attributionSource.getNext()));
+ attributionSource.getToken(), attributionSource.getNext()),
+ skipProxyOperation);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- superImpl.apply(clientId, code, attributionSource);
+ superImpl.apply(code, attributionSource, skipProxyOperation);
}
private boolean isTargetOp(int code) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 541dcdc94a51..fdb3d8ca371c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -84,6 +84,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributionFlags;
import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.Mode;
@@ -200,6 +201,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public class AppOpsService extends IAppOpsService.Stub {
@@ -257,11 +259,6 @@ public class AppOpsService extends IAppOpsService.Stub {
private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
- //TODO: remove this when development is done.
- private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
- private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
-
-
final Context mContext;
final AtomicFile mFile;
private final @Nullable File mNoteOpCallerStacktracesFile;
@@ -414,9 +411,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
- @NonNull Runnable onDeath, int proxyUid, @Nullable String proxyPackageName,
- @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
- @OpFlags int flags) throws RemoteException {
+ @Nullable String attributionTag, @NonNull Runnable onDeath, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @OpFlags int flags, @AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
InProgressStartOpEvent recycled = acquire();
@@ -427,13 +425,14 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (recycled != null) {
- recycled.reinit(startTime, elapsedTime, clientId, onDeath, uidState, flags,
- proxyInfo, mOpEventProxyInfoPool);
+ recycled.reinit(startTime, elapsedTime, clientId, attributionTag, onDeath,
+ uidState, flags, proxyInfo, attributionFlags, attributionChainId,
+ mOpEventProxyInfoPool);
return recycled;
}
- return new InProgressStartOpEvent(startTime, elapsedTime, clientId, onDeath, uidState,
- proxyInfo, flags);
+ return new InProgressStartOpEvent(startTime, elapsedTime, clientId, attributionTag,
+ onDeath, uidState, proxyInfo, flags, attributionFlags, attributionChainId);
}
}
@@ -685,6 +684,9 @@ public class AppOpsService extends IAppOpsService.Stub {
/** Id of the client that started the event */
private @NonNull IBinder mClientId;
+ /** The attribution tag for this operation */
+ private @Nullable String mAttributionTag;
+
/** To call when client dies */
private @NonNull Runnable mOnDeath;
@@ -700,30 +702,44 @@ public class AppOpsService extends IAppOpsService.Stub {
/** How many times the op was started but not finished yet */
int numUnfinishedStarts;
+ /** The attribution flags related to this event */
+ private @AttributionFlags int mAttributionFlags;
+
+ /** The id of the attribution chain this even is a part of */
+ private int mAttributionChainId;
+
/**
* Create a new {@link InProgressStartOpEvent}.
*
* @param startTime The time {@link #startOperation} was called
* @param startElapsedTime The elapsed time when {@link #startOperation} was called
* @param clientId The client id of the caller of {@link #startOperation}
+ * @param attributionTag The attribution tag for the operation.
* @param onDeath The code to execute on client death
* @param uidState The uidstate of the app {@link #startOperation} was called for
+ * @param attributionFlags the attribution flags for this operation.
+ * @param attributionChainId the unique id of the attribution chain this op is a part of.
* @param proxy The proxy information, if {@link #startProxyOperation} was called
* @param flags The trusted/nontrusted/self flags.
*
* @throws RemoteException If the client is dying
*/
private InProgressStartOpEvent(long startTime, long startElapsedTime,
- @NonNull IBinder clientId, @NonNull Runnable onDeath,
- @AppOpsManager.UidState int uidState, @Nullable OpEventProxyInfo proxy,
- @OpFlags int flags) throws RemoteException {
+ @NonNull IBinder clientId, @Nullable String attributionTag,
+ @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState,
+ @Nullable OpEventProxyInfo proxy, @OpFlags int flags,
+ @AttributionFlags int attributionFlags, int attributionChainId)
+ throws RemoteException {
mStartTime = startTime;
mStartElapsedTime = startElapsedTime;
mClientId = clientId;
+ mAttributionTag = attributionTag;
mOnDeath = onDeath;
mUidState = uidState;
mProxy = proxy;
mFlags = flags;
+ mAttributionFlags = attributionFlags;
+ mAttributionChainId = attributionChainId;
clientId.linkToDeath(this, 0);
}
@@ -744,21 +760,27 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param startTime The time {@link #startOperation} was called
* @param startElapsedTime The elapsed time when {@link #startOperation} was called
* @param clientId The client id of the caller of {@link #startOperation}
+ * @param attributionTag The attribution tag for this operation.
* @param onDeath The code to execute on client death
* @param uidState The uidstate of the app {@link #startOperation} was called for
* @param flags The flags relating to the proxy
* @param proxy The proxy information, if {@link #startProxyOperation} was called
+ * @param attributionFlags the attribution flags for this operation.
+ * @param attributionChainId the unique id of the attribution chain this op is a part of.
* @param proxyPool The pool to release previous {@link OpEventProxyInfo} to
*
* @throws RemoteException If the client is dying
*/
public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
- @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState, @OpFlags int flags,
- @Nullable OpEventProxyInfo proxy, @NonNull Pools.Pool<OpEventProxyInfo> proxyPool
+ @Nullable String attributionTag, @NonNull Runnable onDeath,
+ @AppOpsManager.UidState int uidState, @OpFlags int flags,
+ @Nullable OpEventProxyInfo proxy, @AttributionFlags int attributionFlags,
+ int attributionChainId, @NonNull Pools.Pool<OpEventProxyInfo> proxyPool
) throws RemoteException {
mStartTime = startTime;
mStartElapsedTime = startElapsedTime;
mClientId = clientId;
+ mAttributionTag = attributionTag;
mOnDeath = onDeath;
mUidState = uidState;
mFlags = flags;
@@ -767,6 +789,8 @@ public class AppOpsService extends IAppOpsService.Stub {
proxyPool.release(mProxy);
}
mProxy = proxy;
+ mAttributionFlags = attributionFlags;
+ mAttributionChainId = attributionChainId;
clientId.linkToDeath(this, 0);
}
@@ -791,7 +815,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return mUidState;
}
- /** @return proxy info for the access */
+ /** @return proxy tag for the access */
public @Nullable OpEventProxyInfo getProxy() {
return mProxy;
}
@@ -800,6 +824,16 @@ public class AppOpsService extends IAppOpsService.Stub {
public @OpFlags int getFlags() {
return mFlags;
}
+
+ /** @return attributoin flags used for the access */
+ public @AttributionFlags int getAttributionFlags() {
+ return mAttributionFlags;
+ }
+
+ /** @return attributoin chiang id for the access */
+ public int getAttributionChainId() {
+ return mAttributionChainId;
+ }
}
private final class AttributedOp {
@@ -943,32 +977,38 @@ public class AppOpsService extends IAppOpsService.Stub {
* @param proxyAttributionTag The attribution tag of the proxy app
* @param uidState UID state of the app startOp is called for
* @param flags The proxy flags
+ * @param attributionFlags The attribution flags associated with this operation.
+ * @param attributionChainId The if of the attribution chain this operations is a part of.
*/
public void started(@NonNull IBinder clientId, int proxyUid,
@Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags) throws RemoteException {
- started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags,
- true);
+ @AppOpsManager.UidState int uidState, @OpFlags int flags, @AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
+ started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState, flags,/*triggerCallbackIfNeeded*/ true, attributionFlags,
+ attributionChainId);
}
private void started(@NonNull IBinder clientId, int proxyUid,
@Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
@AppOpsManager.UidState int uidState, @OpFlags int flags,
- boolean triggerCallbackIfNeeded) throws RemoteException {
- startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
- uidState, flags, triggerCallbackIfNeeded, true);
+ boolean triggerCallbackIfNeeded, @AttributionFlags int attributionFlags,
+ int attributionChainId) throws RemoteException {
+ startedOrPaused(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState, flags, triggerCallbackIfNeeded,
+ /*triggerCallbackIfNeeded*/ true, attributionFlags, attributionChainId);
}
private void startedOrPaused(@NonNull IBinder clientId, int proxyUid,
@Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
@AppOpsManager.UidState int uidState, @OpFlags int flags,
- boolean triggerCallbackIfNeeded, boolean isStarted) throws RemoteException {
+ boolean triggerCallbackIfNeeded, boolean isStarted, @AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
if (triggerCallbackIfNeeded && !parent.isRunning() && isStarted) {
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, true);
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
+ tag, true, attributionFlags, attributionChainId);
}
-
if (isStarted && mInProgressEvents == null) {
mInProgressEvents = new ArrayMap<>(1);
} else if (mPausedInProgressEvents == null) {
@@ -981,9 +1021,10 @@ public class AppOpsService extends IAppOpsService.Stub {
InProgressStartOpEvent event = events.get(clientId);
if (event == null) {
event = mInProgressStartOpEventPool.acquire(startTime,
- SystemClock.elapsedRealtime(), clientId,
+ SystemClock.elapsedRealtime(), clientId, tag,
PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
- proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags);
+ proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags,
+ attributionFlags, attributionChainId);
events.put(clientId, event);
} else {
if (uidState != event.mUidState) {
@@ -994,10 +1035,10 @@ public class AppOpsService extends IAppOpsService.Stub {
event.numUnfinishedStarts++;
if (isStarted) {
+ // TODO: Consider storing the attribution chain flags and id
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, tag, uidState, flags, startTime);
}
-
}
/**
@@ -1063,7 +1104,8 @@ public class AppOpsService extends IAppOpsService.Stub {
// TODO ntmyren: Also callback for single attribution tag activity changes
if (triggerCallbackIfNeeded && !parent.isRunning()) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, false);
+ parent.packageName, tag, false, event.getAttributionFlags(),
+ event.getAttributionChainId());
}
}
}
@@ -1103,9 +1145,10 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
public void createPaused(@NonNull IBinder clientId, int proxyUid,
@Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags) throws RemoteException {
+ @AppOpsManager.UidState int uidState, @OpFlags int flags, @AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
- uidState, flags, true, false);
+ uidState, flags, true, false, attributionFlags, attributionChainId);
}
/**
@@ -1124,9 +1167,11 @@ public class AppOpsService extends IAppOpsService.Stub {
InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
mPausedInProgressEvents.put(event.mClientId, event);
finishOrPause(event.mClientId, true, true);
+
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, tag, false,
+ event.getAttributionFlags(), event.getAttributionChainId());
}
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, false);
mInProgressEvents = null;
}
@@ -1153,10 +1198,10 @@ public class AppOpsService extends IAppOpsService.Stub {
event.mStartTime = startTime;
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
parent.packageName, tag, event.mUidState, event.mFlags, startTime);
- }
- if (shouldSendActive) {
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, true);
+ if (shouldSendActive) {
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
+ tag, true, event.getAttributionFlags(), event.getAttributionChainId());
+ }
}
mPausedInProgressEvents = null;
}
@@ -1210,10 +1255,12 @@ public class AppOpsService extends IAppOpsService.Stub {
// previously removed unfinished start counts back
if (proxy != null) {
started(event.getClientId(), proxy.getUid(), proxy.getPackageName(),
- proxy.getAttributionTag(), newState, event.getFlags(), false);
+ proxy.getAttributionTag(), newState, event.getFlags(), false,
+ event.getAttributionFlags(), event.getAttributionChainId());
} else {
started(event.getClientId(), Process.INVALID_UID, null, null, newState,
- OP_FLAG_SELF, false);
+ OP_FLAG_SELF, false, event.getAttributionFlags(),
+ event.getAttributionChainId());
}
InProgressStartOpEvent newEvent = mInProgressEvents.get(binders.get(i));
@@ -3321,9 +3368,10 @@ public class AppOpsService extends IAppOpsService.Stub {
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + packageName + "flags: " +
+ AppOpsManager.flagsToString(flags));
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
- packageName);
+ packageName + " flags: " + AppOpsManager.flagsToString(flags));
}
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3349,7 +3397,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
- + packageName);
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
uidMode);
@@ -3362,7 +3410,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
- + packageName);
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
mode);
@@ -3373,7 +3421,8 @@ public class AppOpsService extends IAppOpsService.Stub {
Slog.d(TAG,
"noteOperation: allowing code " + code + " uid " + uid + " package "
+ packageName + (attributionTag == null ? ""
- : "." + attributionTag));
+ : "." + attributionTag) + " flags: "
+ + AppOpsManager.flagsToString(flags));
}
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_ALLOWED);
@@ -3674,16 +3723,18 @@ public class AppOpsService extends IAppOpsService.Stub {
public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
@Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
- String message, boolean shouldCollectMessage) {
+ String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId) {
return mCheckOpsDelegateDispatcher.startOperation(token, code, uid, packageName,
attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage);
+ shouldCollectMessage, attributionFlags, attributionChainId);
}
private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code, int uid,
@Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @NonNull String message,
- boolean shouldCollectMessage) {
+ boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
@@ -3707,29 +3758,36 @@ public class AppOpsService extends IAppOpsService.Stub {
}
return startOperationUnchecked(clientId, code, uid, packageName, attributionTag,
Process.INVALID_UID, null, null, OP_FLAG_SELF, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, false);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
+ attributionChainId, /*dryRun*/ false);
}
@Override
- public SyncNotedAppOp startProxyOperation(IBinder clientId, int code,
+ public SyncNotedAppOp startProxyOperation(int code,
@NonNull AttributionSource attributionSource, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProxyOperation) {
- return mCheckOpsDelegateDispatcher.startProxyOperation(clientId, code, attributionSource,
+ boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
+ @AttributionFlags int proxiedAttributionFlags, int attributionChainId) {
+ return mCheckOpsDelegateDispatcher.startProxyOperation(code, attributionSource,
startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- skipProxyOperation);
+ skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags,
+ attributionChainId);
}
- private SyncNotedAppOp startProxyOperationImpl(IBinder clientId, int code,
+ private SyncNotedAppOp startProxyOperationImpl(int code,
@NonNull AttributionSource attributionSource,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage, boolean skipProxyOperation) {
+ boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags
+ int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags,
+ int attributionChainId) {
final int proxyUid = attributionSource.getUid();
final String proxyPackageName = attributionSource.getPackageName();
final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final IBinder proxyToken = attributionSource.getToken();
final int proxiedUid = attributionSource.getNextUid();
final String proxiedPackageName = attributionSource.getNextPackageName();
final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+ final IBinder proxiedToken = attributionSource.getNextToken();
verifyIncomingProxyUid(attributionSource);
verifyIncomingOp(code);
@@ -3762,10 +3820,11 @@ public class AppOpsService extends IAppOpsService.Stub {
if (!skipProxyOperation) {
// Test if the proxied operation will succeed before starting the proxy operation
- final SyncNotedAppOp testProxiedOp = startOperationUnchecked(clientId, code, proxiedUid,
- resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
+ final SyncNotedAppOp testProxiedOp = startOperationUnchecked(proxiedToken, code,
+ proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag, proxyUid,
resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, true);
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ proxiedAttributionFlags, attributionChainId, /*dryRun*/ true);
if (!shouldStartForMode(testProxiedOp.getOpMode(), startIfModeDefault)) {
return testProxiedOp;
}
@@ -3773,19 +3832,21 @@ public class AppOpsService extends IAppOpsService.Stub {
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
- final SyncNotedAppOp proxyAppOp = startOperationUnchecked(clientId, code, proxyUid,
+ final SyncNotedAppOp proxyAppOp = startOperationUnchecked(proxyToken, code, proxyUid,
resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message,
- shouldCollectMessage, false);
+ shouldCollectMessage, proxyAttributionFlags, attributionChainId,
+ /*dryRun*/ false);
if (!shouldStartForMode(proxyAppOp.getOpMode(), startIfModeDefault)) {
return proxyAppOp;
}
}
- return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
+ return startOperationUnchecked(proxiedToken, code, proxiedUid, resolvedProxiedPackageName,
proxiedAttributionTag, proxyUid, resolvedProxyPackageName, proxyAttributionTag,
proxiedFlags, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, false);
+ shouldCollectMessage, proxiedAttributionFlags, attributionChainId,
+ /*dryRun*/ false);
}
private boolean shouldStartForMode(int mode, boolean startIfModeDefault) {
@@ -3796,7 +3857,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@NonNull String packageName, @Nullable String attributionTag, int proxyUid,
String proxyPackageName, @Nullable String proxyAttributionTag, @OpFlags int flags,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, boolean dryRun) {
+ boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId, boolean dryRun) {
RestrictionBypass bypass;
try {
bypass = verifyAndGetBypass(uid, packageName, attributionTag, proxyPackageName);
@@ -3818,7 +3880,8 @@ public class AppOpsService extends IAppOpsService.Stub {
flags, AppOpsManager.MODE_IGNORED);
}
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + packageName + " flags: "
+ + AppOpsManager.flagsToString(flags));
return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
packageName);
}
@@ -3835,7 +3898,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
- + packageName);
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
}
if (!dryRun) {
attributedOp.rejected(uidState.state, flags);
@@ -3852,7 +3915,7 @@ public class AppOpsService extends IAppOpsService.Stub {
&& (!startIfModeDefault || mode != MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
- + packageName);
+ + packageName + " flags: " + AppOpsManager.flagsToString(flags));
if (!dryRun) {
attributedOp.rejected(uidState.state, flags);
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
@@ -3862,15 +3925,18 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName + " restricted: " + isRestricted);
+ + " package " + packageName + " restricted: " + isRestricted
+ + " flags: " + AppOpsManager.flagsToString(flags));
if (!dryRun) {
try {
if (isRestricted) {
attributedOp.createPaused(clientId, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState.state, flags);
+ proxyAttributionTag, uidState.state, flags, attributionFlags,
+ attributionChainId);
} else {
attributedOp.started(clientId, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState.state, flags);
+ proxyAttributionTag, uidState.state, flags, attributionFlags,
+ attributionChainId);
}
} catch (RemoteException e) {
throw new RuntimeException(e);
@@ -3905,21 +3971,26 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
- public void finishProxyOperation(IBinder clientId, int code,
- @NonNull AttributionSource attributionSource) {
- mCheckOpsDelegateDispatcher.finishProxyOperation(clientId, code, attributionSource);
+ public void finishProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean skipProxyOperation) {
+ mCheckOpsDelegateDispatcher.finishProxyOperation(code, attributionSource,
+ skipProxyOperation);
}
- private Void finishProxyOperationImpl(IBinder clientId, int code,
- @NonNull AttributionSource attributionSource) {
+ private Void finishProxyOperationImpl(int code, @NonNull AttributionSource attributionSource,
+ boolean skipProxyOperation) {
final int proxyUid = attributionSource.getUid();
final String proxyPackageName = attributionSource.getPackageName();
final String proxyAttributionTag = attributionSource.getAttributionTag();
+ final IBinder proxyToken = attributionSource.getToken();
final int proxiedUid = attributionSource.getNextUid();
final String proxiedPackageName = attributionSource.getNextPackageName();
final String proxiedAttributionTag = attributionSource.getNextAttributionTag();
+ final IBinder proxiedToken = attributionSource.getNextToken();
- verifyIncomingUid(proxyUid);
+ skipProxyOperation = resolveSkipProxyOperation(skipProxyOperation, attributionSource);
+
+ verifyIncomingProxyUid(attributionSource);
verifyIncomingOp(code);
verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
@@ -3930,8 +4001,10 @@ public class AppOpsService extends IAppOpsService.Stub {
return null;
}
- finishOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName,
- proxyAttributionTag);
+ if (!skipProxyOperation) {
+ finishOperationUnchecked(proxyToken, code, proxyUid, resolvedProxyPackageName,
+ proxyAttributionTag);
+ }
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
@@ -3939,7 +4012,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return null;
}
- finishOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName,
+ finishOperationUnchecked(proxiedToken, code, proxiedUid, resolvedProxiedPackageName,
proxiedAttributionTag);
return null;
@@ -3981,8 +4054,9 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- private void scheduleOpActiveChangedIfNeededLocked(int code, int uid, String packageName,
- boolean active) {
+ private void scheduleOpActiveChangedIfNeededLocked(int code, int uid, @NonNull
+ String packageName, @Nullable String attributionTag, boolean active, @AttributionFlags
+ int attributionFlags, int attributionChainId) {
ArraySet<ActiveCallback> dispatchedCallbacks = null;
final int callbackListCount = mActiveWatchers.size();
for (int i = 0; i < callbackListCount; i++) {
@@ -4003,11 +4077,13 @@ public class AppOpsService extends IAppOpsService.Stub {
}
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpActiveChanged,
- this, dispatchedCallbacks, code, uid, packageName, active));
+ this, dispatchedCallbacks, code, uid, packageName, attributionTag, active,
+ attributionFlags, attributionChainId));
}
private void notifyOpActiveChanged(ArraySet<ActiveCallback> callbacks,
- int code, int uid, String packageName, boolean active) {
+ int code, int uid, @NonNull String packageName, @Nullable String attributionTag,
+ boolean active, @AttributionFlags int attributionFlags, int attributionChainId) {
// There are features watching for mode changes such as window manager
// and location manager which are in our process. The callbacks in these
// features may require permissions our remote caller does not have.
@@ -4017,7 +4093,8 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int i = 0; i < callbackCount; i++) {
final ActiveCallback callback = callbacks.valueAt(i);
try {
- callback.mCallback.opActiveChanged(code, uid, packageName, active);
+ callback.mCallback.opActiveChanged(code, uid, packageName, attributionTag,
+ active, attributionFlags, attributionChainId);
} catch (RemoteException e) {
/* do nothing */
}
@@ -5023,6 +5100,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
static class Shell extends ShellCommand {
+ static final AtomicInteger sAttributionChainIds = new AtomicInteger(0);
+
final IAppOpsService mInterface;
final AppOpsService mInternal;
@@ -5491,7 +5570,9 @@ public class AppOpsService extends IAppOpsService.Stub {
if (shell.packageName != null) {
shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
shell.packageName, shell.attributionTag, true, true,
- "appops start shell command", true);
+ "appops start shell command", true,
+ AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR,
+ shell.sAttributionChainIds.incrementAndGet());
} else {
return -1;
}
@@ -6421,7 +6502,9 @@ public class AppOpsService extends IAppOpsService.Stub {
@NonNull String proxiedPackageName) {
Objects.requireNonNull(proxyPackageName);
Objects.requireNonNull(proxiedPackageName);
- Binder.withCleanCallingIdentity(() -> {
+ final long callingUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
+ try {
final List<AppOpsManager.PackageOps> packageOps = getOpsForPackage(proxiedUid,
proxiedPackageName, new int[] {op});
if (packageOps == null || packageOps.isEmpty()) {
@@ -6436,13 +6519,13 @@ public class AppOpsService extends IAppOpsService.Stub {
return false;
}
final OpEventProxyInfo proxyInfo = opEntry.getLastProxyInfo(
- AppOpsManager.OP_FLAG_TRUSTED_PROXY
- | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY);
- return proxyInfo != null && Binder.getCallingUid() == proxyInfo.getUid()
+ OP_FLAG_TRUSTED_PROXIED | AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED);
+ return proxyInfo != null && callingUid == proxyInfo.getUid()
&& proxyPackageName.equals(proxyInfo.getPackageName())
&& Objects.equals(proxyAttributionTag, proxyInfo.getAttributionTag());
- });
- return false;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
@@ -7275,89 +7358,101 @@ public class AppOpsService extends IAppOpsService.Stub {
public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
@Nullable String packageName, @NonNull String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
- @Nullable String message, boolean shouldCollectMessage) {
+ @Nullable String message, boolean shouldCollectMessage,
+ @AttributionFlags int attributionFlags, int attributionChainId) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
return mPolicy.startOperation(token, code, uid, packageName,
attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, this::startDelegateOperationImpl);
+ shouldCollectMessage, attributionFlags, attributionChainId,
+ this::startDelegateOperationImpl);
} else {
return mPolicy.startOperation(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, AppOpsService.this::startOperationImpl);
+ shouldCollectMessage, attributionFlags, attributionChainId,
+ AppOpsService.this::startOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
return startDelegateOperationImpl(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage);
+ shouldCollectMessage, attributionFlags, attributionChainId);
}
return startOperationImpl(token, code, uid, packageName, attributionTag,
- startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ attributionFlags, attributionChainId);
}
private SyncNotedAppOp startDelegateOperationImpl(IBinder token, int code, int uid,
@Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage) {
+ boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId) {
return mCheckOpsDelegate.startOperation(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- AppOpsService.this::startOperationImpl);
+ attributionFlags, attributionChainId, AppOpsService.this::startOperationImpl);
}
- public SyncNotedAppOp startProxyOperation(IBinder clientId, int code,
+ public SyncNotedAppOp startProxyOperation(int code,
@NonNull AttributionSource attributionSource, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProxyOperation) {
+ boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
+ @AttributionFlags int proxiedAttributionFlags, int attributionChainId) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- return mPolicy.startProxyOperation(clientId, code, attributionSource,
+ return mPolicy.startProxyOperation(code, attributionSource,
startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, skipProxyOperation,
+ shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
+ proxiedAttributionFlags, attributionChainId,
this::startDelegateProxyOperationImpl);
} else {
- return mPolicy.startProxyOperation(clientId, code, attributionSource,
+ return mPolicy.startProxyOperation(code, attributionSource,
startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, skipProxyOperation,
+ shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
+ proxiedAttributionFlags, attributionChainId,
AppOpsService.this::startProxyOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- return startDelegateProxyOperationImpl(clientId, code,
- attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, skipProxyOperation);
+ return startDelegateProxyOperationImpl(code, attributionSource,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
+ proxiedAttributionFlags, attributionChainId);
}
- return startProxyOperationImpl(clientId, code, attributionSource, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
+ return startProxyOperationImpl(code, attributionSource, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation,
+ proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
}
-
- private SyncNotedAppOp startDelegateProxyOperationImpl(IBinder token, int code,
+ private SyncNotedAppOp startDelegateProxyOperationImpl(int code,
@NonNull AttributionSource attributionSource, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProxyOperation) {
- return mCheckOpsDelegate.startProxyOperation(token, code, attributionSource,
+ boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
+ @AttributionFlags int proxiedAttributionFlsgs, int attributionChainId) {
+ return mCheckOpsDelegate.startProxyOperation(code, attributionSource,
startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- skipProxyOperation, AppOpsService.this::startProxyOperationImpl);
+ skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlsgs,
+ attributionChainId, AppOpsService.this::startProxyOperationImpl);
}
- public void finishProxyOperation(IBinder clientId, int code,
- @NonNull AttributionSource attributionSource) {
+ public void finishProxyOperation(int code,
+ @NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- mPolicy.finishProxyOperation(clientId, code, attributionSource,
- this::finishDelegateProxyOperationImpl);
+ mPolicy.finishProxyOperation(code, attributionSource,
+ skipProxyOperation, this::finishDelegateProxyOperationImpl);
} else {
- mPolicy.finishProxyOperation(clientId, code, attributionSource,
- AppOpsService.this::finishProxyOperationImpl);
+ mPolicy.finishProxyOperation(code, attributionSource,
+ skipProxyOperation, AppOpsService.this::finishProxyOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- finishDelegateProxyOperationImpl(clientId, code, attributionSource);
+ finishDelegateProxyOperationImpl(code, attributionSource, skipProxyOperation);
+ } else {
+ finishProxyOperationImpl(code, attributionSource, skipProxyOperation);
}
- finishProxyOperationImpl(clientId, code, attributionSource);
}
- private Void finishDelegateProxyOperationImpl(IBinder clientId, int code,
- @NonNull AttributionSource attributionSource) {
- mCheckOpsDelegate.finishProxyOperation(clientId, code, attributionSource,
+ private Void finishDelegateProxyOperationImpl(int code,
+ @NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
+ mCheckOpsDelegate.finishProxyOperation(code, attributionSource, skipProxyOperation,
AppOpsService.this::finishProxyOperationImpl);
return null;
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index c1209d466d20..62db886b90e9 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -154,7 +154,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
// must have the required permission and the users must be in the same profile group
// in order to launch any of its own activities.
if (callerUserId != userId) {
- final int permissionFlag = PermissionChecker.checkPermissionForPreflight(
+ final int permissionFlag = PermissionChecker.checkPermissionForPreflight(
mContext,
INTERACT_ACROSS_PROFILES,
callingPid,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 552351119393..24c095b8117a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
@@ -65,6 +66,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributionFlags;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.compat.annotation.ChangeId;
@@ -109,6 +111,7 @@ import android.os.storage.StorageManager;
import android.permission.IOnPermissionsChangeListener;
import android.permission.IPermissionChecker;
import android.permission.IPermissionManager;
+import android.permission.PermissionCheckerManager;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
@@ -172,6 +175,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
/**
@@ -3321,8 +3325,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
- return mAttributionSourceRegistry.registerAttributionSource(source);
+ public void registerAttributionSource(@NonNull AttributionSource source) {
+ mAttributionSourceRegistry.registerAttributionSource(source);
}
@Override
@@ -5288,8 +5292,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param permissionName the name of the permission to be checked
* @param userId the user ID
* @param superImpl the original implementation that can be delegated to
- * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
- * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise
+ * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
+ * the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
*
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
@@ -5303,8 +5307,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param uid the UID to be checked
* @param permissionName the name of the permission to be checked
* @param superImpl the original implementation that can be delegated to
- * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
- * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise
+ * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
+ * the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
*/
int checkUidPermission(int uid, @NonNull String permissionName,
BiFunction<Integer, String, Integer> superImpl);
@@ -5388,8 +5392,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private final WeakHashMap<IBinder, AttributionSource> mAttributions = new WeakHashMap<>();
- public @NonNull AttributionSource registerAttributionSource(
- @NonNull AttributionSource source) {
+ public void registerAttributionSource(@NonNull AttributionSource source) {
// Here we keep track of attribution sources that were created by an app
// from an attribution chain that called into the app and the apps's
// own attribution source. An app can register an attribution chain up
@@ -5436,10 +5439,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
synchronized (mLock) {
- final IBinder token = new Binder();
- final AttributionSource result = source.withToken(token);
- mAttributions.put(token, result);
- return result;
+ mAttributions.put(source.getToken(), source);
}
}
@@ -5465,6 +5465,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions
= new ConcurrentHashMap<>();
+ private static final AtomicInteger sAttributionChainIds = new AtomicInteger(0);
+
private final @NonNull Context mContext;
private final @NonNull AppOpsManager mAppOpsManager;
@@ -5474,53 +5476,108 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
public int checkPermission(@NonNull String permission,
@NonNull AttributionSourceState attributionSourceState, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource,
+ int attributedOp) {
Objects.requireNonNull(permission);
Objects.requireNonNull(attributionSourceState);
final AttributionSource attributionSource = new AttributionSource(
attributionSourceState);
final int result = checkPermission(mContext, permission, attributionSource, message,
- forDataDelivery, startDataDelivery, fromDatasource);
+ forDataDelivery, startDataDelivery, fromDatasource, attributedOp);
// Finish any started op if some step in the attribution chain failed.
if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) {
- finishDataDelivery(AppOpsManager.permissionToOp(permission),
- attributionSource.asState());
+ if (attributedOp == AppOpsManager.OP_NONE) {
+ finishDataDelivery(AppOpsManager.permissionToOpCode(permission),
+ attributionSource.asState(), fromDatasource);
+ } else {
+ finishDataDelivery(attributedOp, attributionSource.asState(), fromDatasource);
+ }
}
return result;
}
@Override
- public void finishDataDelivery(@NonNull String op,
- @NonNull AttributionSourceState attributionSourceState) {
- if (op == null || attributionSourceState.packageName == null) {
+ public void finishDataDelivery(int op,
+ @NonNull AttributionSourceState attributionSourceState, boolean fromDatasource) {
+ Objects.requireNonNull(attributionSourceState);
+
+ if (op == AppOpsManager.OP_NONE) {
return;
}
- mAppOpsManager.finishProxyOp(op, new AttributionSource(attributionSourceState));
- if (attributionSourceState.next != null) {
- finishDataDelivery(op, attributionSourceState.next[0]);
+
+ AttributionSource current = new AttributionSource(attributionSourceState);
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentFinish = (fromDatasource || next != null);
+
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (!(fromDatasource && current.asState() == attributionSourceState)
+ && next != null && !current.isTrusted(mContext)) {
+ return;
+ }
+
+ // The access is for oneself if this is the single receiver of data
+ // after the data source or if this is the single attribution source
+ // in the chain if not from a datasource.
+ final boolean singleReceiverFromDatasource = (fromDatasource
+ && current.asState() == attributionSourceState && next != null
+ && next.getNext() == null);
+ final boolean selfAccess = singleReceiverFromDatasource || next == null;
+
+ final AttributionSource accessorSource = (!singleReceiverFromDatasource)
+ ? current : next;
+
+ if (selfAccess) {
+ final String resolvedPackageName = resolvePackageName(mContext, accessorSource);
+ if (resolvedPackageName == null) {
+ return;
+ }
+ mAppOpsManager.finishOp(accessorSource.getToken(), op,
+ accessorSource.getUid(), resolvedPackageName,
+ accessorSource.getAttributionTag());
+ } else {
+ final AttributionSource resolvedAttributionSource =
+ resolveAttributionSource(mContext, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return;
+ }
+ mAppOpsManager.finishProxyOp(AppOpsManager.opToPublicName(op),
+ resolvedAttributionSource, skipCurrentFinish);
+ }
+
+ if (next == null || next.getNext() == null) {
+ return;
+ }
+
+ current = next;
}
}
@Override
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
public int checkOp(int op, AttributionSourceState attributionSource,
String message, boolean forDataDelivery, boolean startDataDelivery) {
int result = checkOp(mContext, op, new AttributionSource(attributionSource), message,
forDataDelivery, startDataDelivery);
if (result != PermissionChecker.PERMISSION_GRANTED && startDataDelivery) {
// Finish any started op if some step in the attribution chain failed.
- finishDataDelivery(AppOpsManager.opToName(op), attributionSource);
+ finishDataDelivery(op, attributionSource, /*fromDatasource*/ false);
}
return result;
}
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
private static int checkPermission(@NonNull Context context, @NonNull String permission,
@NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource,
+ int attributedOp) {
PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
if (permissionInfo == null) {
@@ -5542,7 +5599,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
if (permissionInfo.isRuntime()) {
return checkRuntimePermission(context, permission, attributionSource, message,
- forDataDelivery, startDataDelivery, fromDatasource);
+ forDataDelivery, startDataDelivery, fromDatasource, attributedOp);
}
if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(),
@@ -5551,15 +5608,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
if (attributionSource.getNext() != null) {
- return checkPermission(context, permission,
- attributionSource.getNext(), message, forDataDelivery,
- startDataDelivery, /*fromDatasource*/ false);
+ return checkPermission(context, permission, attributionSource.getNext(), message,
+ forDataDelivery, startDataDelivery, /*fromDatasource*/ false, attributedOp);
}
return PermissionChecker.PERMISSION_GRANTED;
}
- @PermissionChecker.PermissionResult
+ @PermissionCheckerManager.PermissionResult
private static int checkAppOpPermission(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean forDataDelivery, boolean fromDatasource) {
@@ -5579,7 +5635,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// If the call is from a datasource we need to vet only the chain before it. This
// way we can avoid the datasource creating an attribution context for every call.
- if (!(fromDatasource && current == attributionSource)
+ if (!(fromDatasource && current.equals(attributionSource))
&& next != null && !current.isTrusted(context)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
@@ -5588,12 +5644,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// after the data source or if this is the single attribution source
// in the chain if not from a datasource.
final boolean singleReceiverFromDatasource = (fromDatasource
- && current == attributionSource && next != null && next.getNext() == null);
+ && current.equals(attributionSource) && next != null
+ && next.getNext() == null);
final boolean selfAccess = singleReceiverFromDatasource || next == null;
final int opMode = performOpTransaction(context, op, current, message,
forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks,
- selfAccess, singleReceiverFromDatasource);
+ selfAccess, singleReceiverFromDatasource, AppOpsManager.OP_NONE,
+ AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
switch (opMode) {
case AppOpsManager.MODE_IGNORED:
@@ -5624,9 +5683,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private static int checkRuntimePermission(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
- boolean fromDatasource) {
+ boolean fromDatasource, int attributedOp) {
// Now let's check the identity chain...
final int op = AppOpsManager.permissionToOpCode(permission);
+ final int attributionChainId = (startDataDelivery)
+ ? sAttributionChainIds.incrementAndGet()
+ : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -5637,7 +5699,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// If the call is from a datasource we need to vet only the chain before it. This
// way we can avoid the datasource creating an attribution context for every call.
- if (!(fromDatasource && current == attributionSource)
+ if (!(fromDatasource && current.equals(attributionSource))
&& next != null && !current.isTrusted(context)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
@@ -5671,12 +5733,21 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// after the data source or if this is the single attribution source
// in the chain if not from a datasource.
final boolean singleReceiverFromDatasource = (fromDatasource
- && current == attributionSource && next != null && next.getNext() == null);
+ && current.equals(attributionSource)
+ && next != null && next.getNext() == null);
final boolean selfAccess = singleReceiverFromDatasource || next == null;
+ final int proxyAttributionFlags = (!skipCurrentChecks)
+ ? resolveProxyAttributionFlags(attributionSource, current, fromDatasource,
+ startDataDelivery, selfAccess)
+ : AppOpsManager.ATTRIBUTION_FLAGS_NONE;
+ final int proxiedAttributionFlags = resolveProxiedAttributionFlags(
+ attributionSource, next, fromDatasource, startDataDelivery, selfAccess);
+
final int opMode = performOpTransaction(context, op, current, message,
forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
- singleReceiverFromDatasource);
+ singleReceiverFromDatasource, attributedOp, proxyAttributionFlags,
+ proxiedAttributionFlags, attributionChainId);
switch (opMode) {
case AppOpsManager.MODE_ERRORED: {
@@ -5707,6 +5778,50 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return permissionGranted;
}
+ private static @AttributionFlags int resolveProxyAttributionFlags(
+ @NonNull AttributionSource attributionChain,
+ @NonNull AttributionSource current, boolean fromDatasource,
+ boolean startDataDelivery, boolean selfAccess) {
+ return resolveAttributionFlags(attributionChain, current, fromDatasource,
+ startDataDelivery, selfAccess, /*flagsForProxy*/ true);
+ }
+
+ private static @AttributionFlags int resolveProxiedAttributionFlags(
+ @NonNull AttributionSource attributionChain,
+ @NonNull AttributionSource current, boolean fromDatasource,
+ boolean startDataDelivery, boolean selfAccess) {
+ return resolveAttributionFlags(attributionChain, current, fromDatasource,
+ startDataDelivery, selfAccess, /*flagsForProxy*/ false);
+ }
+
+ private static @AttributionFlags int resolveAttributionFlags(
+ @NonNull AttributionSource attributionChain,
+ @NonNull AttributionSource current, boolean fromDatasource,
+ boolean startDataDelivery, boolean selfAccess, boolean flagsForProxy) {
+ if (current == null || !startDataDelivery) {
+ return AppOpsManager.ATTRIBUTION_FLAGS_NONE;
+ }
+ if (flagsForProxy) {
+ if (selfAccess) {
+ return AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+ } else if (!fromDatasource && current.equals(attributionChain)) {
+ return AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+ }
+ } else {
+ if (selfAccess) {
+ return AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
+ } else if (fromDatasource && current.equals(attributionChain.getNext())) {
+ return AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+ } else if (current.getNext() == null) {
+ return AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
+ }
+ }
+ if (fromDatasource && current.equals(attributionChain)) {
+ return AppOpsManager.ATTRIBUTION_FLAGS_NONE;
+ }
+ return AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY;
+ }
+
private static int checkOp(@NonNull Context context, @NonNull int op,
@NonNull AttributionSource attributionSource, @Nullable String message,
boolean forDataDelivery, boolean startDataDelivery) {
@@ -5714,6 +5829,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
+ final int attributionChainId = (startDataDelivery)
+ ? sAttributionChainIds.incrementAndGet()
+ : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -5730,9 +5849,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// The access is for oneself if this is the single attribution source in the chain.
final boolean selfAccess = (next == null);
+ final int proxyAttributionFlags = (!skipCurrentChecks)
+ ? resolveProxyAttributionFlags(attributionSource, current,
+ /*fromDatasource*/ false, startDataDelivery, selfAccess)
+ : AppOpsManager.ATTRIBUTION_FLAGS_NONE;
+ final int proxiedAttributionFlags = resolveProxiedAttributionFlags(
+ attributionSource, next, /*fromDatasource*/ false, startDataDelivery,
+ selfAccess);
+
final int opMode = performOpTransaction(context, op, current, message,
forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
- /*fromDatasource*/ false);
+ /*fromDatasource*/ false, AppOpsManager.OP_NONE, proxyAttributionFlags,
+ proxiedAttributionFlags, attributionChainId);
switch (opMode) {
case AppOpsManager.MODE_ERRORED: {
@@ -5751,10 +5879,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
+ @SuppressWarnings("ConstantConditions")
private static int performOpTransaction(@NonNull Context context, int op,
@NonNull AttributionSource attributionSource, @Nullable String message,
boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation,
- boolean selfAccess, boolean singleReceiverFromDatasource) {
+ boolean selfAccess, boolean singleReceiverFromDatasource, int attributedOp,
+ @AttributionFlags int proxyAttributionFlags,
+ @AttributionFlags int proxiedAttributionFlags, int attributionChainId) {
// We cannot perform app ops transactions without a package name. In all relevant
// places we pass the package name but just in case there is a bug somewhere we
// do a best effort to resolve the package from the UID (pick first without a loss
@@ -5786,36 +5917,75 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (resolvedAttributionSource.getPackageName() == null) {
return AppOpsManager.MODE_ERRORED;
}
+ // If the datasource is not in a trusted platform component then in would not
+ // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+ // an app is exposing runtime permission protected data but cannot blame others
+ // in a trusted way which would not properly show in permission usage UIs.
+ // As a fallback we note a proxy op that blames the app and the datasource.
+ int startedOp = op;
+ int checkedOpResult = MODE_ALLOWED;
+ int startedOpResult;
+
+ // If the datasource wants to attribute to another app op we need to
+ // make sure the op for the permission and the attributed ops allow
+ // the operation. We return the less permissive of the two and check
+ // the permission op while start the attributed op.
+ if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) {
+ checkedOpResult = appOpsManager.checkOpNoThrow(op,
+ resolvedAttributionSource.getUid(), resolvedAttributionSource
+ .getPackageName());
+ if (checkedOpResult == MODE_ERRORED) {
+ return checkedOpResult;
+ }
+ startedOp = attributedOp;
+ }
if (selfAccess) {
- // If the datasource is not in a trusted platform component then in would not
- // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
- // an app is exposing runtime permission protected data but cannot blame others
- // in a trusted way which would not properly show in permission usage UIs.
- // As a fallback we note a proxy op that blames the app and the datasource.
try {
- return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
+ startedOpResult = appOpsManager.startOpNoThrow(
+ resolvedAttributionSource.getToken(), startedOp,
+ resolvedAttributionSource.getUid(),
resolvedAttributionSource.getPackageName(),
/*startIfModeDefault*/ false,
resolvedAttributionSource.getAttributionTag(),
- message);
+ message, proxyAttributionFlags, attributionChainId);
} catch (SecurityException e) {
Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+ " platform defined runtime permission "
+ AppOpsManager.opToPermission(op) + " while not having "
+ Manifest.permission.UPDATE_APP_OPS_STATS);
- return appOpsManager.startProxyOpNoThrow(op, attributionSource, message,
- skipProxyOperation);
+ startedOpResult = appOpsManager.startProxyOpNoThrow(attributedOp,
+ attributionSource, message, skipProxyOperation,
+ proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
}
} else {
- return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
- skipProxyOperation);
+ startedOpResult = appOpsManager.startProxyOpNoThrow(startedOp,
+ resolvedAttributionSource, message, skipProxyOperation,
+ proxyAttributionFlags, proxiedAttributionFlags, attributionChainId);
}
+ return Math.max(checkedOpResult, startedOpResult);
} else {
final AttributionSource resolvedAttributionSource = resolveAttributionSource(
context, accessorSource);
if (resolvedAttributionSource.getPackageName() == null) {
return AppOpsManager.MODE_ERRORED;
}
+ int notedOp = op;
+ int checkedOpResult = MODE_ALLOWED;
+ int notedOpResult;
+
+ // If the datasource wants to attribute to another app op we need to
+ // make sure the op for the permission and the attributed ops allow
+ // the operation. We return the less permissive of the two and check
+ // the permission op while start the attributed op.
+ if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) {
+ checkedOpResult = appOpsManager.checkOpNoThrow(op,
+ resolvedAttributionSource.getUid(), resolvedAttributionSource
+ .getPackageName());
+ if (checkedOpResult == MODE_ERRORED) {
+ return checkedOpResult;
+ }
+ notedOp = attributedOp;
+ }
if (selfAccess) {
// If the datasource is not in a trusted platform component then in would not
// have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
@@ -5823,7 +5993,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// in a trusted way which would not properly show in permission usage UIs.
// As a fallback we note a proxy op that blames the app and the datasource.
try {
- return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
+ notedOpResult = appOpsManager.noteOpNoThrow(notedOp,
+ resolvedAttributionSource.getUid(),
resolvedAttributionSource.getPackageName(),
resolvedAttributionSource.getAttributionTag(),
message);
@@ -5832,13 +6003,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
+ " platform defined runtime permission "
+ AppOpsManager.opToPermission(op) + " while not having "
+ Manifest.permission.UPDATE_APP_OPS_STATS);
- return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message,
- skipProxyOperation);
+ notedOpResult = appOpsManager.noteProxyOpNoThrow(notedOp, attributionSource,
+ message, skipProxyOperation);
}
} else {
- return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
- skipProxyOperation);
+ notedOpResult = appOpsManager.noteProxyOpNoThrow(notedOp,
+ resolvedAttributionSource, message, skipProxyOperation);
}
+ return Math.max(checkedOpResult, notedOpResult);
}
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 2cfbf26cf996..607bc569ad64 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -19,6 +19,7 @@ package com.android.server.policy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributionFlags;
import android.app.AppOpsManagerInternal;
import android.app.SyncNotedAppOp;
import android.app.role.RoleManager;
@@ -39,6 +40,7 @@ import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+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;
@@ -46,6 +48,7 @@ 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;
import com.android.server.LocalServices;
import java.util.List;
@@ -179,32 +182,37 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
@Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
- boolean shouldCollectMessage, @NonNull NonaFunction<IBinder, Integer, Integer, String,
- String, Boolean, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
+ boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId, @NonNull UndecFunction<IBinder, Integer, Integer, String,
+ String, Boolean, Boolean, String, Boolean, Integer, Integer,
+ SyncNotedAppOp> superImpl) {
return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag),
uid, packageName, attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ message, shouldCollectMessage, attributionFlags, attributionChainId);
}
@Override
- public SyncNotedAppOp startProxyOperation(IBinder token, int code,
+ public SyncNotedAppOp startProxyOperation(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> superImpl) {
- return superImpl.apply(token, resolveDatasourceOp(code, attributionSource.getUid(),
+ boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags,
+ @AttributionFlags int proxiedAttributionFlags, int attributionChainId,
+ @NonNull DecFunction<Integer, AttributionSource, Boolean, Boolean, String, Boolean,
+ Boolean, Integer, Integer, Integer, SyncNotedAppOp> superImpl) {
+ return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(),
attributionSource.getPackageName(), attributionSource.getAttributionTag()),
attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, skipProxyOperation);
+ shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
+ proxiedAttributionFlags, attributionChainId);
}
@Override
- public void finishProxyOperation(IBinder clientId, int code,
- @NonNull AttributionSource attributionSource,
- @NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
- superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(),
+ public void finishProxyOperation(int code, @NonNull AttributionSource attributionSource,
+ boolean skipProxyOperation, @NonNull TriFunction<Integer, AttributionSource,
+ Boolean, Void> superImpl) {
+ superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(),
attributionSource.getPackageName(), attributionSource.getAttributionTag()),
- attributionSource);
+ attributionSource, skipProxyOperation);
}
private int resolveDatasourceOp(int code, int uid, @NonNull String packageName,
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
index 98bc0673f79c..a2aaccc679dc 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
@@ -20,6 +20,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -66,7 +68,8 @@ public class AppOpsActiveWatcherTest {
// Verify that we got called for the op being active
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
- eq(Process.myUid()), eq(getContext().getPackageName()), eq(true));
+ eq(Process.myUid()), eq(getContext().getPackageName()),
+ isNull(), eq(true), anyInt(), anyInt());
// This should be the only callback we got
verifyNoMoreInteractions(listener);
@@ -84,7 +87,8 @@ public class AppOpsActiveWatcherTest {
// Verify that we got called for the op being active
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
- eq(Process.myUid()), eq(getContext().getPackageName()), eq(false));
+ eq(Process.myUid()), eq(getContext().getPackageName()), isNull(),
+ eq(false), anyInt(), anyInt());
// Verify that the op is not active
assertThat(appOpsManager.isOperationActive(AppOpsManager.OP_CAMERA,
@@ -121,7 +125,8 @@ public class AppOpsActiveWatcherTest {
// We should get the callback again (and since we reset the listener, we therefore expect 1)
verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
.times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
- eq(Process.myUid()), eq(getContext().getPackageName()), eq(true));
+ eq(Process.myUid()), eq(getContext().getPackageName()), isNull(),
+ eq(true), anyInt(), anyInt());
// Finish up
appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index 2162c0b20eac..e811c1f315fe 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -5,22 +5,29 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+
import static org.testng.Assert.assertThrows;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.AttributionSourceState;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.CrossProfileAppsInternal;
@@ -34,6 +41,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionCheckerManager;
import android.permission.PermissionManager;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
@@ -411,6 +419,18 @@ public class CrossProfileAppsServiceImplTest {
}
mActivityInfo.exported = false;
+
+ // There's a bug in static mocking if the APK is large - so here is the next best thing...
+ doReturn(Context.PERMISSION_CHECKER_SERVICE).when(mContext)
+ .getSystemServiceName(PermissionCheckerManager.class);
+ PermissionCheckerManager permissionCheckerManager = mock(PermissionCheckerManager.class);
+ doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when(permissionCheckerManager)
+ .checkPermission(eq(Manifest.permission.INTERACT_ACROSS_PROFILES), any(
+ AttributionSourceState.class), anyString(), anyBoolean(), anyBoolean(),
+ anyBoolean(), anyInt());
+ doReturn(permissionCheckerManager).when(mContext).getSystemService(
+ Context.PERMISSION_CHECKER_SERVICE);
+
assertThrows(
SecurityException.class,
() ->