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.pipeline.data.repository 18 19 import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE 20 import android.annotation.WorkerThread 21 import android.content.BroadcastReceiver 22 import android.content.ComponentName 23 import android.content.Context 24 import android.content.Intent 25 import android.content.IntentFilter 26 import android.content.pm.PackageManager 27 import android.content.pm.PackageManager.ResolveInfoFlags 28 import android.os.UserHandle 29 import android.service.quicksettings.TileService 30 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 31 import com.android.systemui.dagger.SysUISingleton 32 import com.android.systemui.dagger.qualifiers.Application 33 import com.android.systemui.dagger.qualifiers.Background 34 import com.android.systemui.util.kotlin.isComponentActuallyEnabled 35 import javax.inject.Inject 36 import kotlinx.coroutines.CoroutineDispatcher 37 import kotlinx.coroutines.channels.awaitClose 38 import kotlinx.coroutines.flow.Flow 39 import kotlinx.coroutines.flow.distinctUntilChanged 40 import kotlinx.coroutines.flow.flowOn 41 import kotlinx.coroutines.flow.map 42 import kotlinx.coroutines.flow.onStart 43 44 interface InstalledTilesComponentRepository { 45 46 fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> 47 } 48 49 @SysUISingleton 50 class InstalledTilesComponentRepositoryImpl 51 @Inject 52 constructor( 53 @Application private val applicationContext: Context, 54 @Background private val backgroundDispatcher: CoroutineDispatcher, 55 ) : InstalledTilesComponentRepository { 56 57 override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> { 58 /* 59 * In order to query [PackageManager] for different users, this implementation will call 60 * [Context.createContextAsUser] and retrieve the [PackageManager] from that context. 61 */ 62 val packageManager = 63 if (applicationContext.userId == userId) { 64 applicationContext.packageManager 65 } else { 66 applicationContext 67 .createContextAsUser( 68 UserHandle.of(userId), 69 /* flags */ 0, 70 ) 71 .packageManager 72 } 73 return conflatedCallbackFlow { 74 val receiver = 75 object : BroadcastReceiver() { 76 override fun onReceive(context: Context?, intent: Intent?) { 77 trySend(Unit) 78 } 79 } 80 applicationContext.registerReceiverAsUser( 81 receiver, 82 UserHandle.of(userId), 83 INTENT_FILTER, 84 /* broadcastPermission = */ null, 85 /* scheduler = */ null 86 ) 87 88 awaitClose { applicationContext.unregisterReceiver(receiver) } 89 } 90 .onStart { emit(Unit) } 91 .map { reloadComponents(userId, packageManager) } 92 .distinctUntilChanged() 93 .flowOn(backgroundDispatcher) 94 } 95 96 @WorkerThread 97 private fun reloadComponents(userId: Int, packageManager: PackageManager): Set<ComponentName> { 98 return packageManager 99 .queryIntentServicesAsUser(INTENT, FLAGS, userId) 100 .mapNotNull { it.serviceInfo } 101 .filter { it.permission == BIND_QUICK_SETTINGS_TILE } 102 .filter { packageManager.isComponentActuallyEnabled(it) } 103 .mapTo(mutableSetOf()) { it.componentName } 104 } 105 106 companion object { 107 private val INTENT_FILTER = 108 IntentFilter().apply { 109 addAction(Intent.ACTION_PACKAGE_ADDED) 110 addAction(Intent.ACTION_PACKAGE_CHANGED) 111 addAction(Intent.ACTION_PACKAGE_REMOVED) 112 addAction(Intent.ACTION_PACKAGE_REPLACED) 113 addDataScheme("package") 114 } 115 private val INTENT = Intent(TileService.ACTION_QS_TILE) 116 private val FLAGS = 117 ResolveInfoFlags.of( 118 (PackageManager.GET_SERVICES or 119 PackageManager.MATCH_DIRECT_BOOT_AWARE or 120 PackageManager.MATCH_DIRECT_BOOT_UNAWARE) 121 .toLong() 122 ) 123 } 124 } 125