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