Merge "Introduce BLASTSyncEngine"
This commit is contained in:
@@ -31,8 +31,19 @@ interface ITaskOrganizerController {
|
||||
*/
|
||||
void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode);
|
||||
|
||||
/** Apply multiple WindowContainer operations at once. */
|
||||
void applyContainerTransaction(in WindowContainerTransaction t);
|
||||
/**
|
||||
* Apply multiple WindowContainer operations at once.
|
||||
* @param organizer If non-null this transaction will use the synchronization
|
||||
* scheme described in BLASTSyncEngine.java. The SurfaceControl transaction
|
||||
* containing the effects of this WindowContainer transaction will be passed
|
||||
* to the organizers Transaction ready callback. If null the transaction
|
||||
* will apply with non particular synchronization constraints (other than
|
||||
* it will all apply at once).
|
||||
* @return If organizer was non-null returns an ID for the sync operation which will
|
||||
* later be passed to transactionReady. This lets TaskOrganizer implementations
|
||||
* differentiate overlapping sync operations.
|
||||
*/
|
||||
int applyContainerTransaction(in WindowContainerTransaction t, ITaskOrganizer organizer);
|
||||
|
||||
/** Creates a persistent root task in WM for a particular windowing-mode. */
|
||||
ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode);
|
||||
|
||||
122
services/core/java/com/android/server/wm/BLASTSyncEngine.java
Normal file
122
services/core/java/com/android/server/wm/BLASTSyncEngine.java
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.view.ITaskOrganizer;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Utility class for collecting and merging transactions from various sources asynchronously.
|
||||
* For example to use to synchronously resize all the children of a window container
|
||||
* 1. Open a new sync set, and pass the listener that will be invoked
|
||||
* int id startSyncSet(TransactionReadyListener)
|
||||
* the returned ID will be eventually passed to the TransactionReadyListener in combination
|
||||
* with the prepared transaction. You also use it to refer to the operation in future steps.
|
||||
* 2. Ask each child to participate:
|
||||
* addToSyncSet(int id, WindowContainer wc)
|
||||
* if the child thinks it will be affected by a configuration change (a.k.a. has a visible
|
||||
* window in its sub hierarchy, then we will increment a counter of expected callbacks
|
||||
* At this point the containers hierarchy will redirect pendingTransaction and sub hierarchy
|
||||
* updates in to the sync engine.
|
||||
* 3. Apply your configuration changes to the window containers.
|
||||
* 4. Tell the engine that the sync set is ready
|
||||
* setReady(int id)
|
||||
* 5. If there were no sub windows anywhere in the hierarchy to wait on, then
|
||||
* transactionReady is immediately invoked, otherwise all the windows are poked
|
||||
* to redraw and to deliver a buffer to WMS#finishDrawing.
|
||||
* Once all this drawing is complete the combined transaction of all the buffers
|
||||
* and pending transaction hierarchy changes will be delivered to the TransactionReadyListener
|
||||
*/
|
||||
class BLASTSyncEngine {
|
||||
private static final String TAG = "BLASTSyncEngine";
|
||||
|
||||
interface TransactionReadyListener {
|
||||
void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction);
|
||||
};
|
||||
|
||||
// Holds state associated with a single synchronous set of operations.
|
||||
class SyncState implements TransactionReadyListener {
|
||||
int mSyncId;
|
||||
SurfaceControl.Transaction mMergedTransaction;
|
||||
int mRemainingTransactions;
|
||||
TransactionReadyListener mListener;
|
||||
boolean mReady = false;
|
||||
|
||||
private void tryFinish() {
|
||||
if (mRemainingTransactions == 0 && mReady) {
|
||||
mListener.transactionReady(mSyncId, mMergedTransaction);
|
||||
mPendingSyncs.remove(mSyncId);
|
||||
}
|
||||
}
|
||||
|
||||
public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) {
|
||||
mRemainingTransactions--;
|
||||
mMergedTransaction.merge(mergedTransaction);
|
||||
tryFinish();
|
||||
}
|
||||
|
||||
void setReady() {
|
||||
mReady = true;
|
||||
tryFinish();
|
||||
}
|
||||
|
||||
boolean addToSync(WindowContainer wc) {
|
||||
if (wc.prepareForSync(this, mSyncId)) {
|
||||
mRemainingTransactions++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SyncState(TransactionReadyListener l, int id) {
|
||||
mListener = l;
|
||||
mSyncId = id;
|
||||
mMergedTransaction = new SurfaceControl.Transaction();
|
||||
mRemainingTransactions = 0;
|
||||
}
|
||||
};
|
||||
|
||||
int mNextSyncId = 0;
|
||||
|
||||
final HashMap<Integer, SyncState> mPendingSyncs = new HashMap();
|
||||
|
||||
BLASTSyncEngine() {
|
||||
}
|
||||
|
||||
int startSyncSet(TransactionReadyListener listener) {
|
||||
final int id = mNextSyncId++;
|
||||
final SyncState s = new SyncState(listener, id);
|
||||
mPendingSyncs.put(id, s);
|
||||
return id;
|
||||
}
|
||||
|
||||
boolean addToSyncSet(int id, WindowContainer wc) {
|
||||
final SyncState st = mPendingSyncs.get(id);
|
||||
return st.addToSync(wc);
|
||||
}
|
||||
|
||||
// TODO(b/148476626): TIMEOUTS!
|
||||
void setReady(int id) {
|
||||
final SyncState st = mPendingSyncs.get(id);
|
||||
st.setReady();
|
||||
}
|
||||
}
|
||||
@@ -526,7 +526,9 @@ public class DisplayRotation {
|
||||
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
|
||||
mIsWaitingForRemoteRotation = false;
|
||||
mDisplayContent.sendNewConfiguration();
|
||||
mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
|
||||
if (t != null) {
|
||||
mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.view.ITaskOrganizer;
|
||||
import android.view.IWindowContainer;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowContainerTransaction;
|
||||
|
||||
import com.android.internal.util.function.pooled.PooledConsumer;
|
||||
@@ -53,7 +54,8 @@ import java.util.WeakHashMap;
|
||||
* Stores the TaskOrganizers associated with a given windowing mode and
|
||||
* their associated state.
|
||||
*/
|
||||
class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
class TaskOrganizerController extends ITaskOrganizerController.Stub
|
||||
implements BLASTSyncEngine.TransactionReadyListener {
|
||||
private static final String TAG = "TaskOrganizerController";
|
||||
|
||||
/** Flag indicating that an applied transaction may have effected lifecycle */
|
||||
@@ -164,6 +166,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
|
||||
private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
|
||||
|
||||
private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
|
||||
|
||||
final ActivityTaskManagerService mService;
|
||||
|
||||
RunningTaskInfo mTmpTaskInfo;
|
||||
@@ -223,7 +227,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
|
||||
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
|
||||
TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
|
||||
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
|
||||
state.addTask(task);
|
||||
}
|
||||
|
||||
@@ -429,15 +433,35 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyContainerTransaction(WindowContainerTransaction t) {
|
||||
public int applyContainerTransaction(WindowContainerTransaction t, ITaskOrganizer organizer) {
|
||||
enforceStackPermission("applyContainerTransaction()");
|
||||
int syncId = -1;
|
||||
if (t == null) {
|
||||
return;
|
||||
throw new IllegalArgumentException(
|
||||
"Null transaction passed to applyContainerTransaction");
|
||||
}
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
int effects = 0;
|
||||
|
||||
/**
|
||||
* If organizer is non-null we are looking to synchronize this transaction
|
||||
* by collecting all the results in to a SurfaceFlinger transaction and
|
||||
* then delivering that to the given organizers transaction ready callback.
|
||||
* See {@link BLASTSyncEngine} for the details of the operation. But at
|
||||
* a high level we create a sync operation with a given ID and an associated
|
||||
* organizer. Then we notify each WindowContainer in this WindowContainer
|
||||
* transaction that it is participating in a sync operation with that
|
||||
* ID. Once everything is notified we tell the BLASTSyncEngine
|
||||
* "setSyncReady" which means that we have added everything
|
||||
* to the set. At any point after this, all the WindowContainers
|
||||
* will eventually finish applying their changes and notify the
|
||||
* BLASTSyncEngine which will deliver the Transaction to the organizer.
|
||||
*/
|
||||
if (organizer != null) {
|
||||
syncId = startSyncWithOrganizer(organizer);
|
||||
}
|
||||
mService.deferWindowLayout();
|
||||
try {
|
||||
ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
|
||||
@@ -450,11 +474,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
entry.getKey()).getContainer();
|
||||
int containerEffect = applyWindowContainerChange(wc, entry.getValue());
|
||||
effects |= containerEffect;
|
||||
|
||||
// Lifecycle changes will trigger ensureConfig for everything.
|
||||
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
|
||||
&& (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
|
||||
haveConfigChanges.add(wc);
|
||||
}
|
||||
if (syncId >= 0) {
|
||||
mBLASTSyncEngine.addToSyncSet(syncId, wc);
|
||||
}
|
||||
}
|
||||
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
|
||||
// Already calls ensureActivityConfig
|
||||
@@ -475,10 +503,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
} finally {
|
||||
mService.continueWindowLayout();
|
||||
if (syncId >= 0) {
|
||||
setSyncReady(syncId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
return syncId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactionReady(int id, SurfaceControl.Transaction sc) {
|
||||
final ITaskOrganizer organizer = mTaskOrganizersByPendingSyncId.get(id);
|
||||
if (organizer == null) {
|
||||
Slog.e(TAG, "Got transaction complete for unexpected ID");
|
||||
}
|
||||
try {
|
||||
organizer.transactionReady(id, sc);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
|
||||
mTaskOrganizersByPendingSyncId.remove(id);
|
||||
}
|
||||
|
||||
int startSyncWithOrganizer(ITaskOrganizer organizer) {
|
||||
int id = mBLASTSyncEngine.startSyncSet(this);
|
||||
mTaskOrganizersByPendingSyncId.put(id, organizer);
|
||||
return id;
|
||||
}
|
||||
|
||||
void setSyncReady(int id) {
|
||||
mBLASTSyncEngine.setReady(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ import java.util.function.Predicate;
|
||||
* changes are made to this class.
|
||||
*/
|
||||
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
|
||||
implements Comparable<WindowContainer>, Animatable {
|
||||
implements Comparable<WindowContainer>, Animatable,
|
||||
BLASTSyncEngine.TransactionReadyListener {
|
||||
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
|
||||
|
||||
@@ -260,6 +261,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
*/
|
||||
RemoteToken mRemoteToken = null;
|
||||
|
||||
BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
|
||||
SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction();
|
||||
boolean mUsingBLASTSyncTransaction = false;
|
||||
BLASTSyncEngine.TransactionReadyListener mWaitingListener;
|
||||
int mWaitingSyncId;
|
||||
|
||||
WindowContainer(WindowManagerService wms) {
|
||||
mWmService = wms;
|
||||
mPendingTransaction = wms.mTransactionFactory.get();
|
||||
@@ -1837,6 +1844,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
|
||||
@Override
|
||||
public Transaction getPendingTransaction() {
|
||||
if (mUsingBLASTSyncTransaction) {
|
||||
return mBLASTSyncTransaction;
|
||||
}
|
||||
|
||||
final DisplayContent displayContent = getDisplayContent();
|
||||
if (displayContent != null && displayContent != this) {
|
||||
return displayContent.getPendingTransaction();
|
||||
@@ -2316,4 +2327,38 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) {
|
||||
mergedTransaction.merge(mBLASTSyncTransaction);
|
||||
mUsingBLASTSyncTransaction = false;
|
||||
|
||||
mWaitingListener.transactionReady(mWaitingSyncId, mergedTransaction);
|
||||
|
||||
mWaitingListener = null;
|
||||
mWaitingSyncId = -1;
|
||||
}
|
||||
|
||||
boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
|
||||
int waitingId) {
|
||||
boolean willSync = false;
|
||||
if (!isVisible()) {
|
||||
return willSync;
|
||||
}
|
||||
mUsingBLASTSyncTransaction = true;
|
||||
|
||||
int localId = mBLASTSyncEngine.startSyncSet(this);
|
||||
for (int i = 0; i < mChildren.size(); i++) {
|
||||
final WindowContainer child = mChildren.get(i);
|
||||
willSync = mBLASTSyncEngine.addToSyncSet(localId, child) | willSync;
|
||||
}
|
||||
|
||||
// Make sure to set these before we call setReady in case the sync was a no-op
|
||||
mWaitingSyncId = waitingId;
|
||||
mWaitingListener = waitingListener;
|
||||
|
||||
mBLASTSyncEngine.setReady(localId);
|
||||
|
||||
return willSync;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2505,7 +2505,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
WindowState win = windowForClientLocked(session, client, false);
|
||||
ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
|
||||
win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
|
||||
if (win != null && win.mWinAnimator.finishDrawingLocked(postDrawTransaction)) {
|
||||
if (win != null && win.finishDrawing(postDrawTransaction)) {
|
||||
if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
|
||||
win.getDisplayContent().pendingLayoutChanges |=
|
||||
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
|
||||
|
||||
@@ -5689,4 +5689,30 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
SurfaceControl getDeferTransactionBarrier() {
|
||||
return mWinAnimator.getDeferTransactionBarrier();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
|
||||
int waitingId) {
|
||||
// TODO(b/148871522): Support child window
|
||||
mWaitingListener = waitingListener;
|
||||
mWaitingSyncId = waitingId;
|
||||
mUsingBLASTSyncTransaction = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
|
||||
if (!mUsingBLASTSyncTransaction) {
|
||||
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
|
||||
}
|
||||
if (postDrawTransaction == null) {
|
||||
postDrawTransaction = new SurfaceControl.Transaction();
|
||||
}
|
||||
postDrawTransaction.merge(mBLASTSyncTransaction);
|
||||
mWaitingListener.transactionReady(mWaitingSyncId, postDrawTransaction);
|
||||
mUsingBLASTSyncTransaction = false;
|
||||
|
||||
mWaitingSyncId = 0;
|
||||
mWaitingListener = null;
|
||||
return mWinAnimator.finishDrawingLocked(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,16 +26,19 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityManager.StackInfo;
|
||||
@@ -207,7 +210,7 @@ public class TaskOrganizerTests extends WindowTestsBase {
|
||||
WindowContainerTransaction t = new WindowContainerTransaction();
|
||||
Rect newBounds = new Rect(10, 10, 100, 100);
|
||||
t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
|
||||
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
|
||||
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
|
||||
assertEquals(newBounds, task.getBounds());
|
||||
}
|
||||
|
||||
@@ -222,7 +225,7 @@ public class TaskOrganizerTests extends WindowTestsBase {
|
||||
assertEquals(stack.mRemoteToken, info.stackToken);
|
||||
Rect newBounds = new Rect(10, 10, 100, 100);
|
||||
t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
|
||||
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
|
||||
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
|
||||
assertEquals(newBounds, stack.getBounds());
|
||||
}
|
||||
|
||||
@@ -235,7 +238,7 @@ public class TaskOrganizerTests extends WindowTestsBase {
|
||||
WindowContainerTransaction t = new WindowContainerTransaction();
|
||||
assertTrue(task.isFocusable());
|
||||
t.setFocusable(stack.mRemoteToken, false);
|
||||
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
|
||||
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
|
||||
assertFalse(task.isFocusable());
|
||||
}
|
||||
|
||||
@@ -364,4 +367,46 @@ public class TaskOrganizerTests extends WindowTestsBase {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrivialBLASTCallback() throws RemoteException {
|
||||
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
|
||||
final Task task = createTaskInStack(stackController1, 0 /* userId */);
|
||||
final ITaskOrganizer organizer = registerMockOrganizer();
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener transactionListener =
|
||||
mock(BLASTSyncEngine.TransactionReadyListener.class);
|
||||
|
||||
int id = bse.startSyncSet(transactionListener);
|
||||
bse.addToSyncSet(id, task);
|
||||
bse.setReady(id);
|
||||
// Since this task has no windows the sync is trivial and completes immediately.
|
||||
verify(transactionListener)
|
||||
.transactionReady(anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBLASTCallbackWithWindow() {
|
||||
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
|
||||
final Task task = createTaskInStack(stackController1, 0 /* userId */);
|
||||
final ITaskOrganizer organizer = registerMockOrganizer();
|
||||
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener transactionListener =
|
||||
mock(BLASTSyncEngine.TransactionReadyListener.class);
|
||||
|
||||
int id = bse.startSyncSet(transactionListener);
|
||||
bse.addToSyncSet(id, task);
|
||||
bse.setReady(id);
|
||||
// Since we have a window we have to wait for it to draw to finish sync.
|
||||
verify(transactionListener, never())
|
||||
.transactionReady(anyInt(), any());
|
||||
w.finishDrawing(null);
|
||||
verify(transactionListener)
|
||||
.transactionReady(anyInt(), any());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class TaskOrganizerPipTest extends Service {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
|
||||
try {
|
||||
ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct);
|
||||
ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, null);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user