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