Merge "Fix (mostly) issue #5109947: Race condition between retrieving a..." into jb-dev

This commit is contained in:
Dianne Hackborn
2012-05-29 15:51:24 -07:00
committed by Android (Google) Code Review
16 changed files with 1188 additions and 541 deletions

View File

@ -580,7 +580,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
IBinder b = data.readStrongBinder(); IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b); IApplicationThread app = ApplicationThreadNative.asInterface(b);
String name = data.readString(); String name = data.readString();
ContentProviderHolder cph = getContentProvider(app, name); boolean stable = data.readInt() != 0;
ContentProviderHolder cph = getContentProvider(app, name, stable);
reply.writeNoException(); reply.writeNoException();
if (cph != null) { if (cph != null) {
reply.writeInt(1); reply.writeInt(1);
@ -617,12 +618,30 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true; return true;
} }
case REF_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
int stable = data.readInt();
int unstable = data.readInt();
boolean res = refContentProvider(b, stable, unstable);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
}
case UNSTABLE_PROVIDER_DIED_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
unstableProviderDied(b);
reply.writeNoException();
return true;
}
case REMOVE_CONTENT_PROVIDER_TRANSACTION: { case REMOVE_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor); data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder(); IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b); boolean stable = data.readInt() != 0;
String name = data.readString(); removeContentProvider(b, stable);
removeContentProvider(app, name);
reply.writeNoException(); reply.writeNoException();
return true; return true;
} }
@ -2314,13 +2333,13 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle(); reply.recycle();
} }
public ContentProviderHolder getContentProvider(IApplicationThread caller, public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name) throws RemoteException String name, boolean stable) throws RemoteException {
{
Parcel data = Parcel.obtain(); Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain(); Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor); data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null); data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(name); data.writeString(name);
data.writeInt(stable ? 1 : 0);
mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0); mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
reply.readException(); reply.readException();
int res = reply.readInt(); int res = reply.readInt();
@ -2364,14 +2383,38 @@ class ActivityManagerProxy implements IActivityManager
data.recycle(); data.recycle();
reply.recycle(); reply.recycle();
} }
public boolean refContentProvider(IBinder connection, int stable, int unstable)
public void removeContentProvider(IApplicationThread caller, throws RemoteException {
String name) throws RemoteException {
Parcel data = Parcel.obtain(); Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain(); Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor); data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null); data.writeStrongBinder(connection);
data.writeString(name); data.writeInt(stable);
data.writeInt(unstable);
mRemote.transact(REF_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
data.recycle();
reply.recycle();
return res;
}
public void unstableProviderDied(IBinder connection) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(connection);
mRemote.transact(UNSTABLE_PROVIDER_DIED_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(connection);
data.writeInt(stable ? 1 : 0);
mRemote.transact(REMOVE_CONTENT_PROVIDER_TRANSACTION, data, reply, 0); mRemote.transact(REMOVE_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
reply.readException(); reply.readException();
data.recycle(); data.recycle();

View File

@ -140,6 +140,7 @@ public final class ActivityThread {
private static final boolean DEBUG_CONFIGURATION = false; private static final boolean DEBUG_CONFIGURATION = false;
private static final boolean DEBUG_SERVICE = false; private static final boolean DEBUG_SERVICE = false;
private static final boolean DEBUG_MEMORY_TRIM = false; private static final boolean DEBUG_MEMORY_TRIM = false;
private static final boolean DEBUG_PROVIDER = false;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000; private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";"); private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@ -210,6 +211,8 @@ public final class ActivityThread {
= new HashMap<IBinder, ProviderRefCount>(); = new HashMap<IBinder, ProviderRefCount>();
final HashMap<IBinder, ProviderClientRecord> mLocalProviders final HashMap<IBinder, ProviderClientRecord> mLocalProviders
= new HashMap<IBinder, ProviderClientRecord>(); = new HashMap<IBinder, ProviderClientRecord>();
final HashMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
= new HashMap<ComponentName, ProviderClientRecord>();
final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new HashMap<Activity, ArrayList<OnActivityPausedListener>>(); = new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
@ -284,20 +287,19 @@ public final class ActivityThread {
} }
} }
final class ProviderClientRecord implements IBinder.DeathRecipient { final class ProviderClientRecord {
final String mName; final String[] mNames;
final IContentProvider mProvider; final IContentProvider mProvider;
final ContentProvider mLocalProvider; final ContentProvider mLocalProvider;
final IActivityManager.ContentProviderHolder mHolder;
ProviderClientRecord(String name, IContentProvider provider, ProviderClientRecord(String[] names, IContentProvider provider,
ContentProvider localProvider) { ContentProvider localProvider,
mName = name; IActivityManager.ContentProviderHolder holder) {
mNames = names;
mProvider = provider; mProvider = provider;
mLocalProvider = localProvider; mLocalProvider = localProvider;
} mHolder = holder;
public void binderDied() {
removeDeadProvider(mName, mProvider);
} }
} }
@ -1061,6 +1063,11 @@ public final class ActivityThread {
pw.flush(); pw.flush();
} }
@Override
public void unstableProviderDied(IBinder provider) {
queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
}
private void printRow(PrintWriter pw, String format, Object...objs) { private void printRow(PrintWriter pw, String format, Object...objs) {
pw.println(String.format(format, objs)); pw.println(String.format(format, objs));
} }
@ -1125,6 +1132,7 @@ public final class ActivityThread {
public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139; public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
public static final int TRIM_MEMORY = 140; public static final int TRIM_MEMORY = 140;
public static final int DUMP_PROVIDER = 141; public static final int DUMP_PROVIDER = 141;
public static final int UNSTABLE_PROVIDER_DIED = 142;
String codeToString(int code) { String codeToString(int code) {
if (DEBUG_MESSAGES) { if (DEBUG_MESSAGES) {
switch (code) { switch (code) {
@ -1170,6 +1178,7 @@ public final class ActivityThread {
case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
case TRIM_MEMORY: return "TRIM_MEMORY"; case TRIM_MEMORY: return "TRIM_MEMORY";
case DUMP_PROVIDER: return "DUMP_PROVIDER"; case DUMP_PROVIDER: return "DUMP_PROVIDER";
case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
} }
} }
return Integer.toString(code); return Integer.toString(code);
@ -1337,7 +1346,7 @@ public final class ActivityThread {
break; break;
case REMOVE_PROVIDER: case REMOVE_PROVIDER:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove"); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove");
completeRemoveProvider((IContentProvider)msg.obj); completeRemoveProvider((ProviderRefCount)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break; break;
case ENABLE_JIT: case ENABLE_JIT:
@ -1377,6 +1386,9 @@ public final class ActivityThread {
handleTrimMemory(msg.arg1); handleTrimMemory(msg.arg1);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break; break;
case UNSTABLE_PROVIDER_DIED:
handleUnstableProviderDied((IBinder)msg.obj, false);
break;
} }
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
} }
@ -2867,10 +2879,24 @@ public final class ActivityThread {
} }
private static final class ProviderRefCount { private static final class ProviderRefCount {
public int count; public final IActivityManager.ContentProviderHolder holder;
public final ProviderClientRecord client;
public int stableCount;
public int unstableCount;
ProviderRefCount(int pCount) { // When this is set, the stable and unstable ref counts are 0 and
count = pCount; // we have a pending operation scheduled to remove the ref count
// from the activity manager. On the activity manager we are still
// holding an unstable ref, though it is not reflected in the counts
// here.
public boolean removePending;
ProviderRefCount(IActivityManager.ContentProviderHolder inHolder,
ProviderClientRecord inClient, int sCount, int uCount) {
holder = inHolder;
client = inClient;
stableCount = sCount;
unstableCount = uCount;
} }
} }
@ -4080,15 +4106,6 @@ public final class ActivityThread {
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
} }
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
} else { } else {
mInstrumentation = new Instrumentation(); mInstrumentation = new Instrumentation();
} }
@ -4119,6 +4136,17 @@ public final class ActivityThread {
} }
} }
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try { try {
mInstrumentation.callApplicationOnCreate(app); mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) { } catch (Exception e) {
@ -4159,12 +4187,9 @@ public final class ActivityThread {
buf.append(": "); buf.append(": ");
buf.append(cpi.name); buf.append(cpi.name);
Log.i(TAG, buf.toString()); Log.i(TAG, buf.toString());
IContentProvider cp = installProvider(context, null, cpi, IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/); false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cp != null) { if (cph != null) {
IActivityManager.ContentProviderHolder cph =
new IActivityManager.ContentProviderHolder(cpi);
cph.provider = cp;
cph.noReleaseNeeded = true; cph.noReleaseNeeded = true;
results.add(cph); results.add(cph);
} }
@ -4177,8 +4202,8 @@ public final class ActivityThread {
} }
} }
public final IContentProvider acquireProvider(Context c, String name) { public final IContentProvider acquireProvider(Context c, String name, boolean stable) {
IContentProvider provider = acquireExistingProvider(c, name); IContentProvider provider = acquireExistingProvider(c, name, stable);
if (provider != null) { if (provider != null) {
return provider; return provider;
} }
@ -4192,7 +4217,7 @@ public final class ActivityThread {
IActivityManager.ContentProviderHolder holder = null; IActivityManager.ContentProviderHolder holder = null;
try { try {
holder = ActivityManagerNative.getDefault().getContentProvider( holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), name); getApplicationThread(), name, stable);
} catch (RemoteException ex) { } catch (RemoteException ex) {
} }
if (holder == null) { if (holder == null) {
@ -4202,23 +4227,79 @@ public final class ActivityThread {
// Install provider will increment the reference count for us, and break // Install provider will increment the reference count for us, and break
// any ties in the race. // any ties in the race.
provider = installProvider(c, holder.provider, holder.info, holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded); true /*noisy*/, holder.noReleaseNeeded, stable);
if (holder.provider != null && provider != holder.provider) { return holder.provider;
if (localLOGV) {
Slog.v(TAG, "acquireProvider: lost the race, releasing extraneous "
+ "reference to the content provider");
}
try {
ActivityManagerNative.getDefault().removeContentProvider(
getApplicationThread(), name);
} catch (RemoteException ex) {
}
}
return provider;
} }
public final IContentProvider acquireExistingProvider(Context c, String name) { private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
if (stable) {
prc.stableCount += 1;
if (prc.stableCount == 1) {
// We are acquiring a new stable reference on the provider.
int unstableDelta;
if (prc.removePending) {
// We have a pending remove operation, which is holding the
// last unstable reference. At this point we are converting
// that unstable reference to our new stable reference.
unstableDelta = -1;
// Cancel the removal of the provider.
if (DEBUG_PROVIDER) {
Slog.v(TAG, "incProviderRef: stable "
+ "snatched provider from the jaws of death");
}
prc.removePending = false;
mH.removeMessages(H.REMOVE_PROVIDER, prc);
} else {
unstableDelta = 0;
}
try {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "incProviderRef Now stable - "
+ prc.holder.info.name + ": unstableDelta="
+ unstableDelta);
}
ActivityManagerNative.getDefault().refContentProvider(
prc.holder.connection, 1, unstableDelta);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
prc.unstableCount += 1;
if (prc.unstableCount == 1) {
// We are acquiring a new unstable reference on the provider.
if (prc.removePending) {
// Oh look, we actually have a remove pending for the
// provider, which is still holding the last unstable
// reference. We just need to cancel that to take new
// ownership of the reference.
if (DEBUG_PROVIDER) {
Slog.v(TAG, "incProviderRef: unstable "
+ "snatched provider from the jaws of death");
}
prc.removePending = false;
mH.removeMessages(H.REMOVE_PROVIDER, prc);
} else {
// First unstable ref, increment our count in the
// activity manager.
try {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "incProviderRef: Now unstable - "
+ prc.holder.info.name);
}
ActivityManagerNative.getDefault().refContentProvider(
prc.holder.connection, 0, 1);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
}
}
public final IContentProvider acquireExistingProvider(Context c, String name,
boolean stable) {
synchronized (mProviderMap) { synchronized (mProviderMap) {
ProviderClientRecord pr = mProviderMap.get(name); ProviderClientRecord pr = mProviderMap.get(name);
if (pr == null) { if (pr == null) {
@ -4232,23 +4313,14 @@ public final class ActivityThread {
// provider is not reference counted and never needs to be released. // provider is not reference counted and never needs to be released.
ProviderRefCount prc = mProviderRefCountMap.get(jBinder); ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) { if (prc != null) {
prc.count += 1; incProviderRefLocked(prc, stable);
if (prc.count == 1) {
if (localLOGV) {
Slog.v(TAG, "acquireExistingProvider: "
+ "snatched provider from the jaws of death");
}
// Because the provider previously had a reference count of zero,
// it was scheduled to be removed. Cancel that.
mH.removeMessages(H.REMOVE_PROVIDER, provider);
}
} }
return provider; return provider;
} }
} }
public final boolean releaseProvider(IContentProvider provider) { public final boolean releaseProvider(IContentProvider provider, boolean stable) {
if(provider == null) { if (provider == null) {
return false; return false;
} }
@ -4260,55 +4332,98 @@ public final class ActivityThread {
return false; return false;
} }
if (prc.count == 0) { boolean lastRef = false;
if (localLOGV) Slog.v(TAG, "releaseProvider: ref count already 0, how?"); if (stable) {
if (prc.stableCount == 0) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"releaseProvider: stable ref count already 0, how?");
return false; return false;
} }
prc.stableCount -= 1;
if (prc.stableCount == 0) {
// What we do at this point depends on whether there are
// any unstable refs left: if there are, we just tell the
// activity manager to decrement its stable count; if there
// aren't, we need to enqueue this provider to be removed,
// and convert to holding a single unstable ref while
// doing so.
lastRef = prc.unstableCount == 0;
try {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "releaseProvider: No longer stable w/lastRef="
+ lastRef + " - " + prc.holder.info.name);
}
ActivityManagerNative.getDefault().refContentProvider(
prc.holder.connection, -1, lastRef ? 1 : 0);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
if (prc.unstableCount == 0) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"releaseProvider: unstable ref count already 0, how?");
return false;
}
prc.unstableCount -= 1;
if (prc.unstableCount == 0) {
// If this is the last reference, we need to enqueue
// this provider to be removed instead of telling the
// activity manager to remove it at this point.
lastRef = prc.stableCount == 0;
if (!lastRef) {
try {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "releaseProvider: No longer unstable - "
+ prc.holder.info.name);
}
ActivityManagerNative.getDefault().refContentProvider(
prc.holder.connection, 0, -1);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
}
prc.count -= 1; if (lastRef) {
if (prc.count == 0) { if (!prc.removePending) {
// Schedule the actual remove asynchronously, since we don't know the context // Schedule the actual remove asynchronously, since we don't know the context
// this will be called in. // this will be called in.
// TODO: it would be nice to post a delayed message, so // TODO: it would be nice to post a delayed message, so
// if we come back and need the same provider quickly // if we come back and need the same provider quickly
// we will still have it available. // we will still have it available.
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider); if (DEBUG_PROVIDER) {
Slog.v(TAG, "releaseProvider: Enqueueing pending removal - "
+ prc.holder.info.name);
}
prc.removePending = true;
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc);
mH.sendMessage(msg); mH.sendMessage(msg);
} else {
Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
}
} }
return true; return true;
} }
} }
public final IContentProvider acquireUnstableProvider(Context c, String name) { final void completeRemoveProvider(ProviderRefCount prc) {
return acquireProvider(c, name); synchronized (mProviderMap) {
} if (!prc.removePending) {
public final boolean releaseUnstableProvider(IContentProvider provider) {
return releaseProvider(provider);
}
final void completeRemoveProvider(IContentProvider provider) {
IBinder jBinder = provider.asBinder();
String remoteProviderName = null;
synchronized(mProviderMap) {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc == null) {
// Either no release is needed (so we shouldn't be here) or the
// provider was already released.
if (localLOGV) Slog.v(TAG, "completeRemoveProvider: release not needed");
return;
}
if (prc.count != 0) {
// There was a race! Some other client managed to acquire // There was a race! Some other client managed to acquire
// the provider before the removal was completed. // the provider before the removal was completed.
// Abort the removal. We will do it later. // Abort the removal. We will do it later.
if (localLOGV) Slog.v(TAG, "completeRemoveProvider: lost the race, " if (DEBUG_PROVIDER) Slog.v(TAG, "completeRemoveProvider: lost the race, "
+ "provider still in use"); + "provider still in use");
return; return;
} }
final IBinder jBinder = prc.holder.provider.asBinder();
ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
if (existingPrc == prc) {
mProviderRefCountMap.remove(jBinder); mProviderRefCountMap.remove(jBinder);
}
Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator(); Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
@ -4316,41 +4431,70 @@ public final class ActivityThread {
IBinder myBinder = pr.mProvider.asBinder(); IBinder myBinder = pr.mProvider.asBinder();
if (myBinder == jBinder) { if (myBinder == jBinder) {
iter.remove(); iter.remove();
if (pr.mLocalProvider == null) {
myBinder.unlinkToDeath(pr, 0);
if (remoteProviderName == null) {
remoteProviderName = pr.mName;
}
}
} }
} }
} }
if (remoteProviderName != null) {
try { try {
if (localLOGV) { if (DEBUG_PROVIDER) {
Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative." Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
+ "removeContentProvider(" + remoteProviderName + ")"); + "removeContentProvider(" + prc.holder.info.name + ")");
} }
ActivityManagerNative.getDefault().removeContentProvider( ActivityManagerNative.getDefault().removeContentProvider(
getApplicationThread(), remoteProviderName); prc.holder.connection, false);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
final void handleUnstableProviderDied(IBinder provider, boolean fromClient) {
synchronized(mProviderMap) {
ProviderRefCount prc = mProviderRefCountMap.get(provider);
if (prc != null) {
if (DEBUG_PROVIDER) Slog.v(TAG, "Cleaning up dead provider "
+ provider + " " + prc.holder.info.name);
mProviderRefCountMap.remove(provider);
if (prc.client != null && prc.client.mNames != null) {
for (String name : prc.client.mNames) {
ProviderClientRecord pr = mProviderMap.get(name);
if (pr != null && pr.mProvider.asBinder() == provider) {
Slog.i(TAG, "Removing dead content provider: " + name);
mProviderMap.remove(name);
}
}
}
if (fromClient) {
// We found out about this due to execution in our client
// code. Tell the activity manager about it now, to ensure
// that the next time we go to do anything with the provider
// it knows it is dead (so we don't race with its death
// notification).
try {
ActivityManagerNative.getDefault().unstableProviderDied(
prc.holder.connection);
} catch (RemoteException e) { } catch (RemoteException e) {
//do nothing content provider object is dead any way //do nothing content provider object is dead any way
} }
} }
} }
}
}
final void removeDeadProvider(String name, IContentProvider provider) { private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
synchronized(mProviderMap) { ContentProvider localProvider,IActivityManager.ContentProviderHolder holder) {
ProviderClientRecord pr = mProviderMap.get(name); String names[] = PATTERN_SEMICOLON.split(holder.info.authority);
if (pr != null && pr.mProvider.asBinder() == provider.asBinder()) { ProviderClientRecord pcr = new ProviderClientRecord(names, provider,
Slog.i(TAG, "Removing dead content provider: " + name); localProvider, holder);
ProviderClientRecord removed = mProviderMap.remove(name); for (int i = 0; i < names.length; i++) {
if (removed != null) { ProviderClientRecord existing = mProviderMap.get(names[i]);
removed.mProvider.asBinder().unlinkToDeath(removed, 0); if (existing != null) {
} Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + names[i]);
} else {
mProviderMap.put(names[i], pcr);
} }
} }
return pcr;
} }
/** /**
@ -4367,12 +4511,13 @@ public final class ActivityThread {
* and returns the existing provider. This can happen due to concurrent * and returns the existing provider. This can happen due to concurrent
* attempts to acquire the same provider. * attempts to acquire the same provider.
*/ */
private IContentProvider installProvider(Context context, private IActivityManager.ContentProviderHolder installProvider(Context context,
IContentProvider provider, ProviderInfo info, IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded) { boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null; ContentProvider localProvider = null;
if (provider == null) { IContentProvider provider;
if (noisy) { if (holder == null) {
if (DEBUG_PROVIDER || noisy) {
Slog.d(TAG, "Loading provider " + info.authority + ": " Slog.d(TAG, "Loading provider " + info.authority + ": "
+ info.name); + info.name);
} }
@ -4409,7 +4554,7 @@ public final class ActivityThread {
info.applicationInfo.sourceDir); info.applicationInfo.sourceDir);
return null; return null;
} }
if (false) Slog.v( if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name); TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider. // XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info); localProvider.attachInfo(c, info);
@ -4421,76 +4566,72 @@ public final class ActivityThread {
} }
return null; return null;
} }
} else if (localLOGV) { } else {
Slog.v(TAG, "Installing external provider " + info.authority + ": " provider = holder.provider;
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
+ info.name); + info.name);
} }
synchronized (mProviderMap) { IActivityManager.ContentProviderHolder retHolder;
// There is a possibility that this thread raced with another thread to
// add the provider. If we find another thread got there first then we
// just get out of the way and return the original provider.
IBinder jBinder = provider.asBinder();
String names[] = PATTERN_SEMICOLON.split(info.authority);
for (int i = 0; i < names.length; i++) {
ProviderClientRecord pr = mProviderMap.get(names[i]);
if (pr != null) {
if (localLOGV) {
Slog.v(TAG, "installProvider: lost the race, "
+ "using existing named provider");
}
provider = pr.mProvider;
} else {
pr = new ProviderClientRecord(names[i], provider, localProvider);
if (localProvider == null) {
try {
jBinder.linkToDeath(pr, 0);
} catch (RemoteException e) {
// Provider already dead. Bail out of here without making
// any changes to the provider map or other data structures.
return null;
}
}
mProviderMap.put(names[i], pr);
}
}
synchronized (mProviderMap) {
if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
+ " / " + info.name);
IBinder jBinder = provider.asBinder();
if (localProvider != null) { if (localProvider != null) {
ProviderClientRecord pr = mLocalProviders.get(jBinder); ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) { if (pr != null) {
if (localLOGV) { if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, " Slog.v(TAG, "installProvider: lost the race, "
+ "using existing local provider"); + "using existing local provider");
} }
provider = pr.mProvider; provider = pr.mProvider;
} else { } else {
pr = new ProviderClientRecord(null, provider, localProvider); holder = new IActivityManager.ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr); mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
// We need to transfer our new reference to the existing
// ref count, releasing the old one... but only if
// release is needed (that is, it is not running in the
// system process).
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
try {
ActivityManagerNative.getDefault().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) {
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
} }
} }
if (!noReleaseNeeded) { return retHolder;
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (localLOGV) {
Slog.v(TAG, "installProvider: lost the race, incrementing ref count");
}
prc.count += 1;
if (prc.count == 1) {
if (localLOGV) {
Slog.v(TAG, "installProvider: "
+ "snatched provider from the jaws of death");
}
// Because the provider previously had a reference count of zero,
// it was scheduled to be removed. Cancel that.
mH.removeMessages(H.REMOVE_PROVIDER, provider);
}
} else {
mProviderRefCountMap.put(jBinder, new ProviderRefCount(1));
}
}
}
return provider;
} }
private void attach(boolean system) { private void attach(boolean system) {

View File

@ -575,6 +575,15 @@ public abstract class ApplicationThreadNative extends Binder
reply.writeNoException(); reply.writeNoException();
return true; return true;
} }
case UNSTABLE_PROVIDER_DIED_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
IBinder provider = data.readStrongBinder();
unstableProviderDied(provider);
reply.writeNoException();
return true;
}
} }
return super.onTransact(code, data, reply, flags); return super.onTransact(code, data, reply, flags);
@ -1163,4 +1172,12 @@ class ApplicationThreadProxy implements IApplicationThread {
mRemote.transact(DUMP_DB_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); mRemote.transact(DUMP_DB_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle(); data.recycle();
} }
public void unstableProviderDied(IBinder provider) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(provider);
mRemote.transact(UNSTABLE_PROVIDER_DIED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
} }

View File

@ -1679,27 +1679,32 @@ class ContextImpl extends Context {
@Override @Override
protected IContentProvider acquireProvider(Context context, String name) { protected IContentProvider acquireProvider(Context context, String name) {
return mMainThread.acquireProvider(context, name); return mMainThread.acquireProvider(context, name, true);
} }
@Override @Override
protected IContentProvider acquireExistingProvider(Context context, String name) { protected IContentProvider acquireExistingProvider(Context context, String name) {
return mMainThread.acquireExistingProvider(context, name); return mMainThread.acquireExistingProvider(context, name, true);
} }
@Override @Override
public boolean releaseProvider(IContentProvider provider) { public boolean releaseProvider(IContentProvider provider) {
return mMainThread.releaseProvider(provider); return mMainThread.releaseProvider(provider, true);
} }
@Override @Override
protected IContentProvider acquireUnstableProvider(Context c, String name) { protected IContentProvider acquireUnstableProvider(Context c, String name) {
return mMainThread.acquireUnstableProvider(c, name); return mMainThread.acquireProvider(c, name, false);
} }
@Override @Override
public boolean releaseUnstableProvider(IContentProvider icp) { public boolean releaseUnstableProvider(IContentProvider icp) {
return mMainThread.releaseUnstableProvider(icp); return mMainThread.releaseProvider(icp, false);
}
@Override
public void unstableProviderDied(IContentProvider icp) {
mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
} }
private final ActivityThread mMainThread; private final ActivityThread mMainThread;

View File

@ -116,14 +116,16 @@ public interface IActivityManager extends IInterface {
public void reportThumbnail(IBinder token, public void reportThumbnail(IBinder token,
Bitmap thumbnail, CharSequence description) throws RemoteException; Bitmap thumbnail, CharSequence description) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller, public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name) throws RemoteException; String name, boolean stable) throws RemoteException;
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
throws RemoteException; throws RemoteException;
public void removeContentProvider(IApplicationThread caller, public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException;
String name) throws RemoteException;
public void removeContentProviderExternal(String name, IBinder token) throws RemoteException; public void removeContentProviderExternal(String name, IBinder token) throws RemoteException;
public void publishContentProviders(IApplicationThread caller, public void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) throws RemoteException; List<ContentProviderHolder> providers) throws RemoteException;
public boolean refContentProvider(IBinder connection, int stableDelta, int unstableDelta)
throws RemoteException;
public void unstableProviderDied(IBinder connection) throws RemoteException;
public PendingIntent getRunningServiceControlPanel(ComponentName service) public PendingIntent getRunningServiceControlPanel(ComponentName service)
throws RemoteException; throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service, public ComponentName startService(IApplicationThread caller, Intent service,
@ -363,6 +365,7 @@ public interface IActivityManager extends IInterface {
public static class ContentProviderHolder implements Parcelable { public static class ContentProviderHolder implements Parcelable {
public final ProviderInfo info; public final ProviderInfo info;
public IContentProvider provider; public IContentProvider provider;
public IBinder connection;
public boolean noReleaseNeeded; public boolean noReleaseNeeded;
public ContentProviderHolder(ProviderInfo _info) { public ContentProviderHolder(ProviderInfo _info) {
@ -380,6 +383,7 @@ public interface IActivityManager extends IInterface {
} else { } else {
dest.writeStrongBinder(null); dest.writeStrongBinder(null);
} }
dest.writeStrongBinder(connection);
dest.writeInt(noReleaseNeeded ? 1:0); dest.writeInt(noReleaseNeeded ? 1:0);
} }
@ -398,6 +402,7 @@ public interface IActivityManager extends IInterface {
info = ProviderInfo.CREATOR.createFromParcel(source); info = ProviderInfo.CREATOR.createFromParcel(source);
provider = ContentProviderNative.asInterface( provider = ContentProviderNative.asInterface(
source.readStrongBinder()); source.readStrongBinder());
connection = source.readStrongBinder();
noReleaseNeeded = source.readInt() != 0; noReleaseNeeded = source.readInt() != 0;
} }
} }
@ -476,7 +481,7 @@ public interface IActivityManager extends IInterface {
int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27; int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28; int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29; int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
int REF_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
int FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31; int FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32; int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33; int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
@ -597,4 +602,5 @@ public interface IActivityManager extends IInterface {
int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147; int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147;
int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148; int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149; int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149;
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
} }

View File

@ -128,6 +128,7 @@ public interface IApplicationThread extends IInterface {
String[] args) throws RemoteException; String[] args) throws RemoteException;
void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException;
void unstableProviderDied(IBinder provider) throws RemoteException;
String descriptor = "android.app.IApplicationThread"; String descriptor = "android.app.IApplicationThread";
@ -176,4 +177,5 @@ public interface IApplicationThread extends IInterface {
int DUMP_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43; int DUMP_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43;
int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44; int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45; int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
} }

View File

@ -988,6 +988,11 @@ public class Instrumentation {
* Perform calling of the application's {@link Application#onCreate} * Perform calling of the application's {@link Application#onCreate}
* method. The default implementation simply calls through to that method. * method. The default implementation simply calls through to that method.
* *
* <p>Note: This method will be called immediately after {@link #onCreate(Bundle)}.
* Often instrumentation tests start their test thread in onCreate(); you
* need to be careful of races between these. (Well between it and
* everything else, but let's start here.)
*
* @param app The application being created. * @param app The application being created.
*/ */
public void callApplicationOnCreate(Application app) { public void callApplicationOnCreate(Application app) {

View File

@ -20,6 +20,7 @@ import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.os.DeadObjectException;
import android.os.ICancellationSignal; import android.os.ICancellationSignal;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
@ -33,11 +34,19 @@ import java.util.ArrayList;
* calling {@link ContentResolver#acquireContentProviderClient}. This object must be released * calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
* using {@link #release} in order to indicate to the system that the {@link ContentProvider} is * using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
* no longer needed and can be killed to free up resources. * no longer needed and can be killed to free up resources.
*
* <p>Note that you should generally create a new ContentProviderClient instance
* for each thread that will be performing operations. Unlike
* {@link ContentResolver}, the methods here such as {@link #query} and
* {@link #openFile} are not thread safe -- you must not call
* {@link #release()} on the ContentProviderClient those calls are made from
* until you are finished with the data they have returned.
*/ */
public class ContentProviderClient { public class ContentProviderClient {
private final IContentProvider mContentProvider; private final IContentProvider mContentProvider;
private final ContentResolver mContentResolver; private final ContentResolver mContentResolver;
private final boolean mStable; private final boolean mStable;
private boolean mReleased;
/** /**
* @hide * @hide
@ -52,7 +61,14 @@ public class ContentProviderClient {
/** See {@link ContentProvider#query ContentProvider.query} */ /** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection, public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException { String[] selectionArgs, String sortOrder) throws RemoteException {
try {
return query(url, projection, selection, selectionArgs, sortOrder, null); return query(url, projection, selection, selectionArgs, sortOrder, null);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#query ContentProvider.query} */ /** See {@link ContentProvider#query ContentProvider.query} */
@ -64,41 +80,90 @@ public class ContentProviderClient {
remoteCancellationSignal = mContentProvider.createCancellationSignal(); remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal); cancellationSignal.setRemote(remoteCancellationSignal);
} }
try {
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder, return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder,
remoteCancellationSignal); remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#getType ContentProvider.getType} */ /** See {@link ContentProvider#getType ContentProvider.getType} */
public String getType(Uri url) throws RemoteException { public String getType(Uri url) throws RemoteException {
try {
return mContentProvider.getType(url); return mContentProvider.getType(url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */ /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException { public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
try {
return mContentProvider.getStreamTypes(url, mimeTypeFilter); return mContentProvider.getStreamTypes(url, mimeTypeFilter);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#insert ContentProvider.insert} */ /** See {@link ContentProvider#insert ContentProvider.insert} */
public Uri insert(Uri url, ContentValues initialValues) public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException { throws RemoteException {
try {
return mContentProvider.insert(url, initialValues); return mContentProvider.insert(url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */ /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException { public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
try {
return mContentProvider.bulkInsert(url, initialValues); return mContentProvider.bulkInsert(url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#delete ContentProvider.delete} */ /** See {@link ContentProvider#delete ContentProvider.delete} */
public int delete(Uri url, String selection, String[] selectionArgs) public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException { throws RemoteException {
try {
return mContentProvider.delete(url, selection, selectionArgs); return mContentProvider.delete(url, selection, selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#update ContentProvider.update} */ /** See {@link ContentProvider#update ContentProvider.update} */
public int update(Uri url, ContentValues values, String selection, public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException { String[] selectionArgs) throws RemoteException {
try {
return mContentProvider.update(url, values, selection, selectionArgs); return mContentProvider.update(url, values, selection, selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** /**
@ -110,7 +175,14 @@ public class ContentProviderClient {
*/ */
public ParcelFileDescriptor openFile(Uri url, String mode) public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException { throws RemoteException, FileNotFoundException {
try {
return mContentProvider.openFile(url, mode); return mContentProvider.openFile(url, mode);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** /**
@ -122,20 +194,41 @@ public class ContentProviderClient {
*/ */
public AssetFileDescriptor openAssetFile(Uri url, String mode) public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException { throws RemoteException, FileNotFoundException {
try {
return mContentProvider.openAssetFile(url, mode); return mContentProvider.openAssetFile(url, mode);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */ /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
String mimeType, Bundle opts) String mimeType, Bundle opts)
throws RemoteException, FileNotFoundException { throws RemoteException, FileNotFoundException {
try {
return mContentProvider.openTypedAssetFile(uri, mimeType, opts); return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */ /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException { throws RemoteException, OperationApplicationException {
try {
return mContentProvider.applyBatch(operations); return mContentProvider.applyBatch(operations);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
} }
/** /**
@ -144,12 +237,18 @@ public class ContentProviderClient {
* @return true if this was release, false if it was already released * @return true if this was release, false if it was already released
*/ */
public boolean release() { public boolean release() {
synchronized (this) {
if (mReleased) {
throw new IllegalStateException("Already released");
}
mReleased = true;
if (mStable) { if (mStable) {
return mContentResolver.releaseProvider(mContentProvider); return mContentResolver.releaseProvider(mContentProvider);
} else { } else {
return mContentResolver.releaseUnstableProvider(mContentProvider); return mContentResolver.releaseUnstableProvider(mContentProvider);
} }
} }
}
/** /**
* Get a reference to the {@link ContentProvider} that is associated with this * Get a reference to the {@link ContentProvider} that is associated with this

View File

@ -20,27 +20,24 @@ import dalvik.system.CloseGuard;
import android.accounts.Account; import android.accounts.Account;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.AppGlobals; import android.app.AppGlobals;
import android.content.ContentProvider.Transport;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetFileDescriptor; import android.content.res.AssetFileDescriptor;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.database.CrossProcessCursorWrapper; import android.database.CrossProcessCursorWrapper;
import android.database.Cursor; import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.IContentObserver; import android.database.IContentObserver;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.os.DeadObjectException;
import android.os.IBinder; import android.os.IBinder;
import android.os.ICancellationSignal; import android.os.ICancellationSignal;
import android.os.OperationCanceledException; import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock; import android.os.SystemClock;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.EventLog; import android.util.EventLog;
@ -202,6 +199,8 @@ public abstract class ContentResolver {
protected abstract IContentProvider acquireUnstableProvider(Context c, String name); protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
/** @hide */ /** @hide */
public abstract boolean releaseUnstableProvider(IContentProvider icp); public abstract boolean releaseUnstableProvider(IContentProvider icp);
/** @hide */
public abstract void unstableProviderDied(IContentProvider icp);
/** /**
* Return the MIME type of the given content URL. * Return the MIME type of the given content URL.
@ -211,6 +210,7 @@ public abstract class ContentResolver {
* @return A MIME type for the content, or null if the URL is invalid or the type is unknown * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
*/ */
public final String getType(Uri url) { public final String getType(Uri url) {
// XXX would like to have an acquireExistingUnstableProvider for this.
IContentProvider provider = acquireExistingProvider(url); IContentProvider provider = acquireExistingProvider(url);
if (provider != null) { if (provider != null) {
try { try {
@ -351,23 +351,37 @@ public abstract class ContentResolver {
public final Cursor query(final Uri uri, String[] projection, public final Cursor query(final Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder, String selection, String[] selectionArgs, String sortOrder,
CancellationSignal cancellationSignal) { CancellationSignal cancellationSignal) {
IContentProvider provider = acquireProvider(uri); IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (provider == null) { if (unstableProvider == null) {
return null; return null;
} }
IContentProvider stableProvider = null;
try { try {
long startTime = SystemClock.uptimeMillis(); long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null; ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) { if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled(); cancellationSignal.throwIfCanceled();
remoteCancellationSignal = provider.createCancellationSignal(); remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal); cancellationSignal.setRemote(remoteCancellationSignal);
} }
Cursor qCursor = provider.query(uri, projection, Cursor qCursor;
try {
qCursor = unstableProvider.query(uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal); selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) { if (qCursor == null) {
releaseProvider(provider);
return null; return null;
} }
// force query execution // force query execution
@ -375,16 +389,21 @@ public abstract class ContentResolver {
long durationMillis = SystemClock.uptimeMillis() - startTime; long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder); maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
// Wrap the cursor object into CursorWrapperInner object // Wrap the cursor object into CursorWrapperInner object
return new CursorWrapperInner(qCursor, provider); CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
stableProvider != null ? stableProvider : acquireProvider(uri));
stableProvider = null;
return wrapper;
} catch (RemoteException e) { } catch (RemoteException e) {
releaseProvider(provider);
// Arbitrary and not worth documenting, as Activity // Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway. // Manager will kill this process shortly anyway.
return null; return null;
} catch (RuntimeException e) { } finally {
releaseProvider(provider); if (unstableProvider != null) {
throw e; releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
} }
} }
@ -592,48 +611,62 @@ public abstract class ContentResolver {
if ("r".equals(mode)) { if ("r".equals(mode)) {
return openTypedAssetFileDescriptor(uri, "*/*", null); return openTypedAssetFileDescriptor(uri, "*/*", null);
} else { } else {
int n = 0; IContentProvider unstableProvider = acquireUnstableProvider(uri);
while (true) { if (unstableProvider == null) {
n++;
IContentProvider provider = acquireUnstableProvider(uri);
if (provider == null) {
throw new FileNotFoundException("No content provider: " + uri); throw new FileNotFoundException("No content provider: " + uri);
} }
IContentProvider stableProvider = null;
AssetFileDescriptor fd = null;
try { try {
AssetFileDescriptor fd = provider.openAssetFile(uri, mode); try {
fd = unstableProvider.openAssetFile(uri, mode);
if (fd == null) { if (fd == null) {
// The provider will be released by the finally{} clause // The provider will be released by the finally{} clause
return null; return null;
} }
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openAssetFile(uri, mode);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
}
if (stableProvider == null) {
stableProvider = acquireProvider(uri);
}
releaseUnstableProvider(unstableProvider);
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
fd.getParcelFileDescriptor(), provider); fd.getParcelFileDescriptor(), stableProvider);
// Success! Don't release the provider when exiting, let // Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed. // ParcelFileDescriptorInner do that when it is closed.
provider = null; stableProvider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(), return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength()); fd.getDeclaredLength());
} catch (RemoteException e) { } catch (RemoteException e) {
// The provider died for some reason. Since we are
// acquiring it unstable, its process could have gotten
// killed and need to be restarted. We'll retry a couple
// times and if still can't succeed then fail.
if (n <= 2) {
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
continue;
}
// Whatever, whatever, we'll go away. // Whatever, whatever, we'll go away.
throw new FileNotFoundException("Dead content provider: " + uri); throw new FileNotFoundException(
"Failed opening content provider: " + uri);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
throw e; throw e;
} finally { } finally {
if (provider != null) { if (stableProvider != null) {
releaseUnstableProvider(provider); releaseProvider(stableProvider);
} }
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
} }
} }
} }
@ -670,48 +703,62 @@ public abstract class ContentResolver {
*/ */
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
String mimeType, Bundle opts) throws FileNotFoundException { String mimeType, Bundle opts) throws FileNotFoundException {
int n = 0; IContentProvider unstableProvider = acquireUnstableProvider(uri);
while (true) { if (unstableProvider == null) {
n++;
IContentProvider provider = acquireUnstableProvider(uri);
if (provider == null) {
throw new FileNotFoundException("No content provider: " + uri); throw new FileNotFoundException("No content provider: " + uri);
} }
IContentProvider stableProvider = null;
AssetFileDescriptor fd = null;
try { try {
AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts); try {
fd = unstableProvider.openTypedAssetFile(uri, mimeType, opts);
if (fd == null) { if (fd == null) {
// The provider will be released by the finally{} clause // The provider will be released by the finally{} clause
return null; return null;
} }
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
fd = stableProvider.openTypedAssetFile(uri, mimeType, opts);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
}
if (stableProvider == null) {
stableProvider = acquireProvider(uri);
}
releaseUnstableProvider(unstableProvider);
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
fd.getParcelFileDescriptor(), provider); fd.getParcelFileDescriptor(), stableProvider);
// Success! Don't release the provider when exiting, let // Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed. // ParcelFileDescriptorInner do that when it is closed.
provider = null; stableProvider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(), return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength()); fd.getDeclaredLength());
} catch (RemoteException e) { } catch (RemoteException e) {
// The provider died for some reason. Since we are
// acquiring it unstable, its process could have gotten
// killed and need to be restarted. We'll retry a couple
// times and if still can't succeed then fail.
if (n <= 2) {
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
continue;
}
// Whatever, whatever, we'll go away. // Whatever, whatever, we'll go away.
throw new FileNotFoundException("Dead content provider: " + uri); throw new FileNotFoundException(
"Failed opening content provider: " + uri);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
throw e; throw e;
} finally { } finally {
if (provider != null) { if (stableProvider != null) {
releaseUnstableProvider(provider); releaseProvider(stableProvider);
} }
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
} }
} }
} }
@ -1061,7 +1108,7 @@ public abstract class ContentResolver {
if (name == null) { if (name == null) {
return null; return null;
} }
return acquireProvider(mContext, name); return acquireUnstableProvider(mContext, name);
} }
/** /**
@ -1113,10 +1160,15 @@ public abstract class ContentResolver {
* use it as needed and it won't disappear, even if your process is in the * use it as needed and it won't disappear, even if your process is in the
* background. If using this method, you need to take care to deal with any * background. If using this method, you need to take care to deal with any
* failures when communicating with the provider, and be sure to close it * failures when communicating with the provider, and be sure to close it
* so that it can be re-opened later. * so that it can be re-opened later. In particular, catching a
* {@link android.os.DeadObjectException} from the calls there will let you
* know that the content provider has gone away; at that point the current
* ContentProviderClient object is invalid, and you should release it. You
* can acquire a new one if you would like to try to restart the provider
* and perform new operations on it.
*/ */
public final ContentProviderClient acquireUnstableContentProviderClient(Uri uri) { public final ContentProviderClient acquireUnstableContentProviderClient(Uri uri) {
IContentProvider provider = acquireProvider(uri); IContentProvider provider = acquireUnstableProvider(uri);
if (provider != null) { if (provider != null) {
return new ContentProviderClient(this, provider, false); return new ContentProviderClient(this, provider, false);
} }
@ -1133,10 +1185,15 @@ public abstract class ContentResolver {
* use it as needed and it won't disappear, even if your process is in the * use it as needed and it won't disappear, even if your process is in the
* background. If using this method, you need to take care to deal with any * background. If using this method, you need to take care to deal with any
* failures when communicating with the provider, and be sure to close it * failures when communicating with the provider, and be sure to close it
* so that it can be re-opened later. * so that it can be re-opened later. In particular, catching a
* {@link android.os.DeadObjectException} from the calls there will let you
* know that the content provider has gone away; at that point the current
* ContentProviderClient object is invalid, and you should release it. You
* can acquire a new one if you would like to try to restart the provider
* and perform new operations on it.
*/ */
public final ContentProviderClient acquireUnstableContentProviderClient(String name) { public final ContentProviderClient acquireUnstableContentProviderClient(String name) {
IContentProvider provider = acquireProvider(name); IContentProvider provider = acquireUnstableProvider(name);
if (provider != null) { if (provider != null) {
return new ContentProviderClient(this, provider, false); return new ContentProviderClient(this, provider, false);
} }
@ -1780,7 +1837,6 @@ public abstract class ContentResolver {
private final class ParcelFileDescriptorInner extends ParcelFileDescriptor { private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
private final IContentProvider mContentProvider; private final IContentProvider mContentProvider;
public static final String TAG="ParcelFileDescriptorInner";
private boolean mReleaseProviderFlag = false; private boolean mReleaseProviderFlag = false;
ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) { ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
@ -1792,7 +1848,7 @@ public abstract class ContentResolver {
public void close() throws IOException { public void close() throws IOException {
if(!mReleaseProviderFlag) { if(!mReleaseProviderFlag) {
super.close(); super.close();
ContentResolver.this.releaseUnstableProvider(mContentProvider); ContentResolver.this.releaseProvider(mContentProvider);
mReleaseProviderFlag = true; mReleaseProviderFlag = true;
} }
} }

View File

@ -62,6 +62,7 @@ import android.content.ContentProvider;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.IContentProvider;
import android.content.IIntentReceiver; import android.content.IIntentReceiver;
import android.content.IIntentSender; import android.content.IIntentSender;
import android.content.Intent; import android.content.Intent;
@ -1810,14 +1811,13 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
} }
} }
if (app.conProviders.size() > 0) { for (int j=app.conProviders.size()-1; j>=0; j--) {
for (ContentProviderRecord cpr : app.conProviders.keySet()) { ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) { if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
updateLruProcessInternalLocked(cpr.proc, oomAdj, updateLruProcessInternalLocked(cpr.proc, oomAdj,
updateActivityTime, i+1); updateActivityTime, i+1);
} }
} }
}
//Slog.i(TAG, "Putting proc to front: " + app.processName); //Slog.i(TAG, "Putting proc to front: " + app.processName);
if (oomAdj) { if (oomAdj) {
@ -3742,7 +3742,7 @@ public final class ActivityManagerService extends ActivityManagerNative
N = providers.size(); N = providers.size();
for (i=0; i<N; i++) { for (i=0; i<N; i++) {
removeDyingProviderLocked(null, providers.get(i)); removeDyingProviderLocked(null, providers.get(i), true);
} }
if (doit) { if (doit) {
@ -6058,53 +6058,72 @@ public final class ActivityManagerService extends ActivityManagerNative
return msg; return msg;
} }
boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr, ContentProviderConnection incProviderCountLocked(ProcessRecord r,
IBinder externalProcessToken) { final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
if (r != null) { if (r != null) {
Integer cnt = r.conProviders.get(cpr); 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, if (DEBUG_PROVIDER) Slog.v(TAG,
"Adding provider requested by " "Adding provider requested by "
+ r.processName + " from process " + r.processName + " from process "
+ cpr.info.processName + ": " + cpr.name.flattenToShortString() + cpr.info.processName + ": " + cpr.name.flattenToShortString()
+ " cnt=" + (cnt == null ? 1 : cnt)); + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
if (cnt == null) { if (stable) {
cpr.clients.add(r); conn.stableCount++;
r.conProviders.put(cpr, new Integer(1)); conn.numStableIncs++;
return true;
} else { } else {
r.conProviders.put(cpr, new Integer(cnt.intValue()+1)); conn.unstableCount++;
conn.numUnstableIncs++;
} }
return conn;
}
}
ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
if (stable) {
conn.stableCount = 1;
conn.numStableIncs = 1;
} else { } else {
conn.unstableCount = 1;
conn.numUnstableIncs = 1;
}
cpr.connections.add(conn);
r.conProviders.add(conn);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken); cpr.addExternalProcessHandleLocked(externalProcessToken);
return null;
}
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 "
+ conn.client.processName + " from process "
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
if (stable) {
conn.stableCount--;
} else {
conn.unstableCount--;
}
if (conn.stableCount == 0 && conn.unstableCount == 0) {
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
return true;
} }
return false; return false;
} }
boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
IBinder externalProcessToken) {
if (r != null) {
Integer cnt = r.conProviders.get(cpr);
if (DEBUG_PROVIDER) Slog.v(TAG,
"Removing provider requested by "
+ r.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;
} else {
r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
}
} else {
cpr.removeExternalProcessHandleLocked(externalProcessToken); cpr.removeExternalProcessHandleLocked(externalProcessToken);
}
return false; return false;
} }
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller, private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token) { String name, IBinder token, boolean stable) {
ContentProviderRecord cpr; ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null; ProviderInfo cpi = null;
synchronized(this) { synchronized(this) {
@ -6135,20 +6154,19 @@ public final class ActivityManagerService extends ActivityManagerNative
// of being published... but it is also allowed to run // of being published... but it is also allowed to run
// in the caller's process, so don't make a connection // in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance. // and just let the caller instantiate its own instance.
if (cpr.provider != null) { ContentProviderHolder holder = cpr.newHolder(null);
// don't give caller the provider object, it needs // don't give caller the provider object, it needs
// to make its own. // to make its own.
cpr = new ContentProviderRecord(cpr); holder.provider = null;
} return holder;
return cpr;
} }
final long origId = Binder.clearCallingIdentity(); final long origId = Binder.clearCallingIdentity();
// In this case the provider instance already exists, so we can // In this case the provider instance already exists, so we can
// return it right away. // return it right away.
final boolean countChanged = incProviderCount(r, cpr, token); conn = incProviderCountLocked(r, cpr, token, stable);
if (countChanged) { if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// If this is a perceptible app accessing the provider, // If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus // make sure to count it as being accessed and thus
@ -6181,7 +6199,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.i(TAG, Slog.i(TAG,
"Existing provider " + cpr.name.flattenToShortString() "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r); + " 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); appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);
if (!lastRef) { if (!lastRef) {
// This wasn't the last ref our process had on // This wasn't the last ref our process had on
@ -6189,6 +6207,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return null; return null;
} }
providerRunning = false; providerRunning = false;
conn = null;
} }
} }
@ -6251,7 +6270,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// info and allow the caller to instantiate it. Only do // info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's // this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process). // process, or can run as root (so can be in any process).
return cpr; return cpr.newHolder(null);
} }
if (DEBUG_PROVIDER) { if (DEBUG_PROVIDER) {
@ -6312,7 +6331,10 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
mProviderMap.putProviderByName(name, cpr); 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=" Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+ cpr.launchingApp); + cpr.launchingApp);
} }
if (conn != null) {
conn.waiting = true;
}
cpr.wait(); cpr.wait();
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
} }
} }
} }
return cpr; }
return cpr != null ? cpr.newHolder(conn) : null;
} }
public final ContentProviderHolder getContentProvider( public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name) { IApplicationThread caller, String name, boolean stable) {
enforceNotIsolatedCaller("getContentProvider"); enforceNotIsolatedCaller("getContentProvider");
if (caller == null) { if (caller == null) {
String msg = "null IApplicationThread when getting content provider " String msg = "null IApplicationThread when getting content provider "
@ -6352,7 +6381,7 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg); throw new SecurityException(msg);
} }
return getContentProviderImpl(caller, name, null); return getContentProviderImpl(caller, name, null, stable);
} }
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) { public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
@ -6362,48 +6391,33 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) { 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 * Drop a content provider from a ProcessRecord's bookkeeping
* @param cpr * @param cpr
*/ */
public void removeContentProvider(IApplicationThread caller, String name) { public void removeContentProvider(IBinder connection, boolean stable) {
enforceNotIsolatedCaller("removeContentProvider"); enforceNotIsolatedCaller("removeContentProvider");
synchronized (this) { synchronized (this) {
int userId = UserId.getUserId(Binder.getCallingUid()); ContentProviderConnection conn;
ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId); try {
if(cpr == null) { conn = (ContentProviderConnection)connection;
// remove from mProvidersByClass } catch (ClassCastException e) {
if (DEBUG_PROVIDER) Slog.v(TAG, name + String msg ="removeContentProvider: " + connection
" provider not found in providers list"); + " not a ContentProviderConnection";
return; Slog.w(TAG, msg);
throw new IllegalArgumentException(msg);
} }
final ProcessRecord r = getRecordForAppLocked(caller); if (conn == null) {
if (r == null) { throw new NullPointerException("connection is null");
throw new SecurityException(
"Unable to find app for caller " + caller +
" when removing content provider " + name);
} }
//update content provider record entry info if (decProviderCountLocked(conn, null, null, stable)) {
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(); updateOomAdjLocked();
} }
} }
} }
}
public void removeContentProviderExternal(String name, IBinder token) { public void removeContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY, enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
@ -6447,7 +6461,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
enforceNotIsolatedCaller("publishContentProviders"); enforceNotIsolatedCaller("publishContentProviders");
synchronized(this) { synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller); final ProcessRecord r = getRecordForAppLocked(caller);
if (DEBUG_MU) if (DEBUG_MU)
Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid); 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() { public static final void installSystemProviders() {
List<ProviderInfo> providers; List<ProviderInfo> providers;
synchronized (mSelf) { synchronized (mSelf) {
@ -10515,22 +10626,35 @@ public final class ActivityManagerService extends ActivityManagerNative
app.executingServices.clear(); app.executingServices.clear();
} }
private final void removeDyingProviderLocked(ProcessRecord proc, private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr) { ContentProviderRecord cpr, boolean always) {
final boolean inLaunching = mLaunchingProviders.contains(cpr);
if (!inLaunching || always) {
synchronized (cpr) { synchronized (cpr) {
cpr.launchingApp = null; cpr.launchingApp = null;
cpr.notifyAll(); cpr.notifyAll();
} }
mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid)); mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";"); String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) { for (int j = 0; j < names.length; j++) {
mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid)); mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
} }
}
Iterator<ProcessRecord> cit = cpr.clients.iterator(); for (int i=0; i<cpr.connections.size(); i++) {
while (cit.hasNext()) { ContentProviderConnection conn = cpr.connections.get(i);
ProcessRecord capp = cit.next(); 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 if (!capp.persistent && capp.thread != null
&& capp.pid != 0 && capp.pid != 0
&& capp.pid != MY_PID) { && capp.pid != MY_PID) {
@ -10542,10 +10666,23 @@ public final class ActivityManagerService extends ActivityManagerNative
+ cpr.name.toShortString()); + cpr.name.toShortString());
Process.killProcessQuiet(capp.pid); 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);
}
} }
if (inLaunching && always) {
mLaunchingProviders.remove(cpr); mLaunchingProviders.remove(cpr);
} }
return inLaunching;
}
/** /**
* Main code for cleaning up a process when it has gone away. This is * Main code for cleaning up a process when it has gone away. This is
@ -10590,34 +10727,21 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean restart = false; boolean restart = false;
int NL = mLaunchingProviders.size();
// Remove published content providers. // Remove published content providers.
if (!app.pubProviders.isEmpty()) { if (!app.pubProviders.isEmpty()) {
Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator(); Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
while (it.hasNext()) { while (it.hasNext()) {
ContentProviderRecord cpr = it.next(); ContentProviderRecord cpr = it.next();
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;
}
cpr.provider = null; cpr.provider = null;
cpr.proc = 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;
}
if (i >= NL) {
removeDyingProviderLocked(app, cpr);
NL = mLaunchingProviders.size();
}
} }
app.pubProviders.clear(); app.pubProviders.clear();
} }
@ -10629,10 +10753,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Unregister from connected content providers. // Unregister from connected content providers.
if (!app.conProviders.isEmpty()) { if (!app.conProviders.isEmpty()) {
Iterator it = app.conProviders.keySet().iterator(); for (int i=0; i<app.conProviders.size(); i++) {
while (it.hasNext()) { ContentProviderConnection conn = app.conProviders.get(i);
ContentProviderRecord cpr = (ContentProviderRecord)it.next(); conn.provider.connections.remove(conn);
cpr.clients.remove(app);
} }
app.conProviders.clear(); 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 // XXX Commented out for now. Trying to figure out a way to reproduce
// the actual situation to identify what is actually going on. // the actual situation to identify what is actually going on.
if (false) { if (false) {
for (int i=0; i<NL; i++) { for (int i=0; i<mLaunchingProviders.size(); i++) {
ContentProviderRecord cpr = (ContentProviderRecord) ContentProviderRecord cpr = (ContentProviderRecord)
mLaunchingProviders.get(i); mLaunchingProviders.get(i);
if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) { if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
synchronized (cpr) { synchronized (cpr) {
cpr.launchingApp = null; cpr.launchingApp = null;
cpr.notifyAll(); cpr.notifyAll();
@ -10743,7 +10866,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!alwaysBad && !app.bad) { if (!alwaysBad && !app.bad) {
restart = true; restart = true;
} else { } else {
removeDyingProviderLocked(app, cpr); removeDyingProviderLocked(app, cpr, true);
NL = mLaunchingProviders.size(); NL = mLaunchingProviders.size();
} }
} }
@ -13948,10 +14071,12 @@ public final class ActivityManagerService extends ActivityManagerNative
while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
ContentProviderRecord cpr = jt.next(); ContentProviderRecord cpr = jt.next();
if (cpr.clients.size() != 0) { for (int i = cpr.connections.size()-1;
Iterator<ProcessRecord> kt = cpr.clients.iterator(); i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
while (kt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) { || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
ProcessRecord client = kt.next(); i--) {
ContentProviderConnection conn = cpr.connections.get(i);
ProcessRecord client = conn.client;
if (client == app) { if (client == app) {
// Being our own client is not interesting. // Being our own client is not interesting.
continue; continue;
@ -13991,7 +14116,6 @@ public final class ActivityManagerService extends ActivityManagerNative
schedGroup = Process.THREAD_GROUP_DEFAULT; schedGroup = Process.THREAD_GROUP_DEFAULT;
} }
} }
}
// If the provider has external (non-framework) process // If the provider has external (non-framework) process
// dependencies, ensure that its adjustment is at least // dependencies, ensure that its adjustment is at least
// FOREGROUND_APP_ADJ. // FOREGROUND_APP_ADJ.

View File

@ -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");
}
}
}

View File

@ -18,6 +18,7 @@ package com.android.server.am;
import android.app.IActivityManager.ContentProviderHolder; import android.app.IActivityManager.ContentProviderHolder;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.IContentProvider;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfo;
import android.os.IBinder; import android.os.IBinder;
@ -27,28 +28,35 @@ import android.os.RemoteException;
import android.util.Slog; import android.util.Slog;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; 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 // 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 // Handles for non-framework processes supported by this provider
HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle; HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
// Count for external process for which we have no handles. // Count for external process for which we have no handles.
int externalProcessNoHandleCount; int externalProcessNoHandleCount;
final ActivityManagerService service;
final int uid;
final ApplicationInfo appInfo;
final ComponentName name;
ProcessRecord proc; // if non-null, hosting process. ProcessRecord proc; // if non-null, hosting process.
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched. ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
String stringName; String stringName;
String shortStringName;
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info, public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
ApplicationInfo ai, ComponentName _name) { ApplicationInfo ai, ComponentName _name) {
super(_info);
service = _service; service = _service;
info = _info;
uid = ai.uid; uid = ai.uid;
appInfo = ai; appInfo = ai;
name = _name; name = _name;
@ -56,12 +64,20 @@ class ContentProviderRecord extends ContentProviderHolder {
} }
public ContentProviderRecord(ContentProviderRecord cpr) { public ContentProviderRecord(ContentProviderRecord cpr) {
super(cpr.info); service = cpr.service;
info = cpr.info;
uid = cpr.uid; uid = cpr.uid;
appInfo = cpr.appInfo; appInfo = cpr.appInfo;
name = cpr.name; name = cpr.name;
noReleaseNeeded = cpr.noReleaseNeeded; 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) { public boolean canRunHere(ProcessRecord app) {
@ -120,30 +136,51 @@ class ContentProviderRecord extends ContentProviderHolder {
return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0); return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
} }
void dump(PrintWriter pw, String prefix) { void dump(PrintWriter pw, String prefix, boolean full) {
if (full) {
pw.print(prefix); pw.print("package="); pw.print(prefix); pw.print("package=");
pw.print(info.applicationInfo.packageName); pw.print(info.applicationInfo.packageName);
pw.print(" process="); pw.println(info.processName); pw.print(" process="); pw.println(info.processName);
}
pw.print(prefix); pw.print("proc="); pw.println(proc); pw.print(prefix); pw.print("proc="); pw.println(proc);
if (launchingApp != null) { if (launchingApp != null) {
pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp); pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
} }
if (full) {
pw.print(prefix); pw.print("uid="); pw.print(uid); pw.print(prefix); pw.print("uid="); pw.print(uid);
pw.print(" provider="); pw.println(provider); pw.print(" provider="); pw.println(provider);
pw.print(prefix); pw.print("name="); pw.println(info.authority); }
pw.print(prefix); pw.print("authority="); pw.println(info.authority);
if (full) {
if (info.isSyncable || info.multiprocess || info.initOrder != 0) { if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable); pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
pw.print("multiprocess="); pw.print(info.multiprocess); pw.print(" multiprocess="); pw.print(info.multiprocess);
pw.print(" initOrder="); pw.println(info.initOrder); pw.print(" initOrder="); pw.println(info.initOrder);
} }
}
if (full) {
if (hasExternalProcessHandles()) { if (hasExternalProcessHandles()) {
pw.print(prefix); pw.print("externals="); pw.print(prefix); pw.print("externals=");
pw.println(externalProcessTokenToHandle.size()); pw.println(externalProcessTokenToHandle.size());
} }
if (clients.size() > 0) { } else {
pw.print(prefix); pw.println("Clients:"); if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
for (ProcessRecord cproc : clients) { pw.print(prefix); pw.print(connections.size());
pw.print(prefix); pw.print(" - "); pw.println(cproc.toShortString()); 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(); 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. // This class represents a handle from an external process to a provider.
private class ExternalProcessHandle implements DeathRecipient { private class ExternalProcessHandle implements DeathRecipient {
private static final String LOG_TAG = "ExternalProcessHanldle"; private static final String LOG_TAG = "ExternalProcessHanldle";

View File

@ -125,8 +125,8 @@ class ProcessRecord {
final HashMap<String, ContentProviderRecord> pubProviders final HashMap<String, ContentProviderRecord> pubProviders
= new HashMap<String, ContentProviderRecord>(); = new HashMap<String, ContentProviderRecord>();
// All ContentProviderRecord process is using // All ContentProviderRecord process is using
final HashMap<ContentProviderRecord, Integer> conProviders final ArrayList<ContentProviderConnection> conProviders
= new HashMap<ContentProviderRecord, Integer>(); = new ArrayList<ContentProviderConnection>();
boolean persistent; // always keep this application running? boolean persistent; // always keep this application running?
boolean crashing; // are we in the process of crashing? boolean crashing; // are we in the process of crashing?
@ -257,25 +257,47 @@ class ProcessRecord {
pw.println(); pw.println();
} }
if (activities.size() > 0) { 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) { 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) { 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) { 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) { 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) { 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) { 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);
}
} }
} }

View File

@ -177,27 +177,9 @@ public class ProviderMap {
while (it.hasNext()) { while (it.hasNext()) {
Map.Entry<ComponentName, ContentProviderRecord> e = it.next(); Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
ContentProviderRecord r = e.getValue(); ContentProviderRecord r = e.getValue();
if (dumpAll) {
pw.print(" * "); pw.print(" * ");
pw.println(r); pw.println(r);
r.dump(pw, " "); r.dump(pw, " ", dumpAll);
} 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");
}
}
} }
} }
@ -210,7 +192,7 @@ public class ProviderMap {
pw.print(" "); pw.print(" ");
pw.print(e.getKey()); pw.print(e.getKey());
pw.print(": "); pw.print(": ");
pw.println(r); pw.println(r.toShortString());
} }
} }
@ -221,10 +203,10 @@ public class ProviderMap {
pw.println(" "); pw.println(" ");
pw.println(" Published content providers (by class):"); pw.println(" Published content providers (by class):");
dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass); dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
pw.println("");
} }
if (mProvidersByClassPerUser.size() > 1) { if (mProvidersByClassPerUser.size() > 1) {
pw.println("");
for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i); HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":"); pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
@ -321,7 +303,7 @@ public class ProviderMap {
if (r.proc != null) pw.println(r.proc.pid); if (r.proc != null) pw.println(r.proc.pid);
else pw.println("(not running)"); else pw.println("(not running)");
if (dumpAll) { if (dumpAll) {
r.dump(pw, innerPrefix); r.dump(pw, innerPrefix, true);
} }
} }
if (r.proc != null && r.proc.thread != null) { if (r.proc != null && r.proc.thread != null) {

View File

@ -118,6 +118,11 @@ public class MockContentResolver extends ContentResolver {
return releaseProvider(icp); return releaseProvider(icp);
} }
/** @hide */
@Override
public void unstableProviderDied(IContentProvider icp) {
}
/** /**
* Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean) * Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)
* ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored. * ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored.

View File

@ -72,6 +72,11 @@ public class BridgeContentResolver extends ContentResolver {
return releaseProvider(icp); return releaseProvider(icp);
} }
/** @hide */
@Override
public void unstableProviderDied(IContentProvider icp) {
}
/** /**
* Stub for the layoutlib bridge content resolver. * Stub for the layoutlib bridge content resolver.
*/ */