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 
17 package com.android.systemui.qs.tiles.base.interactor
18 
19 import android.content.Context
20 import android.os.UserHandle
21 import androidx.annotation.VisibleForTesting
22 import androidx.annotation.WorkerThread
23 import com.android.settingslib.RestrictedLockUtils
24 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
25 import com.android.settingslib.RestrictedLockUtilsInternal
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dagger.qualifiers.Background
28 import com.android.systemui.plugins.ActivityStarter
29 import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor.PolicyResult
30 import javax.inject.Inject
31 import kotlinx.coroutines.CoroutineDispatcher
32 import kotlinx.coroutines.withContext
33 
34 /**
35  * Provides restrictions data for the tiles. This is used in
36  * [com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl] to determine if the tile is
37  * disabled based on the [com.android.systemui.qs.tiles.viewmodel.QSTileConfig.policy].
38  */
39 interface DisabledByPolicyInteractor {
40 
41     /**
42      * Checks if the tile is restricted by the policy for a specific user. Pass the result to the
43      * [handlePolicyResult] to let the user know that the tile is disable by the admin.
44      */
isDisablednull45     suspend fun isDisabled(user: UserHandle, userRestriction: String?): PolicyResult
46 
47     /**
48      * Returns true when [policyResult] is [PolicyResult.TileDisabled] and has been handled by this
49      * method. No further handling is required and the input event can be skipped at this point.
50      *
51      * Returns false when [policyResult] is [PolicyResult.TileEnabled] and this method has done
52      * nothing.
53      */
54     fun handlePolicyResult(policyResult: PolicyResult): Boolean
55 
56     sealed interface PolicyResult {
57         /** Tile has no policy restrictions. */
58         data object TileEnabled : PolicyResult
59 
60         /**
61          * Tile is disabled by policy. Pass this to [DisabledByPolicyInteractor.handlePolicyResult]
62          * to show the user info screen using
63          * [RestrictedLockUtils.getShowAdminSupportDetailsIntent].
64          */
65         data class TileDisabled(val admin: EnforcedAdmin) : PolicyResult
66     }
67 }
68 
69 @SysUISingleton
70 class DisabledByPolicyInteractorImpl
71 @Inject
72 constructor(
73     private val context: Context,
74     private val activityStarter: ActivityStarter,
75     private val restrictedLockProxy: RestrictedLockProxy,
76     @Background private val backgroundDispatcher: CoroutineDispatcher,
77 ) : DisabledByPolicyInteractor {
78 
isDisablednull79     override suspend fun isDisabled(user: UserHandle, userRestriction: String?): PolicyResult =
80         withContext(backgroundDispatcher) {
81             val admin: EnforcedAdmin =
82                 restrictedLockProxy.getEnforcedAdmin(user.identifier, userRestriction)
83                     ?: return@withContext PolicyResult.TileEnabled
84 
85             return@withContext if (
86                 !restrictedLockProxy.hasBaseUserRestriction(user.identifier, userRestriction)
87             ) {
88                 PolicyResult.TileDisabled(admin)
89             } else {
90                 PolicyResult.TileEnabled
91             }
92         }
93 
handlePolicyResultnull94     override fun handlePolicyResult(policyResult: PolicyResult): Boolean =
95         when (policyResult) {
96             is PolicyResult.TileEnabled -> false
97             is PolicyResult.TileDisabled -> {
98                 val intent =
99                     RestrictedLockUtils.getShowAdminSupportDetailsIntent(policyResult.admin)
100                 activityStarter.postStartActivityDismissingKeyguard(intent, 0)
101                 true
102             }
103         }
104 }
105 
106 /** Mockable proxy for [RestrictedLockUtilsInternal] static methods. */
107 @VisibleForTesting
108 class RestrictedLockProxy @Inject constructor(private val context: Context) {
109 
110     @WorkerThread
hasBaseUserRestrictionnull111     fun hasBaseUserRestriction(userId: Int, userRestriction: String?): Boolean =
112         RestrictedLockUtilsInternal.hasBaseUserRestriction(
113             context,
114             userRestriction,
115             userId,
116         )
117 
118     @WorkerThread
119     fun getEnforcedAdmin(userId: Int, userRestriction: String?): EnforcedAdmin? =
120         RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
121             context,
122             userRestriction,
123             userId,
124         )
125 }
126