/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:Suppress("DEPRECATION") package com.android.permissioncontroller.permission.data import android.app.Application import android.content.pm.PackageManager import android.os.UserHandle import android.os.UserManager import android.util.Log import androidx.annotation.MainThread import androidx.lifecycle.Observer import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo import com.android.permissioncontroller.permission.utils.Utils import kotlinx.coroutines.Job /** * LiveData for a LightPackageInfo. * * @param app The current Application * @param packageName The name of the package this LiveData will watch for mode changes for * @param user The user for whom the packageInfo will be defined */ class LightPackageInfoLiveData private constructor( private val app: Application, private val packageName: String, private val user: UserHandle ) : SmartAsyncMediatorLiveData(alwaysUpdateOnActive = false), PackageBroadcastReceiver.PackageBroadcastListener, PermissionListenerMultiplexer.PermissionChangeCallback { private val LOG_TAG = LightPackageInfoLiveData::class.java.simpleName private val userPackagesLiveData = UserPackageInfosLiveData[user] private var uid: Int? = null /** * The currently registered UID on which this LiveData is listening for permission changes. */ private var registeredUid: Int? = null /** * Whether or not this package livedata is watching the UserPackageInfosLiveData */ private var watchingUserPackagesLiveData: Boolean = false /** * Callback from the PackageBroadcastReceiver. Either deletes or generates package data. * * @param packageName the name of the package which was updated. Ignored in this method */ override fun onPackageUpdate(packageName: String) { updateAsync() } override fun setValue(newValue: LightPackageInfo?) { newValue?.let { packageInfo -> if (packageInfo.uid != uid) { uid = packageInfo.uid if (hasActiveObservers()) { PermissionListenerMultiplexer.addOrReplaceCallback(registeredUid, packageInfo.uid, this) registeredUid = uid } } } super.setValue(newValue) } override fun updateAsync() { // If we were watching the userPackageInfosLiveData, stop, since we will override its value if (watchingUserPackagesLiveData) { removeSource(userPackagesLiveData) watchingUserPackagesLiveData = false } super.updateAsync() } override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } postValue(try { var flags = PackageManager.GET_PERMISSIONS if (SdkLevel.isAtLeastS()) { flags = flags or PackageManager.GET_ATTRIBUTIONS } LightPackageInfo(Utils.getUserContext(app, user).packageManager .getPackageInfo(packageName, flags)) } catch (e: Exception) { if (e is PackageManager.NameNotFoundException) { Log.w(LOG_TAG, "Package \"$packageName\" not found for user $user") } else { val profiles = app.getSystemService(UserManager::class.java)!!.userProfiles Log.e(LOG_TAG, "Failed to create context for user $user. " + "User exists : ${user in profiles }", e) } invalidateSingle(packageName to user) null }) } /** * Callback from the PermissionListener. Either deletes or generates package data. */ override fun onPermissionChange() { updateAsync() } override fun onActive() { super.onActive() PackageBroadcastReceiver.addChangeCallback(packageName, this) uid?.let { registeredUid = uid PermissionListenerMultiplexer.addCallback(it, this) } if (userPackagesLiveData.hasActiveObservers() && !watchingUserPackagesLiveData && !userPackagesLiveData.permChangeStale) { watchingUserPackagesLiveData = true addSource(userPackagesLiveData, userPackageInfosObserver) } else { updateAsync() } } private val userPackageInfosObserver = Observer> { updateFromUserPackageInfosLiveData() } @MainThread private fun updateFromUserPackageInfosLiveData() { if (!userPackagesLiveData.isInitialized) { return } val packageInfo = userPackagesLiveData.value!!.find { it.packageName == packageName } if (packageInfo != null) { // Once we get one non-stale update, stop listening, as any further updates will likely // be individual package updates. if (!userPackagesLiveData.isStale) { removeSource(UserPackageInfosLiveData[user]) watchingUserPackagesLiveData = false } value = packageInfo } else { // If the UserPackageInfosLiveData does not contain this package, check for removal, and // stop watching. updateAsync() } } override fun onInactive() { super.onInactive() PackageBroadcastReceiver.removeChangeCallback(packageName, this) registeredUid?.let { PermissionListenerMultiplexer.removeCallback(it, this) registeredUid = null } if (watchingUserPackagesLiveData) { removeSource(userPackagesLiveData) watchingUserPackagesLiveData = false } } /** * Repository for LightPackageInfoLiveDatas *

Key value is a string package name and UserHandle pair, value is its corresponding * LiveData. */ companion object : DataRepositoryForPackage, LightPackageInfoLiveData>() { override fun newValue(key: Pair): LightPackageInfoLiveData { return LightPackageInfoLiveData(PermissionControllerApplication.get(), key.first, key.second) } } }