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.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;
29 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
30 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
31 
32 import android.app.ActivityOptions;
33 import android.app.KeyguardManager;
34 import android.app.admin.DevicePolicyManagerInternal;
35 import android.content.IIntentSender;
36 import android.content.Intent;
37 import android.content.IntentSender;
38 import android.content.pm.ActivityInfo;
39 import android.content.pm.ResolveInfo;
40 import android.content.pm.UserInfo;
41 import android.os.Binder;
42 import android.os.UserHandle;
43 import android.os.UserManager;
44 
45 import com.android.internal.app.UnlaunchableAppActivity;
46 import com.android.server.LocalServices;
47 
48 /**
49  * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
50  * It's initialized
51  */
52 class ActivityStartInterceptor {
53 
54     private final ActivityManagerService mService;
55     private UserManager mUserManager;
56     private final ActivityStackSupervisor mSupervisor;
57 
58     /*
59      * Per-intent states loaded from ActivityStarter than shouldn't be changed by any
60      * interception routines.
61      */
62     private int mRealCallingPid;
63     private int mRealCallingUid;
64     private int mUserId;
65     private int mStartFlags;
66     private String mCallingPackage;
67 
68     /*
69      * Per-intent states that were load from ActivityStarter and are subject to modifications
70      * by the interception routines. After calling {@link #intercept} the caller should assign
71      * these values back to {@link ActivityStarter#startActivityLocked}'s local variables.
72      */
73     Intent mIntent;
74     int mCallingPid;
75     int mCallingUid;
76     ResolveInfo mRInfo;
77     ActivityInfo mAInfo;
78     String mResolvedType;
79     TaskRecord mInTask;
80     ActivityOptions mActivityOptions;
81 
ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor)82     ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
83         mService = service;
84         mSupervisor = supervisor;
85     }
86 
setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, String callingPackage)87     void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
88             String callingPackage) {
89         mRealCallingPid = realCallingPid;
90         mRealCallingUid = realCallingUid;
91         mUserId = userId;
92         mStartFlags = startFlags;
93         mCallingPackage = callingPackage;
94     }
95 
intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions)96     void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
97             TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
98         mUserManager = UserManager.get(mService.mContext);
99         mIntent = intent;
100         mCallingPid = callingPid;
101         mCallingUid = callingUid;
102         mRInfo = rInfo;
103         mAInfo = aInfo;
104         mResolvedType = resolvedType;
105         mInTask = inTask;
106         mActivityOptions = activityOptions;
107         if (interceptSuspendPackageIfNeed()) {
108             // Skip the rest of interceptions as the package is suspended by device admin so
109             // no user action can undo this.
110             return;
111         }
112         if (interceptQuietProfileIfNeeded()) {
113             // If work profile is turned off, skip the work challenge since the profile can only
114             // be unlocked when profile's user is running.
115             return;
116         }
117         interceptWorkProfileChallengeIfNeeded();
118     }
119 
interceptQuietProfileIfNeeded()120     private boolean interceptQuietProfileIfNeeded() {
121         // Do not intercept if the user has not turned off the profile
122         if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
123             return false;
124         }
125         IIntentSender target = mService.getIntentSenderLocked(
126                 INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0,
127                 new Intent[] {mIntent}, new String[] {mResolvedType},
128                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null);
129 
130         mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId,
131                 new IntentSender(target));
132         mCallingPid = mRealCallingPid;
133         mCallingUid = mRealCallingUid;
134         mResolvedType = null;
135 
136         final UserInfo parent = mUserManager.getProfileParent(mUserId);
137         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
138         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
139         return true;
140     }
141 
interceptSuspendPackageIfNeed()142     private boolean interceptSuspendPackageIfNeed() {
143         // Do not intercept if the admin did not suspend the package
144         if (mAInfo == null || mAInfo.applicationInfo == null ||
145                 (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
146             return false;
147         }
148         DevicePolicyManagerInternal devicePolicyManager = LocalServices.getService(
149                 DevicePolicyManagerInternal.class);
150         if (devicePolicyManager == null) {
151             return false;
152         }
153         mIntent = devicePolicyManager.createPackageSuspendedDialogIntent(
154                 mAInfo.packageName, mUserId);
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         }
165         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
166         return true;
167     }
168 
interceptWorkProfileChallengeIfNeeded()169     private boolean interceptWorkProfileChallengeIfNeeded() {
170         final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mIntent,
171                 mResolvedType, mAInfo, mCallingPackage, mUserId);
172         if (interceptingIntent == null) {
173             return false;
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         }
186         if (mActivityOptions == null) {
187             mActivityOptions = ActivityOptions.makeBasic();
188         }
189 
190         ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity();
191         if (homeActivityRecord != null && homeActivityRecord.task != null) {
192             // Showing credential confirmation activity in home task to avoid stopping multi-windowed
193             // mode after showing the full-screen credential confirmation activity.
194             mActivityOptions.setLaunchTaskId(homeActivityRecord.task.taskId);
195         }
196 
197         final UserInfo parent = mUserManager.getProfileParent(mUserId);
198         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
199         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
200         return true;
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      */
interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType, ActivityInfo aInfo, String callingPackage, int userId)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         }
213         final IIntentSender target = mService.getIntentSenderLocked(
214                 INTENT_SENDER_ACTIVITY, callingPackage,
215                 Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
216                 new String[]{ resolvedType },
217                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
218         final KeyguardManager km = (KeyguardManager) mService.mContext
219                 .getSystemService(KEYGUARD_SERVICE);
220         final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
221         if (newIntent == null) {
222             return null;
223         }
224         newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
225                 FLAG_ACTIVITY_TASK_ON_HOME);
226         newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
227         newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
228         return newIntent;
229     }
230 
231 }
232