1 /* 2 * Copyright (C) 2019 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.controls.management 18 19 import android.annotation.WorkerThread 20 import android.content.ComponentName 21 import android.content.Context 22 import android.content.Intent 23 import android.content.pm.PackageManager 24 import android.os.UserHandle 25 import android.service.controls.ControlsProviderService 26 import android.util.Log 27 import com.android.internal.annotations.VisibleForTesting 28 import com.android.settingslib.applications.ServiceListing 29 import com.android.settingslib.widget.CandidateInfo 30 import com.android.systemui.Dumpable 31 import com.android.systemui.controls.ControlsServiceInfo 32 import com.android.systemui.dagger.SysUISingleton 33 import com.android.systemui.dagger.qualifiers.Background 34 import com.android.systemui.dump.DumpManager 35 import com.android.systemui.flags.FeatureFlags 36 import com.android.systemui.flags.Flags 37 import com.android.systemui.settings.UserTracker 38 import com.android.systemui.util.ActivityTaskManagerProxy 39 import com.android.systemui.util.asIndenting 40 import com.android.systemui.util.indentIfPossible 41 import java.io.PrintWriter 42 import java.util.concurrent.Executor 43 import java.util.concurrent.atomic.AtomicInteger 44 import javax.inject.Inject 45 46 private fun createServiceListing(context: Context): ServiceListing { 47 return ServiceListing.Builder(context).apply { 48 setIntentAction(ControlsProviderService.SERVICE_CONTROLS) 49 setPermission("android.permission.BIND_CONTROLS") 50 setNoun("Controls Provider") 51 setSetting("controls_providers") 52 setTag("controls_providers") 53 setAddDeviceLockedFlags(true) 54 }.build() 55 } 56 57 /** 58 * Provides a listing of components to be used as ControlsServiceProvider. 59 * 60 * This controller keeps track of components that satisfy: 61 * 62 * * Has an intent-filter responding to [ControlsProviderService.CONTROLS_ACTION] 63 * * Has the bind permission `android.permission.BIND_CONTROLS` 64 */ 65 @SysUISingleton 66 class ControlsListingControllerImpl @VisibleForTesting constructor( 67 private val context: Context, 68 @Background private val backgroundExecutor: Executor, 69 private val serviceListingBuilder: (Context) -> ServiceListing, 70 private val userTracker: UserTracker, 71 private val activityTaskManagerProxy: ActivityTaskManagerProxy, 72 dumpManager: DumpManager, 73 private val featureFlags: FeatureFlags 74 ) : ControlsListingController, Dumpable { 75 76 @Inject 77 constructor( 78 context: Context, 79 @Background executor: Executor, 80 userTracker: UserTracker, 81 activityTaskManagerProxy: ActivityTaskManagerProxy, 82 dumpManager: DumpManager, 83 featureFlags: FeatureFlags 84 ) : this( 85 context, 86 executor, 87 ::createServiceListing, 88 userTracker, 89 activityTaskManagerProxy, 90 dumpManager, 91 featureFlags 92 ) 93 94 private var serviceListing = serviceListingBuilder(context) 95 // All operations in background thread 96 private val callbacks = mutableSetOf<ControlsListingController.ControlsListingCallback>() 97 98 companion object { 99 private const val TAG = "ControlsListingControllerImpl" 100 } 101 102 private var availableServices = emptyList<ControlsServiceInfo>() 103 private var userChangeInProgress = AtomicInteger(0) 104 105 override var currentUserId = userTracker.userId 106 private set 107 108 private val serviceListingCallback = ServiceListing.Callback { list -> 109 Log.d(TAG, "ServiceConfig reloaded, count: ${list.size}") 110 val newServices = list.map { ControlsServiceInfo(userTracker.userContext, it) } 111 // After here, `list` is not captured, so we don't risk modifying it outside of the callback 112 backgroundExecutor.execute { 113 if (userChangeInProgress.get() > 0) return@execute 114 updateServices(newServices) 115 } 116 } 117 118 init { 119 Log.d(TAG, "Initializing") 120 dumpManager.registerDumpable(TAG, this) 121 serviceListing.addCallback(serviceListingCallback) 122 serviceListing.setListening(true) 123 serviceListing.reload() 124 } 125 126 private fun updateServices(newServices: List<ControlsServiceInfo>) { 127 if (activityTaskManagerProxy.supportsMultiWindow(context)) { 128 val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED) 129 newServices.forEach { 130 it.resolvePanelActivity(allowAllApps) } 131 } 132 133 if (newServices != availableServices) { 134 availableServices = newServices 135 callbacks.forEach { 136 it.onServicesUpdated(getCurrentServices()) 137 } 138 } 139 } 140 141 override fun changeUser(newUser: UserHandle) { 142 userChangeInProgress.incrementAndGet() 143 serviceListing.setListening(false) 144 145 backgroundExecutor.execute { 146 if (userChangeInProgress.decrementAndGet() == 0) { 147 currentUserId = newUser.identifier 148 val contextForUser = context.createContextAsUser(newUser, 0) 149 serviceListing = serviceListingBuilder(contextForUser) 150 serviceListing.addCallback(serviceListingCallback) 151 serviceListing.setListening(true) 152 serviceListing.reload() 153 } 154 } 155 } 156 157 /** 158 * Adds a callback to this controller. 159 * 160 * The callback will be notified after it is added as well as any time that the valid 161 * components change. 162 * 163 * @param listener a callback to be notified 164 */ 165 override fun addCallback(listener: ControlsListingController.ControlsListingCallback) { 166 backgroundExecutor.execute { 167 if (userChangeInProgress.get() > 0) { 168 // repost this event, as callers may rely on the initial callback from 169 // onServicesUpdated 170 addCallback(listener) 171 } else { 172 val services = getCurrentServices() 173 Log.d(TAG, "Subscribing callback, service count: ${services.size}") 174 callbacks.add(listener) 175 listener.onServicesUpdated(services) 176 } 177 } 178 } 179 180 /** 181 * Removes a callback from this controller. 182 * 183 * @param listener the callback to be removed. 184 */ 185 override fun removeCallback(listener: ControlsListingController.ControlsListingCallback) { 186 backgroundExecutor.execute { 187 Log.d(TAG, "Unsubscribing callback") 188 callbacks.remove(listener) 189 } 190 } 191 192 /** 193 * @return a list of components that satisfy the requirements to be a 194 * [ControlsProviderService] 195 */ 196 override fun getCurrentServices(): List<ControlsServiceInfo> = 197 availableServices.map(ControlsServiceInfo::copy) 198 199 @WorkerThread 200 override fun forceReload() { 201 val packageManager = context.packageManager 202 val intent = Intent(ControlsProviderService.SERVICE_CONTROLS) 203 val user = userTracker.userHandle 204 val flags = PackageManager.GET_SERVICES or 205 PackageManager.GET_META_DATA or 206 PackageManager.MATCH_DIRECT_BOOT_UNAWARE or 207 PackageManager.MATCH_DIRECT_BOOT_AWARE 208 val services = packageManager.queryIntentServicesAsUser( 209 intent, 210 PackageManager.ResolveInfoFlags.of(flags.toLong()), 211 user 212 ).map { ControlsServiceInfo(userTracker.userContext, it.serviceInfo) } 213 updateServices(services) 214 } 215 216 /** 217 * Get the localized label for the component. 218 * 219 * @param name the name of the component 220 * @return a label as returned by [CandidateInfo.loadLabel] or `null`. 221 */ 222 override fun getAppLabel(name: ComponentName): CharSequence? { 223 return availableServices.firstOrNull { it.componentName == name } 224 ?.loadLabel() 225 } 226 227 override fun dump(writer: PrintWriter, args: Array<out String>) { 228 writer.println("ControlsListingController:") 229 writer.asIndenting().indentIfPossible { 230 println("Callbacks: $callbacks") 231 println("Services: ${getCurrentServices()}") 232 } 233 } 234 } 235