1 /* <lambda>null2 * 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 package com.android.healthconnect.controller.data.access 17 18 import com.android.healthconnect.controller.permissions.api.IGetGrantedHealthPermissionsUseCase 19 import com.android.healthconnect.controller.permissions.data.HealthPermission.FitnessPermission 20 import com.android.healthconnect.controller.permissions.data.HealthPermissionType 21 import com.android.healthconnect.controller.permissions.data.PermissionsAccessType 22 import com.android.healthconnect.controller.service.IoDispatcher 23 import com.android.healthconnect.controller.shared.HealthPermissionReader 24 import com.android.healthconnect.controller.shared.app.AppInfoReader 25 import com.android.healthconnect.controller.shared.app.AppMetadata 26 import com.android.healthconnect.controller.shared.usecase.UseCaseResults 27 import javax.inject.Inject 28 import javax.inject.Singleton 29 import kotlinx.coroutines.CoroutineDispatcher 30 import kotlinx.coroutines.withContext 31 32 @Singleton 33 class LoadAccessUseCase 34 @Inject 35 constructor( 36 private val loadPermissionTypeContributorAppsUseCase: ILoadPermissionTypeContributorAppsUseCase, 37 private val loadGrantedHealthPermissionsUseCase: IGetGrantedHealthPermissionsUseCase, 38 private val healthPermissionReader: HealthPermissionReader, 39 private val appInfoReader: AppInfoReader, 40 @IoDispatcher private val dispatcher: CoroutineDispatcher 41 ) : ILoadAccessUseCase { 42 /** Returns a map of [AppAccessState] to apps. */ 43 override suspend operator fun invoke( 44 permissionType: HealthPermissionType 45 ): UseCaseResults<Map<AppAccessState, List<AppMetadata>>> = 46 withContext(dispatcher) { 47 try { 48 val appsWithFitnessPermissions: List<String> = 49 healthPermissionReader.getAppsWithFitnessPermissions() 50 val contributingApps: List<AppMetadata> = 51 loadPermissionTypeContributorAppsUseCase.invoke(permissionType) 52 val readAppMetadataSet: MutableSet<AppMetadata> = mutableSetOf() 53 val writeAppMetadataSet: MutableSet<AppMetadata> = mutableSetOf() 54 val writeAppPackageNameSet: MutableSet<String> = mutableSetOf() 55 val inactiveAppMetadataSet: MutableSet<AppMetadata> = mutableSetOf() 56 57 appsWithFitnessPermissions.forEach { 58 val permissionsPerPackage: List<String> = 59 loadGrantedHealthPermissionsUseCase(it) 60 61 // Apps that can READ the given healthPermissionType. 62 if (permissionsPerPackage.contains( 63 FitnessPermission(permissionType, PermissionsAccessType.READ).toString())) { 64 readAppMetadataSet.add(appInfoReader.getAppMetadata(it)) 65 } 66 // Apps that can WRITE the given healthPermissionType. 67 if (permissionsPerPackage.contains( 68 FitnessPermission(permissionType, PermissionsAccessType.WRITE) 69 .toString())) { 70 writeAppMetadataSet.add(appInfoReader.getAppMetadata(it)) 71 writeAppPackageNameSet.add(it) 72 } 73 } 74 // Apps that are inactive: can no longer WRITE, but still have data in Health 75 // Connect. 76 contributingApps.forEach { app -> 77 if (!writeAppPackageNameSet.contains(app.packageName)) { 78 inactiveAppMetadataSet.add(app) 79 } 80 } 81 82 val appAccess = 83 mapOf( 84 AppAccessState.Read to alphabeticallySortedMetadataList(readAppMetadataSet), 85 AppAccessState.Write to 86 alphabeticallySortedMetadataList(writeAppMetadataSet), 87 AppAccessState.Inactive to 88 alphabeticallySortedMetadataList(inactiveAppMetadataSet)) 89 UseCaseResults.Success(appAccess) 90 } catch (ex: Exception) { 91 UseCaseResults.Failed(ex) 92 } 93 } 94 95 private fun alphabeticallySortedMetadataList( 96 packageNames: Set<AppMetadata> 97 ): List<AppMetadata> { 98 return packageNames.sortedBy { appMetadata -> appMetadata.appName } 99 } 100 } 101 102 interface ILoadAccessUseCase { invokenull103 suspend fun invoke( 104 permissionType: HealthPermissionType 105 ): UseCaseResults<Map<AppAccessState, List<AppMetadata>>> 106 } 107