Kenny Guy | b1b3026 | 2016-02-09 16:02:35 +0000 | [diff] [blame] | 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 | |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 17 | package com.android.server.am; |
| 18 | |
| 19 | import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; |
| 20 | import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; |
| 21 | import static android.app.PendingIntent.FLAG_IMMUTABLE; |
| 22 | import static android.app.PendingIntent.FLAG_ONE_SHOT; |
| 23 | import static android.content.Context.KEYGUARD_SERVICE; |
| 24 | import static android.content.Intent.EXTRA_INTENT; |
| 25 | import static android.content.Intent.EXTRA_PACKAGE_NAME; |
| 26 | import static android.content.Intent.EXTRA_TASK_ID; |
| 27 | import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; |
| 28 | import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; |
Tony Mak | 4291c76 | 2016-03-24 12:23:22 +0000 | [diff] [blame] | 29 | import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 30 | import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; |
| 31 | |
Tony Mak | 853304c | 2016-04-18 15:17:41 +0100 | [diff] [blame] | 32 | import android.app.ActivityOptions; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 33 | import android.app.KeyguardManager; |
Sudheer Shanka | 7a9c34b | 2016-03-11 12:25:51 -0800 | [diff] [blame] | 34 | import android.app.admin.DevicePolicyManagerInternal; |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 35 | import android.content.Context; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 36 | import android.content.IIntentSender; |
| 37 | import android.content.Intent; |
| 38 | import android.content.IntentSender; |
| 39 | import android.content.pm.ActivityInfo; |
| 40 | import android.content.pm.ResolveInfo; |
| 41 | import android.content.pm.UserInfo; |
| 42 | import android.os.Binder; |
| 43 | import android.os.UserHandle; |
| 44 | import android.os.UserManager; |
| 45 | |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 46 | import com.android.internal.annotations.VisibleForTesting; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 47 | import com.android.internal.app.UnlaunchableAppActivity; |
Sudheer Shanka | 7a9c34b | 2016-03-11 12:25:51 -0800 | [diff] [blame] | 48 | import com.android.server.LocalServices; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 49 | |
| 50 | /** |
| 51 | * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked} |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 52 | * It's initialized via setStates and interception occurs via the intercept method. |
| 53 | * |
| 54 | * Note that this class is instantiated when {@link ActivityManagerService} gets created so there |
| 55 | * is no guarantee that other system services are already present. |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 56 | */ |
| 57 | class ActivityStartInterceptor { |
| 58 | |
| 59 | private final ActivityManagerService mService; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 60 | private final ActivityStackSupervisor mSupervisor; |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 61 | private final Context mServiceContext; |
| 62 | private final UserController mUserController; |
| 63 | |
| 64 | // UserManager cannot be final as it's not ready when this class is instantiated during boot |
| 65 | private UserManager mUserManager; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 66 | |
| 67 | /* |
| 68 | * Per-intent states loaded from ActivityStarter than shouldn't be changed by any |
| 69 | * interception routines. |
| 70 | */ |
| 71 | private int mRealCallingPid; |
| 72 | private int mRealCallingUid; |
| 73 | private int mUserId; |
| 74 | private int mStartFlags; |
| 75 | private String mCallingPackage; |
| 76 | |
| 77 | /* |
| 78 | * Per-intent states that were load from ActivityStarter and are subject to modifications |
| 79 | * by the interception routines. After calling {@link #intercept} the caller should assign |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 80 | * these values back to {@link ActivityStarter#startActivityLocked}'s local variables if |
| 81 | * {@link #intercept} returns true. |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 82 | */ |
| 83 | Intent mIntent; |
| 84 | int mCallingPid; |
| 85 | int mCallingUid; |
| 86 | ResolveInfo mRInfo; |
| 87 | ActivityInfo mAInfo; |
| 88 | String mResolvedType; |
| 89 | TaskRecord mInTask; |
Tony Mak | 853304c | 2016-04-18 15:17:41 +0100 | [diff] [blame] | 90 | ActivityOptions mActivityOptions; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 91 | |
| 92 | ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) { |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 93 | this(service, supervisor, service.mContext, service.mUserController); |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 94 | } |
| 95 | |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 96 | @VisibleForTesting |
| 97 | ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor, |
| 98 | Context context, UserController userController) { |
| 99 | mService = service; |
| 100 | mSupervisor = supervisor; |
| 101 | mServiceContext = context; |
| 102 | mUserController = userController; |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * Effectively initialize the class before intercepting the start intent. The values set in this |
| 107 | * method should not be changed during intercept. |
| 108 | */ |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 109 | void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, |
| 110 | String callingPackage) { |
| 111 | mRealCallingPid = realCallingPid; |
| 112 | mRealCallingUid = realCallingUid; |
| 113 | mUserId = userId; |
| 114 | mStartFlags = startFlags; |
| 115 | mCallingPackage = callingPackage; |
| 116 | } |
| 117 | |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 118 | /** |
| 119 | * Intercept the launch intent based on various signals. If an interception happened the |
| 120 | * internal variables get assigned and need to be read explicitly by the caller. |
| 121 | * |
| 122 | * @return true if an interception occurred |
| 123 | */ |
| 124 | boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, |
Tony Mak | 853304c | 2016-04-18 15:17:41 +0100 | [diff] [blame] | 125 | TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) { |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 126 | mUserManager = UserManager.get(mServiceContext); |
| 127 | |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 128 | mIntent = intent; |
| 129 | mCallingPid = callingPid; |
| 130 | mCallingUid = callingUid; |
| 131 | mRInfo = rInfo; |
| 132 | mAInfo = aInfo; |
| 133 | mResolvedType = resolvedType; |
| 134 | mInTask = inTask; |
Tony Mak | 853304c | 2016-04-18 15:17:41 +0100 | [diff] [blame] | 135 | mActivityOptions = activityOptions; |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 136 | |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 137 | if (interceptSuspendPackageIfNeed()) { |
| 138 | // Skip the rest of interceptions as the package is suspended by device admin so |
| 139 | // no user action can undo this. |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 140 | return true; |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 141 | } |
| 142 | if (interceptQuietProfileIfNeeded()) { |
| 143 | // If work profile is turned off, skip the work challenge since the profile can only |
| 144 | // be unlocked when profile's user is running. |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 145 | return true; |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 146 | } |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 147 | return interceptWorkProfileChallengeIfNeeded(); |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 148 | } |
| 149 | |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 150 | private boolean interceptQuietProfileIfNeeded() { |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 151 | // Do not intercept if the user has not turned off the profile |
| 152 | if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) { |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 153 | return false; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 154 | } |
Rubin Xu | e420c55 | 2016-04-06 19:04:30 +0100 | [diff] [blame] | 155 | IIntentSender target = mService.getIntentSenderLocked( |
| 156 | INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0, |
| 157 | new Intent[] {mIntent}, new String[] {mResolvedType}, |
| 158 | FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null); |
| 159 | |
| 160 | mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, |
| 161 | new IntentSender(target)); |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 162 | mCallingPid = mRealCallingPid; |
| 163 | mCallingUid = mRealCallingUid; |
| 164 | mResolvedType = null; |
| 165 | |
| 166 | final UserInfo parent = mUserManager.getProfileParent(mUserId); |
| 167 | mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 168 | mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); |
| 169 | return true; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 170 | } |
| 171 | |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 172 | private boolean interceptSuspendPackageIfNeed() { |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 173 | // Do not intercept if the admin did not suspend the package |
| 174 | if (mAInfo == null || mAInfo.applicationInfo == null || |
| 175 | (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) { |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 176 | return false; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 177 | } |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 178 | DevicePolicyManagerInternal devicePolicyManager = LocalServices |
| 179 | .getService(DevicePolicyManagerInternal.class); |
Makoto Onuki | 2670495 | 2016-06-13 14:50:11 -0700 | [diff] [blame] | 180 | if (devicePolicyManager == null) { |
| 181 | return false; |
| 182 | } |
Nicolas Prevot | 709a63d | 2016-06-09 13:14:00 +0100 | [diff] [blame] | 183 | mIntent = devicePolicyManager.createShowAdminSupportIntent(mUserId, true); |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 184 | mCallingPid = mRealCallingPid; |
| 185 | mCallingUid = mRealCallingUid; |
| 186 | mResolvedType = null; |
| 187 | |
| 188 | final UserInfo parent = mUserManager.getProfileParent(mUserId); |
| 189 | if (parent != null) { |
| 190 | mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); |
| 191 | } else { |
| 192 | mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId); |
| 193 | } |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 194 | mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); |
| 195 | return true; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 196 | } |
| 197 | |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 198 | private boolean interceptWorkProfileChallengeIfNeeded() { |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 199 | final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mIntent, |
| 200 | mResolvedType, mAInfo, mCallingPackage, mUserId); |
| 201 | if (interceptingIntent == null) { |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 202 | return false; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 203 | } |
| 204 | mIntent = interceptingIntent; |
| 205 | mCallingPid = mRealCallingPid; |
| 206 | mCallingUid = mRealCallingUid; |
| 207 | mResolvedType = null; |
| 208 | // If we are intercepting and there was a task, convert it into an extra for the |
| 209 | // ConfirmCredentials intent and unassign it, as otherwise the task will move to |
| 210 | // front even if ConfirmCredentials is cancelled. |
| 211 | if (mInTask != null) { |
| 212 | mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId); |
| 213 | mInTask = null; |
| 214 | } |
Tony Mak | 853304c | 2016-04-18 15:17:41 +0100 | [diff] [blame] | 215 | if (mActivityOptions == null) { |
| 216 | mActivityOptions = ActivityOptions.makeBasic(); |
| 217 | } |
Victor Chang | 89d4a9a | 2016-06-14 13:49:32 +0100 | [diff] [blame] | 218 | |
| 219 | ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity(); |
Bryce Lee | af691c0 | 2017-03-20 14:20:22 -0700 | [diff] [blame] | 220 | if (homeActivityRecord != null && homeActivityRecord.getTask() != null) { |
Victor Chang | 89d4a9a | 2016-06-14 13:49:32 +0100 | [diff] [blame] | 221 | // Showing credential confirmation activity in home task to avoid stopping multi-windowed |
| 222 | // mode after showing the full-screen credential confirmation activity. |
Bryce Lee | af691c0 | 2017-03-20 14:20:22 -0700 | [diff] [blame] | 223 | mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId); |
Victor Chang | 89d4a9a | 2016-06-14 13:49:32 +0100 | [diff] [blame] | 224 | } |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 225 | |
| 226 | final UserInfo parent = mUserManager.getProfileParent(mUserId); |
| 227 | mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id); |
Rubin Xu | b93522a | 2016-02-23 18:21:48 +0000 | [diff] [blame] | 228 | mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); |
| 229 | return true; |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | /** |
| 233 | * Creates an intent to intercept the current activity start with Confirm Credentials if needed. |
| 234 | * |
| 235 | * @return The intercepting intent if needed. |
| 236 | */ |
| 237 | private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType, |
| 238 | ActivityInfo aInfo, String callingPackage, int userId) { |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 239 | if (!mUserController.shouldConfirmCredentials(userId)) { |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 240 | return null; |
| 241 | } |
Charles He | e7c5ced | 2017-04-12 16:22:35 +0100 | [diff] [blame] | 242 | // TODO(b/28935539): should allow certain activities to bypass work challenge |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 243 | final IIntentSender target = mService.getIntentSenderLocked( |
| 244 | INTENT_SENDER_ACTIVITY, callingPackage, |
| 245 | Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent }, |
| 246 | new String[]{ resolvedType }, |
| 247 | FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null); |
Benjamin Franz | 563707b | 2017-06-29 15:06:13 +0100 | [diff] [blame] | 248 | final KeyguardManager km = (KeyguardManager) mServiceContext |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 249 | .getSystemService(KEYGUARD_SERVICE); |
| 250 | final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId); |
| 251 | if (newIntent == null) { |
| 252 | return null; |
| 253 | } |
Tony Mak | 4291c76 | 2016-03-24 12:23:22 +0000 | [diff] [blame] | 254 | newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | |
| 255 | FLAG_ACTIVITY_TASK_ON_HOME); |
Rubin Xu | 58d2599 | 2016-01-21 17:47:13 +0000 | [diff] [blame] | 256 | newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName); |
| 257 | newIntent.putExtra(EXTRA_INTENT, new IntentSender(target)); |
| 258 | return newIntent; |
| 259 | } |
| 260 | |
| 261 | } |