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

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

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
parent a5748144
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -4499,7 +4499,6 @@ package android.app {
    method public int describeContents();
    method @Nullable public String getFeatureId();
    method @NonNull public String getMessage();
    method @Nullable public String getNotingPackageName();
    method @IntRange(from=0) public int getNotingUid();
    method @NonNull public String getOp();
    method @IntRange(from=0) public long getTime();
+129 −67
Original line number Diff line number Diff line
@@ -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
                    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) {
                markAppOpNoted(proxiedUid, proxiedPackageName, op, proxiedFeatureId, message);
                        && 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,49 +6973,29 @@ 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) {
        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) {
            boolean shouldCollectNotes;
            try {
                shouldCollectNotes = mService.shouldCollectNotes(code);
            } catch (RemoteException e) {
                return;
            }

            if (shouldCollectNotes) {
                sAppOpsToNote[code] = SHOULD_COLLECT_NOTE_OP;
            } else {
                sAppOpsToNote[code] = SHOULD_NOT_COLLECT_NOTE_OP;
            }
        }

        if (sAppOpsToNote[code] != SHOULD_COLLECT_NOTE_OP) {
            return;
        }

        Integer binderUid = sBinderThreadCallingUid.get();

    private void collectNotedOpForSelf(int op, @Nullable String featureId) {
        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 (sNotedAppOpsCollector != null) {
                sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
            }
        }
    }

        if (binderUid != null && binderUid == uid) {
            // If this is inside of a two-way binder call: Delivered to caller via
            // {@link #prefixParcelWithAppOpsIfNeeded}
    /**
     * 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();
@@ -6989,28 +7010,69 @@ public class AppOpsManager {
            appOpsNoted.put(featureId, appOpsNotedForFeature);
        }

            if (code < 64) {
                appOpsNotedForFeature[0] |= 1L << code;
        if (op < 64) {
            appOpsNotedForFeature[0] |= 1L << op;
        } else {
                appOpsNotedForFeature[1] |= 1L << (code - 64);
            appOpsNotedForFeature[1] |= 1L << (op - 64);
        }
        } 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();
    /** @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[op] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
            boolean shouldCollectNotes;
            try {
                mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code,
                        featureId, message);
                shouldCollectNotes = mService.shouldCollectNotes(op);
            } catch (RemoteException e) {
                e.rethrowFromSystemServer();
            } finally {
                Binder.restoreCallingIdentity(token);
                return DONT_COLLECT;
            }

            if (shouldCollectNotes) {
                sAppOpsToNote[op] = SHOULD_COLLECT_NOTE_OP;
            } else {
                sAppOpsToNote[op] = SHOULD_NOT_COLLECT_NOTE_OP;
            }
        }

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

        if (binderUid != null && binderUid == uid) {
            return COLLECT_SYNC;
        } else {
            return COLLECT_ASYNC;
        }
    }

+7 −2
Original line number Diff line number Diff line
@@ -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);
    }

    /**
+10 −32
Original line number Diff line number Diff line
@@ -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

}
+5 −5
Original line number Diff line number Diff line
@@ -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);
Loading