diff options
| author | 2020-02-25 19:59:16 +0000 | |
|---|---|---|
| committer | 2020-02-25 19:59:16 +0000 | |
| commit | 10398d29f34a23e4076553901e042d5924e9be37 (patch) | |
| tree | 9a26ea208f083c22de49f9529ef29bcf158c0f15 | |
| parent | 5291e518a4bc044ff9c8de82866f701c998226cc (diff) | |
| parent | 38767932a2a548fdf2e53f88a438d260ff85ade9 (diff) | |
Merge "Atom for when fgs accesses appop" into rvc-dev
| -rw-r--r-- | cmds/statsd/src/atoms.proto | 49 | ||||
| -rw-r--r-- | core/java/android/app/AppOpsManager.java | 17 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActiveServices.java | 220 | 
3 files changed, 275 insertions, 11 deletions
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 257554207ef5..92761b54e5f0 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -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;  }  /** diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 8f02f1555edf..a53fc3508001 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -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();          } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7f98c7f2ba85..8ebbce3b2b05 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -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);          }  |