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