1 /* <lambda>null2 * Copyright (C) 2024 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.common.data.repository 18 19 import android.content.pm.PackageInstaller 20 import android.os.Handler 21 import com.android.internal.annotations.GuardedBy 22 import com.android.systemui.common.shared.model.PackageInstallSession 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Background 25 import com.android.systemui.log.LogBuffer 26 import com.android.systemui.log.core.Logger 27 import com.android.systemui.log.dagger.PackageChangeRepoLog 28 import javax.inject.Inject 29 import kotlinx.coroutines.CoroutineScope 30 import kotlinx.coroutines.flow.Flow 31 import kotlinx.coroutines.flow.MutableStateFlow 32 import kotlinx.coroutines.flow.asStateFlow 33 import kotlinx.coroutines.flow.distinctUntilChanged 34 import kotlinx.coroutines.flow.dropWhile 35 import kotlinx.coroutines.flow.launchIn 36 import kotlinx.coroutines.flow.map 37 import kotlinx.coroutines.flow.onEach 38 39 /** Monitors package install sessions for all users. */ 40 @SysUISingleton 41 class PackageInstallerMonitor 42 @Inject 43 constructor( 44 @Background private val bgHandler: Handler, 45 @Background private val bgScope: CoroutineScope, 46 @PackageChangeRepoLog logBuffer: LogBuffer, 47 private val packageInstaller: PackageInstaller, 48 ) : PackageInstaller.SessionCallback() { 49 50 private val logger = Logger(logBuffer, TAG) 51 52 @GuardedBy("sessions") private val sessions = mutableMapOf<Int, PackageInstallSession>() 53 54 private val _installSessions = 55 MutableStateFlow<List<PackageInstallSession>>(emptyList()).apply { 56 subscriptionCount 57 .map { count -> count > 0 } 58 .distinctUntilChanged() 59 // Drop initial false value 60 .dropWhile { !it } 61 .onEach { isActive -> 62 if (isActive) { 63 synchronized(sessions) { 64 sessions.putAll( 65 packageInstaller.allSessions 66 .map { session -> session.toModel() } 67 .associateBy { it.sessionId } 68 ) 69 updateInstallerSessionsFlow() 70 } 71 packageInstaller.registerSessionCallback( 72 this@PackageInstallerMonitor, 73 bgHandler 74 ) 75 } else { 76 synchronized(sessions) { 77 sessions.clear() 78 updateInstallerSessionsFlow() 79 } 80 packageInstaller.unregisterSessionCallback(this@PackageInstallerMonitor) 81 } 82 } 83 .launchIn(bgScope) 84 } 85 86 val installSessionsForPrimaryUser: Flow<List<PackageInstallSession>> = 87 _installSessions.asStateFlow() 88 89 /** Called when a new installer session is created. */ 90 override fun onCreated(sessionId: Int) { 91 logger.i({ "session created $int1" }) { int1 = sessionId } 92 updateSession(sessionId) 93 } 94 95 /** Called when new installer session has finished. */ 96 override fun onFinished(sessionId: Int, success: Boolean) { 97 logger.i({ "session finished $int1" }) { int1 = sessionId } 98 synchronized(sessions) { 99 sessions.remove(sessionId) 100 updateInstallerSessionsFlow() 101 } 102 } 103 104 /** 105 * Badging details for the session changed. For example, the app icon or label has been updated. 106 */ 107 override fun onBadgingChanged(sessionId: Int) { 108 logger.i({ "session badging changed $int1" }) { int1 = sessionId } 109 updateSession(sessionId) 110 } 111 112 /** 113 * A session is considered active when there is ongoing forward progress being made. For 114 * example, a package started downloading. 115 */ 116 override fun onActiveChanged(sessionId: Int, active: Boolean) { 117 // Active status updates are not tracked for now 118 } 119 120 override fun onProgressChanged(sessionId: Int, progress: Float) { 121 // Progress updates are not tracked for now 122 } 123 124 private fun updateSession(sessionId: Int) { 125 val session = packageInstaller.getSessionInfo(sessionId) 126 127 synchronized(sessions) { 128 if (session == null) { 129 sessions.remove(sessionId) 130 } else { 131 sessions[sessionId] = session.toModel() 132 } 133 updateInstallerSessionsFlow() 134 } 135 } 136 137 @GuardedBy("sessions") 138 private fun updateInstallerSessionsFlow() { 139 _installSessions.value = sessions.values.toList() 140 } 141 142 companion object { 143 const val TAG = "PackageInstallerMonitor" 144 145 private fun PackageInstaller.SessionInfo.toModel(): PackageInstallSession { 146 return PackageInstallSession( 147 sessionId = this.sessionId, 148 packageName = this.appPackageName, 149 icon = this.getAppIcon(), 150 user = this.user, 151 ) 152 } 153 } 154 } 155