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

Commit 26c5231b authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Atom for when fgs accesses appop" into rvc-dev am: 10398d29 am: 95f38f35 am: 2f14645c

Change-Id: I3257cefb901a758aabc5a629baf95ba0ca50d29a
parents 24b6a65a 2f14645c
Loading
Loading
Loading
Loading
+47 −2
Original line number Diff line number Diff line
@@ -393,6 +393,8 @@ message Atom {
        WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
        AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
        SnapshotMergeReported snapshot_merge_reported = 255;
        ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
            256  [(module) = "framework"];
        SdkExtensionStatus sdk_extension_status = 354;
    }

@@ -3285,12 +3287,12 @@ message OverlayStateChanged {
    ];
}

/*
/**
 * Logs foreground service starts and stops.
 * Note that this is not when a service starts or stops, but when it is
 * considered foreground.
 * Logged from
 *     //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
 *     frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
 */
message ForegroundServiceStateChanged {
    optional int32 uid = 1 [(is_uid) = true];
@@ -3302,6 +3304,49 @@ message ForegroundServiceStateChanged {
        EXIT = 2;
    }
    optional State state = 3;

    // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user.
    // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions)
    optional bool allow_while_in_use_permission = 4;
}

/**
 * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session.
 * A foreground service session is any continuous period during which the uid holds at least one
 * foreground service; the atom will be pushed when the uid no longer holds any foreground services.
 * Accesses initiated while the uid is in the TOP state are ignored.
 * Sessions with no attempted accesses are not logged.
 * Logged from
 *     frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
 */
message ForegroundServiceAppOpSessionEnded {
    optional int32 uid = 1 [(is_uid) = true];

    // The operation's name.
    // To the extent possible, preserve the mapping from AppOpsManager.OP_ constants.
    // Only these named ops are actually logged.
    enum AppOpName {
        OP_NONE = -1; // Also represents UNKNOWN.
        OP_COARSE_LOCATION = 0;
        OP_FINE_LOCATION = 1;
        OP_CAMERA = 26;
        OP_RECORD_AUDIO = 27;
    }
    optional AppOpName app_op_name = 2 [default = OP_NONE];

    // The uid's permission mode for accessing the AppOp during this fgs session.
    enum Mode {
        MODE_UNKNOWN = 0;
        MODE_ALLOWED = 1; // Always allowed
        MODE_IGNORED = 2; // Denied
        MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time)
    }
    optional Mode app_op_mode = 3;

    // Number of times this AppOp was requested and allowed.
    optional int32 count_ops_accepted = 4;
    // Number of times this AppOp was requested but denied.
    optional int32 count_ops_rejected = 5;
}

/**
+11 −6
Original line number Diff line number Diff line
@@ -6910,11 +6910,7 @@ public class AppOpsManager {
     * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
     */
    public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) {
        try {
            return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return unsafeCheckOpRawNoThrow(op, uid, packageName);
    }

    /**
@@ -6923,8 +6919,17 @@ public class AppOpsManager {
     * {@link #MODE_FOREGROUND}.
     */
    public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
        return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName);
    }

    /**
     * Returns the <em>raw</em> mode associated with the op.
     * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
     * @hide
     */
    public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
        try {
            return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
            return mService.checkOperationRaw(op, uid, packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+217 −3
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_E
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -88,11 +89,13 @@ import android.util.EventLog;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.webkit.WebViewZygote;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
@@ -177,6 +180,10 @@ public final class ActiveServices {
    /** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */
    private ArrayList<ServiceRecord> mTmpCollectionResults = null;

    /** Mapping from uid to their foreground service AppOpCallbacks (if they have one). */
    @GuardedBy("mAm")
    private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>();

    /**
     * For keeping ActiveForegroundApps retaining state while the screen is off.
     */
@@ -1455,7 +1462,9 @@ public final class ActiveServices {
                                null, true, false, "");
                        FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                                r.appInfo.uid, r.shortInstanceName,
                                FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
                                FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
                                r.mAllowWhileInUsePermissionInFgs);
                        registerAppOpCallbackLocked(r);
                        mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
                    }
                    r.postNotification();
@@ -1504,9 +1513,11 @@ public final class ActiveServices {
                mAm.mAppOpsService.finishOperation(
                        AppOpsManager.getToken(mAm.mAppOpsService),
                        AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
                unregisterAppOpCallbackLocked(r);
                FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                        r.appInfo.uid, r.shortInstanceName,
                        FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
                        FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
                        r.mAllowWhileInUsePermissionInFgs);
                mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
                if (r.app != null) {
                    mAm.updateLruProcessLocked(r.app, false, null);
@@ -1527,6 +1538,207 @@ public final class ActiveServices {
        }
    }

    /** Registers an AppOpCallback for monitoring special AppOps for this foreground service. */
    private void registerAppOpCallbackLocked(@NonNull ServiceRecord r) {
        if (r.app == null) {
            return;
        }
        final int uid = r.appInfo.uid;
        AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
        if (callback == null) {
            callback = new AppOpCallback(r.app, mAm.getAppOpsManager());
            mFgsAppOpCallbacks.put(uid, callback);
        }
        callback.registerLocked();
    }

    /** Unregisters a foreground service's AppOpCallback. */
    private void unregisterAppOpCallbackLocked(@NonNull ServiceRecord r) {
        final int uid = r.appInfo.uid;
        final AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
        if (callback != null) {
            callback.unregisterLocked();
            if (callback.isObsoleteLocked()) {
                mFgsAppOpCallbacks.remove(uid);
            }
        }
    }

    /**
     * For monitoring when {@link #LOGGED_AP_OPS} AppOps occur by an app while it is holding
     * at least one foreground service and is not also in the TOP state.
     * Once the uid no longer holds any foreground services, this callback becomes stale
     * (marked by {@link #isObsoleteLocked()}) and must no longer be used.
     *
     * Methods that end in Locked should only be called while the mAm lock is held.
     */
    private static final class AppOpCallback {
        /** AppOps that should be logged if they occur during a foreground service. */
        private static final int[] LOGGED_AP_OPS = new int[] {
                AppOpsManager.OP_COARSE_LOCATION,
                AppOpsManager.OP_FINE_LOCATION,
                AppOpsManager.OP_RECORD_AUDIO,
                AppOpsManager.OP_CAMERA
        };

        private final ProcessRecord mProcessRecord;

        /** Count of acceptances per appop (for LOGGED_AP_OPS) during this fgs session. */
        @GuardedBy("mCounterLock")
        private final SparseIntArray mAcceptedOps = new SparseIntArray();
        /** Count of rejections per appop (for LOGGED_AP_OPS) during this fgs session. */
        @GuardedBy("mCounterLock")
        private final SparseIntArray mRejectedOps = new SparseIntArray();

        /** Lock for the purposes of mAcceptedOps and mRejectedOps. */
        private final Object mCounterLock = new Object();

        /**
         * AppOp Mode (e.g. {@link AppOpsManager#MODE_ALLOWED} per op.
         * This currently cannot change without the process being killed, so they are constants.
         */
        private final SparseIntArray mAppOpModes = new SparseIntArray();

        /**
         * Number of foreground services currently associated with this AppOpCallback (i.e.
         * currently held for this uid).
         */
        @GuardedBy("mAm")
        private int mNumFgs = 0;

        /**
         * Indicates that this Object is stale and must not be used.
         * Specifically, when mNumFgs decreases down to 0, the callbacks will be unregistered and
         * this AppOpCallback is unusable.
         */
        @GuardedBy("mAm")
        private boolean mDestroyed = false;

        private final AppOpsManager mAppOpsManager;

        AppOpCallback(@NonNull ProcessRecord r, @NonNull AppOpsManager appOpsManager) {
            mProcessRecord = r;
            mAppOpsManager = appOpsManager;
            for (int op : LOGGED_AP_OPS) {
                int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, r.uid, r.info.packageName);
                mAppOpModes.put(op, mode);
            }
        }

        private final AppOpsManager.OnOpNotedListener mOpNotedCallback =
                new AppOpsManager.OnOpNotedListener() {
                    @Override
                    public void onOpNoted(int op, int uid, String pkgName, int result) {
                        if (uid == mProcessRecord.uid && isNotTop()) {
                            incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
                        }
                    }
        };

        private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback =
                new AppOpsManager.OnOpActiveChangedInternalListener() {
                    @Override
                    public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) {
                        if (uid == mProcessRecord.uid && active && isNotTop()) {
                            incrementOpCount(op, true);
                        }
                    }
        };

        private boolean isNotTop() {
            return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
        }

        private void incrementOpCount(int op, boolean allowed) {
            synchronized (mCounterLock) {
                final SparseIntArray counter = allowed ? mAcceptedOps : mRejectedOps;
                final int index = counter.indexOfKey(op);
                if (index < 0) {
                    counter.put(op, 1);
                } else {
                    counter.setValueAt(index, counter.valueAt(index) + 1);
                }
            }
        }

        void registerLocked() {
            if (isObsoleteLocked()) {
                Slog.wtf(TAG, "Trying to register on a stale AppOpCallback.");
                return;
            }
            mNumFgs++;
            if (mNumFgs == 1) {
                mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback);
                mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback);
            }
        }

        void unregisterLocked() {
            mNumFgs--;
            if (mNumFgs <= 0) {
                mDestroyed = true;
                logFinalValues();
                mAppOpsManager.stopWatchingNoted(mOpNotedCallback);
                mAppOpsManager.stopWatchingActive(mOpActiveCallback);
            }
        }

        /**
         * Indicates that all foreground services for this uid are now over and the callback is
         * stale and must never be used again.
         */
        boolean isObsoleteLocked() {
            return mDestroyed;
        }

        private void logFinalValues() {
            synchronized (mCounterLock) {
                for (int op : LOGGED_AP_OPS) {
                    final int acceptances = mAcceptedOps.get(op);
                    final int rejections = mRejectedOps.get(op);
                    if (acceptances > 0 ||  rejections > 0) {
                        FrameworkStatsLog.write(
                                FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED,
                                mProcessRecord.uid, opToEnum(op),
                                modeToEnum(mAppOpModes.get(op)),
                                acceptances, rejections
                        );
                    }
                }
            }
        }

        /** Maps AppOp mode to atoms.proto enum. */
        private static int modeToEnum(int mode) {
            switch (mode) {
                case AppOpsManager.MODE_ALLOWED: return FrameworkStatsLog
                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_ALLOWED;
                case AppOpsManager.MODE_IGNORED: return FrameworkStatsLog
                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_IGNORED;
                case AppOpsManager.MODE_FOREGROUND: return FrameworkStatsLog
                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_FOREGROUND;
                default: return FrameworkStatsLog
                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_UNKNOWN;
            }
        }
    }

    /** Maps AppOp op value to atoms.proto enum. */
    private static int opToEnum(int op) {
        switch (op) {
            case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog
                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION;
            case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog
                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION;
            case AppOpsManager.OP_CAMERA: return FrameworkStatsLog
                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA;
            case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog
                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO;
            default: return FrameworkStatsLog
                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE;
        }
    }

    private void cancelForegroundNotificationLocked(ServiceRecord r) {
        if (r.foregroundId != 0) {
            // First check to see if this app has any other active foreground services
@@ -3136,9 +3348,11 @@ public final class ActiveServices {
            mAm.mAppOpsService.finishOperation(
                    AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
            unregisterAppOpCallbackLocked(r);
            FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                    r.appInfo.uid, r.shortInstanceName,
                    FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
                    FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
                    r.mAllowWhileInUsePermissionInFgs);
            mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
        }