Switch media fw permissions checks to AttributionSource

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

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

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

bug: 158792096

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

Merged-In: Ic92c7adc14bd2d135ac13b96f17a1b393dd562e4

Change-Id: Ic92c7adc14bd2d135ac13b96f17a1b393dd562e4
This commit is contained in:
Svet Ganov 2021-05-20 15:09:08 +00:00
parent a6377c9816
commit 2eebf92965
45 changed files with 1342 additions and 698 deletions

View File

@ -201,6 +201,11 @@ package android.app {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
method public static int strOpToOp(@NonNull String);
field public static final int ATTRIBUTION_CHAIN_ID_NONE = -1; // 0xffffffff
field public static final int ATTRIBUTION_FLAGS_NONE = 0; // 0x0
field public static final int ATTRIBUTION_FLAG_ACCESSOR = 1; // 0x1
field public static final int ATTRIBUTION_FLAG_INTERMEDIARY = 2; // 0x2
field public static final int ATTRIBUTION_FLAG_RECEIVER = 4; // 0x4
field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
field public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2; // 0x2
@ -230,6 +235,10 @@ package android.app {
method public void offsetBeginAndEndTime(long);
}
public static interface AppOpsManager.OnOpActiveChangedListener {
method public default void onOpActiveChanged(@NonNull String, int, @NonNull String, @Nullable String, boolean, int, int);
}
public class BroadcastOptions {
ctor public BroadcastOptions(@NonNull android.os.Bundle);
method public int getMaxManifestReceiverApiLevel();
@ -669,7 +678,7 @@ package android.content {
public final class AttributionSource implements android.os.Parcelable {
ctor public AttributionSource(int, @Nullable String, @Nullable String);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable android.content.AttributionSource);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
}
@ -2041,7 +2050,7 @@ package android.permission {
public final class PermissionManager {
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
method public void registerAttributionSource(@NonNull android.content.AttributionSource);
}
}

View File

@ -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();
}

View File

@ -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);
}
/**

View File

@ -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;
}

View File

@ -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

View File

@ -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,25 +281,41 @@ 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);
}

View File

@ -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();

View File

@ -4835,6 +4835,14 @@ public abstract class Context {
@SystemApi
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

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
/**

View File

@ -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());
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -222,7 +222,6 @@ cc_library_shared {
"fd_utils.cpp",
"android_hardware_input_InputWindowHandle.cpp",
"android_hardware_input_InputApplicationHandle.cpp",
"permission_utils.cpp",
],
static_libs: [
@ -241,7 +240,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",
@ -258,6 +256,7 @@ cc_library_shared {
"libgraphicsenv",
"libgui",
"libmediandk",
"libpermission",
"libsensor",
"libinput",
"libcamera_client",

View File

@ -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 {
@ -1591,7 +1590,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),

View File

@ -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},

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -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.CallbackExecutor;
import android.annotation.IntDef;
@ -28,6 +27,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;
@ -35,7 +36,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;
@ -56,7 +56,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;
@ -685,14 +684,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);
}
@ -2475,7 +2478,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();
/**

View File

@ -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());
}
}
/**
@ -1894,14 +1897,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)

View File

@ -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();

View File

@ -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();

View File

@ -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.

View File

@ -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: [

View File

@ -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},

View File

@ -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 },

View File

@ -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",

View File

@ -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)
{
}

View File

@ -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.

View File

@ -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},

View File

@ -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},

View File

@ -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: [

View File

@ -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.

View File

@ -811,7 +811,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);
@ -1881,7 +1882,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);
}

View File

@ -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);
}
});
@ -16866,7 +16871,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 {
@ -16882,8 +16887,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);
@ -16891,57 +16897,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) {

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
@ -108,6 +110,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;
@ -171,6 +174,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;
/**
@ -3320,8 +3324,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
@ -5295,8 +5299,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)
*/
@ -5310,8 +5314,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);
@ -5395,8 +5399,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
@ -5443,10 +5446,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);
}
}
@ -5472,6 +5472,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;
@ -5481,53 +5483,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) {
@ -5549,7 +5606,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(),
@ -5558,15 +5615,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) {
@ -5586,7 +5642,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;
}
@ -5595,12 +5651,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:
@ -5631,9 +5690,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;
@ -5644,7 +5706,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;
}
@ -5678,12 +5740,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: {
@ -5714,6 +5785,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) {
@ -5721,6 +5836,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;
@ -5737,9 +5856,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: {
@ -5758,10 +5886,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
@ -5793,36 +5924,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
@ -5830,7 +6000,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);
@ -5839,13 +6010,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);
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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,
() ->