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。
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;左边的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和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描述了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描述了一个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是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是权限名对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); } }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描述了是否允许系统组件绕过用户限制(在用户限制被激活的情况下)。
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用来指定是否允许在重置所有应用偏好设置后,重置 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 };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,顾名思义,就是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是一个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涉及到一个敏感操作的记录信息,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的值层层推进拿到的授权结果。
加入到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; } }在上面的步骤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; }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; }除了多了一个条件判断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)); }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(); } }在某些特定的时刻,系统会触发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);