Add sync-group timeout for transition
This allows to use different timeout according to transition type.
Also
- Avoid indeterminate timeout by scheduling for individual windows,
or a new window joins the sync-group lately.
- The standalone sync request from applyWithNextDraw won't be mixed
with the control of sync-group.
Bug: 199836343
Test: atest TransitionTests#testTimeout
Test: setprop persist.debug.shell_transit 1;
Launch an activity or change display orientation while the
UI thread of the activity is no response, the transition
should execute eventually within the timeout e.g. 5s and 2s.
Change-Id: Iafecc01818193504ea3fd393817691e1b5e9f084
This commit is contained in:
@@ -3642,6 +3642,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
if (mPendingRelaunchCount > 0) {
|
||||
mPendingRelaunchCount--;
|
||||
if (mPendingRelaunchCount == 0 && !isClientVisible()) {
|
||||
// Don't count if the client won't report drawn.
|
||||
mRelaunchStartTime = 0;
|
||||
}
|
||||
} else {
|
||||
// Update keyguard flags upon finishing relaunch.
|
||||
checkKeyguardFlagsChanged();
|
||||
|
||||
@@ -20,9 +20,11 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
|
||||
/**
|
||||
@@ -65,6 +67,7 @@ class BLASTSyncEngine {
|
||||
class SyncGroup {
|
||||
final int mSyncId;
|
||||
final TransactionReadyListener mListener;
|
||||
final Runnable mOnTimeout;
|
||||
boolean mReady = false;
|
||||
final ArraySet<WindowContainer> mRootMembers = new ArraySet<>();
|
||||
private SurfaceControl.Transaction mOrphanTransaction = null;
|
||||
@@ -72,6 +75,12 @@ class BLASTSyncEngine {
|
||||
private SyncGroup(TransactionReadyListener listener, int id) {
|
||||
mSyncId = id;
|
||||
mListener = listener;
|
||||
mOnTimeout = () -> {
|
||||
Slog.w(TAG, "Sync group " + mSyncId + " timeout");
|
||||
synchronized (mWm.mGlobalLock) {
|
||||
onTimeout();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,6 +123,7 @@ class BLASTSyncEngine {
|
||||
}
|
||||
mListener.onTransactionReady(mSyncId, merged);
|
||||
mActiveSyncs.remove(mSyncId);
|
||||
mWm.mH.removeCallbacks(mOnTimeout);
|
||||
}
|
||||
|
||||
private void setReady(boolean ready) {
|
||||
@@ -136,6 +146,17 @@ class BLASTSyncEngine {
|
||||
void onCancelSync(WindowContainer wc) {
|
||||
mRootMembers.remove(wc);
|
||||
}
|
||||
|
||||
private void onTimeout() {
|
||||
if (!mActiveSyncs.contains(mSyncId)) return;
|
||||
for (int i = mRootMembers.size() - 1; i >= 0; --i) {
|
||||
final WindowContainer<?> wc = mRootMembers.valueAt(i);
|
||||
if (!wc.isSyncFinished()) {
|
||||
Slog.i(TAG, "Unfinished container: " + wc);
|
||||
}
|
||||
}
|
||||
finishNow();
|
||||
}
|
||||
}
|
||||
|
||||
private final WindowManagerService mWm;
|
||||
@@ -147,13 +168,23 @@ class BLASTSyncEngine {
|
||||
}
|
||||
|
||||
int startSyncSet(TransactionReadyListener listener) {
|
||||
return startSyncSet(listener, WindowState.BLAST_TIMEOUT_DURATION);
|
||||
}
|
||||
|
||||
int startSyncSet(TransactionReadyListener listener, long timeoutMs) {
|
||||
final int id = mNextSyncId++;
|
||||
final SyncGroup s = new SyncGroup(listener, id);
|
||||
mActiveSyncs.put(id, s);
|
||||
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s", id, listener);
|
||||
scheduleTimeout(s, timeoutMs);
|
||||
return id;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void scheduleTimeout(SyncGroup s, long timeoutMs) {
|
||||
mWm.mH.postDelayed(s.mOnTimeout, timeoutMs);
|
||||
}
|
||||
|
||||
void addToSyncSet(int id, WindowContainer wc) {
|
||||
mActiveSyncs.get(id).addToSync(wc);
|
||||
}
|
||||
|
||||
@@ -3449,7 +3449,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Display " + mDisplayId + " info=" + mDisplayInfo + " rootTasks=" + mChildren;
|
||||
return "Display{#" + mDisplayId + " state=" + Display.stateToString(mDisplayInfo.state)
|
||||
+ " size=" + mDisplayInfo.logicalWidth + "x" + mDisplayInfo.logicalHeight
|
||||
+ " " + Surface.rotationToString(mDisplayInfo.rotation) + "}";
|
||||
}
|
||||
|
||||
String getName() {
|
||||
|
||||
@@ -165,13 +165,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
private boolean mNavBarAttachedToApp = false;
|
||||
private int mRecentsDisplayId = INVALID_DISPLAY;
|
||||
|
||||
Transition(@TransitionType int type, @TransitionFlags int flags,
|
||||
Transition(@TransitionType int type, @TransitionFlags int flags, long timeoutMs,
|
||||
TransitionController controller, BLASTSyncEngine syncEngine) {
|
||||
mType = type;
|
||||
mFlags = flags;
|
||||
mController = controller;
|
||||
mSyncEngine = syncEngine;
|
||||
mSyncId = mSyncEngine.startSyncSet(this);
|
||||
mSyncId = mSyncEngine.startSyncSet(this, timeoutMs);
|
||||
}
|
||||
|
||||
void addFlag(int flag) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
|
||||
@@ -55,6 +56,11 @@ import java.util.function.LongConsumer;
|
||||
class TransitionController {
|
||||
private static final String TAG = "TransitionController";
|
||||
|
||||
/** The same as legacy APP_TRANSITION_TIMEOUT_MS. */
|
||||
private static final int DEFAULT_TIMEOUT_MS = 5000;
|
||||
/** Less duration for CHANGE type because it does not involve app startup. */
|
||||
private static final int CHANGE_TIMEOUT_MS = 2000;
|
||||
|
||||
// State constants to line-up with legacy app-transition proto expectations.
|
||||
private static final int LEGACY_STATE_IDLE = 0;
|
||||
private static final int LEGACY_STATE_READY = 1;
|
||||
@@ -121,7 +127,10 @@ class TransitionController {
|
||||
if (mCollectingTransition != null) {
|
||||
throw new IllegalStateException("Simultaneous transitions not supported yet.");
|
||||
}
|
||||
mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
|
||||
// Distinguish change type because the response time is usually expected to be not too long.
|
||||
final long timeoutMs = type == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
|
||||
mCollectingTransition = new Transition(type, flags, timeoutMs, this,
|
||||
mAtm.mWindowManager.mSyncEngine);
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s",
|
||||
mCollectingTransition);
|
||||
dispatchLegacyAppTransitionPending();
|
||||
|
||||
@@ -5396,6 +5396,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
case WINDOW_STATE_BLAST_SYNC_TIMEOUT: {
|
||||
synchronized (mGlobalLock) {
|
||||
final WindowState ws = (WindowState) msg.obj;
|
||||
Slog.i(TAG, "Blast sync timeout: " + ws);
|
||||
ws.immediatelyNotifyBlastSync();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -6041,10 +6041,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
// in WAITING state rather than READY.
|
||||
mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
|
||||
requestRedrawForSync();
|
||||
|
||||
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
|
||||
mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
|
||||
BLAST_TIMEOUT_DURATION);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
public void testTrivialSyncCallback() {
|
||||
TestWindowContainer mockWC = new TestWindowContainer(mWm, false /* waiter */);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -90,7 +90,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
public void testWaitingSyncCallback() {
|
||||
TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -114,7 +114,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
public void testInvisibleSyncCallback() {
|
||||
TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -142,7 +142,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
parentWC.addChild(childWC, POSITION_TOP);
|
||||
parentWC.addChild(childWC2, POSITION_TOP);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -175,7 +175,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */);
|
||||
parentWC.addChild(childWC, POSITION_TOP);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -206,7 +206,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
parentWC.addChild(topChildWC, POSITION_TOP);
|
||||
parentWC.addChild(botChildWC, POSITION_BOTTOM);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -238,7 +238,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
parentWC.addChild(topChildWC, POSITION_TOP);
|
||||
parentWC.addChild(botChildWC, POSITION_BOTTOM);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -273,7 +273,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
parentWC.addChild(topChildWC, POSITION_TOP);
|
||||
nonMemberParentWC.addChild(botChildWC, POSITION_BOTTOM);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
@@ -312,7 +312,7 @@ public class SyncEngineTests extends WindowTestsBase {
|
||||
parentWC.addChild(topChildWC, POSITION_TOP);
|
||||
parentWC.addChild(botChildWC, POSITION_BOTTOM);
|
||||
|
||||
BLASTSyncEngine bse = new BLASTSyncEngine(mWm);
|
||||
final BLASTSyncEngine bse = createTestBLASTSyncEngine();
|
||||
|
||||
BLASTSyncEngine.TransactionReadyListener listener = mock(
|
||||
BLASTSyncEngine.TransactionReadyListener.class);
|
||||
|
||||
@@ -21,7 +21,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
||||
@@ -44,6 +43,7 @@ import android.os.IBinder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.ITransitionPlayer;
|
||||
import android.window.TransitionInfo;
|
||||
@@ -53,9 +53,12 @@ import androidx.test.filters.SmallTest;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Build/Install/Run:
|
||||
* atest WmTests:TransitionRecordTests
|
||||
* atest WmTests:TransitionTests
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@@ -64,13 +67,13 @@ public class TransitionTests extends WindowTestsBase {
|
||||
|
||||
private Transition createTestTransition(int transitType) {
|
||||
TransitionController controller = mock(TransitionController.class);
|
||||
BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
|
||||
return new Transition(transitType, 0 /* flags */, controller, sync);
|
||||
final BLASTSyncEngine sync = createTestBLASTSyncEngine();
|
||||
return new Transition(transitType, 0 /* flags */, 0 /* timeoutMs */, controller, sync);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInfo_NewTask() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
|
||||
ArraySet<WindowContainer> participants = transition.mParticipants;
|
||||
|
||||
@@ -88,7 +91,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
closing.mVisibleRequested = false;
|
||||
opening.mVisibleRequested = true;
|
||||
|
||||
int transit = TRANSIT_OLD_TASK_OPEN;
|
||||
final int transit = transition.mType;
|
||||
int flags = 0;
|
||||
|
||||
// Check basic both tasks participating
|
||||
@@ -127,7 +130,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testCreateInfo_NestedTasks() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
|
||||
ArraySet<WindowContainer> participants = transition.mParticipants;
|
||||
|
||||
@@ -152,7 +155,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
opening.mVisibleRequested = true;
|
||||
opening2.mVisibleRequested = true;
|
||||
|
||||
int transit = TRANSIT_OLD_TASK_OPEN;
|
||||
final int transit = transition.mType;
|
||||
int flags = 0;
|
||||
|
||||
// Check full promotion from leaf
|
||||
@@ -177,7 +180,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testCreateInfo_DisplayArea() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
|
||||
ArraySet<WindowContainer> participants = transition.mParticipants;
|
||||
final Task showTask = createTask(mDisplayContent);
|
||||
@@ -199,7 +202,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
showing.mVisibleRequested = true;
|
||||
showing2.mVisibleRequested = true;
|
||||
|
||||
int transit = TRANSIT_OLD_TASK_OPEN;
|
||||
final int transit = transition.mType;
|
||||
int flags = 0;
|
||||
|
||||
// Check promotion to DisplayArea
|
||||
@@ -228,7 +231,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testCreateInfo_existenceChange() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
|
||||
final Task openTask = createTask(mDisplayContent);
|
||||
final ActivityRecord opening = createActivityRecord(openTask);
|
||||
@@ -258,7 +261,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testCreateInfo_ordering() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
// pick some number with a high enough chance of being out-of-order when added to set.
|
||||
final int taskCount = 6;
|
||||
|
||||
@@ -294,7 +297,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testCreateInfo_wallpaper() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
// pick some number with a high enough chance of being out-of-order when added to set.
|
||||
final int taskCount = 4;
|
||||
final int showWallpaperTask = 2;
|
||||
@@ -345,7 +348,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testTargets_noIntermediatesToWallpaper() {
|
||||
final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
|
||||
final Transition transition = createTestTransition(TRANSIT_OPEN);
|
||||
|
||||
final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
|
||||
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
|
||||
@@ -419,7 +422,7 @@ public class TransitionTests extends WindowTestsBase {
|
||||
openInOpen.mVisibleRequested = true;
|
||||
openInChange.mVisibleRequested = true;
|
||||
|
||||
int transit = TRANSIT_OLD_TASK_OPEN;
|
||||
final int transit = transition.mType;
|
||||
int flags = 0;
|
||||
|
||||
// Check full promotion from leaf
|
||||
@@ -449,6 +452,22 @@ public class TransitionTests extends WindowTestsBase {
|
||||
info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeout() {
|
||||
final TransitionController controller = new TransitionController(mAtm,
|
||||
mock(TaskSnapshotController.class));
|
||||
final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
// When the timeout is reached, it will finish the sync-group and notify transaction ready.
|
||||
new Transition(TRANSIT_OPEN, 0 /* flags */, 10 /* timeoutMs */, controller, sync) {
|
||||
@Override
|
||||
public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntermediateVisibility() {
|
||||
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
|
||||
|
||||
@@ -789,6 +789,15 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
};
|
||||
}
|
||||
|
||||
BLASTSyncEngine createTestBLASTSyncEngine() {
|
||||
return new BLASTSyncEngine(mWm) {
|
||||
@Override
|
||||
void scheduleTimeout(SyncGroup s, long timeoutMs) {
|
||||
// Disable timeout.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoids rotating screen disturbed by some conditions. It is usually used for the default
|
||||
* display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions).
|
||||
|
||||
Reference in New Issue
Block a user