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.systemui
18 
19 import android.util.Log
20 import com.android.internal.annotations.GuardedBy
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dump.DumpManager
23 import java.io.PrintWriter
24 import java.lang.ref.WeakReference
25 import java.util.concurrent.atomic.AtomicBoolean
26 import javax.inject.Inject
27 
28 /**
29  * Caches whether the device has reached [SystemService.PHASE_BOOT_COMPLETED].
30  *
31  * This class is constructed and set by [SystemUIApplication] and will notify all listeners when
32  * boot is completed.
33  */
34 @SysUISingleton
35 class BootCompleteCacheImpl @Inject constructor(dumpManager: DumpManager) :
36         BootCompleteCache, Dumpable {
37 
38     companion object {
39         private const val TAG = "BootCompleteCacheImpl"
40         private const val DEBUG = false
41     }
42 
43     init {
44         dumpManager.registerDumpable(TAG, this)
45     }
46 
47     @GuardedBy("listeners")
48     private val listeners = mutableListOf<WeakReference<BootCompleteCache.BootCompleteListener>>()
49     private val bootComplete = AtomicBoolean(false)
50 
51     /**
52      * Provides the current boot state of the system as determined by [SystemUIApplication].
53      * @return `true` if the system has reached [SystemService.PHASE_BOOT_COMPLETED]
54      */
isBootCompletenull55     override fun isBootComplete(): Boolean = bootComplete.get()
56 
57     /**
58      * Indicates to this object that boot is complete. Subsequent calls to this function will have
59      * no effect.
60      */
61     fun setBootComplete() {
62         if (bootComplete.compareAndSet(false, true)) {
63             if (DEBUG) Log.d(TAG, "Boot complete set")
64             synchronized(listeners) {
65                 listeners.forEach {
66                     it.get()?.onBootComplete()
67                 }
68                 listeners.clear()
69             }
70         }
71     }
72 
73     /**
74      * Add a listener for boot complete event. It will immediately return the current boot complete
75      * state. If this value is true, [BootCompleteCache.BootCompleteListener.onBootComplete] will
76      * never be called.
77      *
78      * @param listener a listener for boot complete state.
79      * @return `true` if boot has been completed.
80      */
addListenernull81     override fun addListener(listener: BootCompleteCache.BootCompleteListener): Boolean {
82         if (bootComplete.get()) return true
83         synchronized(listeners) {
84             if (bootComplete.get()) return true
85             listeners.add(WeakReference(listener))
86             if (DEBUG) Log.d(TAG, "Adding listener: $listener")
87             return false
88         }
89     }
90 
91     /**
92      * Removes a listener for boot complete event.
93      *
94      * @param listener a listener to removed.
95      */
removeListenernull96     override fun removeListener(listener: BootCompleteCache.BootCompleteListener) {
97         if (bootComplete.get()) return
98         synchronized(listeners) {
99             listeners.removeIf { it.get() == null || it.get() === listener }
100             if (DEBUG) Log.d(TAG, "Removing listener: $listener")
101         }
102     }
103 
dumpnull104     override fun dump(pw: PrintWriter, args: Array<out String>) {
105         pw.println("BootCompleteCache state:")
106         pw.println("  boot complete: ${isBootComplete()}")
107         if (!isBootComplete()) {
108             pw.println("  listeners:")
109             synchronized(listeners) {
110                 listeners.forEach {
111                     pw.println("    $it")
112                 }
113             }
114         }
115     }
116 }