Merge "Restrict what activity launches show permission prompts" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
c4293b56e2
@@ -155,8 +155,10 @@ package android.app {
|
|||||||
|
|
||||||
public class ActivityOptions {
|
public class ActivityOptions {
|
||||||
method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
|
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 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 @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 static void setExitTransitionTimeout(long);
|
||||||
method public void setLaunchActivityType(int);
|
method public void setLaunchActivityType(int);
|
||||||
method public void setLaunchWindowingMode(int);
|
method public void setLaunchWindowingMode(int);
|
||||||
|
|||||||
@@ -173,6 +173,13 @@ public class ActivityOptions extends ComponentOptions {
|
|||||||
*/
|
*/
|
||||||
public static final String KEY_SPLASH_SCREEN_THEME = "android.activity.splashScreenTheme";
|
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.
|
* Callback for when the last frame of the animation is played.
|
||||||
* @hide
|
* @hide
|
||||||
@@ -445,6 +452,7 @@ public class ActivityOptions extends ComponentOptions {
|
|||||||
private String mSplashScreenThemeResName;
|
private String mSplashScreenThemeResName;
|
||||||
@SplashScreen.SplashScreenStyle
|
@SplashScreen.SplashScreenStyle
|
||||||
private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
|
private int mSplashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
|
||||||
|
private boolean mIsEligibleForLegacyPermissionPrompt;
|
||||||
private boolean mRemoveWithTaskOrganizer;
|
private boolean mRemoveWithTaskOrganizer;
|
||||||
private boolean mLaunchedFromBubble;
|
private boolean mLaunchedFromBubble;
|
||||||
private boolean mTransientLaunch;
|
private boolean mTransientLaunch;
|
||||||
@@ -1243,6 +1251,8 @@ public class ActivityOptions extends ComponentOptions {
|
|||||||
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
|
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
|
||||||
mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE);
|
mSplashScreenStyle = opts.getInt(KEY_SPLASH_SCREEN_STYLE);
|
||||||
mLaunchIntoPipParams = opts.getParcelable(KEY_LAUNCH_INTO_PIP_PARAMS);
|
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;
|
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.
|
* Sets whether the activity is to be launched into LockTask mode.
|
||||||
*
|
*
|
||||||
@@ -1909,6 +1937,7 @@ public class ActivityOptions extends ComponentOptions {
|
|||||||
mSpecsFuture = otherOptions.mSpecsFuture;
|
mSpecsFuture = otherOptions.mSpecsFuture;
|
||||||
mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
|
mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
|
||||||
mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams;
|
mLaunchIntoPipParams = otherOptions.mLaunchIntoPipParams;
|
||||||
|
mIsEligibleForLegacyPermissionPrompt = otherOptions.mIsEligibleForLegacyPermissionPrompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2084,6 +2113,10 @@ public class ActivityOptions extends ComponentOptions {
|
|||||||
if (mLaunchIntoPipParams != null) {
|
if (mLaunchIntoPipParams != null) {
|
||||||
b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams);
|
b.putParcelable(KEY_LAUNCH_INTO_PIP_PARAMS, mLaunchIntoPipParams);
|
||||||
}
|
}
|
||||||
|
if (mIsEligibleForLegacyPermissionPrompt) {
|
||||||
|
b.putBoolean(KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE,
|
||||||
|
mIsEligibleForLegacyPermissionPrompt);
|
||||||
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3972,8 +3972,14 @@ public class CentralSurfaces extends CoreStartable implements
|
|||||||
|
|
||||||
mActivityLaunchAnimator.startPendingIntentWithAnimation(
|
mActivityLaunchAnimator.startPendingIntentWithAnimation(
|
||||||
controller, animate, intent.getCreatorPackage(),
|
controller, animate, intent.getCreatorPackage(),
|
||||||
(animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
|
(animationAdapter) -> {
|
||||||
null, getActivityOptions(mDisplayId, 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) {
|
} catch (PendingIntent.CanceledException e) {
|
||||||
// the stack trace isn't very helpful here.
|
// the stack trace isn't very helpful here.
|
||||||
// Just log the exception message.
|
// Just log the exception message.
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import android.app.ActivityOptions;
|
|||||||
import android.app.ActivityTaskManager;
|
import android.app.ActivityTaskManager;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.app.AppOpsManagerInternal;
|
import android.app.AppOpsManagerInternal;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
import android.app.TaskInfo;
|
import android.app.TaskInfo;
|
||||||
import android.app.compat.CompatChanges;
|
import android.app.compat.CompatChanges;
|
||||||
import android.compat.annotation.ChangeId;
|
import android.compat.annotation.ChangeId;
|
||||||
@@ -150,6 +151,7 @@ public final class PermissionPolicyService extends SystemService {
|
|||||||
private Context mContext;
|
private Context mContext;
|
||||||
private PackageManagerInternal mPackageManagerInternal;
|
private PackageManagerInternal mPackageManagerInternal;
|
||||||
private NotificationManagerInternal mNotificationManager;
|
private NotificationManagerInternal mNotificationManager;
|
||||||
|
private final KeyguardManager mKeyguardManager;
|
||||||
private final PackageManager mPackageManager;
|
private final PackageManager mPackageManager;
|
||||||
|
|
||||||
public PermissionPolicyService(@NonNull Context context) {
|
public PermissionPolicyService(@NonNull Context context) {
|
||||||
@@ -157,6 +159,7 @@ public final class PermissionPolicyService extends SystemService {
|
|||||||
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPackageManager = context.getPackageManager();
|
mPackageManager = context.getPackageManager();
|
||||||
|
mKeyguardManager = context.getSystemService(KeyguardManager.class);
|
||||||
LocalServices.addService(PermissionPolicyInternal.class, new Internal());
|
LocalServices.addService(PermissionPolicyInternal.class, new Internal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1046,13 +1049,22 @@ public final class PermissionPolicyService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
|
public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
|
||||||
super.onActivityLaunched(taskInfo, activityInfo);
|
ActivityInterceptorInfo info) {
|
||||||
clearNotificationReviewFlagsIfNeeded(activityInfo.packageName,
|
super.onActivityLaunched(taskInfo, activityInfo, info);
|
||||||
UserHandle.of(taskInfo.userId));
|
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,
|
showNotificationPromptIfNeeded(activityInfo.packageName,
|
||||||
taskInfo.userId, taskInfo.taskId);
|
taskInfo.userId, taskInfo.taskId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private void onActivityManagerReady() {
|
private void onActivityManagerReady() {
|
||||||
@@ -1092,10 +1104,28 @@ public final class PermissionPolicyService extends SystemService {
|
|||||||
launchNotificationPermissionRequestDialog(packageName, user, taskId);
|
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) {
|
private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) {
|
||||||
if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, user)
|
if ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
|
||||||
|| ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user)
|
& FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
|
||||||
& FLAG_PERMISSION_REVIEW_REQUIRED) == 0)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -1210,8 +1240,8 @@ public final class PermissionPolicyService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
|
if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
|
||||||
|| CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID,
|
|| CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user)
|
||||||
pkg.getPackageName(), user)) {
|
|| mKeyguardManager.isKeyguardLocked()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1220,7 +1250,7 @@ public final class PermissionPolicyService extends SystemService {
|
|||||||
mNotificationManager = LocalServices.getService(NotificationManagerInternal.class);
|
mNotificationManager = LocalServices.getService(NotificationManagerInternal.class);
|
||||||
}
|
}
|
||||||
boolean hasCreatedNotificationChannels = mNotificationManager
|
boolean hasCreatedNotificationChannels = mNotificationManager
|
||||||
.getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0;
|
.getNumNotificationChannelsForPackage(pkgName, uid, true) > 0;
|
||||||
int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user);
|
int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user);
|
||||||
boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0;
|
boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0;
|
||||||
boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
|
boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
|
||||||
|
|||||||
@@ -43,9 +43,13 @@ public abstract class ActivityInterceptorCallback {
|
|||||||
public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
|
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -188,10 +188,7 @@ class ActivityStartInterceptor {
|
|||||||
final SparseArray<ActivityInterceptorCallback> callbacks =
|
final SparseArray<ActivityInterceptorCallback> callbacks =
|
||||||
mService.getActivityInterceptorCallbacks();
|
mService.getActivityInterceptorCallbacks();
|
||||||
final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
|
final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
|
||||||
new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
|
getInterceptorInfo();
|
||||||
mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
|
|
||||||
mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
|
|
||||||
mActivityOptions);
|
|
||||||
|
|
||||||
for (int i = 0; i < callbacks.size(); i++) {
|
for (int i = 0; i < callbacks.size(); i++) {
|
||||||
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
|
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
|
||||||
@@ -412,9 +409,17 @@ class ActivityStartInterceptor {
|
|||||||
void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
|
void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
|
||||||
final SparseArray<ActivityInterceptorCallback> callbacks =
|
final SparseArray<ActivityInterceptorCallback> callbacks =
|
||||||
mService.getActivityInterceptorCallbacks();
|
mService.getActivityInterceptorCallbacks();
|
||||||
|
ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo();
|
||||||
for (int i = 0; i < callbacks.size(); i++) {
|
for (int i = 0; i < callbacks.size(); i++) {
|
||||||
final ActivityInterceptorCallback callback = callbacks.valueAt(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,6 +352,6 @@ public class ActivityStartInterceptorTest {
|
|||||||
spyOn(callback);
|
spyOn(callback);
|
||||||
mInterceptor.onActivityLaunched(null, null);
|
mInterceptor.onActivityLaunched(null, null);
|
||||||
|
|
||||||
verify(callback, times(1)).onActivityLaunched(any(), any());
|
verify(callback, times(1)).onActivityLaunched(any(), any(), any());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user