diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 67 | ||||
-rw-r--r-- | core/java/android/app/ActivityThread.java | 473 | ||||
-rw-r--r-- | core/java/android/app/ApplicationThreadNative.java | 17 | ||||
-rw-r--r-- | core/java/android/app/ContextImpl.java | 15 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 14 | ||||
-rw-r--r-- | core/java/android/app/IApplicationThread.java | 2 | ||||
-rw-r--r-- | core/java/android/app/Instrumentation.java | 7 | ||||
-rw-r--r-- | core/java/android/content/ContentProviderClient.java | 133 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 242 |
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; } } |