Android权限系统(四):应用操作管理类AppOpsManager(Android 10)

AppOpsManager 介绍

  AppOpsManager 是Google在Android4.3里面引进的应用程序操作(权限)的管理类,核心实现类为AppOpsService。Google对AppOpsManager的说明在:AppOpsManager
  app op(应用操作)的出现比运行时权限早,最初在没有出现运行时权限的时候,应用一旦被安装成功,是会被一次性授予所有需要的权限的,所以限制应用权限的唯一方案是使用AppOpsManager。但在现在,app op不但覆盖了所有的运行时权限(例如,拍照的app op是OP_CAMERA,也有对应的运行时权限Manifest.permission.CAMERA),还添加了一些没有对应运行时权限的操作(例如,读剪贴板的app op是OP_READ_CLIPBOARD,却没有对应的运行时权限)。
  此外,AppOpsManager提供了跟踪记录的功能,以方便开发者了解系统敏感操作的访问记录,使用noteOp(String, int, String)/startOp(String, int, String)可以让系统执行记录,而使用unsafeCheckOp(String, int, String),系统不会执行记录。noteOp/startOp/unsafeCheckOp在记录敏感操作信息的同时,还有一个返回值,开发者可以根据这个返回值决定下一步操作。
  返回值有:
  1.MODE_ALLOWED:访问者可以访问该敏感操作;
  2.MODE_IGNORED:访问者不可以访问该敏感操作,但是不会引发crash;
  3.MODE_ERRORED:访问者不可以访问该敏感操作,会引发crash;
  4.MODE_DEFAULT:访问者来决定访问该敏感操作的准入规则。
  为了简化叙述,下面将访问者调用调用startOp(xxx)系列的函数(例如startOp,startOpNoThrow等)并返回允许访问的事件称为start一个Op;将访问者调用调用noteOp(xxx)系列的函数(例如noteOp,noteOpNoThrow,noteProxyOp,noteProxyOpNoThrow等)并返回允许访问的事件称为note一个op。

AppOpsManager重要成员

Op Code

  Android 10目前定义了91个op code。可以自定义添加op code,但是要按开头处的注释完成步骤:
  1.增加_NUM_OP的数目;
  2.定义OPSTR_* 字符串常量;
  3.在sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault添加相应的项;
  4.在Settings/res/values/arrays.xml中添加相应的描述字段;
  5.添加app op到设置app的OpsTemplate中,完成展示分组。
  鉴于当前版本的设置已经隐藏了app op的相关入口,4&5点可以忽略。

frameworks/base/core/java/android/app/AppOpsManager.java

    // when adding one of these:
    //  - increment _NUM_OP
    //  - define an OPSTR_* constant (marked as @SystemApi)
    //  - add rows to sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault
    //  - add descriptive strings to Settings/res/values/arrays.xml
    //  - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)

    /** @hide No operation specified. */
    @UnsupportedAppUsage
    public static final int OP_NONE = -1;
    /** @hide Access to coarse location information. */
    @UnsupportedAppUsage
    @TestApi
    public static final int OP_COARSE_LOCATION = 0;
    /** @hide Access to fine location information. */
    @UnsupportedAppUsage
    public static final int OP_FINE_LOCATION = 1;
    /** @hide Causing GPS to run. */
    @UnsupportedAppUsage
    public static final int OP_GPS = 2;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_VIBRATE = 3;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_READ_CONTACTS = 4;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WRITE_CONTACTS = 5;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_READ_CALL_LOG = 6;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WRITE_CALL_LOG = 7;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_READ_CALENDAR = 8;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WRITE_CALENDAR = 9;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WIFI_SCAN = 10;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_POST_NOTIFICATION = 11;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_NEIGHBORING_CELLS = 12;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_CALL_PHONE = 13;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_READ_SMS = 14;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WRITE_SMS = 15;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_RECEIVE_SMS = 16;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_RECEIVE_EMERGECY_SMS = 17;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_RECEIVE_MMS = 18;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_RECEIVE_WAP_PUSH = 19;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_SEND_SMS = 20;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_READ_ICC_SMS = 21;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WRITE_ICC_SMS = 22;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WRITE_SETTINGS = 23;
    /** @hide Required to draw on top of other apps. */
    @UnsupportedAppUsage
    @TestApi
    public static final int OP_SYSTEM_ALERT_WINDOW = 24;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_ACCESS_NOTIFICATIONS = 25;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_CAMERA = 26;
    /** @hide */
    @UnsupportedAppUsage
    @TestApi
    public static final int OP_RECORD_AUDIO = 27;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_PLAY_AUDIO = 28;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_READ_CLIPBOARD = 29;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WRITE_CLIPBOARD = 30;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_TAKE_MEDIA_BUTTONS = 31;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_TAKE_AUDIO_FOCUS = 32;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_MASTER_VOLUME = 33;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_VOICE_VOLUME = 34;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_RING_VOLUME = 35;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_MEDIA_VOLUME = 36;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_ALARM_VOLUME = 37;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_WAKE_LOCK = 40;
    /** @hide Continually monitoring location data. */
    @UnsupportedAppUsage
    public static final int OP_MONITOR_LOCATION = 41;
    /** @hide Continually monitoring location data with a relatively high power request. */
    @UnsupportedAppUsage
    public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42;
    /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
    @UnsupportedAppUsage
    public static final int OP_GET_USAGE_STATS = 43;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_MUTE_MICROPHONE = 44;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_TOAST_WINDOW = 45;
    /** @hide Capture the device's display contents and/or audio */
    @UnsupportedAppUsage
    public static final int OP_PROJECT_MEDIA = 46;
    /** @hide Activate a VPN connection without user intervention. */
    @UnsupportedAppUsage
    public static final int OP_ACTIVATE_VPN = 47;
    /** @hide Access the WallpaperManagerAPI to write wallpapers. */
    @UnsupportedAppUsage
    public static final int OP_WRITE_WALLPAPER = 48;
    /** @hide Received the assist structure from an app. */
    @UnsupportedAppUsage
    public static final int OP_ASSIST_STRUCTURE = 49;
    /** @hide Received a screenshot from assist. */
    @UnsupportedAppUsage
    public static final int OP_ASSIST_SCREENSHOT = 50;
    /** @hide Read the phone state. */
    @UnsupportedAppUsage
    public static final int OP_READ_PHONE_STATE = 51;
    /** @hide Add voicemail messages to the voicemail content provider. */
    @UnsupportedAppUsage
    public static final int OP_ADD_VOICEMAIL = 52;
    /** @hide Access APIs for SIP calling over VOIP or WiFi. */
    @UnsupportedAppUsage
    public static final int OP_USE_SIP = 53;
    /** @hide Intercept outgoing calls. */
    @UnsupportedAppUsage
    public static final int OP_PROCESS_OUTGOING_CALLS = 54;
    /** @hide User the fingerprint API. */
    @UnsupportedAppUsage
    public static final int OP_USE_FINGERPRINT = 55;
    /** @hide Access to body sensors such as heart rate, etc. */
    @UnsupportedAppUsage
    public static final int OP_BODY_SENSORS = 56;
    /** @hide Read previously received cell broadcast messages. */
    @UnsupportedAppUsage
    public static final int OP_READ_CELL_BROADCASTS = 57;
    /** @hide Inject mock location into the system. */
    @UnsupportedAppUsage
    public static final int OP_MOCK_LOCATION = 58;
    /** @hide Read external storage. */
    @UnsupportedAppUsage
    public static final int OP_READ_EXTERNAL_STORAGE = 59;
    /** @hide Write external storage. */
    @UnsupportedAppUsage
    public static final int OP_WRITE_EXTERNAL_STORAGE = 60;
    /** @hide Turned on the screen. */
    @UnsupportedAppUsage
    public static final int OP_TURN_SCREEN_ON = 61;
    /** @hide Get device accounts. */
    @UnsupportedAppUsage
    public static final int OP_GET_ACCOUNTS = 62;
    /** @hide Control whether an application is allowed to run in the background. */
    @UnsupportedAppUsage
    public static final int OP_RUN_IN_BACKGROUND = 63;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_AUDIO_ACCESSIBILITY_VOLUME = 64;
    /** @hide Read the phone number. */
    @UnsupportedAppUsage
    public static final int OP_READ_PHONE_NUMBERS = 65;
    /** @hide Request package installs through package installer */
    @UnsupportedAppUsage
    public static final int OP_REQUEST_INSTALL_PACKAGES = 66;
    /** @hide Enter picture-in-picture. */
    @UnsupportedAppUsage
    public static final int OP_PICTURE_IN_PICTURE = 67;
    /** @hide Instant app start foreground service. */
    @UnsupportedAppUsage
    public static final int OP_INSTANT_APP_START_FOREGROUND = 68;
    /** @hide Answer incoming phone calls */
    @UnsupportedAppUsage
    public static final int OP_ANSWER_PHONE_CALLS = 69;
    /** @hide Run jobs when in background */
    @UnsupportedAppUsage
    public static final int OP_RUN_ANY_IN_BACKGROUND = 70;
    /** @hide Change Wi-Fi connectivity state */
    @UnsupportedAppUsage
    public static final int OP_CHANGE_WIFI_STATE = 71;
    /** @hide Request package deletion through package installer */
    @UnsupportedAppUsage
    public static final int OP_REQUEST_DELETE_PACKAGES = 72;
    /** @hide Bind an accessibility service. */
    @UnsupportedAppUsage
    public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
    /** @hide Continue handover of a call from another app */
    @UnsupportedAppUsage
    public static final int OP_ACCEPT_HANDOVER = 74;
    /** @hide Create and Manage IPsec Tunnels */
    @UnsupportedAppUsage
    public static final int OP_MANAGE_IPSEC_TUNNELS = 75;
    /** @hide Any app start foreground service. */
    @UnsupportedAppUsage
    @TestApi
    public static final int OP_START_FOREGROUND = 76;
    /** @hide */
    @UnsupportedAppUsage
    public static final int OP_BLUETOOTH_SCAN = 77;
    /** @hide Use the BiometricPrompt/BiometricManager APIs. */
    public static final int OP_USE_BIOMETRIC = 78;
    /** @hide Physical activity recognition. */
    public static final int OP_ACTIVITY_RECOGNITION = 79;
    /** @hide Financial app sms read. */
    public static final int OP_SMS_FINANCIAL_TRANSACTIONS = 80;
    /** @hide Read media of audio type. */
    public static final int OP_READ_MEDIA_AUDIO = 81;
    /** @hide Write media of audio type. */
    public static final int OP_WRITE_MEDIA_AUDIO = 82;
    /** @hide Read media of video type. */
    public static final int OP_READ_MEDIA_VIDEO = 83;
    /** @hide Write media of video type. */
    public static final int OP_WRITE_MEDIA_VIDEO = 84;
    /** @hide Read media of image type. */
    public static final int OP_READ_MEDIA_IMAGES = 85;
    /** @hide Write media of image type. */
    public static final int OP_WRITE_MEDIA_IMAGES = 86;
    /** @hide Has a legacy (non-isolated) view of storage. */
    public static final int OP_LEGACY_STORAGE = 87;
    /** @hide Accessing accessibility features */
    public static final int OP_ACCESS_ACCESSIBILITY = 88;
    /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */
    public static final int OP_READ_DEVICE_IDENTIFIERS = 89;
    /** @hide Read location metadata from media */
    public static final int OP_ACCESS_MEDIA_LOCATION = 90;

    /** @hide */
    @UnsupportedAppUsage
    public static final int _NUM_OP = 91;

sOpToSwitch

  左边的op code是开关,右边的注释是左边开关可以控制的op code。一般情况下左边的op code和右边的op code是一一对应的,也有时候是一对多的,例如,OP_COARSE_LOCATION这个op code可以控制OP_COARSE_LOCATION,OP_FINE_LOCATION和OP_GPS三个op code。sOpToSwitch数组也有91个,和op code的内容是递增对应的。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * This maps each operation to the operation that serves as the
     * switch to determine whether it is allowed.  Generally this is
     * a 1:1 mapping, but for some things (like location) that have
     * multiple low-level operations being tracked that should be
     * presented to the user as one switch then this can be used to
     * make them all controlled by the same single operation.
     */
    private static int[] sOpToSwitch = new int[] {
            OP_COARSE_LOCATION,                 // COARSE_LOCATION
            OP_COARSE_LOCATION,                 // FINE_LOCATION
            OP_COARSE_LOCATION,                 // GPS
            OP_VIBRATE,                         // VIBRATE
            OP_READ_CONTACTS,                   // READ_CONTACTS
            OP_WRITE_CONTACTS,                  // WRITE_CONTACTS
            OP_READ_CALL_LOG,                   // READ_CALL_LOG
            OP_WRITE_CALL_LOG,                  // WRITE_CALL_LOG
            OP_READ_CALENDAR,                   // READ_CALENDAR
            OP_WRITE_CALENDAR,                  // WRITE_CALENDAR
            OP_COARSE_LOCATION,                 // WIFI_SCAN
            OP_POST_NOTIFICATION,               // POST_NOTIFICATION
            OP_COARSE_LOCATION,                 // NEIGHBORING_CELLS
            OP_CALL_PHONE,                      // CALL_PHONE
            OP_READ_SMS,                        // READ_SMS
            OP_WRITE_SMS,                       // WRITE_SMS
            OP_RECEIVE_SMS,                     // RECEIVE_SMS
            OP_RECEIVE_SMS,                     // RECEIVE_EMERGECY_SMS
            OP_RECEIVE_MMS,                     // RECEIVE_MMS
            OP_RECEIVE_WAP_PUSH,                // RECEIVE_WAP_PUSH
            OP_SEND_SMS,                        // SEND_SMS
            OP_READ_SMS,                        // READ_ICC_SMS
            OP_WRITE_SMS,                       // WRITE_ICC_SMS
            OP_WRITE_SETTINGS,                  // WRITE_SETTINGS
            OP_SYSTEM_ALERT_WINDOW,             // SYSTEM_ALERT_WINDOW
            OP_ACCESS_NOTIFICATIONS,            // ACCESS_NOTIFICATIONS
            OP_CAMERA,                          // CAMERA
            OP_RECORD_AUDIO,                    // RECORD_AUDIO
            OP_PLAY_AUDIO,                      // PLAY_AUDIO
            OP_READ_CLIPBOARD,                  // READ_CLIPBOARD
            OP_WRITE_CLIPBOARD,                 // WRITE_CLIPBOARD
            OP_TAKE_MEDIA_BUTTONS,              // TAKE_MEDIA_BUTTONS
            OP_TAKE_AUDIO_FOCUS,                // TAKE_AUDIO_FOCUS
            OP_AUDIO_MASTER_VOLUME,             // AUDIO_MASTER_VOLUME
            OP_AUDIO_VOICE_VOLUME,              // AUDIO_VOICE_VOLUME
            OP_AUDIO_RING_VOLUME,               // AUDIO_RING_VOLUME
            OP_AUDIO_MEDIA_VOLUME,              // AUDIO_MEDIA_VOLUME
            OP_AUDIO_ALARM_VOLUME,              // AUDIO_ALARM_VOLUME
            OP_AUDIO_NOTIFICATION_VOLUME,       // AUDIO_NOTIFICATION_VOLUME
            OP_AUDIO_BLUETOOTH_VOLUME,          // AUDIO_BLUETOOTH_VOLUME
            OP_WAKE_LOCK,                       // WAKE_LOCK
            OP_COARSE_LOCATION,                 // MONITOR_LOCATION
            OP_COARSE_LOCATION,                 // MONITOR_HIGH_POWER_LOCATION
            OP_GET_USAGE_STATS,                 // GET_USAGE_STATS
            OP_MUTE_MICROPHONE,                 // MUTE_MICROPHONE
            OP_TOAST_WINDOW,                    // TOAST_WINDOW
            OP_PROJECT_MEDIA,                   // PROJECT_MEDIA
            OP_ACTIVATE_VPN,                    // ACTIVATE_VPN
            OP_WRITE_WALLPAPER,                 // WRITE_WALLPAPER
            OP_ASSIST_STRUCTURE,                // ASSIST_STRUCTURE
            OP_ASSIST_SCREENSHOT,               // ASSIST_SCREENSHOT
            OP_READ_PHONE_STATE,                // READ_PHONE_STATE
            OP_ADD_VOICEMAIL,                   // ADD_VOICEMAIL
            OP_USE_SIP,                         // USE_SIP
            OP_PROCESS_OUTGOING_CALLS,          // PROCESS_OUTGOING_CALLS
            OP_USE_FINGERPRINT,                 // USE_FINGERPRINT
            OP_BODY_SENSORS,                    // BODY_SENSORS
            OP_READ_CELL_BROADCASTS,            // READ_CELL_BROADCASTS
            OP_MOCK_LOCATION,                   // MOCK_LOCATION
            OP_READ_EXTERNAL_STORAGE,           // READ_EXTERNAL_STORAGE
            OP_WRITE_EXTERNAL_STORAGE,          // WRITE_EXTERNAL_STORAGE
            OP_TURN_SCREEN_ON,                  // TURN_SCREEN_ON
            OP_GET_ACCOUNTS,                    // GET_ACCOUNTS
            OP_RUN_IN_BACKGROUND,               // RUN_IN_BACKGROUND
            OP_AUDIO_ACCESSIBILITY_VOLUME,      // AUDIO_ACCESSIBILITY_VOLUME
            OP_READ_PHONE_NUMBERS,              // READ_PHONE_NUMBERS
            OP_REQUEST_INSTALL_PACKAGES,        // REQUEST_INSTALL_PACKAGES
            OP_PICTURE_IN_PICTURE,              // ENTER_PICTURE_IN_PICTURE_ON_HIDE
            OP_INSTANT_APP_START_FOREGROUND,    // INSTANT_APP_START_FOREGROUND
            OP_ANSWER_PHONE_CALLS,              // ANSWER_PHONE_CALLS
            OP_RUN_ANY_IN_BACKGROUND,           // OP_RUN_ANY_IN_BACKGROUND
            OP_CHANGE_WIFI_STATE,               // OP_CHANGE_WIFI_STATE
            OP_REQUEST_DELETE_PACKAGES,         // OP_REQUEST_DELETE_PACKAGES
            OP_BIND_ACCESSIBILITY_SERVICE,      // OP_BIND_ACCESSIBILITY_SERVICE
            OP_ACCEPT_HANDOVER,                 // ACCEPT_HANDOVER
            OP_MANAGE_IPSEC_TUNNELS,            // MANAGE_IPSEC_HANDOVERS
            OP_START_FOREGROUND,                // START_FOREGROUND
            OP_COARSE_LOCATION,                 // BLUETOOTH_SCAN
            OP_USE_BIOMETRIC,                   // BIOMETRIC
            OP_ACTIVITY_RECOGNITION,            // ACTIVITY_RECOGNITION
            OP_SMS_FINANCIAL_TRANSACTIONS,      // SMS_FINANCIAL_TRANSACTIONS
            OP_READ_MEDIA_AUDIO,                // READ_MEDIA_AUDIO
            OP_WRITE_MEDIA_AUDIO,               // WRITE_MEDIA_AUDIO
            OP_READ_MEDIA_VIDEO,                // READ_MEDIA_VIDEO
            OP_WRITE_MEDIA_VIDEO,               // WRITE_MEDIA_VIDEO
            OP_READ_MEDIA_IMAGES,               // READ_MEDIA_IMAGES
            OP_WRITE_MEDIA_IMAGES,              // WRITE_MEDIA_IMAGES
            OP_LEGACY_STORAGE,                  // LEGACY_STORAGE
            OP_ACCESS_ACCESSIBILITY,            // ACCESS_ACCESSIBILITY
            OP_READ_DEVICE_IDENTIFIERS,         // READ_DEVICE_IDENTIFIERS
            OP_ACCESS_MEDIA_LOCATION,           // ACCESS_MEDIA_LOCATION
    };

sOpPerms

  sOpPerms和sOpToSwitch一样,和op code的内容时递增对应的。sOpPerms是一个运行时和签名权限字符串数组,和op code的内容映射。例如,OP_COARSE_LOCATION映射android.Manifest.permission.ACCESS_COARSE_LOCATION权限,而OP_GPS 映射为null,说明没有对应的权限。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * This optionally maps a permission to an operation.  If there
     * is no permission associated with an operation, it is null.
     */
    @UnsupportedAppUsage
    private static String[] sOpPerms = new String[] {
            android.Manifest.permission.ACCESS_COARSE_LOCATION,
            android.Manifest.permission.ACCESS_FINE_LOCATION,
            null,
            android.Manifest.permission.VIBRATE,
            android.Manifest.permission.READ_CONTACTS,
            android.Manifest.permission.WRITE_CONTACTS,
            android.Manifest.permission.READ_CALL_LOG,
            android.Manifest.permission.WRITE_CALL_LOG,
            android.Manifest.permission.READ_CALENDAR,
            android.Manifest.permission.WRITE_CALENDAR,
            android.Manifest.permission.ACCESS_WIFI_STATE,
            null, // no permission required for notifications
            null, // neighboring cells shares the coarse location perm
            android.Manifest.permission.CALL_PHONE,
            android.Manifest.permission.READ_SMS,
            null, // no permission required for writing sms
            android.Manifest.permission.RECEIVE_SMS,
            android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
            android.Manifest.permission.RECEIVE_MMS,
            android.Manifest.permission.RECEIVE_WAP_PUSH,
            android.Manifest.permission.SEND_SMS,
            android.Manifest.permission.READ_SMS,
            null, // no permission required for writing icc sms
            android.Manifest.permission.WRITE_SETTINGS,
            android.Manifest.permission.SYSTEM_ALERT_WINDOW,
            android.Manifest.permission.ACCESS_NOTIFICATIONS,
            android.Manifest.permission.CAMERA,
            android.Manifest.permission.RECORD_AUDIO,
            null, // no permission for playing audio
            null, // no permission for reading clipboard
            null, // no permission for writing clipboard
            null, // no permission for taking media buttons
            null, // no permission for taking audio focus
            null, // no permission for changing master volume
            null, // no permission for changing voice volume
            null, // no permission for changing ring volume
            null, // no permission for changing media volume
            null, // no permission for changing alarm volume
            null, // no permission for changing notification volume
            null, // no permission for changing bluetooth volume
            android.Manifest.permission.WAKE_LOCK,
            null, // no permission for generic location monitoring
            null, // no permission for high power location monitoring
            android.Manifest.permission.PACKAGE_USAGE_STATS,
            null, // no permission for muting/unmuting microphone
            null, // no permission for displaying toasts
            null, // no permission for projecting media
            null, // no permission for activating 
            null, // no permission for supporting wallpaper
            null, // no permission for receiving assist structure
            null, // no permission for receiving assist screenshot
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.ADD_VOICEMAIL,
            Manifest.permission.USE_SIP,
            Manifest.permission.PROCESS_OUTGOING_CALLS,
            Manifest.permission.USE_FINGERPRINT,
            Manifest.permission.BODY_SENSORS,
            Manifest.permission.READ_CELL_BROADCASTS,
            null,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            null, // no permission for turning the screen on
            Manifest.permission.GET_ACCOUNTS,
            null, // no permission for running in background
            null, // no permission for changing accessibility volume
            Manifest.permission.READ_PHONE_NUMBERS,
            Manifest.permission.REQUEST_INSTALL_PACKAGES,
            null, // no permission for entering picture-in-picture on hide
            Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
            Manifest.permission.ANSWER_PHONE_CALLS,
            null, // no permission for OP_RUN_ANY_IN_BACKGROUND
            Manifest.permission.CHANGE_WIFI_STATE,
            Manifest.permission.REQUEST_DELETE_PACKAGES,
            Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
            Manifest.permission.ACCEPT_HANDOVER,
            null, // no permission for OP_MANAGE_IPSEC_TUNNELS
            Manifest.permission.FOREGROUND_SERVICE,
            null, // no permission for OP_BLUETOOTH_SCAN
            Manifest.permission.USE_BIOMETRIC,
            Manifest.permission.ACTIVITY_RECOGNITION,
            Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
            null,
            null, // no permission for OP_WRITE_MEDIA_AUDIO
            null,
            null, // no permission for OP_WRITE_MEDIA_VIDEO
            null,
            null, // no permission for OP_WRITE_MEDIA_IMAGES
            null, // no permission for OP_LEGACY_STORAGE
            null, // no permission for OP_ACCESS_ACCESSIBILITY
            null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
            Manifest.permission.ACCESS_MEDIA_LOCATION,
    };

sOpToString

  sOpToString描述了op code和描述字符串的映射。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * This maps each operation to the public string constant for it.
     */
    private static String[] sOpToString = new String[]{
            OPSTR_COARSE_LOCATION,
            OPSTR_FINE_LOCATION,
            OPSTR_GPS,
            OPSTR_VIBRATE,
            OPSTR_READ_CONTACTS,
            OPSTR_WRITE_CONTACTS,
            OPSTR_READ_CALL_LOG,
            OPSTR_WRITE_CALL_LOG,
            OPSTR_READ_CALENDAR,
            OPSTR_WRITE_CALENDAR,
            OPSTR_WIFI_SCAN,
            OPSTR_POST_NOTIFICATION,
            OPSTR_NEIGHBORING_CELLS,
            OPSTR_CALL_PHONE,
            OPSTR_READ_SMS,
            OPSTR_WRITE_SMS,
            OPSTR_RECEIVE_SMS,
            OPSTR_RECEIVE_EMERGENCY_BROADCAST,
            OPSTR_RECEIVE_MMS,
            OPSTR_RECEIVE_WAP_PUSH,
            OPSTR_SEND_SMS,
            OPSTR_READ_ICC_SMS,
            OPSTR_WRITE_ICC_SMS,
            OPSTR_WRITE_SETTINGS,
            OPSTR_SYSTEM_ALERT_WINDOW,
            OPSTR_ACCESS_NOTIFICATIONS,
            OPSTR_CAMERA,
            OPSTR_RECORD_AUDIO,
            OPSTR_PLAY_AUDIO,
            OPSTR_READ_CLIPBOARD,
            OPSTR_WRITE_CLIPBOARD,
            OPSTR_TAKE_MEDIA_BUTTONS,
            OPSTR_TAKE_AUDIO_FOCUS,
            OPSTR_AUDIO_MASTER_VOLUME,
            OPSTR_AUDIO_VOICE_VOLUME,
            OPSTR_AUDIO_RING_VOLUME,
            OPSTR_AUDIO_MEDIA_VOLUME,
            OPSTR_AUDIO_ALARM_VOLUME,
            OPSTR_AUDIO_NOTIFICATION_VOLUME,
            OPSTR_AUDIO_BLUETOOTH_VOLUME,
            OPSTR_WAKE_LOCK,
            OPSTR_MONITOR_LOCATION,
            OPSTR_MONITOR_HIGH_POWER_LOCATION,
            OPSTR_GET_USAGE_STATS,
            OPSTR_MUTE_MICROPHONE,
            OPSTR_TOAST_WINDOW,
            OPSTR_PROJECT_MEDIA,
            OPSTR_ACTIVATE_VPN,
            OPSTR_WRITE_WALLPAPER,
            OPSTR_ASSIST_STRUCTURE,
            OPSTR_ASSIST_SCREENSHOT,
            OPSTR_READ_PHONE_STATE,
            OPSTR_ADD_VOICEMAIL,
            OPSTR_USE_SIP,
            OPSTR_PROCESS_OUTGOING_CALLS,
            OPSTR_USE_FINGERPRINT,
            OPSTR_BODY_SENSORS,
            OPSTR_READ_CELL_BROADCASTS,
            OPSTR_MOCK_LOCATION,
            OPSTR_READ_EXTERNAL_STORAGE,
            OPSTR_WRITE_EXTERNAL_STORAGE,
            OPSTR_TURN_SCREEN_ON,
            OPSTR_GET_ACCOUNTS,
            OPSTR_RUN_IN_BACKGROUND,
            OPSTR_AUDIO_ACCESSIBILITY_VOLUME,
            OPSTR_READ_PHONE_NUMBERS,
            OPSTR_REQUEST_INSTALL_PACKAGES,
            OPSTR_PICTURE_IN_PICTURE,
            OPSTR_INSTANT_APP_START_FOREGROUND,
            OPSTR_ANSWER_PHONE_CALLS,
            OPSTR_RUN_ANY_IN_BACKGROUND,
            OPSTR_CHANGE_WIFI_STATE,
            OPSTR_REQUEST_DELETE_PACKAGES,
            OPSTR_BIND_ACCESSIBILITY_SERVICE,
            OPSTR_ACCEPT_HANDOVER,
            OPSTR_MANAGE_IPSEC_TUNNELS,
            OPSTR_START_FOREGROUND,
            OPSTR_BLUETOOTH_SCAN,
            OPSTR_USE_BIOMETRIC,
            OPSTR_ACTIVITY_RECOGNITION,
            OPSTR_SMS_FINANCIAL_TRANSACTIONS,
            OPSTR_READ_MEDIA_AUDIO,
            OPSTR_WRITE_MEDIA_AUDIO,
            OPSTR_READ_MEDIA_VIDEO,
            OPSTR_WRITE_MEDIA_VIDEO,
            OPSTR_READ_MEDIA_IMAGES,
            OPSTR_WRITE_MEDIA_IMAGES,
            OPSTR_LEGACY_STORAGE,
            OPSTR_ACCESS_ACCESSIBILITY,
            OPSTR_READ_DEVICE_IDENTIFIERS,
            OPSTR_ACCESS_MEDIA_LOCATION,
    };

sOpDefaultMode

  sOpDefaultMode描述了一个op code的默认授权情况,例如OP_COARSE_LOCATION的默认授权情况总是MODE_ALLOWED的。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * This specifies the default mode for each operation.
     */
    private static int[] sOpDefaultMode = new int[] {
            AppOpsManager.MODE_ALLOWED, // COARSE_LOCATION
            AppOpsManager.MODE_ALLOWED, // FINE_LOCATION
            AppOpsManager.MODE_ALLOWED, // GPS
            AppOpsManager.MODE_ALLOWED, // VIBRATE
            AppOpsManager.MODE_ALLOWED, // READ_CONTACTS
            AppOpsManager.MODE_ALLOWED, // WRITE_CONTACTS
            AppOpsManager.MODE_ALLOWED, // READ_CALL_LOG
            AppOpsManager.MODE_ALLOWED, // WRITE_CALL_LOG
            AppOpsManager.MODE_ALLOWED, // READ_CALENDAR
            AppOpsManager.MODE_ALLOWED, // WRITE_CALENDAR
            AppOpsManager.MODE_ALLOWED, // WIFI_SCAN
            AppOpsManager.MODE_ALLOWED, // POST_NOTIFICATION
            AppOpsManager.MODE_ALLOWED, // NEIGHBORING_CELLS
            AppOpsManager.MODE_ALLOWED, // CALL_PHONE
            AppOpsManager.MODE_ALLOWED, // READ_SMS
            AppOpsManager.MODE_IGNORED, // WRITE_SMS
            AppOpsManager.MODE_ALLOWED, // RECEIVE_SMS
            AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST
            AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS
            AppOpsManager.MODE_ALLOWED, // RECEIVE_WAP_PUSH
            AppOpsManager.MODE_ALLOWED, // SEND_SMS
            AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
            AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
            AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
            getSystemAlertWindowDefault(), // SYSTEM_ALERT_WINDOW
            AppOpsManager.MODE_ALLOWED, // ACCESS_NOTIFICATIONS
            AppOpsManager.MODE_ALLOWED, // CAMERA
            AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO
            AppOpsManager.MODE_ALLOWED, // PLAY_AUDIO
            AppOpsManager.MODE_ALLOWED, // READ_CLIPBOARD
            AppOpsManager.MODE_ALLOWED, // WRITE_CLIPBOARD
            AppOpsManager.MODE_ALLOWED, // TAKE_MEDIA_BUTTONS
            AppOpsManager.MODE_ALLOWED, // TAKE_AUDIO_FOCUS
            AppOpsManager.MODE_ALLOWED, // AUDIO_MASTER_VOLUME
            AppOpsManager.MODE_ALLOWED, // AUDIO_VOICE_VOLUME
            AppOpsManager.MODE_ALLOWED, // AUDIO_RING_VOLUME
            AppOpsManager.MODE_ALLOWED, // AUDIO_MEDIA_VOLUME
            AppOpsManager.MODE_ALLOWED, // AUDIO_ALARM_VOLUME
            AppOpsManager.MODE_ALLOWED, // AUDIO_NOTIFICATION_VOLUME
            AppOpsManager.MODE_ALLOWED, // AUDIO_BLUETOOTH_VOLUME
            AppOpsManager.MODE_ALLOWED, // WAKE_LOCK
            AppOpsManager.MODE_ALLOWED, // MONITOR_LOCATION
            AppOpsManager.MODE_ALLOWED, // MONITOR_HIGH_POWER_LOCATION
            AppOpsManager.MODE_DEFAULT, // GET_USAGE_STATS
            AppOpsManager.MODE_ALLOWED, // MUTE_MICROPHONE
            AppOpsManager.MODE_ALLOWED, // TOAST_WINDOW
            AppOpsManager.MODE_IGNORED, // PROJECT_MEDIA
            AppOpsManager.MODE_IGNORED, // ACTIVATE_VPN
            AppOpsManager.MODE_ALLOWED, // WRITE_WALLPAPER
            AppOpsManager.MODE_ALLOWED, // ASSIST_STRUCTURE
            AppOpsManager.MODE_ALLOWED, // ASSIST_SCREENSHOT
            AppOpsManager.MODE_ALLOWED, // READ_PHONE_STATE
            AppOpsManager.MODE_ALLOWED, // ADD_VOICEMAIL
            AppOpsManager.MODE_ALLOWED, // USE_SIP
            AppOpsManager.MODE_ALLOWED, // PROCESS_OUTGOING_CALLS
            AppOpsManager.MODE_ALLOWED, // USE_FINGERPRINT
            AppOpsManager.MODE_ALLOWED, // BODY_SENSORS
            AppOpsManager.MODE_ALLOWED, // READ_CELL_BROADCASTS
            AppOpsManager.MODE_ERRORED, // MOCK_LOCATION
            AppOpsManager.MODE_ALLOWED, // READ_EXTERNAL_STORAGE
            AppOpsManager.MODE_ALLOWED, // WRITE_EXTERNAL_STORAGE
            AppOpsManager.MODE_ALLOWED, // TURN_SCREEN_ON
            AppOpsManager.MODE_ALLOWED, // GET_ACCOUNTS
            AppOpsManager.MODE_ALLOWED, // RUN_IN_BACKGROUND
            AppOpsManager.MODE_ALLOWED, // AUDIO_ACCESSIBILITY_VOLUME
            AppOpsManager.MODE_ALLOWED, // READ_PHONE_NUMBERS
            AppOpsManager.MODE_DEFAULT, // REQUEST_INSTALL_PACKAGES
            AppOpsManager.MODE_ALLOWED, // PICTURE_IN_PICTURE
            AppOpsManager.MODE_DEFAULT, // INSTANT_APP_START_FOREGROUND
            AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
            AppOpsManager.MODE_ALLOWED, // RUN_ANY_IN_BACKGROUND
            AppOpsManager.MODE_ALLOWED, // CHANGE_WIFI_STATE
            AppOpsManager.MODE_ALLOWED, // REQUEST_DELETE_PACKAGES
            AppOpsManager.MODE_ALLOWED, // BIND_ACCESSIBILITY_SERVICE
            AppOpsManager.MODE_ALLOWED, // ACCEPT_HANDOVER
            AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
            AppOpsManager.MODE_ALLOWED, // START_FOREGROUND
            AppOpsManager.MODE_ALLOWED, // BLUETOOTH_SCAN
            AppOpsManager.MODE_ALLOWED, // USE_BIOMETRIC
            AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
            AppOpsManager.MODE_DEFAULT, // SMS_FINANCIAL_TRANSACTIONS
            AppOpsManager.MODE_ALLOWED, // READ_MEDIA_AUDIO
            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_AUDIO
            AppOpsManager.MODE_ALLOWED, // READ_MEDIA_VIDEO
            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
            AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
            AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
            AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
            AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
            AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
    };

sOpStrToOp

  sOpStrToOp是op描述字符串对op code的映射。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * Mapping from an app op name to the app op code.
     */
    private static HashMap sOpStrToOp = new HashMap<>();
    ...
        for (int i=0; i<_NUM_OP; i++) {
            if (sOpToString[i] != null) {
                sOpStrToOp.put(sOpToString[i], i);
            }
        }

sPermToOp

  sPermToOp是权限名对op code的映射。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * Mapping from a permission to the corresponding app op.
     */
    private static HashMap sPermToOp = new HashMap<>();
    ...
            for (int op : RUNTIME_AND_APPOP_PERMISSIONS_OPS) {
            if (sOpPerms[op] != null) {
                sPermToOp.put(sOpPerms[op], op);
            }
        }

sOpRestrictions

  op code对用户限制的映射,用户限制可以为null。如果一个op code被添加了用户限制,那么在限制用户下使用startOp/noteOp/unsafeCheckOp是返回AppOpsManager.MODE_IGNORED的。如下面所示,OP_COARSE_LOCATION这个op code映射了DISALLOW_SHARE_LOCATION,但是这个用户限制不一定生效,还需要使用DevicePolicyManager#addUserRestriction(ComponentName, String)设置后才会生效。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * Specifies whether an Op should be restricted by a user restriction.
     * Each Op should be filled with a restriction string from UserManager or
     * null to specify it is not affected by any user restriction.
     */
    private static String[] sOpRestrictions = new String[] {
            UserManager.DISALLOW_SHARE_LOCATION, //COARSE_LOCATION
            UserManager.DISALLOW_SHARE_LOCATION, //FINE_LOCATION
            UserManager.DISALLOW_SHARE_LOCATION, //GPS
            null, //VIBRATE
            null, //READ_CONTACTS
            null, //WRITE_CONTACTS
            UserManager.DISALLOW_OUTGOING_CALLS, //READ_CALL_LOG
            UserManager.DISALLOW_OUTGOING_CALLS, //WRITE_CALL_LOG
            null, //READ_CALENDAR
            null, //WRITE_CALENDAR
            UserManager.DISALLOW_SHARE_LOCATION, //WIFI_SCAN
            null, //POST_NOTIFICATION
            null, //NEIGHBORING_CELLS
            null, //CALL_PHONE
            UserManager.DISALLOW_SMS, //READ_SMS
            UserManager.DISALLOW_SMS, //WRITE_SMS
            UserManager.DISALLOW_SMS, //RECEIVE_SMS
            null, //RECEIVE_EMERGENCY_SMS
            UserManager.DISALLOW_SMS, //RECEIVE_MMS
            null, //RECEIVE_WAP_PUSH
            UserManager.DISALLOW_SMS, //SEND_SMS
            UserManager.DISALLOW_SMS, //READ_ICC_SMS
            UserManager.DISALLOW_SMS, //WRITE_ICC_SMS
            null, //WRITE_SETTINGS
            UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
            null, //ACCESS_NOTIFICATIONS
            UserManager.DISALLOW_CAMERA, //CAMERA
            UserManager.DISALLOW_RECORD_AUDIO, //RECORD_AUDIO
            null, //PLAY_AUDIO
            null, //READ_CLIPBOARD
            null, //WRITE_CLIPBOARD
            null, //TAKE_MEDIA_BUTTONS
            null, //TAKE_AUDIO_FOCUS
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_MASTER_VOLUME
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_VOICE_VOLUME
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_RING_VOLUME
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_MEDIA_VOLUME
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_ALARM_VOLUME
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_NOTIFICATION_VOLUME
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_BLUETOOTH_VOLUME
            null, //WAKE_LOCK
            UserManager.DISALLOW_SHARE_LOCATION, //MONITOR_LOCATION
            UserManager.DISALLOW_SHARE_LOCATION, //MONITOR_HIGH_POWER_LOCATION
            null, //GET_USAGE_STATS
            UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
            UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
            null, //PROJECT_MEDIA
            null, // ACTIVATE_VPN
            UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER
            null, // ASSIST_STRUCTURE
            null, // ASSIST_SCREENSHOT
            null, // READ_PHONE_STATE
            null, // ADD_VOICEMAIL
            null, // USE_SIP
            null, // PROCESS_OUTGOING_CALLS
            null, // USE_FINGERPRINT
            null, // BODY_SENSORS
            null, // READ_CELL_BROADCASTS
            null, // MOCK_LOCATION
            null, // READ_EXTERNAL_STORAGE
            null, // WRITE_EXTERNAL_STORAGE
            null, // TURN_ON_SCREEN
            null, // GET_ACCOUNTS
            null, // RUN_IN_BACKGROUND
            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_ACCESSIBILITY_VOLUME
            null, // READ_PHONE_NUMBERS
            null, // REQUEST_INSTALL_PACKAGES
            null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
            null, // INSTANT_APP_START_FOREGROUND
            null, // ANSWER_PHONE_CALLS
            null, // OP_RUN_ANY_IN_BACKGROUND
            null, // OP_CHANGE_WIFI_STATE
            null, // REQUEST_DELETE_PACKAGES
            null, // OP_BIND_ACCESSIBILITY_SERVICE
            null, // ACCEPT_HANDOVER
            null, // MANAGE_IPSEC_TUNNELS
            null, // START_FOREGROUND
            null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
            null, // USE_BIOMETRIC
            null, // ACTIVITY_RECOGNITION
            UserManager.DISALLOW_SMS, // SMS_FINANCIAL_TRANSACTIONS
            null, // READ_MEDIA_AUDIO
            null, // WRITE_MEDIA_AUDIO
            null, // READ_MEDIA_VIDEO
            null, // WRITE_MEDIA_VIDEO
            null, // READ_MEDIA_IMAGES
            null, // WRITE_MEDIA_IMAGES
            null, // LEGACY_STORAGE
            null, // ACCESS_ACCESSIBILITY
            null, // READ_DEVICE_IDENTIFIERS
            null, // ACCESS_MEDIA_LOCATION
    };

sOpAllowSystemRestrictionBypass

  sOpAllowSystemRestrictionBypass描述了是否允许系统组件绕过用户限制(在用户限制被激活的情况下)。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * This specifies whether each option should allow the system
     * (and system ui) to bypass the user restriction when active.
     */
    private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] {
            true, //COARSE_LOCATION
            true, //FINE_LOCATION
            false, //GPS
            false, //VIBRATE
            false, //READ_CONTACTS
            false, //WRITE_CONTACTS
            false, //READ_CALL_LOG
            false, //WRITE_CALL_LOG
            false, //READ_CALENDAR
            false, //WRITE_CALENDAR
            true, //WIFI_SCAN
            false, //POST_NOTIFICATION
            false, //NEIGHBORING_CELLS
            false, //CALL_PHONE
            false, //READ_SMS
            false, //WRITE_SMS
            false, //RECEIVE_SMS
            false, //RECEIVE_EMERGECY_SMS
            false, //RECEIVE_MMS
            false, //RECEIVE_WAP_PUSH
            false, //SEND_SMS
            false, //READ_ICC_SMS
            false, //WRITE_ICC_SMS
            false, //WRITE_SETTINGS
            true, //SYSTEM_ALERT_WINDOW
            false, //ACCESS_NOTIFICATIONS
            false, //CAMERA
            false, //RECORD_AUDIO
            false, //PLAY_AUDIO
            false, //READ_CLIPBOARD
            false, //WRITE_CLIPBOARD
            false, //TAKE_MEDIA_BUTTONS
            false, //TAKE_AUDIO_FOCUS
            false, //AUDIO_MASTER_VOLUME
            false, //AUDIO_VOICE_VOLUME
            false, //AUDIO_RING_VOLUME
            false, //AUDIO_MEDIA_VOLUME
            false, //AUDIO_ALARM_VOLUME
            false, //AUDIO_NOTIFICATION_VOLUME
            false, //AUDIO_BLUETOOTH_VOLUME
            false, //WAKE_LOCK
            false, //MONITOR_LOCATION
            false, //MONITOR_HIGH_POWER_LOCATION
            false, //GET_USAGE_STATS
            false, //MUTE_MICROPHONE
            true, //TOAST_WINDOW
            false, //PROJECT_MEDIA
            false, //ACTIVATE_VPN
            false, //WALLPAPER
            false, //ASSIST_STRUCTURE
            false, //ASSIST_SCREENSHOT
            false, //READ_PHONE_STATE
            false, //ADD_VOICEMAIL
            false, // USE_SIP
            false, // PROCESS_OUTGOING_CALLS
            false, // USE_FINGERPRINT
            false, // BODY_SENSORS
            false, // READ_CELL_BROADCASTS
            false, // MOCK_LOCATION
            false, // READ_EXTERNAL_STORAGE
            false, // WRITE_EXTERNAL_STORAGE
            false, // TURN_ON_SCREEN
            false, // GET_ACCOUNTS
            false, // RUN_IN_BACKGROUND
            false, // AUDIO_ACCESSIBILITY_VOLUME
            false, // READ_PHONE_NUMBERS
            false, // REQUEST_INSTALL_PACKAGES
            false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
            false, // INSTANT_APP_START_FOREGROUND
            false, // ANSWER_PHONE_CALLS
            false, // OP_RUN_ANY_IN_BACKGROUND
            false, // OP_CHANGE_WIFI_STATE
            false, // OP_REQUEST_DELETE_PACKAGES
            false, // OP_BIND_ACCESSIBILITY_SERVICE
            false, // ACCEPT_HANDOVER
            false, // MANAGE_IPSEC_HANDOVERS
            false, // START_FOREGROUND
            true, // BLUETOOTH_SCAN
            false, // USE_BIOMETRIC
            false, // ACTIVITY_RECOGNITION
            false, // SMS_FINANCIAL_TRANSACTIONS
            false, // READ_MEDIA_AUDIO
            false, // WRITE_MEDIA_AUDIO
            false, // READ_MEDIA_VIDEO
            false, // WRITE_MEDIA_VIDEO
            false, // READ_MEDIA_IMAGES
            false, // WRITE_MEDIA_IMAGES
            false, // LEGACY_STORAGE
            false, // ACCESS_ACCESSIBILITY
            false, // READ_DEVICE_IDENTIFIERS
            false, // ACCESS_MEDIA_LOCATION
    };

sOpDisableReset

  sOpDisableReset用来指定是否允许在重置所有应用偏好设置后,重置 Operation 的授予情况,true 表示禁止重置,false 表示允许重置。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * This specifies whether each option is allowed to be reset
     * when resetting all app preferences.  Disable reset for
     * app ops that are under strong control of some part of the
     * system (such as OP_WRITE_SMS, which should be allowed only
     * for whichever app is selected as the current SMS app).
     */
    private static boolean[] sOpDisableReset = new boolean[] {
            false, // COARSE_LOCATION
            false, // FINE_LOCATION
            false, // GPS
            false, // VIBRATE
            false, // READ_CONTACTS
            false, // WRITE_CONTACTS
            false, // READ_CALL_LOG
            false, // WRITE_CALL_LOG
            false, // READ_CALENDAR
            false, // WRITE_CALENDAR
            false, // WIFI_SCAN
            false, // POST_NOTIFICATION
            false, // NEIGHBORING_CELLS
            false, // CALL_PHONE
            true, // READ_SMS
            true, // WRITE_SMS
            true, // RECEIVE_SMS
            false, // RECEIVE_EMERGENCY_BROADCAST
            false, // RECEIVE_MMS
            true, // RECEIVE_WAP_PUSH
            true, // SEND_SMS
            false, // READ_ICC_SMS
            false, // WRITE_ICC_SMS
            false, // WRITE_SETTINGS
            false, // SYSTEM_ALERT_WINDOW
            false, // ACCESS_NOTIFICATIONS
            false, // CAMERA
            false, // RECORD_AUDIO
            false, // PLAY_AUDIO
            false, // READ_CLIPBOARD
            false, // WRITE_CLIPBOARD
            false, // TAKE_MEDIA_BUTTONS
            false, // TAKE_AUDIO_FOCUS
            false, // AUDIO_MASTER_VOLUME
            false, // AUDIO_VOICE_VOLUME
            false, // AUDIO_RING_VOLUME
            false, // AUDIO_MEDIA_VOLUME
            false, // AUDIO_ALARM_VOLUME
            false, // AUDIO_NOTIFICATION_VOLUME
            false, // AUDIO_BLUETOOTH_VOLUME
            false, // WAKE_LOCK
            false, // MONITOR_LOCATION
            false, // MONITOR_HIGH_POWER_LOCATION
            false, // GET_USAGE_STATS
            false, // MUTE_MICROPHONE
            false, // TOAST_WINDOW
            false, // PROJECT_MEDIA
            false, // ACTIVATE_VPN
            false, // WRITE_WALLPAPER
            false, // ASSIST_STRUCTURE
            false, // ASSIST_SCREENSHOT
            false, // READ_PHONE_STATE
            false, // ADD_VOICEMAIL
            false, // USE_SIP
            false, // PROCESS_OUTGOING_CALLS
            false, // USE_FINGERPRINT
            false, // BODY_SENSORS
            true, // READ_CELL_BROADCASTS
            false, // MOCK_LOCATION
            false, // READ_EXTERNAL_STORAGE
            false, // WRITE_EXTERNAL_STORAGE
            false, // TURN_SCREEN_ON
            false, // GET_ACCOUNTS
            false, // RUN_IN_BACKGROUND
            false, // AUDIO_ACCESSIBILITY_VOLUME
            false, // READ_PHONE_NUMBERS
            false, // REQUEST_INSTALL_PACKAGES
            false, // PICTURE_IN_PICTURE
            false, // INSTANT_APP_START_FOREGROUND
            false, // ANSWER_PHONE_CALLS
            false, // RUN_ANY_IN_BACKGROUND
            false, // CHANGE_WIFI_STATE
            false, // REQUEST_DELETE_PACKAGES
            false, // BIND_ACCESSIBILITY_SERVICE
            false, // ACCEPT_HANDOVER
            false, // MANAGE_IPSEC_TUNNELS
            false, // START_FOREGROUND
            false, // BLUETOOTH_SCAN
            false, // USE_BIOMETRIC
            false, // ACTIVITY_RECOGNITION
            false, // SMS_FINANCIAL_TRANSACTIONS
            false, // READ_MEDIA_AUDIO
            false, // WRITE_MEDIA_AUDIO
            false, // READ_MEDIA_VIDEO
            false, // WRITE_MEDIA_VIDEO
            false, // READ_MEDIA_IMAGES
            false, // WRITE_MEDIA_IMAGES
            false, // LEGACY_STORAGE
            false, // ACCESS_ACCESSIBILITY
            false, // READ_DEVICE_IDENTIFIERS
            false, // ACCESS_MEDIA_LOCATION
    };

AppOpsService重要成员

Op

  Op数据结构描述了一个敏感操作(Op)的具体信息。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    final static class Op {
        int op;//Op code
        boolean running;//Op是否正在运行,可由AppOpsManager#startOp(XXX)返回允许授权时设置
        final UidState uidState;//所在的UidState
        final @NonNull String packageName;//发起敏感操作者的包名

        private @Mode int mode;//授权结果,有默认值,参考AppOpsManager#sOpDefaultMode
        private @Nullable LongSparseLongArray mAccessTimes;//一个键为固定唯一数值,值为准入时间(准入时间可由AppOpsManager#noteOp(XXX)返回允许授权时设置)的LongSparseLongArray
        private @Nullable LongSparseLongArray mRejectTimes;//一个键为固定唯一数值,值为被拒绝时间(被拒绝时间在startOp(XXX)或者noteOp(XXX)返回非允许授权时设置)的LongSparseLongArray
        private @Nullable LongSparseLongArray mDurations;//一个键为固定唯一数值,值为持续时间(即调用start一个op到调用finishOp经历的时间)的LongSparseLongArray
        private @Nullable LongSparseLongArray mProxyUids;//一个键为固定唯一数值,值为发起敏感操作的uid的LongSparseLongArray
        private @Nullable LongSparseArray mProxyPackageNames;//一个键为固定唯一数值,值为发起敏感操作者包名的LongSparseArray

        int startNesting;//启动次数。每次start这个op,该值会加1;finish这个op,该值会减1
        long startRealtime;//该Op被首次start成功的时间

Ops

  Ops,顾名思义,就是Op的复数形式,继承自SparseArray< Op>,是一个以op code为键,Op为值的数据结构。发起敏感操作者的包名又会和Ops组成一个ArrayMap,存放在UidState类的pkgOps成员中,记录每个包名的所有Op信息。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    final static class Ops extends SparseArray {
        final String packageName;//发起敏感操作者包名
        final UidState uidState;//Uid状态UidState
        final boolean isPrivileged;//发起敏感操作者是否是特权应用

        Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
            packageName = _packageName;
            uidState = _uidState;
            isPrivileged = _isPrivileged;
        }
    }

mUidStates

  mUidStates是一个SparseArray,key为uid,值为一个UidState。mUidStates目的在于建立一个UID关于op code的状态记录。
UidState的成员如下。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    static final class UidState {
        public final int uid;//记录的uid

        public int state = UID_STATE_CACHED;//和进程状态关联的uid状态,已提交状态
        public int pendingState = UID_STATE_CACHED;///和进程状态关联的uid状态,预设状态
        public long pendingStateCommitTime;//预设uid state的时间戳

        public int startNesting;//启动次数,也就是该UidState包含的所有op当前被start的次数,每次有包含在内的op被start了,该值加1;如果有包含在内的op被finish了,则要减去1
        public ArrayMap pkgOps;//包名为键,Ops为值的ArrayMap
        public SparseIntArray opModes;//op code为键,授权结果为值的SparseIntArray

        // true indicates there is an interested observer, false there isn't but it has such an op
        public SparseBooleanArray foregroundOps;//授权结果是MODE_FOREGROUND(前台允许)的op code为键,Boolean值为值,当这个前台允许的op code被使用了带WATCH_FOREGROUND_CHANGES的flag的startWatchingMode来监控时,Boolean值为true,否则为false
        public boolean hasForegroundWatchers;//是否有前台允许的op code被使用了带WATCH_FOREGROUND_CHANGES的flag的startWatchingMode来监控

  在计算进程的oom值的updateOomAdjLocked函数中,会把进程的状态传递给AppOpsService,从而让AppOpsService更新uid状态。PROCESS_STATE_TO_UID_STATE是进程状态对uid状态的映射。uid状态优先级和进程状态一样,随着数值的增大,优先级逐渐下降。
  如果是从updateOomAdjLocked之后,uid状态优先级有提升,马上把已提交状态的state设置为预设状态的pendingState。如果uid状态优先级下降了,则只更新预设状态的pendingState,已提交状态的state会选择在合适的时机(例如再次获取该UidState)更新为pendingState的值。这样做的原因可能是让进程的高优先级能维持一段时间吧。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    // Map from process states to the uid states we track.
    private static final int[] PROCESS_STATE_TO_UID_STATE = new int[] {
        UID_STATE_PERSISTENT,           // ActivityManager.PROCESS_STATE_PERSISTENT
        UID_STATE_PERSISTENT,           // ActivityManager.PROCESS_STATE_PERSISTENT_UI
        UID_STATE_TOP,                  // ActivityManager.PROCESS_STATE_TOP
        UID_STATE_FOREGROUND_SERVICE_LOCATION,
                                        // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION
        UID_STATE_FOREGROUND,           // ActivityManager.PROCESS_STATE_BOUND_TOP
        UID_STATE_FOREGROUND_SERVICE,   // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
        UID_STATE_FOREGROUND,           // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
        UID_STATE_FOREGROUND,           // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
        UID_STATE_BACKGROUND,           // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
        UID_STATE_BACKGROUND,           // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
        UID_STATE_BACKGROUND,           // ActivityManager.PROCESS_STATE_BACKUP
        UID_STATE_BACKGROUND,           // ActivityManager.PROCESS_STATE_SERVICE
        UID_STATE_BACKGROUND,           // ActivityManager.PROCESS_STATE_RECEIVER
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_TOP_SLEEPING
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_HOME
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_CACHED_RECENT
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_CACHED_EMPTY
        UID_STATE_CACHED,               // ActivityManager.PROCESS_STATE_NONEXISTENT
    };

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    public void updateUidProcState(int uid, int procState) {
        synchronized (this) {
            final UidState uidState = getUidStateLocked(uid, true);
            int newState = PROCESS_STATE_TO_UID_STATE[procState];
            if (uidState != null && uidState.pendingState != newState) {
                final int oldPendingState = uidState.pendingState;
                uidState.pendingState = newState;
                if (newState < uidState.state
                        || (newState <= UID_STATE_MAX_LAST_NON_RESTRICTED
                                && uidState.state > UID_STATE_MAX_LAST_NON_RESTRICTED)) {
                    // We are moving to a more important state, or the new state may be in the
                    // foreground and the old state is in the background, then always do it
                    // immediately.
                    commitUidPendingStateLocked(uidState);
                } else if (uidState.pendingStateCommitTime == 0) {
                    // We are moving to a less important state for the first time,
                    // delay the application for a bit.
                    final long settleTime;
                    if (uidState.state <= UID_STATE_TOP) {
                        settleTime = mConstants.TOP_STATE_SETTLE_TIME;
                    } else if (uidState.state <= UID_STATE_FOREGROUND_SERVICE) {
                        settleTime = mConstants.FG_SERVICE_STATE_SETTLE_TIME;
                    } else {
                        settleTime = mConstants.BG_STATE_SETTLE_TIME;
                    }
                    uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime;
                }
                if (uidState.startNesting != 0) {
                    // There is some actively running operation...  need to find it
                    // and appropriately update its state.
                    final long now = System.currentTimeMillis();
                    for (int i = uidState.pkgOps.size() - 1; i >= 0; i--) {
                        final Ops ops = uidState.pkgOps.valueAt(i);
                        for (int j = ops.size() - 1; j >= 0; j--) {
                            final Op op = ops.valueAt(j);
                            if (op.startNesting > 0) {
                                final long duration = SystemClock.elapsedRealtime()
                                        - op.startRealtime;
                                // We don't support proxy long running ops (start/stop)
                                mHistoricalRegistry.increaseOpAccessDuration(op.op,
                                        op.uidState.uid, op.packageName, oldPendingState,
                                        AppOpsManager.OP_FLAG_SELF, duration);
                                // Finish the op in the old state
                                op.finished(now, duration, oldPendingState,
                                        AppOpsManager.OP_FLAG_SELF);
                                // Start the op in the new state
                                op.startRealtime = now;
                                op.started(now, newState, AppOpsManager.OP_FLAG_SELF);
                            }
                        }
                    }
                }
            }
        }
    }

Op,Ops,UidState的关系

  Op涉及到一个敏感操作的记录信息,startOp/noteOp会把一些关系信息记录在Op内,而unsafeCheckOp不会涉及到这些记录信息。Ops则是建立了op code和Op的映射,给出一个op code,就可以查询到对应的Op,得到各种详细信息。而每一个uid都对应着一个UidState,因为多个包名可以对应一个uid,所以UidState需要一个pkgOps来保存着包名和Ops的映射。此外,UidState还提供了opModes来直接拿到op和授权结果的映射,不需要经过UidState->Ops->Op->Op的mode的值层层推进拿到授权结果,但是opModes需要经AppOpsManager#setUidMode设置后才会有记录,否则没有记录,对比之下UidState->Ops->Op->Op的mode总会有一个默认值,而且用户可以通过AppOpsManager#setMode来修改。另外opModes记录的授权结果优先于经过UidState->Ops->Op->Op的mode的值层层推进拿到的授权结果。

noteOp核心实现noteOperationUnchecked

  加入到sdk的noteOp参数是noteOp(String, int, String)。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * Make note of an application performing an operation.  Note that you must pass
     * in both the uid and name of the application to be checked; this function will verify
     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
     * succeeds, the last execution time of the operation for this app will be updated to
     * the current time.
     * @param op The operation to note.  One of the OPSTR_* constants.
     * @param uid The user id of the application attempting to perform the operation.
     * @param packageName The name of the application attempting to perform the operation.
     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
     * causing the app to crash).
     * @throws SecurityException If the app has been configured to crash on this op.
     */
    public int noteOp(@NonNull String op, int uid, @NonNull String packageName) {
        return noteOp(strOpToOp(op), uid, packageName);
    }

     */
    @UnsupportedAppUsage
    public int noteOp(int op, int uid, String packageName) {
        final int mode = noteOpNoThrow(op, uid, packageName);
        if (mode == MODE_ERRORED) {
            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
        }
        return mode;
    }

     * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
     * returns {@link #MODE_ERRORED}.
     * @hide
     */
    @UnsupportedAppUsage
    public int noteOpNoThrow(int op, int uid, String packageName) {
        try {
            return mService.noteOperation(op, uid, packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

  AppOpsManager#noteOp核心实现是AppOpsService#noteOperationUnchecked。步骤如下:
  1.通过getOpsRawLocked获得对应的Ops,没有则创建;
  2.通过getOpLocked获得Ops里面对应的Op,没有则创建;
  3.Op如果是受限制的,直接静默拒绝(MODE_IGNORED);
  4.通过opToSwitch获得op code对应的开关op code;
  5.以开关op code为准,根据UidState的opModes来判断授权结果,如果授权结果不是允许授权,直接返回该授权结果;
  6.若步骤5中opModes没有记录,则以开关op code为准,则经过UidState->Ops->Op->Op的mode的值层层推进拿到授权结果,如果授权结果不是允许授权,直接返回该授权结果;
  7.如果运行到这一步,说明返回结果是成功授权了,记录下相关信息到Op里面,并返回结果。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    private int noteOperationUnchecked(int code, int uid, String packageName,
            int proxyUid, String proxyPackageName, @OpFlags int flags) {
        synchronized (this) {
            final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
                    false /* uidMismatchExpected */);
            if (ops == null) {
                scheduleOpNotedIfNeededLocked(code, uid, packageName,
                        AppOpsManager.MODE_IGNORED);
                if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                        + " package " + packageName);
                return AppOpsManager.MODE_ERRORED;
            }
            final Op op = getOpLocked(ops, code, true);
            if (isOpRestrictedLocked(uid, code, packageName)) {
                scheduleOpNotedIfNeededLocked(code, uid, packageName,
                        AppOpsManager.MODE_IGNORED);
                return AppOpsManager.MODE_IGNORED;
            }
            final UidState uidState = ops.uidState;
            if (op.running) {
                final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes,
                    op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames);
                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
                        + " code " + code + " time=" + entry.getLastAccessTime(uidState.state,
                        uidState.state, flags) + " duration=" + entry.getLastDuration(
                                uidState.state, uidState.state, flags));
            }

            final int switchCode = AppOpsManager.opToSwitch(code);
            // If there is a non-default per UID policy (we set UID op mode only if
            // non-default) it takes over, otherwise use the per package policy.
            if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
                final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
                if (uidMode != AppOpsManager.MODE_ALLOWED) {
                    if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                            + switchCode + " (" + code + ") uid " + uid + " package "
                            + packageName);
                    op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
                            uidState.state, flags);
                    mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
                            uidState.state, flags);
                    scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
                    return uidMode;
                }
            } else {
                final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
                final int mode = switchOp.evalMode();
                if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
                    if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
                            + switchCode + " (" + code + ") uid " + uid + " package "
                            + packageName);
                    op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
                            uidState.state, flags);
                    mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
                            uidState.state, flags);
                    scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
                    return mode;
                }
            }
            if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                    + " package " + packageName);
            op.accessed(System.currentTimeMillis(), proxyUid, proxyPackageName,
                    uidState.state, flags);
            mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
                    uidState.state, flags);
            scheduleOpNotedIfNeededLocked(code, uid, packageName,
                    AppOpsManager.MODE_ALLOWED);
            return AppOpsManager.MODE_ALLOWED;
        }
    }

前台授权MODE_FOREGROUND

  在上面的步骤5和6中,当一个op在opModes中或者Op的mode的授权结果是MODE_FOREGROUND,会通过UidState#evalMode决定给调用者返回的是MODE_ALLOWED还是MODE_IGNORED,其依据是当前的uid状态state,如果当前的uid状态小于等于一个阈值,可以当前uid状态还处于前台状态,于是返回MODE_ALLOWED允许授权,否则返回MODE_IGNORED拒绝授权。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

        int evalMode(int op, int mode) {
            if (mode == AppOpsManager.MODE_FOREGROUND) {
                return state <= AppOpsManager.resolveFirstUnrestrictedUidState(op)
                        ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
            }
            return mode;
        }

  对于阈值的决定,OP_FINE_LOCATION/OP_COARSE_LOCATION/OP_MONITOR_LOCATION/OP_MONITOR_HIGH_POWER_LOCATION这些位置相关的op重要性比较高,阈值要设置低一点,为300;其他的情况阈值为400。也就说说,要访问位置的操作获得允许,需要发起访问者拥有相对更高的进程优先级。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * Resolves the first unrestricted state given an app op. Location is
     * special as we want to allow its access only if a dedicated location
     * foreground service is running. For other ops we consider any foreground
     * service as a foreground state.
     *
     * @param op The op to resolve.
     * @return The last restricted UID state.
     *
     * @hide
     */
    public static int resolveFirstUnrestrictedUidState(int op) {
        switch (op) {
            case OP_FINE_LOCATION:
            case OP_COARSE_LOCATION:
            case OP_MONITOR_LOCATION:
            case OP_MONITOR_HIGH_POWER_LOCATION: {
                return UID_STATE_FOREGROUND_SERVICE_LOCATION;
            }
        }
        return UID_STATE_FOREGROUND_SERVICE;
    }

startOp核心实现startOperation

  startOperation的获取授权结果的过程和noteOperationUnchecked基本一样,但是其他方面有一些细节是不同的:

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    @Override
    public int startOperation(IBinder token, int code, int uid, String packageName,
            boolean startIfModeDefault) {
        verifyIncomingUid(uid);
        verifyIncomingOp(code);
        String resolvedPackageName = resolvePackageName(uid, packageName);
        if (resolvedPackageName == null) {
            return  AppOpsManager.MODE_IGNORED;
        }
        ClientState client = (ClientState)token;
        synchronized (this) {
            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
                    false /* uidMismatchExpected */);
            if (ops == null) {
                if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                        + " package " + resolvedPackageName);
                return AppOpsManager.MODE_ERRORED;
            }
            final Op op = getOpLocked(ops, code, true);
            if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
                return AppOpsManager.MODE_IGNORED;
            }
            final int switchCode = AppOpsManager.opToSwitch(code);
            final UidState uidState = ops.uidState;
            // If there is a non-default per UID policy (we set UID op mode only if
            // non-default) it takes over, otherwise use the per package policy.
            final int opCode = op.op;
            if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
                final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
                if (uidMode != AppOpsManager.MODE_ALLOWED
                        && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
                    if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
                            + switchCode + " (" + code + ") uid " + uid + " package "
                            + resolvedPackageName);
                    // We don't support proxy long running ops (start/stop)
                    op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
                            null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF);
                    mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
                            uidState.state, AppOpsManager.OP_FLAG_SELF);
                    return uidMode;
                }
            } else {
                final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
                final int mode = switchOp.evalMode();
                if (mode != AppOpsManager.MODE_ALLOWED
                        && (!startIfModeDefault || mode != AppOpsManager.MODE_DEFAULT)) {
                    if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
                            + switchCode + " (" + code + ") uid " + uid + " package "
                            + resolvedPackageName);
                    // We don't support proxy long running ops (start/stop)
                    op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
                            null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF);
                    mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
                            uidState.state, AppOpsManager.OP_FLAG_SELF);
                    return mode;
                }
            }
            if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
                    + " package " + resolvedPackageName);
            if (op.startNesting == 0) {
                op.startRealtime = SystemClock.elapsedRealtime();
                // We don't support proxy long running ops (start/stop)
                op.started(System.currentTimeMillis(), uidState.state,
                        AppOpsManager.OP_FLAG_SELF);
                mHistoricalRegistry.incrementOpAccessedCount(opCode, uid, packageName,
                        uidState.state, AppOpsManager.OP_FLAG_SELF);

                scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
            }
            op.startNesting++;
            uidState.startNesting++;
            if (client.mStartedOps != null) {
                client.mStartedOps.add(op);
            }
        }

        return AppOpsManager.MODE_ALLOWED;
    }

unsafeCheckOp核心实现checkOperationUnchecked

  除了多了一个条件判断isOpRestrictedDueToSuspend,其他基本与noteOperationUnchecked相同,但是没有记录Op信息,一步到位,目的只是为了不作记录拿到授权结果。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,
                boolean raw, boolean verify) {
        if (isOpRestrictedDueToSuspend(code, packageName, uid)) {
            return AppOpsManager.MODE_IGNORED;
        }
        synchronized (this) {
            if (verify) {
                checkPackage(uid, packageName);
            }
            if (isOpRestrictedLocked(uid, code, packageName)) {
                return AppOpsManager.MODE_IGNORED;
            }
            code = AppOpsManager.opToSwitch(code);
            UidState uidState = getUidStateLocked(uid, false);
            if (uidState != null && uidState.opModes != null
                    && uidState.opModes.indexOfKey(code) >= 0) {
                final int rawMode = uidState.opModes.get(code);
                return raw ? rawMode : uidState.evalMode(code, rawMode);
            }
            Op op = getOpLocked(code, uid, packageName, false, verify, false);
            if (op == null) {
                return AppOpsManager.opToDefaultMode(code);
            }
            return raw ? op.mode : op.evalMode();
        }
    }

  对于已经被suspend的包名发起的OP_PLAY_AUDIO,OP_RECORD_AUDIO,OP_CAMERA操作,是会被静默拒绝的(MODE_IGNORED)。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    private boolean isOpRestrictedDueToSuspend(int code, String packageName, int uid) {
        if (!ArrayUtils.contains(OPS_RESTRICTED_ON_SUSPEND, code)) {
            return false;
        }
        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
        return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
    }

Op监控startWatchingMode

  AppOpsManager向开发者提供了一个startWatchingMode的接口供监控Op变化使用(需要WATCH_APPOPS权限),核心实现在AppOpsService#startWatchingModeWithFlags。
  接口说明如下。开发者需要提供op字符串名称,例如"android:write_sms",监控者的包名和OnOpChangedListener接口实现。

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * Monitor for changes to the operating mode for the given op in the given app package.
     * You can watch op changes only for your UID.
     *
     * @param op The operation to monitor, one of OPSTR_*.
     * @param packageName The name of the application to monitor.
     * @param callback Where to report changes.
     */
    public void startWatchingMode(@NonNull String op, @Nullable String packageName,
            @NonNull final OnOpChangedListener callback) {
        startWatchingMode(strOpToOp(op), packageName, callback);
    }

frameworks/base/core/java/android/app/AppOpsManager.java

    /**
     * Monitor for changes to the operating mode for the given op in the given app package.
     *
     * 

If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission * you can watch changes only for your UID. * * @param op The operation to monitor, one of OP_*. * @param packageName The name of the application to monitor. * @param callback Where to report changes. * @hide */ @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) { startWatchingMode(op, packageName, 0, callback); }

  可以看到,AppOpsManager在后面实现了一个IAppOpsCallback.Stub以实现跨进程通信,AppOpsService在检测到op变化后,通过IAppOpsCallback.Stub#opChanged->OnOpChangedListener#onOpChanged实现回调。使用IAppOpsCallback.Stub的好处是让AppOpsService可以检测到发起监控端的Binde死亡事件以采取相应的措施。

frameworks/base/core/java/android/app/AppOpsManager.java

    @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true)
    public void startWatchingMode(int op, String packageName, int flags,
            final OnOpChangedListener callback) {
        synchronized (mModeWatchers) {
            IAppOpsCallback cb = mModeWatchers.get(callback);
            if (cb == null) {
                cb = new IAppOpsCallback.Stub() {
                    public void opChanged(int op, int uid, String packageName) {
                        if (callback instanceof OnOpChangedInternalListener) {
                            ((OnOpChangedInternalListener)callback).onOpChanged(op, packageName);
                        }
                        if (sOpToString[op] != null) {
                            callback.onOpChanged(sOpToString[op], packageName);
                        }
                    }
                };
                mModeWatchers.put(callback, cb);
            }
            try {
                mService.startWatchingModeWithFlags(op, packageName, flags, cb);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }

frameworks/base/core/java/android/app/AppOpsManager.java

    public interface OnOpChangedListener {
        public void onOpChanged(String op, String packageName);
    }

  AppOpsService使用了ModeCallback对回调进行进一步的封装,额外记录了调用者uid,pid等信息。mModeWatchers保存了回调Binder对象对ModeCallback的映射。mOpModeWatchers保存了op code对ModeCallback集合的映射,因为一个op code可能对应多个ModeCallback。mPackageModeWatchers保存了包名对ModeCallback集合的映射,也是因为一个包名可能对应多个ModeCallback。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    @Override
    public void startWatchingModeWithFlags(int op, String packageName, int flags,
            IAppOpsCallback callback) {
        int watchedUid = -1;
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        // TODO: should have a privileged permission to protect this.
        // Also, if the caller has requested WATCH_FOREGROUND_CHANGES, should we require
        // the USAGE_STATS permission since this can provide information about when an
        // app is in the foreground?
        Preconditions.checkArgumentInRange(op, AppOpsManager.OP_NONE,
                AppOpsManager._NUM_OP - 1, "Invalid op code: " + op);
        if (callback == null) {
            return;
        }
        synchronized (this) {
            op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
            ModeCallback cb = mModeWatchers.get(callback.asBinder());
            if (cb == null) {
                cb = new ModeCallback(callback, watchedUid, flags, callingUid, callingPid);
                mModeWatchers.put(callback.asBinder(), cb);
            }
            if (op != AppOpsManager.OP_NONE) {
                ArraySet cbs = mOpModeWatchers.get(op);
                if (cbs == null) {
                    cbs = new ArraySet<>();
                    mOpModeWatchers.put(op, cbs);
                }
                cbs.add(cb);
            }
            if (packageName != null) {
                ArraySet cbs = mPackageModeWatchers.get(packageName);
                if (cbs == null) {
                    cbs = new ArraySet<>();
                    mPackageModeWatchers.put(packageName, cbs);
                }
                cbs.add(cb);
            }
            evalAllForegroundOpsLocked();
        }
    }

回调notifyOpChanged

  在某些特定的时刻,系统会触发AppOpsService#notifyOpChanged来触发回调,过程是IAppOpsCallback.stub#opChanged->OnOpChangedListener#onOpChanged。
  notifyOpChanged有两个形式。

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    private void notifyOpChanged(ModeCallback callback, int code,
            int uid, String packageName) {
        if (uid != UID_ANY && callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
            return;
        }
        // There are components watching for mode changes such as window manager
        // and location manager which are in our process. The callbacks in these
        // components may require permissions our remote caller does not have.
        final long identity = Binder.clearCallingIdentity();
        try {
            callback.mCallback.opChanged(code, uid, packageName);
        } catch (RemoteException e) {
            /* ignore */
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java

    private void notifyOpChanged(ArraySet callbacks, int code,
            int uid, String packageName) {
        for (int i = 0; i < callbacks.size(); i++) {
            final ModeCallback callback = callbacks.valueAt(i);
            notifyOpChanged(callback, code, uid, packageName);
        }
    }

回调触发时机

  回调notifyOpChanged被触发的时机有:
  1.系统开机就绪时,响应PackageManager#setPackagesSuspended系统调用发送,将OP_PLAY_AUDIO,OP_RECORD_AUDIO和OP_CAMERA三个op可以映射的ModeCallback进行回调;
  2.setUidMode过程中回调;
  3.setMode过程中回调;
  4.重置所有UidState时回调;
  5.当使用了带WATCH_FOREGROUND_CHANGES的flag的startWatchingMode来监控处于前台允许状态的op且uid的状态正在发生切换时(UidState的state设置成pendingState)时回调;
6.设置用户限制时回调(DevicePolicyManager#addUserRestriction);

你可能感兴趣的:(Framework)