Merge "Work on issue #78480444: Start tracking uid state in app ops" into pi-dev
am: 55c2d4106a
Change-Id: I4d44e5aa76fc14ddb5049d617650a19d1bf9255e
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dcf4eec..796e406 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -110,6 +110,27 @@
public static final int MODE_DEFAULT = 3;
/**
+ * Special mode that means "allow only when app is in foreground." This is <b>not</b>
+ * returned from {@link #checkOp}, {@link #noteOp}, {@link #startOp}; rather, when this
+ * mode is set, these functions will return {@link #MODE_ALLOWED} when the app being
+ * checked is currently in the foreground, otherwise {@link #MODE_IGNORED}.
+ * @hide
+ */
+ public static final int MODE_FOREGROUND = 4;
+
+
+ /**
+ * @hide
+ */
+ public static final String[] MODE_NAMES = new String[] {
+ "allow", // MODE_ALLOWED
+ "ignore", // MODE_IGNORED
+ "deny", // MODE_ERRORED
+ "default", // MODE_DEFAULT
+ "foreground", // MODE_FOREGROUND
+ };
+
+ /**
* Metrics about an op when its uid is persistent.
* @hide
*/
@@ -619,83 +640,83 @@
* make them all controlled by the same single operation.
*/
private static int[] sOpToSwitch = new int[] {
- OP_COARSE_LOCATION,
- OP_COARSE_LOCATION,
- OP_COARSE_LOCATION,
- OP_VIBRATE,
- OP_READ_CONTACTS,
- OP_WRITE_CONTACTS,
- OP_READ_CALL_LOG,
- OP_WRITE_CALL_LOG,
- OP_READ_CALENDAR,
- OP_WRITE_CALENDAR,
- OP_COARSE_LOCATION,
- OP_POST_NOTIFICATION,
- OP_COARSE_LOCATION,
- OP_CALL_PHONE,
- OP_READ_SMS,
- OP_WRITE_SMS,
- OP_RECEIVE_SMS,
- OP_RECEIVE_SMS,
- OP_RECEIVE_MMS,
- OP_RECEIVE_WAP_PUSH,
- OP_SEND_SMS,
- OP_READ_SMS,
- OP_WRITE_SMS,
- OP_WRITE_SETTINGS,
- OP_SYSTEM_ALERT_WINDOW,
- OP_ACCESS_NOTIFICATIONS,
- OP_CAMERA,
- OP_RECORD_AUDIO,
- OP_PLAY_AUDIO,
- OP_READ_CLIPBOARD,
- OP_WRITE_CLIPBOARD,
- OP_TAKE_MEDIA_BUTTONS,
- OP_TAKE_AUDIO_FOCUS,
- OP_AUDIO_MASTER_VOLUME,
- OP_AUDIO_VOICE_VOLUME,
- OP_AUDIO_RING_VOLUME,
- OP_AUDIO_MEDIA_VOLUME,
- OP_AUDIO_ALARM_VOLUME,
- OP_AUDIO_NOTIFICATION_VOLUME,
- OP_AUDIO_BLUETOOTH_VOLUME,
- OP_WAKE_LOCK,
- OP_COARSE_LOCATION,
- OP_COARSE_LOCATION,
- OP_GET_USAGE_STATS,
- OP_MUTE_MICROPHONE,
- OP_TOAST_WINDOW,
- OP_PROJECT_MEDIA,
- OP_ACTIVATE_VPN,
- OP_WRITE_WALLPAPER,
- OP_ASSIST_STRUCTURE,
- OP_ASSIST_SCREENSHOT,
- OP_READ_PHONE_STATE,
- OP_ADD_VOICEMAIL,
- OP_USE_SIP,
- OP_PROCESS_OUTGOING_CALLS,
- OP_USE_FINGERPRINT,
- OP_BODY_SENSORS,
- OP_READ_CELL_BROADCASTS,
- OP_MOCK_LOCATION,
- OP_READ_EXTERNAL_STORAGE,
- OP_WRITE_EXTERNAL_STORAGE,
- OP_TURN_SCREEN_ON,
- OP_GET_ACCOUNTS,
- OP_RUN_IN_BACKGROUND,
- OP_AUDIO_ACCESSIBILITY_VOLUME,
- OP_READ_PHONE_NUMBERS,
- OP_REQUEST_INSTALL_PACKAGES,
- OP_PICTURE_IN_PICTURE,
- OP_INSTANT_APP_START_FOREGROUND,
- OP_ANSWER_PHONE_CALLS,
- OP_RUN_ANY_IN_BACKGROUND,
- OP_CHANGE_WIFI_STATE,
- OP_REQUEST_DELETE_PACKAGES,
- OP_BIND_ACCESSIBILITY_SERVICE,
- OP_ACCEPT_HANDOVER,
- OP_MANAGE_IPSEC_TUNNELS,
- OP_START_FOREGROUND,
+ 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
};
/**
@@ -1420,19 +1441,11 @@
* Retrieve the human readable mode.
* @hide
*/
- public static String modeToString(int mode) {
- switch (mode) {
- case MODE_ALLOWED:
- return "allow";
- case MODE_IGNORED:
- return "ignore";
- case MODE_ERRORED:
- return "deny";
- case MODE_DEFAULT:
- return "default";
- default:
- return "mode=" + mode;
+ public static String modeToName(int mode) {
+ if (mode >= 0 && mode < MODE_NAMES.length) {
+ return MODE_NAMES[mode];
}
+ return "mode=" + mode;
}
/**
@@ -1554,30 +1567,42 @@
}
public long getTime() {
- long time = 0;
- for (int i = 0; i < _NUM_UID_STATE; i++) {
- if (mTimes[i] > time) {
- time = mTimes[i];
- }
- }
- return time;
+ return maxTime(mTimes, 0, _NUM_UID_STATE);
}
- public long getTimeFor(int uidState) {
+ public long getLastAccessTime() {
+ return maxTime(mTimes, 0, _NUM_UID_STATE);
+ }
+
+ public long getLastAccessForegroundTime() {
+ return maxTime(mTimes, UID_STATE_PERSISTENT, UID_STATE_FOREGROUND_SERVICE + 1);
+ }
+
+ public long getLastAccessBackgroundTime() {
+ return maxTime(mTimes, UID_STATE_FOREGROUND_SERVICE + 1, _NUM_UID_STATE);
+ }
+
+ public long getLastTimeFor(int uidState) {
return mTimes[uidState];
}
public long getRejectTime() {
- long time = 0;
- for (int i = 0; i < _NUM_UID_STATE; i++) {
- if (mRejectTimes[i] > time) {
- time = mRejectTimes[i];
- }
- }
- return time;
+ return maxTime(mRejectTimes, 0, _NUM_UID_STATE);
}
- public long getRejectTimeFor(int uidState) {
+ public long getLastRejectTime() {
+ return maxTime(mRejectTimes, 0, _NUM_UID_STATE);
+ }
+
+ public long getLastRejectForegroundTime() {
+ return maxTime(mRejectTimes, UID_STATE_PERSISTENT, UID_STATE_FOREGROUND_SERVICE + 1);
+ }
+
+ public long getLastRejectBackgroundTime() {
+ return maxTime(mRejectTimes, UID_STATE_FOREGROUND_SERVICE + 1, _NUM_UID_STATE);
+ }
+
+ public long getLastRejectTimeFor(int uidState) {
return mRejectTimes[uidState];
}
@@ -1680,6 +1705,7 @@
* @param ops The set of operations you are interested in, or null if you want all of them.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
try {
return mService.getPackagesForOps(ops);
@@ -1696,6 +1722,7 @@
* @param ops The set of operations you are interested in, or null if you want all of them.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
try {
return mService.getOpsForPackage(uid, packageName, ops);
@@ -2009,6 +2036,17 @@
* used for a quick check to see if an operation has been disabled for the application,
* as an early reject of some work. This does not modify the time stamp or other data
* about the operation.
+ *
+ * <p>Important things this will not do (which you need to ultimate use
+ * {@link #noteOp(String, int, String)} or {@link #startOp(String, int, String)} to cover):</p>
+ * <ul>
+ * <li>Verifying the uid and package are consistent, so callers can't spoof
+ * their identity.</li>
+ * <li>Taking into account the current foreground/background state of the
+ * app; apps whose mode varies by this state will always be reported
+ * as {@link #MODE_ALLOWED}.</li>
+ * </ul>
+ *
* @param op The operation to check. 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.
@@ -2128,6 +2166,17 @@
* used for a quick check to see if an operation has been disabled for the application,
* as an early reject of some work. This does not modify the time stamp or other data
* about the operation.
+ *
+ * <p>Important things this will not do (which you need to ultimate use
+ * {@link #noteOp(int, int, String)} or {@link #startOp(int, int, String)} to cover):</p>
+ * <ul>
+ * <li>Verifying the uid and package are consistent, so callers can't spoof
+ * their identity.</li>
+ * <li>Taking into account the current foreground/background state of the
+ * app; apps whose mode varies by this state will always be reported
+ * as {@link #MODE_ALLOWED}.</li>
+ * </ul>
+ *
* @param op The operation to check. One of the OP_* 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.
@@ -2445,4 +2494,17 @@
public static String[] getOpStrs() {
return Arrays.copyOf(sOpToString, sOpToString.length);
}
+
+ /**
+ * @hide
+ */
+ public static long maxTime(long[] times, int start, int end) {
+ long time = 0;
+ for (int i = start; i < end; i++) {
+ if (times[i] > time) {
+ time = times[i];
+ }
+ }
+ return time;
+ }
}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index d818bd6..13503e6 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -48,6 +48,7 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.Xml;
@@ -108,6 +109,9 @@
// Write at most every 30 minutes.
static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
+ // How long we want for a drop in uid state to settle before applying it.
+ static final long STATE_SETTLE_TIME = 10*1000;
+
// Constant meaning that any UID should be matched when dispatching callbacks
private static final int UID_ANY = -2;
@@ -162,13 +166,6 @@
"rc", // UID_STATE_CACHED
};
- static final String[] MODE_NAMES = new String[] {
- "allow", // MODE_ALLOWED
- "ignore", // MODE_IGNORED
- "deny", // MODE_ERRORED
- "default", // MODE_DEFAULT
- };
-
Context mContext;
final AtomicFile mFile;
final Handler mHandler;
@@ -194,6 +191,8 @@
@VisibleForTesting
final SparseArray<UidState> mUidStates = new SparseArray<>();
+ long mLastUptime;
+
/*
* These are app op restrictions imposed per user from various parties.
*/
@@ -202,11 +201,17 @@
@VisibleForTesting
static final class UidState {
public final int uid;
+
public int state = UID_STATE_CACHED;
+ public int pendingState = UID_STATE_CACHED;
+ public long pendingStateCommitTime;
+
public int startNesting;
public ArrayMap<String, Ops> pkgOps;
public SparseIntArray opModes;
+ public SparseBooleanArray foregroundOps;
+
public UidState(int uid) {
this.uid = uid;
}
@@ -220,6 +225,32 @@
return (pkgOps == null || pkgOps.isEmpty())
&& (opModes == null || opModes.size() <= 0);
}
+
+ int evalMode(int mode) {
+ if (mode == AppOpsManager.MODE_FOREGROUND) {
+ return state <= UID_STATE_FOREGROUND_SERVICE
+ ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
+ }
+ return mode;
+ }
+
+ public void evalForegroundOps() {
+ SparseBooleanArray which = null;
+ if (pkgOps != null) {
+ for (int i = pkgOps.size() - 1; i >= 0; i--) {
+ Ops ops = pkgOps.valueAt(i);
+ for (int j = ops.size() - 1; j >= 0; j--) {
+ if (ops.valueAt(j).mode == AppOpsManager.MODE_FOREGROUND) {
+ if (which == null) {
+ which = new SparseBooleanArray();
+ }
+ which.put(ops.keyAt(j), true);
+ }
+ }
+ }
+ }
+ foregroundOps = which;
+ }
}
final static class Ops extends SparseArray<Op> {
@@ -267,6 +298,10 @@
}
return false;
}
+
+ int getMode() {
+ return uidState.evalMode(mode);
+ }
}
final SparseArray<ArraySet<ModeCallback>> mOpModeWatchers = new SparseArray<>();
@@ -575,7 +610,16 @@
synchronized (this) {
final UidState uidState = getUidStateLocked(uid, true);
final int newState = PROCESS_STATE_TO_UID_STATE[procState];
- if (uidState != null && uidState.state != newState) {
+ if (uidState != null && uidState.pendingState != newState) {
+ if (newState < uidState.state) {
+ // We are moving to a more important state, always do it immediately.
+ uidState.state = newState;
+ uidState.pendingStateCommitTime = 0;
+ } else if (uidState.pendingStateCommitTime == 0) {
+ // We are moving to a less important state for the first time,
+ // delay the application for a bit.
+ uidState.pendingStateCommitTime = SystemClock.uptimeMillis() + STATE_SETTLE_TIME;
+ }
if (uidState.startNesting != 0) {
// There is some actively running operation... need to find it
// and appropriately update its state.
@@ -585,13 +629,13 @@
for (int j = ops.size() - 1; j >= 0; j--) {
final Op op = ops.valueAt(j);
if (op.startNesting > 0) {
- op.time[uidState.state] = now;
+ op.time[uidState.pendingState] = now;
op.time[newState] = now;
}
}
}
}
- uidState.state = newState;
+ uidState.pendingState = newState;
}
}
}
@@ -887,6 +931,9 @@
if (op != null) {
if (op.mode != mode) {
op.mode = mode;
+ if (uidState != null) {
+ uidState.evalForegroundOps();
+ }
ArraySet<ModeCallback> cbs = mOpModeWatchers.get(code);
if (cbs != null) {
if (repCbs == null) {
@@ -1046,6 +1093,7 @@
Map<String, Ops> packages = uidState.pkgOps;
Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
+ boolean uidChanged = false;
while (it.hasNext()) {
Map.Entry<String, Ops> ent = it.next();
String packageName = ent.getKey();
@@ -1060,6 +1108,7 @@
&& curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
changed = true;
+ uidChanged = true;
callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
mOpModeWatchers.get(curOp.op));
callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
@@ -1076,6 +1125,9 @@
if (uidState.isDefault()) {
mUidStates.remove(uidState.uid);
}
+ if (uidChanged) {
+ uidState.evalForegroundOps();
+ }
}
if (changed) {
@@ -1197,7 +1249,7 @@
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
- return op.mode;
+ return op.mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : op.mode;
}
}
@@ -1352,9 +1404,9 @@
// 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.opModes.get(switchCode);
+ final int uidMode = uidState.evalMode(uidState.opModes.get(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
@@ -1362,12 +1414,13 @@
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ final int mode = switchOp.getMode();
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
- return switchOp.mode;
+ return mode;
}
}
if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
@@ -1458,10 +1511,10 @@
// 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.opModes.get(switchCode);
+ final int uidMode = uidState.evalMode(uidState.opModes.get(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
- if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
op.rejectTime[uidState.state] = System.currentTimeMillis();
@@ -1469,13 +1522,14 @@
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED
- && (!startIfModeDefault || switchOp.mode != AppOpsManager.MODE_DEFAULT)) {
- if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code "
+ final int mode = switchOp.getMode();
+ 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);
op.rejectTime[uidState.state] = System.currentTimeMillis();
- return switchOp.mode;
+ return mode;
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
@@ -1642,6 +1696,19 @@
}
uidState = new UidState(uid);
mUidStates.put(uid, uidState);
+ } else {
+ if (uidState.pendingStateCommitTime != 0) {
+ if (uidState.pendingStateCommitTime < mLastUptime) {
+ uidState.state = uidState.pendingState;
+ uidState.pendingStateCommitTime = 0;
+ } else {
+ mLastUptime = SystemClock.uptimeMillis();
+ if (uidState.pendingStateCommitTime < mLastUptime) {
+ uidState.state = uidState.pendingState;
+ uidState.pendingStateCommitTime = 0;
+ }
+ }
+ }
}
return uidState;
}
@@ -1870,6 +1937,7 @@
if (uidState.pkgOps == null) {
continue;
}
+ boolean changed = false;
for (int j = 0; j < uidState.pkgOps.size(); j++) {
Ops ops = uidState.pkgOps.valueAt(j);
if (ops != null) {
@@ -1879,9 +1947,13 @@
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
copy.mode = op.mode;
ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
+ changed = true;
}
}
}
+ if (changed) {
+ uidState.evalForegroundOps();
+ }
}
}
@@ -2077,6 +2149,10 @@
XmlUtils.skipCurrentTag(parser);
}
}
+ UidState uidState = getUidStateLocked(uid, false);
+ if (uidState != null) {
+ uidState.evalForegroundOps();
+ }
}
void writeState() {
@@ -2152,12 +2228,12 @@
out.attribute(null, "m", Integer.toString(op.getMode()));
}
for (int k = 0; k < _NUM_UID_STATE; k++) {
- final long time = op.getTimeFor(k);
+ final long time = op.getLastTimeFor(k);
if (time != 0) {
out.attribute(null, UID_STATE_TIME_ATTRS[k],
Long.toString(time));
}
- final long rejectTime = op.getRejectTimeFor(k);
+ final long rejectTime = op.getLastRejectTimeFor(k);
if (rejectTime != 0) {
out.attribute(null, UID_STATE_REJECT_ATTRS[k],
Long.toString(rejectTime));
@@ -2229,7 +2305,7 @@
dumpCommandHelp(pw);
}
- private int strOpToOp(String op, PrintWriter err) {
+ static private int strOpToOp(String op, PrintWriter err) {
try {
return AppOpsManager.strOpToOp(op);
} catch (IllegalArgumentException e) {
@@ -2247,8 +2323,8 @@
}
int strModeToMode(String modeStr, PrintWriter err) {
- for (int i = MODE_NAMES.length - 1; i >= 0; i--) {
- if (MODE_NAMES[i].equals(modeStr)) {
+ for (int i = AppOpsManager.MODE_NAMES.length - 1; i >= 0; i--) {
+ if (AppOpsManager.MODE_NAMES[i].equals(modeStr)) {
return i;
}
}
@@ -2470,7 +2546,7 @@
if (ops == null || ops.size() <= 0) {
pw.println("No operations.");
if (shell.op > AppOpsManager.OP_NONE && shell.op < AppOpsManager._NUM_OP) {
- pw.println("Default mode: " + AppOpsManager.modeToString(
+ pw.println("Default mode: " + AppOpsManager.modeToName(
AppOpsManager.opToDefaultMode(shell.op)));
}
return 0;
@@ -2482,7 +2558,7 @@
AppOpsManager.OpEntry ent = entries.get(j);
pw.print(AppOpsManager.opToName(ent.getOp()));
pw.print(": ");
- pw.print(AppOpsManager.modeToString(ent.getMode()));
+ pw.print(AppOpsManager.modeToName(ent.getMode()));
if (ent.getTime() != 0) {
pw.print("; time=");
TimeUtils.formatDuration(now - ent.getTime(), pw);
@@ -2636,7 +2712,10 @@
private void dumpHelp(PrintWriter pw) {
pw.println("AppOps service (appops) dump options:");
- pw.println(" none");
+ pw.println(" -h");
+ pw.println(" Print this help text.");
+ pw.println(" --op [OP]");
+ pw.println(" Limit output to data associated with the given app op code.");
}
private void dumpTimesLocked(PrintWriter pw, String firstPrefix, String prefix, long[] times,
@@ -2671,6 +2750,8 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+ int dumpOp = -1;
+
if (args != null) {
for (int i=0; i<args.length; i++) {
String arg = args[i];
@@ -2679,6 +2760,16 @@
return;
} else if ("-a".equals(arg)) {
// dump all data
+ } else if ("--op".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("No argument for --op option");
+ return;
+ }
+ dumpOp = Shell.strOpToOp(args[i], pw);
+ if (dumpOp < 0) {
+ return;
+ }
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
return;
@@ -2693,13 +2784,21 @@
pw.println("Current AppOps Service state:");
final long now = System.currentTimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowUptime = SystemClock.uptimeMillis();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
final Date date = new Date();
boolean needSep = false;
if (mOpModeWatchers.size() > 0) {
needSep = true;
- pw.println(" Op mode watchers:");
+ boolean printedHeader = false;
for (int i=0; i<mOpModeWatchers.size(); i++) {
+ if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println(" Op mode watchers:");
+ printedHeader = true;
+ }
pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
pw.println(":");
ArraySet<ModeCallback> callbacks = mOpModeWatchers.valueAt(i);
@@ -2722,7 +2821,7 @@
}
}
}
- if (mModeWatchers.size() > 0) {
+ if (mModeWatchers.size() > 0 && dumpOp < 0) {
needSep = true;
pw.println(" All op mode watchers:");
for (int i=0; i<mModeWatchers.size(); i++) {
@@ -2733,12 +2832,19 @@
}
if (mActiveWatchers.size() > 0) {
needSep = true;
- pw.println(" All op active watchers:");
+ boolean printedHeader = false;
for (int i = 0; i < mActiveWatchers.size(); i++) {
final SparseArray<ActiveCallback> activeWatchers = mActiveWatchers.valueAt(i);
if (activeWatchers.size() <= 0) {
continue;
}
+ if (dumpOp >= 0 && activeWatchers.indexOfKey(dumpOp) < 0) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println(" All op active watchers:");
+ printedHeader = true;
+ }
pw.print(" ");
pw.print(Integer.toHexString(System.identityHashCode(
mActiveWatchers.keyAt(i))));
@@ -2746,6 +2852,9 @@
pw.print(" [");
final int opCount = activeWatchers.size();
for (i = 0; i < opCount; i++) {
+ if (i > 0) {
+ pw.print(' ');
+ }
pw.print(AppOpsManager.opToName(activeWatchers.keyAt(i)));
if (i < opCount - 1) {
pw.print(',');
@@ -2758,15 +2867,30 @@
}
if (mClients.size() > 0) {
needSep = true;
- pw.println(" Clients:");
+ boolean printedHeader = false;
for (int i=0; i<mClients.size(); i++) {
- pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":");
+ boolean printedClient = false;
ClientState cs = mClients.valueAt(i);
- pw.print(" "); pw.println(cs);
if (cs.mStartedOps.size() > 0) {
- pw.println(" Started ops:");
+ boolean printedStarted = false;
for (int j=0; j<cs.mStartedOps.size(); j++) {
Op op = cs.mStartedOps.get(j);
+ if (dumpOp >= 0 && op.op != dumpOp) {
+ continue;
+ }
+ if (!printedHeader) {
+ pw.println(" Clients:");
+ printedHeader = true;
+ }
+ if (!printedClient) {
+ pw.print(" "); pw.print(mClients.keyAt(i)); pw.println(":");
+ pw.print(" "); pw.println(cs);
+ printedClient = true;
+ }
+ if (!printedStarted) {
+ pw.println(" Started ops:");
+ printedStarted = true;
+ }
pw.print(" "); pw.print("uid="); pw.print(op.uid);
pw.print(" pkg="); pw.print(op.packageName);
pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
@@ -2774,7 +2898,7 @@
}
}
}
- if (mAudioRestrictions.size() > 0) {
+ if (mAudioRestrictions.size() > 0 && dumpOp < 0) {
boolean printedHeader = false;
for (int o=0; o<mAudioRestrictions.size(); o++) {
final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
@@ -2789,7 +2913,7 @@
pw.print(" "); pw.print(op);
pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage));
Restriction r = restrictions.valueAt(i);
- pw.print(": mode="); pw.println(MODE_NAMES[r.mode]);
+ pw.print(": mode="); pw.println(AppOpsManager.modeToName(r.mode));
if (!r.exceptionPackages.isEmpty()) {
pw.println(" Exceptions:");
for (int j=0; j<r.exceptionPackages.size(); j++) {
@@ -2804,38 +2928,83 @@
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
+ final SparseIntArray opModes = uidState.opModes;
+ final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
+
+ if (dumpOp >= 0) {
+ boolean hasOp = uidState.opModes != null
+ && uidState.opModes.indexOfKey(dumpOp) >= 0;
+ if (pkgOps != null) {
+ for (int pkgi = 0; !hasOp && pkgi < pkgOps.size(); pkgi++) {
+ Ops ops = pkgOps.valueAt(pkgi);
+ if (ops != null && ops.indexOfKey(dumpOp) >= 0) {
+ hasOp = true;
+ continue;
+ }
+ }
+ }
+ if (!hasOp) {
+ continue;
+ }
+ }
pw.print(" Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
pw.print(" state=");
pw.println(UID_STATE_NAMES[uidState.state]);
+ if (uidState.state != uidState.pendingState) {
+ pw.print(" pendingState=");
+ pw.println(UID_STATE_NAMES[uidState.pendingState]);
+ }
+ if (uidState.pendingStateCommitTime != 0) {
+ pw.print(" pendingStateCommitTime=");
+ TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowUptime, pw);
+ pw.println();
+ }
if (uidState.startNesting != 0) {
pw.print(" startNesting=");
pw.println(uidState.startNesting);
}
+ if (uidState.foregroundOps != null) {
+ pw.println(" foregroundOps:");
+ for (int j = 0; j < uidState.foregroundOps.size(); j++) {
+ pw.print(" ");
+ pw.println(AppOpsManager.opToName(uidState.foregroundOps.keyAt(j)));
+ }
+ }
needSep = true;
- SparseIntArray opModes = uidState.opModes;
if (opModes != null) {
final int opModeCount = opModes.size();
for (int j = 0; j < opModeCount; j++) {
final int code = opModes.keyAt(j);
final int mode = opModes.valueAt(j);
+ if (dumpOp >= 0 && dumpOp != code) {
+ continue;
+ }
pw.print(" "); pw.print(AppOpsManager.opToName(code));
- pw.print(": mode="); pw.println(MODE_NAMES[mode]);
+ pw.print(": mode="); pw.println(AppOpsManager.modeToName(mode));
}
}
- ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (pkgOps == null) {
continue;
}
- for (Ops ops : pkgOps.values()) {
- pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
+ for (int pkgi = 0; pkgi < pkgOps.size(); pkgi++) {
+ Ops ops = pkgOps.valueAt(pkgi);
+ boolean printedPackage = false;
for (int j=0; j<ops.size(); j++) {
Op op = ops.valueAt(j);
+ if (dumpOp >= 0 && dumpOp != op.op) {
+ continue;
+ }
+ if (!printedPackage) {
+ pw.print(" Package "); pw.print(ops.packageName); pw.println(":");
+ printedPackage = true;
+ }
pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
- pw.print(" ("); pw.print(MODE_NAMES[op.mode]); pw.println("): ");
+ pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode));
+ pw.println("): ");
dumpTimesLocked(pw,
" Access: ",
" ", op.time, now, sdf, date);