• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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