1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.am; 18 19 import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; 20 import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; 21 import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; 22 import static android.app.PendingIntent.FLAG_IMMUTABLE; 23 import static android.app.PendingIntent.FLAG_ONE_SHOT; 24 import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION; 25 import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES; 26 import static android.content.Context.KEYGUARD_SERVICE; 27 import static android.content.Intent.EXTRA_INTENT; 28 import static android.content.Intent.EXTRA_PACKAGE_NAME; 29 import static android.content.Intent.EXTRA_TASK_ID; 30 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 31 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 32 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; 33 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; 34 35 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 36 37 import android.app.ActivityOptions; 38 import android.app.KeyguardManager; 39 import android.app.admin.DevicePolicyManagerInternal; 40 import android.content.Context; 41 import android.content.IIntentSender; 42 import android.content.Intent; 43 import android.content.IntentSender; 44 import android.content.pm.ActivityInfo; 45 import android.content.pm.PackageManagerInternal; 46 import android.content.pm.ResolveInfo; 47 import android.content.pm.UserInfo; 48 import android.os.Binder; 49 import android.os.Bundle; 50 import android.os.RemoteException; 51 import android.os.UserHandle; 52 import android.os.UserManager; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.internal.app.HarmfulAppWarningActivity; 56 import com.android.internal.app.SuspendedAppActivity; 57 import com.android.internal.app.UnlaunchableAppActivity; 58 import com.android.server.LocalServices; 59 60 /** 61 * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked} 62 * It's initialized via setStates and interception occurs via the intercept method. 63 * 64 * Note that this class is instantiated when {@link ActivityManagerService} gets created so there 65 * is no guarantee that other system services are already present. 66 */ 67 class ActivityStartInterceptor { 68 69 private final ActivityManagerService mService; 70 private final ActivityStackSupervisor mSupervisor; 71 private final Context mServiceContext; 72 private final UserController mUserController; 73 74 // UserManager cannot be final as it's not ready when this class is instantiated during boot 75 private UserManager mUserManager; 76 77 /* 78 * Per-intent states loaded from ActivityStarter than shouldn't be changed by any 79 * interception routines. 80 */ 81 private int mRealCallingPid; 82 private int mRealCallingUid; 83 private int mUserId; 84 private int mStartFlags; 85 private String mCallingPackage; 86 87 /* 88 * Per-intent states that were load from ActivityStarter and are subject to modifications 89 * by the interception routines. After calling {@link #intercept} the caller should assign 90 * these values back to {@link ActivityStarter#startActivityLocked}'s local variables if 91 * {@link #intercept} returns true. 92 */ 93 Intent mIntent; 94 int mCallingPid; 95 int mCallingUid; 96 ResolveInfo mRInfo; 97 ActivityInfo mAInfo; 98 String mResolvedType; 99 TaskRecord mInTask; 100 ActivityOptions mActivityOptions; 101 ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor)102 ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) { 103 this(service, supervisor, service.mContext, service.mUserController); 104 } 105 106 @VisibleForTesting ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor, Context context, UserController userController)107 ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor, 108 Context context, UserController userController) { 109 mService = service; 110 mSupervisor = supervisor; 111 mServiceContext = context; 112 mUserController = userController; 113 } 114 115 /** 116 * Effectively initialize the class before intercepting the start intent. The values set in this 117 * method should not be changed during intercept. 118 */ setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, String callingPackage)119 void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, 120 String callingPackage) { 121 mRealCallingPid = realCallingPid; 122 mRealCallingUid = realCallingUid; 123 mUserId = userId; 124 mStartFlags = startFlags; 125 mCallingPackage = callingPackage; 126 } 127 createIntentSenderForOriginalIntent(int callingUid, int flags)128 private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) { 129 Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary(); 130 final IIntentSender target = mService.getIntentSenderLocked( 131 INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/, 132 null /*resultCode*/, 0 /*requestCode*/, 133 new Intent[] { mIntent }, new String[] { mResolvedType }, 134 flags, activityOptions); 135 return new IntentSender(target); 136 } 137 138 /** 139 * Intercept the launch intent based on various signals. If an interception happened the 140 * internal variables get assigned and need to be read explicitly by the caller. 141 * 142 * @return true if an interception occurred 143 */ intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions)144 boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, 145 TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) { 146 mUserManager = UserManager.get(mServiceContext); 147 148 mIntent = intent; 149 mCallingPid = callingPid; 150 mCallingUid = callingUid; 151 mRInfo = rInfo; 152 mAInfo = aInfo; 153 mResolvedType = resolvedType; 154 mInTask = inTask; 155 mActivityOptions = activityOptions; 156 157 if (interceptSuspendedPackageIfNeeded()) { 158 // Skip the rest of interceptions as the package is suspended by device admin so 159 // no user action can undo this. 160 return true; 161 } 162 if (interceptQuietProfileIfNeeded()) { 163 // If work profile is turned off, skip the work challenge since the profile can only 164 // be unlocked when profile's user is running. 165 return true; 166 } 167 if (interceptHarmfulAppIfNeeded()) { 168 // If the app has a "harmful app" warning associated with it, we should ask to uninstall 169 // before issuing the work challenge. 170 return true; 171 } 172 return interceptWorkProfileChallengeIfNeeded(); 173 } 174 175 /** 176 * If the activity option is the {@link ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} one, 177 * defer the animation until the original intent is started. 178 * 179 * @return the activity option used to start the original intent. 180 */ deferCrossProfileAppsAnimationIfNecessary()181 private Bundle deferCrossProfileAppsAnimationIfNecessary() { 182 if (mActivityOptions != null 183 && mActivityOptions.getAnimationType() == ANIM_OPEN_CROSS_PROFILE_APPS) { 184 mActivityOptions = null; 185 return ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle(); 186 } 187 return null; 188 } 189 interceptQuietProfileIfNeeded()190 private boolean interceptQuietProfileIfNeeded() { 191 // Do not intercept if the user has not turned off the profile 192 if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) { 193 return false; 194 } 195 196 IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, 197 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT); 198 199 mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target); 200 mCallingPid = mRealCallingPid; 201 mCallingUid = mRealCallingUid; 202 mResolvedType = null; 203 204 final UserInfo parent = mUserManager.getProfileParent(mUserId); 205 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid); 206 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 207 return true; 208 } 209 interceptSuspendedByAdminPackage()210 private boolean interceptSuspendedByAdminPackage() { 211 DevicePolicyManagerInternal devicePolicyManager = LocalServices 212 .getService(DevicePolicyManagerInternal.class); 213 if (devicePolicyManager == null) { 214 return false; 215 } 216 mIntent = devicePolicyManager.createShowAdminSupportIntent(mUserId, true); 217 mIntent.putExtra(EXTRA_RESTRICTION, POLICY_SUSPEND_PACKAGES); 218 219 mCallingPid = mRealCallingPid; 220 mCallingUid = mRealCallingUid; 221 mResolvedType = null; 222 223 final UserInfo parent = mUserManager.getProfileParent(mUserId); 224 if (parent != null) { 225 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, 226 mRealCallingUid); 227 } else { 228 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, 229 mRealCallingUid); 230 } 231 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 232 return true; 233 } 234 interceptSuspendedPackageIfNeeded()235 private boolean interceptSuspendedPackageIfNeeded() { 236 // Do not intercept if the package is not suspended 237 if (mAInfo == null || mAInfo.applicationInfo == null || 238 (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) { 239 return false; 240 } 241 final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked(); 242 if (pmi == null) { 243 return false; 244 } 245 final String suspendedPackage = mAInfo.applicationInfo.packageName; 246 final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId); 247 if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { 248 return interceptSuspendedByAdminPackage(); 249 } 250 final String dialogMessage = pmi.getSuspendedDialogMessage(suspendedPackage, mUserId); 251 mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage, 252 suspendingPackage, dialogMessage, mUserId); 253 mCallingPid = mRealCallingPid; 254 mCallingUid = mRealCallingUid; 255 mResolvedType = null; 256 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); 257 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 258 return true; 259 } 260 interceptWorkProfileChallengeIfNeeded()261 private boolean interceptWorkProfileChallengeIfNeeded() { 262 final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId); 263 if (interceptingIntent == null) { 264 return false; 265 } 266 mIntent = interceptingIntent; 267 mCallingPid = mRealCallingPid; 268 mCallingUid = mRealCallingUid; 269 mResolvedType = null; 270 // If we are intercepting and there was a task, convert it into an extra for the 271 // ConfirmCredentials intent and unassign it, as otherwise the task will move to 272 // front even if ConfirmCredentials is cancelled. 273 if (mInTask != null) { 274 mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId); 275 mInTask = null; 276 } 277 if (mActivityOptions == null) { 278 mActivityOptions = ActivityOptions.makeBasic(); 279 } 280 281 ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity(); 282 if (homeActivityRecord != null && homeActivityRecord.getTask() != null) { 283 // Showing credential confirmation activity in home task to avoid stopping multi-windowed 284 // mode after showing the full-screen credential confirmation activity. 285 mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId); 286 } 287 288 final UserInfo parent = mUserManager.getProfileParent(mUserId); 289 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid); 290 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 291 return true; 292 } 293 294 /** 295 * Creates an intent to intercept the current activity start with Confirm Credentials if needed. 296 * 297 * @return The intercepting intent if needed. 298 */ interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId)299 private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) { 300 if (!mUserController.shouldConfirmCredentials(userId)) { 301 return null; 302 } 303 // TODO(b/28935539): should allow certain activities to bypass work challenge 304 final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(), 305 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE); 306 final KeyguardManager km = (KeyguardManager) mServiceContext 307 .getSystemService(KEYGUARD_SERVICE); 308 final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId); 309 if (newIntent == null) { 310 return null; 311 } 312 newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 313 FLAG_ACTIVITY_TASK_ON_HOME); 314 newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName); 315 newIntent.putExtra(EXTRA_INTENT, target); 316 return newIntent; 317 } 318 interceptHarmfulAppIfNeeded()319 private boolean interceptHarmfulAppIfNeeded() { 320 CharSequence harmfulAppWarning; 321 try { 322 harmfulAppWarning = mService.getPackageManager() 323 .getHarmfulAppWarning(mAInfo.packageName, mUserId); 324 } catch (RemoteException ex) { 325 return false; 326 } 327 328 if (harmfulAppWarning == null) { 329 return false; 330 } 331 332 final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, 333 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE); 334 335 mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext, 336 mAInfo.packageName, target, harmfulAppWarning); 337 338 mCallingPid = mRealCallingPid; 339 mCallingUid = mRealCallingUid; 340 mResolvedType = null; 341 342 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); 343 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 344 return true; 345 } 346 } 347