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 17package com.android.server.am; 18 19import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; 20import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; 21import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; 22import static android.app.PendingIntent.FLAG_IMMUTABLE; 23import static android.app.PendingIntent.FLAG_ONE_SHOT; 24import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION; 25import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES; 26import static android.content.Context.KEYGUARD_SERVICE; 27import static android.content.Intent.EXTRA_INTENT; 28import static android.content.Intent.EXTRA_PACKAGE_NAME; 29import static android.content.Intent.EXTRA_TASK_ID; 30import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 31import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 32import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; 33import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; 34 35import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; 36 37import android.app.ActivityOptions; 38import android.app.KeyguardManager; 39import android.app.admin.DevicePolicyManagerInternal; 40import android.content.Context; 41import android.content.IIntentSender; 42import android.content.Intent; 43import android.content.IntentSender; 44import android.content.pm.ActivityInfo; 45import android.content.pm.PackageManagerInternal; 46import android.content.pm.ResolveInfo; 47import android.content.pm.UserInfo; 48import android.os.Binder; 49import android.os.Bundle; 50import android.os.RemoteException; 51import android.os.UserHandle; 52import android.os.UserManager; 53 54import com.android.internal.annotations.VisibleForTesting; 55import com.android.internal.app.HarmfulAppWarningActivity; 56import com.android.internal.app.SuspendedAppActivity; 57import com.android.internal.app.UnlaunchableAppActivity; 58import 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 */ 67class 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 102 ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) { 103 this(service, supervisor, service.mContext, service.mUserController); 104 } 105 106 @VisibleForTesting 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 */ 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 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 */ 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 */ 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 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 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 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 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 */ 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 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