ActivityStartInterceptor.java revision e420c550ee98aefab92bae4017c46c7a969bf132
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.PendingIntent.FLAG_CANCEL_CURRENT; 21import static android.app.PendingIntent.FLAG_IMMUTABLE; 22import static android.app.PendingIntent.FLAG_ONE_SHOT; 23import static android.content.Context.KEYGUARD_SERVICE; 24import static android.content.Intent.EXTRA_INTENT; 25import static android.content.Intent.EXTRA_PACKAGE_NAME; 26import static android.content.Intent.EXTRA_TASK_ID; 27import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 28import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 29import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; 30import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; 31 32import android.app.KeyguardManager; 33import android.app.admin.DevicePolicyManagerInternal; 34import android.content.IIntentSender; 35import android.content.Intent; 36import android.content.IntentSender; 37import android.content.pm.ActivityInfo; 38import android.content.pm.ResolveInfo; 39import android.content.pm.UserInfo; 40import android.os.Binder; 41import android.os.UserHandle; 42import android.os.UserManager; 43 44import com.android.internal.app.UnlaunchableAppActivity; 45import com.android.server.LocalServices; 46 47/** 48 * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked} 49 * It's initialized 50 */ 51class ActivityStartInterceptor { 52 53 private final ActivityManagerService mService; 54 private UserManager mUserManager; 55 private final ActivityStackSupervisor mSupervisor; 56 57 /* 58 * Per-intent states loaded from ActivityStarter than shouldn't be changed by any 59 * interception routines. 60 */ 61 private int mRealCallingPid; 62 private int mRealCallingUid; 63 private int mUserId; 64 private int mStartFlags; 65 private String mCallingPackage; 66 67 /* 68 * Per-intent states that were load from ActivityStarter and are subject to modifications 69 * by the interception routines. After calling {@link #intercept} the caller should assign 70 * these values back to {@link ActivityStarter#startActivityLocked}'s local variables. 71 */ 72 Intent mIntent; 73 int mCallingPid; 74 int mCallingUid; 75 ResolveInfo mRInfo; 76 ActivityInfo mAInfo; 77 String mResolvedType; 78 TaskRecord mInTask; 79 80 ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) { 81 mService = service; 82 mSupervisor = supervisor; 83 } 84 85 void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, 86 String callingPackage) { 87 mRealCallingPid = realCallingPid; 88 mRealCallingUid = realCallingUid; 89 mUserId = userId; 90 mStartFlags = startFlags; 91 mCallingPackage = callingPackage; 92 } 93 94 void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, 95 TaskRecord inTask, int callingPid, int callingUid) { 96 mUserManager = UserManager.get(mService.mContext); 97 mIntent = intent; 98 mCallingPid = callingPid; 99 mCallingUid = callingUid; 100 mRInfo = rInfo; 101 mAInfo = aInfo; 102 mResolvedType = resolvedType; 103 mInTask = inTask; 104 if (interceptSuspendPackageIfNeed()) { 105 // Skip the rest of interceptions as the package is suspended by device admin so 106 // no user action can undo this. 107 return; 108 } 109 if (interceptQuietProfileIfNeeded()) { 110 // If work profile is turned off, skip the work challenge since the profile can only 111 // be unlocked when profile's user is running. 112 return; 113 } 114 interceptWorkProfileChallengeIfNeeded(); 115 } 116 117 private boolean interceptQuietProfileIfNeeded() { 118 // Do not intercept if the user has not turned off the profile 119 if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) { 120 return false; 121 } 122 IIntentSender target = mService.getIntentSenderLocked( 123 INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0, 124 new Intent[] {mIntent}, new String[] {mResolvedType}, 125 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null); 126 127 mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, 128 new IntentSender(target)); 129 mCallingPid = mRealCallingPid; 130 mCallingUid = mRealCallingUid; 131 mResolvedType = null; 132 133 final UserInfo parent = mUserManager.getProfileParent(mUserId); 134 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); 135 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 136 return true; 137 } 138 139 private boolean interceptSuspendPackageIfNeed() { 140 // Do not intercept if the admin did not suspend the package 141 if (mAInfo == null || mAInfo.applicationInfo == null || 142 (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) { 143 return false; 144 } 145 DevicePolicyManagerInternal devicePolicyManager = LocalServices.getService( 146 DevicePolicyManagerInternal.class); 147 mIntent = devicePolicyManager.createPackageSuspendedDialogIntent( 148 mAInfo.packageName, mUserId); 149 mCallingPid = mRealCallingPid; 150 mCallingUid = mRealCallingUid; 151 mResolvedType = null; 152 153 final UserInfo parent = mUserManager.getProfileParent(mUserId); 154 if (parent != null) { 155 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); 156 } else { 157 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId); 158 } 159 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 160 return true; 161 } 162 163 private boolean interceptWorkProfileChallengeIfNeeded() { 164 final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mIntent, 165 mResolvedType, mAInfo, mCallingPackage, mUserId); 166 if (interceptingIntent == null) { 167 return false; 168 } 169 mIntent = interceptingIntent; 170 mCallingPid = mRealCallingPid; 171 mCallingUid = mRealCallingUid; 172 mResolvedType = null; 173 // If we are intercepting and there was a task, convert it into an extra for the 174 // ConfirmCredentials intent and unassign it, as otherwise the task will move to 175 // front even if ConfirmCredentials is cancelled. 176 if (mInTask != null) { 177 mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId); 178 mInTask = null; 179 } 180 181 final UserInfo parent = mUserManager.getProfileParent(mUserId); 182 mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); 183 mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); 184 return true; 185 } 186 187 /** 188 * Creates an intent to intercept the current activity start with Confirm Credentials if needed. 189 * 190 * @return The intercepting intent if needed. 191 */ 192 private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType, 193 ActivityInfo aInfo, String callingPackage, int userId) { 194 if (!mService.mUserController.shouldConfirmCredentials(userId)) { 195 return null; 196 } 197 final IIntentSender target = mService.getIntentSenderLocked( 198 INTENT_SENDER_ACTIVITY, callingPackage, 199 Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent }, 200 new String[]{ resolvedType }, 201 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null); 202 final KeyguardManager km = (KeyguardManager) mService.mContext 203 .getSystemService(KEYGUARD_SERVICE); 204 final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId); 205 if (newIntent == null) { 206 return null; 207 } 208 newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 209 FLAG_ACTIVITY_TASK_ON_HOME); 210 newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName); 211 newIntent.putExtra(EXTRA_INTENT, new IntentSender(target)); 212 return newIntent; 213 } 214 215} 216