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.wm;
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.annotation.Nullable;
38 import android.app.ActivityOptions;
39 import android.app.KeyguardManager;
40 import android.app.admin.DevicePolicyManagerInternal;
41 import android.content.Context;
42 import android.content.IIntentSender;
43 import android.content.Intent;
44 import android.content.IntentSender;
45 import android.content.pm.ActivityInfo;
46 import android.content.pm.PackageManagerInternal;
47 import android.content.pm.ResolveInfo;
48 import android.content.pm.SuspendDialogInfo;
49 import android.content.pm.UserInfo;
50 import android.os.Bundle;
51 import android.os.RemoteException;
52 import android.os.UserHandle;
53 import android.os.UserManager;
54 import android.util.SparseArray;
55 
56 import com.android.internal.annotations.VisibleForTesting;
57 import com.android.internal.app.BlockedAppActivity;
58 import com.android.internal.app.HarmfulAppWarningActivity;
59 import com.android.internal.app.SuspendedAppActivity;
60 import com.android.internal.app.UnlaunchableAppActivity;
61 import com.android.server.LocalServices;
62 import com.android.server.am.ActivityManagerService;
63 
64 /**
65  * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
66  * It's initialized via setStates and interception occurs via the intercept method.
67  *
68  * Note that this class is instantiated when {@link ActivityManagerService} gets created so there
69  * is no guarantee that other system services are already present.
70  */
71 class ActivityStartInterceptor {
72 
73     private final ActivityTaskManagerService mService;
74     private final ActivityTaskSupervisor mSupervisor;
75     private final RootWindowContainer mRootWindowContainer;
76     private final Context mServiceContext;
77 
78     // UserManager cannot be final as it's not ready when this class is instantiated during boot
79     private UserManager mUserManager;
80 
81     /*
82      * Per-intent states loaded from ActivityStarter than shouldn't be changed by any
83      * interception routines.
84      */
85     private int mRealCallingPid;
86     private int mRealCallingUid;
87     private int mUserId;
88     private int mStartFlags;
89     private String mCallingPackage;
90     private @Nullable String mCallingFeatureId;
91 
92     /*
93      * Per-intent states that were load from ActivityStarter and are subject to modifications
94      * by the interception routines. After calling {@link #intercept} the caller should assign
95      * these values back to {@link ActivityStarter#startActivityLocked}'s local variables if
96      * {@link #intercept} returns true.
97      */
98     Intent mIntent;
99     int mCallingPid;
100     int mCallingUid;
101     ResolveInfo mRInfo;
102     ActivityInfo mAInfo;
103     String mResolvedType;
104     Task mInTask;
105     ActivityOptions mActivityOptions;
106 
ActivityStartInterceptor( ActivityTaskManagerService service, ActivityTaskSupervisor supervisor)107     ActivityStartInterceptor(
108             ActivityTaskManagerService service, ActivityTaskSupervisor supervisor) {
109         this(service, supervisor, service.mRootWindowContainer, service.mContext);
110     }
111 
112     @VisibleForTesting
ActivityStartInterceptor(ActivityTaskManagerService service, ActivityTaskSupervisor supervisor, RootWindowContainer root, Context context)113     ActivityStartInterceptor(ActivityTaskManagerService service, ActivityTaskSupervisor supervisor,
114             RootWindowContainer root, Context context) {
115         mService = service;
116         mSupervisor = supervisor;
117         mRootWindowContainer = root;
118         mServiceContext = context;
119     }
120 
121     /**
122      * Effectively initialize the class before intercepting the start intent. The values set in this
123      * method should not be changed during intercept.
124      */
setStates(int userId, int realCallingPid, int realCallingUid, int startFlags, String callingPackage, @Nullable String callingFeatureId)125     void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
126             String callingPackage, @Nullable String callingFeatureId) {
127         mRealCallingPid = realCallingPid;
128         mRealCallingUid = realCallingUid;
129         mUserId = userId;
130         mStartFlags = startFlags;
131         mCallingPackage = callingPackage;
132         mCallingFeatureId = callingFeatureId;
133     }
134 
createIntentSenderForOriginalIntent(int callingUid, int flags)135     private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
136         Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
137         final IIntentSender target = mService.getIntentSenderLocked(
138                 INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingFeatureId, callingUid, mUserId,
139                 null /*token*/, null /*resultCode*/, 0 /*requestCode*/,
140                 new Intent[] { mIntent }, new String[] { mResolvedType },
141                 flags, activityOptions);
142         return new IntentSender(target);
143     }
144 
145     /**
146      * Intercept the launch intent based on various signals. If an interception happened the
147      * internal variables get assigned and need to be read explicitly by the caller.
148      *
149      * @return true if an interception occurred
150      */
intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, Task inTask, int callingPid, int callingUid, ActivityOptions activityOptions)151     boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
152             Task inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
153         mUserManager = UserManager.get(mServiceContext);
154 
155         mIntent = intent;
156         mCallingPid = callingPid;
157         mCallingUid = callingUid;
158         mRInfo = rInfo;
159         mAInfo = aInfo;
160         mResolvedType = resolvedType;
161         mInTask = inTask;
162         mActivityOptions = activityOptions;
163 
164         if (interceptQuietProfileIfNeeded()) {
165             // If work profile is turned off, skip the work challenge since the profile can only
166             // be unlocked when profile's user is running.
167             return true;
168         }
169         if (interceptSuspendedPackageIfNeeded()) {
170             // Skip the rest of interceptions as the package is suspended by device admin so
171             // no user action can undo this.
172             return true;
173         }
174         if (interceptLockTaskModeViolationPackageIfNeeded()) {
175             return true;
176         }
177         if (interceptHarmfulAppIfNeeded()) {
178             // If the app has a "harmful app" warning associated with it, we should ask to uninstall
179             // before issuing the work challenge.
180             return true;
181         }
182         if (interceptLockedManagedProfileIfNeeded()) {
183             return true;
184         }
185 
186         final SparseArray<ActivityInterceptorCallback> callbacks =
187                 mService.getActivityInterceptorCallbacks();
188         final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
189                 new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
190                         mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
191                         mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
192                         mActivityOptions);
193 
194         for (int i = 0; i < callbacks.size(); i++) {
195             final ActivityInterceptorCallback callback = callbacks.valueAt(i);
196             final Intent newIntent = callback.intercept(interceptorInfo);
197             if (newIntent == null) {
198                 continue;
199             }
200             mIntent = newIntent;
201             mCallingPid = mRealCallingPid;
202             mCallingUid = mRealCallingUid;
203             mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
204             mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
205                     null /*profilerInfo*/);
206             return true;
207         }
208         return false;
209     }
210 
hasCrossProfileAnimation()211     private boolean hasCrossProfileAnimation() {
212         return mActivityOptions != null
213                 && mActivityOptions.getAnimationType() == ANIM_OPEN_CROSS_PROFILE_APPS;
214     }
215 
216     /**
217      * If the activity option is the {@link ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} one,
218      * defer the animation until the original intent is started.
219      *
220      * @return the activity option used to start the original intent.
221      */
deferCrossProfileAppsAnimationIfNecessary()222     private Bundle deferCrossProfileAppsAnimationIfNecessary() {
223         if (hasCrossProfileAnimation()) {
224             mActivityOptions = null;
225             return ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
226         }
227         return null;
228     }
229 
interceptQuietProfileIfNeeded()230     private boolean interceptQuietProfileIfNeeded() {
231         // Do not intercept if the user has not turned off the profile
232         if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
233             return false;
234         }
235 
236         IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
237                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
238 
239         mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target);
240         mCallingPid = mRealCallingPid;
241         mCallingUid = mRealCallingUid;
242         mResolvedType = null;
243 
244         final UserInfo parent = mUserManager.getProfileParent(mUserId);
245         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
246         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
247         return true;
248     }
249 
interceptSuspendedByAdminPackage()250     private boolean interceptSuspendedByAdminPackage() {
251         DevicePolicyManagerInternal devicePolicyManager = LocalServices
252                 .getService(DevicePolicyManagerInternal.class);
253         if (devicePolicyManager == null) {
254             return false;
255         }
256         mIntent = devicePolicyManager.createShowAdminSupportIntent(mUserId, true);
257         mIntent.putExtra(EXTRA_RESTRICTION, POLICY_SUSPEND_PACKAGES);
258 
259         mCallingPid = mRealCallingPid;
260         mCallingUid = mRealCallingUid;
261         mResolvedType = null;
262 
263         final UserInfo parent = mUserManager.getProfileParent(mUserId);
264         if (parent != null) {
265             mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
266                     mRealCallingUid);
267         } else {
268             mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
269                     mRealCallingUid);
270         }
271         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
272         return true;
273     }
274 
interceptSuspendedPackageIfNeeded()275     private boolean interceptSuspendedPackageIfNeeded() {
276         // Do not intercept if the package is not suspended
277         if (mAInfo == null || mAInfo.applicationInfo == null ||
278                 (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
279             return false;
280         }
281         final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
282         if (pmi == null) {
283             return false;
284         }
285         final String suspendedPackage = mAInfo.applicationInfo.packageName;
286         final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId);
287         if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
288             return interceptSuspendedByAdminPackage();
289         }
290         final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage,
291                 suspendingPackage, mUserId);
292         final Bundle crossProfileOptions = hasCrossProfileAnimation()
293                 ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
294                 : null;
295         final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
296                 FLAG_IMMUTABLE);
297         mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
298                 suspendingPackage, dialogInfo, crossProfileOptions, target, mUserId);
299         mCallingPid = mRealCallingPid;
300         mCallingUid = mRealCallingUid;
301         mResolvedType = null;
302         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
303         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
304         return true;
305     }
306 
interceptLockTaskModeViolationPackageIfNeeded()307     private boolean interceptLockTaskModeViolationPackageIfNeeded() {
308         if (mAInfo == null || mAInfo.applicationInfo == null) {
309             return false;
310         }
311         LockTaskController controller = mService.getLockTaskController();
312         String packageName = mAInfo.applicationInfo.packageName;
313         int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions);
314         if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) {
315             return false;
316         }
317         mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName);
318         mCallingPid = mRealCallingPid;
319         mCallingUid = mRealCallingUid;
320         mResolvedType = null;
321         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
322         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
323         return true;
324     }
325 
interceptLockedManagedProfileIfNeeded()326     private boolean interceptLockedManagedProfileIfNeeded() {
327         final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
328         if (interceptingIntent == null) {
329             return false;
330         }
331         mIntent = interceptingIntent;
332         mCallingPid = mRealCallingPid;
333         mCallingUid = mRealCallingUid;
334         mResolvedType = null;
335         // If we are intercepting and there was a task, convert it into an extra for the
336         // ConfirmCredentials intent and unassign it, as otherwise the task will move to
337         // front even if ConfirmCredentials is cancelled.
338         if (mInTask != null) {
339             mIntent.putExtra(EXTRA_TASK_ID, mInTask.mTaskId);
340             mInTask = null;
341         }
342         if (mActivityOptions == null) {
343             mActivityOptions = ActivityOptions.makeBasic();
344         }
345 
346         final UserInfo parent = mUserManager.getProfileParent(mUserId);
347         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
348         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
349         return true;
350     }
351 
352     /**
353      * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
354      *
355      * @return The intercepting intent if needed.
356      */
interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId)357     private Intent interceptWithConfirmCredentialsIfNeeded(ActivityInfo aInfo, int userId) {
358         if (!mService.mAmInternal.shouldConfirmCredentials(userId)) {
359             return null;
360         }
361         // TODO(b/28935539): should allow certain activities to bypass work challenge
362         final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
363                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
364         final KeyguardManager km = (KeyguardManager) mServiceContext
365                 .getSystemService(KEYGUARD_SERVICE);
366         final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId,
367                 true /* disallowBiometricsIfPolicyExists */);
368         if (newIntent == null) {
369             return null;
370         }
371         newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
372                 FLAG_ACTIVITY_TASK_ON_HOME);
373         newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
374         newIntent.putExtra(EXTRA_INTENT, target);
375         return newIntent;
376     }
377 
interceptHarmfulAppIfNeeded()378     private boolean interceptHarmfulAppIfNeeded() {
379         CharSequence harmfulAppWarning;
380         try {
381             harmfulAppWarning = mService.getPackageManager()
382                     .getHarmfulAppWarning(mAInfo.packageName, mUserId);
383         } catch (RemoteException ex) {
384             return false;
385         }
386 
387         if (harmfulAppWarning == null) {
388             return false;
389         }
390 
391         final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
392                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
393 
394         mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext,
395                 mAInfo.packageName, target, harmfulAppWarning);
396 
397         mCallingPid = mRealCallingPid;
398         mCallingUid = mRealCallingUid;
399         mResolvedType = null;
400 
401         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
402         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
403         return true;
404     }
405 }
406