Merge "Add TaskFragmentOrganizer#onActivityReparentToTask" into tm-dev am: 0b91be7283
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17817304 Change-Id: I337c320d4a6b02c264e054311f433fb72db89b7d Ignore-AOSP-First: this is an automerge Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.window;
|
package android.window;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -48,4 +49,20 @@ oneway interface ITaskFragmentOrganizer {
|
|||||||
* {@link TaskFragmentOrganizer#putExceptionInBundle}.
|
* {@link TaskFragmentOrganizer#putExceptionInBundle}.
|
||||||
*/
|
*/
|
||||||
void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle exceptionBundle);
|
void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle exceptionBundle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an Activity is reparented to the Task with organized TaskFragment. For example,
|
||||||
|
* when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
|
||||||
|
* orginial Task. In this case, we need to notify the organizer so that it can check if the
|
||||||
|
* Activity matches any split rule.
|
||||||
|
*
|
||||||
|
* @param taskId The Task that the activity is reparented to.
|
||||||
|
* @param activityIntent The intent that the activity is original launched with.
|
||||||
|
* @param activityToken If the activity belongs to the same process as the organizer, this
|
||||||
|
* will be the actual activity token; if the activity belongs to a
|
||||||
|
* different process, the server will generate a temporary token that
|
||||||
|
* the organizer can use to reparent the activity through
|
||||||
|
* {@link WindowContainerTransaction} if needed.
|
||||||
|
*/
|
||||||
|
void onActivityReparentToTask(int taskId, in Intent activityIntent, in IBinder activityToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package android.window;
|
|||||||
import android.annotation.CallSuper;
|
import android.annotation.CallSuper;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.TestApi;
|
import android.annotation.TestApi;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -154,6 +155,24 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
|
|||||||
public void onTaskFragmentError(
|
public void onTaskFragmentError(
|
||||||
@NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
|
@NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an Activity is reparented to the Task with organized TaskFragment. For example,
|
||||||
|
* when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
|
||||||
|
* orginial Task. In this case, we need to notify the organizer so that it can check if the
|
||||||
|
* Activity matches any split rule.
|
||||||
|
*
|
||||||
|
* @param taskId The Task that the activity is reparented to.
|
||||||
|
* @param activityIntent The intent that the activity is original launched with.
|
||||||
|
* @param activityToken If the activity belongs to the same process as the organizer, this
|
||||||
|
* will be the actual activity token; if the activity belongs to a
|
||||||
|
* different process, the server will generate a temporary token that
|
||||||
|
* the organizer can use to reparent the activity through
|
||||||
|
* {@link WindowContainerTransaction} if needed.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
|
||||||
|
@NonNull IBinder activityToken) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyTransaction(@NonNull WindowContainerTransaction t) {
|
public void applyTransaction(@NonNull WindowContainerTransaction t) {
|
||||||
t.setTaskFragmentOrganizer(mInterface);
|
t.setTaskFragmentOrganizer(mInterface);
|
||||||
@@ -203,6 +222,14 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
|
|||||||
errorCallbackToken,
|
errorCallbackToken,
|
||||||
(Throwable) exceptionBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION)));
|
(Throwable) exceptionBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
|
||||||
|
@NonNull IBinder activityToken) {
|
||||||
|
mExecutor.execute(
|
||||||
|
() -> TaskFragmentOrganizer.this.onActivityReparentToTask(
|
||||||
|
taskId, activityIntent, activityToken));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final TaskFragmentOrganizerToken mToken = new TaskFragmentOrganizerToken(mInterface);
|
private final TaskFragmentOrganizerToken mToken = new TaskFragmentOrganizerToken(mInterface);
|
||||||
|
|||||||
@@ -3031,6 +3031,12 @@
|
|||||||
"group": "WM_DEBUG_FOCUS_LIGHT",
|
"group": "WM_DEBUG_FOCUS_LIGHT",
|
||||||
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
"at": "com\/android\/server\/wm\/DisplayContent.java"
|
||||||
},
|
},
|
||||||
|
"873160948": {
|
||||||
|
"message": "Activity=%s reparent to taskId=%d",
|
||||||
|
"level": "VERBOSE",
|
||||||
|
"group": "WM_DEBUG_WINDOW_ORGANIZER",
|
||||||
|
"at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
|
||||||
|
},
|
||||||
"873914452": {
|
"873914452": {
|
||||||
"message": "goodToGo()",
|
"message": "goodToGo()",
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
|||||||
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
|
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
|
||||||
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
|
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
|
||||||
@NonNull Configuration parentConfig);
|
@NonNull Configuration parentConfig);
|
||||||
|
void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
|
||||||
|
@NonNull IBinder activityToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -300,4 +302,12 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
|||||||
mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
|
mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
|
||||||
|
@NonNull IBinder activityToken) {
|
||||||
|
if (mCallback != null) {
|
||||||
|
mCallback.onActivityReparentToTask(taskId, activityIntent, activityToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,6 +222,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
|
||||||
|
@NonNull IBinder activityToken) {
|
||||||
|
// If the activity belongs to the current app process, we treat it as a new activity launch.
|
||||||
|
final Activity activity = ActivityThread.currentActivityThread().getActivity(activityToken);
|
||||||
|
if (activity != null) {
|
||||||
|
onActivityCreated(activity);
|
||||||
|
updateCallbackIfNecessary();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: handle for activity in other process.
|
||||||
|
}
|
||||||
|
|
||||||
/** Called on receiving {@link #onTaskFragmentVanished(TaskFragmentInfo)} for cleanup. */
|
/** Called on receiving {@link #onTaskFragmentVanished(TaskFragmentInfo)} for cleanup. */
|
||||||
private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
|
private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
|
||||||
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
|
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ import android.view.WindowManager;
|
|||||||
import android.view.WindowManager.LayoutParams;
|
import android.view.WindowManager.LayoutParams;
|
||||||
import android.view.WindowManager.TransitionOldType;
|
import android.view.WindowManager.TransitionOldType;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
|
import android.window.ITaskFragmentOrganizer;
|
||||||
import android.window.RemoteTransition;
|
import android.window.RemoteTransition;
|
||||||
import android.window.SizeConfigurationBuckets;
|
import android.window.SizeConfigurationBuckets;
|
||||||
import android.window.SplashScreen;
|
import android.window.SplashScreen;
|
||||||
@@ -635,6 +636,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
|||||||
// host Activity the launch-into-pip Activity is originated from.
|
// host Activity the launch-into-pip Activity is originated from.
|
||||||
private ActivityRecord mLaunchIntoPipHostActivity;
|
private ActivityRecord mLaunchIntoPipHostActivity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets to the previous {@link ITaskFragmentOrganizer} of the {@link TaskFragment} that the
|
||||||
|
* activity is embedded in before it is reparented to a new Task due to picture-in-picture.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
ITaskFragmentOrganizer mLastTaskFragmentOrganizerBeforePip;
|
||||||
|
|
||||||
boolean firstWindowDrawn;
|
boolean firstWindowDrawn;
|
||||||
/** Whether the visible window(s) of this activity is drawn. */
|
/** Whether the visible window(s) of this activity is drawn. */
|
||||||
private boolean mReportedDrawn;
|
private boolean mReportedDrawn;
|
||||||
@@ -1531,7 +1539,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
|||||||
|
|
||||||
updateAnimatingActivityRegistry();
|
updateAnimatingActivityRegistry();
|
||||||
|
|
||||||
if (task == mLastParentBeforePip) {
|
if (task == mLastParentBeforePip && task != null) {
|
||||||
|
// Notify the TaskFragmentOrganizer that the activity is reparented back from pip.
|
||||||
|
mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController
|
||||||
|
.onActivityReparentToTask(this);
|
||||||
// Activity's reparented back from pip, clear the links once established
|
// Activity's reparented back from pip, clear the links once established
|
||||||
clearLastParentBeforePip();
|
clearLastParentBeforePip();
|
||||||
}
|
}
|
||||||
@@ -1654,6 +1665,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
|||||||
: launchIntoPipHostActivity.getTask();
|
: launchIntoPipHostActivity.getTask();
|
||||||
mLastParentBeforePip.mChildPipActivity = this;
|
mLastParentBeforePip.mChildPipActivity = this;
|
||||||
mLaunchIntoPipHostActivity = launchIntoPipHostActivity;
|
mLaunchIntoPipHostActivity = launchIntoPipHostActivity;
|
||||||
|
final TaskFragment organizedTf = launchIntoPipHostActivity == null
|
||||||
|
? getOrganizedTaskFragment()
|
||||||
|
: launchIntoPipHostActivity.getOrganizedTaskFragment();
|
||||||
|
mLastTaskFragmentOrganizerBeforePip = organizedTf != null
|
||||||
|
? organizedTf.getTaskFragmentOrganizer()
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearLastParentBeforePip() {
|
private void clearLastParentBeforePip() {
|
||||||
@@ -1662,6 +1679,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
|||||||
mLastParentBeforePip = null;
|
mLastParentBeforePip = null;
|
||||||
}
|
}
|
||||||
mLaunchIntoPipHostActivity = null;
|
mLaunchIntoPipHostActivity = null;
|
||||||
|
mLastTaskFragmentOrganizerBeforePip = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable Task getLastParentBeforePip() {
|
@Nullable Task getLastParentBeforePip() {
|
||||||
|
|||||||
@@ -519,15 +519,20 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
|
||||||
|
return isAllowedToEmbedActivity(a, mTaskFragmentOrganizerUid);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the organized task fragment is allowed to have the specified activity, which is
|
* Checks if the organized task fragment is allowed to have the specified activity, which is
|
||||||
* allowed if an activity allows embedding in untrusted mode, or if the trusted mode can be
|
* allowed if an activity allows embedding in untrusted mode, or if the trusted mode can be
|
||||||
* enabled.
|
* enabled.
|
||||||
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
|
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
|
||||||
|
* @param uid uid of the TaskFragment organizer.
|
||||||
*/
|
*/
|
||||||
boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
|
boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a, int uid) {
|
||||||
return isAllowedToEmbedActivityInUntrustedMode(a)
|
return isAllowedToEmbedActivityInUntrustedMode(a)
|
||||||
|| isAllowedToEmbedActivityInTrustedMode(a);
|
|| isAllowedToEmbedActivityInTrustedMode(a, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -544,20 +549,25 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
== FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
|
== FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
|
||||||
|
return isAllowedToEmbedActivityInTrustedMode(a, mTaskFragmentOrganizerUid);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the organized task fragment is allowed to embed activity in fully trusted mode,
|
* Checks if the organized task fragment is allowed to embed activity in fully trusted mode,
|
||||||
* which means that all transactions are allowed. This is supported in the following cases:
|
* which means that all transactions are allowed. This is supported in the following cases:
|
||||||
* <li>the activity belongs to the same app as the organizer host;</li>
|
* <li>the activity belongs to the same app as the organizer host;</li>
|
||||||
* <li>the activity has declared the organizer host as trusted explicitly via known
|
* <li>the activity has declared the organizer host as trusted explicitly via known
|
||||||
* certificate.</li>
|
* certificate.</li>
|
||||||
|
* @param uid uid of the TaskFragment organizer.
|
||||||
*/
|
*/
|
||||||
boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
|
boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a, int uid) {
|
||||||
if (UserHandle.getAppId(mTaskFragmentOrganizerUid) == SYSTEM_UID) {
|
if (UserHandle.getAppId(uid) == SYSTEM_UID) {
|
||||||
// The system is trusted to embed other apps securely and for all users.
|
// The system is trusted to embed other apps securely and for all users.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mTaskFragmentOrganizerUid == a.getUid()) {
|
if (uid == a.getUid()) {
|
||||||
// Activities from the same UID can be embedded freely by the host.
|
// Activities from the same UID can be embedded freely by the host.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -570,7 +580,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AndroidPackage hostPackage = mAtmService.getPackageManagerInternalLocked()
|
AndroidPackage hostPackage = mAtmService.getPackageManagerInternalLocked()
|
||||||
.getPackage(mTaskFragmentOrganizerUid);
|
.getPackage(uid);
|
||||||
|
|
||||||
return hostPackage != null && hostPackage.getSigningDetails().hasAncestorOrSelfWithDigest(
|
return hostPackage != null && hostPackage.getSigningDetails().hasAncestorOrSelfWithDigest(
|
||||||
knownActivityEmbeddingCerts);
|
knownActivityEmbeddingCerts);
|
||||||
@@ -581,7 +591,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
|
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
|
||||||
*/
|
*/
|
||||||
boolean isAllowedToBeEmbeddedInTrustedMode() {
|
boolean isAllowedToBeEmbeddedInTrustedMode() {
|
||||||
return forAllActivities(this::isAllowedToEmbedActivityInTrustedMode);
|
final Predicate<ActivityRecord> callback = this::isAllowedToEmbedActivityInTrustedMode;
|
||||||
|
return forAllActivities(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import java.util.WeakHashMap;
|
|||||||
*/
|
*/
|
||||||
public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerController.Stub {
|
public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerController.Stub {
|
||||||
private static final String TAG = "TaskFragmentOrganizerController";
|
private static final String TAG = "TaskFragmentOrganizerController";
|
||||||
|
private static final long TEMPORARY_ACTIVITY_TOKEN_TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
private final ActivityTaskManagerService mAtmService;
|
private final ActivityTaskManagerService mAtmService;
|
||||||
private final WindowManagerGlobalLock mGlobalLock;
|
private final WindowManagerGlobalLock mGlobalLock;
|
||||||
@@ -78,10 +79,14 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
|
private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
|
||||||
private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
|
private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
|
||||||
private final ITaskFragmentOrganizer mOrganizer;
|
private final ITaskFragmentOrganizer mOrganizer;
|
||||||
|
private final int mOrganizerPid;
|
||||||
|
private final int mOrganizerUid;
|
||||||
private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
|
private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
|
||||||
new WeakHashMap<>();
|
new WeakHashMap<>();
|
||||||
private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
|
private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
|
||||||
new WeakHashMap<>();
|
new WeakHashMap<>();
|
||||||
|
private final Map<IBinder, ActivityRecord> mTemporaryActivityTokens =
|
||||||
|
new WeakHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map from Task Id to {@link RemoteAnimationDefinition}.
|
* Map from Task Id to {@link RemoteAnimationDefinition}.
|
||||||
@@ -91,8 +96,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
private final SparseArray<RemoteAnimationDefinition> mRemoteAnimationDefinitions =
|
private final SparseArray<RemoteAnimationDefinition> mRemoteAnimationDefinitions =
|
||||||
new SparseArray<>();
|
new SparseArray<>();
|
||||||
|
|
||||||
TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer) {
|
TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer, int pid, int uid) {
|
||||||
mOrganizer = organizer;
|
mOrganizer = organizer;
|
||||||
|
mOrganizerPid = pid;
|
||||||
|
mOrganizerUid = uid;
|
||||||
try {
|
try {
|
||||||
mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
|
mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -134,23 +141,23 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
|
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
|
void onTaskFragmentAppeared(TaskFragment tf) {
|
||||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
|
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
|
||||||
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
|
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
|
||||||
try {
|
try {
|
||||||
organizer.onTaskFragmentAppeared(info);
|
mOrganizer.onTaskFragmentAppeared(info);
|
||||||
mLastSentTaskFragmentInfos.put(tf, info);
|
mLastSentTaskFragmentInfos.put(tf, info);
|
||||||
tf.mTaskFragmentAppearedSent = true;
|
tf.mTaskFragmentAppearedSent = true;
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
|
Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
|
||||||
}
|
}
|
||||||
onTaskFragmentParentInfoChanged(organizer, tf);
|
onTaskFragmentParentInfoChanged(tf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment tf) {
|
void onTaskFragmentVanished(TaskFragment tf) {
|
||||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
|
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
|
||||||
try {
|
try {
|
||||||
organizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
|
mOrganizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
|
Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
|
||||||
}
|
}
|
||||||
@@ -159,10 +166,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
mLastSentTaskFragmentParentConfigs.remove(tf);
|
mLastSentTaskFragmentParentConfigs.remove(tf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
|
void onTaskFragmentInfoChanged(TaskFragment tf) {
|
||||||
// Parent config may have changed. The controller will check if there is any important
|
// Parent config may have changed. The controller will check if there is any important
|
||||||
// config change for the organizer.
|
// config change for the organizer.
|
||||||
onTaskFragmentParentInfoChanged(organizer, tf);
|
onTaskFragmentParentInfoChanged(tf);
|
||||||
|
|
||||||
// Check if the info is different from the last reported info.
|
// Check if the info is different from the last reported info.
|
||||||
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
|
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
|
||||||
@@ -174,14 +181,14 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
|
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
|
||||||
tf.getName());
|
tf.getName());
|
||||||
try {
|
try {
|
||||||
organizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo());
|
mOrganizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo());
|
||||||
mLastSentTaskFragmentInfos.put(tf, info);
|
mLastSentTaskFragmentInfos.put(tf, info);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
|
Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
|
void onTaskFragmentParentInfoChanged(TaskFragment tf) {
|
||||||
// Check if the parent info is different from the last reported parent info.
|
// Check if the parent info is different from the last reported parent info.
|
||||||
if (tf.getParent() == null || tf.getParent().asTask() == null) {
|
if (tf.getParent() == null || tf.getParent().asTask() == null) {
|
||||||
mLastSentTaskFragmentParentConfigs.remove(tf);
|
mLastSentTaskFragmentParentConfigs.remove(tf);
|
||||||
@@ -197,30 +204,86 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
"TaskFragment parent info changed name=%s parentTaskId=%d",
|
"TaskFragment parent info changed name=%s parentTaskId=%d",
|
||||||
tf.getName(), parent.mTaskId);
|
tf.getName(), parent.mTaskId);
|
||||||
try {
|
try {
|
||||||
organizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
|
mOrganizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
|
||||||
mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
|
mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
|
Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
|
void onTaskFragmentError(IBinder errorCallbackToken, Throwable exception) {
|
||||||
Throwable exception) {
|
|
||||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
|
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
|
||||||
"Sending TaskFragment error exception=%s", exception.toString());
|
"Sending TaskFragment error exception=%s", exception.toString());
|
||||||
final Bundle exceptionBundle = putExceptionInBundle(exception);
|
final Bundle exceptionBundle = putExceptionInBundle(exception);
|
||||||
try {
|
try {
|
||||||
organizer.onTaskFragmentError(errorCallbackToken, exceptionBundle);
|
mOrganizer.onTaskFragmentError(errorCallbackToken, exceptionBundle);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
|
Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onActivityReparentToTask(ActivityRecord activity) {
|
||||||
|
if (activity.finishing) {
|
||||||
|
Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Task task = activity.getTask();
|
||||||
|
if (task == null || task.effectiveUid != mOrganizerUid) {
|
||||||
|
Slog.d(TAG, "Reparent activity=" + activity.token
|
||||||
|
+ " is not in a task belong to the organizer app.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!task.isAllowedToEmbedActivity(activity, mOrganizerUid)) {
|
||||||
|
Slog.d(TAG, "Reparent activity=" + activity.token
|
||||||
|
+ " is not allowed to be embedded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final IBinder activityToken;
|
||||||
|
if (activity.getPid() == mOrganizerPid) {
|
||||||
|
// We only pass the actual token if the activity belongs to the organizer process.
|
||||||
|
activityToken = activity.token;
|
||||||
|
} else {
|
||||||
|
// For security, we can't pass the actual token if the activity belongs to a
|
||||||
|
// different process. In this case, we will pass a temporary token that organizer
|
||||||
|
// can use to reparent through WindowContainerTransaction.
|
||||||
|
activityToken = new Binder("TemporaryActivityToken");
|
||||||
|
mTemporaryActivityTokens.put(activityToken, activity);
|
||||||
|
final Runnable timeout = () -> {
|
||||||
|
synchronized (mGlobalLock) {
|
||||||
|
mTemporaryActivityTokens.remove(activityToken);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mAtmService.mWindowManager.mH.postDelayed(timeout,
|
||||||
|
TEMPORARY_ACTIVITY_TOKEN_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
|
||||||
|
activity.token, task.mTaskId);
|
||||||
|
try {
|
||||||
|
mOrganizer.onActivityReparentToTask(task.mTaskId, activity.intent, activityToken);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.d(TAG, "Exception sending onActivityReparentToTask callback", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ActivityRecord getReparentActivityFromTemporaryToken(
|
||||||
|
@Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder activityToken) {
|
||||||
|
if (organizer == null || activityToken == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final TaskFragmentOrganizerState state = mTaskFragmentOrganizerState.get(
|
||||||
|
organizer.asBinder());
|
||||||
|
return state != null
|
||||||
|
? state.mTemporaryActivityTokens.remove(activityToken)
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerOrganizer(ITaskFragmentOrganizer organizer) {
|
public void registerOrganizer(ITaskFragmentOrganizer organizer) {
|
||||||
final int pid = Binder.getCallingPid();
|
final int pid = Binder.getCallingPid();
|
||||||
final long uid = Binder.getCallingUid();
|
final int uid = Binder.getCallingUid();
|
||||||
synchronized (mGlobalLock) {
|
synchronized (mGlobalLock) {
|
||||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
|
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
|
||||||
"Register task fragment organizer=%s uid=%d pid=%d",
|
"Register task fragment organizer=%s uid=%d pid=%d",
|
||||||
@@ -230,7 +293,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
"Replacing existing organizer currently unsupported");
|
"Replacing existing organizer currently unsupported");
|
||||||
}
|
}
|
||||||
mTaskFragmentOrganizerState.put(organizer.asBinder(),
|
mTaskFragmentOrganizerState.put(organizer.asBinder(),
|
||||||
new TaskFragmentOrganizerState(organizer));
|
new TaskFragmentOrganizerState(organizer, pid, uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,8 +384,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
|
PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
|
||||||
PendingTaskFragmentEvent.EVENT_APPEARED);
|
PendingTaskFragmentEvent.EVENT_APPEARED);
|
||||||
if (pendingEvent == null) {
|
if (pendingEvent == null) {
|
||||||
pendingEvent = new PendingTaskFragmentEvent(taskFragment, organizer,
|
pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||||
PendingTaskFragmentEvent.EVENT_APPEARED);
|
PendingTaskFragmentEvent.EVENT_APPEARED, organizer)
|
||||||
|
.setTaskFragment(taskFragment)
|
||||||
|
.build();
|
||||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +412,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
}
|
}
|
||||||
PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
|
PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
|
||||||
if (pendingEvent == null) {
|
if (pendingEvent == null) {
|
||||||
pendingEvent = new PendingTaskFragmentEvent(taskFragment, organizer, eventType);
|
pendingEvent = new PendingTaskFragmentEvent.Builder(eventType, organizer)
|
||||||
|
.setTaskFragment(taskFragment)
|
||||||
|
.build();
|
||||||
} else {
|
} else {
|
||||||
if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
|
if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
|
||||||
// Skipped the info changed event if vanished event is pending.
|
// Skipped the info changed event if vanished event is pending.
|
||||||
@@ -374,8 +441,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
if (!taskFragment.mTaskFragmentAppearedSent) {
|
if (!taskFragment.mTaskFragmentAppearedSent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(taskFragment,
|
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||||
organizer, PendingTaskFragmentEvent.EVENT_VANISHED);
|
PendingTaskFragmentEvent.EVENT_VANISHED, organizer)
|
||||||
|
.setTaskFragment(taskFragment)
|
||||||
|
.build();
|
||||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||||
state.removeTaskFragment(taskFragment);
|
state.removeTaskFragment(taskFragment);
|
||||||
}
|
}
|
||||||
@@ -384,8 +453,43 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
Throwable exception) {
|
Throwable exception) {
|
||||||
validateAndGetState(organizer);
|
validateAndGetState(organizer);
|
||||||
Slog.w(TAG, "onTaskFragmentError ", exception);
|
Slog.w(TAG, "onTaskFragmentError ", exception);
|
||||||
PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(organizer,
|
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||||
errorCallbackToken, exception, PendingTaskFragmentEvent.EVENT_ERROR);
|
PendingTaskFragmentEvent.EVENT_ERROR, organizer)
|
||||||
|
.setErrorCallbackToken(errorCallbackToken)
|
||||||
|
.setException(exception)
|
||||||
|
.build();
|
||||||
|
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onActivityReparentToTask(ActivityRecord activity) {
|
||||||
|
final ITaskFragmentOrganizer organizer;
|
||||||
|
if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
|
||||||
|
// If the activity is previously embedded in an organized TaskFragment.
|
||||||
|
organizer = activity.mLastTaskFragmentOrganizerBeforePip;
|
||||||
|
} else {
|
||||||
|
// Find the topmost TaskFragmentOrganizer.
|
||||||
|
final Task task = activity.getTask();
|
||||||
|
final TaskFragment[] organizedTf = new TaskFragment[1];
|
||||||
|
task.forAllLeafTaskFragments(tf -> {
|
||||||
|
if (tf.isOrganizedTaskFragment()) {
|
||||||
|
organizedTf[0] = tf;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (organizedTf[0] == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
organizer = organizedTf[0].getTaskFragmentOrganizer();
|
||||||
|
}
|
||||||
|
if (!mTaskFragmentOrganizerState.containsKey(organizer.asBinder())) {
|
||||||
|
Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||||
|
PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK, organizer)
|
||||||
|
.setActivity(activity)
|
||||||
|
.build();
|
||||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,13 +526,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
static final int EVENT_INFO_CHANGED = 2;
|
static final int EVENT_INFO_CHANGED = 2;
|
||||||
static final int EVENT_PARENT_INFO_CHANGED = 3;
|
static final int EVENT_PARENT_INFO_CHANGED = 3;
|
||||||
static final int EVENT_ERROR = 4;
|
static final int EVENT_ERROR = 4;
|
||||||
|
static final int EVENT_ACTIVITY_REPARENT_TO_TASK = 5;
|
||||||
|
|
||||||
@IntDef(prefix = "EVENT_", value = {
|
@IntDef(prefix = "EVENT_", value = {
|
||||||
EVENT_APPEARED,
|
EVENT_APPEARED,
|
||||||
EVENT_VANISHED,
|
EVENT_VANISHED,
|
||||||
EVENT_INFO_CHANGED,
|
EVENT_INFO_CHANGED,
|
||||||
EVENT_PARENT_INFO_CHANGED,
|
EVENT_PARENT_INFO_CHANGED,
|
||||||
EVENT_ERROR
|
EVENT_ERROR,
|
||||||
|
EVENT_ACTIVITY_REPARENT_TO_TASK
|
||||||
})
|
})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface EventType {}
|
public @interface EventType {}
|
||||||
@@ -436,34 +542,30 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
@EventType
|
@EventType
|
||||||
private final int mEventType;
|
private final int mEventType;
|
||||||
private final ITaskFragmentOrganizer mTaskFragmentOrg;
|
private final ITaskFragmentOrganizer mTaskFragmentOrg;
|
||||||
|
@Nullable
|
||||||
private final TaskFragment mTaskFragment;
|
private final TaskFragment mTaskFragment;
|
||||||
private final IBinder mErrorCallback;
|
@Nullable
|
||||||
|
private final IBinder mErrorCallbackToken;
|
||||||
|
@Nullable
|
||||||
private final Throwable mException;
|
private final Throwable mException;
|
||||||
|
@Nullable
|
||||||
|
private final ActivityRecord mActivity;
|
||||||
// Set when the event is deferred due to the host task is invisible. The defer time will
|
// Set when the event is deferred due to the host task is invisible. The defer time will
|
||||||
// be the last active time of the host task.
|
// be the last active time of the host task.
|
||||||
private long mDeferTime;
|
private long mDeferTime;
|
||||||
|
|
||||||
private PendingTaskFragmentEvent(TaskFragment taskFragment,
|
private PendingTaskFragmentEvent(@EventType int eventType,
|
||||||
ITaskFragmentOrganizer taskFragmentOrg, @EventType int eventType) {
|
ITaskFragmentOrganizer taskFragmentOrg,
|
||||||
this(taskFragment, taskFragmentOrg, null /* errorCallback */,
|
@Nullable TaskFragment taskFragment,
|
||||||
null /* exception */, eventType);
|
@Nullable IBinder errorCallbackToken,
|
||||||
|
@Nullable Throwable exception,
|
||||||
}
|
@Nullable ActivityRecord activity) {
|
||||||
|
|
||||||
private PendingTaskFragmentEvent(ITaskFragmentOrganizer taskFragmentOrg,
|
|
||||||
IBinder errorCallback, Throwable exception, @EventType int eventType) {
|
|
||||||
this(null /* taskFragment */, taskFragmentOrg, errorCallback, exception,
|
|
||||||
eventType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PendingTaskFragmentEvent(TaskFragment taskFragment,
|
|
||||||
ITaskFragmentOrganizer taskFragmentOrg, IBinder errorCallback, Throwable exception,
|
|
||||||
@EventType int eventType) {
|
|
||||||
mTaskFragment = taskFragment;
|
|
||||||
mTaskFragmentOrg = taskFragmentOrg;
|
|
||||||
mErrorCallback = errorCallback;
|
|
||||||
mException = exception;
|
|
||||||
mEventType = eventType;
|
mEventType = eventType;
|
||||||
|
mTaskFragmentOrg = taskFragmentOrg;
|
||||||
|
mTaskFragment = taskFragment;
|
||||||
|
mErrorCallbackToken = errorCallbackToken;
|
||||||
|
mException = exception;
|
||||||
|
mActivity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -481,6 +583,50 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Builder {
|
||||||
|
@EventType
|
||||||
|
private final int mEventType;
|
||||||
|
private final ITaskFragmentOrganizer mTaskFragmentOrg;
|
||||||
|
@Nullable
|
||||||
|
private TaskFragment mTaskFragment;
|
||||||
|
@Nullable
|
||||||
|
private IBinder mErrorCallbackToken;
|
||||||
|
@Nullable
|
||||||
|
private Throwable mException;
|
||||||
|
@Nullable
|
||||||
|
private ActivityRecord mActivity;
|
||||||
|
|
||||||
|
Builder(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg) {
|
||||||
|
mEventType = eventType;
|
||||||
|
mTaskFragmentOrg = taskFragmentOrg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder setTaskFragment(@Nullable TaskFragment taskFragment) {
|
||||||
|
mTaskFragment = taskFragment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
|
||||||
|
mErrorCallbackToken = errorCallbackToken;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder setException(@Nullable Throwable exception) {
|
||||||
|
mException = exception;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder setActivity(@Nullable ActivityRecord activity) {
|
||||||
|
mActivity = activity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingTaskFragmentEvent build() {
|
||||||
|
return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
|
||||||
|
mErrorCallbackToken, mException, mActivity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -596,20 +742,22 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
|||||||
}
|
}
|
||||||
switch (event.mEventType) {
|
switch (event.mEventType) {
|
||||||
case PendingTaskFragmentEvent.EVENT_APPEARED:
|
case PendingTaskFragmentEvent.EVENT_APPEARED:
|
||||||
state.onTaskFragmentAppeared(taskFragmentOrg, taskFragment);
|
state.onTaskFragmentAppeared(taskFragment);
|
||||||
break;
|
break;
|
||||||
case PendingTaskFragmentEvent.EVENT_VANISHED:
|
case PendingTaskFragmentEvent.EVENT_VANISHED:
|
||||||
state.onTaskFragmentVanished(taskFragmentOrg, taskFragment);
|
state.onTaskFragmentVanished(taskFragment);
|
||||||
break;
|
break;
|
||||||
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
|
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
|
||||||
state.onTaskFragmentInfoChanged(taskFragmentOrg, taskFragment);
|
state.onTaskFragmentInfoChanged(taskFragment);
|
||||||
break;
|
break;
|
||||||
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
|
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
|
||||||
state.onTaskFragmentParentInfoChanged(taskFragmentOrg, taskFragment);
|
state.onTaskFragmentParentInfoChanged(taskFragment);
|
||||||
break;
|
break;
|
||||||
case PendingTaskFragmentEvent.EVENT_ERROR:
|
case PendingTaskFragmentEvent.EVENT_ERROR:
|
||||||
state.onTaskFragmentError(taskFragmentOrg, event.mErrorCallback,
|
state.onTaskFragmentError(event.mErrorCallbackToken, event.mException);
|
||||||
event.mException);
|
break;
|
||||||
|
case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
|
||||||
|
state.onActivityReparentToTask(event.mActivity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -705,7 +705,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
}
|
}
|
||||||
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
|
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
|
||||||
final IBinder fragmentToken = hop.getNewParent();
|
final IBinder fragmentToken = hop.getNewParent();
|
||||||
final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
|
final IBinder activityToken = hop.getContainer();
|
||||||
|
ActivityRecord activity = ActivityRecord.forTokenLocked(activityToken);
|
||||||
|
if (activity == null) {
|
||||||
|
// The token may be a temporary token if the activity doesn't belong to
|
||||||
|
// the organizer process.
|
||||||
|
activity = mTaskFragmentOrganizerController
|
||||||
|
.getReparentActivityFromTemporaryToken(organizer, activityToken);
|
||||||
|
}
|
||||||
final TaskFragment parent = mLaunchTaskFragments.get(fragmentToken);
|
final TaskFragment parent = mLaunchTaskFragments.get(fragmentToken);
|
||||||
if (parent == null || activity == null) {
|
if (parent == null || activity == null) {
|
||||||
final Throwable exception = new IllegalArgumentException(
|
final Throwable exception = new IllegalArgumentException(
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import static com.android.server.wm.testing.Assert.assertThrows;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -38,6 +39,7 @@ import static org.mockito.Mockito.clearInvocations;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -63,6 +65,7 @@ import androidx.test.filters.SmallTest;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build/Install/Run:
|
* Build/Install/Run:
|
||||||
@@ -222,6 +225,85 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
|||||||
verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
|
verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnActivityReparentToTask_activityInOrganizerProcess_useActivityToken() {
|
||||||
|
// Make sure the activity pid/uid is the same as the organizer caller.
|
||||||
|
final int pid = Binder.getCallingPid();
|
||||||
|
final int uid = Binder.getCallingUid();
|
||||||
|
mController.registerOrganizer(mIOrganizer);
|
||||||
|
final ActivityRecord activity = createActivityRecord(mDisplayContent);
|
||||||
|
final Task task = activity.getTask();
|
||||||
|
activity.info.applicationInfo.uid = uid;
|
||||||
|
doReturn(pid).when(activity).getPid();
|
||||||
|
task.effectiveUid = uid;
|
||||||
|
|
||||||
|
// No need to notify organizer if it is not embedded.
|
||||||
|
mController.onActivityReparentToTask(activity);
|
||||||
|
mController.dispatchPendingEvents();
|
||||||
|
|
||||||
|
verify(mOrganizer, never()).onActivityReparentToTask(anyInt(), any(), any());
|
||||||
|
|
||||||
|
// Notify organizer if it was embedded before entered Pip.
|
||||||
|
activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer;
|
||||||
|
mController.onActivityReparentToTask(activity);
|
||||||
|
mController.dispatchPendingEvents();
|
||||||
|
|
||||||
|
verify(mOrganizer).onActivityReparentToTask(task.mTaskId, activity.intent, activity.token);
|
||||||
|
|
||||||
|
// Notify organizer if there is any embedded in the Task.
|
||||||
|
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||||
|
.setParentTask(task)
|
||||||
|
.setOrganizer(mOrganizer)
|
||||||
|
.build();
|
||||||
|
taskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid,
|
||||||
|
DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
|
||||||
|
activity.reparent(taskFragment, POSITION_TOP);
|
||||||
|
activity.mLastTaskFragmentOrganizerBeforePip = null;
|
||||||
|
mController.onActivityReparentToTask(activity);
|
||||||
|
mController.dispatchPendingEvents();
|
||||||
|
|
||||||
|
verify(mOrganizer, times(2))
|
||||||
|
.onActivityReparentToTask(task.mTaskId, activity.intent, activity.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnActivityReparentToTask_activityNotInOrganizerProcess_useTemporaryToken() {
|
||||||
|
final int pid = Binder.getCallingPid();
|
||||||
|
final int uid = Binder.getCallingUid();
|
||||||
|
mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid,
|
||||||
|
DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
|
||||||
|
mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
|
||||||
|
mController.registerOrganizer(mIOrganizer);
|
||||||
|
mOrganizer.applyTransaction(mTransaction);
|
||||||
|
final Task task = createTask(mDisplayContent);
|
||||||
|
task.addChild(mTaskFragment, POSITION_TOP);
|
||||||
|
final ActivityRecord activity = createActivityRecord(task);
|
||||||
|
|
||||||
|
// Make sure the activity belongs to the same app, but it is in a different pid.
|
||||||
|
activity.info.applicationInfo.uid = uid;
|
||||||
|
doReturn(pid + 1).when(activity).getPid();
|
||||||
|
task.effectiveUid = uid;
|
||||||
|
final ArgumentCaptor<IBinder> token = ArgumentCaptor.forClass(IBinder.class);
|
||||||
|
|
||||||
|
// Notify organizer if it was embedded before entered Pip.
|
||||||
|
// Create a temporary token since the activity doesn't belong to the same process.
|
||||||
|
activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer;
|
||||||
|
mController.onActivityReparentToTask(activity);
|
||||||
|
mController.dispatchPendingEvents();
|
||||||
|
|
||||||
|
// Allow organizer to reparent activity in other process using the temporary token.
|
||||||
|
verify(mOrganizer).onActivityReparentToTask(eq(task.mTaskId), eq(activity.intent),
|
||||||
|
token.capture());
|
||||||
|
final IBinder temporaryToken = token.getValue();
|
||||||
|
assertNotEquals(activity.token, temporaryToken);
|
||||||
|
mTransaction.reparentActivityToTaskFragment(mFragmentToken, temporaryToken);
|
||||||
|
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
|
||||||
|
|
||||||
|
assertEquals(mTaskFragment, activity.getTaskFragment());
|
||||||
|
// The temporary token can only be used once.
|
||||||
|
assertNull(mController.getReparentActivityFromTemporaryToken(mIOrganizer, temporaryToken));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegisterRemoteAnimations() {
|
public void testRegisterRemoteAnimations() {
|
||||||
mController.registerOrganizer(mIOrganizer);
|
mController.registerOrganizer(mIOrganizer);
|
||||||
|
|||||||
@@ -28,9 +28,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
|
|||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||||
import static com.android.server.wm.ActivityRecord.State.RESUMED;
|
import static com.android.server.wm.ActivityRecord.State.RESUMED;
|
||||||
|
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
@@ -64,6 +66,7 @@ import org.mockito.MockitoAnnotations;
|
|||||||
public class TaskFragmentTest extends WindowTestsBase {
|
public class TaskFragmentTest extends WindowTestsBase {
|
||||||
|
|
||||||
private TaskFragmentOrganizer mOrganizer;
|
private TaskFragmentOrganizer mOrganizer;
|
||||||
|
private ITaskFragmentOrganizer mIOrganizer;
|
||||||
private TaskFragment mTaskFragment;
|
private TaskFragment mTaskFragment;
|
||||||
private SurfaceControl mLeash;
|
private SurfaceControl mLeash;
|
||||||
@Mock
|
@Mock
|
||||||
@@ -73,10 +76,10 @@ public class TaskFragmentTest extends WindowTestsBase {
|
|||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
|
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
|
||||||
final ITaskFragmentOrganizer iOrganizer =
|
mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken()
|
||||||
ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken().asBinder());
|
.asBinder());
|
||||||
mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController
|
mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController
|
||||||
.registerOrganizer(iOrganizer);
|
.registerOrganizer(mIOrganizer);
|
||||||
mTaskFragment = new TaskFragmentBuilder(mAtm)
|
mTaskFragment = new TaskFragmentBuilder(mAtm)
|
||||||
.setCreateParentTask()
|
.setCreateParentTask()
|
||||||
.setOrganizer(mOrganizer)
|
.setOrganizer(mOrganizer)
|
||||||
@@ -242,6 +245,8 @@ public class TaskFragmentTest extends WindowTestsBase {
|
|||||||
assertEquals(taskBounds, taskFragment.getBounds());
|
assertEquals(taskBounds, taskFragment.getBounds());
|
||||||
assertEquals(taskBounds, activity.getBounds());
|
assertEquals(taskBounds, activity.getBounds());
|
||||||
assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
|
assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
|
||||||
|
// Because the whole Task is entering PiP, no need to record for future reparent.
|
||||||
|
assertNull(activity.mLastTaskFragmentOrganizerBeforePip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -280,6 +285,38 @@ public class TaskFragmentTest extends WindowTestsBase {
|
|||||||
assertTrue(task.isVisibleRequested());
|
assertTrue(task.isVisibleRequested());
|
||||||
verify(mAtm.mTaskFragmentOrganizerController)
|
verify(mAtm.mTaskFragmentOrganizerController)
|
||||||
.dispatchPendingInfoChangedEvent(taskFragment0);
|
.dispatchPendingInfoChangedEvent(taskFragment0);
|
||||||
|
// Make sure the organizer is recorded so that it can be reused when the activity is
|
||||||
|
// reparented back on exiting PiP.
|
||||||
|
assertEquals(mIOrganizer, activity0.mLastTaskFragmentOrganizerBeforePip);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmbeddedActivityExitPip_notifyOrganizer() {
|
||||||
|
final Task task = createTask(mDisplayContent);
|
||||||
|
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||||
|
.setParentTask(task)
|
||||||
|
.setOrganizer(mOrganizer)
|
||||||
|
.setFragmentToken(new Binder())
|
||||||
|
.createActivityCount(1)
|
||||||
|
.build();
|
||||||
|
new TaskFragmentBuilder(mAtm)
|
||||||
|
.setParentTask(task)
|
||||||
|
.setOrganizer(mOrganizer)
|
||||||
|
.setFragmentToken(new Binder())
|
||||||
|
.createActivityCount(1)
|
||||||
|
.build();
|
||||||
|
final ActivityRecord activity = taskFragment.getTopMostActivity();
|
||||||
|
mRootWindowContainer.moveActivityToPinnedRootTask(activity,
|
||||||
|
null /* launchIntoPipHostActivity */, "test");
|
||||||
|
spyOn(mAtm.mTaskFragmentOrganizerController);
|
||||||
|
assertEquals(mIOrganizer, activity.mLastTaskFragmentOrganizerBeforePip);
|
||||||
|
|
||||||
|
// Move the activity back to its original Task.
|
||||||
|
activity.reparent(task, POSITION_TOP);
|
||||||
|
|
||||||
|
// Notify the organizer about the reparent.
|
||||||
|
verify(mAtm.mTaskFragmentOrganizerController).onActivityReparentToTask(activity);
|
||||||
|
assertNull(activity.mLastTaskFragmentOrganizerBeforePip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user