From 7b7c58b3842d47c4c8df4876e2e2248c58477d97 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 2 Dec 2014 18:32:20 -0800 Subject: [PATCH] Work on issue #18572506: AppOps in-memory state is invalid after... ...uninstalling updates to a system app Things seem to be working fine, however we were not as aggressive at writing out the current state in this case as we probably should be. Also introduce more features to the appops command, which are useful for testing this. Change-Id: I177a9cc0e16e98b76fee0d052d742e06842bb3f9 --- .../commands/appops/AppOpsCommand.java | 207 +++++++++++++++++- core/java/android/app/AppOpsManager.java | 15 +- .../android/internal/app/IAppOpsService.aidl | 2 +- .../com/android/server/AppOpsService.java | 48 ++-- 4 files changed, 241 insertions(+), 31 deletions(-) diff --git a/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java b/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java index c414f58300c33..3ec63b429a28e 100644 --- a/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java +++ b/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java @@ -24,10 +24,12 @@ import android.content.pm.IPackageManager; import android.os.ServiceManager; import android.os.UserHandle; +import android.util.TimeUtils; import com.android.internal.app.IAppOpsService; import com.android.internal.os.BaseCommand; import java.io.PrintStream; +import java.util.List; /** * This class is a command line utility for manipulating AppOps permissions. @@ -40,15 +42,19 @@ public class AppOpsCommand extends BaseCommand { @Override public void onShowUsage(PrintStream out) { - out.println("usage: adb shell appops set " - + " [--user ]\n" + out.println("usage: appops set [--user ] \n" + + " appops get [--user ] []\n" + + " appops reset [--user ] []\n" + " an Android package name.\n" + " an AppOps operation.\n" + + " one of allow, ignore, deny, or default\n" + " the user id under which the package is installed. If --user is not\n" + " specified, the current user is assumed.\n"); } private static final String COMMAND_SET = "set"; + private static final String COMMAND_GET = "get"; + private static final String COMMAND_RESET = "reset"; @Override public void onRun() throws Exception { @@ -58,8 +64,17 @@ public class AppOpsCommand extends BaseCommand { runSet(); break; + case COMMAND_GET: + runGet(); + break; + + case COMMAND_RESET: + runReset(); + break; + default: - throw new IllegalArgumentException("Unknown command '" + command + "'."); + System.err.println("Error: Unknown command: '" + command + "'."); + break; } } @@ -71,6 +86,23 @@ public class AppOpsCommand extends BaseCommand { private static final String MODE_IGNORE = "ignore"; private static final String MODE_DEFAULT = "default"; + private int strOpToOp(String op) { + try { + return AppOpsManager.strOpToOp(op); + } catch (IllegalArgumentException e) { + } + try { + return Integer.parseInt(op); + } catch (NumberFormatException e) { + } + try { + return AppOpsManager.strDebugOpToOp(op); + } catch (IllegalArgumentException e) { + System.err.println("Error: " + e.getMessage()); + return -1; + } + } + private void runSet() throws Exception { String packageName = null; String op = null; @@ -87,20 +119,27 @@ public class AppOpsCommand extends BaseCommand { } else if (mode == null) { mode = argument; } else { - throw new IllegalArgumentException("Unsupported argument: " + argument); + System.err.println("Error: Unsupported argument: " + argument); + return; } } } if (packageName == null) { - throw new IllegalArgumentException("Package name not specified."); + System.err.println("Error: Package name not specified."); + return; } else if (op == null) { - throw new IllegalArgumentException("Operation not specified."); + System.err.println("Error: Operation not specified."); + return; } else if (mode == null) { - throw new IllegalArgumentException("Mode not specified."); + System.err.println("Error: Mode not specified."); + return; } - final int opInt = AppOpsManager.strOpToOp(op); + final int opInt = strOpToOp(op); + if (opInt < 0) { + return; + } final int modeInt; switch (mode) { case MODE_ALLOW: @@ -116,7 +155,8 @@ public class AppOpsCommand extends BaseCommand { modeInt = AppOpsManager.MODE_DEFAULT; break; default: - throw new IllegalArgumentException("Mode is invalid."); + System.err.println("Error: Mode " + mode + " is not valid,"); + return; } // Parsing complete, let's execute the command. @@ -130,8 +170,155 @@ public class AppOpsCommand extends BaseCommand { ServiceManager.getService(Context.APP_OPS_SERVICE)); final int uid = pm.getPackageUid(packageName, userId); if (uid < 0) { - throw new Exception("No UID for " + packageName + " for user " + userId); + System.err.println("Error: No UID for " + packageName + " in user " + userId); + return; } appOpsService.setMode(opInt, uid, packageName, modeInt); } + + private void runGet() throws Exception { + String packageName = null; + String op = null; + int userId = UserHandle.USER_CURRENT; + for (String argument; (argument = nextArg()) != null;) { + if (ARGUMENT_USER.equals(argument)) { + userId = Integer.parseInt(nextArgRequired()); + } else { + if (packageName == null) { + packageName = argument; + } else if (op == null) { + op = argument; + } else { + System.err.println("Error: Unsupported argument: " + argument); + return; + } + } + } + + if (packageName == null) { + System.err.println("Error: Package name not specified."); + return; + } + + final int opInt = op != null ? strOpToOp(op) : 0; + + // Parsing complete, let's execute the command. + + if (userId == UserHandle.USER_CURRENT) { + userId = ActivityManager.getCurrentUser(); + } + + final IPackageManager pm = ActivityThread.getPackageManager(); + final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); + final int uid = pm.getPackageUid(packageName, userId); + if (uid < 0) { + System.err.println("Error: No UID for " + packageName + " in user " + userId); + return; + } + List ops = appOpsService.getOpsForPackage(uid, packageName, + op != null ? new int[] {opInt} : null); + if (ops == null || ops.size() <= 0) { + System.out.println("No operations."); + return; + } + final long now = System.currentTimeMillis(); + for (int i=0; i entries = ops.get(i).getOps(); + for (int j=0; j getPackagesForOps(in int[] ops); List getOpsForPackage(int uid, String packageName, in int[] ops); void setMode(int code, int uid, String packageName, int mode); - void resetAllModes(); + void resetAllModes(int reqUserId, String reqPackageName); int checkAudioOperation(int code, int usage, int uid, String packageName); void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages); diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index c3465d1d62c45..42a5195ac6467 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -29,6 +29,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppOpsManager; import android.content.Context; @@ -53,7 +54,6 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.Xml; @@ -78,10 +78,12 @@ public class AppOpsService extends IAppOpsService.Stub { final Handler mHandler; boolean mWriteScheduled; + boolean mFastWriteScheduled; final Runnable mWriteRunner = new Runnable() { public void run() { synchronized (AppOpsService.this) { mWriteScheduled = false; + mFastWriteScheduled = false; AsyncTask task = new AsyncTask() { @Override protected Void doInBackground(Void... params) { writeState(); @@ -237,7 +239,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (changed) { - scheduleWriteLocked(); + scheduleFastWriteLocked(); } } } @@ -250,7 +252,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (pkgs.size() <= 0) { mUidOps.remove(uid); } - scheduleWriteLocked(); + scheduleFastWriteLocked(); } } } @@ -260,7 +262,7 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { if (mUidOps.indexOfKey(uid) >= 0) { mUidOps.remove(uid); - scheduleWriteLocked(); + scheduleFastWriteLocked(); } } } @@ -400,7 +402,7 @@ public class AppOpsService extends IAppOpsService.Stub { // if there is nothing else interesting in it. pruneOp(op, uid, packageName); } - scheduleWriteNowLocked(); + scheduleFastWriteLocked(); } } } @@ -436,16 +438,20 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override - public void resetAllModes() { - int callingUid = Binder.getCallingUid(); + public void resetAllModes(int reqUserId, String reqPackageName) { + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, - Binder.getCallingPid(), callingUid, null); + callingPid, callingUid, null); + reqUserId = ActivityManager.handleIncomingUser(callingPid, callingUid, reqUserId, + true, true, "resetAllModes", null); HashMap>> callbacks = null; synchronized (this) { boolean changed = false; for (int i=mUidOps.size()-1; i>=0; i--) { HashMap packages = mUidOps.valueAt(i); - if (UserHandle.getUserId(callingUid) != UserHandle.getUserId(mUidOps.keyAt(i))) { + if (reqUserId != UserHandle.USER_ALL + && reqUserId != UserHandle.getUserId(mUidOps.keyAt(i))) { // Skip any ops for a different user continue; } @@ -453,6 +459,10 @@ public class AppOpsService extends IAppOpsService.Stub { while (it.hasNext()) { Map.Entry ent = it.next(); String packageName = ent.getKey(); + if (reqPackageName != null && !reqPackageName.equals(packageName)) { + // Skip any ops for a different package + continue; + } Ops pkgOps = ent.getValue(); for (int j=pkgOps.size()-1; j>=0; j--) { Op curOp = pkgOps.valueAt(j); @@ -478,7 +488,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (changed) { - scheduleWriteNowLocked(); + scheduleFastWriteLocked(); } } if (callbacks != null) { @@ -837,12 +847,13 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void scheduleWriteNowLocked() { - if (!mWriteScheduled) { + private void scheduleFastWriteLocked() { + if (!mFastWriteScheduled) { mWriteScheduled = true; + mFastWriteScheduled = true; + mHandler.removeCallbacks(mWriteRunner); + mHandler.postDelayed(mWriteRunner, 10*1000); } - mHandler.removeCallbacks(mWriteRunner); - mHandler.post(mWriteRunner); } private Op getOpLocked(int code, int uid, String packageName, boolean edit) { @@ -1236,12 +1247,11 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print(" ago"); } if (op.duration == -1) { - pw.println(" (running)"); - } else { - pw.print("; duration="); - TimeUtils.formatDuration(op.duration, pw); - pw.println(); + pw.print(" (running)"); + } else if (op.duration != 0) { + pw.print("; duration="); TimeUtils.formatDuration(op.duration, pw); } + pw.println(); } } }