ActivityStartInterceptor.java revision 58d25998321d9f4b1b76d18a5af9d42cdbaad30d
1package com.android.server.am;
2
3import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
4import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
5import static android.app.PendingIntent.FLAG_IMMUTABLE;
6import static android.app.PendingIntent.FLAG_ONE_SHOT;
7import static android.content.Context.KEYGUARD_SERVICE;
8import static android.content.Intent.EXTRA_INTENT;
9import static android.content.Intent.EXTRA_PACKAGE_NAME;
10import static android.content.Intent.EXTRA_TASK_ID;
11import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
12import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
13import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
14
15import android.app.KeyguardManager;
16import android.content.IIntentSender;
17import android.content.Intent;
18import android.content.IntentSender;
19import android.content.pm.ActivityInfo;
20import android.content.pm.ResolveInfo;
21import android.content.pm.UserInfo;
22import android.os.Binder;
23import android.os.UserHandle;
24import android.os.UserManager;
25
26import com.android.internal.app.UnlaunchableAppActivity;
27
28/**
29 * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
30 * It's initialized
31 */
32class ActivityStartInterceptor {
33
34    private final ActivityManagerService mService;
35    private UserManager mUserManager;
36    private final ActivityStackSupervisor mSupervisor;
37
38    /*
39     * Per-intent states loaded from ActivityStarter than shouldn't be changed by any
40     * interception routines.
41     */
42    private int mRealCallingPid;
43    private int mRealCallingUid;
44    private int mUserId;
45    private int mStartFlags;
46    private String mCallingPackage;
47
48    /*
49     * Per-intent states that were load from ActivityStarter and are subject to modifications
50     * by the interception routines. After calling {@link #intercept} the caller should assign
51     * these values back to {@link ActivityStarter#startActivityLocked}'s local variables.
52     */
53    Intent mIntent;
54    int mCallingPid;
55    int mCallingUid;
56    ResolveInfo mRInfo;
57    ActivityInfo mAInfo;
58    String mResolvedType;
59    TaskRecord mInTask;
60
61    ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
62        mService = service;
63        mSupervisor = supervisor;
64    }
65
66    void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
67            String callingPackage) {
68        mRealCallingPid = realCallingPid;
69        mRealCallingUid = realCallingUid;
70        mUserId = userId;
71        mStartFlags = startFlags;
72        mCallingPackage = callingPackage;
73    }
74
75    void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
76            TaskRecord inTask, int callingPid, int callingUid) {
77        mUserManager = UserManager.get(mService.mContext);
78        mIntent = intent;
79        mCallingPid = callingPid;
80        mCallingUid = callingUid;
81        mRInfo = rInfo;
82        mAInfo = aInfo;
83        mResolvedType = resolvedType;
84        mInTask = inTask;
85        interceptQuietProfileIfNeeded();
86        interceptSuspendPackageIfNeed();
87        interceptWorkProfileChallengeIfNeeded();
88    }
89
90    private void interceptQuietProfileIfNeeded() {
91        // Do not intercept if the user has not turned off the profile
92        if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
93            return;
94        }
95        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId);
96        mCallingPid = mRealCallingPid;
97        mCallingUid = mRealCallingUid;
98        mResolvedType = null;
99
100        final UserInfo parent = mUserManager.getProfileParent(mUserId);
101        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
102        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
103                null /*profilerInfo*/);
104    }
105
106    private void interceptSuspendPackageIfNeed() {
107        // Do not intercept if the admin did not suspend the package
108        if (mAInfo == null || mAInfo.applicationInfo == null ||
109                (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
110            return;
111        }
112        mIntent = UnlaunchableAppActivity.createPackageSuspendedDialogIntent(mAInfo.packageName,
113                mUserId);
114        mCallingPid = mRealCallingPid;
115        mCallingUid = mRealCallingUid;
116        mResolvedType = null;
117
118        final UserInfo parent = mUserManager.getProfileParent(mUserId);
119        if (parent != null) {
120            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
121        } else {
122            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId);
123        }
124        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
125                null /*profilerInfo*/);
126    }
127
128    private void interceptWorkProfileChallengeIfNeeded() {
129        final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mIntent,
130                mResolvedType, mAInfo, mCallingPackage, mUserId);
131        if (interceptingIntent == null) {
132            return;
133        }
134        mIntent = interceptingIntent;
135        mCallingPid = mRealCallingPid;
136        mCallingUid = mRealCallingUid;
137        mResolvedType = null;
138        // If we are intercepting and there was a task, convert it into an extra for the
139        // ConfirmCredentials intent and unassign it, as otherwise the task will move to
140        // front even if ConfirmCredentials is cancelled.
141        if (mInTask != null) {
142            mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
143            mInTask = null;
144        }
145
146        final UserInfo parent = mUserManager.getProfileParent(mUserId);
147        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
148        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
149                null /*profilerInfo*/);
150    }
151
152    /**
153     * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
154     *
155     * @return The intercepting intent if needed.
156     */
157    private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType,
158            ActivityInfo aInfo, String callingPackage, int userId) {
159        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
160            return null;
161        }
162        final IIntentSender target = mService.getIntentSenderLocked(
163                INTENT_SENDER_ACTIVITY, callingPackage,
164                Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
165                new String[]{ resolvedType },
166                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
167        final int flags = intent.getFlags();
168        final KeyguardManager km = (KeyguardManager) mService.mContext
169                .getSystemService(KEYGUARD_SERVICE);
170        final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
171        if (newIntent == null) {
172            return null;
173        }
174        newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
175        newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
176        newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
177        return newIntent;
178    }
179
180}
181