Merge "Fix (mostly) issue #5109947: Race condition between retrieving a..." into jb-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ae5811c714
@ -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();
|
||||
|
@ -140,6 +140,7 @@ public final class ActivityThread {
|
||||
private static final boolean DEBUG_CONFIGURATION = false;
|
||||
private static final boolean DEBUG_SERVICE = false;
|
||||
private static final boolean DEBUG_MEMORY_TRIM = false;
|
||||
private static final boolean DEBUG_PROVIDER = false;
|
||||
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
|
||||
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
|
||||
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
|
||||
@ -210,6 +211,8 @@ public final class ActivityThread {
|
||||
= new HashMap<IBinder, ProviderRefCount>();
|
||||
final HashMap<IBinder, ProviderClientRecord> mLocalProviders
|
||||
= new HashMap<IBinder, ProviderClientRecord>();
|
||||
final HashMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
|
||||
= new HashMap<ComponentName, ProviderClientRecord>();
|
||||
|
||||
final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
|
||||
= new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
|
||||
@ -284,20 +287,19 @@ public final class ActivityThread {
|
||||
}
|
||||
}
|
||||
|
||||
final class ProviderClientRecord implements IBinder.DeathRecipient {
|
||||
final String mName;
|
||||
final class ProviderClientRecord {
|
||||
final String[] mNames;
|
||||
final IContentProvider mProvider;
|
||||
final ContentProvider mLocalProvider;
|
||||
final IActivityManager.ContentProviderHolder mHolder;
|
||||
|
||||
ProviderClientRecord(String name, IContentProvider provider,
|
||||
ContentProvider localProvider) {
|
||||
mName = name;
|
||||
ProviderClientRecord(String[] names, IContentProvider provider,
|
||||
ContentProvider localProvider,
|
||||
IActivityManager.ContentProviderHolder holder) {
|
||||
mNames = names;
|
||||
mProvider = provider;
|
||||
mLocalProvider = localProvider;
|
||||
}
|
||||
|
||||
public void binderDied() {
|
||||
removeDeadProvider(mName, mProvider);
|
||||
mHolder = holder;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1061,6 +1063,11 @@ public final class ActivityThread {
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unstableProviderDied(IBinder provider) {
|
||||
queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
|
||||
}
|
||||
|
||||
private void printRow(PrintWriter pw, String format, Object...objs) {
|
||||
pw.println(String.format(format, objs));
|
||||
}
|
||||
@ -1125,6 +1132,7 @@ public final class ActivityThread {
|
||||
public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
|
||||
public static final int TRIM_MEMORY = 140;
|
||||
public static final int DUMP_PROVIDER = 141;
|
||||
public static final int UNSTABLE_PROVIDER_DIED = 142;
|
||||
String codeToString(int code) {
|
||||
if (DEBUG_MESSAGES) {
|
||||
switch (code) {
|
||||
@ -1170,6 +1178,7 @@ public final class ActivityThread {
|
||||
case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
|
||||
case TRIM_MEMORY: return "TRIM_MEMORY";
|
||||
case DUMP_PROVIDER: return "DUMP_PROVIDER";
|
||||
case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
|
||||
}
|
||||
}
|
||||
return Integer.toString(code);
|
||||
@ -1337,7 +1346,7 @@ public final class ActivityThread {
|
||||
break;
|
||||
case REMOVE_PROVIDER:
|
||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove");
|
||||
completeRemoveProvider((IContentProvider)msg.obj);
|
||||
completeRemoveProvider((ProviderRefCount)msg.obj);
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
break;
|
||||
case ENABLE_JIT:
|
||||
@ -1377,6 +1386,9 @@ public final class ActivityThread {
|
||||
handleTrimMemory(msg.arg1);
|
||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||
break;
|
||||
case UNSTABLE_PROVIDER_DIED:
|
||||
handleUnstableProviderDied((IBinder)msg.obj, false);
|
||||
break;
|
||||
}
|
||||
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
|
||||
}
|
||||
@ -2867,10 +2879,24 @@ public final class ActivityThread {
|
||||
}
|
||||
|
||||
private static final class ProviderRefCount {
|
||||
public int count;
|
||||
public final IActivityManager.ContentProviderHolder holder;
|
||||
public final ProviderClientRecord client;
|
||||
public int stableCount;
|
||||
public int unstableCount;
|
||||
|
||||
ProviderRefCount(int pCount) {
|
||||
count = pCount;
|
||||
// When this is set, the stable and unstable ref counts are 0 and
|
||||
// we have a pending operation scheduled to remove the ref count
|
||||
// from the activity manager. On the activity manager we are still
|
||||
// holding an unstable ref, though it is not reflected in the counts
|
||||
// here.
|
||||
public boolean removePending;
|
||||
|
||||
ProviderRefCount(IActivityManager.ContentProviderHolder inHolder,
|
||||
ProviderClientRecord inClient, int sCount, int uCount) {
|
||||
holder = inHolder;
|
||||
client = inClient;
|
||||
stableCount = sCount;
|
||||
unstableCount = uCount;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4080,15 +4106,6 @@ public final class ActivityThread {
|
||||
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
|
||||
}
|
||||
|
||||
try {
|
||||
mInstrumentation.onCreate(data.instrumentationArgs);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Exception thrown in onCreate() of "
|
||||
+ data.instrumentationName + ": " + e.toString(), e);
|
||||
}
|
||||
|
||||
} else {
|
||||
mInstrumentation = new Instrumentation();
|
||||
}
|
||||
@ -4119,6 +4136,17 @@ public final class ActivityThread {
|
||||
}
|
||||
}
|
||||
|
||||
// Do this after providers, since instrumentation tests generally start their
|
||||
// test thread at this point, and we don't want that racing.
|
||||
try {
|
||||
mInstrumentation.onCreate(data.instrumentationArgs);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Exception thrown in onCreate() of "
|
||||
+ data.instrumentationName + ": " + e.toString(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
mInstrumentation.callApplicationOnCreate(app);
|
||||
} catch (Exception e) {
|
||||
@ -4159,12 +4187,9 @@ public final class ActivityThread {
|
||||
buf.append(": ");
|
||||
buf.append(cpi.name);
|
||||
Log.i(TAG, buf.toString());
|
||||
IContentProvider cp = installProvider(context, null, cpi,
|
||||
false /*noisy*/, true /*noReleaseNeeded*/);
|
||||
if (cp != null) {
|
||||
IActivityManager.ContentProviderHolder cph =
|
||||
new IActivityManager.ContentProviderHolder(cpi);
|
||||
cph.provider = cp;
|
||||
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
|
||||
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
|
||||
if (cph != null) {
|
||||
cph.noReleaseNeeded = true;
|
||||
results.add(cph);
|
||||
}
|
||||
@ -4177,8 +4202,8 @@ public final class ActivityThread {
|
||||
}
|
||||
}
|
||||
|
||||
public final IContentProvider acquireProvider(Context c, String name) {
|
||||
IContentProvider provider = acquireExistingProvider(c, name);
|
||||
public final IContentProvider acquireProvider(Context c, String name, boolean stable) {
|
||||
IContentProvider provider = acquireExistingProvider(c, name, stable);
|
||||
if (provider != null) {
|
||||
return provider;
|
||||
}
|
||||
@ -4192,7 +4217,7 @@ public final class ActivityThread {
|
||||
IActivityManager.ContentProviderHolder holder = null;
|
||||
try {
|
||||
holder = ActivityManagerNative.getDefault().getContentProvider(
|
||||
getApplicationThread(), name);
|
||||
getApplicationThread(), name, stable);
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
if (holder == null) {
|
||||
@ -4202,23 +4227,79 @@ public final class ActivityThread {
|
||||
|
||||
// Install provider will increment the reference count for us, and break
|
||||
// any ties in the race.
|
||||
provider = installProvider(c, holder.provider, holder.info,
|
||||
true /*noisy*/, holder.noReleaseNeeded);
|
||||
if (holder.provider != null && provider != holder.provider) {
|
||||
if (localLOGV) {
|
||||
Slog.v(TAG, "acquireProvider: lost the race, releasing extraneous "
|
||||
+ "reference to the content provider");
|
||||
}
|
||||
try {
|
||||
ActivityManagerNative.getDefault().removeContentProvider(
|
||||
getApplicationThread(), name);
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
}
|
||||
return provider;
|
||||
holder = installProvider(c, holder, holder.info,
|
||||
true /*noisy*/, holder.noReleaseNeeded, stable);
|
||||
return holder.provider;
|
||||
}
|
||||
|
||||
public final IContentProvider acquireExistingProvider(Context c, String name) {
|
||||
private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
|
||||
if (stable) {
|
||||
prc.stableCount += 1;
|
||||
if (prc.stableCount == 1) {
|
||||
// We are acquiring a new stable reference on the provider.
|
||||
int unstableDelta;
|
||||
if (prc.removePending) {
|
||||
// We have a pending remove operation, which is holding the
|
||||
// last unstable reference. At this point we are converting
|
||||
// that unstable reference to our new stable reference.
|
||||
unstableDelta = -1;
|
||||
// Cancel the removal of the provider.
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "incProviderRef: stable "
|
||||
+ "snatched provider from the jaws of death");
|
||||
}
|
||||
prc.removePending = false;
|
||||
mH.removeMessages(H.REMOVE_PROVIDER, prc);
|
||||
} else {
|
||||
unstableDelta = 0;
|
||||
}
|
||||
try {
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "incProviderRef Now stable - "
|
||||
+ prc.holder.info.name + ": unstableDelta="
|
||||
+ unstableDelta);
|
||||
}
|
||||
ActivityManagerNative.getDefault().refContentProvider(
|
||||
prc.holder.connection, 1, unstableDelta);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prc.unstableCount += 1;
|
||||
if (prc.unstableCount == 1) {
|
||||
// We are acquiring a new unstable reference on the provider.
|
||||
if (prc.removePending) {
|
||||
// Oh look, we actually have a remove pending for the
|
||||
// provider, which is still holding the last unstable
|
||||
// reference. We just need to cancel that to take new
|
||||
// ownership of the reference.
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "incProviderRef: unstable "
|
||||
+ "snatched provider from the jaws of death");
|
||||
}
|
||||
prc.removePending = false;
|
||||
mH.removeMessages(H.REMOVE_PROVIDER, prc);
|
||||
} else {
|
||||
// First unstable ref, increment our count in the
|
||||
// activity manager.
|
||||
try {
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "incProviderRef: Now unstable - "
|
||||
+ prc.holder.info.name);
|
||||
}
|
||||
ActivityManagerNative.getDefault().refContentProvider(
|
||||
prc.holder.connection, 0, 1);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final IContentProvider acquireExistingProvider(Context c, String name,
|
||||
boolean stable) {
|
||||
synchronized (mProviderMap) {
|
||||
ProviderClientRecord pr = mProviderMap.get(name);
|
||||
if (pr == null) {
|
||||
@ -4232,23 +4313,14 @@ public final class ActivityThread {
|
||||
// provider is not reference counted and never needs to be released.
|
||||
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
|
||||
if (prc != null) {
|
||||
prc.count += 1;
|
||||
if (prc.count == 1) {
|
||||
if (localLOGV) {
|
||||
Slog.v(TAG, "acquireExistingProvider: "
|
||||
+ "snatched provider from the jaws of death");
|
||||
}
|
||||
// Because the provider previously had a reference count of zero,
|
||||
// it was scheduled to be removed. Cancel that.
|
||||
mH.removeMessages(H.REMOVE_PROVIDER, provider);
|
||||
}
|
||||
incProviderRefLocked(prc, stable);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean releaseProvider(IContentProvider provider) {
|
||||
if(provider == null) {
|
||||
public final boolean releaseProvider(IContentProvider provider, boolean stable) {
|
||||
if (provider == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4260,55 +4332,98 @@ public final class ActivityThread {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prc.count == 0) {
|
||||
if (localLOGV) Slog.v(TAG, "releaseProvider: ref count already 0, how?");
|
||||
return false;
|
||||
boolean lastRef = false;
|
||||
if (stable) {
|
||||
if (prc.stableCount == 0) {
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG,
|
||||
"releaseProvider: stable ref count already 0, how?");
|
||||
return false;
|
||||
}
|
||||
prc.stableCount -= 1;
|
||||
if (prc.stableCount == 0) {
|
||||
// What we do at this point depends on whether there are
|
||||
// any unstable refs left: if there are, we just tell the
|
||||
// activity manager to decrement its stable count; if there
|
||||
// aren't, we need to enqueue this provider to be removed,
|
||||
// and convert to holding a single unstable ref while
|
||||
// doing so.
|
||||
lastRef = prc.unstableCount == 0;
|
||||
try {
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "releaseProvider: No longer stable w/lastRef="
|
||||
+ lastRef + " - " + prc.holder.info.name);
|
||||
}
|
||||
ActivityManagerNative.getDefault().refContentProvider(
|
||||
prc.holder.connection, -1, lastRef ? 1 : 0);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (prc.unstableCount == 0) {
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG,
|
||||
"releaseProvider: unstable ref count already 0, how?");
|
||||
return false;
|
||||
}
|
||||
prc.unstableCount -= 1;
|
||||
if (prc.unstableCount == 0) {
|
||||
// If this is the last reference, we need to enqueue
|
||||
// this provider to be removed instead of telling the
|
||||
// activity manager to remove it at this point.
|
||||
lastRef = prc.stableCount == 0;
|
||||
if (!lastRef) {
|
||||
try {
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "releaseProvider: No longer unstable - "
|
||||
+ prc.holder.info.name);
|
||||
}
|
||||
ActivityManagerNative.getDefault().refContentProvider(
|
||||
prc.holder.connection, 0, -1);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prc.count -= 1;
|
||||
if (prc.count == 0) {
|
||||
// Schedule the actual remove asynchronously, since we don't know the context
|
||||
// this will be called in.
|
||||
// TODO: it would be nice to post a delayed message, so
|
||||
// if we come back and need the same provider quickly
|
||||
// we will still have it available.
|
||||
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider);
|
||||
mH.sendMessage(msg);
|
||||
if (lastRef) {
|
||||
if (!prc.removePending) {
|
||||
// Schedule the actual remove asynchronously, since we don't know the context
|
||||
// this will be called in.
|
||||
// TODO: it would be nice to post a delayed message, so
|
||||
// if we come back and need the same provider quickly
|
||||
// we will still have it available.
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "releaseProvider: Enqueueing pending removal - "
|
||||
+ prc.holder.info.name);
|
||||
}
|
||||
prc.removePending = true;
|
||||
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc);
|
||||
mH.sendMessage(msg);
|
||||
} else {
|
||||
Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public final IContentProvider acquireUnstableProvider(Context c, String name) {
|
||||
return acquireProvider(c, name);
|
||||
}
|
||||
|
||||
public final boolean releaseUnstableProvider(IContentProvider provider) {
|
||||
return releaseProvider(provider);
|
||||
}
|
||||
|
||||
final void completeRemoveProvider(IContentProvider provider) {
|
||||
IBinder jBinder = provider.asBinder();
|
||||
String remoteProviderName = null;
|
||||
synchronized(mProviderMap) {
|
||||
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
|
||||
if (prc == null) {
|
||||
// Either no release is needed (so we shouldn't be here) or the
|
||||
// provider was already released.
|
||||
if (localLOGV) Slog.v(TAG, "completeRemoveProvider: release not needed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (prc.count != 0) {
|
||||
final void completeRemoveProvider(ProviderRefCount prc) {
|
||||
synchronized (mProviderMap) {
|
||||
if (!prc.removePending) {
|
||||
// There was a race! Some other client managed to acquire
|
||||
// the provider before the removal was completed.
|
||||
// Abort the removal. We will do it later.
|
||||
if (localLOGV) Slog.v(TAG, "completeRemoveProvider: lost the race, "
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG, "completeRemoveProvider: lost the race, "
|
||||
+ "provider still in use");
|
||||
return;
|
||||
}
|
||||
|
||||
mProviderRefCountMap.remove(jBinder);
|
||||
final IBinder jBinder = prc.holder.provider.asBinder();
|
||||
ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
|
||||
if (existingPrc == prc) {
|
||||
mProviderRefCountMap.remove(jBinder);
|
||||
}
|
||||
|
||||
Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
|
||||
while (iter.hasNext()) {
|
||||
@ -4316,41 +4431,70 @@ public final class ActivityThread {
|
||||
IBinder myBinder = pr.mProvider.asBinder();
|
||||
if (myBinder == jBinder) {
|
||||
iter.remove();
|
||||
if (pr.mLocalProvider == null) {
|
||||
myBinder.unlinkToDeath(pr, 0);
|
||||
if (remoteProviderName == null) {
|
||||
remoteProviderName = pr.mName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
|
||||
+ "removeContentProvider(" + prc.holder.info.name + ")");
|
||||
}
|
||||
ActivityManagerNative.getDefault().removeContentProvider(
|
||||
prc.holder.connection, false);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
}
|
||||
|
||||
final void handleUnstableProviderDied(IBinder provider, boolean fromClient) {
|
||||
synchronized(mProviderMap) {
|
||||
ProviderRefCount prc = mProviderRefCountMap.get(provider);
|
||||
if (prc != null) {
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG, "Cleaning up dead provider "
|
||||
+ provider + " " + prc.holder.info.name);
|
||||
mProviderRefCountMap.remove(provider);
|
||||
if (prc.client != null && prc.client.mNames != null) {
|
||||
for (String name : prc.client.mNames) {
|
||||
ProviderClientRecord pr = mProviderMap.get(name);
|
||||
if (pr != null && pr.mProvider.asBinder() == provider) {
|
||||
Slog.i(TAG, "Removing dead content provider: " + name);
|
||||
mProviderMap.remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fromClient) {
|
||||
// We found out about this due to execution in our client
|
||||
// code. Tell the activity manager about it now, to ensure
|
||||
// that the next time we go to do anything with the provider
|
||||
// it knows it is dead (so we don't race with its death
|
||||
// notification).
|
||||
try {
|
||||
ActivityManagerNative.getDefault().unstableProviderDied(
|
||||
prc.holder.connection);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remoteProviderName != null) {
|
||||
try {
|
||||
if (localLOGV) {
|
||||
Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
|
||||
+ "removeContentProvider(" + remoteProviderName + ")");
|
||||
}
|
||||
ActivityManagerNative.getDefault().removeContentProvider(
|
||||
getApplicationThread(), remoteProviderName);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final void removeDeadProvider(String name, IContentProvider provider) {
|
||||
synchronized(mProviderMap) {
|
||||
ProviderClientRecord pr = mProviderMap.get(name);
|
||||
if (pr != null && pr.mProvider.asBinder() == provider.asBinder()) {
|
||||
Slog.i(TAG, "Removing dead content provider: " + name);
|
||||
ProviderClientRecord removed = mProviderMap.remove(name);
|
||||
if (removed != null) {
|
||||
removed.mProvider.asBinder().unlinkToDeath(removed, 0);
|
||||
}
|
||||
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
|
||||
ContentProvider localProvider,IActivityManager.ContentProviderHolder holder) {
|
||||
String names[] = PATTERN_SEMICOLON.split(holder.info.authority);
|
||||
ProviderClientRecord pcr = new ProviderClientRecord(names, provider,
|
||||
localProvider, holder);
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
ProviderClientRecord existing = mProviderMap.get(names[i]);
|
||||
if (existing != null) {
|
||||
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
|
||||
+ " already published as " + names[i]);
|
||||
} else {
|
||||
mProviderMap.put(names[i], pcr);
|
||||
}
|
||||
}
|
||||
return pcr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4367,12 +4511,13 @@ public final class ActivityThread {
|
||||
* and returns the existing provider. This can happen due to concurrent
|
||||
* attempts to acquire the same provider.
|
||||
*/
|
||||
private IContentProvider installProvider(Context context,
|
||||
IContentProvider provider, ProviderInfo info,
|
||||
boolean noisy, boolean noReleaseNeeded) {
|
||||
private IActivityManager.ContentProviderHolder installProvider(Context context,
|
||||
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
|
||||
boolean noisy, boolean noReleaseNeeded, boolean stable) {
|
||||
ContentProvider localProvider = null;
|
||||
if (provider == null) {
|
||||
if (noisy) {
|
||||
IContentProvider provider;
|
||||
if (holder == null) {
|
||||
if (DEBUG_PROVIDER || noisy) {
|
||||
Slog.d(TAG, "Loading provider " + info.authority + ": "
|
||||
+ info.name);
|
||||
}
|
||||
@ -4409,7 +4554,7 @@ public final class ActivityThread {
|
||||
info.applicationInfo.sourceDir);
|
||||
return null;
|
||||
}
|
||||
if (false) Slog.v(
|
||||
if (DEBUG_PROVIDER) Slog.v(
|
||||
TAG, "Instantiating local provider " + info.name);
|
||||
// XXX Need to create the correct context for this provider.
|
||||
localProvider.attachInfo(c, info);
|
||||
@ -4421,76 +4566,72 @@ public final class ActivityThread {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} else if (localLOGV) {
|
||||
Slog.v(TAG, "Installing external provider " + info.authority + ": "
|
||||
} else {
|
||||
provider = holder.provider;
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
|
||||
+ info.name);
|
||||
}
|
||||
|
||||
synchronized (mProviderMap) {
|
||||
// There is a possibility that this thread raced with another thread to
|
||||
// add the provider. If we find another thread got there first then we
|
||||
// just get out of the way and return the original provider.
|
||||
IBinder jBinder = provider.asBinder();
|
||||
String names[] = PATTERN_SEMICOLON.split(info.authority);
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
ProviderClientRecord pr = mProviderMap.get(names[i]);
|
||||
if (pr != null) {
|
||||
if (localLOGV) {
|
||||
Slog.v(TAG, "installProvider: lost the race, "
|
||||
+ "using existing named provider");
|
||||
}
|
||||
provider = pr.mProvider;
|
||||
} else {
|
||||
pr = new ProviderClientRecord(names[i], provider, localProvider);
|
||||
if (localProvider == null) {
|
||||
try {
|
||||
jBinder.linkToDeath(pr, 0);
|
||||
} catch (RemoteException e) {
|
||||
// Provider already dead. Bail out of here without making
|
||||
// any changes to the provider map or other data structures.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
mProviderMap.put(names[i], pr);
|
||||
}
|
||||
}
|
||||
IActivityManager.ContentProviderHolder retHolder;
|
||||
|
||||
synchronized (mProviderMap) {
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
|
||||
+ " / " + info.name);
|
||||
IBinder jBinder = provider.asBinder();
|
||||
if (localProvider != null) {
|
||||
ProviderClientRecord pr = mLocalProviders.get(jBinder);
|
||||
ComponentName cname = new ComponentName(info.packageName, info.name);
|
||||
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
|
||||
if (pr != null) {
|
||||
if (localLOGV) {
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "installProvider: lost the race, "
|
||||
+ "using existing local provider");
|
||||
}
|
||||
provider = pr.mProvider;
|
||||
} else {
|
||||
pr = new ProviderClientRecord(null, provider, localProvider);
|
||||
holder = new IActivityManager.ContentProviderHolder(info);
|
||||
holder.provider = provider;
|
||||
holder.noReleaseNeeded = true;
|
||||
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
|
||||
mLocalProviders.put(jBinder, pr);
|
||||
mLocalProvidersByName.put(cname, pr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!noReleaseNeeded) {
|
||||
retHolder = pr.mHolder;
|
||||
} else {
|
||||
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
|
||||
if (prc != null) {
|
||||
if (localLOGV) {
|
||||
Slog.v(TAG, "installProvider: lost the race, incrementing ref count");
|
||||
if (DEBUG_PROVIDER) {
|
||||
Slog.v(TAG, "installProvider: lost the race, updating ref count");
|
||||
}
|
||||
prc.count += 1;
|
||||
if (prc.count == 1) {
|
||||
if (localLOGV) {
|
||||
Slog.v(TAG, "installProvider: "
|
||||
+ "snatched provider from the jaws of death");
|
||||
// We need to transfer our new reference to the existing
|
||||
// ref count, releasing the old one... but only if
|
||||
// release is needed (that is, it is not running in the
|
||||
// system process).
|
||||
if (!noReleaseNeeded) {
|
||||
incProviderRefLocked(prc, stable);
|
||||
try {
|
||||
ActivityManagerNative.getDefault().removeContentProvider(
|
||||
holder.connection, stable);
|
||||
} catch (RemoteException e) {
|
||||
//do nothing content provider object is dead any way
|
||||
}
|
||||
// Because the provider previously had a reference count of zero,
|
||||
// it was scheduled to be removed. Cancel that.
|
||||
mH.removeMessages(H.REMOVE_PROVIDER, provider);
|
||||
}
|
||||
} else {
|
||||
mProviderRefCountMap.put(jBinder, new ProviderRefCount(1));
|
||||
ProviderClientRecord client = installProviderAuthoritiesLocked(
|
||||
provider, localProvider, holder);
|
||||
if (noReleaseNeeded) {
|
||||
prc = new ProviderRefCount(holder, client, 1000, 1000);
|
||||
} else {
|
||||
prc = stable
|
||||
? new ProviderRefCount(holder, client, 1, 0)
|
||||
: new ProviderRefCount(holder, client, 0, 1);
|
||||
}
|
||||
mProviderRefCountMap.put(jBinder, prc);
|
||||
}
|
||||
retHolder = prc.holder;
|
||||
}
|
||||
}
|
||||
return provider;
|
||||
|
||||
return retHolder;
|
||||
}
|
||||
|
||||
private void attach(boolean system) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ import android.content.ContentProvider;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.IContentProvider;
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
@ -1810,12 +1811,11 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
}
|
||||
}
|
||||
if (app.conProviders.size() > 0) {
|
||||
for (ContentProviderRecord cpr : app.conProviders.keySet()) {
|
||||
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
|
||||
updateLruProcessInternalLocked(cpr.proc, oomAdj,
|
||||
updateActivityTime, i+1);
|
||||
}
|
||||
for (int j=app.conProviders.size()-1; j>=0; j--) {
|
||||
ContentProviderRecord cpr = app.conProviders.get(j).provider;
|
||||
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
|
||||
updateLruProcessInternalLocked(cpr.proc, oomAdj,
|
||||
updateActivityTime, i+1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3742,7 +3742,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
|
||||
N = providers.size();
|
||||
for (i=0; i<N; i++) {
|
||||
removeDyingProviderLocked(null, providers.get(i));
|
||||
removeDyingProviderLocked(null, providers.get(i), true);
|
||||
}
|
||||
|
||||
if (doit) {
|
||||
@ -6058,53 +6058,72 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
return msg;
|
||||
}
|
||||
|
||||
boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
|
||||
IBinder externalProcessToken) {
|
||||
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
|
||||
final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
|
||||
if (r != null) {
|
||||
Integer cnt = r.conProviders.get(cpr);
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG,
|
||||
"Adding provider requested by "
|
||||
+ r.processName + " from process "
|
||||
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
|
||||
+ " cnt=" + (cnt == null ? 1 : cnt));
|
||||
if (cnt == null) {
|
||||
cpr.clients.add(r);
|
||||
r.conProviders.put(cpr, new Integer(1));
|
||||
return true;
|
||||
} else {
|
||||
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
|
||||
for (int i=0; i<r.conProviders.size(); i++) {
|
||||
ContentProviderConnection conn = r.conProviders.get(i);
|
||||
if (conn.provider == cpr) {
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG,
|
||||
"Adding provider requested by "
|
||||
+ r.processName + " from process "
|
||||
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
|
||||
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
|
||||
if (stable) {
|
||||
conn.stableCount++;
|
||||
conn.numStableIncs++;
|
||||
} else {
|
||||
conn.unstableCount++;
|
||||
conn.numUnstableIncs++;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cpr.addExternalProcessHandleLocked(externalProcessToken);
|
||||
ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
|
||||
if (stable) {
|
||||
conn.stableCount = 1;
|
||||
conn.numStableIncs = 1;
|
||||
} else {
|
||||
conn.unstableCount = 1;
|
||||
conn.numUnstableIncs = 1;
|
||||
}
|
||||
cpr.connections.add(conn);
|
||||
r.conProviders.add(conn);
|
||||
return conn;
|
||||
}
|
||||
return false;
|
||||
cpr.addExternalProcessHandleLocked(externalProcessToken);
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
|
||||
IBinder externalProcessToken) {
|
||||
if (r != null) {
|
||||
Integer cnt = r.conProviders.get(cpr);
|
||||
boolean decProviderCountLocked(ContentProviderConnection conn,
|
||||
ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
|
||||
if (conn != null) {
|
||||
cpr = conn.provider;
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG,
|
||||
"Removing provider requested by "
|
||||
+ r.processName + " from process "
|
||||
+ conn.client.processName + " from process "
|
||||
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
|
||||
+ " cnt=" + cnt);
|
||||
if (cnt == null || cnt.intValue() <= 1) {
|
||||
cpr.clients.remove(r);
|
||||
r.conProviders.remove(cpr);
|
||||
return true;
|
||||
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
|
||||
if (stable) {
|
||||
conn.stableCount--;
|
||||
} else {
|
||||
r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
|
||||
conn.unstableCount--;
|
||||
}
|
||||
} else {
|
||||
cpr.removeExternalProcessHandleLocked(externalProcessToken);
|
||||
if (conn.stableCount == 0 && conn.unstableCount == 0) {
|
||||
cpr.connections.remove(conn);
|
||||
conn.client.conProviders.remove(conn);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
cpr.removeExternalProcessHandleLocked(externalProcessToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
|
||||
String name, IBinder token) {
|
||||
String name, IBinder token, boolean stable) {
|
||||
ContentProviderRecord cpr;
|
||||
ContentProviderConnection conn = null;
|
||||
ProviderInfo cpi = null;
|
||||
|
||||
synchronized(this) {
|
||||
@ -6135,20 +6154,19 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
// of being published... but it is also allowed to run
|
||||
// in the caller's process, so don't make a connection
|
||||
// and just let the caller instantiate its own instance.
|
||||
if (cpr.provider != null) {
|
||||
// don't give caller the provider object, it needs
|
||||
// to make its own.
|
||||
cpr = new ContentProviderRecord(cpr);
|
||||
}
|
||||
return cpr;
|
||||
ContentProviderHolder holder = cpr.newHolder(null);
|
||||
// don't give caller the provider object, it needs
|
||||
// to make its own.
|
||||
holder.provider = null;
|
||||
return holder;
|
||||
}
|
||||
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
|
||||
// In this case the provider instance already exists, so we can
|
||||
// return it right away.
|
||||
final boolean countChanged = incProviderCount(r, cpr, token);
|
||||
if (countChanged) {
|
||||
conn = incProviderCountLocked(r, cpr, token, stable);
|
||||
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
|
||||
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
|
||||
// If this is a perceptible app accessing the provider,
|
||||
// make sure to count it as being accessed and thus
|
||||
@ -6181,7 +6199,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
Slog.i(TAG,
|
||||
"Existing provider " + cpr.name.flattenToShortString()
|
||||
+ " is crashing; detaching " + r);
|
||||
boolean lastRef = decProviderCount(r, cpr, token);
|
||||
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
|
||||
appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);
|
||||
if (!lastRef) {
|
||||
// This wasn't the last ref our process had on
|
||||
@ -6189,6 +6207,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
return null;
|
||||
}
|
||||
providerRunning = false;
|
||||
conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6251,7 +6270,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
// info and allow the caller to instantiate it. Only do
|
||||
// this if the provider is the same user as the caller's
|
||||
// process, or can run as root (so can be in any process).
|
||||
return cpr;
|
||||
return cpr.newHolder(null);
|
||||
}
|
||||
|
||||
if (DEBUG_PROVIDER) {
|
||||
@ -6312,7 +6331,10 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
|
||||
mProviderMap.putProviderByName(name, cpr);
|
||||
incProviderCount(r, cpr, token);
|
||||
conn = incProviderCountLocked(r, cpr, token, stable);
|
||||
if (conn != null) {
|
||||
conn.waiting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6334,16 +6356,23 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
|
||||
+ cpr.launchingApp);
|
||||
}
|
||||
if (conn != null) {
|
||||
conn.waiting = true;
|
||||
}
|
||||
cpr.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.waiting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cpr;
|
||||
return cpr != null ? cpr.newHolder(conn) : null;
|
||||
}
|
||||
|
||||
public final ContentProviderHolder getContentProvider(
|
||||
IApplicationThread caller, String name) {
|
||||
IApplicationThread caller, String name, boolean stable) {
|
||||
enforceNotIsolatedCaller("getContentProvider");
|
||||
if (caller == null) {
|
||||
String msg = "null IApplicationThread when getting content provider "
|
||||
@ -6352,7 +6381,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
|
||||
return getContentProviderImpl(caller, name, null);
|
||||
return getContentProviderImpl(caller, name, null, stable);
|
||||
}
|
||||
|
||||
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
|
||||
@ -6362,45 +6391,30 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
|
||||
private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
|
||||
return getContentProviderImpl(null, name, token);
|
||||
return getContentProviderImpl(null, name, token, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a content provider from a ProcessRecord's bookkeeping
|
||||
* @param cpr
|
||||
*/
|
||||
public void removeContentProvider(IApplicationThread caller, String name) {
|
||||
public void removeContentProvider(IBinder connection, boolean stable) {
|
||||
enforceNotIsolatedCaller("removeContentProvider");
|
||||
synchronized (this) {
|
||||
int userId = UserId.getUserId(Binder.getCallingUid());
|
||||
ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
|
||||
if(cpr == null) {
|
||||
// remove from mProvidersByClass
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG, name +
|
||||
" provider not found in providers list");
|
||||
return;
|
||||
ContentProviderConnection conn;
|
||||
try {
|
||||
conn = (ContentProviderConnection)connection;
|
||||
} catch (ClassCastException e) {
|
||||
String msg ="removeContentProvider: " + connection
|
||||
+ " not a ContentProviderConnection";
|
||||
Slog.w(TAG, msg);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
final ProcessRecord r = getRecordForAppLocked(caller);
|
||||
if (r == null) {
|
||||
throw new SecurityException(
|
||||
"Unable to find app for caller " + caller +
|
||||
" when removing content provider " + name);
|
||||
if (conn == null) {
|
||||
throw new NullPointerException("connection is null");
|
||||
}
|
||||
//update content provider record entry info
|
||||
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
|
||||
ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
|
||||
if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
|
||||
+ r.info.processName + " from process "
|
||||
+ localCpr.appInfo.processName);
|
||||
if (localCpr.launchingApp == r) {
|
||||
//should not happen. taken care of as a local provider
|
||||
Slog.w(TAG, "removeContentProvider called on local provider: "
|
||||
+ cpr.info.name + " in process " + r.processName);
|
||||
return;
|
||||
} else {
|
||||
if (decProviderCount(r, localCpr, null)) {
|
||||
updateOomAdjLocked();
|
||||
}
|
||||
if (decProviderCountLocked(conn, null, null, stable)) {
|
||||
updateOomAdjLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6447,7 +6461,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
|
||||
enforceNotIsolatedCaller("publishContentProviders");
|
||||
synchronized(this) {
|
||||
synchronized (this) {
|
||||
final ProcessRecord r = getRecordForAppLocked(caller);
|
||||
if (DEBUG_MU)
|
||||
Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
|
||||
@ -6499,6 +6513,103 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
}
|
||||
}
|
||||
|
||||
public boolean refContentProvider(IBinder connection, int stable, int unstable) {
|
||||
ContentProviderConnection conn;
|
||||
try {
|
||||
conn = (ContentProviderConnection)connection;
|
||||
} catch (ClassCastException e) {
|
||||
String msg ="refContentProvider: " + connection
|
||||
+ " not a ContentProviderConnection";
|
||||
Slog.w(TAG, msg);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
if (conn == null) {
|
||||
throw new NullPointerException("connection is null");
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (stable > 0) {
|
||||
conn.numStableIncs += stable;
|
||||
}
|
||||
stable = conn.stableCount + stable;
|
||||
if (stable < 0) {
|
||||
throw new IllegalStateException("stableCount < 0: " + stable);
|
||||
}
|
||||
|
||||
if (unstable > 0) {
|
||||
conn.numUnstableIncs += unstable;
|
||||
}
|
||||
unstable = conn.unstableCount + unstable;
|
||||
if (unstable < 0) {
|
||||
throw new IllegalStateException("unstableCount < 0: " + unstable);
|
||||
}
|
||||
|
||||
if ((stable+unstable) <= 0) {
|
||||
throw new IllegalStateException("ref counts can't go to zero here: stable="
|
||||
+ stable + " unstable=" + unstable);
|
||||
}
|
||||
conn.stableCount = stable;
|
||||
conn.unstableCount = unstable;
|
||||
return !conn.dead;
|
||||
}
|
||||
}
|
||||
|
||||
public void unstableProviderDied(IBinder connection) {
|
||||
ContentProviderConnection conn;
|
||||
try {
|
||||
conn = (ContentProviderConnection)connection;
|
||||
} catch (ClassCastException e) {
|
||||
String msg ="refContentProvider: " + connection
|
||||
+ " not a ContentProviderConnection";
|
||||
Slog.w(TAG, msg);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
if (conn == null) {
|
||||
throw new NullPointerException("connection is null");
|
||||
}
|
||||
|
||||
// Safely retrieve the content provider associated with the connection.
|
||||
IContentProvider provider;
|
||||
synchronized (this) {
|
||||
provider = conn.provider.provider;
|
||||
}
|
||||
|
||||
if (provider == null) {
|
||||
// Um, yeah, we're way ahead of you.
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the caller is being honest with us.
|
||||
if (provider.asBinder().pingBinder()) {
|
||||
// Er, no, still looks good to us.
|
||||
synchronized (this) {
|
||||
Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
|
||||
+ " says " + conn + " died, but we don't agree");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Well look at that! It's dead!
|
||||
synchronized (this) {
|
||||
if (conn.provider.provider != provider) {
|
||||
// But something changed... good enough.
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessRecord proc = conn.provider.proc;
|
||||
if (proc == null || proc.thread == null) {
|
||||
// Seems like the process is already cleaned up.
|
||||
return;
|
||||
}
|
||||
|
||||
// As far as we're concerned, this is just like receiving a
|
||||
// death notification... just a bit prematurely.
|
||||
Slog.i(TAG, "Process " + proc.processName + " (pid " + proc.pid
|
||||
+ ") early provider death");
|
||||
appDiedLocked(proc, proc.pid, proc.thread);
|
||||
}
|
||||
}
|
||||
|
||||
public static final void installSystemProviders() {
|
||||
List<ProviderInfo> providers;
|
||||
synchronized (mSelf) {
|
||||
@ -10515,36 +10626,62 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
app.executingServices.clear();
|
||||
}
|
||||
|
||||
private final void removeDyingProviderLocked(ProcessRecord proc,
|
||||
ContentProviderRecord cpr) {
|
||||
synchronized (cpr) {
|
||||
cpr.launchingApp = null;
|
||||
cpr.notifyAll();
|
||||
}
|
||||
|
||||
mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
|
||||
String names[] = cpr.info.authority.split(";");
|
||||
for (int j = 0; j < names.length; j++) {
|
||||
mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
|
||||
}
|
||||
|
||||
Iterator<ProcessRecord> cit = cpr.clients.iterator();
|
||||
while (cit.hasNext()) {
|
||||
ProcessRecord capp = cit.next();
|
||||
if (!capp.persistent && capp.thread != null
|
||||
&& capp.pid != 0
|
||||
&& capp.pid != MY_PID) {
|
||||
Slog.i(TAG, "Kill " + capp.processName
|
||||
+ " (pid " + capp.pid + "): provider " + cpr.info.name
|
||||
+ " in dying process " + (proc != null ? proc.processName : "??"));
|
||||
EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
|
||||
capp.processName, capp.setAdj, "dying provider "
|
||||
+ cpr.name.toShortString());
|
||||
Process.killProcessQuiet(capp.pid);
|
||||
private final boolean removeDyingProviderLocked(ProcessRecord proc,
|
||||
ContentProviderRecord cpr, boolean always) {
|
||||
final boolean inLaunching = mLaunchingProviders.contains(cpr);
|
||||
|
||||
if (!inLaunching || always) {
|
||||
synchronized (cpr) {
|
||||
cpr.launchingApp = null;
|
||||
cpr.notifyAll();
|
||||
}
|
||||
mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
|
||||
String names[] = cpr.info.authority.split(";");
|
||||
for (int j = 0; j < names.length; j++) {
|
||||
mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
|
||||
}
|
||||
}
|
||||
|
||||
mLaunchingProviders.remove(cpr);
|
||||
|
||||
for (int i=0; i<cpr.connections.size(); i++) {
|
||||
ContentProviderConnection conn = cpr.connections.get(i);
|
||||
if (conn.waiting) {
|
||||
// If this connection is waiting for the provider, then we don't
|
||||
// need to mess with its process unless we are always removing
|
||||
// or for some reason the provider is not currently launching.
|
||||
if (inLaunching && !always) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ProcessRecord capp = conn.client;
|
||||
conn.dead = true;
|
||||
if (conn.stableCount > 0) {
|
||||
if (!capp.persistent && capp.thread != null
|
||||
&& capp.pid != 0
|
||||
&& capp.pid != MY_PID) {
|
||||
Slog.i(TAG, "Kill " + capp.processName
|
||||
+ " (pid " + capp.pid + "): provider " + cpr.info.name
|
||||
+ " in dying process " + (proc != null ? proc.processName : "??"));
|
||||
EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
|
||||
capp.processName, capp.setAdj, "dying provider "
|
||||
+ cpr.name.toShortString());
|
||||
Process.killProcessQuiet(capp.pid);
|
||||
}
|
||||
} else if (capp.thread != null && conn.provider.provider != null) {
|
||||
try {
|
||||
capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
// In the protocol here, we don't expect the client to correctly
|
||||
// clean up this connection, we'll just remove it.
|
||||
cpr.connections.remove(i);
|
||||
conn.client.conProviders.remove(conn);
|
||||
}
|
||||
}
|
||||
|
||||
if (inLaunching && always) {
|
||||
mLaunchingProviders.remove(cpr);
|
||||
}
|
||||
return inLaunching;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -10590,34 +10727,21 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
|
||||
boolean restart = false;
|
||||
|
||||
int NL = mLaunchingProviders.size();
|
||||
|
||||
// Remove published content providers.
|
||||
if (!app.pubProviders.isEmpty()) {
|
||||
Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
ContentProviderRecord cpr = it.next();
|
||||
|
||||
final boolean always = app.bad || !allowRestart;
|
||||
if (removeDyingProviderLocked(app, cpr, always) || always) {
|
||||
// We left the provider in the launching list, need to
|
||||
// restart it.
|
||||
restart = true;
|
||||
}
|
||||
|
||||
cpr.provider = null;
|
||||
cpr.proc = null;
|
||||
|
||||
// See if someone is waiting for this provider... in which
|
||||
// case we don't remove it, but just let it restart.
|
||||
int i = 0;
|
||||
if (!app.bad && allowRestart) {
|
||||
for (; i<NL; i++) {
|
||||
if (mLaunchingProviders.get(i) == cpr) {
|
||||
restart = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i = NL;
|
||||
}
|
||||
|
||||
if (i >= NL) {
|
||||
removeDyingProviderLocked(app, cpr);
|
||||
NL = mLaunchingProviders.size();
|
||||
}
|
||||
}
|
||||
app.pubProviders.clear();
|
||||
}
|
||||
@ -10629,10 +10753,9 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
|
||||
// Unregister from connected content providers.
|
||||
if (!app.conProviders.isEmpty()) {
|
||||
Iterator it = app.conProviders.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
ContentProviderRecord cpr = (ContentProviderRecord)it.next();
|
||||
cpr.clients.remove(app);
|
||||
for (int i=0; i<app.conProviders.size(); i++) {
|
||||
ContentProviderConnection conn = app.conProviders.get(i);
|
||||
conn.provider.connections.remove(conn);
|
||||
}
|
||||
app.conProviders.clear();
|
||||
}
|
||||
@ -10643,10 +10766,10 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
// XXX Commented out for now. Trying to figure out a way to reproduce
|
||||
// the actual situation to identify what is actually going on.
|
||||
if (false) {
|
||||
for (int i=0; i<NL; i++) {
|
||||
for (int i=0; i<mLaunchingProviders.size(); i++) {
|
||||
ContentProviderRecord cpr = (ContentProviderRecord)
|
||||
mLaunchingProviders.get(i);
|
||||
if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) {
|
||||
if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
|
||||
synchronized (cpr) {
|
||||
cpr.launchingApp = null;
|
||||
cpr.notifyAll();
|
||||
@ -10743,7 +10866,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
if (!alwaysBad && !app.bad) {
|
||||
restart = true;
|
||||
} else {
|
||||
removeDyingProviderLocked(app, cpr);
|
||||
removeDyingProviderLocked(app, cpr, true);
|
||||
NL = mLaunchingProviders.size();
|
||||
}
|
||||
}
|
||||
@ -13948,48 +14071,49 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ
|
||||
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
|
||||
ContentProviderRecord cpr = jt.next();
|
||||
if (cpr.clients.size() != 0) {
|
||||
Iterator<ProcessRecord> kt = cpr.clients.iterator();
|
||||
while (kt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
|
||||
ProcessRecord client = kt.next();
|
||||
if (client == app) {
|
||||
// Being our own client is not interesting.
|
||||
continue;
|
||||
for (int i = cpr.connections.size()-1;
|
||||
i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|
||||
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
|
||||
i--) {
|
||||
ContentProviderConnection conn = cpr.connections.get(i);
|
||||
ProcessRecord client = conn.client;
|
||||
if (client == app) {
|
||||
// Being our own client is not interesting.
|
||||
continue;
|
||||
}
|
||||
int myHiddenAdj = hiddenAdj;
|
||||
if (myHiddenAdj > client.hiddenAdj) {
|
||||
if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
|
||||
myHiddenAdj = client.hiddenAdj;
|
||||
} else {
|
||||
myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
|
||||
}
|
||||
int myHiddenAdj = hiddenAdj;
|
||||
if (myHiddenAdj > client.hiddenAdj) {
|
||||
if (client.hiddenAdj > ProcessList.FOREGROUND_APP_ADJ) {
|
||||
myHiddenAdj = client.hiddenAdj;
|
||||
} else {
|
||||
myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
|
||||
}
|
||||
}
|
||||
int clientAdj = computeOomAdjLocked(
|
||||
client, myHiddenAdj, TOP_APP, true, doingAll);
|
||||
if (adj > clientAdj) {
|
||||
if (app.hasShownUi && app != mHomeProcess
|
||||
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
|
||||
app.adjType = "bg-ui-provider";
|
||||
} else {
|
||||
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
|
||||
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
|
||||
app.adjType = "provider";
|
||||
}
|
||||
int clientAdj = computeOomAdjLocked(
|
||||
client, myHiddenAdj, TOP_APP, true, doingAll);
|
||||
if (adj > clientAdj) {
|
||||
if (app.hasShownUi && app != mHomeProcess
|
||||
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
|
||||
app.adjType = "bg-ui-provider";
|
||||
} else {
|
||||
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
|
||||
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
|
||||
app.adjType = "provider";
|
||||
}
|
||||
if (!client.hidden) {
|
||||
app.hidden = false;
|
||||
}
|
||||
if (client.keeping) {
|
||||
app.keeping = true;
|
||||
}
|
||||
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
|
||||
.REASON_PROVIDER_IN_USE;
|
||||
app.adjSource = client;
|
||||
app.adjSourceOom = clientAdj;
|
||||
app.adjTarget = cpr.name;
|
||||
if (!client.hidden) {
|
||||
app.hidden = false;
|
||||
}
|
||||
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
|
||||
schedGroup = Process.THREAD_GROUP_DEFAULT;
|
||||
if (client.keeping) {
|
||||
app.keeping = true;
|
||||
}
|
||||
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
|
||||
.REASON_PROVIDER_IN_USE;
|
||||
app.adjSource = client;
|
||||
app.adjSourceOom = clientAdj;
|
||||
app.adjTarget = cpr.name;
|
||||
}
|
||||
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
|
||||
schedGroup = Process.THREAD_GROUP_DEFAULT;
|
||||
}
|
||||
}
|
||||
// If the provider has external (non-framework) process
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.am;
|
||||
|
||||
import android.os.Binder;
|
||||
|
||||
/**
|
||||
* Represents a link between a content provider and client.
|
||||
*/
|
||||
public class ContentProviderConnection extends Binder {
|
||||
public final ContentProviderRecord provider;
|
||||
public final ProcessRecord client;
|
||||
public int stableCount;
|
||||
public int unstableCount;
|
||||
// The client of this connection is currently waiting for the provider to appear.
|
||||
// Protected by the provider lock.
|
||||
public boolean waiting;
|
||||
// The provider of this connection is now dead.
|
||||
public boolean dead;
|
||||
|
||||
// For debugging.
|
||||
public int numStableIncs;
|
||||
public int numUnstableIncs;
|
||||
|
||||
public ContentProviderConnection(ContentProviderRecord _provider, ProcessRecord _client) {
|
||||
provider = _provider;
|
||||
client = _client;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
sb.append("ContentProviderConnection{");
|
||||
toShortString(sb);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toShortString() {
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
toShortString(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toClientString() {
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
toClientString(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void toShortString(StringBuilder sb) {
|
||||
sb.append(provider.toShortString());
|
||||
sb.append("->");
|
||||
toClientString(sb);
|
||||
}
|
||||
|
||||
public void toClientString(StringBuilder sb) {
|
||||
sb.append(client.toShortString());
|
||||
sb.append(" s");
|
||||
sb.append(stableCount);
|
||||
sb.append("/");
|
||||
sb.append(numStableIncs);
|
||||
sb.append(" u");
|
||||
sb.append(unstableCount);
|
||||
sb.append("/");
|
||||
sb.append(numUnstableIncs);
|
||||
if (waiting) {
|
||||
sb.append(" WAITING");
|
||||
}
|
||||
if (dead) {
|
||||
sb.append(" DEAD");
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ package com.android.server.am;
|
||||
|
||||
import android.app.IActivityManager.ContentProviderHolder;
|
||||
import android.content.ComponentName;
|
||||
import android.content.IContentProvider;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.os.IBinder;
|
||||
@ -27,28 +28,35 @@ import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
class ContentProviderRecord extends ContentProviderHolder {
|
||||
class ContentProviderRecord {
|
||||
final ActivityManagerService service;
|
||||
public final ProviderInfo info;
|
||||
final int uid;
|
||||
final ApplicationInfo appInfo;
|
||||
final ComponentName name;
|
||||
public IContentProvider provider;
|
||||
public boolean noReleaseNeeded;
|
||||
// All attached clients
|
||||
final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
|
||||
final ArrayList<ContentProviderConnection> connections
|
||||
= new ArrayList<ContentProviderConnection>();
|
||||
//final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
|
||||
// Handles for non-framework processes supported by this provider
|
||||
HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
|
||||
// Count for external process for which we have no handles.
|
||||
int externalProcessNoHandleCount;
|
||||
final ActivityManagerService service;
|
||||
final int uid;
|
||||
final ApplicationInfo appInfo;
|
||||
final ComponentName name;
|
||||
ProcessRecord proc; // if non-null, hosting process.
|
||||
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
|
||||
String stringName;
|
||||
String shortStringName;
|
||||
|
||||
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
|
||||
ApplicationInfo ai, ComponentName _name) {
|
||||
super(_info);
|
||||
service = _service;
|
||||
info = _info;
|
||||
uid = ai.uid;
|
||||
appInfo = ai;
|
||||
name = _name;
|
||||
@ -56,12 +64,20 @@ class ContentProviderRecord extends ContentProviderHolder {
|
||||
}
|
||||
|
||||
public ContentProviderRecord(ContentProviderRecord cpr) {
|
||||
super(cpr.info);
|
||||
service = cpr.service;
|
||||
info = cpr.info;
|
||||
uid = cpr.uid;
|
||||
appInfo = cpr.appInfo;
|
||||
name = cpr.name;
|
||||
noReleaseNeeded = cpr.noReleaseNeeded;
|
||||
service = cpr.service;
|
||||
}
|
||||
|
||||
public ContentProviderHolder newHolder(ContentProviderConnection conn) {
|
||||
ContentProviderHolder holder = new ContentProviderHolder(info);
|
||||
holder.provider = provider;
|
||||
holder.noReleaseNeeded = noReleaseNeeded;
|
||||
holder.connection = conn;
|
||||
return holder;
|
||||
}
|
||||
|
||||
public boolean canRunHere(ProcessRecord app) {
|
||||
@ -120,30 +136,51 @@ class ContentProviderRecord extends ContentProviderHolder {
|
||||
return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
pw.print(prefix); pw.print("package=");
|
||||
pw.print(info.applicationInfo.packageName);
|
||||
pw.print(" process="); pw.println(info.processName);
|
||||
void dump(PrintWriter pw, String prefix, boolean full) {
|
||||
if (full) {
|
||||
pw.print(prefix); pw.print("package=");
|
||||
pw.print(info.applicationInfo.packageName);
|
||||
pw.print(" process="); pw.println(info.processName);
|
||||
}
|
||||
pw.print(prefix); pw.print("proc="); pw.println(proc);
|
||||
if (launchingApp != null) {
|
||||
pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
|
||||
}
|
||||
pw.print(prefix); pw.print("uid="); pw.print(uid);
|
||||
pw.print(" provider="); pw.println(provider);
|
||||
pw.print(prefix); pw.print("name="); pw.println(info.authority);
|
||||
if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
|
||||
pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
|
||||
pw.print("multiprocess="); pw.print(info.multiprocess);
|
||||
pw.print(" initOrder="); pw.println(info.initOrder);
|
||||
if (full) {
|
||||
pw.print(prefix); pw.print("uid="); pw.print(uid);
|
||||
pw.print(" provider="); pw.println(provider);
|
||||
}
|
||||
if (hasExternalProcessHandles()) {
|
||||
pw.print(prefix); pw.print("externals=");
|
||||
pw.println(externalProcessTokenToHandle.size());
|
||||
pw.print(prefix); pw.print("authority="); pw.println(info.authority);
|
||||
if (full) {
|
||||
if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
|
||||
pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
|
||||
pw.print(" multiprocess="); pw.print(info.multiprocess);
|
||||
pw.print(" initOrder="); pw.println(info.initOrder);
|
||||
}
|
||||
}
|
||||
if (clients.size() > 0) {
|
||||
pw.print(prefix); pw.println("Clients:");
|
||||
for (ProcessRecord cproc : clients) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(cproc.toShortString());
|
||||
if (full) {
|
||||
if (hasExternalProcessHandles()) {
|
||||
pw.print(prefix); pw.print("externals=");
|
||||
pw.println(externalProcessTokenToHandle.size());
|
||||
}
|
||||
} else {
|
||||
if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
|
||||
pw.print(prefix); pw.print(connections.size());
|
||||
pw.print(" connections, "); pw.print(externalProcessNoHandleCount);
|
||||
pw.println(" external handles");
|
||||
}
|
||||
}
|
||||
if (connections.size() > 0) {
|
||||
if (full) {
|
||||
pw.print(prefix); pw.println("Connections:");
|
||||
}
|
||||
for (int i=0; i<connections.size(); i++) {
|
||||
ContentProviderConnection conn = connections.get(i);
|
||||
pw.print(prefix); pw.print(" -> "); pw.println(conn.toClientString());
|
||||
if (conn.provider != this) {
|
||||
pw.print(prefix); pw.print(" *** WRONG PROVIDER: ");
|
||||
pw.println(conn.provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,6 +199,17 @@ class ContentProviderRecord extends ContentProviderHolder {
|
||||
return stringName = sb.toString();
|
||||
}
|
||||
|
||||
public String toShortString() {
|
||||
if (shortStringName != null) {
|
||||
return shortStringName;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
||||
sb.append('/');
|
||||
sb.append(name.flattenToShortString());
|
||||
return shortStringName = sb.toString();
|
||||
}
|
||||
|
||||
// This class represents a handle from an external process to a provider.
|
||||
private class ExternalProcessHandle implements DeathRecipient {
|
||||
private static final String LOG_TAG = "ExternalProcessHanldle";
|
||||
|
@ -125,8 +125,8 @@ class ProcessRecord {
|
||||
final HashMap<String, ContentProviderRecord> pubProviders
|
||||
= new HashMap<String, ContentProviderRecord>();
|
||||
// All ContentProviderRecord process is using
|
||||
final HashMap<ContentProviderRecord, Integer> conProviders
|
||||
= new HashMap<ContentProviderRecord, Integer>();
|
||||
final ArrayList<ContentProviderConnection> conProviders
|
||||
= new ArrayList<ContentProviderConnection>();
|
||||
|
||||
boolean persistent; // always keep this application running?
|
||||
boolean crashing; // are we in the process of crashing?
|
||||
@ -257,25 +257,47 @@ class ProcessRecord {
|
||||
pw.println();
|
||||
}
|
||||
if (activities.size() > 0) {
|
||||
pw.print(prefix); pw.print("activities="); pw.println(activities);
|
||||
pw.print(prefix); pw.println("Activities:");
|
||||
for (int i=0; i<activities.size(); i++) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(activities.get(i));
|
||||
}
|
||||
}
|
||||
if (services.size() > 0) {
|
||||
pw.print(prefix); pw.print("services="); pw.println(services);
|
||||
pw.print(prefix); pw.println("Services:");
|
||||
for (ServiceRecord sr : services) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(sr);
|
||||
}
|
||||
}
|
||||
if (executingServices.size() > 0) {
|
||||
pw.print(prefix); pw.print("executingServices="); pw.println(executingServices);
|
||||
pw.print(prefix); pw.println("Executing Services:");
|
||||
for (ServiceRecord sr : executingServices) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(sr);
|
||||
}
|
||||
}
|
||||
if (connections.size() > 0) {
|
||||
pw.print(prefix); pw.print("connections="); pw.println(connections);
|
||||
pw.print(prefix); pw.println("Connections:");
|
||||
for (ConnectionRecord cr : connections) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(cr);
|
||||
}
|
||||
}
|
||||
if (pubProviders.size() > 0) {
|
||||
pw.print(prefix); pw.print("pubProviders="); pw.println(pubProviders);
|
||||
pw.print(prefix); pw.println("Published Providers:");
|
||||
for (HashMap.Entry<String, ContentProviderRecord> ent : pubProviders.entrySet()) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(ent.getKey());
|
||||
pw.print(prefix); pw.print(" -> "); pw.println(ent.getValue());
|
||||
}
|
||||
}
|
||||
if (conProviders.size() > 0) {
|
||||
pw.print(prefix); pw.print("conProviders="); pw.println(conProviders);
|
||||
pw.print(prefix); pw.println("Connected Providers:");
|
||||
for (int i=0; i<conProviders.size(); i++) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(conProviders.get(i).toShortString());
|
||||
}
|
||||
}
|
||||
if (receivers.size() > 0) {
|
||||
pw.print(prefix); pw.print("receivers="); pw.println(receivers);
|
||||
pw.print(prefix); pw.println("Receivers:");
|
||||
for (ReceiverList rl : receivers) {
|
||||
pw.print(prefix); pw.print(" - "); pw.println(rl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,27 +177,9 @@ public class ProviderMap {
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
|
||||
ContentProviderRecord r = e.getValue();
|
||||
if (dumpAll) {
|
||||
pw.print(" * ");
|
||||
pw.println(r);
|
||||
r.dump(pw, " ");
|
||||
} else {
|
||||
pw.print(" * ");
|
||||
pw.println(r);
|
||||
if (r.proc != null) {
|
||||
pw.print(" proc=");
|
||||
pw.println(r.proc);
|
||||
}
|
||||
if (r.launchingApp != null) {
|
||||
pw.print(" launchingApp=");
|
||||
pw.println(r.launchingApp);
|
||||
}
|
||||
if (r.clients.size() > 0 || r.externalProcessNoHandleCount > 0) {
|
||||
pw.print(" "); pw.print(r.clients.size());
|
||||
pw.print(" clients, "); pw.print(r.externalProcessNoHandleCount);
|
||||
pw.println(" external handles");
|
||||
}
|
||||
}
|
||||
pw.print(" * ");
|
||||
pw.println(r);
|
||||
r.dump(pw, " ", dumpAll);
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,7 +192,7 @@ public class ProviderMap {
|
||||
pw.print(" ");
|
||||
pw.print(e.getKey());
|
||||
pw.print(": ");
|
||||
pw.println(r);
|
||||
pw.println(r.toShortString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,10 +203,10 @@ public class ProviderMap {
|
||||
pw.println(" ");
|
||||
pw.println(" Published content providers (by class):");
|
||||
dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
|
||||
pw.println("");
|
||||
}
|
||||
|
||||
if (mProvidersByClassPerUser.size() > 1) {
|
||||
pw.println("");
|
||||
for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
|
||||
HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
|
||||
pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
|
||||
@ -321,7 +303,7 @@ public class ProviderMap {
|
||||
if (r.proc != null) pw.println(r.proc.pid);
|
||||
else pw.println("(not running)");
|
||||
if (dumpAll) {
|
||||
r.dump(pw, innerPrefix);
|
||||
r.dump(pw, innerPrefix, true);
|
||||
}
|
||||
}
|
||||
if (r.proc != null && r.proc.thread != null) {
|
||||
|
@ -118,6 +118,11 @@ public class MockContentResolver extends ContentResolver {
|
||||
return releaseProvider(icp);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public void unstableProviderDied(IContentProvider icp) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)
|
||||
* ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored.
|
||||
|
@ -72,6 +72,11 @@ public class BridgeContentResolver extends ContentResolver {
|
||||
return releaseProvider(icp);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public void unstableProviderDied(IContentProvider icp) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub for the layoutlib bridge content resolver.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user