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();
|
IBinder b = data.readStrongBinder();
|
||||||
IApplicationThread app = ApplicationThreadNative.asInterface(b);
|
IApplicationThread app = ApplicationThreadNative.asInterface(b);
|
||||||
String name = data.readString();
|
String name = data.readString();
|
||||||
ContentProviderHolder cph = getContentProvider(app, name);
|
boolean stable = data.readInt() != 0;
|
||||||
|
ContentProviderHolder cph = getContentProvider(app, name, stable);
|
||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
if (cph != null) {
|
if (cph != null) {
|
||||||
reply.writeInt(1);
|
reply.writeInt(1);
|
||||||
@ -617,12 +618,30 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
|
|||||||
return true;
|
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: {
|
case REMOVE_CONTENT_PROVIDER_TRANSACTION: {
|
||||||
data.enforceInterface(IActivityManager.descriptor);
|
data.enforceInterface(IActivityManager.descriptor);
|
||||||
IBinder b = data.readStrongBinder();
|
IBinder b = data.readStrongBinder();
|
||||||
IApplicationThread app = ApplicationThreadNative.asInterface(b);
|
boolean stable = data.readInt() != 0;
|
||||||
String name = data.readString();
|
removeContentProvider(b, stable);
|
||||||
removeContentProvider(app, name);
|
|
||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2314,13 +2333,13 @@ class ActivityManagerProxy implements IActivityManager
|
|||||||
reply.recycle();
|
reply.recycle();
|
||||||
}
|
}
|
||||||
public ContentProviderHolder getContentProvider(IApplicationThread caller,
|
public ContentProviderHolder getContentProvider(IApplicationThread caller,
|
||||||
String name) throws RemoteException
|
String name, boolean stable) throws RemoteException {
|
||||||
{
|
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
Parcel reply = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
||||||
data.writeInterfaceToken(IActivityManager.descriptor);
|
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||||
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
|
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
|
||||||
data.writeString(name);
|
data.writeString(name);
|
||||||
|
data.writeInt(stable ? 1 : 0);
|
||||||
mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
|
mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
|
||||||
reply.readException();
|
reply.readException();
|
||||||
int res = reply.readInt();
|
int res = reply.readInt();
|
||||||
@ -2364,14 +2383,38 @@ class ActivityManagerProxy implements IActivityManager
|
|||||||
data.recycle();
|
data.recycle();
|
||||||
reply.recycle();
|
reply.recycle();
|
||||||
}
|
}
|
||||||
|
public boolean refContentProvider(IBinder connection, int stable, int unstable)
|
||||||
public void removeContentProvider(IApplicationThread caller,
|
throws RemoteException {
|
||||||
String name) throws RemoteException {
|
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
Parcel reply = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
||||||
data.writeInterfaceToken(IActivityManager.descriptor);
|
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||||
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
|
data.writeStrongBinder(connection);
|
||||||
data.writeString(name);
|
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);
|
mRemote.transact(REMOVE_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
|
||||||
reply.readException();
|
reply.readException();
|
||||||
data.recycle();
|
data.recycle();
|
||||||
|
@ -140,6 +140,7 @@ public final class ActivityThread {
|
|||||||
private static final boolean DEBUG_CONFIGURATION = false;
|
private static final boolean DEBUG_CONFIGURATION = false;
|
||||||
private static final boolean DEBUG_SERVICE = false;
|
private static final boolean DEBUG_SERVICE = false;
|
||||||
private static final boolean DEBUG_MEMORY_TRIM = 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 long MIN_TIME_BETWEEN_GCS = 5*1000;
|
||||||
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
|
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
|
||||||
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
|
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
|
||||||
@ -210,6 +211,8 @@ public final class ActivityThread {
|
|||||||
= new HashMap<IBinder, ProviderRefCount>();
|
= new HashMap<IBinder, ProviderRefCount>();
|
||||||
final HashMap<IBinder, ProviderClientRecord> mLocalProviders
|
final HashMap<IBinder, ProviderClientRecord> mLocalProviders
|
||||||
= new HashMap<IBinder, ProviderClientRecord>();
|
= new HashMap<IBinder, ProviderClientRecord>();
|
||||||
|
final HashMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
|
||||||
|
= new HashMap<ComponentName, ProviderClientRecord>();
|
||||||
|
|
||||||
final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
|
final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
|
||||||
= new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
|
= new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
|
||||||
@ -284,20 +287,19 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ProviderClientRecord implements IBinder.DeathRecipient {
|
final class ProviderClientRecord {
|
||||||
final String mName;
|
final String[] mNames;
|
||||||
final IContentProvider mProvider;
|
final IContentProvider mProvider;
|
||||||
final ContentProvider mLocalProvider;
|
final ContentProvider mLocalProvider;
|
||||||
|
final IActivityManager.ContentProviderHolder mHolder;
|
||||||
|
|
||||||
ProviderClientRecord(String name, IContentProvider provider,
|
ProviderClientRecord(String[] names, IContentProvider provider,
|
||||||
ContentProvider localProvider) {
|
ContentProvider localProvider,
|
||||||
mName = name;
|
IActivityManager.ContentProviderHolder holder) {
|
||||||
|
mNames = names;
|
||||||
mProvider = provider;
|
mProvider = provider;
|
||||||
mLocalProvider = localProvider;
|
mLocalProvider = localProvider;
|
||||||
}
|
mHolder = holder;
|
||||||
|
|
||||||
public void binderDied() {
|
|
||||||
removeDeadProvider(mName, mProvider);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1061,6 +1063,11 @@ public final class ActivityThread {
|
|||||||
pw.flush();
|
pw.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unstableProviderDied(IBinder provider) {
|
||||||
|
queueOrSendMessage(H.UNSTABLE_PROVIDER_DIED, provider);
|
||||||
|
}
|
||||||
|
|
||||||
private void printRow(PrintWriter pw, String format, Object...objs) {
|
private void printRow(PrintWriter pw, String format, Object...objs) {
|
||||||
pw.println(String.format(format, 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 UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
|
||||||
public static final int TRIM_MEMORY = 140;
|
public static final int TRIM_MEMORY = 140;
|
||||||
public static final int DUMP_PROVIDER = 141;
|
public static final int DUMP_PROVIDER = 141;
|
||||||
|
public static final int UNSTABLE_PROVIDER_DIED = 142;
|
||||||
String codeToString(int code) {
|
String codeToString(int code) {
|
||||||
if (DEBUG_MESSAGES) {
|
if (DEBUG_MESSAGES) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
@ -1170,6 +1178,7 @@ public final class ActivityThread {
|
|||||||
case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
|
case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
|
||||||
case TRIM_MEMORY: return "TRIM_MEMORY";
|
case TRIM_MEMORY: return "TRIM_MEMORY";
|
||||||
case DUMP_PROVIDER: return "DUMP_PROVIDER";
|
case DUMP_PROVIDER: return "DUMP_PROVIDER";
|
||||||
|
case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Integer.toString(code);
|
return Integer.toString(code);
|
||||||
@ -1337,7 +1346,7 @@ public final class ActivityThread {
|
|||||||
break;
|
break;
|
||||||
case REMOVE_PROVIDER:
|
case REMOVE_PROVIDER:
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove");
|
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove");
|
||||||
completeRemoveProvider((IContentProvider)msg.obj);
|
completeRemoveProvider((ProviderRefCount)msg.obj);
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||||
break;
|
break;
|
||||||
case ENABLE_JIT:
|
case ENABLE_JIT:
|
||||||
@ -1377,6 +1386,9 @@ public final class ActivityThread {
|
|||||||
handleTrimMemory(msg.arg1);
|
handleTrimMemory(msg.arg1);
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||||
break;
|
break;
|
||||||
|
case UNSTABLE_PROVIDER_DIED:
|
||||||
|
handleUnstableProviderDied((IBinder)msg.obj, false);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
|
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
|
||||||
}
|
}
|
||||||
@ -2867,10 +2879,24 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final class ProviderRefCount {
|
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) {
|
// When this is set, the stable and unstable ref counts are 0 and
|
||||||
count = pCount;
|
// 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);
|
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 {
|
} else {
|
||||||
mInstrumentation = new Instrumentation();
|
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 {
|
try {
|
||||||
mInstrumentation.callApplicationOnCreate(app);
|
mInstrumentation.callApplicationOnCreate(app);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -4159,12 +4187,9 @@ public final class ActivityThread {
|
|||||||
buf.append(": ");
|
buf.append(": ");
|
||||||
buf.append(cpi.name);
|
buf.append(cpi.name);
|
||||||
Log.i(TAG, buf.toString());
|
Log.i(TAG, buf.toString());
|
||||||
IContentProvider cp = installProvider(context, null, cpi,
|
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
|
||||||
false /*noisy*/, true /*noReleaseNeeded*/);
|
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
|
||||||
if (cp != null) {
|
if (cph != null) {
|
||||||
IActivityManager.ContentProviderHolder cph =
|
|
||||||
new IActivityManager.ContentProviderHolder(cpi);
|
|
||||||
cph.provider = cp;
|
|
||||||
cph.noReleaseNeeded = true;
|
cph.noReleaseNeeded = true;
|
||||||
results.add(cph);
|
results.add(cph);
|
||||||
}
|
}
|
||||||
@ -4177,8 +4202,8 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final IContentProvider acquireProvider(Context c, String name) {
|
public final IContentProvider acquireProvider(Context c, String name, boolean stable) {
|
||||||
IContentProvider provider = acquireExistingProvider(c, name);
|
IContentProvider provider = acquireExistingProvider(c, name, stable);
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
@ -4192,7 +4217,7 @@ public final class ActivityThread {
|
|||||||
IActivityManager.ContentProviderHolder holder = null;
|
IActivityManager.ContentProviderHolder holder = null;
|
||||||
try {
|
try {
|
||||||
holder = ActivityManagerNative.getDefault().getContentProvider(
|
holder = ActivityManagerNative.getDefault().getContentProvider(
|
||||||
getApplicationThread(), name);
|
getApplicationThread(), name, stable);
|
||||||
} catch (RemoteException ex) {
|
} catch (RemoteException ex) {
|
||||||
}
|
}
|
||||||
if (holder == null) {
|
if (holder == null) {
|
||||||
@ -4202,23 +4227,79 @@ public final class ActivityThread {
|
|||||||
|
|
||||||
// Install provider will increment the reference count for us, and break
|
// Install provider will increment the reference count for us, and break
|
||||||
// any ties in the race.
|
// any ties in the race.
|
||||||
provider = installProvider(c, holder.provider, holder.info,
|
holder = installProvider(c, holder, holder.info,
|
||||||
true /*noisy*/, holder.noReleaseNeeded);
|
true /*noisy*/, holder.noReleaseNeeded, stable);
|
||||||
if (holder.provider != null && provider != holder.provider) {
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
synchronized (mProviderMap) {
|
||||||
ProviderClientRecord pr = mProviderMap.get(name);
|
ProviderClientRecord pr = mProviderMap.get(name);
|
||||||
if (pr == null) {
|
if (pr == null) {
|
||||||
@ -4232,23 +4313,14 @@ public final class ActivityThread {
|
|||||||
// provider is not reference counted and never needs to be released.
|
// provider is not reference counted and never needs to be released.
|
||||||
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
|
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
|
||||||
if (prc != null) {
|
if (prc != null) {
|
||||||
prc.count += 1;
|
incProviderRefLocked(prc, stable);
|
||||||
if (prc.count == 1) {
|
|
||||||
if (localLOGV) {
|
|
||||||
Slog.v(TAG, "acquireExistingProvider: "
|
|
||||||
+ "snatched provider from the jaws of death");
|
|
||||||
}
|
|
||||||
// Because the provider previously had a reference count of zero,
|
|
||||||
// it was scheduled to be removed. Cancel that.
|
|
||||||
mH.removeMessages(H.REMOVE_PROVIDER, provider);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean releaseProvider(IContentProvider provider) {
|
public final boolean releaseProvider(IContentProvider provider, boolean stable) {
|
||||||
if(provider == null) {
|
if (provider == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4260,55 +4332,98 @@ public final class ActivityThread {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prc.count == 0) {
|
boolean lastRef = false;
|
||||||
if (localLOGV) Slog.v(TAG, "releaseProvider: ref count already 0, how?");
|
if (stable) {
|
||||||
|
if (prc.stableCount == 0) {
|
||||||
|
if (DEBUG_PROVIDER) Slog.v(TAG,
|
||||||
|
"releaseProvider: stable ref count already 0, how?");
|
||||||
return false;
|
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 (lastRef) {
|
||||||
if (prc.count == 0) {
|
if (!prc.removePending) {
|
||||||
// Schedule the actual remove asynchronously, since we don't know the context
|
// Schedule the actual remove asynchronously, since we don't know the context
|
||||||
// this will be called in.
|
// this will be called in.
|
||||||
// TODO: it would be nice to post a delayed message, so
|
// TODO: it would be nice to post a delayed message, so
|
||||||
// if we come back and need the same provider quickly
|
// if we come back and need the same provider quickly
|
||||||
// we will still have it available.
|
// we will still have it available.
|
||||||
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider);
|
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);
|
mH.sendMessage(msg);
|
||||||
|
} else {
|
||||||
|
Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final IContentProvider acquireUnstableProvider(Context c, String name) {
|
final void completeRemoveProvider(ProviderRefCount prc) {
|
||||||
return acquireProvider(c, name);
|
synchronized (mProviderMap) {
|
||||||
}
|
if (!prc.removePending) {
|
||||||
|
|
||||||
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) {
|
|
||||||
// There was a race! Some other client managed to acquire
|
// There was a race! Some other client managed to acquire
|
||||||
// the provider before the removal was completed.
|
// the provider before the removal was completed.
|
||||||
// Abort the removal. We will do it later.
|
// 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");
|
+ "provider still in use");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final IBinder jBinder = prc.holder.provider.asBinder();
|
||||||
|
ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
|
||||||
|
if (existingPrc == prc) {
|
||||||
mProviderRefCountMap.remove(jBinder);
|
mProviderRefCountMap.remove(jBinder);
|
||||||
|
}
|
||||||
|
|
||||||
Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
|
Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
@ -4316,41 +4431,70 @@ public final class ActivityThread {
|
|||||||
IBinder myBinder = pr.mProvider.asBinder();
|
IBinder myBinder = pr.mProvider.asBinder();
|
||||||
if (myBinder == jBinder) {
|
if (myBinder == jBinder) {
|
||||||
iter.remove();
|
iter.remove();
|
||||||
if (pr.mLocalProvider == null) {
|
|
||||||
myBinder.unlinkToDeath(pr, 0);
|
|
||||||
if (remoteProviderName == null) {
|
|
||||||
remoteProviderName = pr.mName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remoteProviderName != null) {
|
|
||||||
try {
|
try {
|
||||||
if (localLOGV) {
|
if (DEBUG_PROVIDER) {
|
||||||
Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
|
Slog.v(TAG, "removeProvider: Invoking ActivityManagerNative."
|
||||||
+ "removeContentProvider(" + remoteProviderName + ")");
|
+ "removeContentProvider(" + prc.holder.info.name + ")");
|
||||||
}
|
}
|
||||||
ActivityManagerNative.getDefault().removeContentProvider(
|
ActivityManagerNative.getDefault().removeContentProvider(
|
||||||
getApplicationThread(), remoteProviderName);
|
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) {
|
} catch (RemoteException e) {
|
||||||
//do nothing content provider object is dead any way
|
//do nothing content provider object is dead any way
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final void removeDeadProvider(String name, IContentProvider provider) {
|
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
|
||||||
synchronized(mProviderMap) {
|
ContentProvider localProvider,IActivityManager.ContentProviderHolder holder) {
|
||||||
ProviderClientRecord pr = mProviderMap.get(name);
|
String names[] = PATTERN_SEMICOLON.split(holder.info.authority);
|
||||||
if (pr != null && pr.mProvider.asBinder() == provider.asBinder()) {
|
ProviderClientRecord pcr = new ProviderClientRecord(names, provider,
|
||||||
Slog.i(TAG, "Removing dead content provider: " + name);
|
localProvider, holder);
|
||||||
ProviderClientRecord removed = mProviderMap.remove(name);
|
for (int i = 0; i < names.length; i++) {
|
||||||
if (removed != null) {
|
ProviderClientRecord existing = mProviderMap.get(names[i]);
|
||||||
removed.mProvider.asBinder().unlinkToDeath(removed, 0);
|
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
|
* and returns the existing provider. This can happen due to concurrent
|
||||||
* attempts to acquire the same provider.
|
* attempts to acquire the same provider.
|
||||||
*/
|
*/
|
||||||
private IContentProvider installProvider(Context context,
|
private IActivityManager.ContentProviderHolder installProvider(Context context,
|
||||||
IContentProvider provider, ProviderInfo info,
|
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
|
||||||
boolean noisy, boolean noReleaseNeeded) {
|
boolean noisy, boolean noReleaseNeeded, boolean stable) {
|
||||||
ContentProvider localProvider = null;
|
ContentProvider localProvider = null;
|
||||||
if (provider == null) {
|
IContentProvider provider;
|
||||||
if (noisy) {
|
if (holder == null) {
|
||||||
|
if (DEBUG_PROVIDER || noisy) {
|
||||||
Slog.d(TAG, "Loading provider " + info.authority + ": "
|
Slog.d(TAG, "Loading provider " + info.authority + ": "
|
||||||
+ info.name);
|
+ info.name);
|
||||||
}
|
}
|
||||||
@ -4409,7 +4554,7 @@ public final class ActivityThread {
|
|||||||
info.applicationInfo.sourceDir);
|
info.applicationInfo.sourceDir);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (false) Slog.v(
|
if (DEBUG_PROVIDER) Slog.v(
|
||||||
TAG, "Instantiating local provider " + info.name);
|
TAG, "Instantiating local provider " + info.name);
|
||||||
// XXX Need to create the correct context for this provider.
|
// XXX Need to create the correct context for this provider.
|
||||||
localProvider.attachInfo(c, info);
|
localProvider.attachInfo(c, info);
|
||||||
@ -4421,76 +4566,72 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if (localLOGV) {
|
} else {
|
||||||
Slog.v(TAG, "Installing external provider " + info.authority + ": "
|
provider = holder.provider;
|
||||||
|
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
|
||||||
+ info.name);
|
+ info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mProviderMap) {
|
IActivityManager.ContentProviderHolder retHolder;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
synchronized (mProviderMap) {
|
||||||
|
if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
|
||||||
|
+ " / " + info.name);
|
||||||
|
IBinder jBinder = provider.asBinder();
|
||||||
if (localProvider != null) {
|
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 (pr != null) {
|
||||||
if (localLOGV) {
|
if (DEBUG_PROVIDER) {
|
||||||
Slog.v(TAG, "installProvider: lost the race, "
|
Slog.v(TAG, "installProvider: lost the race, "
|
||||||
+ "using existing local provider");
|
+ "using existing local provider");
|
||||||
}
|
}
|
||||||
provider = pr.mProvider;
|
provider = pr.mProvider;
|
||||||
} else {
|
} 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);
|
mLocalProviders.put(jBinder, pr);
|
||||||
|
mLocalProvidersByName.put(cname, pr);
|
||||||
|
}
|
||||||
|
retHolder = pr.mHolder;
|
||||||
|
} else {
|
||||||
|
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
|
||||||
|
if (prc != null) {
|
||||||
|
if (DEBUG_PROVIDER) {
|
||||||
|
Slog.v(TAG, "installProvider: lost the race, updating ref count");
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!noReleaseNeeded) {
|
return retHolder;
|
||||||
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
|
|
||||||
if (prc != null) {
|
|
||||||
if (localLOGV) {
|
|
||||||
Slog.v(TAG, "installProvider: lost the race, incrementing ref count");
|
|
||||||
}
|
|
||||||
prc.count += 1;
|
|
||||||
if (prc.count == 1) {
|
|
||||||
if (localLOGV) {
|
|
||||||
Slog.v(TAG, "installProvider: "
|
|
||||||
+ "snatched provider from the jaws of death");
|
|
||||||
}
|
|
||||||
// Because the provider previously had a reference count of zero,
|
|
||||||
// it was scheduled to be removed. Cancel that.
|
|
||||||
mH.removeMessages(H.REMOVE_PROVIDER, provider);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mProviderRefCountMap.put(jBinder, new ProviderRefCount(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return provider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void attach(boolean system) {
|
private void attach(boolean system) {
|
||||||
|
@ -575,6 +575,15 @@ public abstract class ApplicationThreadNative extends Binder
|
|||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
return true;
|
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);
|
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);
|
mRemote.transact(DUMP_DB_INFO_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
|
||||||
data.recycle();
|
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
|
@Override
|
||||||
protected IContentProvider acquireProvider(Context context, String name) {
|
protected IContentProvider acquireProvider(Context context, String name) {
|
||||||
return mMainThread.acquireProvider(context, name);
|
return mMainThread.acquireProvider(context, name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IContentProvider acquireExistingProvider(Context context, String name) {
|
protected IContentProvider acquireExistingProvider(Context context, String name) {
|
||||||
return mMainThread.acquireExistingProvider(context, name);
|
return mMainThread.acquireExistingProvider(context, name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean releaseProvider(IContentProvider provider) {
|
public boolean releaseProvider(IContentProvider provider) {
|
||||||
return mMainThread.releaseProvider(provider);
|
return mMainThread.releaseProvider(provider, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IContentProvider acquireUnstableProvider(Context c, String name) {
|
protected IContentProvider acquireUnstableProvider(Context c, String name) {
|
||||||
return mMainThread.acquireUnstableProvider(c, name);
|
return mMainThread.acquireProvider(c, name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean releaseUnstableProvider(IContentProvider icp) {
|
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;
|
private final ActivityThread mMainThread;
|
||||||
|
@ -116,14 +116,16 @@ public interface IActivityManager extends IInterface {
|
|||||||
public void reportThumbnail(IBinder token,
|
public void reportThumbnail(IBinder token,
|
||||||
Bitmap thumbnail, CharSequence description) throws RemoteException;
|
Bitmap thumbnail, CharSequence description) throws RemoteException;
|
||||||
public ContentProviderHolder getContentProvider(IApplicationThread caller,
|
public ContentProviderHolder getContentProvider(IApplicationThread caller,
|
||||||
String name) throws RemoteException;
|
String name, boolean stable) throws RemoteException;
|
||||||
public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
|
public ContentProviderHolder getContentProviderExternal(String name, IBinder token)
|
||||||
throws RemoteException;
|
throws RemoteException;
|
||||||
public void removeContentProvider(IApplicationThread caller,
|
public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException;
|
||||||
String name) throws RemoteException;
|
|
||||||
public void removeContentProviderExternal(String name, IBinder token) throws RemoteException;
|
public void removeContentProviderExternal(String name, IBinder token) throws RemoteException;
|
||||||
public void publishContentProviders(IApplicationThread caller,
|
public void publishContentProviders(IApplicationThread caller,
|
||||||
List<ContentProviderHolder> providers) throws RemoteException;
|
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)
|
public PendingIntent getRunningServiceControlPanel(ComponentName service)
|
||||||
throws RemoteException;
|
throws RemoteException;
|
||||||
public ComponentName startService(IApplicationThread caller, Intent service,
|
public ComponentName startService(IApplicationThread caller, Intent service,
|
||||||
@ -363,6 +365,7 @@ public interface IActivityManager extends IInterface {
|
|||||||
public static class ContentProviderHolder implements Parcelable {
|
public static class ContentProviderHolder implements Parcelable {
|
||||||
public final ProviderInfo info;
|
public final ProviderInfo info;
|
||||||
public IContentProvider provider;
|
public IContentProvider provider;
|
||||||
|
public IBinder connection;
|
||||||
public boolean noReleaseNeeded;
|
public boolean noReleaseNeeded;
|
||||||
|
|
||||||
public ContentProviderHolder(ProviderInfo _info) {
|
public ContentProviderHolder(ProviderInfo _info) {
|
||||||
@ -380,6 +383,7 @@ public interface IActivityManager extends IInterface {
|
|||||||
} else {
|
} else {
|
||||||
dest.writeStrongBinder(null);
|
dest.writeStrongBinder(null);
|
||||||
}
|
}
|
||||||
|
dest.writeStrongBinder(connection);
|
||||||
dest.writeInt(noReleaseNeeded ? 1:0);
|
dest.writeInt(noReleaseNeeded ? 1:0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,6 +402,7 @@ public interface IActivityManager extends IInterface {
|
|||||||
info = ProviderInfo.CREATOR.createFromParcel(source);
|
info = ProviderInfo.CREATOR.createFromParcel(source);
|
||||||
provider = ContentProviderNative.asInterface(
|
provider = ContentProviderNative.asInterface(
|
||||||
source.readStrongBinder());
|
source.readStrongBinder());
|
||||||
|
connection = source.readStrongBinder();
|
||||||
noReleaseNeeded = source.readInt() != 0;
|
noReleaseNeeded = source.readInt() != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,7 +481,7 @@ public interface IActivityManager extends IInterface {
|
|||||||
int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
|
int REPORT_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
|
||||||
int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
|
int GET_CONTENT_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
|
||||||
int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
|
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 FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
|
||||||
int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
|
int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
|
||||||
int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
|
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 SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147;
|
||||||
int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
|
int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
|
||||||
int GET_LAUNCHED_FROM_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+149;
|
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;
|
String[] args) throws RemoteException;
|
||||||
void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
|
void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException;
|
||||||
void dumpDbInfo(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";
|
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_GFX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+43;
|
||||||
int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
|
int DUMP_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+44;
|
||||||
int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
|
int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
|
||||||
|
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
|
||||||
}
|
}
|
||||||
|
@ -988,6 +988,11 @@ public class Instrumentation {
|
|||||||
* Perform calling of the application's {@link Application#onCreate}
|
* Perform calling of the application's {@link Application#onCreate}
|
||||||
* method. The default implementation simply calls through to that method.
|
* 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.
|
* @param app The application being created.
|
||||||
*/
|
*/
|
||||||
public void callApplicationOnCreate(Application app) {
|
public void callApplicationOnCreate(Application app) {
|
||||||
|
@ -20,6 +20,7 @@ import android.database.Cursor;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.DeadObjectException;
|
||||||
import android.os.ICancellationSignal;
|
import android.os.ICancellationSignal;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
@ -33,11 +34,19 @@ import java.util.ArrayList;
|
|||||||
* calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
|
* calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
|
||||||
* using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
|
* 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.
|
* 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 {
|
public class ContentProviderClient {
|
||||||
private final IContentProvider mContentProvider;
|
private final IContentProvider mContentProvider;
|
||||||
private final ContentResolver mContentResolver;
|
private final ContentResolver mContentResolver;
|
||||||
private final boolean mStable;
|
private final boolean mStable;
|
||||||
|
private boolean mReleased;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
@ -52,7 +61,14 @@ public class ContentProviderClient {
|
|||||||
/** See {@link ContentProvider#query ContentProvider.query} */
|
/** See {@link ContentProvider#query ContentProvider.query} */
|
||||||
public Cursor query(Uri url, String[] projection, String selection,
|
public Cursor query(Uri url, String[] projection, String selection,
|
||||||
String[] selectionArgs, String sortOrder) throws RemoteException {
|
String[] selectionArgs, String sortOrder) throws RemoteException {
|
||||||
|
try {
|
||||||
return query(url, projection, selection, selectionArgs, sortOrder, null);
|
return query(url, projection, selection, selectionArgs, sortOrder, null);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#query ContentProvider.query} */
|
/** See {@link ContentProvider#query ContentProvider.query} */
|
||||||
@ -64,41 +80,90 @@ public class ContentProviderClient {
|
|||||||
remoteCancellationSignal = mContentProvider.createCancellationSignal();
|
remoteCancellationSignal = mContentProvider.createCancellationSignal();
|
||||||
cancellationSignal.setRemote(remoteCancellationSignal);
|
cancellationSignal.setRemote(remoteCancellationSignal);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder,
|
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder,
|
||||||
remoteCancellationSignal);
|
remoteCancellationSignal);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#getType ContentProvider.getType} */
|
/** See {@link ContentProvider#getType ContentProvider.getType} */
|
||||||
public String getType(Uri url) throws RemoteException {
|
public String getType(Uri url) throws RemoteException {
|
||||||
|
try {
|
||||||
return mContentProvider.getType(url);
|
return mContentProvider.getType(url);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
|
/** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
|
||||||
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
|
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
|
||||||
|
try {
|
||||||
return mContentProvider.getStreamTypes(url, mimeTypeFilter);
|
return mContentProvider.getStreamTypes(url, mimeTypeFilter);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#insert ContentProvider.insert} */
|
/** See {@link ContentProvider#insert ContentProvider.insert} */
|
||||||
public Uri insert(Uri url, ContentValues initialValues)
|
public Uri insert(Uri url, ContentValues initialValues)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
|
try {
|
||||||
return mContentProvider.insert(url, initialValues);
|
return mContentProvider.insert(url, initialValues);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
|
/** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
|
||||||
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
|
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
|
||||||
|
try {
|
||||||
return mContentProvider.bulkInsert(url, initialValues);
|
return mContentProvider.bulkInsert(url, initialValues);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#delete ContentProvider.delete} */
|
/** See {@link ContentProvider#delete ContentProvider.delete} */
|
||||||
public int delete(Uri url, String selection, String[] selectionArgs)
|
public int delete(Uri url, String selection, String[] selectionArgs)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
|
try {
|
||||||
return mContentProvider.delete(url, selection, selectionArgs);
|
return mContentProvider.delete(url, selection, selectionArgs);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#update ContentProvider.update} */
|
/** See {@link ContentProvider#update ContentProvider.update} */
|
||||||
public int update(Uri url, ContentValues values, String selection,
|
public int update(Uri url, ContentValues values, String selection,
|
||||||
String[] selectionArgs) throws RemoteException {
|
String[] selectionArgs) throws RemoteException {
|
||||||
|
try {
|
||||||
return mContentProvider.update(url, values, selection, selectionArgs);
|
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)
|
public ParcelFileDescriptor openFile(Uri url, String mode)
|
||||||
throws RemoteException, FileNotFoundException {
|
throws RemoteException, FileNotFoundException {
|
||||||
|
try {
|
||||||
return mContentProvider.openFile(url, mode);
|
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)
|
public AssetFileDescriptor openAssetFile(Uri url, String mode)
|
||||||
throws RemoteException, FileNotFoundException {
|
throws RemoteException, FileNotFoundException {
|
||||||
|
try {
|
||||||
return mContentProvider.openAssetFile(url, mode);
|
return mContentProvider.openAssetFile(url, mode);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
|
/** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
|
||||||
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
|
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
|
||||||
String mimeType, Bundle opts)
|
String mimeType, Bundle opts)
|
||||||
throws RemoteException, FileNotFoundException {
|
throws RemoteException, FileNotFoundException {
|
||||||
|
try {
|
||||||
return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
|
return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
|
/** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
|
||||||
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
|
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
|
||||||
throws RemoteException, OperationApplicationException {
|
throws RemoteException, OperationApplicationException {
|
||||||
|
try {
|
||||||
return mContentProvider.applyBatch(operations);
|
return mContentProvider.applyBatch(operations);
|
||||||
|
} catch (DeadObjectException e) {
|
||||||
|
if (!mStable) {
|
||||||
|
mContentResolver.unstableProviderDied(mContentProvider);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,12 +237,18 @@ public class ContentProviderClient {
|
|||||||
* @return true if this was release, false if it was already released
|
* @return true if this was release, false if it was already released
|
||||||
*/
|
*/
|
||||||
public boolean release() {
|
public boolean release() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (mReleased) {
|
||||||
|
throw new IllegalStateException("Already released");
|
||||||
|
}
|
||||||
|
mReleased = true;
|
||||||
if (mStable) {
|
if (mStable) {
|
||||||
return mContentResolver.releaseProvider(mContentProvider);
|
return mContentResolver.releaseProvider(mContentProvider);
|
||||||
} else {
|
} else {
|
||||||
return mContentResolver.releaseUnstableProvider(mContentProvider);
|
return mContentResolver.releaseUnstableProvider(mContentProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a reference to the {@link ContentProvider} that is associated with this
|
* Get a reference to the {@link ContentProvider} that is associated with this
|
||||||
|
@ -20,27 +20,24 @@ import dalvik.system.CloseGuard;
|
|||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.app.ActivityManagerNative;
|
import android.app.ActivityManagerNative;
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.AppGlobals;
|
import android.app.AppGlobals;
|
||||||
import android.content.ContentProvider.Transport;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.res.AssetFileDescriptor;
|
import android.content.res.AssetFileDescriptor;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.database.CrossProcessCursorWrapper;
|
import android.database.CrossProcessCursorWrapper;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.CursorWrapper;
|
|
||||||
import android.database.IContentObserver;
|
import android.database.IContentObserver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.DeadObjectException;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ICancellationSignal;
|
import android.os.ICancellationSignal;
|
||||||
import android.os.OperationCanceledException;
|
import android.os.OperationCanceledException;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.StrictMode;
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
@ -202,6 +199,8 @@ public abstract class ContentResolver {
|
|||||||
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
|
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public abstract boolean releaseUnstableProvider(IContentProvider icp);
|
public abstract boolean releaseUnstableProvider(IContentProvider icp);
|
||||||
|
/** @hide */
|
||||||
|
public abstract void unstableProviderDied(IContentProvider icp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the MIME type of the given content URL.
|
* 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
|
* @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) {
|
public final String getType(Uri url) {
|
||||||
|
// XXX would like to have an acquireExistingUnstableProvider for this.
|
||||||
IContentProvider provider = acquireExistingProvider(url);
|
IContentProvider provider = acquireExistingProvider(url);
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
try {
|
try {
|
||||||
@ -351,23 +351,37 @@ public abstract class ContentResolver {
|
|||||||
public final Cursor query(final Uri uri, String[] projection,
|
public final Cursor query(final Uri uri, String[] projection,
|
||||||
String selection, String[] selectionArgs, String sortOrder,
|
String selection, String[] selectionArgs, String sortOrder,
|
||||||
CancellationSignal cancellationSignal) {
|
CancellationSignal cancellationSignal) {
|
||||||
IContentProvider provider = acquireProvider(uri);
|
IContentProvider unstableProvider = acquireUnstableProvider(uri);
|
||||||
if (provider == null) {
|
if (unstableProvider == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
IContentProvider stableProvider = null;
|
||||||
try {
|
try {
|
||||||
long startTime = SystemClock.uptimeMillis();
|
long startTime = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
ICancellationSignal remoteCancellationSignal = null;
|
ICancellationSignal remoteCancellationSignal = null;
|
||||||
if (cancellationSignal != null) {
|
if (cancellationSignal != null) {
|
||||||
cancellationSignal.throwIfCanceled();
|
cancellationSignal.throwIfCanceled();
|
||||||
remoteCancellationSignal = provider.createCancellationSignal();
|
remoteCancellationSignal = unstableProvider.createCancellationSignal();
|
||||||
cancellationSignal.setRemote(remoteCancellationSignal);
|
cancellationSignal.setRemote(remoteCancellationSignal);
|
||||||
}
|
}
|
||||||
Cursor qCursor = provider.query(uri, projection,
|
Cursor qCursor;
|
||||||
|
try {
|
||||||
|
qCursor = unstableProvider.query(uri, projection,
|
||||||
selection, selectionArgs, sortOrder, remoteCancellationSignal);
|
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) {
|
if (qCursor == null) {
|
||||||
releaseProvider(provider);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// force query execution
|
// force query execution
|
||||||
@ -375,16 +389,21 @@ public abstract class ContentResolver {
|
|||||||
long durationMillis = SystemClock.uptimeMillis() - startTime;
|
long durationMillis = SystemClock.uptimeMillis() - startTime;
|
||||||
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
|
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
|
||||||
// Wrap the cursor object into CursorWrapperInner object
|
// 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) {
|
} catch (RemoteException e) {
|
||||||
releaseProvider(provider);
|
|
||||||
|
|
||||||
// Arbitrary and not worth documenting, as Activity
|
// Arbitrary and not worth documenting, as Activity
|
||||||
// Manager will kill this process shortly anyway.
|
// Manager will kill this process shortly anyway.
|
||||||
return null;
|
return null;
|
||||||
} catch (RuntimeException e) {
|
} finally {
|
||||||
releaseProvider(provider);
|
if (unstableProvider != null) {
|
||||||
throw e;
|
releaseUnstableProvider(unstableProvider);
|
||||||
|
}
|
||||||
|
if (stableProvider != null) {
|
||||||
|
releaseProvider(stableProvider);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,48 +611,62 @@ public abstract class ContentResolver {
|
|||||||
if ("r".equals(mode)) {
|
if ("r".equals(mode)) {
|
||||||
return openTypedAssetFileDescriptor(uri, "*/*", null);
|
return openTypedAssetFileDescriptor(uri, "*/*", null);
|
||||||
} else {
|
} else {
|
||||||
int n = 0;
|
IContentProvider unstableProvider = acquireUnstableProvider(uri);
|
||||||
while (true) {
|
if (unstableProvider == null) {
|
||||||
n++;
|
|
||||||
IContentProvider provider = acquireUnstableProvider(uri);
|
|
||||||
if (provider == null) {
|
|
||||||
throw new FileNotFoundException("No content provider: " + uri);
|
throw new FileNotFoundException("No content provider: " + uri);
|
||||||
}
|
}
|
||||||
|
IContentProvider stableProvider = null;
|
||||||
|
AssetFileDescriptor fd = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
|
try {
|
||||||
|
fd = unstableProvider.openAssetFile(uri, mode);
|
||||||
if (fd == null) {
|
if (fd == null) {
|
||||||
// The provider will be released by the finally{} clause
|
// The provider will be released by the finally{} clause
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
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(
|
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
|
||||||
fd.getParcelFileDescriptor(), provider);
|
fd.getParcelFileDescriptor(), stableProvider);
|
||||||
|
|
||||||
// Success! Don't release the provider when exiting, let
|
// Success! Don't release the provider when exiting, let
|
||||||
// ParcelFileDescriptorInner do that when it is closed.
|
// ParcelFileDescriptorInner do that when it is closed.
|
||||||
provider = null;
|
stableProvider = null;
|
||||||
|
|
||||||
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
|
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
|
||||||
fd.getDeclaredLength());
|
fd.getDeclaredLength());
|
||||||
|
|
||||||
} catch (RemoteException e) {
|
} 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;
|
|
||||||
}
|
|
||||||
// Whatever, whatever, we'll go away.
|
// Whatever, whatever, we'll go away.
|
||||||
throw new FileNotFoundException("Dead content provider: " + uri);
|
throw new FileNotFoundException(
|
||||||
|
"Failed opening content provider: " + uri);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
if (provider != null) {
|
if (stableProvider != null) {
|
||||||
releaseUnstableProvider(provider);
|
releaseProvider(stableProvider);
|
||||||
}
|
}
|
||||||
|
if (unstableProvider != null) {
|
||||||
|
releaseUnstableProvider(unstableProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -670,48 +703,62 @@ public abstract class ContentResolver {
|
|||||||
*/
|
*/
|
||||||
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
|
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
|
||||||
String mimeType, Bundle opts) throws FileNotFoundException {
|
String mimeType, Bundle opts) throws FileNotFoundException {
|
||||||
int n = 0;
|
IContentProvider unstableProvider = acquireUnstableProvider(uri);
|
||||||
while (true) {
|
if (unstableProvider == null) {
|
||||||
n++;
|
|
||||||
IContentProvider provider = acquireUnstableProvider(uri);
|
|
||||||
if (provider == null) {
|
|
||||||
throw new FileNotFoundException("No content provider: " + uri);
|
throw new FileNotFoundException("No content provider: " + uri);
|
||||||
}
|
}
|
||||||
|
IContentProvider stableProvider = null;
|
||||||
|
AssetFileDescriptor fd = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
|
try {
|
||||||
|
fd = unstableProvider.openTypedAssetFile(uri, mimeType, opts);
|
||||||
if (fd == null) {
|
if (fd == null) {
|
||||||
// The provider will be released by the finally{} clause
|
// The provider will be released by the finally{} clause
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
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(
|
ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
|
||||||
fd.getParcelFileDescriptor(), provider);
|
fd.getParcelFileDescriptor(), stableProvider);
|
||||||
|
|
||||||
// Success! Don't release the provider when exiting, let
|
// Success! Don't release the provider when exiting, let
|
||||||
// ParcelFileDescriptorInner do that when it is closed.
|
// ParcelFileDescriptorInner do that when it is closed.
|
||||||
provider = null;
|
stableProvider = null;
|
||||||
|
|
||||||
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
|
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
|
||||||
fd.getDeclaredLength());
|
fd.getDeclaredLength());
|
||||||
|
|
||||||
} catch (RemoteException e) {
|
} 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;
|
|
||||||
}
|
|
||||||
// Whatever, whatever, we'll go away.
|
// Whatever, whatever, we'll go away.
|
||||||
throw new FileNotFoundException("Dead content provider: " + uri);
|
throw new FileNotFoundException(
|
||||||
|
"Failed opening content provider: " + uri);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
if (provider != null) {
|
if (stableProvider != null) {
|
||||||
releaseUnstableProvider(provider);
|
releaseProvider(stableProvider);
|
||||||
}
|
}
|
||||||
|
if (unstableProvider != null) {
|
||||||
|
releaseUnstableProvider(unstableProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1061,7 +1108,7 @@ public abstract class ContentResolver {
|
|||||||
if (name == null) {
|
if (name == null) {
|
||||||
return 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
|
* 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
|
* 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
|
* 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) {
|
public final ContentProviderClient acquireUnstableContentProviderClient(Uri uri) {
|
||||||
IContentProvider provider = acquireProvider(uri);
|
IContentProvider provider = acquireUnstableProvider(uri);
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
return new ContentProviderClient(this, provider, false);
|
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
|
* 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
|
* 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
|
* 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) {
|
public final ContentProviderClient acquireUnstableContentProviderClient(String name) {
|
||||||
IContentProvider provider = acquireProvider(name);
|
IContentProvider provider = acquireUnstableProvider(name);
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
return new ContentProviderClient(this, provider, false);
|
return new ContentProviderClient(this, provider, false);
|
||||||
}
|
}
|
||||||
@ -1780,7 +1837,6 @@ public abstract class ContentResolver {
|
|||||||
|
|
||||||
private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
|
private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
|
||||||
private final IContentProvider mContentProvider;
|
private final IContentProvider mContentProvider;
|
||||||
public static final String TAG="ParcelFileDescriptorInner";
|
|
||||||
private boolean mReleaseProviderFlag = false;
|
private boolean mReleaseProviderFlag = false;
|
||||||
|
|
||||||
ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
|
ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
|
||||||
@ -1792,7 +1848,7 @@ public abstract class ContentResolver {
|
|||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if(!mReleaseProviderFlag) {
|
if(!mReleaseProviderFlag) {
|
||||||
super.close();
|
super.close();
|
||||||
ContentResolver.this.releaseUnstableProvider(mContentProvider);
|
ContentResolver.this.releaseProvider(mContentProvider);
|
||||||
mReleaseProviderFlag = true;
|
mReleaseProviderFlag = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,7 @@ import android.content.ContentProvider;
|
|||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.IContentProvider;
|
||||||
import android.content.IIntentReceiver;
|
import android.content.IIntentReceiver;
|
||||||
import android.content.IIntentSender;
|
import android.content.IIntentSender;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -1810,14 +1811,13 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (app.conProviders.size() > 0) {
|
for (int j=app.conProviders.size()-1; j>=0; j--) {
|
||||||
for (ContentProviderRecord cpr : app.conProviders.keySet()) {
|
ContentProviderRecord cpr = app.conProviders.get(j).provider;
|
||||||
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
|
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) {
|
||||||
updateLruProcessInternalLocked(cpr.proc, oomAdj,
|
updateLruProcessInternalLocked(cpr.proc, oomAdj,
|
||||||
updateActivityTime, i+1);
|
updateActivityTime, i+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//Slog.i(TAG, "Putting proc to front: " + app.processName);
|
//Slog.i(TAG, "Putting proc to front: " + app.processName);
|
||||||
if (oomAdj) {
|
if (oomAdj) {
|
||||||
@ -3742,7 +3742,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
|
|
||||||
N = providers.size();
|
N = providers.size();
|
||||||
for (i=0; i<N; i++) {
|
for (i=0; i<N; i++) {
|
||||||
removeDyingProviderLocked(null, providers.get(i));
|
removeDyingProviderLocked(null, providers.get(i), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doit) {
|
if (doit) {
|
||||||
@ -6058,53 +6058,72 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
|
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
|
||||||
IBinder externalProcessToken) {
|
final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
Integer cnt = r.conProviders.get(cpr);
|
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,
|
if (DEBUG_PROVIDER) Slog.v(TAG,
|
||||||
"Adding provider requested by "
|
"Adding provider requested by "
|
||||||
+ r.processName + " from process "
|
+ r.processName + " from process "
|
||||||
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
|
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
|
||||||
+ " cnt=" + (cnt == null ? 1 : cnt));
|
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
|
||||||
if (cnt == null) {
|
if (stable) {
|
||||||
cpr.clients.add(r);
|
conn.stableCount++;
|
||||||
r.conProviders.put(cpr, new Integer(1));
|
conn.numStableIncs++;
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
|
conn.unstableCount++;
|
||||||
|
conn.numUnstableIncs++;
|
||||||
}
|
}
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
|
||||||
|
if (stable) {
|
||||||
|
conn.stableCount = 1;
|
||||||
|
conn.numStableIncs = 1;
|
||||||
} else {
|
} else {
|
||||||
|
conn.unstableCount = 1;
|
||||||
|
conn.numUnstableIncs = 1;
|
||||||
|
}
|
||||||
|
cpr.connections.add(conn);
|
||||||
|
r.conProviders.add(conn);
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
cpr.addExternalProcessHandleLocked(externalProcessToken);
|
cpr.addExternalProcessHandleLocked(externalProcessToken);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 "
|
||||||
|
+ conn.client.processName + " from process "
|
||||||
|
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
|
||||||
|
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
|
||||||
|
if (stable) {
|
||||||
|
conn.stableCount--;
|
||||||
|
} else {
|
||||||
|
conn.unstableCount--;
|
||||||
|
}
|
||||||
|
if (conn.stableCount == 0 && conn.unstableCount == 0) {
|
||||||
|
cpr.connections.remove(conn);
|
||||||
|
conn.client.conProviders.remove(conn);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
|
|
||||||
IBinder externalProcessToken) {
|
|
||||||
if (r != null) {
|
|
||||||
Integer cnt = r.conProviders.get(cpr);
|
|
||||||
if (DEBUG_PROVIDER) Slog.v(TAG,
|
|
||||||
"Removing provider requested by "
|
|
||||||
+ r.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;
|
|
||||||
} else {
|
|
||||||
r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cpr.removeExternalProcessHandleLocked(externalProcessToken);
|
cpr.removeExternalProcessHandleLocked(externalProcessToken);
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
|
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
|
||||||
String name, IBinder token) {
|
String name, IBinder token, boolean stable) {
|
||||||
ContentProviderRecord cpr;
|
ContentProviderRecord cpr;
|
||||||
|
ContentProviderConnection conn = null;
|
||||||
ProviderInfo cpi = null;
|
ProviderInfo cpi = null;
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
@ -6135,20 +6154,19 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
// of being published... but it is also allowed to run
|
// of being published... but it is also allowed to run
|
||||||
// in the caller's process, so don't make a connection
|
// in the caller's process, so don't make a connection
|
||||||
// and just let the caller instantiate its own instance.
|
// and just let the caller instantiate its own instance.
|
||||||
if (cpr.provider != null) {
|
ContentProviderHolder holder = cpr.newHolder(null);
|
||||||
// don't give caller the provider object, it needs
|
// don't give caller the provider object, it needs
|
||||||
// to make its own.
|
// to make its own.
|
||||||
cpr = new ContentProviderRecord(cpr);
|
holder.provider = null;
|
||||||
}
|
return holder;
|
||||||
return cpr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final long origId = Binder.clearCallingIdentity();
|
final long origId = Binder.clearCallingIdentity();
|
||||||
|
|
||||||
// In this case the provider instance already exists, so we can
|
// In this case the provider instance already exists, so we can
|
||||||
// return it right away.
|
// return it right away.
|
||||||
final boolean countChanged = incProviderCount(r, cpr, token);
|
conn = incProviderCountLocked(r, cpr, token, stable);
|
||||||
if (countChanged) {
|
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
|
||||||
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
|
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
|
||||||
// If this is a perceptible app accessing the provider,
|
// If this is a perceptible app accessing the provider,
|
||||||
// make sure to count it as being accessed and thus
|
// make sure to count it as being accessed and thus
|
||||||
@ -6181,7 +6199,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
Slog.i(TAG,
|
Slog.i(TAG,
|
||||||
"Existing provider " + cpr.name.flattenToShortString()
|
"Existing provider " + cpr.name.flattenToShortString()
|
||||||
+ " is crashing; detaching " + r);
|
+ " 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);
|
appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);
|
||||||
if (!lastRef) {
|
if (!lastRef) {
|
||||||
// This wasn't the last ref our process had on
|
// This wasn't the last ref our process had on
|
||||||
@ -6189,6 +6207,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
providerRunning = false;
|
providerRunning = false;
|
||||||
|
conn = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6251,7 +6270,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
// info and allow the caller to instantiate it. Only do
|
// info and allow the caller to instantiate it. Only do
|
||||||
// this if the provider is the same user as the caller's
|
// this if the provider is the same user as the caller's
|
||||||
// process, or can run as root (so can be in any process).
|
// process, or can run as root (so can be in any process).
|
||||||
return cpr;
|
return cpr.newHolder(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_PROVIDER) {
|
if (DEBUG_PROVIDER) {
|
||||||
@ -6312,7 +6331,10 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
}
|
}
|
||||||
|
|
||||||
mProviderMap.putProviderByName(name, cpr);
|
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="
|
Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
|
||||||
+ cpr.launchingApp);
|
+ cpr.launchingApp);
|
||||||
}
|
}
|
||||||
|
if (conn != null) {
|
||||||
|
conn.waiting = true;
|
||||||
|
}
|
||||||
cpr.wait();
|
cpr.wait();
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
|
} finally {
|
||||||
|
if (conn != null) {
|
||||||
|
conn.waiting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cpr;
|
}
|
||||||
|
return cpr != null ? cpr.newHolder(conn) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ContentProviderHolder getContentProvider(
|
public final ContentProviderHolder getContentProvider(
|
||||||
IApplicationThread caller, String name) {
|
IApplicationThread caller, String name, boolean stable) {
|
||||||
enforceNotIsolatedCaller("getContentProvider");
|
enforceNotIsolatedCaller("getContentProvider");
|
||||||
if (caller == null) {
|
if (caller == null) {
|
||||||
String msg = "null IApplicationThread when getting content provider "
|
String msg = "null IApplicationThread when getting content provider "
|
||||||
@ -6352,7 +6381,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
throw new SecurityException(msg);
|
throw new SecurityException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getContentProviderImpl(caller, name, null);
|
return getContentProviderImpl(caller, name, null, stable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
|
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
|
||||||
@ -6362,48 +6391,33 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
|
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
|
* Drop a content provider from a ProcessRecord's bookkeeping
|
||||||
* @param cpr
|
* @param cpr
|
||||||
*/
|
*/
|
||||||
public void removeContentProvider(IApplicationThread caller, String name) {
|
public void removeContentProvider(IBinder connection, boolean stable) {
|
||||||
enforceNotIsolatedCaller("removeContentProvider");
|
enforceNotIsolatedCaller("removeContentProvider");
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
int userId = UserId.getUserId(Binder.getCallingUid());
|
ContentProviderConnection conn;
|
||||||
ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
|
try {
|
||||||
if(cpr == null) {
|
conn = (ContentProviderConnection)connection;
|
||||||
// remove from mProvidersByClass
|
} catch (ClassCastException e) {
|
||||||
if (DEBUG_PROVIDER) Slog.v(TAG, name +
|
String msg ="removeContentProvider: " + connection
|
||||||
" provider not found in providers list");
|
+ " not a ContentProviderConnection";
|
||||||
return;
|
Slog.w(TAG, msg);
|
||||||
|
throw new IllegalArgumentException(msg);
|
||||||
}
|
}
|
||||||
final ProcessRecord r = getRecordForAppLocked(caller);
|
if (conn == null) {
|
||||||
if (r == null) {
|
throw new NullPointerException("connection is null");
|
||||||
throw new SecurityException(
|
|
||||||
"Unable to find app for caller " + caller +
|
|
||||||
" when removing content provider " + name);
|
|
||||||
}
|
}
|
||||||
//update content provider record entry info
|
if (decProviderCountLocked(conn, null, null, stable)) {
|
||||||
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();
|
updateOomAdjLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void removeContentProviderExternal(String name, IBinder token) {
|
public void removeContentProviderExternal(String name, IBinder token) {
|
||||||
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
|
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
|
||||||
@ -6447,7 +6461,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
}
|
}
|
||||||
|
|
||||||
enforceNotIsolatedCaller("publishContentProviders");
|
enforceNotIsolatedCaller("publishContentProviders");
|
||||||
synchronized(this) {
|
synchronized (this) {
|
||||||
final ProcessRecord r = getRecordForAppLocked(caller);
|
final ProcessRecord r = getRecordForAppLocked(caller);
|
||||||
if (DEBUG_MU)
|
if (DEBUG_MU)
|
||||||
Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
|
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() {
|
public static final void installSystemProviders() {
|
||||||
List<ProviderInfo> providers;
|
List<ProviderInfo> providers;
|
||||||
synchronized (mSelf) {
|
synchronized (mSelf) {
|
||||||
@ -10515,22 +10626,35 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
app.executingServices.clear();
|
app.executingServices.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final void removeDyingProviderLocked(ProcessRecord proc,
|
private final boolean removeDyingProviderLocked(ProcessRecord proc,
|
||||||
ContentProviderRecord cpr) {
|
ContentProviderRecord cpr, boolean always) {
|
||||||
|
final boolean inLaunching = mLaunchingProviders.contains(cpr);
|
||||||
|
|
||||||
|
if (!inLaunching || always) {
|
||||||
synchronized (cpr) {
|
synchronized (cpr) {
|
||||||
cpr.launchingApp = null;
|
cpr.launchingApp = null;
|
||||||
cpr.notifyAll();
|
cpr.notifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
|
mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
|
||||||
String names[] = cpr.info.authority.split(";");
|
String names[] = cpr.info.authority.split(";");
|
||||||
for (int j = 0; j < names.length; j++) {
|
for (int j = 0; j < names.length; j++) {
|
||||||
mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
|
mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Iterator<ProcessRecord> cit = cpr.clients.iterator();
|
for (int i=0; i<cpr.connections.size(); i++) {
|
||||||
while (cit.hasNext()) {
|
ContentProviderConnection conn = cpr.connections.get(i);
|
||||||
ProcessRecord capp = cit.next();
|
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
|
if (!capp.persistent && capp.thread != null
|
||||||
&& capp.pid != 0
|
&& capp.pid != 0
|
||||||
&& capp.pid != MY_PID) {
|
&& capp.pid != MY_PID) {
|
||||||
@ -10542,10 +10666,23 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
+ cpr.name.toShortString());
|
+ cpr.name.toShortString());
|
||||||
Process.killProcessQuiet(capp.pid);
|
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);
|
mLaunchingProviders.remove(cpr);
|
||||||
}
|
}
|
||||||
|
return inLaunching;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main code for cleaning up a process when it has gone away. This is
|
* Main code for cleaning up a process when it has gone away. This is
|
||||||
@ -10590,34 +10727,21 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
|
|
||||||
boolean restart = false;
|
boolean restart = false;
|
||||||
|
|
||||||
int NL = mLaunchingProviders.size();
|
|
||||||
|
|
||||||
// Remove published content providers.
|
// Remove published content providers.
|
||||||
if (!app.pubProviders.isEmpty()) {
|
if (!app.pubProviders.isEmpty()) {
|
||||||
Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
|
Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
ContentProviderRecord cpr = it.next();
|
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.provider = null;
|
||||||
cpr.proc = 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();
|
app.pubProviders.clear();
|
||||||
}
|
}
|
||||||
@ -10629,10 +10753,9 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
|
|
||||||
// Unregister from connected content providers.
|
// Unregister from connected content providers.
|
||||||
if (!app.conProviders.isEmpty()) {
|
if (!app.conProviders.isEmpty()) {
|
||||||
Iterator it = app.conProviders.keySet().iterator();
|
for (int i=0; i<app.conProviders.size(); i++) {
|
||||||
while (it.hasNext()) {
|
ContentProviderConnection conn = app.conProviders.get(i);
|
||||||
ContentProviderRecord cpr = (ContentProviderRecord)it.next();
|
conn.provider.connections.remove(conn);
|
||||||
cpr.clients.remove(app);
|
|
||||||
}
|
}
|
||||||
app.conProviders.clear();
|
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
|
// XXX Commented out for now. Trying to figure out a way to reproduce
|
||||||
// the actual situation to identify what is actually going on.
|
// the actual situation to identify what is actually going on.
|
||||||
if (false) {
|
if (false) {
|
||||||
for (int i=0; i<NL; i++) {
|
for (int i=0; i<mLaunchingProviders.size(); i++) {
|
||||||
ContentProviderRecord cpr = (ContentProviderRecord)
|
ContentProviderRecord cpr = (ContentProviderRecord)
|
||||||
mLaunchingProviders.get(i);
|
mLaunchingProviders.get(i);
|
||||||
if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) {
|
if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
|
||||||
synchronized (cpr) {
|
synchronized (cpr) {
|
||||||
cpr.launchingApp = null;
|
cpr.launchingApp = null;
|
||||||
cpr.notifyAll();
|
cpr.notifyAll();
|
||||||
@ -10743,7 +10866,7 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
if (!alwaysBad && !app.bad) {
|
if (!alwaysBad && !app.bad) {
|
||||||
restart = true;
|
restart = true;
|
||||||
} else {
|
} else {
|
||||||
removeDyingProviderLocked(app, cpr);
|
removeDyingProviderLocked(app, cpr, true);
|
||||||
NL = mLaunchingProviders.size();
|
NL = mLaunchingProviders.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13948,10 +14071,12 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ
|
while (jt.hasNext() && (adj > ProcessList.FOREGROUND_APP_ADJ
|
||||||
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
|
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
|
||||||
ContentProviderRecord cpr = jt.next();
|
ContentProviderRecord cpr = jt.next();
|
||||||
if (cpr.clients.size() != 0) {
|
for (int i = cpr.connections.size()-1;
|
||||||
Iterator<ProcessRecord> kt = cpr.clients.iterator();
|
i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|
||||||
while (kt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
|
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
|
||||||
ProcessRecord client = kt.next();
|
i--) {
|
||||||
|
ContentProviderConnection conn = cpr.connections.get(i);
|
||||||
|
ProcessRecord client = conn.client;
|
||||||
if (client == app) {
|
if (client == app) {
|
||||||
// Being our own client is not interesting.
|
// Being our own client is not interesting.
|
||||||
continue;
|
continue;
|
||||||
@ -13991,7 +14116,6 @@ public final class ActivityManagerService extends ActivityManagerNative
|
|||||||
schedGroup = Process.THREAD_GROUP_DEFAULT;
|
schedGroup = Process.THREAD_GROUP_DEFAULT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// If the provider has external (non-framework) process
|
// If the provider has external (non-framework) process
|
||||||
// dependencies, ensure that its adjustment is at least
|
// dependencies, ensure that its adjustment is at least
|
||||||
// FOREGROUND_APP_ADJ.
|
// FOREGROUND_APP_ADJ.
|
||||||
|
@ -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.app.IActivityManager.ContentProviderHolder;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.IContentProvider;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.ProviderInfo;
|
import android.content.pm.ProviderInfo;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@ -27,28 +28,35 @@ import android.os.RemoteException;
|
|||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
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
|
// 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
|
// Handles for non-framework processes supported by this provider
|
||||||
HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
|
HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
|
||||||
// Count for external process for which we have no handles.
|
// Count for external process for which we have no handles.
|
||||||
int externalProcessNoHandleCount;
|
int externalProcessNoHandleCount;
|
||||||
final ActivityManagerService service;
|
|
||||||
final int uid;
|
|
||||||
final ApplicationInfo appInfo;
|
|
||||||
final ComponentName name;
|
|
||||||
ProcessRecord proc; // if non-null, hosting process.
|
ProcessRecord proc; // if non-null, hosting process.
|
||||||
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
|
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
|
||||||
String stringName;
|
String stringName;
|
||||||
|
String shortStringName;
|
||||||
|
|
||||||
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
|
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
|
||||||
ApplicationInfo ai, ComponentName _name) {
|
ApplicationInfo ai, ComponentName _name) {
|
||||||
super(_info);
|
|
||||||
service = _service;
|
service = _service;
|
||||||
|
info = _info;
|
||||||
uid = ai.uid;
|
uid = ai.uid;
|
||||||
appInfo = ai;
|
appInfo = ai;
|
||||||
name = _name;
|
name = _name;
|
||||||
@ -56,12 +64,20 @@ class ContentProviderRecord extends ContentProviderHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ContentProviderRecord(ContentProviderRecord cpr) {
|
public ContentProviderRecord(ContentProviderRecord cpr) {
|
||||||
super(cpr.info);
|
service = cpr.service;
|
||||||
|
info = cpr.info;
|
||||||
uid = cpr.uid;
|
uid = cpr.uid;
|
||||||
appInfo = cpr.appInfo;
|
appInfo = cpr.appInfo;
|
||||||
name = cpr.name;
|
name = cpr.name;
|
||||||
noReleaseNeeded = cpr.noReleaseNeeded;
|
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) {
|
public boolean canRunHere(ProcessRecord app) {
|
||||||
@ -120,30 +136,51 @@ class ContentProviderRecord extends ContentProviderHolder {
|
|||||||
return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
|
return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump(PrintWriter pw, String prefix) {
|
void dump(PrintWriter pw, String prefix, boolean full) {
|
||||||
|
if (full) {
|
||||||
pw.print(prefix); pw.print("package=");
|
pw.print(prefix); pw.print("package=");
|
||||||
pw.print(info.applicationInfo.packageName);
|
pw.print(info.applicationInfo.packageName);
|
||||||
pw.print(" process="); pw.println(info.processName);
|
pw.print(" process="); pw.println(info.processName);
|
||||||
|
}
|
||||||
pw.print(prefix); pw.print("proc="); pw.println(proc);
|
pw.print(prefix); pw.print("proc="); pw.println(proc);
|
||||||
if (launchingApp != null) {
|
if (launchingApp != null) {
|
||||||
pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
|
pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
|
||||||
}
|
}
|
||||||
|
if (full) {
|
||||||
pw.print(prefix); pw.print("uid="); pw.print(uid);
|
pw.print(prefix); pw.print("uid="); pw.print(uid);
|
||||||
pw.print(" provider="); pw.println(provider);
|
pw.print(" provider="); pw.println(provider);
|
||||||
pw.print(prefix); pw.print("name="); pw.println(info.authority);
|
}
|
||||||
|
pw.print(prefix); pw.print("authority="); pw.println(info.authority);
|
||||||
|
if (full) {
|
||||||
if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
|
if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
|
||||||
pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
|
pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
|
||||||
pw.print("multiprocess="); pw.print(info.multiprocess);
|
pw.print(" multiprocess="); pw.print(info.multiprocess);
|
||||||
pw.print(" initOrder="); pw.println(info.initOrder);
|
pw.print(" initOrder="); pw.println(info.initOrder);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (full) {
|
||||||
if (hasExternalProcessHandles()) {
|
if (hasExternalProcessHandles()) {
|
||||||
pw.print(prefix); pw.print("externals=");
|
pw.print(prefix); pw.print("externals=");
|
||||||
pw.println(externalProcessTokenToHandle.size());
|
pw.println(externalProcessTokenToHandle.size());
|
||||||
}
|
}
|
||||||
if (clients.size() > 0) {
|
} else {
|
||||||
pw.print(prefix); pw.println("Clients:");
|
if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
|
||||||
for (ProcessRecord cproc : clients) {
|
pw.print(prefix); pw.print(connections.size());
|
||||||
pw.print(prefix); pw.print(" - "); pw.println(cproc.toShortString());
|
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();
|
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.
|
// This class represents a handle from an external process to a provider.
|
||||||
private class ExternalProcessHandle implements DeathRecipient {
|
private class ExternalProcessHandle implements DeathRecipient {
|
||||||
private static final String LOG_TAG = "ExternalProcessHanldle";
|
private static final String LOG_TAG = "ExternalProcessHanldle";
|
||||||
|
@ -125,8 +125,8 @@ class ProcessRecord {
|
|||||||
final HashMap<String, ContentProviderRecord> pubProviders
|
final HashMap<String, ContentProviderRecord> pubProviders
|
||||||
= new HashMap<String, ContentProviderRecord>();
|
= new HashMap<String, ContentProviderRecord>();
|
||||||
// All ContentProviderRecord process is using
|
// All ContentProviderRecord process is using
|
||||||
final HashMap<ContentProviderRecord, Integer> conProviders
|
final ArrayList<ContentProviderConnection> conProviders
|
||||||
= new HashMap<ContentProviderRecord, Integer>();
|
= new ArrayList<ContentProviderConnection>();
|
||||||
|
|
||||||
boolean persistent; // always keep this application running?
|
boolean persistent; // always keep this application running?
|
||||||
boolean crashing; // are we in the process of crashing?
|
boolean crashing; // are we in the process of crashing?
|
||||||
@ -257,25 +257,47 @@ class ProcessRecord {
|
|||||||
pw.println();
|
pw.println();
|
||||||
}
|
}
|
||||||
if (activities.size() > 0) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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()) {
|
while (it.hasNext()) {
|
||||||
Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
|
Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
|
||||||
ContentProviderRecord r = e.getValue();
|
ContentProviderRecord r = e.getValue();
|
||||||
if (dumpAll) {
|
|
||||||
pw.print(" * ");
|
pw.print(" * ");
|
||||||
pw.println(r);
|
pw.println(r);
|
||||||
r.dump(pw, " ");
|
r.dump(pw, " ", dumpAll);
|
||||||
} 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +192,7 @@ public class ProviderMap {
|
|||||||
pw.print(" ");
|
pw.print(" ");
|
||||||
pw.print(e.getKey());
|
pw.print(e.getKey());
|
||||||
pw.print(": ");
|
pw.print(": ");
|
||||||
pw.println(r);
|
pw.println(r.toShortString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,10 +203,10 @@ public class ProviderMap {
|
|||||||
pw.println(" ");
|
pw.println(" ");
|
||||||
pw.println(" Published content providers (by class):");
|
pw.println(" Published content providers (by class):");
|
||||||
dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
|
dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
|
||||||
pw.println("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mProvidersByClassPerUser.size() > 1) {
|
if (mProvidersByClassPerUser.size() > 1) {
|
||||||
|
pw.println("");
|
||||||
for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
|
for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
|
||||||
HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
|
HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
|
||||||
pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
|
pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
|
||||||
@ -321,7 +303,7 @@ public class ProviderMap {
|
|||||||
if (r.proc != null) pw.println(r.proc.pid);
|
if (r.proc != null) pw.println(r.proc.pid);
|
||||||
else pw.println("(not running)");
|
else pw.println("(not running)");
|
||||||
if (dumpAll) {
|
if (dumpAll) {
|
||||||
r.dump(pw, innerPrefix);
|
r.dump(pw, innerPrefix, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (r.proc != null && r.proc.thread != null) {
|
if (r.proc != null && r.proc.thread != null) {
|
||||||
|
@ -118,6 +118,11 @@ public class MockContentResolver extends ContentResolver {
|
|||||||
return releaseProvider(icp);
|
return releaseProvider(icp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public void unstableProviderDied(IContentProvider icp) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)
|
* Overrides {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)
|
||||||
* ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored.
|
* ContentResolver.notifChange(Uri, ContentObserver, boolean)}. All parameters are ignored.
|
||||||
|
@ -72,6 +72,11 @@ public class BridgeContentResolver extends ContentResolver {
|
|||||||
return releaseProvider(icp);
|
return releaseProvider(icp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Override
|
||||||
|
public void unstableProviderDied(IContentProvider icp) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stub for the layoutlib bridge content resolver.
|
* Stub for the layoutlib bridge content resolver.
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user