2/ Add ShellInit callback mechanism
- Keep track of callbacks in ShellInit and deprecate old path (to be removed in a follow up CL) Test: atest ShellInitImplTest Test: atest WMShellUnitTests Test: atest SystemUITests Bug: 238217847 Change-Id: Ic17d488ba8715641c01502bb501ad6876edf1455
This commit is contained in:
@@ -17,7 +17,15 @@
|
||||
package com.android.wm.shell;
|
||||
|
||||
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
|
||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.bubbles.BubbleController;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
@@ -37,10 +45,12 @@ import com.android.wm.shell.transition.Transitions;
|
||||
import com.android.wm.shell.unfold.UnfoldAnimationController;
|
||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The entry point implementation into the shell for initializing shell internal state.
|
||||
* The entry point implementation into the shell for initializing shell internal state. Classes
|
||||
* which need to setup on start should inject an instance of this class and add an init callback.
|
||||
*/
|
||||
public class ShellInitImpl {
|
||||
private static final String TAG = ShellInitImpl.class.getSimpleName();
|
||||
@@ -64,6 +74,9 @@ public class ShellInitImpl {
|
||||
private final Optional<RecentTasksController> mRecentTasks;
|
||||
|
||||
private final InitImpl mImpl = new InitImpl();
|
||||
// An ordered list of init callbacks to be made once shell is first started
|
||||
private final ArrayList<Pair<String, Runnable>> mInitCallbacks = new ArrayList<>();
|
||||
private boolean mHasInitialized;
|
||||
|
||||
public ShellInitImpl(
|
||||
DisplayController displayController,
|
||||
@@ -106,7 +119,7 @@ public class ShellInitImpl {
|
||||
return mImpl;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
private void legacyInit() {
|
||||
// Start listening for display and insets changes
|
||||
mDisplayController.initialize();
|
||||
mDisplayInsetsController.initialize();
|
||||
@@ -153,12 +166,52 @@ public class ShellInitImpl {
|
||||
mKidsModeTaskOrganizer.initialize(mStartingWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a callback to the ordered list of callbacks be made when Shell is first started. This
|
||||
* can be used in class constructors when dagger is used to ensure that the initialization order
|
||||
* matches the dependency order.
|
||||
*/
|
||||
public <T extends Object> void addInitCallback(Runnable r, T instance) {
|
||||
if (mHasInitialized) {
|
||||
if (Build.isDebuggable()) {
|
||||
// All callbacks must be added prior to the Shell being initialized
|
||||
throw new IllegalArgumentException("Can not add callback after init");
|
||||
}
|
||||
return;
|
||||
}
|
||||
final String className = instance.getClass().getSimpleName();
|
||||
mInitCallbacks.add(new Pair<>(className, r));
|
||||
ProtoLog.v(WM_SHELL_INIT, "Adding init callback for %s", className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls all the init callbacks when the Shell is first starting.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void init() {
|
||||
ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size());
|
||||
// Init in order of registration
|
||||
for (int i = 0; i < mInitCallbacks.size(); i++) {
|
||||
final Pair<String, Runnable> info = mInitCallbacks.get(i);
|
||||
final long t1 = SystemClock.uptimeMillis();
|
||||
info.second.run();
|
||||
final long t2 = SystemClock.uptimeMillis();
|
||||
ProtoLog.v(WM_SHELL_INIT, "\t%s took %dms", info.first, (t2 - t1));
|
||||
}
|
||||
mInitCallbacks.clear();
|
||||
|
||||
// TODO: To be removed
|
||||
legacyInit();
|
||||
|
||||
mHasInitialized = true;
|
||||
}
|
||||
|
||||
@ExternalThread
|
||||
private class InitImpl implements ShellInit {
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
mMainExecutor.executeBlocking(() -> ShellInitImpl.this.init());
|
||||
mMainExecutor.executeBlocking(ShellInitImpl.this::init);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Failed to initialize the Shell in 2s", e);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.android.internal.protolog.common.IProtoLogGroup;
|
||||
public enum ShellProtoLogGroup implements IProtoLogGroup {
|
||||
// NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
|
||||
// with those in the framework ProtoLogGroup
|
||||
WM_SHELL_INIT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
|
||||
Consts.TAG_WM_SHELL),
|
||||
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM_SHELL),
|
||||
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
|
||||
@@ -38,8 +40,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
|
||||
"ShellBackPreview"),
|
||||
WM_SHELL_RECENT_TASKS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM_SHELL),
|
||||
WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG,
|
||||
false, Consts.TAG_WM_SHELL),
|
||||
WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM_SHELL),
|
||||
WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||
Consts.TAG_WM_SHELL),
|
||||
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.wm.shell;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.wm.shell.bubbles.BubbleController;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
import com.android.wm.shell.common.DisplayInsetsController;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||
import com.android.wm.shell.freeform.FreeformTaskListener;
|
||||
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
|
||||
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
|
||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
import com.android.wm.shell.startingsurface.StartingWindowController;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
import com.android.wm.shell.unfold.UnfoldAnimationController;
|
||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
public class ShellInitImplTest extends ShellTestCase {
|
||||
|
||||
@Mock private DisplayController mDisplayController;
|
||||
@Mock private DisplayImeController mDisplayImeController;
|
||||
@Mock private DisplayInsetsController mDisplayInsetsController;
|
||||
@Mock private DragAndDropController mDragAndDropController;
|
||||
@Mock private ShellTaskOrganizer mShellTaskOrganizer;
|
||||
@Mock private KidsModeTaskOrganizer mKidsModeTaskOrganizer;
|
||||
@Mock private Optional<BubbleController> mBubblesOptional;
|
||||
@Mock private Optional<SplitScreenController> mSplitScreenOptional;
|
||||
@Mock private Optional<PipTouchHandler> mPipTouchHandlerOptional;
|
||||
@Mock private FullscreenTaskListener mFullscreenTaskListener;
|
||||
@Mock private Optional<UnfoldAnimationController> mUnfoldAnimationController;
|
||||
@Mock private Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler;
|
||||
@Mock private Optional<FreeformTaskListener<?>> mFreeformTaskListenerOptional;
|
||||
@Mock private Optional<RecentTasksController> mRecentTasks;
|
||||
@Mock private Transitions mTransitions;
|
||||
@Mock private StartingWindowController mStartingWindow;
|
||||
@Mock private ShellExecutor mMainExecutor;
|
||||
|
||||
private ShellInitImpl mImpl;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mImpl = new ShellInitImpl(mDisplayController, mDisplayImeController,
|
||||
mDisplayInsetsController, mDragAndDropController, mShellTaskOrganizer,
|
||||
mKidsModeTaskOrganizer, mBubblesOptional, mSplitScreenOptional,
|
||||
mPipTouchHandlerOptional, mFullscreenTaskListener, mUnfoldAnimationController,
|
||||
mUnfoldTransitionHandler, mFreeformTaskListenerOptional, mRecentTasks, mTransitions,
|
||||
mStartingWindow, mMainExecutor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddInitCallbacks_expectCalledInOrder() {
|
||||
ArrayList<Integer> results = new ArrayList<>();
|
||||
mImpl.addInitCallback(() -> {
|
||||
results.add(1);
|
||||
}, new Object());
|
||||
mImpl.addInitCallback(() -> {
|
||||
results.add(2);
|
||||
}, new Object());
|
||||
mImpl.addInitCallback(() -> {
|
||||
results.add(3);
|
||||
}, new Object());
|
||||
mImpl.init();
|
||||
assertTrue(results.get(0) == 1);
|
||||
assertTrue(results.get(1) == 2);
|
||||
assertTrue(results.get(2) == 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoInitCallbacksAfterInit_expectException() {
|
||||
mImpl.init();
|
||||
try {
|
||||
mImpl.addInitCallback(() -> {}, new Object());
|
||||
fail("Expected exception when adding callback after init");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleInit_expectNoOp() {
|
||||
ArrayList<Integer> results = new ArrayList<>();
|
||||
mImpl.addInitCallback(() -> {
|
||||
results.add(1);
|
||||
}, new Object());
|
||||
mImpl.init();
|
||||
assertTrue(results.size() == 1);
|
||||
mImpl.init();
|
||||
assertTrue(results.size() == 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user