Collect AsyncNotedAppOp in same call as noteOp

This reduces the overhead of collecting noted app-ops to the same amount
of binder calls as if we would not have the feature

Before (conceptionally):
---------------------------------
mode = service.noteOp()
if (mode == allowed && shouldCollectAsyncOp) {
   fixup(message)
   service.noteAsyncOp()
}
----------------------------------

After (conceptionally):
----------------------------------
if (shouldCollectAsyncOp) {
   fixup(message)
}
mode = service.noteOp(shouldCollectAsyncOp, message)
----------------------------------

Bug: 136505050
Test: atest CtsAppOpsTestCases
Change-Id: If1b535a7c4b0f431f251c5d06cdf496c34920e23
This commit is contained in:
Philip P. Moltmann
2019-12-20 11:21:02 -08:00
parent a574814474
commit da554e4de0
14 changed files with 281 additions and 210 deletions

View File

@@ -2046,7 +2046,7 @@ public class AppOpsManager {
* transaction. Not set if this thread is currently not executing a two way binder transaction.
*
* @see #startNotedAppOpsCollection
* @see #markAppOpNoted
* @see #getNotedOpCollectionMode
*/
private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
@@ -2054,7 +2054,8 @@ public class AppOpsManager {
* If a thread is currently executing a two-way binder transaction, this stores the op-codes of
* the app-ops that were noted during this transaction.
*
* @see #markAppOpNoted
* @see #getNotedOpCollectionMode
* @see #collectNotedOpSync
*/
private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
new ThreadLocal<>();
@@ -6322,9 +6323,23 @@ public class AppOpsManager {
public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
@Nullable String featureId, @Nullable String message) {
try {
int mode = mService.noteOperation(op, uid, packageName, featureId);
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
message = getFormattedStackTrace();
}
}
int mode = mService.noteOperation(op, uid, packageName, featureId,
collectionMode == COLLECT_ASYNC, message);
if (mode == MODE_ALLOWED) {
markAppOpNoted(uid, packageName, op, featureId, message);
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(op, featureId);
} else if (collectionMode == COLLECT_SYNC) {
collectNotedOpSync(op, featureId);
}
}
return mode;
@@ -6468,14 +6483,27 @@ public class AppOpsManager {
int myUid = Process.myUid();
try {
int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, op);
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
message = getFormattedStackTrace();
}
}
int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
proxiedFeatureId, myUid, mContext.getOpPackageName(),
mContext.getFeatureId());
if (mode == MODE_ALLOWED
// Only collect app-ops when the proxy is trusted
&& mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid)
== PackageManager.PERMISSION_GRANTED) {
markAppOpNoted(proxiedUid, proxiedPackageName, op, proxiedFeatureId, message);
mContext.getFeatureId(), collectionMode == COLLECT_ASYNC, message);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(op, proxiedFeatureId);
} else if (collectionMode == COLLECT_SYNC
// Only collect app-ops when the proxy is trusted
&& mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
myUid) == PackageManager.PERMISSION_GRANTED) {
collectNotedOpSync(op, proxiedFeatureId);
}
}
return mode;
@@ -6765,10 +6793,23 @@ public class AppOpsManager {
public int startOpNoThrow(int op, int uid, @NonNull String packageName,
boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) {
try {
int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
if (collectionMode == COLLECT_ASYNC) {
if (message == null) {
// Set stack trace as default message
message = getFormattedStackTrace();
}
}
int mode = mService.startOperation(getClientId(), op, uid, packageName,
featureId, startIfModeDefault);
featureId, startIfModeDefault, collectionMode == COLLECT_ASYNC, message);
if (mode == MODE_ALLOWED) {
markAppOpNoted(uid, packageName, op, featureId, message);
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(op, featureId);
} else if (collectionMode == COLLECT_SYNC) {
collectNotedOpSync(op, featureId);
}
}
return mode;
@@ -6932,85 +6973,106 @@ public class AppOpsManager {
}
/**
* Mark an app-op as noted
* Collect a noted op for the current process.
*
* @param op The noted op
* @param featureId The feature the op is noted for
*/
private void markAppOpNoted(int uid, @Nullable String packageName, int code,
@Nullable String featureId, @Nullable String message) {
private void collectNotedOpForSelf(int op, @Nullable String featureId) {
synchronized (sLock) {
if (sNotedAppOpsCollector != null) {
sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
}
}
}
/**
* Collect a noted op when inside of a two-way binder call.
*
* <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
*
* @param op The noted op
* @param featureId The feature the op is noted for
*/
private void collectNotedOpSync(int op, @Nullable String featureId) {
// If this is inside of a two-way binder call:
// We are inside of a two-way binder call. Delivered to caller via
// {@link #prefixParcelWithAppOpsIfNeeded}
ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
if (appOpsNoted == null) {
appOpsNoted = new ArrayMap<>(1);
sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
}
long[] appOpsNotedForFeature = appOpsNoted.get(featureId);
if (appOpsNotedForFeature == null) {
appOpsNotedForFeature = new long[2];
appOpsNoted.put(featureId, appOpsNotedForFeature);
}
if (op < 64) {
appOpsNotedForFeature[0] |= 1L << op;
} else {
appOpsNotedForFeature[1] |= 1L << (op - 64);
}
}
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
DONT_COLLECT,
COLLECT_SELF,
COLLECT_SYNC,
COLLECT_ASYNC
})
private @interface NotedOpCollectionMode {}
private static final int DONT_COLLECT = 0;
private static final int COLLECT_SELF = 1;
private static final int COLLECT_SYNC = 2;
private static final int COLLECT_ASYNC = 3;
/**
* Mark an app-op as noted.
*/
private @NotedOpCollectionMode int getNotedOpCollectionMode(int uid,
@Nullable String packageName, int op) {
if (packageName == null) {
packageName = "android";
}
// check it the appops needs to be collected and cache result
if (sAppOpsToNote[code] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
if (sAppOpsToNote[op] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
boolean shouldCollectNotes;
try {
shouldCollectNotes = mService.shouldCollectNotes(code);
shouldCollectNotes = mService.shouldCollectNotes(op);
} catch (RemoteException e) {
return;
return DONT_COLLECT;
}
if (shouldCollectNotes) {
sAppOpsToNote[code] = SHOULD_COLLECT_NOTE_OP;
sAppOpsToNote[op] = SHOULD_COLLECT_NOTE_OP;
} else {
sAppOpsToNote[code] = SHOULD_NOT_COLLECT_NOTE_OP;
sAppOpsToNote[op] = SHOULD_NOT_COLLECT_NOTE_OP;
}
}
if (sAppOpsToNote[code] != SHOULD_COLLECT_NOTE_OP) {
return;
if (sAppOpsToNote[op] != SHOULD_COLLECT_NOTE_OP) {
return DONT_COLLECT;
}
synchronized (sLock) {
if (uid == Process.myUid()
&& packageName.equals(ActivityThread.currentOpPackageName())) {
return COLLECT_SELF;
}
}
Integer binderUid = sBinderThreadCallingUid.get();
synchronized (sLock) {
if (sNotedAppOpsCollector != null && uid == Process.myUid() && packageName.equals(
ActivityThread.currentOpPackageName())) {
// This app is noting an app-op for itself. Deliver immediately.
sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code, featureId));
return;
}
}
if (binderUid != null && binderUid == uid) {
// If this is inside of a two-way binder call: Delivered to caller via
// {@link #prefixParcelWithAppOpsIfNeeded}
// We are inside of a two-way binder call. Delivered to caller via
// {@link #prefixParcelWithAppOpsIfNeeded}
ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
if (appOpsNoted == null) {
appOpsNoted = new ArrayMap<>(1);
sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
}
long[] appOpsNotedForFeature = appOpsNoted.get(featureId);
if (appOpsNotedForFeature == null) {
appOpsNotedForFeature = new long[2];
appOpsNoted.put(featureId, appOpsNotedForFeature);
}
if (code < 64) {
appOpsNotedForFeature[0] |= 1L << code;
} else {
appOpsNotedForFeature[1] |= 1L << (code - 64);
}
return COLLECT_SYNC;
} else {
// Cannot deliver the note synchronous: Hence send it to the system server to
// notify the noted process.
if (message == null) {
// Default message is a stack trace
message = getFormattedStackTrace();
}
long token = Binder.clearCallingIdentity();
try {
mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code,
featureId, message);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
} finally {
Binder.restoreCallingIdentity(token);
}
return COLLECT_ASYNC;
}
}

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.SparseIntArray;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.QuadFunction;
/**
@@ -63,12 +64,16 @@ public abstract class AppOpsManagerInternal {
* @param uid The UID for which to note.
* @param packageName The package for which to note. {@code null} for system package.
* @param featureId Id of the feature in the package
* @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
* @param message The message in the async noted op
* @param superImpl The super implementation.
* @return The app op note result.
*/
int noteOperation(int code, int uid, @Nullable String packageName,
@Nullable String featureId,
@NonNull QuadFunction<Integer, Integer, String, String, Integer> superImpl);
@Nullable String featureId, boolean shouldCollectAsyncNotedOp,
@Nullable String message,
@NonNull HexFunction<Integer, Integer, String, String, Boolean, String, Integer>
superImpl);
}
/**

View File

@@ -45,12 +45,6 @@ public final class AsyncNotedAppOp implements Parcelable {
/** Uid that noted the op */
private final @IntRange(from = 0) int mNotingUid;
/**
* Package that noted the op. {@code null} if the package name that noted the op could be not
* be determined (e.g. when the op is noted from native code).
*/
private final @Nullable String mNotingPackageName;
/** {@link android.content.Context#createFeatureContext Feature} in the app */
private final @Nullable String mFeatureId;
@@ -69,7 +63,7 @@ public final class AsyncNotedAppOp implements Parcelable {
// Code below generated by codegen v1.0.9.
// Code below generated by codegen v1.0.14.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -89,9 +83,6 @@ public final class AsyncNotedAppOp implements Parcelable {
* Op that was noted
* @param notingUid
* Uid that noted the op
* @param notingPackageName
* Package that noted the op. {@code null} if the package name that noted the op could be not
* be determined (e.g. when the op is noted from native code).
* @param featureId
* {@link android.content.Context#createFeatureContext Feature} in the app
* @param message
@@ -104,7 +95,6 @@ public final class AsyncNotedAppOp implements Parcelable {
public AsyncNotedAppOp(
@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
@IntRange(from = 0) int notingUid,
@Nullable String notingPackageName,
@Nullable String featureId,
@NonNull String message,
@IntRange(from = 0) long time) {
@@ -117,7 +107,6 @@ public final class AsyncNotedAppOp implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mNotingUid,
"from", 0);
this.mNotingPackageName = notingPackageName;
this.mFeatureId = featureId;
this.mMessage = message;
com.android.internal.util.AnnotationValidations.validate(
@@ -138,15 +127,6 @@ public final class AsyncNotedAppOp implements Parcelable {
return mNotingUid;
}
/**
* Package that noted the op. {@code null} if the package name that noted the op could be not
* be determined (e.g. when the op is noted from native code).
*/
@DataClass.Generated.Member
public @Nullable String getNotingPackageName() {
return mNotingPackageName;
}
/**
* {@link android.content.Context#createFeatureContext Feature} in the app
*/
@@ -186,7 +166,6 @@ public final class AsyncNotedAppOp implements Parcelable {
return true
&& mOpCode == that.mOpCode
&& mNotingUid == that.mNotingUid
&& java.util.Objects.equals(mNotingPackageName, that.mNotingPackageName)
&& java.util.Objects.equals(mFeatureId, that.mFeatureId)
&& java.util.Objects.equals(mMessage, that.mMessage)
&& mTime == that.mTime;
@@ -201,7 +180,6 @@ public final class AsyncNotedAppOp implements Parcelable {
int _hash = 1;
_hash = 31 * _hash + mOpCode;
_hash = 31 * _hash + mNotingUid;
_hash = 31 * _hash + java.util.Objects.hashCode(mNotingPackageName);
_hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
_hash = 31 * _hash + java.util.Objects.hashCode(mMessage);
_hash = 31 * _hash + Long.hashCode(mTime);
@@ -215,12 +193,10 @@ public final class AsyncNotedAppOp implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
if (mNotingPackageName != null) flg |= 0x4;
if (mFeatureId != null) flg |= 0x8;
if (mFeatureId != null) flg |= 0x4;
dest.writeByte(flg);
dest.writeInt(mOpCode);
dest.writeInt(mNotingUid);
if (mNotingPackageName != null) dest.writeString(mNotingPackageName);
if (mFeatureId != null) dest.writeString(mFeatureId);
dest.writeString(mMessage);
dest.writeLong(mTime);
@@ -240,8 +216,7 @@ public final class AsyncNotedAppOp implements Parcelable {
byte flg = in.readByte();
int opCode = in.readInt();
int notingUid = in.readInt();
String notingPackageName = (flg & 0x4) == 0 ? null : in.readString();
String featureId = (flg & 0x8) == 0 ? null : in.readString();
String featureId = (flg & 0x4) == 0 ? null : in.readString();
String message = in.readString();
long time = in.readLong();
@@ -254,7 +229,6 @@ public final class AsyncNotedAppOp implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mNotingUid,
"from", 0);
this.mNotingPackageName = notingPackageName;
this.mFeatureId = featureId;
this.mMessage = message;
com.android.internal.util.AnnotationValidations.validate(
@@ -282,11 +256,15 @@ public final class AsyncNotedAppOp implements Parcelable {
};
@DataClass.Generated(
time = 1571327470155L,
codegenVersion = "1.0.9",
time = 1576864422226L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
inputSignatures = "private final @android.annotation.IntRange(from=0L, to=92L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
inputSignatures = "private final @android.annotation.IntRange(from=0L, to=92L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -31,9 +31,11 @@ interface IAppOpsService {
// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
// and not be reordered
int checkOperation(int code, int uid, String packageName);
int noteOperation(int code, int uid, String packageName, @nullable String featureId);
int noteOperation(int code, int uid, String packageName, @nullable String featureId,
boolean shouldCollectAsyncNotedOp, String message);
int startOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String featureId, boolean startIfModeDefault);
@nullable String featureId, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message);
@UnsupportedAppUsage
void finishOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String featureId);
@@ -41,8 +43,6 @@ interface IAppOpsService {
void stopWatchingMode(IAppOpsCallback callback);
int permissionToOpCode(String permission);
int checkAudioOperation(int code, int usage, int uid, String packageName);
void noteAsyncOp(@nullable String callingPackageName, int uid, @nullable String packageName,
int opCode, @nullable String featureId, String message);
boolean shouldCollectNotes(int opCode);
void setCameraAudioRestriction(int mode);
// End of methods also called by native code.
@@ -50,7 +50,7 @@ interface IAppOpsService {
int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
String proxiedFeatureId, int proxyUid, String proxyPackageName,
String proxyFeatureId);
String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);