1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.sysui;
18 
19 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT;
20 
21 import android.os.Build;
22 import android.os.SystemClock;
23 import android.util.Pair;
24 import android.view.SurfaceControl;
25 
26 import androidx.annotation.VisibleForTesting;
27 
28 import com.android.internal.protolog.common.ProtoLog;
29 import com.android.wm.shell.common.ShellExecutor;
30 
31 import java.util.ArrayList;
32 
33 /**
34  * The entry point implementation into the shell for initializing shell internal state.  Classes
35  * which need to initialize on start of the host SysUI should inject an instance of this class and
36  * add an init callback.
37  */
38 public class ShellInit {
39     private static final String TAG = ShellInit.class.getSimpleName();
40 
41     private final ShellExecutor mMainExecutor;
42 
43     // An ordered list of init callbacks to be made once shell is first started
44     private final ArrayList<Pair<String, Runnable>> mInitCallbacks = new ArrayList<>();
45     private boolean mHasInitialized;
46 
47 
ShellInit(ShellExecutor mainExecutor)48     public ShellInit(ShellExecutor mainExecutor) {
49         mMainExecutor = mainExecutor;
50     }
51 
52     /**
53      * Adds a callback to the ordered list of callbacks be made when Shell is first started.  This
54      * can be used in class constructors when dagger is used to ensure that the initialization order
55      * matches the dependency order.
56      *
57      * @param r the callback to be made when Shell is initialized
58      * @param instance used for debugging only
59      */
addInitCallback(Runnable r, T instance)60     public <T extends Object> void addInitCallback(Runnable r, T instance) {
61         if (mHasInitialized) {
62             if (Build.isDebuggable()) {
63                 // All callbacks must be added prior to the Shell being initialized
64                 throw new IllegalArgumentException("Can not add callback after init");
65             }
66             return;
67         }
68         final String className = instance.getClass().getSimpleName();
69         mInitCallbacks.add(new Pair<>(className, r));
70         ProtoLog.v(WM_SHELL_INIT, "Adding init callback for %s", className);
71     }
72 
73     /**
74      * Calls all the init callbacks when the Shell is first starting.
75      */
76     @VisibleForTesting
init()77     public void init() {
78         ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size());
79         SurfaceControl.setDebugUsageAfterRelease(true);
80         // Init in order of registration
81         for (int i = 0; i < mInitCallbacks.size(); i++) {
82             final Pair<String, Runnable> info = mInitCallbacks.get(i);
83             final long t1 = SystemClock.uptimeMillis();
84             info.second.run();
85             final long t2 = SystemClock.uptimeMillis();
86             ProtoLog.v(WM_SHELL_INIT, "\t%s init took %dms", info.first, (t2 - t1));
87         }
88         mInitCallbacks.clear();
89         mHasInitialized = true;
90     }
91 }
92