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

Commit 1b64e0d8 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Work on death recipient leaks in Activity Manager and Content Service.

This should fix a leak of process death recipients in the activity manager.

Also add debugging of content observers to try to track down what looks
like a leak of them in the content service.

Change-Id: Id6823679493ef0cde5307bb66490ebe31b878556
parent c9b5970d
Loading
Loading
Loading
Loading
+96 −8
Original line number Diff line number Diff line
@@ -20,17 +20,21 @@ import android.accounts.Account;
import android.database.IContentObserver;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.SparseIntArray;
import android.Manifest;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
@@ -70,6 +74,40 @@ public final class ContentService extends IContentService.Stub {
            } else {
                mSyncManager.dump(fd, pw);
            }
            pw.println();
            pw.println("Observer tree:");
            synchronized (mRootNode) {
                int[] counts = new int[2];
                final SparseIntArray pidCounts = new SparseIntArray();
                mRootNode.dumpLocked(fd, pw, args, "", "  ", counts, pidCounts);
                pw.println();
                ArrayList<Integer> sorted = new ArrayList<Integer>();
                for (int i=0; i<pidCounts.size(); i++) {
                    sorted.add(pidCounts.keyAt(i));
                }
                Collections.sort(sorted, new Comparator<Integer>() {
                    @Override
                    public int compare(Integer lhs, Integer rhs) {
                        int lc = pidCounts.get(lhs);
                        int rc = pidCounts.get(rhs);
                        if (lc < rc) {
                            return 1;
                        } else if (lc > rc) {
                            return -1;
                        }
                        return 0;
                    }

                });
                for (int i=0; i<sorted.size(); i++) {
                    int pid = sorted.get(i);
                    pw.print("  pid "); pw.print(pid); pw.print(": ");
                            pw.print(pidCounts.get(pid)); pw.println(" observers");
                }
                pw.println();
                pw.print(" Total number of nodes: "); pw.println(counts[0]);
                pw.print(" Total number of observers: "); pw.println(counts[1]);
            }
        } finally {
            restoreCallingIdentity(identityToken);
        }
@@ -102,7 +140,8 @@ public final class ContentService extends IContentService.Stub {
            throw new IllegalArgumentException("You must pass a valid uri and observer");
        }
        synchronized (mRootNode) {
            mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode);
            mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode,
                    Binder.getCallingUid(), Binder.getCallingPid());
            if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
                    " with notifyForDescendents " + notifyForDescendents);
        }
@@ -465,12 +504,17 @@ public final class ContentService extends IContentService.Stub {
    public static final class ObserverNode {
        private class ObserverEntry implements IBinder.DeathRecipient {
            public final IContentObserver observer;
            public final int uid;
            public final int pid;
            public final boolean notifyForDescendents;
            private final Object observersLock;

            public ObserverEntry(IContentObserver o, boolean n, Object observersLock) {
            public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
                    int _uid, int _pid) {
                this.observersLock = observersLock;
                observer = o;
                uid = _uid;
                pid = _pid;
                notifyForDescendents = n;
                try {
                    observer.asBinder().linkToDeath(this, 0);
@@ -484,6 +528,16 @@ public final class ContentService extends IContentService.Stub {
                    removeObserverLocked(observer);
                }
            }

            public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
                    String name, String prefix, SparseIntArray pidCounts) {
                pidCounts.put(pid, pidCounts.get(pid)+1);
                pw.print(prefix); pw.print(name); pw.print(": pid=");
                        pw.print(pid); pw.print(" uid=");
                        pw.print(uid); pw.print(" target=");
                        pw.println(Integer.toHexString(System.identityHashCode(
                                observer != null ? observer.asBinder() : null)));
            }
        }

        public static final int INSERT_TYPE = 0;
@@ -498,6 +552,37 @@ public final class ContentService extends IContentService.Stub {
            mName = name;
        }

        public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
                String name, String prefix, int[] counts, SparseIntArray pidCounts) {
            String innerName = null;
            if (mObservers.size() > 0) {
                if ("".equals(name)) {
                    innerName = mName;
                } else {
                    innerName = name + "/" + mName;
                }
                for (int i=0; i<mObservers.size(); i++) {
                    counts[1]++;
                    mObservers.get(i).dumpLocked(fd, pw, args, innerName, prefix,
                            pidCounts);
                }
            }
            if (mChildren.size() > 0) {
                if (innerName == null) {
                    if ("".equals(name)) {
                        innerName = mName;
                    } else {
                        innerName = name + "/" + mName;
                    }
                }
                for (int i=0; i<mChildren.size(); i++) {
                    counts[0]++;
                    mChildren.get(i).dumpLocked(fd, pw, args, innerName, prefix,
                            counts, pidCounts);
                }
            }
        }

        private String getUriSegment(Uri uri, int index) {
            if (uri != null) {
                if (index == 0) {
@@ -518,15 +603,16 @@ public final class ContentService extends IContentService.Stub {
        }

        public void addObserverLocked(Uri uri, IContentObserver observer,
                boolean notifyForDescendents, Object observersLock) {
            addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock);
                boolean notifyForDescendents, Object observersLock, int uid, int pid) {
            addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock, uid, pid);
        }

        private void addObserverLocked(Uri uri, int index, IContentObserver observer,
                boolean notifyForDescendents, Object observersLock) {
                boolean notifyForDescendents, Object observersLock, int uid, int pid) {
            // If this is the leaf node add the observer
            if (index == countUriSegments(uri)) {
                mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock));
                mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock,
                        uid, pid));
                return;
            }

@@ -539,7 +625,8 @@ public final class ContentService extends IContentService.Stub {
            for (int i = 0; i < N; i++) {
                ObserverNode node = mChildren.get(i);
                if (node.mName.equals(segment)) {
                    node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
                    node.addObserverLocked(uri, index + 1, observer, notifyForDescendents,
                            observersLock, uid, pid);
                    return;
                }
            }
@@ -547,7 +634,8 @@ public final class ContentService extends IContentService.Stub {
            // No child found, create one
            ObserverNode node = new ObserverNode(segment);
            mChildren.add(node);
            node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
            node.addObserverLocked(uri, index + 1, observer, notifyForDescendents,
                    observersLock, uid, pid);
        }

        public boolean removeObserverLocked(IContentObserver observer) {
+3 −3
Original line number Diff line number Diff line
@@ -48,9 +48,9 @@ public class ObserverNodeTest extends AndroidTestCase {
        int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3};

        // special case
        root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root);
        root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, 0, 0);
        for(int i = 1; i < uris.length; i++) {
            root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root);
            root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, 0, 0);
        }

        ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
@@ -77,7 +77,7 @@ public class ObserverNodeTest extends AndroidTestCase {
        int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1};

        for(int i = 0; i < uris.length; i++) {
            root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root);
            root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, 0, 0);
        }

        ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
+6 −3
Original line number Diff line number Diff line
@@ -3610,8 +3610,10 @@ public final class ActivityManagerService extends ActivityManagerNative
        String processName = app.processName;
        try {
            thread.asBinder().linkToDeath(new AppDeathRecipient(
                    app, pid, thread), 0);
            AppDeathRecipient adr = new AppDeathRecipient(
                    app, pid, thread);
            thread.asBinder().linkToDeath(adr, 0);
            app.deathRecipient = adr;
        } catch (RemoteException e) {
            app.resetPackageList();
            startProcessLocked(app, "link fail", processName);
@@ -3687,6 +3689,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            Slog.w(TAG, "Exception thrown during bind!", e);
            app.resetPackageList();
            app.unlinkDeathRecipient();
            startProcessLocked(app, "bind fail", processName);
            return false;
        }
@@ -9210,6 +9213,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        app.notResponding = false;
        
        app.resetPackageList();
        app.unlinkDeathRecipient();
        app.thread = null;
        app.forcingToForeground = null;
        app.foregroundServices = false;
@@ -9327,7 +9331,6 @@ public final class ActivityManagerService extends ActivityManagerNative
            // This app is persistent, so we need to keep its record around.
            // If it is not already on the pending app list, add it there
            // and start a new process for it.
            app.thread = null;
            app.forcingToForeground = null;
            app.foregroundServices = false;
            if (mPersistentStartingProcesses.indexOf(app) < 0) {
+8 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ class ProcessRecord {
    int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
    int lruSeq;                 // Sequence id for identifying LRU update cycles
    CompatibilityInfo compat;   // last used compatibility mode
    IBinder.DeathRecipient deathRecipient; // Who is watching for the death.
    ComponentName instrumentationClass;// class installed to instrument app
    ApplicationInfo instrumentationInfo; // the application being instrumented
    String instrumentationProfileFile; // where to save profiling
@@ -297,6 +298,13 @@ class ProcessRecord {
        }
    }
    
    public void unlinkDeathRecipient() {
        if (deathRecipient != null && thread != null) {
            thread.asBinder().unlinkToDeath(deathRecipient, 0);
        }
        deathRecipient = null;
    }

    public String toShortString() {
        if (shortStringName != null) {
            return shortStringName;