/*
* Copyright (C) 2022 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.
*/
package androidx.health.platform.client.impl
import android.annotation.SuppressLint
import android.content.Context
import androidx.annotation.RestrictTo
import androidx.health.platform.client.HealthDataAsyncClient
import androidx.health.platform.client.SdkConfig
import androidx.health.platform.client.impl.internal.ProviderConnectionManager
import androidx.health.platform.client.impl.ipc.Client
import androidx.health.platform.client.impl.ipc.ClientConfiguration
import androidx.health.platform.client.impl.ipc.internal.ConnectionManager
import androidx.health.platform.client.impl.permission.token.PermissionTokenManager
import androidx.health.platform.client.permission.Permission
import androidx.health.platform.client.proto.DataProto
import androidx.health.platform.client.proto.PermissionProto
import androidx.health.platform.client.proto.RequestProto
import androidx.health.platform.client.proto.ResponseProto
import androidx.health.platform.client.request.AggregateDataRequest
import androidx.health.platform.client.request.DeleteDataRangeRequest
import androidx.health.platform.client.request.DeleteDataRequest
import androidx.health.platform.client.request.GetChangesRequest
import androidx.health.platform.client.request.GetChangesTokenRequest
import androidx.health.platform.client.request.ReadDataRangeRequest
import androidx.health.platform.client.request.ReadDataRequest
import androidx.health.platform.client.request.RequestContext
import androidx.health.platform.client.request.UpsertDataRequest
import androidx.health.platform.client.service.IHealthDataService
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.Executors
/** An IPC backed HealthDataClient implementation. */
@RestrictTo(RestrictTo.Scope.LIBRARY)
@SuppressWarnings("RestrictedApi")
class ServiceBackedHealthDataClient(
private val context: Context,
clientConfiguration: ClientConfiguration,
connectionManager: ConnectionManager,
) :
Client<IHealthDataService>(
clientConfiguration,
connectionManager,
IHealthDataService.Stub::asInterface,
IHealthDataService::getApiVersion
),
HealthDataAsyncClient {
private val callingPackageName = context.packageName
private val executorService = Executors.newSingleThreadScheduledExecutor()
@get:SuppressLint("RestrictedApi") // Use permission token manager internal
val backgroundRequestContext: RequestContext
get() =
RequestContext(
callingPackageName,
SdkConfig.SDK_VERSION,
PermissionTokenManager.getCurrentToken(context),
false
)
val requestContext: RequestContext
get() =
RequestContext(
callingPackageName,
SdkConfig.SDK_VERSION,
PermissionTokenManager.getCurrentToken(context),
true
)
constructor(
context: Context,
clientConfiguration: ClientConfiguration,
) : this(context, clientConfiguration, ProviderConnectionManager.getInstance(context))
override fun getGrantedPermissions(
permissions: Set<PermissionProto.Permission>,
): ListenableFuture<Set<PermissionProto.Permission>> {
return executeWithVersionCheck(
{ service, resultFuture ->
service.getGrantedPermissions(
requestContext,
permissions.map { Permission(it) }.toList(),
GetGrantedPermissionsCallback(resultFuture)
)
},
IHealthDataService.MIN_API_VERSION
)
}
override fun revokeAllPermissions(): ListenableFuture<Unit> {
return executeWithVersionCheck(
{ service, resultFuture ->
service.revokeAllPermissions(
requestContext,
RevokeAllPermissionsCallback(resultFuture)
)
},
IHealthDataService.MIN_API_VERSION
)
}
override fun insertData(
dataCollection: List<DataProto.DataPoint>
): ListenableFuture<List<String>> {
val request = UpsertDataRequest(dataCollection)
return executeWithVersionCheck(
{ service, resultFuture ->
service.insertData(requestContext, request, InsertDataCallback(resultFuture))
},
IHealthDataService.MIN_API_VERSION
)
}
override fun updateData(dataCollection: List<DataProto.DataPoint>): ListenableFuture<Unit> {
val request = UpsertDataRequest(dataCollection)
return executeWithVersionCheck(
{ service, resultFuture ->
service.updateData(requestContext, request, UpdateDataCallback(resultFuture))
},
IHealthDataService.MIN_API_VERSION
)
}
override fun deleteData(
uidsCollection: List<RequestProto.DataTypeIdPair>,
clientIdsCollection: List<RequestProto.DataTypeIdPair>
): ListenableFuture<Unit> {
val request = DeleteDataRequest(uidsCollection, clientIdsCollection)
return executeWithVersionCheck(
{ service, resultFuture ->
service.deleteData(requestContext, request, DeleteDataCallback(resultFuture))
},
IHealthDataService.MIN_API_VERSION
)
}
override fun deleteDataRange(
dataCollection: RequestProto.DeleteDataRangeRequest
): ListenableFuture<Unit> {
val request = DeleteDataRangeRequest(dataCollection)
return executeWithVersionCheck(
{ service, resultFuture ->
service.deleteDataRange(
requestContext,
request,
DeleteDataRangeCallback(resultFuture)
)
},
IHealthDataService.MIN_API_VERSION
)
}
override fun readData(
dataCollection: RequestProto.ReadDataRequest
): ListenableFuture<DataProto.DataPoint> {
val request = ReadDataRequest(dataCollection)
return executeWithVersionCheck(
{ service, resultFuture ->
service.readData(requestContext, request, ReadDataCallback(resultFuture))
},
IHealthDataService.MIN_API_VERSION
)
}
override fun readDataRange(
dataCollection: RequestProto.ReadDataRangeRequest
): ListenableFuture<ResponseProto.ReadDataRangeResponse> {
val request = ReadDataRangeRequest(dataCollection)
return executeWithVersionCheck(
{ service, resultFuture ->
service.readDataRange(requestContext, request, ReadDataRangeCallback(resultFuture))
},
IHealthDataService.MIN_API_VERSION
)
}
override fun aggregate(
request: RequestProto.AggregateDataRequest
): ListenableFuture<ResponseProto.AggregateDataResponse> {
return executeWithVersionCheck(
{ service, resultFuture ->
service.aggregate(
requestContext,
AggregateDataRequest(request),
AggregateDataCallback(resultFuture)
)
},
IHealthDataService.MIN_API_VERSION
)
}
override fun getChangesToken(
request: RequestProto.GetChangesTokenRequest
): ListenableFuture<ResponseProto.GetChangesTokenResponse> {
return executeWithVersionCheck(
{ service, resultFuture ->
service.getChangesToken(
requestContext,
GetChangesTokenRequest(request),
GetChangesTokenCallback(resultFuture)
)
},
IHealthDataService.MIN_API_VERSION
)
}
override fun getChanges(
request: RequestProto.GetChangesRequest
): ListenableFuture<ResponseProto.GetChangesResponse> {
return executeWithVersionCheck(
{ service, resultFuture ->
service.getChanges(
requestContext,
GetChangesRequest(request),
GetChangesCallback(resultFuture)
)
},
IHealthDataService.MIN_API_VERSION
)
}
}