summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/ActivityManagerNative.java67
-rw-r--r--core/java/android/app/ActivityThread.java473
-rw-r--r--core/java/android/app/ApplicationThreadNative.java17
-rw-r--r--core/java/android/app/ContextImpl.java15
-rw-r--r--core/java/android/app/IActivityManager.java14
-rw-r--r--core/java/android/app/IApplicationThread.java2
-rw-r--r--core/java/android/app/Instrumentation.java7
-rw-r--r--core/java/android/content/ContentProviderClient.java133
-rw-r--r--core/java/android/content/ContentResolver.java242
9 files changed, 672 insertions, 298 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4506546..2ed93f4 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -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();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 33e639e..a457e3c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -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");
+ holder = installProvider(c, holder, holder.info,
+ true /*noisy*/, holder.noReleaseNeeded, stable);
+ return holder.provider;
+ }
+
+ 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
+ }
}
- try {
- ActivityManagerNative.getDefault().removeContentProvider(
- getApplicationThread(), name);
- } catch (RemoteException ex) {
+ } 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
+ }
+ }
}
}
- return provider;
}
- public final IContentProvider acquireExistingProvider(Context c, String name) {
+ 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,43 +4431,72 @@ 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;
- }
- }
}
}
}
- 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
+ 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 removeDeadProvider(String name, IContentProvider provider) {
+ final void handleUnstableProviderDied(IBinder provider, boolean fromClient) {
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);
+ 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
+ }
}
}
}
}
+ 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;
+ }
+
/**
* Installs the provider.
*
@@ -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);
}
+ IActivityManager.ContentProviderHolder retHolder;
+
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.
+ if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
+ + " / " + info.name);
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);
- }
- }
-
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) {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 437362b..3e726e0 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -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();
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 299e408..4c35a8c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -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;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cf304df..609a047 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -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;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 70029d2..f60cfd6 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -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;
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 75c6e11..cad4b01 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -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) {
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 423f1f6..5c315ce 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -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);
+ }
}
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f509fd8..34b5a30 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -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;
}
}