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 
24 class LockedUserState(private val mContext: Context) : SafeCloseable {
25     val isUserUnlockedAtLauncherStartup: Boolean
26     var isUserUnlocked: Boolean
27         private set
28     private val mUserUnlockedActions: RunnableList = RunnableList()
29 
30     @VisibleForTesting
<lambda>null31     val mUserUnlockedReceiver = SimpleBroadcastReceiver {
32         if (Intent.ACTION_USER_UNLOCKED == it.action) {
33             isUserUnlocked = true
34             notifyUserUnlocked()
35         }
36     }
37 
38     init {
39         // 1) when user reboots devices, launcher process starts at lock screen and both
40         // isUserUnlocked and isUserUnlockedAtLauncherStartup are init as false. After user unlocks
41         // screen, isUserUnlocked will be updated to true via Intent.ACTION_USER_UNLOCKED,
42         // yet isUserUnlockedAtLauncherStartup will remains as false.
43         // 2) when launcher process restarts after user has unlocked screen, both variable are
44         // init as true and will not change.
45         isUserUnlocked =
46             mContext
47                 .getSystemService(UserManager::class.java)!!
48                 .isUserUnlocked(Process.myUserHandle())
49         isUserUnlockedAtLauncherStartup = isUserUnlocked
50         if (isUserUnlocked) {
51             notifyUserUnlocked()
52         } else {
53             mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
54         }
55     }
56 
notifyUserUnlockednull57     private fun notifyUserUnlocked() {
58         mUserUnlockedActions.executeAllAndDestroy()
59         Executors.THREAD_POOL_EXECUTOR.execute {
60             mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
61         }
62     }
63 
64     /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
closenull65     override fun close() {
66         Executors.THREAD_POOL_EXECUTOR.execute {
67             mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
68         }
69     }
70 
71     /**
72      * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
73      * this runnable will run immediately because RunnableList will already have been destroyed.
74      */
runOnUserUnlockednull75     fun runOnUserUnlocked(action: Runnable) {
76         mUserUnlockedActions.add(action)
77     }
78 
79     companion object {
80         @VisibleForTesting
81         @JvmField
<lambda>null82         val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
83 
getnull84         @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
85     }
86 }
87