Merge "Intercepting activites that could cause lock task mode violation in ActivityStartInterceptor."

This commit is contained in:
Yuhan Zhao
2020-02-05 07:42:49 +00:00
committed by Android (Google) Code Review
11 changed files with 239 additions and 11 deletions

View File

@@ -7140,6 +7140,7 @@ package android.app.admin {
field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
field public static final int LEAVE_ALL_SYSTEM_APPS_ENABLED = 16; // 0x10
field public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 64; // 0x40
field public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 16; // 0x10
field public static final int LOCK_TASK_FEATURE_HOME = 4; // 0x4
field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20

View File

@@ -2089,6 +2089,13 @@ public class DevicePolicyManager {
*/
public static final int LOCK_TASK_FEATURE_KEYGUARD = 1 << 5;
/**
* Enable blocking of non-whitelisted activities from being started into a locked task.
*
* @see #setLockTaskFeatures(ComponentName, int)
*/
public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 1 << 6;
/**
* Flags supplied to {@link #setLockTaskFeatures(ComponentName, int)}.
*
@@ -2102,7 +2109,8 @@ public class DevicePolicyManager {
LOCK_TASK_FEATURE_HOME,
LOCK_TASK_FEATURE_OVERVIEW,
LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
LOCK_TASK_FEATURE_KEYGUARD
LOCK_TASK_FEATURE_KEYGUARD,
LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK
})
public @interface LockTaskFeature {}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2020 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 android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.R;
/**
* A dialog shown to the user when they try to launch an app that is not allowed in lock task
* mode. The intent to start this activity must be created with the static factory method provided
* below.
*/
public class BlockedAppActivity extends AlertActivity {
private static final String TAG = "BlockedAppActivity";
private static final String PACKAGE_NAME = "com.android.internal.app";
private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, /* defaultValue= */ -1);
if (userId < 0) {
Slog.wtf(TAG, "Invalid user: " + userId);
finish();
return;
}
String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
if (TextUtils.isEmpty(packageName)) {
Slog.wtf(TAG, "Invalid package: " + packageName);
finish();
return;
}
CharSequence appLabel = getAppLabel(userId, packageName);
mAlertParams.mTitle = getString(R.string.app_blocked_title);
mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
setupAlert();
}
private CharSequence getAppLabel(int userId, String packageName) {
PackageManager pm = getPackageManager();
try {
ApplicationInfo aInfo =
pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId);
return aInfo.loadLabel(pm);
} catch (PackageManager.NameNotFoundException ne) {
Slog.e(TAG, "Package " + packageName + " not found", ne);
}
return packageName;
}
/** Creates an intent that launches {@link BlockedAppActivity}. */
public static Intent createIntent(int userId, String packageName) {
return new Intent()
.setClassName("android", BlockedAppActivity.class.getName())
.putExtra(Intent.EXTRA_USER_ID, userId)
.putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
}
}

View File

@@ -5075,6 +5075,12 @@
android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.BlockedAppActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
android:process=":ui">
</activity>
<activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true">

View File

@@ -4936,6 +4936,13 @@
<!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] -->
<string name="work_mode_turn_on">Turn on</string>
<!-- Title of the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=50] -->
<string name="app_blocked_title">App is not available</string>
<!-- Default message shown in the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=NONE] -->
<string name="app_blocked_message">
<xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
</string>
<!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
<string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
<!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->

View File

@@ -3048,6 +3048,9 @@
<java-symbol type="string" name="app_suspended_unsuspend_message" />
<java-symbol type="string" name="app_suspended_default_message" />
<java-symbol type="string" name="app_blocked_title" />
<java-symbol type="string" name="app_blocked_message" />
<!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" />

View File

@@ -1629,12 +1629,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
requestedVrComponent = (aInfo.requestedVrComponent == null) ?
null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
if (info.applicationInfo.isPrivilegedApp()
&& (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
|| lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
}
lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options);
if (options != null) {
pendingOptions = options;
@@ -1642,15 +1637,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
}
final boolean useLockTask = pendingOptions.getLockTaskMode();
if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
}
// Gets launch display id from options. It returns INVALID_DISPLAY if not set.
mHandoverLaunchDisplayId = options.getLaunchDisplayId();
}
}
static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) {
int lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
if (aInfo.applicationInfo.isPrivilegedApp()
&& (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
|| lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
}
if (options != null) {
final boolean useLockTask = options.getLockTaskMode();
if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
}
}
return lockTaskLaunchMode;
}
@Override
ActivityRecord asActivityRecord() {
// I am an activity record!

View File

@@ -52,6 +52,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -166,6 +167,9 @@ class ActivityStartInterceptor {
// no user action can undo this.
return true;
}
if (interceptLockTaskModeViolationPackageIfNeeded()) {
return true;
}
if (interceptHarmfulAppIfNeeded()) {
// If the app has a "harmful app" warning associated with it, we should ask to uninstall
// before issuing the work challenge.
@@ -270,6 +274,25 @@ class ActivityStartInterceptor {
return true;
}
private boolean interceptLockTaskModeViolationPackageIfNeeded() {
if (mAInfo == null || mAInfo.applicationInfo == null) {
return false;
}
LockTaskController controller = mService.getLockTaskController();
String packageName = mAInfo.applicationInfo.packageName;
int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions);
if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) {
return false;
}
mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName);
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
}
private boolean interceptWorkProfileChallengeIfNeeded() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) {

View File

@@ -23,6 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.DEVICE_POLICY_SERVICE;
import static android.content.Context.STATUS_BAR_SERVICE;
import static android.content.Intent.ACTION_CALL_EMERGENCY;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_CURRENT;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -339,6 +341,25 @@ public class LockTaskController {
& DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
}
private boolean isBlockingInTaskEnabled(int userId) {
return (getLockTaskFeaturesForUser(userId)
& DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) != 0;
}
boolean isActivityAllowed(int userId, String packageName, int lockTaskLaunchMode) {
if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED || !isBlockingInTaskEnabled(userId)) {
return true;
}
switch (lockTaskLaunchMode) {
case LOCK_TASK_LAUNCH_MODE_ALWAYS:
return true;
case LOCK_TASK_LAUNCH_MODE_NEVER:
return false;
default:
}
return isPackageWhitelisted(userId, packageName);
}
private boolean isEmergencyCallTask(Task task) {
final Intent intent = task.intent;
if (intent == null) {

View File

@@ -16,6 +16,7 @@
package com.android.server.wm;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -44,6 +45,7 @@ import android.testing.DexmakerShareClassLoaderRule;
import androidx.test.filters.SmallTest;
import com.android.internal.app.BlockedAppActivity;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
@@ -105,6 +107,8 @@ public class ActivityStartInterceptorTest {
private PackageManagerService mPackageManager;
@Mock
private ActivityManagerInternal mAmInternal;
@Mock
private LockTaskController mLockTaskController;
private ActivityStartInterceptor mInterceptor;
private ActivityInfo mAInfo = new ActivityInfo();
@@ -145,6 +149,13 @@ public class ActivityStartInterceptorTest {
when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(null);
// Mock LockTaskController
mAInfo.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
when(mService.getLockTaskController()).thenReturn(mLockTaskController);
when(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
.thenReturn(true);
// Initialise activity info
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -195,6 +206,18 @@ public class ActivityStartInterceptorTest {
return dialogInfo;
}
@Test
public void testInterceptLockTaskModeViolationPackage() {
when(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
.thenReturn(false);
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
.filterEquals(mInterceptor.mIntent));
}
@Test
public void testInterceptQuietProfile() {
// GIVEN that the user the activity is starting as is currently in quiet mode

View File

@@ -25,10 +25,14 @@ import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
import static android.app.StatusBarManager.DISABLE_HOME;
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.os.Process.SYSTEM_UID;
import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -693,6 +697,45 @@ public class LockTaskControllerTest {
assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
}
@Test
public void testIsActivityAllowed() {
// WHEN lock task mode is not enabled
assertTrue(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
// Start lock task mode
Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
// WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled
assertTrue(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
// Enable LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK feature
mLockTaskController.updateLockTaskFeatures(
TEST_USER_ID, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK);
// package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed
assertTrue(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
// unwhitelisted package should not be allowed
assertFalse(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
// update the whitelist
String[] whitelist = new String[] { TEST_PACKAGE_NAME };
mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
// whitelisted package should be allowed
assertTrue(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
// package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed
assertFalse(mLockTaskController.isActivityAllowed(
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER));
}
private Task getTask(int lockTaskAuth) {
return getTask(TEST_PACKAGE_NAME, lockTaskAuth);
}