Merge "Check all tasks for good intent launch when creating channels" into tm-dev

This commit is contained in:
Nate Myren
2022-03-07 21:27:48 +00:00
committed by Android (Google) Code Review
7 changed files with 121 additions and 42 deletions

View File

@@ -3746,6 +3746,10 @@ public class NotificationManagerService extends SystemService {
if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
&& startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
hasRequestedNotificationPermission = true;
if (mPermissionPolicyInternal == null) {
mPermissionPolicyInternal =
LocalServices.getService(PermissionPolicyInternal.class);
}
mHandler.post(new ShowNotificationPermissionPromptRunnable(pkg,
UserHandle.getUserId(uid), startingTaskId,
mPermissionPolicyInternal));
@@ -3764,19 +3768,7 @@ public class NotificationManagerService extends SystemService {
try {
int uid = mPackageManager.getPackageUid(pkg, 0,
UserHandle.getUserId(Binder.getCallingUid()));
List<ActivityManager.AppTask> tasks = mAtm.getAppTasks(pkg, uid);
for (int i = 0; i < tasks.size(); i++) {
ActivityManager.RecentTaskInfo task = tasks.get(i).getTaskInfo();
if (mPermissionPolicyInternal == null) {
mPermissionPolicyInternal =
LocalServices.getService(PermissionPolicyInternal.class);
}
if (mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.canShowPermissionPromptForTask(task)) {
taskId = task.taskId;
break;
}
}
taskId = mAtm.getTaskToShowPermissionDialogOn(pkg, uid);
} catch (RemoteException e) {
// Do nothing
}

View File

@@ -57,6 +57,7 @@ public abstract class PermissionPolicyInternal {
* prompt should be shown if the app targets S-, is currently running in a visible, focused
* task, has the REVIEW_REQUIRED flag set on its implicit notification permission, and has
* created at least one notification channel (even if it has since been deleted).
*
* @param packageName The package whose permission is being checked
* @param userId The user for whom the package is being started
* @param taskId The task the notification prompt should be attached to
@@ -66,10 +67,22 @@ public abstract class PermissionPolicyInternal {
/**
* Determine if a particular task is in the proper state to show a system-triggered permission
* prompt. A prompt can be shown if the task is focused, visible, and running.
* prompt. A prompt can be shown if the task is focused, visible, and running and
* 1. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
* 2. The activity belongs to the same package as the one which launched the task originally,
* and the task was started with a launcher intent
*
* @param taskInfo The task to be checked
* @param currPkg The package of the current top visible activity
* @param intent The intent of the current top visible activity
*/
public abstract boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo);
public abstract boolean shouldShowNotificationDialogForTask(@Nullable TaskInfo taskInfo,
@Nullable String currPkg, @Nullable Intent intent);
/**
* @return true if an intent will resolve to a permission request dialog activity
*/
public abstract boolean isIntentToPermissionDialog(@NonNull Intent intent);
/**
* @return Whether the policy is initialized for a user.

View File

@@ -21,6 +21,8 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
@@ -1016,7 +1018,7 @@ public final class PermissionPolicyService extends SystemService {
ActivityInterceptorInfo info) {
String action = info.intent.getAction();
ActivityInterceptResult result = null;
if (!PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
if (!ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)
&& !PackageManager.ACTION_REQUEST_PERMISSIONS.equals(action)) {
return null;
}
@@ -1033,7 +1035,7 @@ public final class PermissionPolicyService extends SystemService {
&& !mContinueNotifGrantMessageUids.contains(info.realCallingUid)) {
return result;
}
if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) {
String otherPkg = info.intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
if (otherPkg == null || (mPackageManager.getPermissionFlags(
POST_NOTIFICATIONS, otherPkg, UserHandle.of(info.userId))
@@ -1052,8 +1054,8 @@ public final class PermissionPolicyService extends SystemService {
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
ActivityInterceptorInfo info) {
super.onActivityLaunched(taskInfo, activityInfo, info);
if (!shouldShowNotificationDialogOrClearFlags(info.intent,
info.checkedOptions)) {
if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
activityInfo.packageName, info.intent, info.checkedOptions, true)) {
return;
}
UserHandle user = UserHandle.of(taskInfo.userId);
@@ -1085,7 +1087,7 @@ public final class PermissionPolicyService extends SystemService {
return false;
}
if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
if (ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction())
&& (callingUid != Process.SYSTEM_UID || !SYSTEM_PKG.equals(callingPackage))) {
return false;
}
@@ -1104,18 +1106,48 @@ public final class PermissionPolicyService extends SystemService {
launchNotificationPermissionRequestDialog(packageName, user, taskId);
}
@Override
public boolean isIntentToPermissionDialog(@NonNull Intent intent) {
return Objects.equals(intent.getPackage(),
mPackageManager.getPermissionControllerPackageName())
&& (Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS_FOR_OTHER)
|| Objects.equals(intent.getAction(), ACTION_REQUEST_PERMISSIONS));
}
@Override
public boolean shouldShowNotificationDialogForTask(TaskInfo taskInfo, String currPkg,
Intent intent) {
return shouldShowNotificationDialogOrClearFlags(
taskInfo, currPkg, intent, null, false);
}
/**
* Determine if we should show a notification dialog, or clear the REVIEW_REQUIRED flag,
* from a particular package for a particular intent. Returns true if:
* Determine if a particular task is in the proper state to show a system-triggered
* permission prompt. A prompt can be shown if the task is just starting, or the task is
* currently focused, visible, and running, and,
* 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
* 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER)
* 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or
* 3. The activity belongs to the same package as the one which launched the task
* originally, and the task was started with a launcher intent
* @param taskInfo The task to be checked
* @param currPkg The package of the current top visible activity
* @param intent The intent of the current top visible activity
*/
private boolean shouldShowNotificationDialogOrClearFlags(Intent intent,
ActivityOptions options) {
if ((options != null && options.isEligibleForLegacyPermissionPrompt())) {
return true;
private boolean shouldShowNotificationDialogOrClearFlags(TaskInfo taskInfo, String currPkg,
Intent intent, ActivityOptions options, boolean activityStart) {
if (intent == null || currPkg == null || taskInfo == null
|| (!(taskInfo.isFocused && taskInfo.isVisible && taskInfo.isRunning)
&& !activityStart)) {
return false;
}
return isLauncherIntent(intent)
|| (options != null && options.isEligibleForLegacyPermissionPrompt())
|| (currPkg.equals(taskInfo.baseActivity.getPackageName())
&& isLauncherIntent(taskInfo.baseIntent));
}
private boolean isLauncherIntent(Intent intent) {
return Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.getCategories() != null
&& (intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)
@@ -1144,7 +1176,7 @@ public final class PermissionPolicyService extends SystemService {
Intent grantPermission = mPackageManager
.buildRequestPermissionsIntent(new String[] { POST_NOTIFICATIONS });
grantPermission.setAction(
PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName);
ActivityOptions options = new ActivityOptions(new Bundle());
@@ -1170,12 +1202,6 @@ public final class PermissionPolicyService extends SystemService {
}
}
@Override
public boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo) {
return taskInfo != null && taskInfo.isFocused && taskInfo.isVisible
&& taskInfo.isRunning;
}
/**
* Check if the intent action is removed for the calling package (often based on target SDK
* version). If the action is removed, we'll silently cancel the activity launch.

View File

@@ -680,4 +680,15 @@ public abstract class ActivityTaskManagerInternal {
/** Get the app tasks for a package */
public abstract List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid);
/**
* Determine if there exists a task which meets the criteria set by the PermissionPolicyService
* to show a system-owned permission dialog over, for a given package
* @see PermissionPolicyInternal.shouldShowNotificationDialogForTask
*
* @param pkgName The package whose activity must be top
* @param uid The uid that must have a top activity
* @return a task ID if a valid task ID is found. Otherwise, return INVALID_TASK_ID
*/
public abstract int getTaskToShowPermissionDialogOn(String pkgName, int uid);
}

View File

@@ -6754,5 +6754,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
return tasks;
}
@Override
public int getTaskToShowPermissionDialogOn(String pkgName, int uid) {
synchronized (ActivityTaskManagerService.this.mGlobalLock) {
return ActivityTaskManagerService.this.mRootWindowContainer
.getTaskToShowPermissionDialogOn(pkgName, uid);
}
}
}
}

View File

@@ -149,6 +149,7 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.UserState;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import java.io.FileDescriptor;
@@ -3331,6 +3332,36 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mService.startLaunchPowerMode(reason);
}
/**
* Iterate over all task fragments, to see if there exists one that meets the
* PermissionPolicyService's criteria to show a permission dialog.
*/
public int getTaskToShowPermissionDialogOn(String pkgName, int uid) {
PermissionPolicyInternal pPi = mService.getPermissionPolicyInternal();
if (pPi == null) {
return INVALID_TASK_ID;
}
final int[] validTaskId = {INVALID_TASK_ID};
forAllLeafTaskFragments(fragment -> {
ActivityRecord record = fragment.getActivity((r) -> {
// skip hidden (or about to hide) apps, or the permission dialog
return r.canBeTopRunning() && r.isVisibleRequested()
&& !pPi.isIntentToPermissionDialog(r.intent);
});
if (record != null && record.isUid(uid)
&& Objects.equals(pkgName, record.packageName)
&& pPi.shouldShowNotificationDialogForTask(record.getTask().getTaskInfo(),
pkgName, record.intent)) {
validTaskId[0] = record.getTask().mTaskId;
return true;
}
return false;
});
return validTaskId[0];
}
/**
* Dumps the activities matching the given {@param name} in the either the focused root task
* or all visible root tasks if {@param dumpVisibleRootTasksOnly} is true.

View File

@@ -18,6 +18,7 @@ package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.Notification.FLAG_AUTO_CANCEL;
@@ -432,8 +433,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
when(mPermissionPolicyInternal.canShowPermissionPromptForTask(
any(ActivityManager.RecentTaskInfo.class))).thenReturn(false);
when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt()))
.thenReturn(INVALID_TASK_ID);
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
@@ -971,8 +972,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_FirstChannelWithFgndTaskStartsPermDialog()
throws Exception {
when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
ActivityManager.RecentTaskInfo.class))).thenReturn(true);
when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
final NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels(PKG_NO_CHANNELS,
@@ -985,8 +985,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testCreateNotificationChannels_SecondChannelWithFgndTaskDoesntStartPermDialog()
throws Exception {
when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
ActivityManager.RecentTaskInfo.class))).thenReturn(true);
when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
assertTrue(mBinderService.getNumNotificationChannelsForPackage(PKG, mUid, true) > 0);
final NotificationChannel channel =
@@ -1001,8 +1000,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testCreateNotificationChannels_FirstChannelWithBgndTaskDoesntStartPermDialog()
throws Exception {
reset(mPermissionPolicyInternal);
when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any(
ActivityManager.RecentTaskInfo.class))).thenReturn(false);
when(mAtm.getTaskToShowPermissionDialogOn(anyString(), anyInt())).thenReturn(TEST_TASK_ID);
final NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);