1 /* <lambda>null2 * 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 17 package com.android.permissioncontroller.privacysources 18 19 import android.content.BroadcastReceiver 20 import android.content.ComponentName 21 import android.content.Context 22 import android.content.Intent 23 import android.content.Intent.ACTION_BOOT_COMPLETED 24 import android.content.pm.PackageManager 25 import android.os.Build 26 import android.provider.DeviceConfig 27 import android.safetycenter.SafetyCenterManager 28 import android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES 29 import android.safetycenter.SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED 30 import android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS 31 import androidx.annotation.RequiresApi 32 import com.android.modules.utils.build.SdkLevel 33 import com.android.permissioncontroller.Constants.UNUSED_APPS_SAFETY_CENTER_SOURCE_ID 34 import com.android.permissioncontroller.PermissionControllerApplication 35 import com.android.permissioncontroller.permission.service.LocationAccessCheck.BG_LOCATION_SOURCE_ID 36 import com.android.permissioncontroller.permission.service.v33.SafetyCenterQsTileService 37 import com.android.permissioncontroller.permission.service.v33.SafetyCenterQsTileService.Companion.QS_TILE_COMPONENT_SETTING_FLAGS 38 import com.android.permissioncontroller.permission.utils.Utils 39 import com.android.permissioncontroller.privacysources.WorkPolicyInfo.Companion.WORK_POLICY_INFO_SOURCE_ID 40 import com.android.permissioncontroller.privacysources.v34.AppDataSharingUpdatesPrivacySource 41 import com.android.permissioncontroller.privacysources.v34.AppDataSharingUpdatesPrivacySource.Companion.APP_DATA_SHARING_UPDATES_SOURCE_ID 42 import kotlinx.coroutines.CoroutineDispatcher 43 import kotlinx.coroutines.CoroutineScope 44 import kotlinx.coroutines.Dispatchers.Default 45 import kotlinx.coroutines.launch 46 47 private fun createMapOfSourceIdsToSources(context: Context): Map<String, PrivacySource> { 48 val sourceMap: MutableMap<String, PrivacySource> = mutableMapOf() 49 50 if (SdkLevel.isAtLeastT()) { 51 sourceMap[SC_NLS_SOURCE_ID] = NotificationListenerPrivacySource() 52 sourceMap[WORK_POLICY_INFO_SOURCE_ID] = WorkPolicyInfo.create(context) 53 sourceMap[SC_ACCESSIBILITY_SOURCE_ID] = AccessibilitySourceService(context) 54 sourceMap[BG_LOCATION_SOURCE_ID] = LocationAccessPrivacySource() 55 sourceMap[UNUSED_APPS_SAFETY_CENTER_SOURCE_ID] = AutoRevokePrivacySource() 56 } 57 58 if (SdkLevel.isAtLeastU()) { 59 sourceMap[APP_DATA_SHARING_UPDATES_SOURCE_ID] = AppDataSharingUpdatesPrivacySource() 60 } 61 62 return sourceMap 63 } 64 65 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 66 class SafetyCenterReceiver( 67 private val getMapOfSourceIdsToSources: (Context) -> Map<String, PrivacySource> = 68 ::createMapOfSourceIdsToSources, 69 private val dispatcher: CoroutineDispatcher = Default 70 ) : BroadcastReceiver() { 71 72 enum class RefreshEvent { 73 UNKNOWN, 74 EVENT_DEVICE_REBOOTED, 75 EVENT_REFRESH_REQUESTED 76 } 77 onReceivenull78 override fun onReceive(context: Context, intent: Intent) { 79 if (!SdkLevel.isAtLeastT()) { 80 return 81 } 82 val safetyCenterManager: SafetyCenterManager = 83 Utils.getSystemServiceSafe( 84 PermissionControllerApplication.get().applicationContext, 85 SafetyCenterManager::class.java 86 ) 87 88 val mapOfSourceIdsToSources = getMapOfSourceIdsToSources(context) 89 90 when (intent.action) { 91 ACTION_SAFETY_CENTER_ENABLED_CHANGED -> { 92 safetyCenterEnabledChanged( 93 context, 94 safetyCenterManager.isSafetyCenterEnabled, 95 mapOfSourceIdsToSources.values 96 ) 97 } 98 ACTION_REFRESH_SAFETY_SOURCES -> { 99 if (safetyCenterManager.isSafetyCenterEnabled) { 100 val sourceIdsExtra = intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS) 101 if (sourceIdsExtra != null && sourceIdsExtra.isNotEmpty()) { 102 refreshSafetySources( 103 context, 104 intent, 105 RefreshEvent.EVENT_REFRESH_REQUESTED, 106 mapOfSourceIdsToSources, 107 sourceIdsExtra.toList() 108 ) 109 } 110 } 111 } 112 ACTION_BOOT_COMPLETED -> { 113 updateTileVisibility(context, safetyCenterManager.isSafetyCenterEnabled) 114 if (safetyCenterManager.isSafetyCenterEnabled) { 115 refreshSafetySources( 116 context, 117 intent, 118 RefreshEvent.EVENT_DEVICE_REBOOTED, 119 mapOfSourceIdsToSources, 120 mapOfSourceIdsToSources.keys.toList() 121 ) 122 } 123 } 124 } 125 } 126 safetyCenterEnabledChangednull127 private fun safetyCenterEnabledChanged( 128 context: Context, 129 enabled: Boolean, 130 privacySources: Collection<PrivacySource> 131 ) { 132 privacySources.forEach { source -> 133 CoroutineScope(dispatcher).launch { 134 if (source.shouldProcessRequest(context)) { 135 source.safetyCenterEnabledChanged(context, enabled) 136 } 137 } 138 } 139 updateTileVisibility(context, enabled) 140 } 141 updateTileVisibilitynull142 private fun updateTileVisibility(context: Context, enabled: Boolean) { 143 val tileComponent = ComponentName(context, SafetyCenterQsTileService::class.java) 144 val wasEnabled = 145 context.packageManager?.getComponentEnabledSetting(tileComponent) != 146 PackageManager.COMPONENT_ENABLED_STATE_DISABLED 147 val qsTileComponentSettingFlags = 148 DeviceConfig.getInt( 149 DeviceConfig.NAMESPACE_PRIVACY, 150 QS_TILE_COMPONENT_SETTING_FLAGS, 151 PackageManager.DONT_KILL_APP 152 ) 153 if (enabled && !wasEnabled) { 154 context.packageManager.setComponentEnabledSetting( 155 tileComponent, 156 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 157 qsTileComponentSettingFlags 158 ) 159 } else if (!enabled && wasEnabled) { 160 context.packageManager.setComponentEnabledSetting( 161 tileComponent, 162 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 163 qsTileComponentSettingFlags 164 ) 165 } 166 } 167 refreshSafetySourcesnull168 private fun refreshSafetySources( 169 context: Context, 170 intent: Intent, 171 refreshEvent: RefreshEvent, 172 mapOfSourceIdsToSources: Map<String, PrivacySource>, 173 sourceIdsToRefresh: List<String> 174 ) { 175 for (sourceId in sourceIdsToRefresh) { 176 CoroutineScope(dispatcher).launch { 177 val privacySource = mapOfSourceIdsToSources[sourceId] ?: return@launch 178 if (privacySource.shouldProcessRequest(context)) { 179 privacySource.rescanAndPushSafetyCenterData(context, intent, refreshEvent) 180 } 181 } 182 } 183 } 184 shouldProcessRequestnull185 private fun PrivacySource.shouldProcessRequest(context: Context): Boolean { 186 if (!isProfile(context)) { 187 return true 188 } 189 return shouldProcessProfileRequest 190 } 191 } 192