diff options
Diffstat (limited to 'services/java')
5 files changed, 496 insertions, 233 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 6464d7f..9d9b5b8 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -62,6 +62,7 @@ import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; +import android.content.IContentProvider; import android.content.IIntentReceiver; import android.content.IIntentSender; import android.content.Intent; @@ -1810,12 +1811,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - if (app.conProviders.size() > 0) { - for (ContentProviderRecord cpr : app.conProviders.keySet()) { - if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cpr.proc, oomAdj, - updateActivityTime, i+1); - } + for (int j=app.conProviders.size()-1; j>=0; j--) { + ContentProviderRecord cpr = app.conProviders.get(j).provider; + if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) { + updateLruProcessInternalLocked(cpr.proc, oomAdj, + updateActivityTime, i+1); } } @@ -3742,7 +3742,7 @@ public final class ActivityManagerService extends ActivityManagerNative N = providers.size(); for (i=0; i<N; i++) { - removeDyingProviderLocked(null, providers.get(i)); + removeDyingProviderLocked(null, providers.get(i), true); } if (doit) { @@ -6058,53 +6058,72 @@ public final class ActivityManagerService extends ActivityManagerNative return msg; } - boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr, - IBinder externalProcessToken) { + ContentProviderConnection incProviderCountLocked(ProcessRecord r, + final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) { if (r != null) { - Integer cnt = r.conProviders.get(cpr); - if (DEBUG_PROVIDER) Slog.v(TAG, - "Adding provider requested by " - + r.processName + " from process " - + cpr.info.processName + ": " + cpr.name.flattenToShortString() - + " cnt=" + (cnt == null ? 1 : cnt)); - if (cnt == null) { - cpr.clients.add(r); - r.conProviders.put(cpr, new Integer(1)); - return true; + for (int i=0; i<r.conProviders.size(); i++) { + ContentProviderConnection conn = r.conProviders.get(i); + if (conn.provider == cpr) { + if (DEBUG_PROVIDER) Slog.v(TAG, + "Adding provider requested by " + + r.processName + " from process " + + cpr.info.processName + ": " + cpr.name.flattenToShortString() + + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount); + if (stable) { + conn.stableCount++; + conn.numStableIncs++; + } else { + conn.unstableCount++; + conn.numUnstableIncs++; + } + return conn; + } + } + ContentProviderConnection conn = new ContentProviderConnection(cpr, r); + if (stable) { + conn.stableCount = 1; + conn.numStableIncs = 1; } else { - r.conProviders.put(cpr, new Integer(cnt.intValue()+1)); + conn.unstableCount = 1; + conn.numUnstableIncs = 1; } - } else { - cpr.addExternalProcessHandleLocked(externalProcessToken); + cpr.connections.add(conn); + r.conProviders.add(conn); + return conn; } - return false; + cpr.addExternalProcessHandleLocked(externalProcessToken); + return null; } - boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr, - IBinder externalProcessToken) { - if (r != null) { - Integer cnt = r.conProviders.get(cpr); + boolean decProviderCountLocked(ContentProviderConnection conn, + ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) { + if (conn != null) { + cpr = conn.provider; if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by " - + r.processName + " from process " + + conn.client.processName + " from process " + cpr.info.processName + ": " + cpr.name.flattenToShortString() - + " cnt=" + cnt); - if (cnt == null || cnt.intValue() <= 1) { - cpr.clients.remove(r); - r.conProviders.remove(cpr); - return true; + + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount); + if (stable) { + conn.stableCount--; } else { - r.conProviders.put(cpr, new Integer(cnt.intValue()-1)); + conn.unstableCount--; } - } else { - cpr.removeExternalProcessHandleLocked(externalProcessToken); + if (conn.stableCount == 0 && conn.unstableCount == 0) { + cpr.connections.remove(conn); + conn.client.conProviders.remove(conn); + return true; + } + return false; } + cpr.removeExternalProcessHandleLocked(externalProcessToken); return false; } private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller, - String name, IBinder token) { + String name, IBinder token, boolean stable) { ContentProviderRecord cpr; + ContentProviderConnection conn = null; ProviderInfo cpi = null; synchronized(this) { @@ -6135,20 +6154,19 @@ public final class ActivityManagerService extends ActivityManagerNative // of being published... but it is also allowed to run // in the caller's process, so don't make a connection // and just let the caller instantiate its own instance. - if (cpr.provider != null) { - // don't give caller the provider object, it needs - // to make its own. - cpr = new ContentProviderRecord(cpr); - } - return cpr; + ContentProviderHolder holder = cpr.newHolder(null); + // don't give caller the provider object, it needs + // to make its own. + holder.provider = null; + return holder; } final long origId = Binder.clearCallingIdentity(); // In this case the provider instance already exists, so we can // return it right away. - final boolean countChanged = incProviderCount(r, cpr, token); - if (countChanged) { + conn = incProviderCountLocked(r, cpr, token, stable); + if (conn != null && (conn.stableCount+conn.unstableCount) == 1) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { // If this is a perceptible app accessing the provider, // make sure to count it as being accessed and thus @@ -6181,7 +6199,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString() + " is crashing; detaching " + r); - boolean lastRef = decProviderCount(r, cpr, token); + boolean lastRef = decProviderCountLocked(conn, cpr, token, stable); appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread); if (!lastRef) { // This wasn't the last ref our process had on @@ -6189,6 +6207,7 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } providerRunning = false; + conn = null; } } @@ -6251,7 +6270,7 @@ public final class ActivityManagerService extends ActivityManagerNative // info and allow the caller to instantiate it. Only do // this if the provider is the same user as the caller's // process, or can run as root (so can be in any process). - return cpr; + return cpr.newHolder(null); } if (DEBUG_PROVIDER) { @@ -6312,7 +6331,10 @@ public final class ActivityManagerService extends ActivityManagerNative } mProviderMap.putProviderByName(name, cpr); - incProviderCount(r, cpr, token); + conn = incProviderCountLocked(r, cpr, token, stable); + if (conn != null) { + conn.waiting = true; + } } } @@ -6334,16 +6356,23 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp=" + cpr.launchingApp); } + if (conn != null) { + conn.waiting = true; + } cpr.wait(); } catch (InterruptedException ex) { + } finally { + if (conn != null) { + conn.waiting = false; + } } } } - return cpr; + return cpr != null ? cpr.newHolder(conn) : null; } public final ContentProviderHolder getContentProvider( - IApplicationThread caller, String name) { + IApplicationThread caller, String name, boolean stable) { enforceNotIsolatedCaller("getContentProvider"); if (caller == null) { String msg = "null IApplicationThread when getting content provider " @@ -6352,7 +6381,7 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } - return getContentProviderImpl(caller, name, null); + return getContentProviderImpl(caller, name, null, stable); } public ContentProviderHolder getContentProviderExternal(String name, IBinder token) { @@ -6362,45 +6391,30 @@ public final class ActivityManagerService extends ActivityManagerNative } private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) { - return getContentProviderImpl(null, name, token); + return getContentProviderImpl(null, name, token, true); } /** * Drop a content provider from a ProcessRecord's bookkeeping * @param cpr */ - public void removeContentProvider(IApplicationThread caller, String name) { + public void removeContentProvider(IBinder connection, boolean stable) { enforceNotIsolatedCaller("removeContentProvider"); synchronized (this) { - int userId = UserId.getUserId(Binder.getCallingUid()); - ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId); - if(cpr == null) { - // remove from mProvidersByClass - if (DEBUG_PROVIDER) Slog.v(TAG, name + - " provider not found in providers list"); - return; + ContentProviderConnection conn; + try { + conn = (ContentProviderConnection)connection; + } catch (ClassCastException e) { + String msg ="removeContentProvider: " + connection + + " not a ContentProviderConnection"; + Slog.w(TAG, msg); + throw new IllegalArgumentException(msg); } - final ProcessRecord r = getRecordForAppLocked(caller); - if (r == null) { - throw new SecurityException( - "Unable to find app for caller " + caller + - " when removing content provider " + name); + if (conn == null) { + throw new NullPointerException("connection is null"); } - //update content provider record entry info - ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name); - ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId); - if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by " - + r.info.processName + " from process " - + localCpr.appInfo.processName); - if (localCpr.launchingApp == r) { - //should not happen. taken care of as a local provider - Slog.w(TAG, "removeContentProvider called on local provider: " - + cpr.info.name + " in process " + r.processName); - return; - } else { - if (decProviderCount(r, localCpr, null)) { - updateOomAdjLocked(); - } + if (decProviderCountLocked(conn, null, null, stable)) { + updateOomAdjLocked(); } } } @@ -6447,7 +6461,7 @@ public final class ActivityManagerService extends ActivityManagerNative } enforceNotIsolatedCaller("publishContentProviders"); - synchronized(this) { + synchronized (this) { final ProcessRecord r = getRecordForAppLocked(caller); if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid); @@ -6499,6 +6513,103 @@ public final class ActivityManagerService extends ActivityManagerNative } } + public boolean refContentProvider(IBinder connection, int stable, int unstable) { + ContentProviderConnection conn; + try { + conn = (ContentProviderConnection)connection; + } catch (ClassCastException e) { + String msg ="refContentProvider: " + connection + + " not a ContentProviderConnection"; + Slog.w(TAG, msg); + throw new IllegalArgumentException(msg); + } + if (conn == null) { + throw new NullPointerException("connection is null"); + } + + synchronized (this) { + if (stable > 0) { + conn.numStableIncs += stable; + } + stable = conn.stableCount + stable; + if (stable < 0) { + throw new IllegalStateException("stableCount < 0: " + stable); + } + + if (unstable > 0) { + conn.numUnstableIncs += unstable; + } + unstable = conn.unstableCount + unstable; + if (unstable < 0) { + throw new IllegalStateException("unstableCount < 0: " + unstable); + } + + if ((stable+unstable) <= 0) { + throw new IllegalStateException("ref counts can't go to zero here: stable=" + + stable + " unstable=" + unstable); + } + conn.stableCount = stable; + conn.unstableCount = unstable; + return !conn.dead; + } + } + + public void unstableProviderDied(IBinder connection) { + ContentProviderConnection conn; + try { + conn = (ContentProviderConnection)connection; + } catch (ClassCastException e) { + String msg ="refContentProvider: " + connection + + " not a ContentProviderConnection"; + Slog.w(TAG, msg); + throw new IllegalArgumentException(msg); + } + if (conn == null) { + throw new NullPointerException("connection is null"); + } + + // Safely retrieve the content provider associated with the connection. + IContentProvider provider; + synchronized (this) { + provider = conn.provider.provider; + } + + if (provider == null) { + // Um, yeah, we're way ahead of you. + return; + } + + // Make sure the caller is being honest with us. + if (provider.asBinder().pingBinder()) { + // Er, no, still looks good to us. + synchronized (this) { + Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid() + + " says " + conn + " died, but we don't agree"); + return; + } + } + + // Well look at that! It's dead! + synchronized (this) { + if (conn.provider.provider != provider) { + // But something changed... good enough. + return; + } + + ProcessRecord proc = conn.provider.proc; + if (proc == null || proc.thread == null) { + // Seems like the process is already cleaned up. + return; + } + + // As far as we're concerned, this is just like receiving a + // death notification... just a bit prematurely. + Slog.i(TAG, "Process " + proc.processName + " (pid " + proc.pid + + ") early provider death"); + appDiedLocked(proc, proc.pid, proc.thread); + } + } + public static final void installSystemProviders() { List<ProviderInfo> providers; synchronized (mSelf) { @@ -10515,36 +10626,62 @@ public final class ActivityManagerService extends ActivityManagerNative app.executingServices.clear(); } - private final void removeDyingProviderLocked(ProcessRecord proc, - ContentProviderRecord cpr) { - synchronized (cpr) { - cpr.launchingApp = null; - cpr.notifyAll(); - } - - mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid)); - String names[] = cpr.info.authority.split(";"); - for (int j = 0; j < names.length; j++) { - mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid)); + private final boolean removeDyingProviderLocked(ProcessRecord proc, + ContentProviderRecord cpr, boolean always) { + final boolean inLaunching = mLaunchingProviders.contains(cpr); + + if (!inLaunching || always) { + synchronized (cpr) { + cpr.launchingApp = null; + cpr.notifyAll(); + } + mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid)); + String names[] = cpr.info.authority.split(";"); + for (int j = 0; j < names.length; j++) { + mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid)); + } } - - Iterator<ProcessRecord> cit = cpr.clients.iterator(); - while (cit.hasNext()) { - ProcessRecord capp = cit.next(); - if (!capp.persistent && capp.thread != null - && capp.pid != 0 - && capp.pid != MY_PID) { - Slog.i(TAG, "Kill " + capp.processName - + " (pid " + capp.pid + "): provider " + cpr.info.name - + " in dying process " + (proc != null ? proc.processName : "??")); - EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid, - capp.processName, capp.setAdj, "dying provider " - + cpr.name.toShortString()); - Process.killProcessQuiet(capp.pid); + + for (int i=0; i<cpr.connections.size(); i++) { + ContentProviderConnection conn = cpr.connections.get(i); + if (conn.waiting) { + // If this connection is waiting for the provider, then we don't + // need to mess with its process unless we are always removing + // or for some reason the provider is not currently launching. + if (inLaunching && !always) { + continue; + } + } + ProcessRecord capp = conn.client; + conn.dead = true; + if (conn.stableCount > 0) { + if (!capp.persistent && capp.thread != null + && capp.pid != 0 + && capp.pid != MY_PID) { + Slog.i(TAG, "Kill " + capp.processName + + " (pid " + capp.pid + "): provider " + cpr.info.name + + " in dying process " + (proc != null ? proc.processName : "??")); + EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid, + capp.processName, capp.setAdj, "dying provider " + + cpr.name.toShortString()); + Process.killProcessQuiet(capp.pid); + } + } else if (capp.thread != null && conn.provider.provider != null) { + try { + capp.thread.unstableProviderDied(conn.provider.provider.asBinder()); + } catch (RemoteException e) { + } + // In the protocol here, we don't expect the client to correctly + // clean up this connection, we'll just remove it. + cpr.connections.remove(i); + conn.client.conProviders.remove(conn); } } - - mLaunchingProviders.remove(cpr); + + if (inLaunching && always) { + mLaunchingProviders.remove(cpr); + } + return inLaunching; } /** @@ -10590,34 +10727,21 @@ public final class ActivityManagerService extends ActivityManagerNative boolean restart = false; - int NL = mLaunchingProviders.size(); - // Remove published content providers. if (!app.pubProviders.isEmpty()) { Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator(); while (it.hasNext()) { ContentProviderRecord cpr = it.next(); - cpr.provider = null; - cpr.proc = null; - // See if someone is waiting for this provider... in which - // case we don't remove it, but just let it restart. - int i = 0; - if (!app.bad && allowRestart) { - for (; i<NL; i++) { - if (mLaunchingProviders.get(i) == cpr) { - restart = true; - break; - } - } - } else { - i = NL; + final boolean always = app.bad || !allowRestart; + if (removeDyingProviderLocked(app, cpr, always) || always) { + // We left the provider in the launching list, need to + // restart it. + restart = true; } - if (i >= NL) { - removeDyingProviderLocked(app, cpr); - NL = mLaunchingProviders.size(); - } + cpr.provider = null; + cpr.proc = null; } app.pubProviders.clear(); } @@ -10629,10 +10753,9 @@ public final class ActivityManagerService extends ActivityManagerNative // Unregister from connected content providers. if (!app.conProviders.isEmpty()) { - Iterator it = app.conProviders.keySet().iterator(); - while (it.hasNext()) { - ContentProviderRecord cpr = (ContentProviderRecord)it.next(); - cpr.clients.remove(app); + for (int i=0; i<app.conProviders.size(); i++) { + ContentProviderConnection conn = app.conProviders.get(i); + conn.provider.connections.remove(conn); } app.conProviders.clear(); } @@ -10643,10 +10766,10 @@ public final class ActivityManagerService extends ActivityManagerNative // XXX Commented out for now. Trying to figure out a way to reproduce // the actual situation to identify what is actually going on. if (false) { - for (int i=0; i<NL; i++) { + for (int i=0; i<mLaunchingProviders.size(); i++) { ContentProviderRecord cpr = (ContentProviderRecord) mLaunchingProviders.get(i); - if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) { + if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) { synchronized (cpr) { cpr.launchingApp = null; cpr.notifyAll(); @@ -10743,7 +10866,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (!alwaysBad && !app.bad) { restart = true; } else { - removeDyingProviderLocked(app, cpr); + removeDyingProviderLocked(app, cpr, true); NL = mLaunchingProviders.size(); } } @@ -13948,48 +14071,49 @@ public final class ActivityManagerService extends ActivityManagerNative while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { ContentProviderRecord cpr = jt.next(); - if (cpr.clients.size() != 0) { - Iterator<ProcessRecord> kt = cpr.clients.iterator(); - while (kt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) { - ProcessRecord client = kt.next(); - if (client == app) { - // Being our own client is not interesting. - continue; + for (int i = cpr.connections.size()-1; + i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ + || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE); + i--) { + ContentProviderConnection conn = cpr.connections.get(i); + ProcessRecord client = conn.client; + if (client == app) { + // Being our own client is not interesting. + continue; + } + int myHiddenAdj = hiddenAdj; + if (myHiddenAdj > client.hiddenAdj) { + if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) { + myHiddenAdj = client.hiddenAdj; + } else { + myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ; } - int myHiddenAdj = hiddenAdj; - if (myHiddenAdj > client.hiddenAdj) { - if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) { - myHiddenAdj = client.hiddenAdj; - } else { - myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ; - } + } + int clientAdj = computeOomAdjLocked( + client, myHiddenAdj, TOP_APP, true, doingAll); + if (adj > clientAdj) { + if (app.hasShownUi && app != mHomeProcess + && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { + app.adjType = "bg-ui-provider"; + } else { + adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ + ? clientAdj : ProcessList.FOREGROUND_APP_ADJ; + app.adjType = "provider"; } - int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP, true, doingAll); - if (adj > clientAdj) { - if (app.hasShownUi && app != mHomeProcess - && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { - app.adjType = "bg-ui-provider"; - } else { - adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ - ? clientAdj : ProcessList.FOREGROUND_APP_ADJ; - app.adjType = "provider"; - } - if (!client.hidden) { - app.hidden = false; - } - if (client.keeping) { - app.keeping = true; - } - app.adjTypeCode = ActivityManager.RunningAppProcessInfo - .REASON_PROVIDER_IN_USE; - app.adjSource = client; - app.adjSourceOom = clientAdj; - app.adjTarget = cpr.name; + if (!client.hidden) { + app.hidden = false; } - if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { - schedGroup = Process.THREAD_GROUP_DEFAULT; + if (client.keeping) { + app.keeping = true; } + app.adjTypeCode = ActivityManager.RunningAppProcessInfo + .REASON_PROVIDER_IN_USE; + app.adjSource = client; + app.adjSourceOom = clientAdj; + app.adjTarget = cpr.name; + } + if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { + schedGroup = Process.THREAD_GROUP_DEFAULT; } } // If the provider has external (non-framework) process diff --git a/services/java/com/android/server/am/ContentProviderConnection.java b/services/java/com/android/server/am/ContentProviderConnection.java new file mode 100644 index 0000000..84f8f02 --- /dev/null +++ b/services/java/com/android/server/am/ContentProviderConnection.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.os.Binder; + +/** + * Represents a link between a content provider and client. + */ +public class ContentProviderConnection extends Binder { + public final ContentProviderRecord provider; + public final ProcessRecord client; + public int stableCount; + public int unstableCount; + // The client of this connection is currently waiting for the provider to appear. + // Protected by the provider lock. + public boolean waiting; + // The provider of this connection is now dead. + public boolean dead; + + // For debugging. + public int numStableIncs; + public int numUnstableIncs; + + public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) { + provider = _provider; + client = _client; + } + + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("ContentProviderConnection{"); + toShortString(sb); + sb.append('}'); + return sb.toString(); + } + + public String toShortString() { + StringBuilder sb = new StringBuilder(128); + toShortString(sb); + return sb.toString(); + } + + public String toClientString() { + StringBuilder sb = new StringBuilder(128); + toClientString(sb); + return sb.toString(); + } + + public void toShortString(StringBuilder sb) { + sb.append(provider.toShortString()); + sb.append("->"); + toClientString(sb); + } + + public void toClientString(StringBuilder sb) { + sb.append(client.toShortString()); + sb.append(" s"); + sb.append(stableCount); + sb.append("/"); + sb.append(numStableIncs); + sb.append(" u"); + sb.append(unstableCount); + sb.append("/"); + sb.append(numUnstableIncs); + if (waiting) { + sb.append(" WAITING"); + } + if (dead) { + sb.append(" DEAD"); + } + } +} 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 @@ package com.android.server.am; 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.os.RemoteException; 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 @@ class ContentProviderRecord extends ContentProviderHolder { } 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 @@ class ContentProviderRecord extends ContentProviderHolder { 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 @@ class ContentProviderRecord extends ContentProviderHolder { 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"; diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 4529ecc..8a3ba7f 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -125,8 +125,8 @@ class ProcessRecord { final HashMap<String, ContentProviderRecord> pubProviders = new HashMap<String, ContentProviderRecord>(); // All ContentProviderRecord process is using - final HashMap<ContentProviderRecord, Integer> conProviders - = new HashMap<ContentProviderRecord, Integer>(); + final ArrayList<ContentProviderConnection> conProviders + = new ArrayList<ContentProviderConnection>(); boolean persistent; // always keep this application running? boolean crashing; // are we in the process of crashing? @@ -257,25 +257,47 @@ class ProcessRecord { pw.println(); } if (activities.size() > 0) { - pw.print(prefix); pw.print("activities="); pw.println(activities); + pw.print(prefix); pw.println("Activities:"); + for (int i=0; i<activities.size(); i++) { + pw.print(prefix); pw.print(" - "); pw.println(activities.get(i)); + } } if (services.size() > 0) { - pw.print(prefix); pw.print("services="); pw.println(services); + pw.print(prefix); pw.println("Services:"); + for (ServiceRecord sr : services) { + pw.print(prefix); pw.print(" - "); pw.println(sr); + } } if (executingServices.size() > 0) { - pw.print(prefix); pw.print("executingServices="); pw.println(executingServices); + pw.print(prefix); pw.println("Executing Services:"); + for (ServiceRecord sr : executingServices) { + pw.print(prefix); pw.print(" - "); pw.println(sr); + } } if (connections.size() > 0) { - pw.print(prefix); pw.print("connections="); pw.println(connections); + pw.print(prefix); pw.println("Connections:"); + for (ConnectionRecord cr : connections) { + pw.print(prefix); pw.print(" - "); pw.println(cr); + } } if (pubProviders.size() > 0) { - pw.print(prefix); pw.print("pubProviders="); pw.println(pubProviders); + pw.print(prefix); pw.println("Published Providers:"); + for (HashMap.Entry<String, ContentProviderRecord> ent : pubProviders.entrySet()) { + pw.print(prefix); pw.print(" - "); pw.println(ent.getKey()); + pw.print(prefix); pw.print(" -> "); pw.println(ent.getValue()); + } } if (conProviders.size() > 0) { - pw.print(prefix); pw.print("conProviders="); pw.println(conProviders); + pw.print(prefix); pw.println("Connected Providers:"); + for (int i=0; i<conProviders.size(); i++) { + pw.print(prefix); pw.print(" - "); pw.println(conProviders.get(i).toShortString()); + } } if (receivers.size() > 0) { - pw.print(prefix); pw.print("receivers="); pw.println(receivers); + pw.print(prefix); pw.println("Receivers:"); + for (ReceiverList rl : receivers) { + pw.print(prefix); pw.print(" - "); pw.println(rl); + } } } diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java index ccc928f..d148ec3 100644 --- a/services/java/com/android/server/am/ProviderMap.java +++ b/services/java/com/android/server/am/ProviderMap.java @@ -177,27 +177,9 @@ public class ProviderMap { while (it.hasNext()) { Map.Entry<ComponentName, ContentProviderRecord> e = it.next(); ContentProviderRecord r = e.getValue(); - if (dumpAll) { - pw.print(" * "); - pw.println(r); - r.dump(pw, " "); - } else { - pw.print(" * "); - pw.println(r); - if (r.proc != null) { - pw.print(" proc="); - pw.println(r.proc); - } - if (r.launchingApp != null) { - pw.print(" launchingApp="); - pw.println(r.launchingApp); - } - if (r.clients.size() > 0 || r.externalProcessNoHandleCount > 0) { - pw.print(" "); pw.print(r.clients.size()); - pw.print(" clients, "); pw.print(r.externalProcessNoHandleCount); - pw.println(" external handles"); - } - } + pw.print(" * "); + pw.println(r); + r.dump(pw, " ", dumpAll); } } @@ -210,7 +192,7 @@ public class ProviderMap { pw.print(" "); pw.print(e.getKey()); pw.print(": "); - pw.println(r); + pw.println(r.toShortString()); } } @@ -221,10 +203,10 @@ public class ProviderMap { pw.println(" "); pw.println(" Published content providers (by class):"); dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass); - pw.println(""); } if (mProvidersByClassPerUser.size() > 1) { + pw.println(""); for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i); pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":"); @@ -321,7 +303,7 @@ public class ProviderMap { if (r.proc != null) pw.println(r.proc.pid); else pw.println("(not running)"); if (dumpAll) { - r.dump(pw, innerPrefix); + r.dump(pw, innerPrefix, true); } } if (r.proc != null && r.proc.thread != null) { |