OSDN Git Service

Show dialog when launching work apps is not possible.
authorRubin Xu <rubinxu@google.com>
Thu, 21 Jan 2016 17:47:13 +0000 (17:47 +0000)
committerRubin Xu <rubinxu@google.com>
Fri, 22 Jan 2016 19:53:01 +0000 (19:53 +0000)
There are two cases when this could happen. First is when the work
profile is turned off by the user, and the second is when the profile
owner has suspened the package.

Bug: 22776761
Bug: 22541941
Change-Id: Ibc671e93f87dff0168bb7643494b9d6b45a1a7f9

core/java/com/android/internal/app/UnlaunchableAppActivity.java [new file with mode: 0644]
core/res/AndroidManifest.xml
core/res/res/layout/unlaunchable_app_activity.xml [new file with mode: 0644]
core/res/res/values/strings.xml
core/res/res/values/symbols.xml
services/core/java/com/android/server/am/ActivityStartInterceptor.java [new file with mode: 0644]
services/core/java/com/android/server/am/ActivityStarter.java

diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
new file mode 100644 (file)
index 0000000..aada6e3
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+/**
+ * A dialog shown to the user when they try to launch an app from a quiet profile
+ * ({@link UserManager#isQuietModeEnabled(UserHandle)}, or when the app is suspended by the
+ * profile owner or device owner.
+ */
+public class UnlaunchableAppActivity extends Activity
+        implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
+    private static final String TAG = "UnlaunchableAppActivity";
+
+    private static final int UNLAUNCHABLE_REASON_QUIET_MODE = 1;
+    private static final int UNLAUNCHABLE_REASON_SUSPENDED_PACKAGE = 2;
+    private static final String EXTRA_UNLAUNCHABLE_REASON = "unlaunchable_reason";
+
+    private int mUserId;
+    private int mReason;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = getIntent();
+        mReason = intent.getIntExtra(EXTRA_UNLAUNCHABLE_REASON, -1);
+        mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
+        if (mUserId == UserHandle.USER_NULL) {
+            Log.wtf(TAG, "Invalid user id: " + mUserId + ". Stopping.");
+            finish();
+            return;
+        }
+
+        String dialogTitle;
+        String dialogMessage;
+        if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) {
+            dialogTitle = getResources().getString(R.string.work_mode_off_title);
+            dialogMessage = getResources().getString(R.string.work_mode_off_message);
+        } else if (mReason == UNLAUNCHABLE_REASON_SUSPENDED_PACKAGE) {
+            PackageManager pm = getPackageManager();
+            DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
+                    Context.DEVICE_POLICY_SERVICE);
+            String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+            String packageLabel = packageName;
+            try {
+                Context userContext = createPackageContextAsUser(packageName, 0,
+                        UserHandle.of(mUserId));
+                ApplicationInfo appInfo = userContext.getApplicationInfo();
+                if (appInfo != null) {
+                    packageLabel = userContext.getPackageManager().getApplicationLabel(appInfo)
+                            .toString();
+                }
+            } catch (NameNotFoundException e) {
+            }
+            dialogTitle = String.format(getResources().getString(R.string.suspended_package_title),
+                    packageLabel);
+            dialogMessage = dpm.getShortSupportMessageForUser(dpm.getProfileOwnerAsUser(mUserId),
+                    mUserId);
+            if (dialogMessage == null) {
+                dialogMessage = String.format(
+                        getResources().getString(R.string.suspended_package_message),
+                        dpm.getProfileOwnerNameAsUser(mUserId));
+            }
+        } else {
+            Log.wtf(TAG, "Invalid unlaunchable type: " + mReason);
+            finish();
+            return;
+        }
+
+        View rootView = LayoutInflater.from(this).inflate(R.layout.unlaunchable_app_activity, null);
+        TextView titleView = (TextView)rootView.findViewById(R.id.unlaunchable_app_title);
+        TextView messageView = (TextView)rootView.findViewById(R.id.unlaunchable_app_message);
+        titleView.setText(dialogTitle);
+        messageView.setText(dialogMessage);
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(this)
+                .setView(rootView)
+                .setOnDismissListener(this);
+        if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE) {
+            builder.setPositiveButton(R.string.work_mode_turn_on, this)
+                    .setNegativeButton(R.string.cancel, null);
+        } else {
+            builder.setPositiveButton(R.string.ok, null);
+        }
+        builder.show();
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        finish();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
+            UserManager.get(this).setQuietModeEnabled(mUserId, false);
+        }
+    }
+
+    private static final Intent createBaseIntent() {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName("android", UnlaunchableAppActivity.class.getName()));
+        intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        return intent;
+    }
+
+    public static Intent createInQuietModeDialogIntent(int userId) {
+        Intent intent = createBaseIntent();
+        intent.putExtra(EXTRA_UNLAUNCHABLE_REASON, UNLAUNCHABLE_REASON_QUIET_MODE);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        return intent;
+    }
+
+    public static Intent createPackageSuspendedDialogIntent(String packageName, int userId) {
+        Intent intent = createBaseIntent();
+        intent.putExtra(EXTRA_UNLAUNCHABLE_REASON, UNLAUNCHABLE_REASON_SUSPENDED_PACKAGE);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+        return intent;
+    }
+}
index a82bfbe..4ce54fa 100644 (file)
             </intent-filter>
         </activity>
 
+        <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
+                android:theme="@style/Theme.Material.DayNight.Dialog.Alert"
+                android:excludeFromRecents="true"
+                android:process=":ui">
+        </activity>
+
         <receiver android:name="com.android.server.BootReceiver"
                 android:systemUserOnly="true">
             <intent-filter android:priority="1000">
diff --git a/core/res/res/layout/unlaunchable_app_activity.xml b/core/res/res/layout/unlaunchable_app_activity.xml
new file mode 100644 (file)
index 0000000..429d5ed
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="@dimen/dialog_padding"
+        android:orientation="vertical">
+        <TextView android:id="@+id/unlaunchable_app_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingStart="@dimen/dialog_padding"
+                android:paddingBottom="@dimen/dialog_padding"
+                android:textAppearance="@android:style/TextAppearance.Material.Title" />
+
+        <TextView android:id="@+id/unlaunchable_app_message"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingStart="@dimen/dialog_padding"
+                android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+                android:textColor="?android:attr/textColorSecondary" />
+</LinearLayout>
+
index eebdd61..1cb4a36 100644 (file)
     <!-- Menu item in the locale menu  [CHAR LIMIT=30] -->
     <string name="locale_search_menu">Search</string>
 
+    <!-- Title for dialog displayed when work profile is turned off. [CHAR LIMIT=30] -->
+    <string name="work_mode_off_title">Work mode is OFF</string>
+    <!-- Message displayed in dialog when work profile is turned off. [CHAR LIMIT=NONE] -->
+    <string name="work_mode_off_message">Allow work profile to function, including apps, background sync, and related features.</string>
+    <!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] -->
+    <string name="work_mode_turn_on">Turn on</string>
+    <!-- Title for dialog displayed when a packge is suspended by device admin. [CHAR LIMIT=30] -->
+    <string name="suspended_package_title">%1$s disabled</string>
+    <!-- Message for dialog displayed when a packge is suspended by device admin. [CHAR LIMIT=NONE] -->
+    <string name="suspended_package_message">Disabled by %1$s administrator. Contact them to learn more.</string>
 </resources>
index a5d0020..57ff243 100644 (file)
   <java-symbol type="string" name="language_selection_title" />
   <java-symbol type="string" name="search_language_hint" />
 
+  <java-symbol type="layout" name="unlaunchable_app_activity" />
+  <java-symbol type="id" name="unlaunchable_app_title" />
+  <java-symbol type="id" name="unlaunchable_app_message" />
+  <java-symbol type="string" name="work_mode_off_title" />
+  <java-symbol type="string" name="work_mode_off_message" />
+  <java-symbol type="string" name="work_mode_turn_on" />
+  <java-symbol type="string" name="suspended_package_title" />
+  <java-symbol type="string" name="suspended_package_message" />
 </resources>
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
new file mode 100644 (file)
index 0000000..1ed749f
--- /dev/null
@@ -0,0 +1,180 @@
+package com.android.server.am;
+
+import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.EXTRA_INTENT;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_TASK_ID;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
+import android.app.KeyguardManager;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.app.UnlaunchableAppActivity;
+
+/**
+ * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
+ * It's initialized
+ */
+class ActivityStartInterceptor {
+
+    private final ActivityManagerService mService;
+    private UserManager mUserManager;
+    private final ActivityStackSupervisor mSupervisor;
+
+    /*
+     * Per-intent states loaded from ActivityStarter than shouldn't be changed by any
+     * interception routines.
+     */
+    private int mRealCallingPid;
+    private int mRealCallingUid;
+    private int mUserId;
+    private int mStartFlags;
+    private String mCallingPackage;
+
+    /*
+     * Per-intent states that were load from ActivityStarter and are subject to modifications
+     * by the interception routines. After calling {@link #intercept} the caller should assign
+     * these values back to {@link ActivityStarter#startActivityLocked}'s local variables.
+     */
+    Intent mIntent;
+    int mCallingPid;
+    int mCallingUid;
+    ResolveInfo mRInfo;
+    ActivityInfo mAInfo;
+    String mResolvedType;
+    TaskRecord mInTask;
+
+    ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
+        mService = service;
+        mSupervisor = supervisor;
+    }
+
+    void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
+            String callingPackage) {
+        mRealCallingPid = realCallingPid;
+        mRealCallingUid = realCallingUid;
+        mUserId = userId;
+        mStartFlags = startFlags;
+        mCallingPackage = callingPackage;
+    }
+
+    void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
+            TaskRecord inTask, int callingPid, int callingUid) {
+        mUserManager = UserManager.get(mService.mContext);
+        mIntent = intent;
+        mCallingPid = callingPid;
+        mCallingUid = callingUid;
+        mRInfo = rInfo;
+        mAInfo = aInfo;
+        mResolvedType = resolvedType;
+        mInTask = inTask;
+        interceptQuietProfileIfNeeded();
+        interceptSuspendPackageIfNeed();
+        interceptWorkProfileChallengeIfNeeded();
+    }
+
+    private void interceptQuietProfileIfNeeded() {
+        // Do not intercept if the user has not turned off the profile
+        if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
+            return;
+        }
+        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+
+        final UserInfo parent = mUserManager.getProfileParent(mUserId);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
+                null /*profilerInfo*/);
+    }
+
+    private void interceptSuspendPackageIfNeed() {
+        // Do not intercept if the admin did not suspend the package
+        if (mAInfo == null || mAInfo.applicationInfo == null ||
+                (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
+            return;
+        }
+        mIntent = UnlaunchableAppActivity.createPackageSuspendedDialogIntent(mAInfo.packageName,
+                mUserId);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+
+        final UserInfo parent = mUserManager.getProfileParent(mUserId);
+        if (parent != null) {
+            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
+        } else {
+            mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId);
+        }
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
+                null /*profilerInfo*/);
+    }
+
+    private void interceptWorkProfileChallengeIfNeeded() {
+        final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mIntent,
+                mResolvedType, mAInfo, mCallingPackage, mUserId);
+        if (interceptingIntent == null) {
+            return;
+        }
+        mIntent = interceptingIntent;
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+        // If we are intercepting and there was a task, convert it into an extra for the
+        // ConfirmCredentials intent and unassign it, as otherwise the task will move to
+        // front even if ConfirmCredentials is cancelled.
+        if (mInTask != null) {
+            mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
+            mInTask = null;
+        }
+
+        final UserInfo parent = mUserManager.getProfileParent(mUserId);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
+                null /*profilerInfo*/);
+    }
+
+    /**
+     * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
+     *
+     * @return The intercepting intent if needed.
+     */
+    private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType,
+            ActivityInfo aInfo, String callingPackage, int userId) {
+        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+            return null;
+        }
+        final IIntentSender target = mService.getIntentSenderLocked(
+                INTENT_SENDER_ACTIVITY, callingPackage,
+                Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
+                new String[]{ resolvedType },
+                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
+        final int flags = intent.getFlags();
+        final KeyguardManager km = (KeyguardManager) mService.mContext
+                .getSystemService(KEYGUARD_SERVICE);
+        final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
+        if (newIntent == null) {
+            return null;
+        }
+        newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
+        newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
+        return newIntent;
+    }
+
+}
index d847824..23dc0f6 100644 (file)
@@ -1,11 +1,9 @@
 package com.android.server.am;
 
 import static android.app.Activity.RESULT_CANCELED;
-import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED;
-import static android.app.ActivityManager.START_PERMISSION_DENIED;
 import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
 import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
 import static android.app.ActivityManager.START_SUCCESS;
@@ -17,16 +15,8 @@ import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
-import static android.app.PendingIntent.FLAG_IMMUTABLE;
-import static android.app.PendingIntent.FLAG_ONE_SHOT;
-import static android.content.Context.KEYGUARD_SERVICE;
-import static android.content.Intent.EXTRA_INTENT;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
-import static android.content.Intent.EXTRA_TASK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
@@ -43,7 +33,6 @@ import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
-import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -76,7 +65,6 @@ import android.app.AppGlobals;
 import android.app.IActivityContainer;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
-import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.ProfilerInfo;
 import android.content.ComponentName;
@@ -86,7 +74,6 @@ import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -96,7 +83,6 @@ import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
 import android.util.Slog;
@@ -124,6 +110,7 @@ class ActivityStarter {
 
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
+    private ActivityStartInterceptor mInterceptor;
     private WindowManagerService mWindowManager;
 
     final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
@@ -204,6 +191,7 @@ class ActivityStarter {
     ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
         mService = service;
         mSupervisor = supervisor;
+        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
     }
 
     final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
@@ -230,17 +218,6 @@ class ActivityStarter {
             }
         }
 
-        if (aInfo != null) {
-            if ((aInfo.applicationInfo.flags & FLAG_SUSPENDED) != 0) {
-                Slog.w(TAG, "Application \"" + aInfo.applicationInfo.packageName
-                        + "\" is suspended. Refusing to start: " + intent.toString());
-                // TODO: show a dialog/activity informing the user that the application is suspended 
-                // and redirect the launch to it. Do not return START_PERMISSION_DENIED because 
-                // it is wrong.
-                err = ActivityManager.START_PERMISSION_DENIED;
-            }
-        }
-
         final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
 
         if (err == ActivityManager.START_SUCCESS) {
@@ -381,26 +358,15 @@ class ActivityStarter {
             }
         }
 
-        final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(intent,
-                resolvedType, aInfo, callingPackage, userId);
-        if (interceptingIntent != null) {
-            intent = interceptingIntent;
-            callingPid = realCallingPid;
-            callingUid = realCallingUid;
-            resolvedType = null;
-            // If we are intercepting and there was a task, convert it into an extra for the
-            // ConfirmCredentials intent and unassign it, as otherwise the task will move to
-            // front even if ConfirmCredentials is cancelled.
-            if (inTask != null) {
-                intent.putExtra(EXTRA_TASK_ID, inTask.taskId);
-                inTask = null;
-            }
-
-            final UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
-            rInfo = mSupervisor.resolveIntent(intent, resolvedType, parent.id);
-            aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
-                    null /*profilerInfo*/);
-        }
+        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
+        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid);
+        intent = mInterceptor.mIntent;
+        rInfo = mInterceptor.mRInfo;
+        aInfo = mInterceptor.mAInfo;
+        resolvedType = mInterceptor.mResolvedType;
+        inTask = mInterceptor.mInTask;
+        callingPid = mInterceptor.mCallingPid;
+        callingUid = mInterceptor.mCallingUid;
 
         if (abort) {
             if (resultRecord != null) {
@@ -552,34 +518,6 @@ class ActivityStarter {
         return err;
     }
 
-    /**
-     * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
-     *
-     * @return The intercepting intent if needed.
-     */
-    private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType,
-            ActivityInfo aInfo, String callingPackage, int userId) {
-        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
-            return null;
-        }
-        final IIntentSender target = mService.getIntentSenderLocked(
-                INTENT_SENDER_ACTIVITY, callingPackage,
-                Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
-                new String[]{ resolvedType },
-                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
-        final int flags = intent.getFlags();
-        final KeyguardManager km = (KeyguardManager) mService.mContext
-                .getSystemService(KEYGUARD_SERVICE);
-        final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
-        if (newIntent == null) {
-            return null;
-        }
-        newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
-        newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
-        return newIntent;
-    }
-
     void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
         mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
         startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,