diff options
| -rw-r--r-- | services/core/java/com/android/server/am/ContentProviderHelper.java | 1488 |
1 files changed, 743 insertions, 745 deletions
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 1e0c243ff3f5..88a99c52a92b 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -82,16 +82,14 @@ public class ContentProviderHelper { private ActivityManagerService mService; - private boolean mSystemProvidersInstalled; - - private final ProviderMap mProviderMap; - /** * List of content providers who have clients waiting for them. The * application is currently being launched and the provider will be * 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 +100,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 +120,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) { @@ -1015,7 +386,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; } @@ -1214,62 +585,6 @@ public class ContentProviderHelper { 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) { if (providers == null) { @@ -1346,23 +661,440 @@ public class ContentProviderHelper { } } - 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, "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); } - userState.mProviderLastReportedFg.put(authority, now); + } + } 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; + } + + /** + * 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); + } + } + + 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) { + 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; } private final class DevelopmentSettingsObserver extends ContentObserver { @@ -1486,6 +1218,297 @@ 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) { + 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; + } + + /** + * 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; + } + + 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) 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); + } + 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 (!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)) { + + 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; + } + /** * Remove the dying provider from known provider map and launching provider map. * @param proc The dying process recoder @@ -1614,31 +1637,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) { |