• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package com.android.systemui.qs.pipeline.data.repository
2 
3 import android.content.Intent
4 import android.content.IntentFilter
5 import android.os.UserHandle
6 import android.provider.Settings
7 import android.util.Log
8 import com.android.systemui.broadcast.BroadcastDispatcher
9 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
10 import com.android.systemui.common.shared.model.PackageChangeModel.Empty.user
11 import com.android.systemui.dagger.SysUISingleton
12 import com.android.systemui.dagger.qualifiers.Application
13 import com.android.systemui.dagger.qualifiers.Background
14 import com.android.systemui.qs.pipeline.data.model.RestoreData
15 import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository.Companion.BUFFER_CAPACITY
16 import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
17 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
18 import com.android.systemui.statusbar.policy.DeviceProvisionedController
19 import com.android.systemui.util.kotlin.emitOnStart
20 import javax.inject.Inject
21 import kotlinx.coroutines.CoroutineDispatcher
22 import kotlinx.coroutines.CoroutineScope
23 import kotlinx.coroutines.channels.awaitClose
24 import kotlinx.coroutines.flow.Flow
25 import kotlinx.coroutines.flow.SharingStarted
26 import kotlinx.coroutines.flow.asFlow
27 import kotlinx.coroutines.flow.buffer
28 import kotlinx.coroutines.flow.catch
29 import kotlinx.coroutines.flow.filter
30 import kotlinx.coroutines.flow.flattenConcat
31 import kotlinx.coroutines.flow.flowOn
32 import kotlinx.coroutines.flow.map
33 import kotlinx.coroutines.flow.mapNotNull
34 import kotlinx.coroutines.flow.merge
35 import kotlinx.coroutines.flow.onEach
36 import kotlinx.coroutines.flow.shareIn
37 import kotlinx.coroutines.sync.Mutex
38 import kotlinx.coroutines.sync.withLock
39 
40 /** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
41 interface QSSettingsRestoredRepository {
42     val restoreData: Flow<RestoreData>
43 
44     companion object {
45         // This capacity is the number of restore data that we will keep buffered in the shared
46         // flow. It is unlikely that at any given time there would be this many restores being
47         // processed by consumers, but just in case that a couple of users are restored at the
48         // same time and they need to be replayed for the consumers of the flow.
49         const val BUFFER_CAPACITY = 10
50     }
51 }
52 
53 @SysUISingleton
54 class QSSettingsRestoredBroadcastRepository
55 @Inject
56 constructor(
57     broadcastDispatcher: BroadcastDispatcher,
58     private val deviceProvisionedController: DeviceProvisionedController,
59     logger: QSPipelineLogger,
60     @Application private val scope: CoroutineScope,
61     @Background private val backgroundDispatcher: CoroutineDispatcher,
62 ) : QSSettingsRestoredRepository {
63 
64     private val onUserSetupChangedForSomeUser =
<lambda>null65         conflatedCallbackFlow {
66                 val callback =
67                     object : DeviceProvisionedController.DeviceProvisionedListener {
68                         override fun onUserSetupChanged() {
69                             trySend(Unit)
70                         }
71                     }
72                 deviceProvisionedController.addCallback(callback)
73                 awaitClose { deviceProvisionedController.removeCallback(callback) }
74             }
75             .emitOnStart()
76 
77     override val restoreData =
<lambda>null78         run {
79                 val mutex = Mutex()
80                 val firstIntent = mutableMapOf<Int, Intent>()
81 
82                 val restoresFromTwoBroadcasts: Flow<RestoreData> =
83                     broadcastDispatcher
84                         .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
85                             intent to receiver.sendingUserId
86                         }
87                         .filter { it.first.isCorrectSetting() }
88                         .mapNotNull { (intent, user) ->
89                             mutex.withLock {
90                                 if (user !in firstIntent) {
91                                     firstIntent[user] = intent
92                                     null
93                                 } else {
94                                     val firstRestored = firstIntent.remove(user)!!
95                                     processIntents(user, firstRestored, intent)
96                                 }
97                             }
98                         }
99                         .catch { Log.e(TAG, "Error parsing broadcast", it) }
100 
101                 val restoresFromUserSetup: Flow<RestoreData> =
102                     onUserSetupChangedForSomeUser
103                         .map {
104                             mutex.withLock {
105                                 firstIntent
106                                     .filter { (userId, _) ->
107                                         deviceProvisionedController.isUserSetup(userId)
108                                     }
109                                     .onEach { firstIntent.remove(it.key) }
110                                     .map { processSingleIntent(it.key, it.value) }
111                                     .asFlow()
112                             }
113                         }
114                         .flattenConcat()
115                         .catch { Log.e(TAG, "Error parsing tiles intent after user setup", it) }
116                         .onEach { logger.logSettingsRestoredOnUserSetupComplete(it.userId) }
117                 merge(restoresFromTwoBroadcasts, restoresFromUserSetup)
118             }
119             .flowOn(backgroundDispatcher)
120             .buffer(BUFFER_CAPACITY)
121             .shareIn(scope, SharingStarted.Eagerly)
122             .onEach(logger::logSettingsRestored)
123 
processSingleIntentnull124     private fun processSingleIntent(user: Int, intent: Intent): RestoreData {
125         intent.validateIntent()
126         if (intent.getStringExtra(Intent.EXTRA_SETTING_NAME) != TILES_SETTING) {
127             throw IllegalStateException(
128                 "Single intent restored for user $user is not tiles: $intent"
129             )
130         }
131         return RestoreData(
132             (intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesList(),
133             emptySet(),
134             user,
135         )
136     }
137 
processIntentsnull138     private fun processIntents(user: Int, intent1: Intent, intent2: Intent): RestoreData {
139         intent1.validateIntent()
140         intent2.validateIntent()
141         val setting1 = intent1.getStringExtra(Intent.EXTRA_SETTING_NAME)
142         val setting2 = intent2.getStringExtra(Intent.EXTRA_SETTING_NAME)
143         val (tiles, autoAdd) =
144             if (setting1 == TILES_SETTING && setting2 == AUTO_ADD_SETTING) {
145                 intent1 to intent2
146             } else if (setting1 == AUTO_ADD_SETTING && setting2 == TILES_SETTING) {
147                 intent2 to intent1
148             } else {
149                 throw IllegalStateException("Wrong intents ($intent1, $intent2)")
150             }
151 
152         return RestoreData(
153             (tiles.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesList(),
154             (autoAdd.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesSet(),
155             user,
156         )
157     }
158 
159     private companion object {
160         private const val TAG = "QSSettingsRestoredBroadcastRepository"
161 
162         private val INTENT_FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
163         private const val TILES_SETTING = Settings.Secure.QS_TILES
164         private const val AUTO_ADD_SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
165         private val requiredExtras =
166             listOf(
167                 Intent.EXTRA_SETTING_NAME,
168                 Intent.EXTRA_SETTING_PREVIOUS_VALUE,
169                 Intent.EXTRA_SETTING_NEW_VALUE,
170             )
171 
isCorrectSettingnull172         private fun Intent.isCorrectSetting(): Boolean {
173             val setting = getStringExtra(Intent.EXTRA_SETTING_NAME)
174             return setting == TILES_SETTING || setting == AUTO_ADD_SETTING
175         }
176 
validateIntentnull177         private fun Intent.validateIntent() {
178             requiredExtras.forEach { extra ->
179                 if (!hasExtra(extra)) {
180                     throw IllegalStateException("$this doesn't have $extra")
181                 }
182             }
183         }
184 
toTilesListnull185         private fun String.toTilesList() = toTilesList(this)
186 
187         private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
188     }
189 }
190