AppOpsManager介绍 - Android 安全专题

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.增加_NUMOP的数目;
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 vpn
        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<String, Integer> 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<String, Integer> 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<String> 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<Op> {
    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<String, Ops> 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.
 *
 * <p> 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<ModeCallback> cbs = mOpModeWatchers.get(op);
            if (cbs == null) {
                cbs = new ArraySet<>();
                mOpModeWatchers.put(op, cbs);
            }
            cbs.add(cb);
        }
        if (packageName != null) {
            ArraySet<ModeCallback> 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<ModeCallback> 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);

赞(0)
未经允许不得转载:极客笔记 » AppOpsManager介绍

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址