1 /* <lambda>null2 * Copyright (C) 2022 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.healthconnect.controller.dataaccess 18 19 import com.android.healthconnect.controller.permissions.api.GetGrantedHealthPermissionsUseCase 20 import com.android.healthconnect.controller.permissions.data.HealthPermission 21 import com.android.healthconnect.controller.permissions.data.HealthPermissionType 22 import com.android.healthconnect.controller.permissions.data.PermissionsAccessType 23 import com.android.healthconnect.controller.service.IoDispatcher 24 import com.android.healthconnect.controller.shared.HealthPermissionReader 25 import com.android.healthconnect.controller.shared.app.AppInfoReader 26 import com.android.healthconnect.controller.shared.app.AppMetadata 27 import com.android.healthconnect.controller.shared.usecase.UseCaseResults 28 import javax.inject.Inject 29 import javax.inject.Singleton 30 import kotlinx.coroutines.CoroutineDispatcher 31 import kotlinx.coroutines.withContext 32 33 @Singleton 34 class LoadDataAccessUseCase 35 @Inject 36 constructor( 37 private val loadPermissionTypeContributorAppsUseCase: LoadPermissionTypeContributorAppsUseCase, 38 private val loadGrantedHealthPermissionsUseCase: GetGrantedHealthPermissionsUseCase, 39 private val healthPermissionReader: HealthPermissionReader, 40 private val appInfoReader: AppInfoReader, 41 @IoDispatcher private val dispatcher: CoroutineDispatcher 42 ) { 43 /** Returns a map of [DataAccessAppState] to apps. */ 44 suspend operator fun invoke( 45 permissionType: HealthPermissionType 46 ): UseCaseResults<Map<DataAccessAppState, List<AppMetadata>>> = 47 withContext(dispatcher) { 48 try { 49 val appsWithHealthPermissions: List<String> = 50 healthPermissionReader.getAppsWithHealthPermissions() 51 val contributingApps: List<AppMetadata> = 52 loadPermissionTypeContributorAppsUseCase.invoke(permissionType) 53 val readAppMetadataSet: MutableSet<AppMetadata> = mutableSetOf() 54 val writeAppMetadataSet: MutableSet<AppMetadata> = mutableSetOf() 55 val writeAppPackageNameSet: MutableSet<String> = mutableSetOf() 56 val inactiveAppMetadataSet: MutableSet<AppMetadata> = mutableSetOf() 57 58 appsWithHealthPermissions.forEach { 59 val permissionsPerPackage: List<String> = 60 loadGrantedHealthPermissionsUseCase(it) 61 62 // Apps that can READ the given healthPermissionType. 63 if (permissionsPerPackage.contains( 64 HealthPermission(permissionType, PermissionsAccessType.READ).toString())) { 65 readAppMetadataSet.add(appInfoReader.getAppMetadata(it)) 66 } 67 // Apps that can WRITE the given healthPermissionType. 68 if (permissionsPerPackage.contains( 69 HealthPermission(permissionType, PermissionsAccessType.WRITE).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 DataAccessAppState.Read to 85 alphabeticallySortedMetadataList(readAppMetadataSet), 86 DataAccessAppState.Write to 87 alphabeticallySortedMetadataList(writeAppMetadataSet), 88 DataAccessAppState.Inactive to 89 alphabeticallySortedMetadataList(inactiveAppMetadataSet)) 90 UseCaseResults.Success(appAccess) 91 } catch (ex: Exception) { 92 UseCaseResults.Failed(ex) 93 } 94 } 95 96 private fun alphabeticallySortedMetadataList( 97 packageNames: Set<AppMetadata> 98 ): List<AppMetadata> { 99 return packageNames.sortedBy { appMetadata -> appMetadata.appName } 100 } 101 } 102