From 70973b7bea5af6db40cf37f3eacb4ad7a5781b8c Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 29 Jul 2013 18:11:02 -0700 Subject: [PATCH 1/6] (DO NOT MERGE) Fix pub issue #58043: Copy crash in Android 4.3... ...when clipboard listener attached We need to clear the calling identity before dispatching change notifications. Also make this more robust, so that in the face of any failure we will clean up the broadcast state. Integrated from master. Change-Id: I05e1f76ffd486439535631fe4062dabe94bd2ccf --- core/java/android/app/ActivityManager.java | 5 +-- .../com/android/server/ClipboardService.java | 35 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index bb9e19f771d45..a25e311f882aa 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -16,6 +16,7 @@ package android.app; +import android.R; import com.android.internal.app.IUsageStats; import com.android.internal.os.PkgUsageStats; import com.android.internal.util.MemInfoReader; @@ -369,9 +370,9 @@ public class ActivityManager { // Really brain dead right now -- just take this from the configured // vm heap size, and assume it is in megabytes and thus ends with "m". String vmHeapSize = SystemProperties.get("dalvik.vm.heapsize", "16m"); - return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1)); + return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length() - 1)); } - + /** * Used by persistent processes to determine if they are running on a * higher-end device so should be okay using hardware drawing acceleration diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java index 058857ddb6052..0bf03b5ff4531 100644 --- a/services/java/com/android/server/ClipboardService.java +++ b/services/java/com/android/server/ClipboardService.java @@ -154,31 +154,36 @@ public class ClipboardService extends IClipboard.Stub { if (clip != null && clip.getItemCount() <= 0) { throw new IllegalArgumentException("No items"); } - if (mAppOps.noteOp(AppOpsManager.OP_WRITE_CLIPBOARD, Binder.getCallingUid(), + final int callingUid = Binder.getCallingUid(); + if (mAppOps.noteOp(AppOpsManager.OP_WRITE_CLIPBOARD, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } - checkDataOwnerLocked(clip, Binder.getCallingUid()); + checkDataOwnerLocked(clip, callingUid); clearActiveOwnersLocked(); PerUserClipboard clipboard = getClipboard(); clipboard.primaryClip = clip; + final long ident = Binder.clearCallingIdentity(); final int n = clipboard.primaryClipListeners.beginBroadcast(); - for (int i = 0; i < n; i++) { - try { - ListenerInfo li = (ListenerInfo) - clipboard.primaryClipListeners.getBroadcastCookie(i); - if (mAppOps.checkOpNoThrow(AppOpsManager.OP_READ_CLIPBOARD, li.mUid, - li.mPackageName) == AppOpsManager.MODE_ALLOWED) { - clipboard.primaryClipListeners.getBroadcastItem(i) - .dispatchPrimaryClipChanged(); + try { + for (int i = 0; i < n; i++) { + try { + ListenerInfo li = (ListenerInfo) + clipboard.primaryClipListeners.getBroadcastCookie(i); + if (mAppOps.checkOpNoThrow(AppOpsManager.OP_READ_CLIPBOARD, li.mUid, + li.mPackageName) == AppOpsManager.MODE_ALLOWED) { + clipboard.primaryClipListeners.getBroadcastItem(i) + .dispatchPrimaryClipChanged(); + } + } catch (RemoteException e) { + // The RemoteCallbackList will take care of removing + // the dead object for us. } - } catch (RemoteException e) { - - // The RemoteCallbackList will take care of removing - // the dead object for us. } + } finally { + clipboard.primaryClipListeners.finishBroadcast(); + Binder.restoreCallingIdentity(ident); } - clipboard.primaryClipListeners.finishBroadcast(); } } From 74ff4588a9bcfe40a7e2ceb254aaada2bcd5b0c2 Mon Sep 17 00:00:00 2001 From: Daniel Sandler Date: Wed, 31 Jul 2013 20:21:07 -0400 Subject: [PATCH 2/6] Allow the user to block notifications for foreground services. Stepping carefully around the fix in change Idc788527, we perform security (and AppOps, and therefore user block) checks against the uid passed to enqueueNotificationInternal rather than the Binder caller. Bug: 9502541 Change-Id: I91745db92823381df75721c1b8f11013a7fa4ecc --- .../android/server/NotificationManagerService.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 29aaeaf33f4f3..29780c06e8e5d 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -1606,7 +1606,7 @@ public class NotificationManagerService extends INotificationManager.Stub Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); } checkCallerIsSystemOrSameApp(pkg); - final boolean isSystemNotification = isCallerSystem() || ("android".equals(pkg)); + final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); userId = ActivityManager.handleIncomingUser(callingPid, callingUid, userId, true, false, "enqueueNotification", pkg); @@ -2084,14 +2084,18 @@ public class NotificationManagerService extends INotificationManager.Stub cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId); } - // Return true if the caller is a system or phone UID and therefore should not have + // Return true if the UID is a system or phone UID and therefore should not have // any notifications or toasts blocked. - boolean isCallerSystem() { - final int uid = Binder.getCallingUid(); + boolean isUidSystem(int uid) { final int appid = UserHandle.getAppId(uid); return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0); } + // same as isUidSystem(int, int) for the Binder caller's UID. + boolean isCallerSystem() { + return isUidSystem(Binder.getCallingUid()); + } + void checkCallerIsSystem() { if (isCallerSystem()) { return; From 6ad6be5ffa3fb75a2c6887808e30d370345131f4 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 1 Aug 2013 11:09:27 -0700 Subject: [PATCH 3/6] Add version identifier to app ops. All old versions drop their modes except for the notification op (which is the only one there is currently a user-visible control for). Change-Id: I9e09cebe63e9ea81f2adc01aef7d1a5a59f57a56 --- .../com/android/server/AppOpsService.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java index a402642bc0de9..983447969b1dc 100644 --- a/services/java/com/android/server/AppOpsService.java +++ b/services/java/com/android/server/AppOpsService.java @@ -63,6 +63,8 @@ public class AppOpsService extends IAppOpsService.Stub { // Write at most every 30 minutes. static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000; + static final int CURRENT_VERSION = 1; + Context mContext; final AtomicFile mFile; final Handler mHandler; @@ -631,6 +633,9 @@ public class AppOpsService extends IAppOpsService.Stub { throw new IllegalStateException("no start tag found"); } + String versStr = parser.getAttributeValue(null, "vers"); + int vers = versStr != null ? Integer.parseInt(versStr) : 0; + int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { @@ -640,7 +645,7 @@ public class AppOpsService extends IAppOpsService.Stub { String tagName = parser.getName(); if (tagName.equals("pkg")) { - readPackage(parser); + readPackage(parser, vers); } else { Slog.w(TAG, "Unknown element under : " + parser.getName()); @@ -673,7 +678,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - void readPackage(XmlPullParser parser) throws NumberFormatException, + void readPackage(XmlPullParser parser, int vers) throws NumberFormatException, XmlPullParserException, IOException { String pkgName = parser.getAttributeValue(null, "n"); int outerDepth = parser.getDepth(); @@ -686,7 +691,7 @@ public class AppOpsService extends IAppOpsService.Stub { String tagName = parser.getName(); if (tagName.equals("uid")) { - readUid(parser, pkgName); + readUid(parser, vers, pkgName); } else { Slog.w(TAG, "Unknown element under : " + parser.getName()); @@ -695,7 +700,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, + void readUid(XmlPullParser parser, int vers, String pkgName) throws NumberFormatException, XmlPullParserException, IOException { int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); int outerDepth = parser.getDepth(); @@ -711,7 +716,12 @@ public class AppOpsService extends IAppOpsService.Stub { Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n"))); String mode = parser.getAttributeValue(null, "m"); if (mode != null) { - op.mode = Integer.parseInt(mode); + if (vers < CURRENT_VERSION && op.op != AppOpsManager.OP_POST_NOTIFICATION) { + Slog.w(TAG, "AppOps vers " + vers + ": drop mode from " + + pkgName + "/" + uid + " op " + op.op); + } else { + op.mode = Integer.parseInt(mode); + } } String time = parser.getAttributeValue(null, "t"); if (time != null) { @@ -761,6 +771,7 @@ public class AppOpsService extends IAppOpsService.Stub { out.setOutput(stream, "utf-8"); out.startDocument(null, true); out.startTag(null, "app-ops"); + out.attribute(null, "vers", Integer.toString(CURRENT_VERSION)); if (allOps != null) { String lastPkg = null; From 990b8e2a45f2b2005d684f6bf79d981d83bdd01d Mon Sep 17 00:00:00 2001 From: Dave Burke Date: Fri, 2 Aug 2013 20:25:02 +0000 Subject: [PATCH 4/6] Revert "Add version identifier to app ops." This reverts commit 483a6bbfc118409b9c46b226cf0aa6afdc4d9159. Change-Id: I91d6b8b8d1789089bf9764e734f7fe59d03151a2 --- .../com/android/server/AppOpsService.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java index 983447969b1dc..a402642bc0de9 100644 --- a/services/java/com/android/server/AppOpsService.java +++ b/services/java/com/android/server/AppOpsService.java @@ -63,8 +63,6 @@ public class AppOpsService extends IAppOpsService.Stub { // Write at most every 30 minutes. static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000; - static final int CURRENT_VERSION = 1; - Context mContext; final AtomicFile mFile; final Handler mHandler; @@ -633,9 +631,6 @@ public class AppOpsService extends IAppOpsService.Stub { throw new IllegalStateException("no start tag found"); } - String versStr = parser.getAttributeValue(null, "vers"); - int vers = versStr != null ? Integer.parseInt(versStr) : 0; - int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { @@ -645,7 +640,7 @@ public class AppOpsService extends IAppOpsService.Stub { String tagName = parser.getName(); if (tagName.equals("pkg")) { - readPackage(parser, vers); + readPackage(parser); } else { Slog.w(TAG, "Unknown element under : " + parser.getName()); @@ -678,7 +673,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - void readPackage(XmlPullParser parser, int vers) throws NumberFormatException, + void readPackage(XmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { String pkgName = parser.getAttributeValue(null, "n"); int outerDepth = parser.getDepth(); @@ -691,7 +686,7 @@ public class AppOpsService extends IAppOpsService.Stub { String tagName = parser.getName(); if (tagName.equals("uid")) { - readUid(parser, vers, pkgName); + readUid(parser, pkgName); } else { Slog.w(TAG, "Unknown element under : " + parser.getName()); @@ -700,7 +695,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - void readUid(XmlPullParser parser, int vers, String pkgName) throws NumberFormatException, + void readUid(XmlPullParser parser, String pkgName) throws NumberFormatException, XmlPullParserException, IOException { int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); int outerDepth = parser.getDepth(); @@ -716,12 +711,7 @@ public class AppOpsService extends IAppOpsService.Stub { Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n"))); String mode = parser.getAttributeValue(null, "m"); if (mode != null) { - if (vers < CURRENT_VERSION && op.op != AppOpsManager.OP_POST_NOTIFICATION) { - Slog.w(TAG, "AppOps vers " + vers + ": drop mode from " - + pkgName + "/" + uid + " op " + op.op); - } else { - op.mode = Integer.parseInt(mode); - } + op.mode = Integer.parseInt(mode); } String time = parser.getAttributeValue(null, "t"); if (time != null) { @@ -771,7 +761,6 @@ public class AppOpsService extends IAppOpsService.Stub { out.setOutput(stream, "utf-8"); out.startDocument(null, true); out.startTag(null, "app-ops"); - out.attribute(null, "vers", Integer.toString(CURRENT_VERSION)); if (allOps != null) { String lastPkg = null; From 6037ec56af9b38bc5603dc0073a0d8411b948294 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 2 Aug 2013 18:10:10 -0700 Subject: [PATCH 5/6] Add new app ops method to reset all op modes. Change-Id: I5ee6764de8dc31d812e5a788914ab0099bbef4c0 --- core/java/android/app/AppOpsManager.java | 8 ++ .../android/internal/app/IAppOpsService.aidl | 1 + .../com/android/server/AppOpsService.java | 102 +++++++++++++++--- 3 files changed, 96 insertions(+), 15 deletions(-) diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index c9776f17fc9a1..4fcb18a84b4f8 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -422,6 +422,14 @@ public class AppOpsManager { } } + /** @hide */ + public void resetAllModes() { + try { + mService.resetAllModes(); + } catch (RemoteException e) { + } + } + public void startWatchingMode(int op, String packageName, final Callback callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index a9da863761531..cfd9cc7cb071c 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -33,4 +33,5 @@ interface IAppOpsService { List 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(); } diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java index a402642bc0de9..20ad63683acd8 100644 --- a/services/java/com/android/server/AppOpsService.java +++ b/services/java/com/android/server/AppOpsService.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import android.app.AppOpsManager; import android.content.Context; @@ -42,6 +43,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.util.AtomicFile; import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -288,6 +290,24 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private void pruneOp(Op op, int uid, String packageName) { + if (op.time == 0 && op.rejectTime == 0) { + Ops ops = getOpsLocked(uid, packageName, false); + if (ops != null) { + ops.remove(op.op); + if (ops.size() <= 0) { + HashMap pkgOps = mUidOps.get(uid); + if (pkgOps != null) { + pkgOps.remove(ops.packageName); + if (pkgOps.size() <= 0) { + mUidOps.remove(uid); + } + } + } + } + } + } + @Override public void setMode(int code, int uid, String packageName, int mode) { verifyIncomingUid(uid); @@ -316,21 +336,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == AppOpsManager.MODE_ALLOWED) { // If going into the default mode, prune this op // if there is nothing else interesting in it. - if (op.time == 0 && op.rejectTime == 0) { - Ops ops = getOpsLocked(uid, packageName, false); - if (ops != null) { - ops.remove(op.op); - if (ops.size() <= 0) { - HashMap pkgOps = mUidOps.get(uid); - if (pkgOps != null) { - pkgOps.remove(ops.packageName); - if (pkgOps.size() <= 0) { - mUidOps.remove(uid); - } - } - } - } - } + pruneOp(op, uid, packageName); } scheduleWriteNowLocked(); } @@ -346,6 +352,72 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private static HashMap>> addCallbacks( + HashMap>> callbacks, + String packageName, int op, ArrayList cbs) { + if (cbs == null) { + return callbacks; + } + if (callbacks == null) { + callbacks = new HashMap>>(); + } + for (int i=0; i> reports = callbacks.get(cb); + if (reports == null) { + reports = new ArrayList>(); + callbacks.put(cb, reports); + } + reports.add(new Pair(packageName, op)); + } + return callbacks; + } + + @Override + public void resetAllModes() { + mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, + Binder.getCallingPid(), Binder.getCallingUid(), null); + HashMap>> callbacks = null; + synchronized (this) { + boolean changed = false; + for (int i=0; i packages = mUidOps.valueAt(i); + for (Map.Entry ent : packages.entrySet()) { + String packageName = ent.getKey(); + Ops pkgOps = ent.getValue(); + for (int j=0; j>> ent : callbacks.entrySet()) { + Callback cb = ent.getKey(); + ArrayList> reports = ent.getValue(); + for (int i=0; i rep = reports.get(i); + try { + cb.mCallback.opChanged(rep.second, rep.first); + } catch (RemoteException e) { + } + } + } + } + } + @Override public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) { synchronized (this) { From 1d3ff5b4546a0ff4bcc6af61ff3c7b024cbac656 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 2 Aug 2013 18:10:10 -0700 Subject: [PATCH 6/6] Add new app ops method to reset all op modes. Change-Id: I5ee6764de8dc31d812e5a788914ab0099bbef4c0 --- core/java/android/app/AppOpsManager.java | 8 ++ .../android/internal/app/IAppOpsService.aidl | 1 + .../com/android/server/AppOpsService.java | 102 +++++++++++++++--- 3 files changed, 96 insertions(+), 15 deletions(-) diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index c9776f17fc9a1..4fcb18a84b4f8 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -422,6 +422,14 @@ public class AppOpsManager { } } + /** @hide */ + public void resetAllModes() { + try { + mService.resetAllModes(); + } catch (RemoteException e) { + } + } + public void startWatchingMode(int op, String packageName, final Callback callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index a9da863761531..cfd9cc7cb071c 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -33,4 +33,5 @@ interface IAppOpsService { List 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(); } diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java index a402642bc0de9..20ad63683acd8 100644 --- a/services/java/com/android/server/AppOpsService.java +++ b/services/java/com/android/server/AppOpsService.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import android.app.AppOpsManager; import android.content.Context; @@ -42,6 +43,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.util.AtomicFile; import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -288,6 +290,24 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private void pruneOp(Op op, int uid, String packageName) { + if (op.time == 0 && op.rejectTime == 0) { + Ops ops = getOpsLocked(uid, packageName, false); + if (ops != null) { + ops.remove(op.op); + if (ops.size() <= 0) { + HashMap pkgOps = mUidOps.get(uid); + if (pkgOps != null) { + pkgOps.remove(ops.packageName); + if (pkgOps.size() <= 0) { + mUidOps.remove(uid); + } + } + } + } + } + } + @Override public void setMode(int code, int uid, String packageName, int mode) { verifyIncomingUid(uid); @@ -316,21 +336,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == AppOpsManager.MODE_ALLOWED) { // If going into the default mode, prune this op // if there is nothing else interesting in it. - if (op.time == 0 && op.rejectTime == 0) { - Ops ops = getOpsLocked(uid, packageName, false); - if (ops != null) { - ops.remove(op.op); - if (ops.size() <= 0) { - HashMap pkgOps = mUidOps.get(uid); - if (pkgOps != null) { - pkgOps.remove(ops.packageName); - if (pkgOps.size() <= 0) { - mUidOps.remove(uid); - } - } - } - } - } + pruneOp(op, uid, packageName); } scheduleWriteNowLocked(); } @@ -346,6 +352,72 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private static HashMap>> addCallbacks( + HashMap>> callbacks, + String packageName, int op, ArrayList cbs) { + if (cbs == null) { + return callbacks; + } + if (callbacks == null) { + callbacks = new HashMap>>(); + } + for (int i=0; i> reports = callbacks.get(cb); + if (reports == null) { + reports = new ArrayList>(); + callbacks.put(cb, reports); + } + reports.add(new Pair(packageName, op)); + } + return callbacks; + } + + @Override + public void resetAllModes() { + mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, + Binder.getCallingPid(), Binder.getCallingUid(), null); + HashMap>> callbacks = null; + synchronized (this) { + boolean changed = false; + for (int i=0; i packages = mUidOps.valueAt(i); + for (Map.Entry ent : packages.entrySet()) { + String packageName = ent.getKey(); + Ops pkgOps = ent.getValue(); + for (int j=0; j>> ent : callbacks.entrySet()) { + Callback cb = ent.getKey(); + ArrayList> reports = ent.getValue(); + for (int i=0; i rep = reports.get(i); + try { + cb.mCallback.opChanged(rep.second, rep.first); + } catch (RemoteException e) { + } + } + } + } + } + @Override public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) { synchronized (this) {