1 /*
2  * Copyright (C) 2024 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.settings.restriction
18 
19 import android.content.Context
20 import android.os.Bundle
21 import android.os.IUserRestrictionsListener
22 import android.os.UserManager
23 import com.android.settingslib.datastore.KeyedDataObservable
24 import com.android.settingslib.datastore.KeyedObserver
25 import java.util.concurrent.Executor
26 import java.util.concurrent.atomic.AtomicBoolean
27 
28 /** Helper class to monitor user restriction changes. */
29 object UserRestrictions {
30     private val observable = KeyedDataObservable<String>()
31 
32     private val userRestrictionsListener =
33         object : IUserRestrictionsListener.Stub() {
onUserRestrictionsChangednull34             override fun onUserRestrictionsChanged(
35                 userId: Int,
36                 newRestrictions: Bundle,
37                 prevRestrictions: Bundle,
38             ) {
39                 // there is no API to remove listener, do a quick check to avoid unnecessary work
40                 if (!observable.hasAnyObserver()) return
41 
42                 val changedKeys = mutableSetOf<String>()
43                 val keys = newRestrictions.keySet() + prevRestrictions.keySet()
44                 for (key in keys) {
45                     if (newRestrictions.getBoolean(key) != prevRestrictions.getBoolean(key)) {
46                         changedKeys.add(key)
47                     }
48                 }
49 
50                 for (key in changedKeys) observable.notifyChange(key, 0)
51             }
52         }
53 
54     private val listenerAdded = AtomicBoolean()
55 
addObservernull56     fun addObserver(context: Context, observer: KeyedObserver<String?>, executor: Executor) {
57         context.addUserRestrictionsListener()
58         observable.addObserver(observer, executor)
59     }
60 
addObservernull61     fun addObserver(
62         context: Context,
63         key: String,
64         observer: KeyedObserver<String>,
65         executor: Executor,
66     ) {
67         context.addUserRestrictionsListener()
68         observable.addObserver(key, observer, executor)
69     }
70 
Contextnull71     private fun Context.addUserRestrictionsListener() {
72         if (listenerAdded.getAndSet(true)) return
73         // surprisingly, there is no way to remove the listener
74         applicationContext
75             .getSystemService(UserManager::class.java)
76             .addUserRestrictionsListener(userRestrictionsListener)
77     }
78 
removeObservernull79     fun removeObserver(observer: KeyedObserver<String?>) = observable.removeObserver(observer)
80 
81     fun removeObserver(key: String, observer: KeyedObserver<String>) =
82         observable.removeObserver(key, observer)
83 }
84