Fix (mostly) issue #5109947: Race condition between retrieving a...

...content provider and updating its oom adj

This introduces the concept of an "unstable" reference on a content
provider.  When holding such a reference (and no normal stable ref),
the content provider dying will not cause the client process to be
killed.

This is used in ContentResolver.query(), .openAssetFileDescriptor(),
and .openTypedAssetFileDescriptor() to first access the provider
with an unstable reference, and if at the point of calling into the
provider we find it is dead then acquiring a new stable reference
and doing the operation again.  Thus if the provider process dies
at any point until we get the result back, our own process will not
be killed and we can safely retry the operation.

Arguably there is still the potential for a race -- if somehow the
provider is killed way late by the OOM killer after the query or
open has returned -- but this should now be *extremely* unlikely.
We also continue to have the issue with the other calls, but these
are much less critical, and the same model can't be used there (we
wouldn't want to execute two insert operations for example).

The implementation of this required some significant changes to the
underlying plumbing of content providers, now keeping track of the
two different reference counts, and managing them appropriately.  To
facilitate this, the activity manager now has a formal connection
object for a client reference on a content provider, which hands to
the application when opening the provider.

These changes have allowed a lot of the code to be cleaned up and
subtle issues closed.  For example, when a process is crashing, we
now have a much better idea of the state of content provider clients
(olding a stable ref, unstable ref, or waiting for it to launch), so
that we can correctly handle each of these.

The client side code is also a fair amount cleaner, though in the
future there is more than should be done.  In particular, the two
ProviderClientRecord and ProviderRefCount classes should be combined
into one, part of which is exposed to the ContentResolver internal
API as a reference on a content provider with methods for updating
reference counts and such.  Some day we'll do that.

Change-Id: I87b10d1b67573ab899e09ca428f1b556fd669c8c
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 608b09a..fb21b06 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -18,6 +18,7 @@
 
 import android.app.IActivityManager.ContentProviderHolder;
 import android.content.ComponentName;
+import android.content.IContentProvider;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.os.IBinder;
@@ -27,28 +28,35 @@
 import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 
-class ContentProviderRecord extends ContentProviderHolder {
+class ContentProviderRecord {
+    final ActivityManagerService service;
+    public final ProviderInfo info;
+    final int uid;
+    final ApplicationInfo appInfo;
+    final ComponentName name;
+    public IContentProvider provider;
+    public boolean noReleaseNeeded;
     // All attached clients
-    final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
+    final ArrayList<ContentProviderConnection> connections
+            = new ArrayList<ContentProviderConnection>();
+    //final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
     // Handles for non-framework processes supported by this provider
     HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
     // Count for external process for which we have no handles.
     int externalProcessNoHandleCount;
-    final ActivityManagerService service;
-    final int uid;
-    final ApplicationInfo appInfo;
-    final ComponentName name;
     ProcessRecord proc; // if non-null, hosting process.
     ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
     String stringName;
+    String shortStringName;
 
     public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
             ApplicationInfo ai, ComponentName _name) {
-        super(_info);
         service = _service;
+        info = _info;
         uid = ai.uid;
         appInfo = ai;
         name = _name;
@@ -56,12 +64,20 @@
     }
 
     public ContentProviderRecord(ContentProviderRecord cpr) {
-        super(cpr.info);
+        service = cpr.service;
+        info = cpr.info;
         uid = cpr.uid;
         appInfo = cpr.appInfo;
         name = cpr.name;
         noReleaseNeeded = cpr.noReleaseNeeded;
-        service = cpr.service;
+    }
+
+    public ContentProviderHolder newHolder(ContentProviderConnection conn) {
+        ContentProviderHolder holder = new ContentProviderHolder(info);
+        holder.provider = provider;
+        holder.noReleaseNeeded = noReleaseNeeded;
+        holder.connection = conn;
+        return holder;
     }
 
     public boolean canRunHere(ProcessRecord app) {
@@ -120,30 +136,51 @@
         return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
     }
 
-    void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("package=");
-                pw.print(info.applicationInfo.packageName);
-                pw.print(" process="); pw.println(info.processName);
+    void dump(PrintWriter pw, String prefix, boolean full) {
+        if (full) {
+            pw.print(prefix); pw.print("package=");
+                    pw.print(info.applicationInfo.packageName);
+                    pw.print(" process="); pw.println(info.processName);
+        }
         pw.print(prefix); pw.print("proc="); pw.println(proc);
         if (launchingApp != null) {
             pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
         }
-        pw.print(prefix); pw.print("uid="); pw.print(uid);
-                pw.print(" provider="); pw.println(provider);
-        pw.print(prefix); pw.print("name="); pw.println(info.authority);
-        if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
-            pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
-                    pw.print("multiprocess="); pw.print(info.multiprocess);
-                    pw.print(" initOrder="); pw.println(info.initOrder);
+        if (full) {
+            pw.print(prefix); pw.print("uid="); pw.print(uid);
+                    pw.print(" provider="); pw.println(provider);
         }
-        if (hasExternalProcessHandles()) {
-            pw.print(prefix); pw.print("externals=");
-            pw.println(externalProcessTokenToHandle.size());
+        pw.print(prefix); pw.print("authority="); pw.println(info.authority);
+        if (full) {
+            if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
+                pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
+                        pw.print(" multiprocess="); pw.print(info.multiprocess);
+                        pw.print(" initOrder="); pw.println(info.initOrder);
+            }
         }
-        if (clients.size() > 0) {
-            pw.print(prefix); pw.println("Clients:");
-            for (ProcessRecord cproc : clients) {
-                pw.print(prefix); pw.print("  - "); pw.println(cproc.toShortString());
+        if (full) {
+            if (hasExternalProcessHandles()) {
+                pw.print(prefix); pw.print("externals=");
+                        pw.println(externalProcessTokenToHandle.size());
+            }
+        } else {
+            if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
+                pw.print(prefix); pw.print(connections.size());
+                        pw.print(" connections, "); pw.print(externalProcessNoHandleCount);
+                        pw.println(" external handles");
+            }
+        }
+        if (connections.size() > 0) {
+            if (full) {
+                pw.print(prefix); pw.println("Connections:");
+            }
+            for (int i=0; i<connections.size(); i++) {
+                ContentProviderConnection conn = connections.get(i);
+                pw.print(prefix); pw.print("  -> "); pw.println(conn.toClientString());
+                if (conn.provider != this) {
+                    pw.print(prefix); pw.print("    *** WRONG PROVIDER: ");
+                            pw.println(conn.provider);
+                }
             }
         }
     }
@@ -162,6 +199,17 @@
         return stringName = sb.toString();
     }
 
+    public String toShortString() {
+        if (shortStringName != null) {
+            return shortStringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append('/');
+        sb.append(name.flattenToShortString());
+        return shortStringName = sb.toString();
+    }
+
     // This class represents a handle from an external process to a provider.
     private class ExternalProcessHandle implements DeathRecipient {
         private static final String LOG_TAG = "ExternalProcessHanldle";