1 /*
2  * Copyright (C) 2023 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 package com.android.launcher3.util
17 
18 import android.content.Context
19 import android.content.Intent
20 import android.os.Process
21 import android.os.UserManager
22 import androidx.annotation.VisibleForTesting
23 import com.android.launcher3.util.Executors.MAIN_EXECUTOR
24 import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
25 
26 class LockedUserState(private val mContext: Context) : SafeCloseable {
27     val isUserUnlockedAtLauncherStartup: Boolean
28     var isUserUnlocked = false
29         private set(value) {
30             field = value
31             if (value) {
32                 notifyUserUnlocked()
33             }
34         }
35 
36     private val mUserUnlockedActions: RunnableList = RunnableList()
37 
38     @VisibleForTesting
39     val mUserUnlockedReceiver =
<lambda>null40         SimpleBroadcastReceiver(UI_HELPER_EXECUTOR) {
41             if (Intent.ACTION_USER_UNLOCKED == it.action) {
42                 isUserUnlocked = true
43             }
44         }
45 
46     init {
47         // 1) when user reboots devices, launcher process starts at lock screen and both
48         // isUserUnlocked and isUserUnlockedAtLauncherStartup are init as false. After user unlocks
49         // screen, isUserUnlocked will be updated to true via Intent.ACTION_USER_UNLOCKED,
50         // yet isUserUnlockedAtLauncherStartup will remains as false.
51         // 2) when launcher process restarts after user has unlocked screen, both variable are
52         // init as true and will not change.
53         isUserUnlocked = checkIsUserUnlocked()
54         isUserUnlockedAtLauncherStartup = isUserUnlocked
55         if (!isUserUnlocked) {
56             mUserUnlockedReceiver.register(
57                 mContext,
<lambda>null58                 {
59                     // If user is unlocked while registering broadcast receiver, we should update
60                     // [isUserUnlocked], which will call [notifyUserUnlocked] in setter
61                     if (checkIsUserUnlocked()) {
62                         MAIN_EXECUTOR.execute { isUserUnlocked = true }
63                     }
64                 },
65                 Intent.ACTION_USER_UNLOCKED
66             )
67         }
68     }
69 
checkIsUserUnlockednull70     private fun checkIsUserUnlocked() =
71         mContext.getSystemService(UserManager::class.java)!!.isUserUnlocked(Process.myUserHandle())
72 
73     private fun notifyUserUnlocked() {
74         mUserUnlockedActions.executeAllAndDestroy()
75         mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
76     }
77 
78     /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
closenull79     override fun close() {
80         mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
81     }
82 
83     /**
84      * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
85      * this runnable will run immediately because RunnableList will already have been destroyed.
86      */
runOnUserUnlockednull87     fun runOnUserUnlocked(action: Runnable) {
88         mUserUnlockedActions.add(action)
89     }
90 
91     /**
92      * Removes a previously queued `Runnable` to be run when the user is unlocked.
93      */
removeOnUserUnlockedRunnablenull94     fun removeOnUserUnlockedRunnable(action: Runnable) {
95         mUserUnlockedActions.remove(action)
96     }
97 
98     companion object {
99         @VisibleForTesting
100         @JvmField
<lambda>null101         val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
102 
getnull103         @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
104     }
105 }
106