From 1a34274336c19b50173ea60071dfc6427d40dbf6 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Thu, 26 Apr 2018 14:56:59 -0700 Subject: [PATCH] Propagate calling UID to AM from LauncherApps - So that AM can perform all the necessary caller checks, except for the cross-profile/user check. - Note PixelLauncher is the recent app which gets extra privileges. So I used ShortcutLauncherDemo and a 3p launcher for manual tests. Fixes: 78635323 Test: manual test, with a 3p launcher. (nova) - Launch primary profile app -> launches fine - Launch work profile app-> launches fine - Launch suspended work profile app -> "can't open this app" dialog is shown. - Launch the primary counterpart of the suspended work profile app -> launches fine. - Launch work profile app in quiet mode, with separate work challenge -> "turn on work profile"? dialog is shown -> then "cancel" -> nothing happens. -> then "turn on" -> "re-enter your pin" is shown -> type pin -> work profile app starts fine. - Launch work profile app without separate work challenge -> "turn on work profile"? dialog is shown -> then "cancel" -> nothing happens. -> then "turn on" -> work profile starts and the app starts fine. - "App info" on work profile app -> Setting page opens fine. - "App info" on primary profile app -> Setting page opens fine. Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest*.java Test: atest cts/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherApps*.java Change-Id: Ie665a8890407d05c1d877f04d9c5c3a1caad18e1 --- .../android/app/ActivityManagerInternal.java | 11 ++++++++ .../android/content/pm/ILauncherApps.aidl | 5 ++-- .../java/android/content/pm/LauncherApps.java | 6 +++-- .../server/am/ActivityManagerService.java | 25 ++++++++++++++++-- .../server/am/ActivityStackSupervisor.java | 18 ++++++++----- .../server/am/ActivityStartController.java | 2 +- .../server/am/ActivityStartInterceptor.java | 17 +++++++----- .../android/server/am/ActivityStarter.java | 15 ++++++----- .../server/pm/LauncherAppsService.java | 26 +++++++++++++------ 9 files changed, 91 insertions(+), 34 deletions(-) diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 97c9fa58622fb..7338bfea9d4db 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -235,6 +235,17 @@ public abstract class ActivityManagerInternal { public abstract int startActivitiesAsPackage(String packageName, int userId, Intent[] intents, Bundle bOptions); + /** + * Start activity {@code intent} without calling user-id check. + * + * - DO NOT call it with the calling UID cleared. + * - The caller must do the calling user ID check. + * + * @return error codes used by {@link IActivityManager#startActivity} and its siblings. + */ + public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage, + Intent intent, @Nullable Bundle options, int userId); + /** * Get the procstate for the UID. The return value will be between * {@link ActivityManager#MIN_PROCESS_STATE} and {@link ActivityManager#MAX_PROCESS_STATE}. diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index ae1c2071eecac..ba7710b8ef48a 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -16,6 +16,7 @@ package android.content.pm; +import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Intent; import android.content.IntentSender; @@ -42,10 +43,10 @@ interface ILauncherApps { String callingPackage, String packageName, in UserHandle user); ActivityInfo resolveActivity( String callingPackage, in ComponentName component, in UserHandle user); - void startActivityAsUser(String callingPackage, + void startActivityAsUser(in IApplicationThread caller, String callingPackage, in ComponentName component, in Rect sourceBounds, in Bundle opts, in UserHandle user); - void showAppDetailsAsUser( + void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage, in ComponentName component, in Rect sourceBounds, in Bundle opts, in UserHandle user); boolean isPackageEnabled(String callingPackage, String packageName, in UserHandle user); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 8717601cfde00..fa423e29406ae 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -548,7 +548,8 @@ public class LauncherApps { Log.i(TAG, "StartMainActivity " + component + " " + user.getIdentifier()); } try { - mService.startActivityAsUser(mContext.getPackageName(), + mService.startActivityAsUser(mContext.getIApplicationThread(), + mContext.getPackageName(), component, sourceBounds, opts, user); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); @@ -568,7 +569,8 @@ public class LauncherApps { Rect sourceBounds, Bundle opts) { logErrorForInvalidProfileAccess(user); try { - mService.showAppDetailsAsUser(mContext.getPackageName(), + mService.showAppDetailsAsUser(mContext.getIApplicationThread(), + mContext.getPackageName(), component, sourceBounds, opts, user); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ae26c2377f287..ea9f4119740cc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5049,9 +5049,20 @@ public class ActivityManagerService extends IActivityManager.Stub public final int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { + return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, + resultWho, requestCode, startFlags, profilerInfo, bOptions, userId, + true /*validateIncomingUser*/); + } + + public final int startActivityAsUser(IApplicationThread caller, String callingPackage, + Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, + int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, + boolean validateIncomingUser) { enforceNotIsolatedCaller("startActivity"); - userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), - userId, false, ALLOW_FULL_ONLY, "startActivity", null); + + userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser, + Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser"); + // TODO: Switch to user app stacks here. return mActivityStartController.obtainStarter(intent, "startActivityAsUser") .setCaller(caller) @@ -26342,6 +26353,16 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public int startActivityAsUser(IApplicationThread caller, String callerPacakge, + Intent intent, Bundle options, int userId) { + return ActivityManagerService.this.startActivityAsUser( + caller, callerPacakge, intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId, + false /*validateIncomingUser*/); + } + @Override public int getUidProcessState(int uid) { return getUidState(uid); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index e5565dccc70ea..ccf7d0621ac4e 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1315,10 +1315,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return aInfo; } - ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) { - return resolveIntent(intent, resolvedType, userId, 0, Binder.getCallingUid()); - } - ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags, int filterCallingUid) { synchronized (mService) { @@ -1330,9 +1326,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { modifiedFlags |= PackageManager.MATCH_INSTANT; } - return mService.getPackageManagerInternalLocked().resolveIntent( - intent, resolvedType, modifiedFlags, userId, true, filterCallingUid); + // In order to allow cross-profile lookup, we clear the calling identity here. + // Note the binder identity won't affect the result, but filterCallingUid will. + + // Cross-user/profile call check are done at the entry points + // (e.g. AMS.startActivityAsUser). + final long token = Binder.clearCallingIdentity(); + try { + return mService.getPackageManagerInternalLocked().resolveIntent( + intent, resolvedType, modifiedFlags, userId, true, filterCallingUid); + } finally { + Binder.restoreCallingIdentity(token); + } } finally { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java index 31ccf3530fb99..5e29d10908cab 100644 --- a/services/core/java/com/android/server/am/ActivityStartController.java +++ b/services/core/java/com/android/server/am/ActivityStartController.java @@ -233,7 +233,7 @@ public class ActivityStartController { * ensures {@code targetUserId} is a real user ID and not a special user ID such as * {@link android.os.UserHandle#USER_ALL}, etc. */ - private int checkTargetUser(int targetUserId, boolean validateIncomingUser, + int checkTargetUser(int targetUserId, boolean validateIncomingUser, int realCallingPid, int realCallingUid, String reason) { if (validateIncomingUser) { return mService.mUserController.handleIncomingUser(realCallingPid, realCallingUid, diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java index 5b6b50869dda2..8c3ff34aa73d2 100644 --- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java @@ -203,7 +203,7 @@ class ActivityStartInterceptor { mResolvedType = null; final UserInfo parent = mUserManager.getProfileParent(mUserId); - mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid); mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); return true; } @@ -223,9 +223,11 @@ class ActivityStartInterceptor { final UserInfo parent = mUserManager.getProfileParent(mUserId); if (parent != null) { - mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, + mRealCallingUid); } else { - mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId); + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, + mRealCallingUid); } mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); return true; @@ -244,7 +246,8 @@ class ActivityStartInterceptor { final Intent moreDetailsIntent = new Intent(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS) .setPackage(suspendingPackage); final String requiredPermission = Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS; - final ResolveInfo resolvedInfo = mSupervisor.resolveIntent(moreDetailsIntent, null, userId); + final ResolveInfo resolvedInfo = mSupervisor.resolveIntent(moreDetailsIntent, null, userId, + 0, mRealCallingUid); if (resolvedInfo != null && resolvedInfo.activityInfo != null && requiredPermission.equals(resolvedInfo.activityInfo.permission)) { moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage) @@ -276,7 +279,7 @@ class ActivityStartInterceptor { mCallingPid = mRealCallingPid; mCallingUid = mRealCallingUid; mResolvedType = null; - mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, 0); + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); return true; } @@ -309,7 +312,7 @@ class ActivityStartInterceptor { } final UserInfo parent = mUserManager.getProfileParent(mUserId); - mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid); mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); return true; } @@ -362,7 +365,7 @@ class ActivityStartInterceptor { mCallingUid = mRealCallingUid; mResolvedType = null; - mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId); + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); return true; } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 7ff7d9a9b6c47..fb4107cfd221f 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -791,7 +791,7 @@ class ActivityStarter { callingUid = realCallingUid; callingPid = realCallingPid; - rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId); + rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0, realCallingUid); aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/); @@ -952,6 +952,9 @@ class ActivityStarter { mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(); boolean componentSpecified = intent.getComponent() != null; + final int realCallingPid = Binder.getCallingPid(); + final int realCallingUid = Binder.getCallingUid(); + // Save a copy in case ephemeral needs it final Intent ephemeralIntent = new Intent(intent); // Don't modify the client's object! @@ -969,7 +972,8 @@ class ActivityStarter { componentSpecified = false; } - ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId); + ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, + 0 /* matchFlags */, realCallingUid); if (rInfo == null) { UserInfo userInfo = mSupervisor.getUserInfo(userId); if (userInfo != null && userInfo.isManagedProfile()) { @@ -991,7 +995,7 @@ class ActivityStarter { rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - Binder.getCallingUid()); + realCallingUid); } } } @@ -999,8 +1003,6 @@ class ActivityStarter { ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo); synchronized (mService) { - final int realCallingPid = Binder.getCallingPid(); - final int realCallingUid = Binder.getCallingUid(); int callingPid; if (callingUid >= 0) { callingPid = -1; @@ -1073,7 +1075,8 @@ class ActivityStarter { callingUid = Binder.getCallingUid(); callingPid = Binder.getCallingPid(); componentSpecified = true; - rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId); + rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId, + 0 /* matchFlags */, realCallingUid); aInfo = rInfo != null ? rInfo.activityInfo : null; if (aInfo != null) { aInfo = mService.getActivityInfoForUser(aInfo, userId); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 595de9e35a7f7..feac8e6a45094 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -21,6 +21,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; +import android.app.IApplicationThread; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -560,7 +561,7 @@ public class LauncherAppsService extends SystemService { } @Override - public void startActivityAsUser(String callingPackage, + public void startActivityAsUser(IApplicationThread caller, String callingPackage, ComponentName component, Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException { if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) { @@ -574,6 +575,8 @@ public class LauncherAppsService extends SystemService { | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); launchIntent.setPackage(component.getPackageName()); + boolean canLaunch = false; + final int callingUid = injectBinderCallingUid(); long ident = Binder.clearCallingIdentity(); try { @@ -604,35 +607,42 @@ public class LauncherAppsService extends SystemService { // this component so ok to launch. launchIntent.setPackage(null); launchIntent.setComponent(component); - mContext.startActivityAsUser(launchIntent, opts, user); - return; + canLaunch = true; + break; } } - throw new SecurityException("Attempt to launch activity without " - + " category Intent.CATEGORY_LAUNCHER " + component); + if (!canLaunch) { + throw new SecurityException("Attempt to launch activity without " + + " category Intent.CATEGORY_LAUNCHER " + component); + } } finally { Binder.restoreCallingIdentity(ident); } + mActivityManagerInternal.startActivityAsUser(caller, callingPackage, + launchIntent, opts, user.getIdentifier()); } @Override - public void showAppDetailsAsUser(String callingPackage, ComponentName component, + public void showAppDetailsAsUser(IApplicationThread caller, + String callingPackage, ComponentName component, Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException { if (!canAccessProfile(user.getIdentifier(), "Cannot show app details")) { return; } + final Intent intent; long ident = Binder.clearCallingIdentity(); try { String packageName = component.getPackageName(); - Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", packageName, null)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.setSourceBounds(sourceBounds); - mContext.startActivityAsUser(intent, opts, user); } finally { Binder.restoreCallingIdentity(ident); } + mActivityManagerInternal.startActivityAsUser(caller, callingPackage, + intent, opts, user.getIdentifier()); } /** Checks if user is a profile of or same as listeningUser.