Home | History | Annotate | Download | only in am
      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 
    102     ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
    103         this(service, supervisor, service.mContext, service.mUserController);
    104     }
    105 
    106     @VisibleForTesting
    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      */
    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 
    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      */
    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      */
    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 
    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 
    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 
    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 
    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      */
    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 
    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