/* * Copyright (C) 2021 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.accessibilityservice.AccessibilityService import android.app.Application import android.app.admin.DevicePolicyManager import android.content.Intent import android.content.pm.PackageManager import android.os.UserHandle import android.printservice.PrintService import android.service.autofill.AutofillService import android.service.dreams.DreamService import android.service.notification.NotificationListenerService import android.service.voice.VoiceInteractionService import android.service.wallpaper.WallpaperService import android.view.inputmethod.InputMethod import com.android.permissioncontroller.DumpableLog import com.android.permissioncontroller.PermissionControllerApplication import com.android.permissioncontroller.hibernation.DEBUG_HIBERNATION_POLICY import com.android.permissioncontroller.permission.utils.Utils.getUserContext import kotlinx.coroutines.Job /** * A LiveData which tracks services for a certain type * * @param app The current application * @param intentAction The name of interface the service implements * @param permission The permission required for the service * @param user The user the services should be determined for */ class ServiceLiveData( private val app: Application, override val intentAction: String, private val permission: String, private val user: UserHandle ) : SmartAsyncMediatorLiveData>(), PackageBroadcastReceiver.PackageBroadcastListener, HasIntentAction { private val name = intentAction.substringAfterLast(".") private val enabledAccessibilityServicesLiveData = EnabledAccessibilityServicesLiveData[user] private val enabledInputMethodsLiveData = EnabledInputMethodsLiveData[user] private val enabledNotificationListenersLiveData = EnabledNotificationListenersLiveData[user] private val selectedWallpaperServiceLiveData = SelectedWallpaperServiceLiveData[user] private val selectedVoiceInteractionServiceLiveData = SelectedVoiceInteractionServiceLiveData[user] private val selectedAutofillServiceLiveData = SelectedAutofillServiceLiveData[user] private val enabledDreamServicesLiveData = EnabledDreamServicesLiveData[user] private val disabledPrintServicesLiveData = DisabledPrintServicesLiveData[user] private val enabledDeviceAdminsLiveDataLiveData = EnabledDeviceAdminsLiveData[user] init { if (intentAction == AccessibilityService.SERVICE_INTERFACE) { addSource(enabledAccessibilityServicesLiveData) { updateAsync() } } if (intentAction == InputMethod.SERVICE_INTERFACE) { addSource(enabledInputMethodsLiveData) { updateAsync() } } if (intentAction == NotificationListenerService.SERVICE_INTERFACE) { addSource(enabledNotificationListenersLiveData) { updateAsync() } } if (intentAction == WallpaperService.SERVICE_INTERFACE) { addSource(selectedWallpaperServiceLiveData) { updateAsync() } } if (intentAction == VoiceInteractionService.SERVICE_INTERFACE) { addSource(selectedVoiceInteractionServiceLiveData) { updateAsync() } } if (intentAction == AutofillService.SERVICE_INTERFACE) { addSource(selectedAutofillServiceLiveData) { updateAsync() } } if (intentAction == DreamService.SERVICE_INTERFACE) { addSource(enabledDreamServicesLiveData) { updateAsync() } } if (intentAction == PrintService.SERVICE_INTERFACE) { addSource(disabledPrintServicesLiveData) { updateAsync() } } if (intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE) { addSource(enabledDeviceAdminsLiveDataLiveData) { updateAsync() } } } override fun onPackageUpdate(packageName: String) { updateAsync() } override suspend fun loadDataAndPostValue(job: Job) { if (job.isCancelled) { return } if ( intentAction == AccessibilityService.SERVICE_INTERFACE && !enabledAccessibilityServicesLiveData.isInitialized ) { return } if ( intentAction == InputMethod.SERVICE_INTERFACE && !enabledInputMethodsLiveData.isInitialized ) { return } if ( intentAction == NotificationListenerService.SERVICE_INTERFACE && !enabledNotificationListenersLiveData.isInitialized ) { return } if ( intentAction == WallpaperService.SERVICE_INTERFACE && !selectedWallpaperServiceLiveData.isInitialized ) { return } if ( intentAction == VoiceInteractionService.SERVICE_INTERFACE && !selectedVoiceInteractionServiceLiveData.isInitialized ) { return } if ( intentAction == AutofillService.SERVICE_INTERFACE && !selectedAutofillServiceLiveData.isInitialized ) { return } if ( intentAction == DreamService.SERVICE_INTERFACE && !enabledDreamServicesLiveData.isInitialized ) { return } if ( intentAction == PrintService.SERVICE_INTERFACE && !disabledPrintServicesLiveData.isInitialized ) { return } if ( intentAction == DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE && !enabledDeviceAdminsLiveDataLiveData.isInitialized ) { return } val packageNames = getUserContext(app, user) .packageManager .queryIntentServices( Intent(intentAction), PackageManager.GET_SERVICES or PackageManager.GET_META_DATA ) .mapNotNull { resolveInfo -> if (resolveInfo?.serviceInfo?.permission != permission) { return@mapNotNull null } val packageName = resolveInfo.serviceInfo?.packageName if (!isServiceEnabled(packageName)) { if (DEBUG_HIBERNATION_POLICY) { DumpableLog.i( LOG_TAG, "Not exempting $packageName - not an active $name " + "for u${user.identifier}" ) } return@mapNotNull null } packageName } .toSet() if (DEBUG_HIBERNATION_POLICY) { DumpableLog.i(LOG_TAG, "Detected ${name}s: $packageNames") } postValue(packageNames) } suspend fun isServiceEnabled(pkg: String?): Boolean { if (pkg == null) { return false } return when (intentAction) { AccessibilityService.SERVICE_INTERFACE -> { pkg in (enabledAccessibilityServicesLiveData.value ?: emptyList()) } InputMethod.SERVICE_INTERFACE -> { pkg in (enabledInputMethodsLiveData.value ?: emptyList()) } NotificationListenerService.SERVICE_INTERFACE -> { pkg in (enabledNotificationListenersLiveData.value ?: emptyList()) } WallpaperService.SERVICE_INTERFACE -> { pkg == selectedWallpaperServiceLiveData.value } VoiceInteractionService.SERVICE_INTERFACE -> { pkg == selectedVoiceInteractionServiceLiveData.value } AutofillService.SERVICE_INTERFACE -> { pkg == selectedAutofillServiceLiveData.value } DreamService.SERVICE_INTERFACE -> { pkg in (enabledDreamServicesLiveData.value ?: emptyList()) } PrintService.SERVICE_INTERFACE -> { pkg !in (disabledPrintServicesLiveData.value ?: emptyList()) } DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE -> { pkg in (enabledDeviceAdminsLiveDataLiveData.value ?: emptyList()) } else -> true } } override fun onActive() { super.onActive() PackageBroadcastReceiver.addAllCallback(this) } override fun onInactive() { super.onInactive() PackageBroadcastReceiver.removeAllCallback(this) } /** * Repository for [ServiceLiveData] * *

Key value is a (string service name, required permission, user) triple, value is its * corresponding LiveData. */ companion object : DataRepositoryForPackage, ServiceLiveData>() { private const val LOG_TAG = "ServiceLiveData" override fun newValue(key: Triple): ServiceLiveData { return ServiceLiveData( PermissionControllerApplication.get(), key.first, key.second, key.third ) } } }