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