Merge "Restrict what activity launches show permission prompts" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-01 17:21:36 +00:00
committed by Android (Google) Code Review
7 changed files with 102 additions and 22 deletions

View File

@@ -155,8 +155,10 @@ package android.app {
public class ActivityOptions {
method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
method public boolean isEligibleForLegacyPermissionPrompt();
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public void setEligibleForLegacyPermissionPrompt(boolean);
method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int);
method public void setLaunchWindowingMode(int);

View File

@@ -173,6 +173,13 @@ public class ActivityOptions extends ComponentOptions {
*/
public static final String KEY_SPLASH_SCREEN_THEME = "android.activity.splashScreenTheme";
/**
* Indicates that this activity launch is eligible to show a legacy permission prompt
* @hide
*/
public static final String KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE =
"android:activity.legacyPermissionPromptEligible";
/**
* Callback for when the last frame of the animation is played.
* @hide
@@ -445,6 +452,7 @@ public class ActivityOptions extends ComponentOptions {
private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle
private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
private boolean mIsEligibleForLegacyPermissionPrompt;
private boolean mRemoveWithTaskOrganizer;
private boolean mLaunchedFromBubble;
private boolean mTransientLaunch;
@@ -1243,6 +1251,8 @@ public class ActivityOptions extends ComponentOptions {
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE);
mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS);
mIsEligibleForLegacyPermissionPrompt =
opts.getBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE);
}
/**
@@ -1473,6 +1483,24 @@ public class ActivityOptions extends ComponentOptions {
return this;
}
/**
* Whether the activity is eligible to show a legacy permission prompt
* @hide
*/
@TestApi
public boolean isEligibleForLegacyPermissionPrompt() {
return mIsEligibleForLegacyPermissionPrompt;
}
/**
* Sets whether the activity is eligible to show a legacy permission prompt
* @hide
*/
@TestApi
public void setEligibleForLegacyPermissionPrompt(boolean eligible) {
mIsEligibleForLegacyPermissionPrompt = eligible;
}
/**
* Sets whether the activity is to be launched into LockTask mode.
*
@@ -1909,6 +1937,7 @@ public class ActivityOptions extends ComponentOptions {
mSpecsFuture = otherOptions.mSpecsFuture;
mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams;
mIsEligibleForLegacyPermissionPrompt = otherOptions.mIsEligibleForLegacyPermissionPrompt;
}
/**
@@ -2084,6 +2113,10 @@ public class ActivityOptions extends ComponentOptions {
if (mLaunchIntoPipParams != null) {
b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams);
}
if (mIsEligibleForLegacyPermissionPrompt) {
b.putBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE,
mIsEligibleForLegacyPermissionPrompt);
}
return b;
}

View File

@@ -3972,8 +3972,14 @@ public class CentralSurfaces extends CoreStartable implements
mActivityLaunchAnimator.startPendingIntentWithAnimation(
controller, animate, intent.getCreatorPackage(),
(animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
null, getActivityOptions(mDisplayId, animationAdapter)));
(animationAdapter) -> {
ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, animationAdapter));
// TODO b/221255671: restrict this to only be set for notifications
options.setEligibleForLegacyPermissionPrompt(true);
return intent.sendAndReturnResult(null, 0, null, null, null,
null, options.toBundle());
});
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.

View File

@@ -34,6 +34,7 @@ import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
import android.app.KeyguardManager;
import android.app.TaskInfo;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
@@ -150,6 +151,7 @@ public final class PermissionPolicyService extends SystemService {
private Context mContext;
private PackageManagerInternal mPackageManagerInternal;
private NotificationManagerInternal mNotificationManager;
private final KeyguardManager mKeyguardManager;
private final PackageManager mPackageManager;
public PermissionPolicyService(@NonNull Context context) {
@@ -157,6 +159,7 @@ public final class PermissionPolicyService extends SystemService {
mContext = context;
mPackageManager = context.getPackageManager();
mKeyguardManager = context.getSystemService(KeyguardManager.class);
LocalServices.addService(PermissionPolicyInternal.class, new Internal());
}
@@ -1046,12 +1049,21 @@ public final class PermissionPolicyService extends SystemService {
}
@Override
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
super.onActivityLaunched(taskInfo, activityInfo);
clearNotificationReviewFlagsIfNeeded(activityInfo.packageName,
UserHandle.of(taskInfo.userId));
showNotificationPromptIfNeeded(activityInfo.packageName,
taskInfo.userId, taskInfo.taskId);
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
ActivityInterceptorInfo info) {
super.onActivityLaunched(taskInfo, activityInfo, info);
if (!shouldShowNotificationDialogOrClearFlags(info.intent,
info.checkedOptions)) {
return;
}
UserHandle user = UserHandle.of(taskInfo.userId);
if (CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID,
activityInfo.packageName, user)) {
clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, user);
} else {
showNotificationPromptIfNeeded(activityInfo.packageName,
taskInfo.userId, taskInfo.taskId);
}
}
};
@@ -1092,10 +1104,28 @@ public final class PermissionPolicyService extends SystemService {
launchNotificationPermissionRequestDialog(packageName, user, taskId);
}
/**
* 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:
* 1. The isEligibleForLegacyPermissionPrompt ActivityOption is set, or
* 2. The intent is a launcher intent (action is ACTION_MAIN, category is LAUNCHER)
*/
private boolean shouldShowNotificationDialogOrClearFlags(Intent intent,
ActivityOptions options) {
if ((options != null && options.isEligibleForLegacyPermissionPrompt())) {
return true;
}
return Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.getCategories() != null
&& (intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)
|| intent.getCategories().contains(Intent.CATEGORY_LEANBACK_LAUNCHER)
|| intent.getCategories().contains(Intent.CATEGORY_CAR_LAUNCHER));
}
private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) {
if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, user)
|| ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
& FLAG_PERMISSION_REVIEW_REQUIRED) == 0)) {
if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
& FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
return;
}
try {
@@ -1210,8 +1240,8 @@ public final class PermissionPolicyService extends SystemService {
}
if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
|| CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID,
pkg.getPackageName(), user)) {
|| CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user)
|| mKeyguardManager.isKeyguardLocked()) {
return false;
}
@@ -1220,7 +1250,7 @@ public final class PermissionPolicyService extends SystemService {
mNotificationManager = LocalServices.getService(NotificationManagerInternal.class);
}
boolean hasCreatedNotificationChannels = mNotificationManager
.getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0;
.getNumNotificationChannelsForPackage(pkgName, uid, true) > 0;
int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user);
boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0;
boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;

View File

@@ -43,9 +43,13 @@ public abstract class ActivityInterceptorCallback {
public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
/**
* Called when an activity is successfully launched.
* Called when an activity is successfully launched. The intent included in the
* ActivityInterceptorInfo may have changed from the one sent in
* {@link #intercept(ActivityInterceptorInfo)}, due to the return from
* {@link #intercept(ActivityInterceptorInfo)}.
*/
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
ActivityInterceptorInfo info) {
}
/**

View File

@@ -188,10 +188,7 @@ class ActivityStartInterceptor {
final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks();
final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
mActivityOptions);
getInterceptorInfo();
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
@@ -412,9 +409,17 @@ class ActivityStartInterceptor {
void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
final SparseArray<ActivityInterceptorCallback> callbacks =
mService.getActivityInterceptorCallbacks();
ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo();
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
callback.onActivityLaunched(taskInfo, activityInfo);
callback.onActivityLaunched(taskInfo, activityInfo, info);
}
}
private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo() {
return new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
mActivityOptions);
}
}

View File

@@ -352,6 +352,6 @@ public class ActivityStartInterceptorTest {
spyOn(callback);
mInterceptor.onActivityLaunched(null, null);
verify(callback, times(1)).onActivityLaunched(any(), any());
verify(callback, times(1)).onActivityLaunched(any(), any(), any());
}
}