1 /*
2  * Copyright (C) 2022 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.permissioncontroller.privacysources
17 
18 import android.content.ComponentName
19 import android.content.Context
20 import android.provider.Settings
21 import android.text.TextUtils
22 import android.util.Log
23 import kotlinx.coroutines.sync.Mutex
24 import kotlinx.coroutines.sync.withLock
25 
26 /**
27  * Code reference from https://source.corp.google.com/android/frameworks/base/packages/SettingsLib/
28  * src/com/android/settingslib/accessibility/AccessibilityUtils.java
29  */
30 object AccessibilitySettingsUtil {
31     private const val ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':'
32     private val LOG_TAG = AccessibilitySettingsUtil::class.java.simpleName
33     private val lock = Mutex()
34 
35     /** Changes an accessibility component's state. */
disableAccessibilityServicenull36     suspend fun disableAccessibilityService(context: Context, serviceToBeDisabled: ComponentName) {
37         lock.withLock {
38             val settingsEnabledA11yServices = getEnabledServicesFromSettings(context)
39             if (
40                 settingsEnabledA11yServices.isEmpty() ||
41                     !settingsEnabledA11yServices.contains(serviceToBeDisabled)
42             ) {
43                 Log.w(
44                     LOG_TAG,
45                     "${serviceToBeDisabled.toShortString()} is already disabled " +
46                         "or not installed."
47                 )
48                 return
49             }
50 
51             settingsEnabledA11yServices.remove(serviceToBeDisabled)
52 
53             val updatedEnabledServices =
54                 settingsEnabledA11yServices
55                     .map { it.flattenToString() }
56                     .joinToString(separator = ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR.toString())
57 
58             Settings.Secure.putString(
59                 context.contentResolver,
60                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
61                 updatedEnabledServices
62             )
63         }
64     }
65 
66     /** @return the mutable set of enabled accessibility services. */
getEnabledServicesFromSettingsnull67     fun getEnabledServicesFromSettings(context: Context): MutableSet<ComponentName> {
68         val enabledServicesSetting =
69             Settings.Secure.getString(
70                 context.contentResolver,
71                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
72             )
73         val enabledServices = mutableSetOf<ComponentName>()
74         if (TextUtils.isEmpty(enabledServicesSetting)) {
75             return enabledServices
76         }
77 
78         val colonSplitter: TextUtils.StringSplitter =
79             TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR)
80         colonSplitter.setString(enabledServicesSetting)
81 
82         for (componentNameString in colonSplitter) {
83             val enabledService = ComponentName.unflattenFromString(componentNameString)
84             if (enabledService != null) {
85                 enabledServices.add(enabledService)
86             } else {
87                 Log.e(LOG_TAG, "unable to parse accessibility service $componentNameString")
88             }
89         }
90 
91         return enabledServices
92     }
93 }
94