Merge "Show Notification Permission dialog on (some) task trampolines" into tm-dev am: 7585a9580d am: 4e084c4879

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17776866

Change-Id: I9bc8c52556f34449aa58001ce0e90eee3a271dbb
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Nate Myren
2022-04-29 18:21:36 +00:00
committed by Automerger Merge Worker
4 changed files with 70 additions and 20 deletions

View File

@@ -74,10 +74,13 @@ public abstract class PermissionPolicyInternal {
* *
* @param taskInfo The task to be checked * @param taskInfo The task to be checked
* @param currPkg The package of the current top visible activity * @param currPkg The package of the current top visible activity
* @param callingPkg The package that started the top visible activity
* @param intent The intent of the current top visible activity * @param intent The intent of the current top visible activity
* @param activityName The name of the current top visible activity
*/ */
public abstract boolean shouldShowNotificationDialogForTask(@Nullable TaskInfo taskInfo, public abstract boolean shouldShowNotificationDialogForTask(@Nullable TaskInfo taskInfo,
@Nullable String currPkg, @Nullable Intent intent); @Nullable String currPkg, @Nullable String callingPkg, @Nullable Intent intent,
@NonNull String activityName);
/** /**
* @return true if an intent will resolve to a permission request dialog activity * @return true if an intent will resolve to a permission request dialog activity

View File

@@ -35,6 +35,7 @@ import android.Manifest;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.app.AppOpsManager; import android.app.AppOpsManager;
@@ -1166,7 +1167,8 @@ public final class PermissionPolicyService extends SystemService {
ActivityInterceptorInfo info) { ActivityInterceptorInfo info) {
super.onActivityLaunched(taskInfo, activityInfo, info); super.onActivityLaunched(taskInfo, activityInfo, info);
if (!shouldShowNotificationDialogOrClearFlags(taskInfo, if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
activityInfo.packageName, info.intent, info.checkedOptions, true) activityInfo.packageName, info.callingPackage, info.intent,
info.checkedOptions, activityInfo.name, true)
|| isNoDisplayActivity(activityInfo)) { || isNoDisplayActivity(activityInfo)) {
return; return;
} }
@@ -1237,9 +1239,9 @@ public final class PermissionPolicyService extends SystemService {
@Override @Override
public boolean shouldShowNotificationDialogForTask(TaskInfo taskInfo, String currPkg, public boolean shouldShowNotificationDialogForTask(TaskInfo taskInfo, String currPkg,
Intent intent) { String callingPkg, Intent intent, String activityName) {
return shouldShowNotificationDialogOrClearFlags( return shouldShowNotificationDialogOrClearFlags(taskInfo, currPkg, callingPkg, intent,
taskInfo, currPkg, intent, null, false); null, activityName, false);
} }
private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo) { private boolean isNoDisplayActivity(@NonNull ActivityInfo aInfo) {
@@ -1265,23 +1267,61 @@ public final class PermissionPolicyService extends SystemService {
* 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or * 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
* 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER), or * 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 * 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 * originally, and the task was started with a launcher intent, or
* 4. The activity is the first activity in a new task, and was started by the app the
* activity belongs to, and that app has another task that is currently focused, which was
* started with a launcher intent. This case seeks to identify cases where an app launches,
* then immediately trampolines to a new activity and task.
* @param taskInfo The task to be checked * @param taskInfo The task to be checked
* @param currPkg The package of the current top visible activity * @param currPkg The package of the current top visible activity
* @param callingPkg The package that initiated this dialog action
* @param intent The intent of the current top visible activity * @param intent The intent of the current top visible activity
* @param options The ActivityOptions of the newly started activity, if this is called due
* to an activity start
* @param startedActivity The ActivityInfo of the newly started activity, if this is called
* due to an activity start
*/ */
private boolean shouldShowNotificationDialogOrClearFlags(TaskInfo taskInfo, String currPkg, private boolean shouldShowNotificationDialogOrClearFlags(TaskInfo taskInfo, String currPkg,
Intent intent, ActivityOptions options, boolean activityStart) { String callingPkg, Intent intent, ActivityOptions options,
if (intent == null || currPkg == null || taskInfo == null String topActivityName, boolean startedActivity) {
if (intent == null || currPkg == null || taskInfo == null || topActivityName == null
|| (!(taskInfo.isFocused && taskInfo.isVisible && taskInfo.isRunning) || (!(taskInfo.isFocused && taskInfo.isVisible && taskInfo.isRunning)
&& !activityStart)) { && !startedActivity)) {
return false; return false;
} }
return isLauncherIntent(intent) return isLauncherIntent(intent)
|| (options != null && options.isEligibleForLegacyPermissionPrompt()) || (options != null && options.isEligibleForLegacyPermissionPrompt())
|| (currPkg.equals(taskInfo.baseActivity.getPackageName()) || isTaskStartedFromLauncher(currPkg, taskInfo)
&& isLauncherIntent(taskInfo.baseIntent)); || (isTaskPotentialTrampoline(topActivityName, currPkg, callingPkg, taskInfo,
intent)
&& (!startedActivity || pkgHasRunningLauncherTask(currPkg, taskInfo)));
}
private boolean isTaskPotentialTrampoline(String activityName, String currPkg,
String callingPkg, TaskInfo taskInfo, Intent intent) {
return currPkg.equals(callingPkg) && taskInfo.baseIntent.filterEquals(intent)
&& taskInfo.numActivities == 1
&& activityName.equals(taskInfo.topActivityInfo.name);
}
private boolean pkgHasRunningLauncherTask(String currPkg, TaskInfo taskInfo) {
ActivityTaskManagerInternal m =
LocalServices.getService(ActivityTaskManagerInternal.class);
try {
// TODO(b/230616478) Investigate alternatives like ActivityMetricsLaunchObserver
List<ActivityManager.AppTask> tasks =
m.getAppTasks(currPkg, mPackageManager.getPackageUid(currPkg, 0));
for (int i = 0; i < tasks.size(); i++) {
TaskInfo other = tasks.get(i).getTaskInfo();
if (other.taskId != taskInfo.taskId && other.isFocused && other.isRunning
&& isTaskStartedFromLauncher(currPkg, other)) {
return true;
}
}
} catch (PackageManager.NameNotFoundException e) {
// Fall through
}
return false;
} }
private boolean isLauncherIntent(Intent intent) { private boolean isLauncherIntent(Intent intent) {
@@ -1292,6 +1332,11 @@ public final class PermissionPolicyService extends SystemService {
|| intent.getCategories().contains(Intent.CATEGORY_CAR_LAUNCHER)); || intent.getCategories().contains(Intent.CATEGORY_CAR_LAUNCHER));
} }
private boolean isTaskStartedFromLauncher(String currPkg, TaskInfo taskInfo) {
return currPkg.equals(taskInfo.baseActivity.getPackageName())
&& isLauncherIntent(taskInfo.baseIntent);
}
private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) { private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) {
if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user) if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
& FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {

View File

@@ -27,6 +27,7 @@ import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.Parcel; import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
@@ -48,8 +49,9 @@ class AppTaskImpl extends IAppTask.Stub {
mCallingUid = callingUid; mCallingUid = callingUid;
} }
private void checkCaller() { private void checkCallerOrSystemOrRoot() {
if (mCallingUid != Binder.getCallingUid()) { if (mCallingUid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()
&& Process.ROOT_UID != Binder.getCallingUid()) {
throw new SecurityException("Caller " + mCallingUid throw new SecurityException("Caller " + mCallingUid
+ " does not match caller of getAppTasks(): " + Binder.getCallingUid()); + " does not match caller of getAppTasks(): " + Binder.getCallingUid());
} }
@@ -67,7 +69,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override @Override
public void finishAndRemoveTask() { public void finishAndRemoveTask() {
checkCaller(); checkCallerOrSystemOrRoot();
synchronized (mService.mGlobalLock) { synchronized (mService.mGlobalLock) {
final long origId = Binder.clearCallingIdentity(); final long origId = Binder.clearCallingIdentity();
@@ -85,7 +87,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override @Override
public ActivityManager.RecentTaskInfo getTaskInfo() { public ActivityManager.RecentTaskInfo getTaskInfo() {
checkCaller(); checkCallerOrSystemOrRoot();
synchronized (mService.mGlobalLock) { synchronized (mService.mGlobalLock) {
final long origId = Binder.clearCallingIdentity(); final long origId = Binder.clearCallingIdentity();
@@ -105,7 +107,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override @Override
public void moveToFront(IApplicationThread appThread, String callingPackage) { public void moveToFront(IApplicationThread appThread, String callingPackage) {
checkCaller(); checkCallerOrSystemOrRoot();
// Will bring task to front if it already has a root activity. // Will bring task to front if it already has a root activity.
final int callingPid = Binder.getCallingPid(); final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid();
@@ -136,7 +138,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override @Override
public int startActivity(IBinder whoThread, String callingPackage, String callingFeatureId, public int startActivity(IBinder whoThread, String callingPackage, String callingFeatureId,
Intent intent, String resolvedType, Bundle bOptions) { Intent intent, String resolvedType, Bundle bOptions) {
checkCaller(); checkCallerOrSystemOrRoot();
mService.assertPackageMatchesCallingUid(callingPackage); mService.assertPackageMatchesCallingUid(callingPackage);
int callingUser = UserHandle.getCallingUserId(); int callingUser = UserHandle.getCallingUserId();
@@ -167,7 +169,7 @@ class AppTaskImpl extends IAppTask.Stub {
@Override @Override
public void setExcludeFromRecents(boolean exclude) { public void setExcludeFromRecents(boolean exclude) {
checkCaller(); checkCallerOrSystemOrRoot();
synchronized (mService.mGlobalLock) { synchronized (mService.mGlobalLock) {
final long origId = Binder.clearCallingIdentity(); final long origId = Binder.clearCallingIdentity();

View File

@@ -3382,7 +3382,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (record != null && record.isUid(uid) if (record != null && record.isUid(uid)
&& Objects.equals(pkgName, record.packageName) && Objects.equals(pkgName, record.packageName)
&& pPi.shouldShowNotificationDialogForTask(record.getTask().getTaskInfo(), && pPi.shouldShowNotificationDialogForTask(record.getTask().getTaskInfo(),
pkgName, record.intent)) { pkgName, record.launchedFromPackage, record.intent, record.getName())) {
validTaskId[0] = record.getTask().mTaskId; validTaskId[0] = record.getTask().mTaskId;
return true; return true;
} }