Transfer remote animation option from an intercepted activity.
While an activity starts, ActivityStartIntercepter may intercept it and
launch another activity. If this happens, we don't have chance to apply
pending remote animation of the intercepted activity.
The pending remote animation originally came from ActivityOption, so we
extract the remote animation from an ActivityOption of the intercepted
activity, and pipe it through to the activity being launched by the
ActivityStartIntercepter.
This also reverts commit a17263ae78.
Bug: 222757698
Test: manual. Start YouTube app from the launcher and check animation.
Change-Id: Ice62f4ee3453a06362e3acf820ef22235656845c
This commit is contained in:
@@ -264,7 +264,6 @@ import android.widget.Toast;
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.IAppOpsCallback;
|
||||
import com.android.internal.app.IAppOpsService;
|
||||
import com.android.internal.compat.IPlatformCompat;
|
||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.policy;
|
||||
|
||||
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
||||
import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
|
||||
import static android.app.AppOpsManager.MODE_ALLOWED;
|
||||
import static android.app.AppOpsManager.MODE_FOREGROUND;
|
||||
import static android.app.AppOpsManager.MODE_IGNORED;
|
||||
@@ -58,8 +59,6 @@ import android.content.pm.PackageManagerInternal.PackageListObserver;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
@@ -93,6 +92,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
|
||||
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
|
||||
import com.android.server.utils.TimingsTraceAndSlog;
|
||||
import com.android.server.wm.ActivityInterceptorCallback;
|
||||
import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -115,7 +115,6 @@ public final class PermissionPolicyService extends SystemService {
|
||||
private static final String SYSTEM_PKG = "android";
|
||||
private static final boolean DEBUG = false;
|
||||
private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 60000;
|
||||
private static final long ACTIVITY_START_DELAY_MS = 200;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@@ -155,8 +154,7 @@ public final class PermissionPolicyService extends SystemService {
|
||||
|
||||
private List<String> mAppOpPermissions;
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
private Context mContext;
|
||||
private PackageManagerInternal mPackageManagerInternal;
|
||||
private PermissionManagerServiceInternal mPermissionManagerInternal;
|
||||
private NotificationManagerInternal mNotificationManager;
|
||||
@@ -167,7 +165,6 @@ public final class PermissionPolicyService extends SystemService {
|
||||
super(context);
|
||||
|
||||
mContext = context;
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mPackageManager = context.getPackageManager();
|
||||
mKeyguardManager = context.getSystemService(KeyguardManager.class);
|
||||
LocalServices.addService(PermissionPolicyInternal.class, new Internal());
|
||||
@@ -1072,7 +1069,7 @@ public final class PermissionPolicyService extends SystemService {
|
||||
clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, user);
|
||||
} else {
|
||||
showNotificationPromptIfNeeded(activityInfo.packageName,
|
||||
taskInfo.userId, taskInfo.taskId);
|
||||
taskInfo.userId, taskInfo.taskId, info);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1103,15 +1100,21 @@ public final class PermissionPolicyService extends SystemService {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showNotificationPromptIfNeeded(@NonNull String packageName, int userId,
|
||||
int taskId) {
|
||||
showNotificationPromptIfNeeded(packageName, userId, taskId, null /* info */);
|
||||
}
|
||||
|
||||
void showNotificationPromptIfNeeded(@NonNull String packageName, int userId,
|
||||
int taskId, @Nullable ActivityInterceptorInfo info) {
|
||||
UserHandle user = UserHandle.of(userId);
|
||||
if (packageName == null || taskId == ActivityTaskManager.INVALID_TASK_ID
|
||||
|| !shouldForceShowNotificationPermissionRequest(packageName, user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
launchNotificationPermissionRequestDialog(packageName, user, taskId);
|
||||
launchNotificationPermissionRequestDialog(packageName, user, taskId, info);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1180,7 +1183,7 @@ public final class PermissionPolicyService extends SystemService {
|
||||
}
|
||||
|
||||
private void launchNotificationPermissionRequestDialog(String pkgName, UserHandle user,
|
||||
int taskId) {
|
||||
int taskId, @Nullable ActivityInterceptorInfo info) {
|
||||
Intent grantPermission = mPackageManager
|
||||
.buildRequestPermissionsIntent(new String[] { POST_NOTIFICATIONS });
|
||||
// Prevent the front-most activity entering pip due to overlay activity started on top.
|
||||
@@ -1189,18 +1192,29 @@ public final class PermissionPolicyService extends SystemService {
|
||||
ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
|
||||
grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName);
|
||||
|
||||
ActivityOptions options = new ActivityOptions(new Bundle());
|
||||
final boolean remoteAnimation = info != null && info.checkedOptions != null
|
||||
&& info.checkedOptions.getAnimationType() == ANIM_REMOTE_ANIMATION
|
||||
&& info.clearOptionsAnimation != null;
|
||||
ActivityOptions options = remoteAnimation ? ActivityOptions.makeRemoteAnimation(
|
||||
info.checkedOptions.getRemoteAnimationAdapter(),
|
||||
info.checkedOptions.getRemoteTransition())
|
||||
: new ActivityOptions(new Bundle());
|
||||
options.setTaskOverlay(true, false);
|
||||
options.setLaunchTaskId(taskId);
|
||||
mHandler.postDelayed(() -> {
|
||||
try {
|
||||
mContext.startActivityAsUser(
|
||||
grantPermission, options.toBundle(), user);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "couldn't start grant permission dialog"
|
||||
+ "for other package " + pkgName, e);
|
||||
}
|
||||
}, ACTIVITY_START_DELAY_MS);
|
||||
if (remoteAnimation) {
|
||||
// Remote animation set on the intercepted activity will be handled by the grant
|
||||
// permission activity, which is launched below. So we need to clear remote
|
||||
// animation from the intercepted activity and its siblings to prevent duplication.
|
||||
// This should trigger ActivityRecord#clearOptionsAnimationForSiblings for the
|
||||
// intercepted activity.
|
||||
info.clearOptionsAnimation.run();
|
||||
}
|
||||
try {
|
||||
mContext.startActivityAsUser(grantPermission, options.toBundle(), user);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "couldn't start grant permission dialog"
|
||||
+ "for other package " + pkgName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -108,11 +108,13 @@ public abstract class ActivityInterceptorCallback {
|
||||
public final int callingPid;
|
||||
public final int callingUid;
|
||||
public final ActivityOptions checkedOptions;
|
||||
public final @Nullable Runnable clearOptionsAnimation;
|
||||
|
||||
public ActivityInterceptorInfo(int realCallingUid, int realCallingPid, int userId,
|
||||
String callingPackage, String callingFeatureId, Intent intent,
|
||||
ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, int callingPid,
|
||||
int callingUid, ActivityOptions checkedOptions) {
|
||||
int callingUid, ActivityOptions checkedOptions,
|
||||
@Nullable Runnable clearOptionsAnimation) {
|
||||
this.realCallingUid = realCallingUid;
|
||||
this.realCallingPid = realCallingPid;
|
||||
this.userId = userId;
|
||||
@@ -125,6 +127,7 @@ public abstract class ActivityInterceptorCallback {
|
||||
this.callingPid = callingPid;
|
||||
this.callingUid = callingUid;
|
||||
this.checkedOptions = checkedOptions;
|
||||
this.clearOptionsAnimation = clearOptionsAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4592,14 +4592,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
applyOptionsAnimation(mPendingOptions, intent);
|
||||
}
|
||||
if (task == null) {
|
||||
clearOptionsAnimation();
|
||||
} else {
|
||||
// This will clear the options for all the ActivityRecords for this Task.
|
||||
task.forAllActivities((r) -> {
|
||||
r.clearOptionsAnimation();
|
||||
});
|
||||
}
|
||||
clearOptionsAnimationForSiblings();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4786,6 +4779,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
mPendingRemoteTransition = null;
|
||||
}
|
||||
|
||||
void clearOptionsAnimationForSiblings() {
|
||||
if (task == null) {
|
||||
clearOptionsAnimation();
|
||||
} else {
|
||||
// This will clear the options for all the ActivityRecords for this Task.
|
||||
task.forAllActivities(ActivityRecord::clearOptionsAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
ActivityOptions getOptions() {
|
||||
return mPendingOptions;
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ class ActivityStartInterceptor {
|
||||
final SparseArray<ActivityInterceptorCallback> callbacks =
|
||||
mService.getActivityInterceptorCallbacks();
|
||||
final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
|
||||
getInterceptorInfo();
|
||||
getInterceptorInfo(null /* clearOptionsAnimation */);
|
||||
|
||||
for (int i = 0; i < callbacks.size(); i++) {
|
||||
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
|
||||
@@ -406,20 +406,22 @@ class ActivityStartInterceptor {
|
||||
/**
|
||||
* Called when an activity is successfully launched.
|
||||
*/
|
||||
void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
|
||||
void onActivityLaunched(TaskInfo taskInfo, ActivityRecord r) {
|
||||
final SparseArray<ActivityInterceptorCallback> callbacks =
|
||||
mService.getActivityInterceptorCallbacks();
|
||||
ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo();
|
||||
ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo(
|
||||
r::clearOptionsAnimationForSiblings);
|
||||
for (int i = 0; i < callbacks.size(); i++) {
|
||||
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
|
||||
callback.onActivityLaunched(taskInfo, activityInfo, info);
|
||||
callback.onActivityLaunched(taskInfo, r.info, info);
|
||||
}
|
||||
}
|
||||
|
||||
private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo() {
|
||||
private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo(
|
||||
@Nullable Runnable clearOptionsAnimation) {
|
||||
return new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
|
||||
mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
|
||||
mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
|
||||
mActivityOptions);
|
||||
mActivityOptions, clearOptionsAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1585,7 +1585,7 @@ class ActivityStarter {
|
||||
}
|
||||
|
||||
if (ActivityManager.isStartResultSuccessful(result)) {
|
||||
mInterceptor.onActivityLaunched(targetTask.getTaskInfo(), r.info);
|
||||
mInterceptor.onActivityLaunched(targetTask.getTaskInfo(), r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -350,7 +351,7 @@ public class ActivityStartInterceptorTest {
|
||||
assertEquals(1, mActivityInterceptorCallbacks.size());
|
||||
final ActivityInterceptorCallback callback = mActivityInterceptorCallbacks.valueAt(0);
|
||||
spyOn(callback);
|
||||
mInterceptor.onActivityLaunched(null, null);
|
||||
mInterceptor.onActivityLaunched(null, mock(ActivityRecord.class));
|
||||
|
||||
verify(callback, times(1)).onActivityLaunched(any(), any(), any());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user