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

View File

@ -140,6 +140,7 @@ public final class ActivityThread {
private static final boolean DEBUG_CONFIGURATION = false;
private static final boolean DEBUG_SERVICE = 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 Pattern PATTERN_SEMICOLON = Pattern.compile(";");
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@ -210,6 +211,8 @@ public final class ActivityThread {
= new HashMap<IBinder, ProviderRefCount>();
final HashMap<IBinder, ProviderClientRecord> mLocalProviders
= new HashMap<IBinder, ProviderClientRecord>();
final HashMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
= new HashMap<ComponentName, ProviderClientRecord>();
final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
= new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
@ -284,20 +287,19 @@ public final class ActivityThread {
}
}
final class ProviderClientRecord implements IBinder.DeathRecipient {
final String mName;
final class ProviderClientRecord {
final String[] mNames;
final IContentProvider mProvider;
final ContentProvider mLocalProvider;
final IActivityManager.ContentProviderHolder mHolder;
ProviderClientRecord(String name, IContentProvider provider,
ContentProvider localProvider) {
mName = name;
ProviderClientRecord(String[] names, IContentProvider provider,
ContentProvider localProvider,
IActivityManager.ContentProviderHolder holder) {
mNames = names;
mProvider = provider;
mLocalProvider = localProvider;
}
public void binderDied() {
removeDeadProvider(mName, mProvider);
mHolder = holder;
}
}
@ -1061,6 +1063,11 @@ public final class ActivityThread {
pw.flush();
}
@Override
public void unstableProviderDied(IBinder provider) {
queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
}
private void printRow(PrintWriter pw, String format, Object...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 TRIM_MEMORY = 140;
public static final int DUMP_PROVIDER = 141;
public static final int UNSTABLE_PROVIDER_DIED = 142;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
@ -1170,6 +1178,7 @@ public final class ActivityThread {
case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
case TRIM_MEMORY: return "TRIM_MEMORY";
case DUMP_PROVIDER: return "DUMP_PROVIDER";
case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
}
}
return Integer.toString(code);
@ -1337,7 +1346,7 @@ public final class ActivityThread {
break;
case REMOVE_PROVIDER:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove");
completeRemoveProvider((IContentProvider)msg.obj);
completeRemoveProvider((ProviderRefCount)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case ENABLE_JIT:
@ -1377,6 +1386,9 @@ public final class ActivityThread {
handleTrimMemory(msg.arg1);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case UNSTABLE_PROVIDER_DIED:
handleUnstableProviderDied((IBinder)msg.obj, false);
break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
@ -2867,10 +2879,24 @@ public final class ActivityThread {
}
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) {
count = pCount;
// When this is set, the stable and unstable ref counts are 0 and
// 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);
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
} else {
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 {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
@ -4159,12 +4187,9 @@ public final class ActivityThread {
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
IContentProvider cp = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/);
if (cp != null) {
IActivityManager.ContentProviderHolder cph =
new IActivityManager.ContentProviderHolder(cpi);
cph.provider = cp;
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
@ -4177,8 +4202,8 @@ public final class ActivityThread {
}
}
public final IContentProvider acquireProvider(Context c, String name) {
IContentProvider provider = acquireExistingProvider(c, name);
public final IContentProvider acquireProvider(Context c, String name, boolean stable) {
IContentProvider provider = acquireExistingProvider(c, name, stable);
if (provider != null) {
return provider;
}
@ -4192,7 +4217,7 @@ public final class ActivityThread {
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), name);
getApplicationThread(), name, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
@ -4202,23 +4227,79 @@ public final class ActivityThread {
// Install provider will increment the reference count for us, and break
// any ties in the race.
provider = installProvider(c, holder.provider, holder.info,
true /*noisy*/, holder.noReleaseNeeded);
if (holder.provider != null && provider != 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;
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.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) {
ProviderClientRecord pr = mProviderMap.get(name);
if (pr == null) {
@ -4232,23 +4313,14 @@ public final class ActivityThread {
// provider is not reference counted and never needs to be released.
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
prc.count += 1;
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);
}
incProviderRefLocked(prc, stable);
}
return provider;
}
}
public final boolean releaseProvider(IContentProvider provider) {
if(provider == null) {
public final boolean releaseProvider(IContentProvider provider, boolean stable) {
if (provider == null) {
return false;
}
@ -4260,55 +4332,98 @@ public final class ActivityThread {
return false;
}
if (prc.count == 0) {
if (localLOGV) Slog.v(TAG, "releaseProvider: ref count already 0, how?");
return false;
boolean lastRef = false;
if (stable) {
if (prc.stableCount == 0) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"releaseProvider: stable ref count already 0, how?");
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 (prc.count == 0) {
// Schedule the actual remove asynchronously, since we don't know the context
// this will be called in.
// TODO: it would be nice to post a delayed message, so
// if we come back and need the same provider quickly
// we will still have it available.
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider);
mH.sendMessage(msg);
if (lastRef) {
if (!prc.removePending) {
// Schedule the actual remove asynchronously, since we don't know the context
// this will be called in.
// TODO: it would be nice to post a delayed message, so
// if we come back and need the same provider quickly
// we will still have it available.
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);
} else {
Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
}
}
return true;
}
}
public final IContentProvider acquireUnstableProvider(Context c, String name) {
return acquireProvider(c, name);
}
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) {
final void completeRemoveProvider(ProviderRefCount prc) {
synchronized (mProviderMap) {
if (!prc.removePending) {
// There was a race! Some other client managed to acquire
// the provider before the removal was completed.
// 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");
return;
}
mProviderRefCountMap.remove(jBinder);
final IBinder jBinder = prc.holder.provider.asBinder();
ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
if (existingPrc == prc) {
mProviderRefCountMap.remove(jBinder);
}
Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
while (iter.hasNext()) {
@ -4316,41 +4431,70 @@ public final class ActivityThread {
IBinder myBinder = pr.mProvider.asBinder();
if (myBinder == jBinder) {
iter.remove();
if (pr.mLocalProvider == null) {
myBinder.unlinkToDeath(pr, 0);
if (remoteProviderName == null) {
remoteProviderName = pr.mName;
}
}
}
try {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
+ "removeContentProvider(" + prc.holder.info.name + ")");
}
ActivityManagerNative.getDefault().removeContentProvider(
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) {
//do nothing content provider object is dead any way
}
}
}
}
if (remoteProviderName != null) {
try {
if (localLOGV) {
Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
+ "removeContentProvider(" + remoteProviderName + ")");
}
ActivityManagerNative.getDefault().removeContentProvider(
getApplicationThread(), remoteProviderName);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
final void removeDeadProvider(String name, IContentProvider provider) {
synchronized(mProviderMap) {
ProviderClientRecord pr = mProviderMap.get(name);
if (pr != null && pr.mProvider.asBinder() == provider.asBinder()) {
Slog.i(TAG, "Removing dead content provider: " + name);
ProviderClientRecord removed = mProviderMap.remove(name);
if (removed != null) {
removed.mProvider.asBinder().unlinkToDeath(removed, 0);
}
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider,IActivityManager.ContentProviderHolder holder) {
String names[] = PATTERN_SEMICOLON.split(holder.info.authority);
ProviderClientRecord pcr = new ProviderClientRecord(names, provider,
localProvider, holder);
for (int i = 0; i < names.length; i++) {
ProviderClientRecord existing = mProviderMap.get(names[i]);
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
* attempts to acquire the same provider.
*/
private IContentProvider installProvider(Context context,
IContentProvider provider, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded) {
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
if (provider == null) {
if (noisy) {
IContentProvider provider;
if (holder == null) {
if (DEBUG_PROVIDER || noisy) {
Slog.d(TAG, "Loading provider " + info.authority + ": "
+ info.name);
}
@ -4409,7 +4554,7 @@ public final class ActivityThread {
info.applicationInfo.sourceDir);
return null;
}
if (false) Slog.v(
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
@ -4421,76 +4566,72 @@ public final class ActivityThread {
}
return null;
}
} else if (localLOGV) {
Slog.v(TAG, "Installing external provider " + info.authority + ": "
} else {
provider = holder.provider;
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
+ info.name);
}
synchronized (mProviderMap) {
// 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);
}
}
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
+ " / " + info.name);
IBinder jBinder = provider.asBinder();
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 (localLOGV) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, "
+ "using existing local provider");
}
provider = pr.mProvider;
} 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);
mLocalProvidersByName.put(cname, pr);
}
}
if (!noReleaseNeeded) {
retHolder = pr.mHolder;
} else {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (localLOGV) {
Slog.v(TAG, "installProvider: lost the race, incrementing ref count");
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
prc.count += 1;
if (prc.count == 1) {
if (localLOGV) {
Slog.v(TAG, "installProvider: "
+ "snatched provider from the jaws of death");
// 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
}
// 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));
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;
}
}
return provider;
return retHolder;
}
private void attach(boolean system) {

View File

@ -575,6 +575,15 @@ public abstract class ApplicationThreadNative extends Binder
reply.writeNoException();
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);
@ -1163,4 +1172,12 @@ class ApplicationThreadProxy implements IApplicationThread {
mRemote.transact(DUMP_DB_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
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
protected IContentProvider acquireProvider(Context context, String name) {
return mMainThread.acquireProvider(context, name);
return mMainThread.acquireProvider(context, name, true);
}
@Override
protected IContentProvider acquireExistingProvider(Context context, String name) {
return mMainThread.acquireExistingProvider(context, name);
return mMainThread.acquireExistingProvider(context, name, true);
}
@Override
public boolean releaseProvider(IContentProvider provider) {
return mMainThread.releaseProvider(provider);
return mMainThread.releaseProvider(provider, true);
}
@Override
protected IContentProvider acquireUnstableProvider(Context c, String name) {
return mMainThread.acquireUnstableProvider(c, name);
return mMainThread.acquireProvider(c, name, false);
}
@Override
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;

View File

@ -116,14 +116,16 @@ public interface IActivityManager extends IInterface {
public void reportThumbnail(IBinder token,
Bitmap thumbnail, CharSequence description) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name) throws RemoteException;
String name, boolean stable) throws RemoteException;
public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
throws RemoteException;
public void removeContentProvider(IApplicationThread caller,
String name) throws RemoteException;
public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException;
public void removeContentProviderExternal(String name, IBinder token) throws RemoteException;
public void publishContentProviders(IApplicationThread caller,
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)
throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service,
@ -363,6 +365,7 @@ public interface IActivityManager extends IInterface {
public static class ContentProviderHolder implements Parcelable {
public final ProviderInfo info;
public IContentProvider provider;
public IBinder connection;
public boolean noReleaseNeeded;
public ContentProviderHolder(ProviderInfo _info) {
@ -380,6 +383,7 @@ public interface IActivityManager extends IInterface {
} else {
dest.writeStrongBinder(null);
}
dest.writeStrongBinder(connection);
dest.writeInt(noReleaseNeeded ? 1:0);
}
@ -398,6 +402,7 @@ public interface IActivityManager extends IInterface {
info = ProviderInfo.CREATOR.createFromParcel(source);
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
connection = source.readStrongBinder();
noReleaseNeeded = source.readInt() != 0;
}
}
@ -476,7 +481,7 @@ public interface IActivityManager extends IInterface {
int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
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 GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
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 FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
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;
void dumpGfxInfo(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";
@ -176,4 +177,5 @@ public interface IApplicationThread extends IInterface {
int DUMP_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43;
int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
}

View File

@ -987,7 +987,12 @@ public class Instrumentation {
/**
* Perform calling of the application's {@link Application#onCreate}
* 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.
*/
public void callApplicationOnCreate(Application app) {

View File

@ -20,6 +20,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.os.ParcelFileDescriptor;
@ -33,11 +34,19 @@ import java.util.ArrayList;
* calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
* 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.
*
* <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 {
private final IContentProvider mContentProvider;
private final ContentResolver mContentResolver;
private final boolean mStable;
private boolean mReleased;
/**
* @hide
@ -52,7 +61,14 @@ public class ContentProviderClient {
/** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
return query(url, projection, selection, selectionArgs, sortOrder, null);
try {
return query(url, projection, selection, selectionArgs, sortOrder, null);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#query ContentProvider.query} */
@ -64,41 +80,90 @@ public class ContentProviderClient {
remoteCancellationSignal = mContentProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder,
remoteCancellationSignal);
try {
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder,
remoteCancellationSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#getType ContentProvider.getType} */
public String getType(Uri url) throws RemoteException {
return mContentProvider.getType(url);
try {
return mContentProvider.getType(url);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
return mContentProvider.getStreamTypes(url, mimeTypeFilter);
try {
return mContentProvider.getStreamTypes(url, mimeTypeFilter);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#insert ContentProvider.insert} */
public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException {
return mContentProvider.insert(url, initialValues);
try {
return mContentProvider.insert(url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
return mContentProvider.bulkInsert(url, initialValues);
try {
return mContentProvider.bulkInsert(url, initialValues);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#delete ContentProvider.delete} */
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
return mContentProvider.delete(url, selection, selectionArgs);
try {
return mContentProvider.delete(url, selection, selectionArgs);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#update ContentProvider.update} */
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
return mContentProvider.update(url, values, selection, selectionArgs);
try {
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)
throws RemoteException, FileNotFoundException {
return mContentProvider.openFile(url, mode);
try {
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)
throws RemoteException, FileNotFoundException {
return mContentProvider.openAssetFile(url, mode);
try {
return mContentProvider.openAssetFile(url, mode);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
String mimeType, Bundle opts)
throws RemoteException, FileNotFoundException {
return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
try {
return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return mContentProvider.applyBatch(operations);
try {
return mContentProvider.applyBatch(operations);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
}
throw e;
}
}
/**
@ -144,10 +237,16 @@ public class ContentProviderClient {
* @return true if this was release, false if it was already released
*/
public boolean release() {
if (mStable) {
return mContentResolver.releaseProvider(mContentProvider);
} else {
return mContentResolver.releaseUnstableProvider(mContentProvider);
synchronized (this) {
if (mReleased) {
throw new IllegalStateException("Already released");
}
mReleased = true;
if (mStable) {
return mContentResolver.releaseProvider(mContentProvider);
} else {
return mContentResolver.releaseUnstableProvider(mContentProvider);
}
}
}

View File

@ -20,27 +20,24 @@ import dalvik.system.CloseGuard;
import android.accounts.Account;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.content.ContentProvider.Transport;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.IContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.EventLog;
@ -202,6 +199,8 @@ public abstract class ContentResolver {
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
/** @hide */
public abstract boolean releaseUnstableProvider(IContentProvider icp);
/** @hide */
public abstract void unstableProviderDied(IContentProvider icp);
/**
* 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
*/
public final String getType(Uri url) {
// XXX would like to have an acquireExistingUnstableProvider for this.
IContentProvider provider = acquireExistingProvider(url);
if (provider != null) {
try {
@ -351,23 +351,37 @@ public abstract class ContentResolver {
public final Cursor query(final Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
CancellationSignal cancellationSignal) {
IContentProvider provider = acquireProvider(uri);
if (provider == null) {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = provider.createCancellationSignal();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
Cursor qCursor = provider.query(uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
Cursor qCursor;
try {
qCursor = unstableProvider.query(uri, projection,
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) {
releaseProvider(provider);
return null;
}
// force query execution
@ -375,16 +389,21 @@ public abstract class ContentResolver {
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
// 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) {
releaseProvider(provider);
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} catch (RuntimeException e) {
releaseProvider(provider);
throw e;
} finally {
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
}
}
@ -592,49 +611,63 @@ public abstract class ContentResolver {
if ("r".equals(mode)) {
return openTypedAssetFileDescriptor(uri, "*/*", null);
} else {
int n = 0;
while (true) {
n++;
IContentProvider provider = acquireUnstableProvider(uri);
if (provider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
IContentProvider stableProvider = null;
AssetFileDescriptor fd = null;
try {
try {
AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
fd = unstableProvider.openAssetFile(uri, mode);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
fd.getParcelFileDescriptor(), provider);
// Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed.
provider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength());
} 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;
} 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);
}
// Whatever, whatever, we'll go away.
throw new FileNotFoundException("Dead content provider: " + uri);
} catch (FileNotFoundException e) {
throw e;
} finally {
if (provider != null) {
releaseUnstableProvider(provider);
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(
fd.getParcelFileDescriptor(), stableProvider);
// Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed.
stableProvider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength());
} catch (RemoteException e) {
// Whatever, whatever, we'll go away.
throw new FileNotFoundException(
"Failed opening content provider: " + uri);
} catch (FileNotFoundException e) {
throw e;
} finally {
if (stableProvider != null) {
releaseProvider(stableProvider);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
}
}
}
@ -670,49 +703,63 @@ public abstract class ContentResolver {
*/
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
String mimeType, Bundle opts) throws FileNotFoundException {
int n = 0;
while (true) {
n++;
IContentProvider provider = acquireUnstableProvider(uri);
if (provider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
IContentProvider stableProvider = null;
AssetFileDescriptor fd = null;
try {
try {
AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
fd = unstableProvider.openTypedAssetFile(uri, mimeType, opts);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
}
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
fd.getParcelFileDescriptor(), provider);
// Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed.
provider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength());
} 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;
} 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);
}
// Whatever, whatever, we'll go away.
throw new FileNotFoundException("Dead content provider: " + uri);
} catch (FileNotFoundException e) {
throw e;
} finally {
if (provider != null) {
releaseUnstableProvider(provider);
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(
fd.getParcelFileDescriptor(), stableProvider);
// Success! Don't release the provider when exiting, let
// ParcelFileDescriptorInner do that when it is closed.
stableProvider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength());
} catch (RemoteException e) {
// Whatever, whatever, we'll go away.
throw new FileNotFoundException(
"Failed opening content provider: " + uri);
} catch (FileNotFoundException e) {
throw e;
} finally {
if (stableProvider != null) {
releaseProvider(stableProvider);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
}
}
@ -1061,7 +1108,7 @@ public abstract class ContentResolver {
if (name == 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
* 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
* 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) {
IContentProvider provider = acquireProvider(uri);
IContentProvider provider = acquireUnstableProvider(uri);
if (provider != null) {
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
* 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
* 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) {
IContentProvider provider = acquireProvider(name);
IContentProvider provider = acquireUnstableProvider(name);
if (provider != null) {
return new ContentProviderClient(this, provider, false);
}
@ -1780,7 +1837,6 @@ public abstract class ContentResolver {
private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
private final IContentProvider mContentProvider;
public static final String TAG="ParcelFileDescriptorInner";
private boolean mReleaseProviderFlag = false;
ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
@ -1792,7 +1848,7 @@ public abstract class ContentResolver {
public void close() throws IOException {
if(!mReleaseProviderFlag) {
super.close();
ContentResolver.this.releaseUnstableProvider(mContentProvider);
ContentResolver.this.releaseProvider(mContentProvider);
mReleaseProviderFlag = true;
}
}

View File

@ -62,6 +62,7 @@ import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.IContentProvider;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
@ -1810,12 +1811,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
if (app.conProviders.size() > 0) {
for (ContentProviderRecord cpr : app.conProviders.keySet()) {
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
updateLruProcessInternalLocked(cpr.proc, oomAdj,
updateActivityTime, i+1);
}
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
updateLruProcessInternalLocked(cpr.proc, oomAdj,
updateActivityTime, i+1);
}
}
@ -3742,7 +3742,7 @@ public final class ActivityManagerService extends ActivityManagerNative
N = providers.size();
for (i=0; i<N; i++) {
removeDyingProviderLocked(null, providers.get(i));
removeDyingProviderLocked(null, providers.get(i), true);
}
if (doit) {
@ -6058,53 +6058,72 @@ public final class ActivityManagerService extends ActivityManagerNative
return msg;
}
boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
IBinder externalProcessToken) {
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
if (r != null) {
Integer cnt = r.conProviders.get(cpr);
if (DEBUG_PROVIDER) Slog.v(TAG,
"Adding provider requested by "
+ r.processName + " from process "
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
+ " cnt=" + (cnt == null ? 1 : cnt));
if (cnt == null) {
cpr.clients.add(r);
r.conProviders.put(cpr, new Integer(1));
return true;
} else {
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
for (int i=0; i<r.conProviders.size(); i++) {
ContentProviderConnection conn = r.conProviders.get(i);
if (conn.provider == cpr) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"Adding provider requested by "
+ r.processName + " from process "
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
if (stable) {
conn.stableCount++;
conn.numStableIncs++;
} else {
conn.unstableCount++;
conn.numUnstableIncs++;
}
return conn;
}
}
} else {
cpr.addExternalProcessHandleLocked(externalProcessToken);
ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
if (stable) {
conn.stableCount = 1;
conn.numStableIncs = 1;
} else {
conn.unstableCount = 1;
conn.numUnstableIncs = 1;
}
cpr.connections.add(conn);
r.conProviders.add(conn);
return conn;
}
return false;
cpr.addExternalProcessHandleLocked(externalProcessToken);
return null;
}
boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
IBinder externalProcessToken) {
if (r != null) {
Integer cnt = r.conProviders.get(cpr);
boolean decProviderCountLocked(ContentProviderConnection conn,
ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
if (conn != null) {
cpr = conn.provider;
if (DEBUG_PROVIDER) Slog.v(TAG,
"Removing provider requested by "
+ r.processName + " from process "
+ conn.client.processName + " from process "
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
+ " cnt=" + cnt);
if (cnt == null || cnt.intValue() <= 1) {
cpr.clients.remove(r);
r.conProviders.remove(cpr);
return true;
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
if (stable) {
conn.stableCount--;
} else {
r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
conn.unstableCount--;
}
} else {
cpr.removeExternalProcessHandleLocked(externalProcessToken);
if (conn.stableCount == 0 && conn.unstableCount == 0) {
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
return true;
}
return false;
}
cpr.removeExternalProcessHandleLocked(externalProcessToken);
return false;
}
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token) {
String name, IBinder token, boolean stable) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this) {
@ -6135,20 +6154,19 @@ public final class ActivityManagerService extends ActivityManagerNative
// of being published... but it is also allowed to run
// in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance.
if (cpr.provider != null) {
// don't give caller the provider object, it needs
// to make its own.
cpr = new ContentProviderRecord(cpr);
}
return cpr;
ContentProviderHolder holder = cpr.newHolder(null);
// don't give caller the provider object, it needs
// to make its own.
holder.provider = null;
return holder;
}
final long origId = Binder.clearCallingIdentity();
// In this case the provider instance already exists, so we can
// return it right away.
final boolean countChanged = incProviderCount(r, cpr, token);
if (countChanged) {
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus
@ -6181,7 +6199,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.i(TAG,
"Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCount(r, cpr, token);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);
if (!lastRef) {
// This wasn't the last ref our process had on
@ -6189,6 +6207,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
providerRunning = false;
conn = null;
}
}
@ -6251,7 +6270,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr;
return cpr.newHolder(null);
}
if (DEBUG_PROVIDER) {
@ -6312,7 +6331,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mProviderMap.putProviderByName(name, cpr);
incProviderCount(r, cpr, token);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
}
@ -6334,16 +6356,23 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+ cpr.launchingApp);
}
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr;
return cpr != null ? cpr.newHolder(conn) : null;
}
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name) {
IApplicationThread caller, String name, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
@ -6352,7 +6381,7 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
return getContentProviderImpl(caller, name, null);
return getContentProviderImpl(caller, name, null, stable);
}
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
@ -6362,45 +6391,30 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
return getContentProviderImpl(null, name, token);
return getContentProviderImpl(null, name, token, true);
}
/**
* Drop a content provider from a ProcessRecord's bookkeeping
* @param cpr
*/
public void removeContentProvider(IApplicationThread caller, String name) {
public void removeContentProvider(IBinder connection, boolean stable) {
enforceNotIsolatedCaller("removeContentProvider");
synchronized (this) {
int userId = UserId.getUserId(Binder.getCallingUid());
ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
" provider not found in providers list");
return;
ContentProviderConnection conn;
try {
conn = (ContentProviderConnection)connection;
} catch (ClassCastException e) {
String msg ="removeContentProvider: " + connection
+ " not a ContentProviderConnection";
Slog.w(TAG, msg);
throw new IllegalArgumentException(msg);
}
final ProcessRecord r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller +
" when removing content provider " + name);
if (conn == null) {
throw new NullPointerException("connection is null");
}
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ r.info.processName + " from process "
+ localCpr.appInfo.processName);
if (localCpr.launchingApp == r) {
//should not happen. taken care of as a local provider
Slog.w(TAG, "removeContentProvider called on local provider: "
+ cpr.info.name + " in process " + r.processName);
return;
} else {
if (decProviderCount(r, localCpr, null)) {
updateOomAdjLocked();
}
if (decProviderCountLocked(conn, null, null, stable)) {
updateOomAdjLocked();
}
}
}
@ -6447,7 +6461,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
enforceNotIsolatedCaller("publishContentProviders");
synchronized(this) {
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (DEBUG_MU)
Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
@ -6499,6 +6513,103 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
public boolean refContentProvider(IBinder connection, int stable, int unstable) {
ContentProviderConnection conn;
try {
conn = (ContentProviderConnection)connection;
} catch (ClassCastException e) {
String msg ="refContentProvider: " + connection
+ " not a ContentProviderConnection";
Slog.w(TAG, msg);
throw new IllegalArgumentException(msg);
}
if (conn == null) {
throw new NullPointerException("connection is null");
}
synchronized (this) {
if (stable > 0) {
conn.numStableIncs += stable;
}
stable = conn.stableCount + stable;
if (stable < 0) {
throw new IllegalStateException("stableCount < 0: " + stable);
}
if (unstable > 0) {
conn.numUnstableIncs += unstable;
}
unstable = conn.unstableCount + unstable;
if (unstable < 0) {
throw new IllegalStateException("unstableCount < 0: " + unstable);
}
if ((stable+unstable) <= 0) {
throw new IllegalStateException("ref counts can't go to zero here: stable="
+ stable + " unstable=" + unstable);
}
conn.stableCount = stable;
conn.unstableCount = unstable;
return !conn.dead;
}
}
public void unstableProviderDied(IBinder connection) {
ContentProviderConnection conn;
try {
conn = (ContentProviderConnection)connection;
} catch (ClassCastException e) {
String msg ="refContentProvider: " + connection
+ " not a ContentProviderConnection";
Slog.w(TAG, msg);
throw new IllegalArgumentException(msg);
}
if (conn == null) {
throw new NullPointerException("connection is null");
}
// Safely retrieve the content provider associated with the connection.
IContentProvider provider;
synchronized (this) {
provider = conn.provider.provider;
}
if (provider == null) {
// Um, yeah, we're way ahead of you.
return;
}
// Make sure the caller is being honest with us.
if (provider.asBinder().pingBinder()) {
// Er, no, still looks good to us.
synchronized (this) {
Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
+ " says " + conn + " died, but we don't agree");
return;
}
}
// Well look at that! It's dead!
synchronized (this) {
if (conn.provider.provider != provider) {
// But something changed... good enough.
return;
}
ProcessRecord proc = conn.provider.proc;
if (proc == null || proc.thread == null) {
// Seems like the process is already cleaned up.
return;
}
// As far as we're concerned, this is just like receiving a
// death notification... just a bit prematurely.
Slog.i(TAG, "Process " + proc.processName + " (pid " + proc.pid
+ ") early provider death");
appDiedLocked(proc, proc.pid, proc.thread);
}
}
public static final void installSystemProviders() {
List<ProviderInfo> providers;
synchronized (mSelf) {
@ -10515,36 +10626,62 @@ public final class ActivityManagerService extends ActivityManagerNative
app.executingServices.clear();
}
private final void removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
}
mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
}
Iterator<ProcessRecord> cit = cpr.clients.iterator();
while (cit.hasNext()) {
ProcessRecord capp = cit.next();
if (!capp.persistent && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
Slog.i(TAG, "Kill " + capp.processName
+ " (pid " + capp.pid + "): provider " + cpr.info.name
+ " in dying process " + (proc != null ? proc.processName : "??"));
EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
capp.processName, capp.setAdj, "dying provider "
+ cpr.name.toShortString());
Process.killProcessQuiet(capp.pid);
private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr, boolean always) {
final boolean inLaunching = mLaunchingProviders.contains(cpr);
if (!inLaunching || always) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
}
mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
}
}
mLaunchingProviders.remove(cpr);
for (int i=0; i<cpr.connections.size(); i++) {
ContentProviderConnection conn = cpr.connections.get(i);
if (conn.waiting) {
// If this connection is waiting for the provider, then we don't
// need to mess with its process unless we are always removing
// or for some reason the provider is not currently launching.
if (inLaunching && !always) {
continue;
}
}
ProcessRecord capp = conn.client;
conn.dead = true;
if (conn.stableCount > 0) {
if (!capp.persistent && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
Slog.i(TAG, "Kill " + capp.processName
+ " (pid " + capp.pid + "): provider " + cpr.info.name
+ " in dying process " + (proc != null ? proc.processName : "??"));
EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
capp.processName, capp.setAdj, "dying provider "
+ cpr.name.toShortString());
Process.killProcessQuiet(capp.pid);
}
} else if (capp.thread != null && conn.provider.provider != null) {
try {
capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
} catch (RemoteException e) {
}
// In the protocol here, we don't expect the client to correctly
// clean up this connection, we'll just remove it.
cpr.connections.remove(i);
conn.client.conProviders.remove(conn);
}
}
if (inLaunching && always) {
mLaunchingProviders.remove(cpr);
}
return inLaunching;
}
/**
@ -10590,34 +10727,21 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean restart = false;
int NL = mLaunchingProviders.size();
// Remove published content providers.
if (!app.pubProviders.isEmpty()) {
Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
while (it.hasNext()) {
ContentProviderRecord cpr = it.next();
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.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();
}
@ -10629,10 +10753,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Unregister from connected content providers.
if (!app.conProviders.isEmpty()) {
Iterator it = app.conProviders.keySet().iterator();
while (it.hasNext()) {
ContentProviderRecord cpr = (ContentProviderRecord)it.next();
cpr.clients.remove(app);
for (int i=0; i<app.conProviders.size(); i++) {
ContentProviderConnection conn = app.conProviders.get(i);
conn.provider.connections.remove(conn);
}
app.conProviders.clear();
}
@ -10643,10 +10766,10 @@ public final class ActivityManagerService extends ActivityManagerNative
// XXX Commented out for now. Trying to figure out a way to reproduce
// the actual situation to identify what is actually going on.
if (false) {
for (int i=0; i<NL; i++) {
for (int i=0; i<mLaunchingProviders.size(); i++) {
ContentProviderRecord cpr = (ContentProviderRecord)
mLaunchingProviders.get(i);
if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) {
if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
@ -10743,7 +10866,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!alwaysBad && !app.bad) {
restart = true;
} else {
removeDyingProviderLocked(app, cpr);
removeDyingProviderLocked(app, cpr, true);
NL = mLaunchingProviders.size();
}
}
@ -13948,48 +14071,49 @@ public final class ActivityManagerService extends ActivityManagerNative
while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
ContentProviderRecord cpr = jt.next();
if (cpr.clients.size() != 0) {
Iterator<ProcessRecord> kt = cpr.clients.iterator();
while (kt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
ProcessRecord client = kt.next();
if (client == app) {
// Being our own client is not interesting.
continue;
for (int i = cpr.connections.size()-1;
i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
i--) {
ContentProviderConnection conn = cpr.connections.get(i);
ProcessRecord client = conn.client;
if (client == app) {
// Being our own client is not interesting.
continue;
}
int myHiddenAdj = hiddenAdj;
if (myHiddenAdj > client.hiddenAdj) {
if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
myHiddenAdj = client.hiddenAdj;
} else {
myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
}
int myHiddenAdj = hiddenAdj;
if (myHiddenAdj > client.hiddenAdj) {
if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
myHiddenAdj = client.hiddenAdj;
} else {
myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
}
}
int clientAdj = computeOomAdjLocked(
client, myHiddenAdj, TOP_APP, true, doingAll);
if (adj > clientAdj) {
if (app.hasShownUi && app != mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
app.adjType = "bg-ui-provider";
} else {
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
app.adjType = "provider";
}
int clientAdj = computeOomAdjLocked(
client, myHiddenAdj, TOP_APP, true, doingAll);
if (adj > clientAdj) {
if (app.hasShownUi && app != mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
app.adjType = "bg-ui-provider";
} else {
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
app.adjType = "provider";
}
if (!client.hidden) {
app.hidden = false;
}
if (client.keeping) {
app.keeping = true;
}
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_PROVIDER_IN_USE;
app.adjSource = client;
app.adjSourceOom = clientAdj;
app.adjTarget = cpr.name;
if (!client.hidden) {
app.hidden = false;
}
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
if (client.keeping) {
app.keeping = true;
}
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_PROVIDER_IN_USE;
app.adjSource = client;
app.adjSourceOom = clientAdj;
app.adjTarget = cpr.name;
}
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
schedGroup = Process.THREAD_GROUP_DEFAULT;
}
}
// If the provider has external (non-framework) process

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

View File

@ -125,8 +125,8 @@ class ProcessRecord {
final HashMap<String, ContentProviderRecord> pubProviders
= new HashMap<String, ContentProviderRecord>();
// All ContentProviderRecord process is using
final HashMap<ContentProviderRecord, Integer> conProviders
= new HashMap<ContentProviderRecord, Integer>();
final ArrayList<ContentProviderConnection> conProviders
= new ArrayList<ContentProviderConnection>();
boolean persistent; // always keep this application running?
boolean crashing; // are we in the process of crashing?
@ -257,25 +257,47 @@ class ProcessRecord {
pw.println();
}
if (activities.size() > 0) {
pw.print(prefix); pw.print("activities="); pw.println(activities);
pw.print(prefix); pw.println("Activities:");
for (int i=0; i<activities.size(); i++) {
pw.print(prefix); pw.print(" - "); pw.println(activities.get(i));
}
}
if (services.size() > 0) {
pw.print(prefix); pw.print("services="); pw.println(services);
pw.print(prefix); pw.println("Services:");
for (ServiceRecord sr : services) {
pw.print(prefix); pw.print(" - "); pw.println(sr);
}
}
if (executingServices.size() > 0) {
pw.print(prefix); pw.print("executingServices="); pw.println(executingServices);
pw.print(prefix); pw.println("Executing Services:");
for (ServiceRecord sr : executingServices) {
pw.print(prefix); pw.print(" - "); pw.println(sr);
}
}
if (connections.size() > 0) {
pw.print(prefix); pw.print("connections="); pw.println(connections);
pw.print(prefix); pw.println("Connections:");
for (ConnectionRecord cr : connections) {
pw.print(prefix); pw.print(" - "); pw.println(cr);
}
}
if (pubProviders.size() > 0) {
pw.print(prefix); pw.print("pubProviders="); pw.println(pubProviders);
pw.print(prefix); pw.println("Published Providers:");
for (HashMap.Entry<String, ContentProviderRecord> ent : pubProviders.entrySet()) {
pw.print(prefix); pw.print(" - "); pw.println(ent.getKey());
pw.print(prefix); pw.print(" -> "); pw.println(ent.getValue());
}
}
if (conProviders.size() > 0) {
pw.print(prefix); pw.print("conProviders="); pw.println(conProviders);
pw.print(prefix); pw.println("Connected Providers:");
for (int i=0; i<conProviders.size(); i++) {
pw.print(prefix); pw.print(" - "); pw.println(conProviders.get(i).toShortString());
}
}
if (receivers.size() > 0) {
pw.print(prefix); pw.print("receivers="); pw.println(receivers);
pw.print(prefix); pw.println("Receivers:");
for (ReceiverList rl : receivers) {
pw.print(prefix); pw.print(" - "); pw.println(rl);
}
}
}

View File

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

View File

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

View File

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