Loading core/java/android/app/ActivityThread.java +190 −134 Original line number Diff line number Diff line Loading @@ -2734,8 +2734,9 @@ public final class ActivityThread { CharSequence description; } private class ProviderRefCount { private static final class ProviderRefCount { public int count; ProviderRefCount(int pCount) { count = pCount; } Loading Loading @@ -3988,16 +3989,14 @@ public final class ActivityThread { buf.append(": "); buf.append(cpi.name); Log.i(TAG, buf.toString()); IContentProvider cp = installProvider(context, null, cpi, false); IContentProvider cp = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/); if (cp != null) { IActivityManager.ContentProviderHolder cph = new IActivityManager.ContentProviderHolder(cpi); cph.provider = cp; cph.noReleaseNeeded = true; results.add(cph); // Don't ever unload this provider from the process. synchronized(mProviderMap) { mProviderRefCountMap.put(cp.asBinder(), new ProviderRefCount(10000)); } } } Loading @@ -4008,22 +4007,18 @@ public final class ActivityThread { } } private IContentProvider getExistingProvider(Context context, String name) { synchronized(mProviderMap) { final ProviderClientRecord pr = mProviderMap.get(name); if (pr != null) { return pr.mProvider; } return null; } } private IContentProvider getProvider(Context context, String name) { IContentProvider existing = getExistingProvider(context, name); if (existing != null) { return existing; public final IContentProvider acquireProvider(Context c, String name) { IContentProvider provider = acquireExistingProvider(c, name); if (provider != null) { return provider; } // There is a possible race here. Another thread may try to acquire // the same provider at the same time. When this happens, we want to ensure // that the first one wins. // Note that we cannot hold the lock while acquiring and installing the // provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. IActivityManager.ContentProviderHolder holder = null; try { holder = ActivityManagerNative.getDefault().getContentProvider( Loading @@ -4035,135 +4030,136 @@ public final class ActivityThread { return null; } IContentProvider prov = installProvider(context, holder.provider, holder.info, true); //Slog.i(TAG, "noReleaseNeeded=" + holder.noReleaseNeeded); if (holder.noReleaseNeeded || holder.provider == null) { // We are not going to release the provider if it is an external // provider that doesn't care about being released, or if it is // a local provider running in this process. //Slog.i(TAG, "*** NO RELEASE NEEDED"); synchronized(mProviderMap) { mProviderRefCountMap.put(prov.asBinder(), new ProviderRefCount(10000)); // Install provider will increment the reference count for us, and break // any ties in the race. provider = installProvider(c, holder.provider, holder.info, true /*noisy*/, holder.noReleaseNeeded); if (holder.provider != null && provider != holder.provider) { if (localLOGV) { Slog.v(TAG, "acquireProvider: lost the race, releasing extraneous " + "reference to the content provider"); } try { ActivityManagerNative.getDefault().removeContentProvider( getApplicationThread(), name); } catch (RemoteException ex) { } return prov; } public final IContentProvider acquireProvider(Context c, String name) { IContentProvider provider = getProvider(c, name); if(provider == null) return null; IBinder jBinder = provider.asBinder(); synchronized(mProviderMap) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if(prc == null) { mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); } else { prc.count++; } //end else } //end synchronized return provider; } public final IContentProvider acquireExistingProvider(Context c, String name) { IContentProvider provider = getExistingProvider(c, name); if(provider == null) synchronized (mProviderMap) { ProviderClientRecord pr = mProviderMap.get(name); if (pr == null) { return null; } IContentProvider provider = pr.mProvider; IBinder jBinder = provider.asBinder(); synchronized(mProviderMap) { // Only increment the ref count if we have one. If we don't then the // provider is not reference counted and never needs to be released. ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if(prc == null) { mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); } else { prc.count++; } //end else } //end synchronized 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); } } return provider; } } public final boolean releaseProvider(IContentProvider provider) { if(provider == null) { return false; } IBinder jBinder = provider.asBinder(); synchronized (mProviderMap) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc == null) { if(localLOGV) Slog.v(TAG, "releaseProvider::Weird shouldn't be here"); // The provider has no ref count, no release is needed. return false; } else { prc.count--; } if (prc.count == 0) { if (localLOGV) Slog.v(TAG, "releaseProvider: ref count already 0, how?"); return false; } prc.count -= 1; if (prc.count == 0) { // Schedule the actual remove asynchronously, since we // don't know the context this will be called in. // 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); } //end if } //end else } //end synchronized } return true; } } final void completeRemoveProvider(IContentProvider provider) { IBinder jBinder = provider.asBinder(); String name = null; String remoteProviderName = null; synchronized(mProviderMap) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if(prc != null && prc.count == 0) { mProviderRefCountMap.remove(jBinder); //invoke removeProvider to dereference provider name = removeProviderLocked(provider); } } if (name != null) { try { if(localLOGV) Slog.v(TAG, "removeProvider::Invoking " + "ActivityManagerNative.removeContentProvider(" + name); ActivityManagerNative.getDefault().removeContentProvider( getApplicationThread(), name); } catch (RemoteException e) { //do nothing content provider object is dead any way } //end catch } 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; } public final String removeProviderLocked(IContentProvider provider) { if (provider == null) { return null; if (prc.count != 0) { // 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, " + "provider still in use"); return; } IBinder providerBinder = provider.asBinder(); String name = null; mProviderRefCountMap.remove(jBinder); // remove the provider from mProviderMap Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator(); while (iter.hasNext()) { ProviderClientRecord pr = iter.next(); IBinder myBinder = pr.mProvider.asBinder(); if (myBinder == providerBinder) { //find if its published by this process itself if(pr.mLocalProvider != null) { if(localLOGV) Slog.i(TAG, "removeProvider::found local provider returning"); return name; } if(localLOGV) Slog.v(TAG, "removeProvider::Not local provider Unlinking " + "death recipient"); //content provider is in another process myBinder.unlinkToDeath(pr, 0); if (myBinder == jBinder) { iter.remove(); //invoke remove only once for the very first name seen if(name == null) { name = pr.mName; if (pr.mLocalProvider == null) { myBinder.unlinkToDeath(pr, 0); if (remoteProviderName == null) { remoteProviderName = pr.mName; } } } } } } //end if myBinder } //end while iter return name; if (remoteProviderName != null) { try { if (localLOGV) { Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative." + "removeContentProvider(" + remoteProviderName + ")"); } ActivityManagerNative.getDefault().removeContentProvider( getApplicationThread(), remoteProviderName); } catch (RemoteException e) { //do nothing content provider object is dead any way } } } final void removeDeadProvider(String name, IContentProvider provider) { Loading @@ -4179,8 +4175,23 @@ public final class ActivityThread { } } /** * Installs the provider. * * Providers that are local to the process or that come from the system server * may be installed permanently which is indicated by setting noReleaseNeeded to true. * Other remote providers are reference counted. The initial reference count * for all reference counted providers is one. Providers that are not reference * counted do not have a reference count (at all). * * This method detects when a provider has already been installed. When this happens, * it increments the reference count of the existing provider (if appropriate) * 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) { IContentProvider provider, ProviderInfo info, boolean noisy, boolean noReleaseNeeded) { ContentProvider localProvider = null; if (provider == null) { if (noisy) { Loading Loading @@ -4238,24 +4249,69 @@ public final class ActivityThread { } synchronized (mProviderMap) { // Cache the pointer for the remote provider. // There is a possibility that this thread raced with another thread to // add the provider. If we find another thread got there first then we // just get out of the way and return the original provider. IBinder jBinder = provider.asBinder(); String names[] = PATTERN_SEMICOLON.split(info.authority); for (int i = 0; i < names.length; i++) { ProviderClientRecord pr = new ProviderClientRecord(names[i], provider, localProvider); 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 { provider.asBinder().linkToDeath(pr, 0); mProviderMap.put(names[i], pr); 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) { mLocalProviders.put(provider.asBinder(), new ProviderClientRecord(null, provider, localProvider)); ProviderClientRecord pr = mLocalProviders.get(jBinder); if (pr != null) { if (localLOGV) { Slog.v(TAG, "installProvider: lost the race, " + "using existing local provider"); } provider = pr.mProvider; } else { pr = new ProviderClientRecord(null, provider, localProvider); mLocalProviders.put(jBinder, pr); } } if (!noReleaseNeeded) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null) { if (localLOGV) { Slog.v(TAG, "installProvider: lost the race, incrementing ref count"); } prc.count += 1; if (prc.count == 1) { if (localLOGV) { Slog.v(TAG, "installProvider: " + "snatched provider from the jaws of death"); } // Because the provider previously had a reference count of zero, // it was scheduled to be removed. Cancel that. mH.removeMessages(H.REMOVE_PROVIDER, provider); } } else { mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); } } } return provider; } Loading Loading
core/java/android/app/ActivityThread.java +190 −134 Original line number Diff line number Diff line Loading @@ -2734,8 +2734,9 @@ public final class ActivityThread { CharSequence description; } private class ProviderRefCount { private static final class ProviderRefCount { public int count; ProviderRefCount(int pCount) { count = pCount; } Loading Loading @@ -3988,16 +3989,14 @@ public final class ActivityThread { buf.append(": "); buf.append(cpi.name); Log.i(TAG, buf.toString()); IContentProvider cp = installProvider(context, null, cpi, false); IContentProvider cp = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/); if (cp != null) { IActivityManager.ContentProviderHolder cph = new IActivityManager.ContentProviderHolder(cpi); cph.provider = cp; cph.noReleaseNeeded = true; results.add(cph); // Don't ever unload this provider from the process. synchronized(mProviderMap) { mProviderRefCountMap.put(cp.asBinder(), new ProviderRefCount(10000)); } } } Loading @@ -4008,22 +4007,18 @@ public final class ActivityThread { } } private IContentProvider getExistingProvider(Context context, String name) { synchronized(mProviderMap) { final ProviderClientRecord pr = mProviderMap.get(name); if (pr != null) { return pr.mProvider; } return null; } } private IContentProvider getProvider(Context context, String name) { IContentProvider existing = getExistingProvider(context, name); if (existing != null) { return existing; public final IContentProvider acquireProvider(Context c, String name) { IContentProvider provider = acquireExistingProvider(c, name); if (provider != null) { return provider; } // There is a possible race here. Another thread may try to acquire // the same provider at the same time. When this happens, we want to ensure // that the first one wins. // Note that we cannot hold the lock while acquiring and installing the // provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. IActivityManager.ContentProviderHolder holder = null; try { holder = ActivityManagerNative.getDefault().getContentProvider( Loading @@ -4035,135 +4030,136 @@ public final class ActivityThread { return null; } IContentProvider prov = installProvider(context, holder.provider, holder.info, true); //Slog.i(TAG, "noReleaseNeeded=" + holder.noReleaseNeeded); if (holder.noReleaseNeeded || holder.provider == null) { // We are not going to release the provider if it is an external // provider that doesn't care about being released, or if it is // a local provider running in this process. //Slog.i(TAG, "*** NO RELEASE NEEDED"); synchronized(mProviderMap) { mProviderRefCountMap.put(prov.asBinder(), new ProviderRefCount(10000)); // Install provider will increment the reference count for us, and break // any ties in the race. provider = installProvider(c, holder.provider, holder.info, true /*noisy*/, holder.noReleaseNeeded); if (holder.provider != null && provider != holder.provider) { if (localLOGV) { Slog.v(TAG, "acquireProvider: lost the race, releasing extraneous " + "reference to the content provider"); } try { ActivityManagerNative.getDefault().removeContentProvider( getApplicationThread(), name); } catch (RemoteException ex) { } return prov; } public final IContentProvider acquireProvider(Context c, String name) { IContentProvider provider = getProvider(c, name); if(provider == null) return null; IBinder jBinder = provider.asBinder(); synchronized(mProviderMap) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if(prc == null) { mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); } else { prc.count++; } //end else } //end synchronized return provider; } public final IContentProvider acquireExistingProvider(Context c, String name) { IContentProvider provider = getExistingProvider(c, name); if(provider == null) synchronized (mProviderMap) { ProviderClientRecord pr = mProviderMap.get(name); if (pr == null) { return null; } IContentProvider provider = pr.mProvider; IBinder jBinder = provider.asBinder(); synchronized(mProviderMap) { // Only increment the ref count if we have one. If we don't then the // provider is not reference counted and never needs to be released. ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if(prc == null) { mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); } else { prc.count++; } //end else } //end synchronized 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); } } return provider; } } public final boolean releaseProvider(IContentProvider provider) { if(provider == null) { return false; } IBinder jBinder = provider.asBinder(); synchronized (mProviderMap) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc == null) { if(localLOGV) Slog.v(TAG, "releaseProvider::Weird shouldn't be here"); // The provider has no ref count, no release is needed. return false; } else { prc.count--; } if (prc.count == 0) { if (localLOGV) Slog.v(TAG, "releaseProvider: ref count already 0, how?"); return false; } prc.count -= 1; if (prc.count == 0) { // Schedule the actual remove asynchronously, since we // don't know the context this will be called in. // 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); } //end if } //end else } //end synchronized } return true; } } final void completeRemoveProvider(IContentProvider provider) { IBinder jBinder = provider.asBinder(); String name = null; String remoteProviderName = null; synchronized(mProviderMap) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if(prc != null && prc.count == 0) { mProviderRefCountMap.remove(jBinder); //invoke removeProvider to dereference provider name = removeProviderLocked(provider); } } if (name != null) { try { if(localLOGV) Slog.v(TAG, "removeProvider::Invoking " + "ActivityManagerNative.removeContentProvider(" + name); ActivityManagerNative.getDefault().removeContentProvider( getApplicationThread(), name); } catch (RemoteException e) { //do nothing content provider object is dead any way } //end catch } 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; } public final String removeProviderLocked(IContentProvider provider) { if (provider == null) { return null; if (prc.count != 0) { // 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, " + "provider still in use"); return; } IBinder providerBinder = provider.asBinder(); String name = null; mProviderRefCountMap.remove(jBinder); // remove the provider from mProviderMap Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator(); while (iter.hasNext()) { ProviderClientRecord pr = iter.next(); IBinder myBinder = pr.mProvider.asBinder(); if (myBinder == providerBinder) { //find if its published by this process itself if(pr.mLocalProvider != null) { if(localLOGV) Slog.i(TAG, "removeProvider::found local provider returning"); return name; } if(localLOGV) Slog.v(TAG, "removeProvider::Not local provider Unlinking " + "death recipient"); //content provider is in another process myBinder.unlinkToDeath(pr, 0); if (myBinder == jBinder) { iter.remove(); //invoke remove only once for the very first name seen if(name == null) { name = pr.mName; if (pr.mLocalProvider == null) { myBinder.unlinkToDeath(pr, 0); if (remoteProviderName == null) { remoteProviderName = pr.mName; } } } } } } //end if myBinder } //end while iter return name; if (remoteProviderName != null) { try { if (localLOGV) { Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative." + "removeContentProvider(" + remoteProviderName + ")"); } ActivityManagerNative.getDefault().removeContentProvider( getApplicationThread(), remoteProviderName); } catch (RemoteException e) { //do nothing content provider object is dead any way } } } final void removeDeadProvider(String name, IContentProvider provider) { Loading @@ -4179,8 +4175,23 @@ public final class ActivityThread { } } /** * Installs the provider. * * Providers that are local to the process or that come from the system server * may be installed permanently which is indicated by setting noReleaseNeeded to true. * Other remote providers are reference counted. The initial reference count * for all reference counted providers is one. Providers that are not reference * counted do not have a reference count (at all). * * This method detects when a provider has already been installed. When this happens, * it increments the reference count of the existing provider (if appropriate) * 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) { IContentProvider provider, ProviderInfo info, boolean noisy, boolean noReleaseNeeded) { ContentProvider localProvider = null; if (provider == null) { if (noisy) { Loading Loading @@ -4238,24 +4249,69 @@ public final class ActivityThread { } synchronized (mProviderMap) { // Cache the pointer for the remote provider. // There is a possibility that this thread raced with another thread to // add the provider. If we find another thread got there first then we // just get out of the way and return the original provider. IBinder jBinder = provider.asBinder(); String names[] = PATTERN_SEMICOLON.split(info.authority); for (int i = 0; i < names.length; i++) { ProviderClientRecord pr = new ProviderClientRecord(names[i], provider, localProvider); 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 { provider.asBinder().linkToDeath(pr, 0); mProviderMap.put(names[i], pr); 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) { mLocalProviders.put(provider.asBinder(), new ProviderClientRecord(null, provider, localProvider)); ProviderClientRecord pr = mLocalProviders.get(jBinder); if (pr != null) { if (localLOGV) { Slog.v(TAG, "installProvider: lost the race, " + "using existing local provider"); } provider = pr.mProvider; } else { pr = new ProviderClientRecord(null, provider, localProvider); mLocalProviders.put(jBinder, pr); } } if (!noReleaseNeeded) { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc != null) { if (localLOGV) { Slog.v(TAG, "installProvider: lost the race, incrementing ref count"); } prc.count += 1; if (prc.count == 1) { if (localLOGV) { Slog.v(TAG, "installProvider: " + "snatched provider from the jaws of death"); } // Because the provider previously had a reference count of zero, // it was scheduled to be removed. Cancel that. mH.removeMessages(H.REMOVE_PROVIDER, provider); } } else { mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); } } } return provider; } Loading