summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Varun Shah <varunshah@google.com> 2020-07-29 02:23:12 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2020-07-29 02:23:12 +0000
commit9ac25b3189bba5c46ada89a1b967b6df1c70a9c9 (patch)
tree3f678075afa066998993f7c08e02e36b73ab36d7
parentd50f513701a6a075cb5b250228cbf66ce3c9d9a9 (diff)
parent67640823bb98ba2692bdb5d0fadf1b4e64b6537e (diff)
Merge changes I65974f90,I98015097
* changes: Clean up some of the Content Provider code. Reorganize some of the ContentProvider methods.
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java1735
2 files changed, 854 insertions, 883 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cca2654815ad..2f7d105e7a48 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13394,7 +13394,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// XXX Commented out for now. Trying to figure out a way to reproduce
// the actual situation to identify what is actually going on.
if (false) {
- mCpHelper.cleanupLaunchingProviders();
+ mCpHelper.cleanupLaunchingProvidersLocked();
}
skipCurrentReceiverLocked(app);
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 1e0c243ff3f5..5cc7aba736e1 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -15,19 +15,15 @@
*/
package com.android.server.am;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Process.PROC_CHAR;
import static android.os.Process.PROC_OUT_LONG;
import static android.os.Process.PROC_PARENS;
import static android.os.Process.PROC_SPACE_TERM;
import static android.os.Process.SYSTEM_UID;
-import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
-import static android.os.Process.readProcFile;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerService.TAG_MU;
-import android.Manifest;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -80,11 +76,7 @@ import java.util.Objects;
public class ContentProviderHelper {
private static final String TAG = "ContentProviderHelper";
- private ActivityManagerService mService;
-
- private boolean mSystemProvidersInstalled;
-
- private final ProviderMap mProviderMap;
+ private final ActivityManagerService mService;
/**
* List of content providers who have clients waiting for them. The
@@ -92,6 +84,8 @@ public class ContentProviderHelper {
* removed from this list once it is published.
*/
private final ArrayList<ContentProviderRecord> mLaunchingProviders = new ArrayList<>();
+ private final ProviderMap mProviderMap;
+ private boolean mSystemProvidersInstalled;
ContentProviderHelper(ActivityManagerService service, boolean createProviderMap) {
mService = service;
@@ -102,651 +96,6 @@ public class ContentProviderHelper {
return mProviderMap;
}
- List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
- List<ProviderInfo> providers = null;
- try {
- providers = AppGlobals.getPackageManager().queryContentProviders(
- app.processName, app.uid, ActivityManagerService.STOCK_PM_FLAGS
- | PackageManager.GET_URI_PERMISSION_PATTERNS
- | PackageManager.MATCH_DIRECT_BOOT_AUTO, /*metaDataKey=*/ null)
- .getList();
- } catch (RemoteException ex) {
- }
- if (DEBUG_MU) {
- Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
- }
- int userId = app.userId;
- if (providers != null) {
- int N = providers.size();
- app.pubProviders.ensureCapacity(N + app.pubProviders.size());
- for (int i = 0; i < N; i++) {
- // TODO: keep logic in sync with installEncryptionUnawareProviders
- ProviderInfo cpi = (ProviderInfo) providers.get(i);
- boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo,
- cpi.name, cpi.flags);
- if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
- // This is a singleton provider, but a user besides the
- // default user is asking to initialize a process it runs
- // in... well, no, it doesn't actually run in this process,
- // it runs in the process of the default user. Get rid of it.
- providers.remove(i);
- N--;
- i--;
- continue;
- }
-
- ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
- if (cpr == null) {
- cpr = new ContentProviderRecord(mService, cpi, app.info, comp, singleton);
- mProviderMap.putProviderByClass(comp, cpr);
- }
- if (DEBUG_MU) {
- Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
- }
- app.pubProviders.put(cpi.name, cpr);
- if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
- // Don't add this if it is a platform component that is marked
- // to run in multiple processes, because this is actually
- // part of the framework so doesn't make sense to track as a
- // separate apk in the process.
- app.addPackage(cpi.applicationInfo.packageName,
- cpi.applicationInfo.longVersionCode, mService.mProcessStats);
- }
- mService.notifyPackageUse(cpi.applicationInfo.packageName,
- PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
- }
- }
- return providers;
- }
-
- @GuardedBy("mService")
- private ContentProviderConnection incProviderCountLocked(ProcessRecord r,
- final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
- String callingPackage, String callingTag, boolean stable, boolean updateLru,
- long startTime, ProcessList processList) {
- if (r != null) {
- for (int i = 0; i < r.conProviders.size(); i++) {
- ContentProviderConnection conn = r.conProviders.get(i);
- if (conn.provider == cpr) {
- conn.incrementCount(stable);
- return conn;
- }
- }
-
- // Create a new ContentProviderConnection. The reference count
- // is known to be 1.
- ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
- conn.startAssociationIfNeeded();
- conn.initializeCount(stable);
- cpr.connections.add(conn);
- r.conProviders.add(conn);
- mService.startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
- cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- if (updateLru && cpr.proc != null
- && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
- // If this is a perceptible app accessing the provider, make
- // sure to count it as being accessed and thus back up on
- // the LRU list. This is good because content providers are
- // often expensive to start. The calls to checkTime() use
- // the "getContentProviderImpl" tag here, because it's part
- // of the checktime log in getContentProviderImpl().
- checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
- processList.updateLruProcessLocked(cpr.proc, false, null);
- checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
- }
- return conn;
- }
- cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
- return null;
- }
-
- @GuardedBy("mService")
- private boolean decProviderCountLocked(ContentProviderConnection conn,
- ContentProviderRecord cpr,
- IBinder externalProcessToken, boolean stable) {
- if (conn != null) {
- cpr = conn.provider;
- final int referenceCount = conn.decrementCount(stable);
- if (referenceCount == 0) {
- conn.stopAssociation();
- cpr.connections.remove(conn);
- conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- // The client is more important than last activity -- note the time this
- // is happening, so we keep the old provider process around a bit as last
- // activity to avoid thrashing it.
- if (cpr.proc != null) {
- cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
- }
- }
- mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
- cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- return true;
- }
- return false;
- }
- cpr.removeExternalProcessHandleLocked(externalProcessToken);
- return false;
- }
-
- private static final class StartActivityRunnable implements Runnable {
- private final Context mContext;
- private final Intent mIntent;
- private final UserHandle mUserHandle;
-
- StartActivityRunnable(Context context, Intent intent, UserHandle userHandle) {
- this.mContext = context;
- this.mIntent = intent;
- this.mUserHandle = userHandle;
- }
-
- @Override
- public void run() {
- mContext.startActivityAsUser(mIntent, mUserHandle);
- }
- }
-
- private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
- ProcessRecord r, final int userId, Context context) {
- if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
- cpi.packageName, userId)) {
-
- final boolean callerForeground = r == null || r.setSchedGroup
- != ProcessList.SCHED_GROUP_BACKGROUND;
-
- // Show a permission review UI only for starting from a foreground app
- if (!callerForeground) {
- Slog.w(TAG, "u" + userId + " Instantiating a provider in package"
- + cpi.packageName + " requires a permissions review");
- return false;
- }
-
- final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- intent.addFlags(FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName);
-
- if (ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW) {
- Slog.i(TAG, "u" + userId + " Launching permission review "
- + "for package " + cpi.packageName);
- }
-
- final UserHandle userHandle = new UserHandle(userId);
- mService.mHandler.post(new StartActivityRunnable(context, intent, userHandle));
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Check if the calling UID has a possible chance at accessing the provider
- * at the given authority and user.
- */
- public String checkContentProviderAccess(String authority, int userId) {
- if (userId == UserHandle.USER_ALL) {
- mService.mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
- userId = UserHandle.getCallingUserId();
- }
-
- ProviderInfo cpi = null;
- try {
- cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
- ActivityManagerService.STOCK_PM_FLAGS
- | PackageManager.GET_URI_PERMISSION_PATTERNS
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
- } catch (RemoteException ignored) {
- }
- if (cpi == null) {
- return "Failed to find provider " + authority + " for user " + userId
- + "; expected to find a valid ContentProvider for this authority";
- }
-
- ProcessRecord r = null;
- synchronized (mService.mPidsSelfLocked) {
- r = mService.mPidsSelfLocked.get(Binder.getCallingPid());
- }
- if (r == null) {
- return "Failed to find PID " + Binder.getCallingPid();
- }
-
- synchronized (mService) {
- return checkContentProviderPermissionLocked(cpi, r, userId, true);
- }
- }
-
- /**
- * Check if {@link ProcessRecord} has a possible chance at accessing the
- * given {@link ProviderInfo}. Final permission checking is always done
- * in {@link ContentProvider}.
- */
- private String checkContentProviderPermissionLocked(
- ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) {
- final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
- final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
- boolean checkedGrants = false;
- if (checkUser) {
- // Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
- if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
- if (mService.mUgmInternal.checkAuthorityGrants(
- callingUid, cpi, tmpTargetUserId, checkUser)) {
- return null;
- }
- checkedGrants = true;
- }
- userId = mService.mUserController.handleIncomingUser(callingPid, callingUid, userId,
- false, ActivityManagerInternal.ALLOW_NON_FULL,
- "checkContentProviderPermissionLocked " + cpi.authority, null);
- if (userId != tmpTargetUserId) {
- // When we actually went to determine the final targer user ID, this ended
- // up different than our initial check for the authority. This is because
- // they had asked for USER_CURRENT_OR_SELF and we ended up switching to
- // SELF. So we need to re-check the grants again.
- checkedGrants = false;
- }
- }
- if (ActivityManagerService.checkComponentPermission(cpi.readPermission,
- callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- if (ActivityManagerService.checkComponentPermission(cpi.writePermission,
- callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
-
- PathPermission[] pps = cpi.pathPermissions;
- if (pps != null) {
- int i = pps.length;
- while (i > 0) {
- i--;
- PathPermission pp = pps[i];
- String pprperm = pp.getReadPermission();
- if (pprperm != null && ActivityManagerService.checkComponentPermission(pprperm,
- callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- String ppwperm = pp.getWritePermission();
- if (ppwperm != null && ActivityManagerService.checkComponentPermission(ppwperm,
- callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
- == PackageManager.PERMISSION_GRANTED) {
- return null;
- }
- }
- }
- if (!checkedGrants
- && mService.mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
- return null;
- }
-
- final String suffix;
- if (!cpi.exported) {
- suffix = " that is not exported from UID " + cpi.applicationInfo.uid;
- } else if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(cpi.readPermission)) {
- suffix = " requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs";
- } else {
- suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission;
- }
- final String msg = "Permission Denial: opening provider " + cpi.name
- + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
- + ", uid=" + callingUid + ")" + suffix;
- Slog.w(TAG, msg);
- return msg;
- }
-
- private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid,
- ProviderInfo cpi) {
- if (callingApp == null) {
- return mService.validateAssociationAllowedLocked(cpi.packageName,
- cpi.applicationInfo.uid, null, callingUid) ? null : "<null>";
- }
- for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
- if (!mService.validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i),
- callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
- return cpi.packageName;
- }
- }
- return null;
- }
-
- void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
- mService.enforceCallingPermission(
- android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
- "Do not have permission in call removeContentProviderExternal()");
- long ident = Binder.clearCallingIdentity();
- try {
- removeContentProviderExternalUnchecked(name, token, userId);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
- synchronized (mService) {
- ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
- if (cpr == null) {
- //remove from mProvidersByClass
- if (ActivityManagerDebugConfig.DEBUG_ALL) {
- Slog.v(TAG, name + " content provider not found in providers list");
- }
- return;
- }
-
- // update content provider record entry info
- ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
- if (localCpr.hasExternalProcessHandles()) {
- if (localCpr.removeExternalProcessHandleLocked(token)) {
- mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
- } 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.");
- }
- }
- }
-
- 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");
- }
-
- conn.adjustCounts(stable, 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 (mService) {
- 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 (mService) {
- Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
- + " says " + conn + " died, but we don't agree");
- return;
- }
- }
-
- // Well look at that! It's dead!
- synchronized (mService) {
- 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.
- mService.reportUidInfoMessageLocked(TAG,
- "Process " + proc.processName + " (pid " + proc.pid
- + ") early provider death",
- proc.info.uid);
- final long ident = Binder.clearCallingIdentity();
- try {
- mService.appDiedLocked(proc, "unstable content provider");
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- public void appNotRespondingViaProvider(IBinder connection) {
- mService.enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
- "appNotRespondingViaProvider()");
-
- final ContentProviderConnection conn = (ContentProviderConnection) connection;
- if (conn == null) {
- Slog.w(TAG, "ContentProviderConnection is null");
- return;
- }
-
- final ProcessRecord host = conn.provider.proc;
- if (host == null) {
- Slog.w(TAG, "Failed to find hosting ProcessRecord");
- return;
- }
-
- mService.mAnrHelper.appNotResponding(host, "ContentProvider not responding");
- }
-
- /**
- * Allows apps to retrieve the MIME type of a URI.
- * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
- * users, then it does not need permission to access the ContentProvider.
- * Either, it needs cross-user uri grants.
- *
- * CTS tests for this functionality can be run with "runtest cts-appsecurity".
- *
- * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
- * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
- *
- * @deprecated -- use getProviderMimeTypeAsync.
- */
- @Deprecated
- public String getProviderMimeType(Uri uri, int userId) {
- mService.enforceNotIsolatedCaller("getProviderMimeType");
- final String name = uri.getAuthority();
- int callingUid = Binder.getCallingUid();
- int callingPid = Binder.getCallingPid();
- long ident = 0;
- boolean clearedIdentity = false;
- userId = mService.mUserController.unsafeConvertIncomingUser(userId);
- if (canClearIdentity(callingPid, callingUid, userId)) {
- clearedIdentity = true;
- ident = Binder.clearCallingIdentity();
- }
- ContentProviderHolder holder = null;
- try {
- holder = getContentProviderExternalUnchecked(name, null, callingUid,
- "*getmimetype*", userId);
- if (holder != null) {
- final IBinder providerConnection = holder.connection;
- final ComponentName providerName = holder.info.getComponentName();
- // Note: creating a new Runnable instead of using a lambda here since lambdas in
- // java provide no guarantee that there will be a new instance returned every call.
- // Hence, it's possible that a cached copy is returned and the ANR is executed on
- // the incorrect provider.
- final Runnable providerNotResponding = new Runnable() {
- @Override
- public void run() {
- Log.w(TAG, "Provider " + providerName + " didn't return from getType().");
- appNotRespondingViaProvider(providerConnection);
- }
- };
- mService.mHandler.postDelayed(providerNotResponding, 1000);
- try {
- return holder.provider.getType(uri);
- } finally {
- mService.mHandler.removeCallbacks(providerNotResponding);
- }
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- return null;
- } catch (Exception e) {
- Log.w(TAG, "Exception while determining type of " + uri, e);
- return null;
- } finally {
- // We need to clear the identity to call removeContentProviderExternalUnchecked
- if (!clearedIdentity) {
- ident = Binder.clearCallingIdentity();
- }
- try {
- if (holder != null) {
- removeContentProviderExternalUnchecked(name, null, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- return null;
- }
-
- /**
- * Allows apps to retrieve the MIME type of a URI.
- * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
- * users, then it does not need permission to access the ContentProvider.
- * Either way, it needs cross-user uri grants.
- */
- public void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) {
- mService.enforceNotIsolatedCaller("getProviderMimeTypeAsync");
- final String name = uri.getAuthority();
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
- final long ident = canClearIdentity(callingPid, callingUid, userId)
- ? Binder.clearCallingIdentity() : 0;
- try {
- final ContentProviderHolder holder = getContentProviderExternalUnchecked(name, null,
- callingUid, "*getmimetype*", safeUserId);
- if (holder != null) {
- holder.provider.getTypeAsync(uri, new RemoteCallback(result -> {
- final long identity = Binder.clearCallingIdentity();
- try {
- removeContentProviderExternalUnchecked(name, null, safeUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- resultCallback.sendResult(result);
- }));
- } else {
- resultCallback.sendResult(Bundle.EMPTY);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- resultCallback.sendResult(Bundle.EMPTY);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private boolean canClearIdentity(int callingPid, int callingUid, int userId) {
- if (UserHandle.getUserId(callingUid) == userId) {
- return true;
- }
- if (ActivityManagerService.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, callingPid,
- callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
- || ActivityManagerService.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPid,
- callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- return false;
- }
-
- int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
- if (Thread.holdsLock(mService.mActivityTaskManager.getGlobalLock())) {
- Slog.wtf(TAG, new IllegalStateException("Unable to check Uri permission"
- + " because caller is holding WM lock; assuming permission denied"));
- return PackageManager.PERMISSION_DENIED;
- }
-
- final String name = uri.getAuthority();
- final long ident = Binder.clearCallingIdentity();
- ContentProviderHolder holder = null;
- try {
- holder = getContentProviderExternalUnchecked(name, null, callingUid,
- "*checkContentProviderUriPermission*", userId);
- if (holder != null) {
- return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Content provider dead retrieving " + uri, e);
- return PackageManager.PERMISSION_DENIED;
- } catch (Exception e) {
- Log.w(TAG, "Exception while determining type of " + uri, e);
- return PackageManager.PERMISSION_DENIED;
- } finally {
- try {
- if (holder != null) {
- removeContentProviderExternalUnchecked(name, null, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- return PackageManager.PERMISSION_DENIED;
- }
-
- /**
- * Drop a content provider from a ProcessRecord's bookkeeping
- */
- void removeContentProvider(IBinder connection, boolean stable) {
- mService.enforceNotIsolatedCaller("removeContentProvider");
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mService) {
- ContentProviderConnection conn;
- try {
- conn = (ContentProviderConnection) connection;
- } catch (ClassCastException e) {
- String msg = "removeContentProvider: " + connection
- + " not a ContentProviderConnection";
- Slog.w(TAG, msg);
- throw new IllegalArgumentException(msg);
- }
- if (conn == null) {
- throw new NullPointerException("connection is null");
- }
- if (decProviderCountLocked(conn, null, null, stable)) {
- mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
String name, int userId, boolean stable) {
mService.enforceNotIsolatedCaller("getContentProvider");
@@ -767,6 +116,24 @@ public class ContentProviderHelper {
null, stable, userId);
}
+ ContentProviderHolder getContentProviderExternal(
+ String name, int userId, IBinder token, String tag) {
+ mService.enforceCallingPermission(
+ android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
+ "Do not have permission in call getContentProviderExternal()");
+ userId = mService.mUserController.handleIncomingUser(
+ Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, ActivityManagerInternal.ALLOW_FULL_ONLY, "getContentProvider", null);
+ return getContentProviderExternalUnchecked(name, token, Binder.getCallingUid(),
+ tag != null ? tag : "*external*", userId);
+ }
+
+ ContentProviderHolder getContentProviderExternalUnchecked(String name,
+ IBinder token, int callingUid, String callingTag, int userId) {
+ return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
+ true, userId);
+ }
+
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
@@ -799,10 +166,10 @@ public class ContentProviderHelper {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
- if (mService.isSingleton(cpi.processName, cpi.applicationInfo,
- cpi.name, cpi.flags)
- && mService.isValidSingletonCall(r == null ? callingUid : r.uid,
- cpi.applicationInfo.uid)) {
+ if (mService.isSingleton(
+ cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
+ && mService.isValidSingletonCall(
+ r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
@@ -852,25 +219,24 @@ public class ContentProviderHelper {
// in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance.
ContentProviderHolder holder = cpr.newHolder(null);
- // don't give caller the provider object, it needs
- // to make its own.
+ // don't give caller the provider object, it needs to make its own.
holder.provider = null;
- return holder; //what?
+ return holder;
}
// Don't expose providers between normal apps and instant apps
try {
if (AppGlobals.getPackageManager()
- .resolveContentProvider(name, 0 /*flags*/, userId) == null) {
+ .resolveContentProvider(name, /*flags=*/ 0, userId) == null) {
return null;
}
} catch (RemoteException e) {
}
if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
- throw new SecurityException("Content provider lookup "
- + cpr.name.flattenToShortString()
- + " failed: association not allowed with package " + msg);
+ throw new SecurityException(
+ "Content provider lookup " + cpr.name.flattenToShortString()
+ + " failed: association not allowed with package " + msg);
}
checkTime(startTime,
"getContentProviderImpl: before checkContentProviderPermission");
@@ -918,8 +284,7 @@ public class ContentProviderHelper {
// doesn't kill our process.
Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
- boolean lastRef = decProviderCountLocked(conn, cpr,
- token, stable);
+ boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we will be killed during cleaning up, bail.
@@ -941,7 +306,8 @@ public class ContentProviderHelper {
checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
cpi = AppGlobals.getPackageManager().resolveContentProvider(name,
ActivityManagerService.STOCK_PM_FLAGS
- | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
+ | PackageManager.GET_URI_PERMISSION_PATTERNS,
+ userId);
checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
} catch (RemoteException ex) {
}
@@ -949,13 +315,12 @@ public class ContentProviderHelper {
return null;
}
// If the provider is a singleton AND
- // (it's a call within the same user || the provider is a
- // privileged app)
+ // (it's a call within the same user || the provider is a privileged app)
// Then allow connecting to the singleton provider
- boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo,
- cpi.name, cpi.flags)
- && mService.isValidSingletonCall(r == null ? callingUid : r.uid,
- cpi.applicationInfo.uid);
+ boolean singleton = mService.isSingleton(
+ cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
+ && mService.isValidSingletonCall(
+ r == null ? callingUid : r.uid, cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_SYSTEM;
}
@@ -976,8 +341,7 @@ public class ContentProviderHelper {
checkTime(startTime,
"getContentProviderImpl: after checkContentProviderPermission");
- if (!mService.mProcessesReady
- && !cpi.processName.equals("system")) {
+ if (!mService.mProcessesReady && !cpi.processName.equals("system")) {
// If this content provider does not run in the system
// process, and the system is not yet ready to run other
// processes, then fail fast instead of hanging.
@@ -997,9 +361,8 @@ public class ContentProviderHelper {
// we don't want to allow it to run.
if (!mService.mUserController.isUserRunning(userId, 0)) {
Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": user " + userId + " is stopped");
+ + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid
+ + " for provider " + name + ": user " + userId + " is stopped");
return null;
}
@@ -1015,7 +378,7 @@ public class ContentProviderHelper {
// we return no provider and launch a review activity if the calling app
// is in the foreground.
if (!requestTargetProviderPermissionsReviewIfNeededLocked(
- cpi, r, userId, mService.mContext)) {
+ cpi, r, userId, mService.mContext)) {
return null;
}
@@ -1062,19 +425,17 @@ public class ContentProviderHelper {
}
// This is single process, and our app is now connecting to it.
- // See if we are already in the process of launching this
- // provider.
- final int N = mLaunchingProviders.size();
+ // See if we are already in the process of launching this provider.
+ final int numLaunchingProviders = mLaunchingProviders.size();
int i;
- for (i = 0; i < N; i++) {
+ for (i = 0; i < numLaunchingProviders; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
- // If the provider is not already being launched, then get it
- // started.
- if (i >= N) {
+ // If the provider is not already being launched, then get it started.
+ if (i >= numLaunchingProviders) {
final long origId = Binder.clearCallingIdentity();
try {
@@ -1109,18 +470,18 @@ public class ContentProviderHelper {
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
- proc = mService.startProcessLocked(cpi.processName,
- cpr.appInfo, false, 0,
+ proc = mService.startProcessLocked(
+ cpi.processName, cpr.appInfo, false, 0,
new HostingRecord("content provider",
- new ComponentName(cpi.applicationInfo.packageName,
- cpi.name)),
- ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
+ new ComponentName(
+ cpi.applicationInfo.packageName, cpi.name)),
+ Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": process is bad");
+ + cpi.applicationInfo.uid + " for provider " + name
+ + ": process is bad");
return null;
}
}
@@ -1148,7 +509,7 @@ public class ContentProviderHelper {
}
checkTime(startTime, "getContentProviderImpl: done!");
- mService.grantImplicitAccess(userId, null /*intent*/, callingUid,
+ mService.grantImplicitAccess(userId, null, callingUid,
UserHandle.getAppId(cpi.applicationInfo.uid));
}
@@ -1160,13 +521,11 @@ public class ContentProviderHelper {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": launching app became null");
+ + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid
+ + " for provider " + name + ": launching app became null");
EventLogTags.writeAmProviderLostProcess(
UserHandle.getUserId(cpi.applicationInfo.uid),
- cpi.applicationInfo.packageName,
- cpi.applicationInfo.uid, name);
+ cpi.applicationInfo.packageName, cpi.applicationInfo.uid, name);
return null;
}
try {
@@ -1205,73 +564,15 @@ public class ContentProviderHelper {
}
Slog.wtf(TAG, "Timeout waiting for provider "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider " + name
- + " providerRunning=" + providerRunning
+ + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid
+ + " for provider " + name + " providerRunning=" + providerRunning
+ " caller=" + callerName + "/" + Binder.getCallingUid());
return null;
}
return cpr.newHolder(conn);
}
- ContentProviderHolder getContentProviderExternal(
- String name, int userId, IBinder token, String tag) {
- mService.enforceCallingPermission(
- android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
- "Do not have permission in call getContentProviderExternal()");
- userId = mService.mUserController.handleIncomingUser(
- Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, ActivityManagerInternal.ALLOW_FULL_ONLY, "getContentProvider", null);
- return getContentProviderExternalUnchecked(name, token, Binder.getCallingUid(),
- tag != null ? tag : "*external*", userId);
- }
-
- ContentProviderHolder getContentProviderExternalUnchecked(String name,
- IBinder token, int callingUid, String callingTag, int userId) {
- return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
- true, userId);
- }
-
- private static final int[] PROCESS_STATE_STATS_FORMAT = new int[] {
- PROC_SPACE_TERM,
- PROC_SPACE_TERM | PROC_PARENS,
- PROC_SPACE_TERM | PROC_CHAR | PROC_OUT_LONG, // 3: process state
- };
-
- private final long[] mProcessStateStatsLongs = new long[1];
-
- private boolean isProcessAliveLocked(ProcessRecord proc) {
- if (proc.pid <= 0) {
- if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
- Slog.d(ActivityManagerService.TAG, "Process hasn't started yet: " + proc);
- }
- return false;
- }
- if (proc.procStatFile == null) {
- proc.procStatFile = "/proc/" + proc.pid + "/stat";
- }
- mProcessStateStatsLongs[0] = 0;
- if (!readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null,
- mProcessStateStatsLongs, null)) {
- if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
- Slog.d(ActivityManagerService.TAG,
- "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile);
- }
- return false;
- }
- final long state = mProcessStateStatsLongs[0];
- if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
- Slog.d(ActivityManagerService.TAG,
- "RETRIEVED STATE FOR " + proc.procStatFile + ": " + (char) state);
- }
- if (state != 'Z' && state != 'X' && state != 'x' && state != 'K') {
- return Process.getUidForPid(proc.pid) == proc.uid;
- }
- return false;
- }
-
- public final void publishContentProviders(IApplicationThread caller,
- List<ContentProviderHolder> providers) {
+ void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
@@ -1290,81 +591,493 @@ public class ContentProviderHelper {
final long origId = Binder.clearCallingIdentity();
- final int N = providers.size();
- for (int i = 0; i < N; i++) {
+ for (int i = 0, size = providers.size(); i < size; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+ if (dst == null) {
+ continue;
+ }
if (DEBUG_MU) {
Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
}
- if (dst != null) {
- ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
- mProviderMap.putProviderByClass(comp, dst);
- String[] names = dst.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- mProviderMap.putProviderByName(names[j], dst);
- }
- int launchingCount = mLaunchingProviders.size();
- int j;
- boolean wasInLaunchingProviders = false;
- for (j = 0; j < launchingCount; j++) {
- if (mLaunchingProviders.get(j) == dst) {
- mLaunchingProviders.remove(j);
- wasInLaunchingProviders = true;
- j--;
- launchingCount--;
- }
- }
- if (wasInLaunchingProviders) {
- mService.mHandler.removeMessages(
- ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
- }
- // Make sure the package is associated with the process.
- // XXX We shouldn't need to do this, since we have added the package
- // when we generated the providers in generateApplicationProvidersLocked().
- // But for some reason in some cases we get here with the package no longer
- // added... for now just patch it in to make things happy.
- r.addPackage(dst.info.applicationInfo.packageName,
- dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
- synchronized (dst) {
- dst.provider = src.provider;
- dst.setProcess(r);
- dst.notifyAll();
+ ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
+ mProviderMap.putProviderByClass(comp, dst);
+ String[] names = dst.info.authority.split(";");
+ for (int j = 0; j < names.length; j++) {
+ mProviderMap.putProviderByName(names[j], dst);
+ }
+
+ boolean wasInLaunchingProviders = false;
+ for (int j = 0, numLaunching = mLaunchingProviders.size(); j < numLaunching; j++) {
+ if (mLaunchingProviders.get(j) == dst) {
+ mLaunchingProviders.remove(j);
+ wasInLaunchingProviders = true;
+ j--;
+ numLaunching--;
}
- dst.mRestartCount = 0;
- mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
- maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
- src.info.authority);
}
+ if (wasInLaunchingProviders) {
+ mService.mHandler.removeMessages(
+ ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
+ }
+ // Make sure the package is associated with the process.
+ // XXX We shouldn't need to do this, since we have added the package
+ // when we generated the providers in generateApplicationProvidersLocked().
+ // But for some reason in some cases we get here with the package no longer
+ // added... for now just patch it in to make things happy.
+ r.addPackage(dst.info.applicationInfo.packageName,
+ dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
+ synchronized (dst) {
+ dst.provider = src.provider;
+ dst.setProcess(r);
+ dst.notifyAll();
+ }
+ dst.mRestartCount = 0;
+ mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
+ maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority);
}
Binder.restoreCallingIdentity(origId);
}
}
- private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
- String authority) {
- if (app == null) return;
- if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- UserState userState = mService.mUserController.getStartedUserState(app.userId);
- if (userState == null) return;
- final long now = SystemClock.elapsedRealtime();
- Long lastReported = userState.mProviderLastReportedFg.get(authority);
- if (lastReported == null || lastReported < now - 60 * 1000L) {
- if (mService.mSystemReady) {
- // Cannot touch the user stats if not system ready
- mService.mUsageStatsService.reportContentProviderUsage(
- authority, providerPkgName, app.userId);
+ /**
+ * Drop a content provider from a ProcessRecord's bookkeeping
+ */
+ void removeContentProvider(IBinder connection, boolean stable) {
+ mService.enforceNotIsolatedCaller("removeContentProvider");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mService) {
+ ContentProviderConnection conn;
+ try {
+ conn = (ContentProviderConnection) connection;
+ } catch (ClassCastException e) {
+ String msg = "removeContentProvider: " + connection
+ + " not a ContentProviderConnection";
+ Slog.w(TAG, msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (conn == null) {
+ throw new NullPointerException("connection is null");
+ }
+ if (decProviderCountLocked(conn, null, null, stable)) {
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ void removeContentProviderExternalAsUser(String name, IBinder token, int userId) {
+ mService.enforceCallingPermission(
+ android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
+ "Do not have permission in call removeContentProviderExternal()");
+ long ident = Binder.clearCallingIdentity();
+ try {
+ removeContentProviderExternalUnchecked(name, token, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
+ synchronized (mService) {
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
+ if (cpr == null) {
+ //remove from mProvidersByClass
+ if (ActivityManagerDebugConfig.DEBUG_ALL) {
+ Slog.v(TAG, name + " content provider not found in providers list");
+ }
+ return;
+ }
+
+ // update content provider record entry info
+ ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
+ if (localCpr.hasExternalProcessHandles()) {
+ if (localCpr.removeExternalProcessHandleLocked(token)) {
+ mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
+ } else {
+ Slog.e(TAG, "Attempt to remove content provider " + localCpr
+ + " with no external reference for token: " + token + ".");
+ }
+ } else {
+ Slog.e(TAG, "Attempt to remove content provider: " + localCpr
+ + " with no external references.");
+ }
+ }
+ }
+
+ 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");
+ }
+
+ conn.adjustCounts(stable, unstable);
+ return !conn.dead;
+ }
+
+ 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 (mService) {
+ 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 (mService) {
+ Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
+ + " says " + conn + " died, but we don't agree");
+ return;
+ }
+ }
+
+ // Well look at that! It's dead!
+ synchronized (mService) {
+ 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.
+ mService.reportUidInfoMessageLocked(TAG, "Process " + proc.processName
+ + " (pid " + proc.pid + ") early provider death", proc.info.uid);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mService.appDiedLocked(proc, "unstable content provider");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ void appNotRespondingViaProvider(IBinder connection) {
+ mService.enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
+ "appNotRespondingViaProvider()");
+
+ final ContentProviderConnection conn = (ContentProviderConnection) connection;
+ if (conn == null) {
+ Slog.w(TAG, "ContentProviderConnection is null");
+ return;
+ }
+
+ final ProcessRecord host = conn.provider.proc;
+ if (host == null) {
+ Slog.w(TAG, "Failed to find hosting ProcessRecord");
+ return;
+ }
+
+ mService.mAnrHelper.appNotResponding(host, "ContentProvider not responding");
+ }
+
+ /**
+ * Allows apps to retrieve the MIME type of a URI.
+ * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
+ * users, then it does not need permission to access the ContentProvider.
+ * Either, it needs cross-user uri grants.
+ *
+ * CTS tests for this functionality can be run with "runtest cts-appsecurity".
+ *
+ * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
+ * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+ *
+ * @deprecated -- use getProviderMimeTypeAsync.
+ */
+ @Deprecated
+ String getProviderMimeType(Uri uri, int userId) {
+ mService.enforceNotIsolatedCaller("getProviderMimeType");
+ final String name = uri.getAuthority();
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ long ident = 0;
+ boolean clearedIdentity = false;
+ userId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ if (canClearIdentity(callingPid, callingUid, userId)) {
+ clearedIdentity = true;
+ ident = Binder.clearCallingIdentity();
+ }
+ ContentProviderHolder holder = null;
+ try {
+ holder = getContentProviderExternalUnchecked(name, null, callingUid,
+ "*getmimetype*", userId);
+ if (holder != null) {
+ final IBinder providerConnection = holder.connection;
+ final ComponentName providerName = holder.info.getComponentName();
+ // Note: creating a new Runnable instead of using a lambda here since lambdas in
+ // java provide no guarantee that there will be a new instance returned every call.
+ // Hence, it's possible that a cached copy is returned and the ANR is executed on
+ // the incorrect provider.
+ final Runnable providerNotResponding = new Runnable() {
+ @Override
+ public void run() {
+ Log.w(TAG, "Provider " + providerName + " didn't return from getType().");
+ appNotRespondingViaProvider(providerConnection);
+ }
+ };
+ mService.mHandler.postDelayed(providerNotResponding, 1000);
+ try {
+ return holder.provider.getType(uri);
+ } finally {
+ mService.mHandler.removeCallbacks(providerNotResponding);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ return null;
+ } catch (Exception e) {
+ Log.w(TAG, "Exception while determining type of " + uri, e);
+ return null;
+ } finally {
+ // We need to clear the identity to call removeContentProviderExternalUnchecked
+ if (!clearedIdentity) {
+ ident = Binder.clearCallingIdentity();
+ }
+ try {
+ if (holder != null) {
+ removeContentProviderExternalUnchecked(name, null, userId);
}
- userState.mProviderLastReportedFg.put(authority, now);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Allows apps to retrieve the MIME type of a URI.
+ * If an app is in the same user as the ContentProvider, or if it is allowed to interact across
+ * users, then it does not need permission to access the ContentProvider.
+ * Either way, it needs cross-user uri grants.
+ */
+ void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) {
+ mService.enforceNotIsolatedCaller("getProviderMimeTypeAsync");
+ final String name = uri.getAuthority();
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ final long ident = canClearIdentity(callingPid, callingUid, userId)
+ ? Binder.clearCallingIdentity() : 0;
+ try {
+ final ContentProviderHolder holder = getContentProviderExternalUnchecked(name, null,
+ callingUid, "*getmimetype*", safeUserId);
+ if (holder != null) {
+ holder.provider.getTypeAsync(uri, new RemoteCallback(result -> {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ removeContentProviderExternalUnchecked(name, null, safeUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ resultCallback.sendResult(result);
+ }));
+ } else {
+ resultCallback.sendResult(Bundle.EMPTY);
}
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ resultCallback.sendResult(Bundle.EMPTY);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean canClearIdentity(int callingPid, int callingUid, int userId) {
+ if (UserHandle.getUserId(callingUid) == userId) {
+ return true;
+ }
+ return ActivityManagerService.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, callingPid,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
+ || ActivityManagerService.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPid,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Check if the calling UID has a possible chance at accessing the provider
+ * at the given authority and user.
+ */
+ String checkContentProviderAccess(String authority, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ mService.mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
+ userId = UserHandle.getCallingUserId();
+ }
+
+ ProviderInfo cpi = null;
+ try {
+ cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
+ ActivityManagerService.STOCK_PM_FLAGS
+ | PackageManager.GET_URI_PERMISSION_PATTERNS
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ } catch (RemoteException ignored) {
+ }
+ if (cpi == null) {
+ return "Failed to find provider " + authority + " for user " + userId
+ + "; expected to find a valid ContentProvider for this authority";
+ }
+
+ ProcessRecord r;
+ synchronized (mService.mPidsSelfLocked) {
+ r = mService.mPidsSelfLocked.get(Binder.getCallingPid());
+ }
+ if (r == null) {
+ return "Failed to find PID " + Binder.getCallingPid();
+ }
+
+ synchronized (mService) {
+ return checkContentProviderPermissionLocked(cpi, r, userId, true);
}
}
+ int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
+ if (Thread.holdsLock(mService.mActivityTaskManager.getGlobalLock())) {
+ Slog.wtf(TAG, new IllegalStateException("Unable to check Uri permission"
+ + " because caller is holding WM lock; assuming permission denied"));
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ final String name = uri.getAuthority();
+ final long ident = Binder.clearCallingIdentity();
+ ContentProviderHolder holder = null;
+ try {
+ holder = getContentProviderExternalUnchecked(name, null, callingUid,
+ "*checkContentProviderUriPermission*", userId);
+ if (holder != null) {
+ return holder.provider.checkUriPermission(null, null, uri, callingUid, modeFlags);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ return PackageManager.PERMISSION_DENIED;
+ } catch (Exception e) {
+ Log.w(TAG, "Exception while determining type of " + uri, e);
+ return PackageManager.PERMISSION_DENIED;
+ } finally {
+ try {
+ if (holder != null) {
+ removeContentProviderExternalUnchecked(name, null, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+
+ @GuardedBy("mService")
+ void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
+ cleanupAppInLaunchingProvidersLocked(app, true);
+ mService.mProcessList.removeProcessLocked(app, false, true,
+ ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ "timeout publishing content providers");
+ }
+
+ List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
+ final List<ProviderInfo> providers;
+ try {
+ providers = AppGlobals.getPackageManager().queryContentProviders(
+ app.processName, app.uid, ActivityManagerService.STOCK_PM_FLAGS
+ | PackageManager.GET_URI_PERMISSION_PATTERNS
+ | PackageManager.MATCH_DIRECT_BOOT_AUTO, /*metaDataKey=*/ null)
+ .getList();
+ } catch (RemoteException ex) {
+ return null;
+ }
+ if (providers == null) {
+ return null;
+ }
+
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
+ }
+
+ int numProviders = providers.size();
+ app.pubProviders.ensureCapacity(numProviders + app.pubProviders.size());
+ for (int i = 0; i < numProviders; i++) {
+ // NOTE: keep logic in sync with installEncryptionUnawareProviders
+ ProviderInfo cpi = providers.get(i);
+ boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo,
+ cpi.name, cpi.flags);
+ if (singleton && app.userId != UserHandle.USER_SYSTEM) {
+ // This is a singleton provider, but a user besides the
+ // default user is asking to initialize a process it runs
+ // in... well, no, it doesn't actually run in this process,
+ // it runs in the process of the default user. Get rid of it.
+ providers.remove(i);
+ numProviders--;
+ i--;
+ continue;
+ }
+
+ ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, app.userId);
+ if (cpr == null) {
+ cpr = new ContentProviderRecord(mService, cpi, app.info, comp, singleton);
+ mProviderMap.putProviderByClass(comp, cpr);
+ }
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
+ }
+ app.pubProviders.put(cpi.name, cpr);
+ if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
+ // Don't add this if it is a platform component that is marked
+ // to run in multiple processes, because this is actually
+ // part of the framework so doesn't make sense to track as a
+ // separate apk in the process.
+ app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.longVersionCode,
+ mService.mProcessStats);
+ }
+ mService.notifyPackageUse(cpi.applicationInfo.packageName,
+ PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
+ }
+ return providers;
+ }
+
private final class DevelopmentSettingsObserver extends ContentObserver {
private final Uri mUri = Settings.Global.getUriFor(
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
@@ -1428,11 +1141,8 @@ public class ContentProviderHelper {
SettingsToPropertiesMapper.start(mService.mContext.getContentResolver());
mService.mOomAdjuster.initSettings();
- // Now that the settings provider is published we can consider sending
- // in a rescue party.
+ // Now that the settings provider is published we can consider sending in a rescue party.
RescueParty.onSettingsProviderPublished(mService.mContext);
-
- //mUsageStatsService.monitorPackages();
}
/**
@@ -1447,29 +1157,28 @@ public class ContentProviderHelper {
PackageManager.GET_PROVIDERS | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
synchronized (mService) {
- final int NP = mService.mProcessList.mProcessNames.getMap().size();
- for (int ip = 0; ip < NP; ip++) {
+ final int numProc = mService.mProcessList.mProcessNames.getMap().size();
+ for (int iProc = 0; iProc < numProc; iProc++) {
final SparseArray<ProcessRecord> apps =
- mService.mProcessList.mProcessNames.getMap().valueAt(ip);
- final int NA = apps.size();
- for (int ia = 0; ia < NA; ia++) {
- final ProcessRecord app = apps.valueAt(ia);
+ mService.mProcessList.mProcessNames.getMap().valueAt(iProc);
+ for (int iApp = 0, numApps = apps.size(); iApp < numApps; iApp++) {
+ final ProcessRecord app = apps.valueAt(iApp);
if (app.userId != userId || app.thread == null || app.unlocked) continue;
- final int NG = app.pkgList.size();
- for (int ig = 0; ig < NG; ig++) {
+ for (int iPkg = 0, numPkgs = app.pkgList.size(); iPkg < numPkgs; iPkg++) {
try {
- final String pkgName = app.pkgList.keyAt(ig);
+ final String pkgName = app.pkgList.keyAt(iPkg);
final PackageInfo pkgInfo = AppGlobals.getPackageManager()
.getPackageInfo(pkgName, matchFlags, userId);
if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
for (ProviderInfo pi : pkgInfo.providers) {
- // TODO: keep in sync with generateApplicationProvidersLocked
- final boolean processMatch = Objects.equals(pi.processName,
- app.processName) || pi.multiprocess;
- final boolean userMatch = mService.isSingleton(pi.processName,
- pi.applicationInfo, pi.name, pi.flags)
- ? (app.userId == UserHandle.USER_SYSTEM) : true;
+ // NOTE: keep in sync with generateApplicationProvidersLocked
+ final boolean processMatch =
+ Objects.equals(pi.processName, app.processName)
+ || pi.multiprocess;
+ final boolean userMatch = !mService.isSingleton(
+ pi.processName, pi.applicationInfo, pi.name, pi.flags)
+ || app.userId == UserHandle.USER_SYSTEM;
if (processMatch && userMatch) {
Log.v(TAG, "Installing " + pi);
app.thread.scheduleInstallProvider(pi);
@@ -1486,6 +1195,296 @@ public class ContentProviderHelper {
}
}
+ @GuardedBy("mService")
+ private ContentProviderConnection incProviderCountLocked(ProcessRecord r,
+ final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
+ String callingPackage, String callingTag, boolean stable, boolean updateLru,
+ long startTime, ProcessList processList) {
+ if (r == null) {
+ cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
+ return null;
+ }
+
+
+ for (int i = 0, size = r.conProviders.size(); i < size; i++) {
+ ContentProviderConnection conn = r.conProviders.get(i);
+ if (conn.provider == cpr) {
+ conn.incrementCount(stable);
+ return conn;
+ }
+ }
+
+ // Create a new ContentProviderConnection. The reference count is known to be 1.
+ ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
+ conn.startAssociationIfNeeded();
+ conn.initializeCount(stable);
+ cpr.connections.add(conn);
+ r.conProviders.add(conn);
+ mService.startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
+ cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ if (updateLru && cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+ // If this is a perceptible app accessing the provider, make
+ // sure to count it as being accessed and thus back up on
+ // the LRU list. This is good because content providers are
+ // often expensive to start. The calls to checkTime() use
+ // the "getContentProviderImpl" tag here, because it's part
+ // of the checktime log in getContentProviderImpl().
+ checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
+ processList.updateLruProcessLocked(cpr.proc, false, null);
+ checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
+ }
+ return conn;
+ }
+
+ @GuardedBy("mService")
+ private boolean decProviderCountLocked(ContentProviderConnection conn,
+ ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+ if (conn == null) {
+ cpr.removeExternalProcessHandleLocked(externalProcessToken);
+ return false;
+ }
+ if (conn.decrementCount(stable) != 0) {
+ return false;
+ }
+
+ cpr = conn.provider;
+ conn.stopAssociation();
+ cpr.connections.remove(conn);
+ conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
+ mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ return true;
+ }
+
+ /**
+ * Check if {@link ProcessRecord} has a possible chance at accessing the
+ * given {@link ProviderInfo}. Final permission checking is always done
+ * in {@link ContentProvider}.
+ */
+ private String checkContentProviderPermissionLocked(ProviderInfo cpi, ProcessRecord r,
+ int userId, boolean checkUser) {
+ final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
+ final int callingUid = (r != null) ? r.uid : Binder.getCallingUid();
+ boolean checkedGrants = false;
+ if (checkUser) {
+ // Looking for cross-user grants before enforcing the typical cross-users permissions
+ int tmpTargetUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
+ if (mService.mUgmInternal.checkAuthorityGrants(
+ callingUid, cpi, tmpTargetUserId, checkUser)) {
+ return null;
+ }
+ checkedGrants = true;
+ }
+ userId = mService.mUserController.handleIncomingUser(callingPid, callingUid, userId,
+ false, ActivityManagerInternal.ALLOW_NON_FULL,
+ "checkContentProviderPermissionLocked " + cpi.authority, null);
+ if (userId != tmpTargetUserId) {
+ // When we actually went to determine the final target user ID, this ended
+ // up different than our initial check for the authority. This is because
+ // they had asked for USER_CURRENT_OR_SELF and we ended up switching to
+ // SELF. So we need to re-check the grants again.
+ checkedGrants = false;
+ }
+ }
+ if (ActivityManagerService.checkComponentPermission(cpi.readPermission,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ if (ActivityManagerService.checkComponentPermission(cpi.writePermission,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+
+ PathPermission[] pps = cpi.pathPermissions;
+ if (pps != null) {
+ int i = pps.length;
+ while (i > 0) {
+ i--;
+ PathPermission pp = pps[i];
+ String pprperm = pp.getReadPermission();
+ if (pprperm != null && ActivityManagerService.checkComponentPermission(pprperm,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ String ppwperm = pp.getWritePermission();
+ if (ppwperm != null && ActivityManagerService.checkComponentPermission(ppwperm,
+ callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported)
+ == PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+ }
+ }
+ if (!checkedGrants
+ && mService.mUgmInternal.checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
+ return null;
+ }
+
+ final String suffix;
+ if (!cpi.exported) {
+ suffix = " that is not exported from UID " + cpi.applicationInfo.uid;
+ } else if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(cpi.readPermission)) {
+ suffix = " requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs";
+ } else {
+ suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission;
+ }
+ final String msg = "Permission Denial: opening provider " + cpi.name
+ + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")" + suffix;
+ Slog.w(TAG, msg);
+ return msg;
+ }
+
+ private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid,
+ ProviderInfo cpi) {
+ if (callingApp == null) {
+ return mService.validateAssociationAllowedLocked(cpi.packageName,
+ cpi.applicationInfo.uid, null, callingUid) ? null : "<null>";
+ }
+ for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
+ if (!mService.validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i),
+ callingApp.uid, cpi.packageName, cpi.applicationInfo.uid)) {
+ return cpi.packageName;
+ }
+ }
+ return null;
+ }
+
+ ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId, int pmFlags) {
+ ProviderInfo pi = null;
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
+ if (cpr != null) {
+ pi = cpr.info;
+ } else {
+ try {
+ pi = AppGlobals.getPackageManager().resolveContentProvider(
+ authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId);
+ } catch (RemoteException ex) {
+ }
+ }
+ return pi;
+ }
+
+ private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
+ String authority) {
+ if (app == null
+ || app.getCurProcState() > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ return;
+ }
+
+
+ UserState userState = mService.mUserController.getStartedUserState(app.userId);
+ if (userState == null) return;
+ final long now = SystemClock.elapsedRealtime();
+ Long lastReported = userState.mProviderLastReportedFg.get(authority);
+ if (lastReported == null || lastReported < now - 60 * 1000L) {
+ if (mService.mSystemReady) {
+ // Cannot touch the user stats if not system ready
+ mService.mUsageStatsService.reportContentProviderUsage(
+ authority, providerPkgName, app.userId);
+ }
+ userState.mProviderLastReportedFg.put(authority, now);
+ }
+ }
+
+ private static final int[] PROCESS_STATE_STATS_FORMAT = new int[] {
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM | PROC_PARENS,
+ PROC_SPACE_TERM | PROC_CHAR | PROC_OUT_LONG, // 3: process state
+ };
+
+ private final long[] mProcessStateStatsLongs = new long[1];
+
+ private boolean isProcessAliveLocked(ProcessRecord proc) {
+ if (proc.pid <= 0) {
+ if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
+ Slog.d(ActivityManagerService.TAG, "Process hasn't started yet: " + proc);
+ }
+ return false;
+ }
+ if (proc.procStatFile == null) {
+ proc.procStatFile = "/proc/" + proc.pid + "/stat";
+ }
+ mProcessStateStatsLongs[0] = 0;
+ if (!Process.readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null,
+ mProcessStateStatsLongs, null)) {
+ if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
+ Slog.d(ActivityManagerService.TAG,
+ "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile);
+ }
+ return false;
+ }
+ final long state = mProcessStateStatsLongs[0];
+ if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
+ Slog.d(ActivityManagerService.TAG,
+ "RETRIEVED STATE FOR " + proc.procStatFile + ": " + (char) state);
+ }
+ if (state != 'Z' && state != 'X' && state != 'x' && state != 'K') {
+ return Process.getUidForPid(proc.pid) == proc.uid;
+ }
+ return false;
+ }
+
+ private static final class StartActivityRunnable implements Runnable {
+ private final Context mContext;
+ private final Intent mIntent;
+ private final UserHandle mUserHandle;
+
+ StartActivityRunnable(Context context, Intent intent, UserHandle userHandle) {
+ this.mContext = context;
+ this.mIntent = intent;
+ this.mUserHandle = userHandle;
+ }
+
+ @Override
+ public void run() {
+ mContext.startActivityAsUser(mIntent, mUserHandle);
+ }
+ }
+
+ private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
+ ProcessRecord r, final int userId, Context context) {
+ if (!mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ cpi.packageName, userId)) {
+ return true;
+ }
+
+ final boolean callerForeground = r == null
+ || r.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
+
+ // Show a permission review UI only for starting from a foreground app
+ if (!callerForeground) {
+ Slog.w(TAG, "u" + userId + " Instantiating a provider in package "
+ + cpi.packageName + " requires a permissions review");
+ return false;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName);
+
+ if (ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW) {
+ Slog.i(TAG, "u" + userId + " Launching permission review "
+ + "for package " + cpi.packageName);
+ }
+
+ final UserHandle userHandle = new UserHandle(userId);
+ mService.mHandler.post(new StartActivityRunnable(context, intent, userHandle));
+
+ return false;
+ }
+
/**
* Remove the dying provider from known provider map and launching provider map.
* @param proc The dying process recoder
@@ -1493,8 +1492,8 @@ public class ContentProviderHelper {
* @param always If true, remove the provider from launching map always, no more restart attempt
* @return true if the given provider is in launching
*/
- boolean removeDyingProviderLocked(ProcessRecord proc,
- ContentProviderRecord cpr, boolean always) {
+ boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr,
+ boolean always) {
boolean inLaunching = mLaunchingProviders.contains(cpr);
if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
// It's being launched but we've reached maximum attempts, force the removal
@@ -1536,10 +1535,8 @@ public class ContentProviderHelper {
conn.dead = true;
if (conn.stableCount() > 0) {
if (!capp.isPersistent() && capp.thread != null
- && capp.pid != 0
- && capp.pid != ActivityManagerService.MY_PID) {
- capp.kill("depends on provider "
- + cpr.name.flattenToShortString()
+ && capp.pid != 0 && capp.pid != ActivityManagerService.MY_PID) {
+ capp.kill("depends on provider " + cpr.name.flattenToShortString()
+ " in dying proc " + (proc != null ? proc.processName : "??")
+ " (adj " + (proc != null ? proc.setAdj : "??") + ")",
ApplicationExitInfo.REASON_DEPENDENCY_DIED,
@@ -1555,8 +1552,8 @@ public class ContentProviderHelper {
// clean up this connection, we'll just remove it.
cpr.connections.remove(i);
if (conn.client.conProviders.remove(conn)) {
- mService.stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
- cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ mService.stopAssociationLocked(capp.uid, capp.processName,
+ cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
}
}
}
@@ -1587,22 +1584,24 @@ public class ContentProviderHelper {
boolean restart = false;
for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = mLaunchingProviders.get(i);
- if (cpr.launchingApp == app) {
- if (++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
- // It's being launched but we've reached maximum attempts, mark it as bad
- alwaysBad = true;
- }
- if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
- restart = true;
- } else {
- removeDyingProviderLocked(app, cpr, true);
- }
+ if (cpr.launchingApp != app) {
+ continue;
+ }
+
+ if (++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
+ // It's being launched but we've reached maximum attempts, mark it as bad
+ alwaysBad = true;
+ }
+ if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
+ restart = true;
+ } else {
+ removeDyingProviderLocked(app, cpr, true);
}
}
return restart;
}
- void cleanupLaunchingProviders() {
+ void cleanupLaunchingProvidersLocked() {
for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) {
@@ -1614,31 +1613,6 @@ public class ContentProviderHelper {
}
}
- ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId,
- int pmFlags) {
- ProviderInfo pi = null;
- ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId);
- if (cpr != null) {
- pi = cpr.info;
- } else {
- try {
- pi = AppGlobals.getPackageManager().resolveContentProvider(
- authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId);
- } catch (RemoteException ex) {
- }
- }
- return pi;
- }
-
- @GuardedBy("mService")
- void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
- cleanupAppInLaunchingProvidersLocked(app, true);
- mService.mProcessList.removeProcessLocked(app, false, true,
- ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- "timeout publishing content providers");
- }
-
private void checkTime(long startTime, String where) {
long now = SystemClock.uptimeMillis();
if ((now - startTime) > 50) {
@@ -1649,16 +1623,13 @@ public class ContentProviderHelper {
void dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
- boolean needSep;
- boolean printedAnything = false;
-
ActivityManagerService.ItemMatcher matcher = new ActivityManagerService.ItemMatcher();
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
- needSep = mProviderMap.dumpProvidersLocked(pw, dumpAll, dumpPackage);
- printedAnything |= needSep;
+ boolean needSep = mProviderMap.dumpProvidersLocked(pw, dumpAll, dumpPackage);
+ boolean printedAnything = needSep;
if (mLaunchingProviders.size() > 0) {
boolean printed = false;