Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4482c37e authored by riddle_hsu's avatar riddle_hsu Committed by Steve Kondik
Browse files

[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
parent 6db877af
Loading
Loading
Loading
Loading
+53 −49
Original line number Diff line number Diff line
@@ -15701,30 +15701,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<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
        if (stickies == null) {
            return cur;
        }
        final ArrayList<Intent> list = stickies.get(action);
        if (list == null) {
            return cur;
        }
        int N = list.size();
        for (int i=0; i<N; i++) {
            Intent intent = list.get(i);
            if (filter.match(resolver, intent, true, TAG) >= 0) {
                if (cur == null) {
                    cur = new ArrayList<Intent>();
                }
                cur.add(intent);
            }
        }
        return cur;
    }
    boolean isPendingBroadcastProcessLocked(int pid) {
        return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid)
                || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid);
@@ -15749,10 +15725,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<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        int callingUid;
        int callingPid;
        synchronized(this) {
            ProcessRecord callerApp = null;
            if (caller != null) {
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
@@ -15775,39 +15752,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<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }
            // Look for any matching sticky broadcasts...
            Iterator actions = filter.actionsIterator();
            if (actions != null) {
            // Collect stickies of users
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            while (actions.hasNext()) {
                    String action = (String)actions.next();
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.USER_ALL);
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.getUserId(callingUid));
                String action = actions.next();
                for (int id : userIds) {
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                    if (stickies != null) {
                        ArrayList<Intent> intents = stickies.get(action);
                        if (intents != null) {
                            if (stickyIntents == null) {
                                stickyIntents = new ArrayList<Intent>();
                            }
                            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);
        ArrayList<Intent> 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<Intent>();
                    }
                    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) {