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