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 {
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user