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在记录敏感操作信息的同时,还有一个返回值,开发者可以根据这个返回值决定下一步操作。
返回值有:
- MODE_ALLOWED:访问者可以访问该敏感操作;
- MODE_IGNORED:访问者不可以访问该敏感操作,但是不会引发crash;
- MODE_ERRORED:访问者不可以访问该敏感操作,会引发crash;
- 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);