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.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
21 import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
22 import static android.app.PendingIntent.FLAG_IMMUTABLE;
23 import static android.app.PendingIntent.FLAG_ONE_SHOT;
24 import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
25 import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
26 import static android.content.Context.KEYGUARD_SERVICE;
27 import static android.content.Intent.EXTRA_INTENT;
28 import static android.content.Intent.EXTRA_PACKAGE_NAME;
29 import static android.content.Intent.EXTRA_TASK_ID;
30 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
31 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
32 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
33 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
34 
35 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
36 
37 import android.app.ActivityOptions;
38 import android.app.KeyguardManager;
39 import android.app.admin.DevicePolicyManagerInternal;
40 import android.content.Context;
41 import android.content.IIntentSender;
42 import android.content.Intent;
43 import android.content.IntentSender;
44 import android.content.pm.ActivityInfo;
45 import android.content.pm.PackageManagerInternal;
46 import android.content.pm.ResolveInfo;
47 import android.content.pm.UserInfo;
48 import android.os.Binder;
49 import android.os.Bundle;
50 import android.os.RemoteException;
51 import android.os.UserHandle;
52 import android.os.UserManager;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.internal.app.HarmfulAppWarningActivity;
56 import com.android.internal.app.SuspendedAppActivity;
57 import com.android.internal.app.UnlaunchableAppActivity;
58 import 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  */
67 class 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 
ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor)102     ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
103         this(service, supervisor, service.mContext, service.mUserController);
104     }
105 
106     @VisibleForTesting
ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor, Context context, UserController userController)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      */
setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, String callingPackage)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 
createIntentSenderForOriginalIntent(int callingUid, int flags)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      */
intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions)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      */
deferCrossProfileAppsAnimationIfNecessary()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 
interceptQuietProfileIfNeeded()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 
interceptSuspendedByAdminPackage()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 
interceptSuspendedPackageIfNeeded()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 
interceptWorkProfileChallengeIfNeeded()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      */
interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId)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 
interceptHarmfulAppIfNeeded()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