Merge "Adding shell commands for modifying content."

This commit is contained in:
Svetoslav Ganov
2012-02-16 13:06:29 -08:00
committed by Android (Google) Code Review
12 changed files with 899 additions and 34 deletions

View File

@ -5754,7 +5754,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info, comp);
cpr = new ContentProviderRecord(this, cpi, app.info, comp);
mProviderMap.putProviderByClass(comp, cpr);
}
if (DEBUG_MU)
@ -5826,7 +5826,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return msg;
}
boolean incProviderCount(ProcessRecord r, ContentProviderRecord cpr) {
boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
IBinder externalProcessToken) {
if (r != null) {
Integer cnt = r.conProviders.get(cpr);
if (DEBUG_PROVIDER) Slog.v(TAG,
@ -5842,12 +5843,13 @@ public final class ActivityManagerService extends ActivityManagerNative
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
}
} else {
cpr.externals++;
cpr.addExternalProcessHandleLocked(externalProcessToken);
}
return false;
}
boolean decProviderCount(ProcessRecord r, ContentProviderRecord cpr) {
boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr,
IBinder externalProcessToken) {
if (r != null) {
Integer cnt = r.conProviders.get(cpr);
if (DEBUG_PROVIDER) Slog.v(TAG,
@ -5863,13 +5865,13 @@ public final class ActivityManagerService extends ActivityManagerNative
r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
}
} else {
cpr.externals++;
cpr.removeExternalProcessHandleLocked(externalProcessToken);
}
return false;
}
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name) {
String name, IBinder token) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
@ -5913,7 +5915,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// In this case the provider instance already exists, so we can
// return it right away.
final boolean countChanged = incProviderCount(r, cpr);
final boolean countChanged = incProviderCount(r, cpr, token);
if (countChanged) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// If this is a perceptible app accessing the provider,
@ -5947,7 +5949,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.i(TAG,
"Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCount(r, cpr);
boolean lastRef = decProviderCount(r, cpr, token);
appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);
if (!lastRef) {
// This wasn't the last ref our process had on
@ -6005,7 +6007,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
ai = getAppInfoForUser(ai, Binder.getOrigCallingUser());
cpr = new ContentProviderRecord(cpi, ai, comp);
cpr = new ContentProviderRecord(this, cpi, ai, comp);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
@ -6075,8 +6077,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
incProviderCount(r, cpr);
incProviderCount(r, cpr, token);
}
}
@ -6116,12 +6119,17 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
ContentProviderHolder contentProvider = getContentProviderImpl(caller, name);
return contentProvider;
return getContentProviderImpl(caller, name, null);
}
private ContentProviderHolder getContentProviderExternal(String name) {
return getContentProviderImpl(null, name);
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call getContentProviderExternal()");
return getContentProviderExternalUnchecked(name, token);
}
private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
return getContentProviderImpl(null, name, token);
}
/**
@ -6157,14 +6165,20 @@ public final class ActivityManagerService extends ActivityManagerNative
+ cpr.info.name + " in process " + r.processName);
return;
} else {
if (decProviderCount(r, localCpr)) {
if (decProviderCount(r, localCpr, null)) {
updateOomAdjLocked();
}
}
}
}
private void removeContentProviderExternal(String name) {
public void removeContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call removeContentProviderExternal()");
removeContentProviderExternalUnchecked(name, token);
}
private void removeContentProviderExternalUnchecked(String name, IBinder token) {
synchronized (this) {
ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
Binder.getOrigCallingUser());
@ -6178,11 +6192,18 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
Binder.getOrigCallingUser());
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
if (localCpr.hasExternalProcessHandles()) {
if (localCpr.removeExternalProcessHandleLocked(token)) {
updateOomAdjLocked();
} else {
Slog.e(TAG, "Attmpt to remove content provider " + localCpr
+ " with no external reference for token: "
+ token + ".");
}
} else {
Slog.e(TAG, "Attmpt to remove content provider: " + localCpr
+ " with no external references.");
}
updateOomAdjLocked();
}
}
@ -6286,7 +6307,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ContentProviderHolder holder = null;
try {
holder = getContentProviderExternal(name);
holder = getContentProviderExternalUnchecked(name, null);
if (holder != null) {
return holder.provider.getType(uri);
}
@ -6295,7 +6316,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
} finally {
if (holder != null) {
removeContentProviderExternal(name);
removeContentProviderExternalUnchecked(name, null);
}
Binder.restoreCallingIdentity(ident);
}
@ -6400,7 +6421,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
String name = uri.getAuthority();
ContentProviderHolder cph = getContentProviderExternal(name);
ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null);
ParcelFileDescriptor pfd = null;
if (cph != null) {
// We record the binder invoker's uid in thread-local storage before
@ -6422,7 +6443,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// We've got the fd now, so we're done with the provider.
removeContentProviderExternal(name);
removeContentProviderExternalUnchecked(name, null);
} else {
Slog.d(TAG, "Failed to get provider for authority '" + name + "'");
}
@ -10253,7 +10274,7 @@ public final class ActivityManagerService extends ActivityManagerNative
for (int i=0; i<NL; i++) {
ContentProviderRecord cpr = (ContentProviderRecord)
mLaunchingProviders.get(i);
if (cpr.clients.size() <= 0 && cpr.externals <= 0) {
if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
@ -13455,7 +13476,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// If the provider has external (non-framework) process
// dependencies, ensure that its adjustment is at least
// FOREGROUND_APP_ADJ.
if (cpr.externals != 0) {
if (cpr.hasExternalProcessHandles()) {
if (adj > ProcessList.FOREGROUND_APP_ADJ) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;

View File

@ -20,24 +20,35 @@ import android.app.IActivityManager.ContentProviderHolder;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
class ContentProviderRecord extends ContentProviderHolder {
// All attached clients
final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
// Handles for non-framework processes supported by this provider
HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
// Count for external process for which we have no handles.
int externalProcessNoHandleCount;
final ActivityManagerService service;
final int uid;
final ApplicationInfo appInfo;
final ComponentName name;
int externals; // number of non-framework processes supported by this provider
ProcessRecord proc; // if non-null, hosting process.
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
String stringName;
public ContentProviderRecord(ProviderInfo _info, ApplicationInfo ai, ComponentName _name) {
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
ApplicationInfo ai, ComponentName _name) {
super(_info);
service = _service;
uid = ai.uid;
appInfo = ai;
name = _name;
@ -50,6 +61,7 @@ class ContentProviderRecord extends ContentProviderHolder {
appInfo = cpr.appInfo;
name = cpr.name;
noReleaseNeeded = cpr.noReleaseNeeded;
service = cpr.service;
}
public boolean canRunHere(ProcessRecord app) {
@ -57,6 +69,57 @@ class ContentProviderRecord extends ContentProviderHolder {
&& (uid == Process.SYSTEM_UID || uid == app.info.uid);
}
public void addExternalProcessHandleLocked(IBinder token) {
if (token == null) {
externalProcessNoHandleCount++;
} else {
if (externalProcessTokenToHandle == null) {
externalProcessTokenToHandle = new HashMap<IBinder, ExternalProcessHandle>();
}
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
if (handle == null) {
handle = new ExternalProcessHandle(token);
externalProcessTokenToHandle.put(token, handle);
}
handle.mAcquisitionCount++;
}
}
public boolean removeExternalProcessHandleLocked(IBinder token) {
if (hasExternalProcessHandles()) {
boolean hasHandle = false;
if (externalProcessTokenToHandle != null) {
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
if (handle != null) {
hasHandle = true;
handle.mAcquisitionCount--;
if (handle.mAcquisitionCount == 0) {
removeExternalProcessHandleInternalLocked(token);
return true;
}
}
}
if (!hasHandle) {
externalProcessNoHandleCount--;
return true;
}
}
return false;
}
private void removeExternalProcessHandleInternalLocked(IBinder token) {
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
handle.unlinkFromOwnDeathLocked();
externalProcessTokenToHandle.remove(token);
if (externalProcessTokenToHandle.size() == 0) {
externalProcessTokenToHandle = null;
}
}
public boolean hasExternalProcessHandles() {
return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
}
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("package=");
pw.print(info.applicationInfo.packageName);
@ -73,8 +136,9 @@ class ContentProviderRecord extends ContentProviderHolder {
pw.print("multiprocess="); pw.print(info.multiprocess);
pw.print(" initOrder="); pw.println(info.initOrder);
}
if (externals != 0) {
pw.print(prefix); pw.print("externals="); pw.println(externals);
if (hasExternalProcessHandles()) {
pw.print(prefix); pw.print("externals=");
pw.println(externalProcessTokenToHandle.size());
}
if (clients.size() > 0) {
pw.print(prefix); pw.println("Clients:");
@ -84,6 +148,7 @@ class ContentProviderRecord extends ContentProviderHolder {
}
}
@Override
public String toString() {
if (stringName != null) {
return stringName;
@ -96,4 +161,35 @@ class ContentProviderRecord extends ContentProviderHolder {
sb.append('}');
return stringName = sb.toString();
}
// This class represents a handle from an external process to a provider.
private class ExternalProcessHandle implements DeathRecipient {
private static final String LOG_TAG = "ExternalProcessHanldle";
private final IBinder mToken;
private int mAcquisitionCount;
public ExternalProcessHandle(IBinder token) {
mToken = token;
try {
token.linkToDeath(this, 0);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Couldn't register for death for token: " + mToken, re);
}
}
public void unlinkFromOwnDeathLocked() {
mToken.unlinkToDeath(this, 0);
}
@Override
public void binderDied() {
synchronized (service) {
if (hasExternalProcessHandles() &&
externalProcessTokenToHandle.get(mToken) != null) {
removeExternalProcessHandleInternalLocked(mToken);
}
}
}
}
}