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