From 9ac9609f4d078dc0d7fe7c703fb88d20fd629bfe Mon Sep 17 00:00:00 2001 From: riddle_hsu Date: Tue, 23 Dec 2014 22:52:34 +0800 Subject: [PATCH] [ActivityManager] Prevent application holding AMS lock Symptom: Watchdog timeout. Reproduce code: String action = "lets.deadlock"; Uri baseUri = Uri.parse("content://i.am.bad"); Uri uri = ContentUris.withAppendedId(baseUri, 1); Intent intent = new Intent(action, uri); sendStickyBroadcast(intent); IntentFilter filter = new IntentFilter(action); filter.addDataScheme(baseUri.getScheme()); filter.addDataAuthority(baseUri.getAuthority(), null); filter.addDataPath(uri.getPath(), 0); registerReceiver(null, filter); In target provider's getType: Invoke AMS function will result deadlock. Or sleep a long time will also trigger watchdog timeout. Root Cause: If broadcast is sticky with content scheme intent. Register receiver will trigger access provider when matching intent with IntentFilter, and it executes in ActivityManagerService's lock. Solution: Obtain necessary data to local to split lock block. Change-Id: I0fb94472cdc478997e40ba2a60a988c5f53badb2 --- .../server/am/ActivityManagerService.java | 104 +++++++++--------- 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e3f7fb3c91200..379410d8d9d52 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -15137,30 +15137,6 @@ public final class ActivityManagerService extends ActivityManagerNative // BROADCASTS // ========================================================= - private final List getStickiesLocked(String action, IntentFilter filter, - List cur, int userId) { - final ContentResolver resolver = mContext.getContentResolver(); - ArrayMap> stickies = mStickyBroadcasts.get(userId); - if (stickies == null) { - return cur; - } - final ArrayList list = stickies.get(action); - if (list == null) { - return cur; - } - int N = list.size(); - for (int i=0; i= 0) { - if (cur == null) { - cur = new ArrayList(); - } - cur.add(intent); - } - } - return cur; - } - boolean isPendingBroadcastProcessLocked(int pid) { return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid) || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid); @@ -15185,10 +15161,11 @@ public final class ActivityManagerService extends ActivityManagerNative public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) { enforceNotIsolatedCaller("registerReceiver"); + ArrayList stickyIntents = null; + ProcessRecord callerApp = null; int callingUid; int callingPid; synchronized(this) { - ProcessRecord callerApp = null; if (caller != null) { callerApp = getRecordForAppLocked(caller); if (callerApp == null) { @@ -15211,39 +15188,66 @@ public final class ActivityManagerService extends ActivityManagerNative callingPid = Binder.getCallingPid(); } - userId = this.handleIncomingUser(callingPid, callingUid, userId, + userId = handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage); - List allSticky = null; + Iterator actions = filter.actionsIterator(); + if (actions == null) { + ArrayList noAction = new ArrayList(1); + noAction.add(null); + actions = noAction.iterator(); + } - // Look for any matching sticky broadcasts... - Iterator actions = filter.actionsIterator(); - if (actions != null) { - while (actions.hasNext()) { - String action = (String)actions.next(); - allSticky = getStickiesLocked(action, filter, allSticky, - UserHandle.USER_ALL); - allSticky = getStickiesLocked(action, filter, allSticky, - UserHandle.getUserId(callingUid)); + // Collect stickies of users + int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; + while (actions.hasNext()) { + String action = actions.next(); + for (int id : userIds) { + ArrayMap> stickies = mStickyBroadcasts.get(id); + if (stickies != null) { + ArrayList intents = stickies.get(action); + if (intents != null) { + if (stickyIntents == null) { + stickyIntents = new ArrayList(); + } + stickyIntents.addAll(intents); + } + } } - } else { - allSticky = getStickiesLocked(null, filter, allSticky, - UserHandle.USER_ALL); - allSticky = getStickiesLocked(null, filter, allSticky, - UserHandle.getUserId(callingUid)); } + } - // The first sticky in the list is returned directly back to - // the client. - Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null; - - if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter - + ": " + sticky); - - if (receiver == null) { - return sticky; + ArrayList allSticky = null; + if (stickyIntents != null) { + final ContentResolver resolver = mContext.getContentResolver(); + // Look for any matching sticky broadcasts... + for (int i = 0, N = stickyIntents.size(); i < N; i++) { + Intent intent = stickyIntents.get(i); + // If intent has scheme "content", it will need to acccess + // provider that needs to lock mProviderMap in ActivityThread + // and also it may need to wait application response, so we + // cannot lock ActivityManagerService here. + if (filter.match(resolver, intent, true, TAG) >= 0) { + if (allSticky == null) { + allSticky = new ArrayList(); + } + allSticky.add(intent); + } } + } + // The first sticky in the list is returned directly back to the client. + Intent sticky = allSticky != null ? allSticky.get(0) : null; + if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter + ": " + sticky); + if (receiver == null) { + return sticky; + } + + synchronized (this) { + if (callerApp != null && callerApp.pid == 0) { + // Caller already died + return null; + } ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder()); if (rl == null) {