/* * 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 android.health.connect; import static android.health.connect.Constants.DEFAULT_LONG; import static android.health.connect.Constants.MAXIMUM_PAGE_SIZE; import static android.health.connect.HealthPermissions.MANAGE_HEALTH_DATA_PERMISSION; import static android.health.connect.HealthPermissions.MANAGE_HEALTH_PERMISSIONS; import static com.android.healthfitness.flags.Flags.FLAG_EXPORT_IMPORT; import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserHandleAware; import android.annotation.WorkerThread; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.health.connect.accesslog.AccessLog; import android.health.connect.accesslog.AccessLogsResponseParcel; import android.health.connect.aidl.ActivityDatesRequestParcel; import android.health.connect.aidl.ActivityDatesResponseParcel; import android.health.connect.aidl.AggregateDataRequestParcel; import android.health.connect.aidl.AggregateDataResponseParcel; import android.health.connect.aidl.ApplicationInfoResponseParcel; import android.health.connect.aidl.DeleteUsingFiltersRequestParcel; import android.health.connect.aidl.GetPriorityResponseParcel; import android.health.connect.aidl.HealthConnectExceptionParcel; import android.health.connect.aidl.IAccessLogsResponseCallback; import android.health.connect.aidl.IActivityDatesResponseCallback; import android.health.connect.aidl.IAggregateRecordsResponseCallback; import android.health.connect.aidl.IApplicationInfoResponseCallback; import android.health.connect.aidl.IChangeLogsResponseCallback; import android.health.connect.aidl.IDataStagingFinishedCallback; import android.health.connect.aidl.IEmptyResponseCallback; import android.health.connect.aidl.IGetChangeLogTokenCallback; import android.health.connect.aidl.IGetHealthConnectDataStateCallback; import android.health.connect.aidl.IGetHealthConnectMigrationUiStateCallback; import android.health.connect.aidl.IGetPriorityResponseCallback; import android.health.connect.aidl.IHealthConnectService; import android.health.connect.aidl.IInsertRecordsResponseCallback; import android.health.connect.aidl.IMigrationCallback; import android.health.connect.aidl.IReadRecordsResponseCallback; import android.health.connect.aidl.IRecordTypeInfoResponseCallback; import android.health.connect.aidl.InsertRecordsResponseParcel; import android.health.connect.aidl.ReadRecordsResponseParcel; import android.health.connect.aidl.RecordIdFiltersParcel; import android.health.connect.aidl.RecordTypeInfoResponseParcel; import android.health.connect.aidl.RecordsParcel; import android.health.connect.aidl.UpdatePriorityRequestParcel; import android.health.connect.changelog.ChangeLogTokenRequest; import android.health.connect.changelog.ChangeLogTokenResponse; import android.health.connect.changelog.ChangeLogsRequest; import android.health.connect.changelog.ChangeLogsResponse; import android.health.connect.datatypes.AggregationType; import android.health.connect.datatypes.DataOrigin; import android.health.connect.datatypes.MedicalResource; import android.health.connect.datatypes.Record; import android.health.connect.exportimport.ExportImportDocumentProvider; import android.health.connect.exportimport.IQueryDocumentProvidersCallback; import android.health.connect.exportimport.IScheduledExportStatusCallback; import android.health.connect.exportimport.ScheduledExportSettings; import android.health.connect.exportimport.ScheduledExportStatus; import android.health.connect.internal.datatypes.RecordInternal; import android.health.connect.internal.datatypes.utils.InternalExternalRecordConverter; import android.health.connect.migration.HealthConnectMigrationUiState; import android.health.connect.migration.MigrationEntity; import android.health.connect.migration.MigrationEntityParcel; import android.health.connect.migration.MigrationException; import android.health.connect.restore.StageRemoteDataException; import android.health.connect.restore.StageRemoteDataRequest; import android.os.Binder; import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.Period; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.stream.Collectors; /** * This class provides APIs to interact with the centralized HealthConnect storage maintained by the * system. * *
HealthConnect is an offline, on-device storage that unifies data from multiple devices and * apps into an ecosystem featuring. * *
The basic unit of data in HealthConnect is represented as a {@link Record} object, which is * the base class for all the other data types such as {@link * android.health.connect.datatypes.StepsRecord}. */ @SystemService(Context.HEALTHCONNECT_SERVICE) public class HealthConnectManager { /** * Used in conjunction with {@link android.content.Intent#ACTION_VIEW_PERMISSION_USAGE} to * launch UI to show an app’s health permission rationale/data policy. * *
Note: Used by apps to define an intent filter in conjunction with {@link * android.content.Intent#ACTION_VIEW_PERMISSION_USAGE} that the HC UI can link out to. */ // We use intent.category prefix to be compatible with HealthPermissions strings definitions. @SdkConstant(SdkConstant.SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_HEALTH_PERMISSIONS = "android.intent.category.HEALTH_PERMISSIONS"; /** * Activity action: Launch UI to manage (e.g. grant/revoke) health permissions. * *
Shows a list of apps which request at least one permission of the Health permission group. * *
Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with the name of the * app requesting the action. Optional: Adding package name extras launches a UI to manager * (e.g. grant/revoke) for this app. */ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_HEALTH_PERMISSIONS = "android.health.connect.action.MANAGE_HEALTH_PERMISSIONS"; /** * Activity action: Launch UI to share the route associated with an exercise session. * *
Input: caller must provide `String` extra EXTRA_SESSION_ID * *
Result will be delivered via [Activity.onActivityResult] with `ExerciseRoute` * EXTRA_EXERCISE_ROUTE. */ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_EXERCISE_ROUTE = "android.health.connect.action.REQUEST_EXERCISE_ROUTE"; /** * A string ID of a session to be used with {@link #ACTION_REQUEST_EXERCISE_ROUTE}. * *
This is used to specify route of which exercise session we want to request. */ public static final String EXTRA_SESSION_ID = "android.health.connect.extra.SESSION_ID"; /** * An exercise route requested via {@link #ACTION_REQUEST_EXERCISE_ROUTE}. * *
This is returned for a successful request to access a route associated with an exercise * session. */ public static final String EXTRA_EXERCISE_ROUTE = "android.health.connect.extra.EXERCISE_ROUTE"; /** * Activity action: Launch UI to show and manage (e.g. grant/revoke) health permissions. * *
Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with the name of the * app requesting the action must be present. An app can open only its own page. * *
Input: caller must provide `String[]` extra [EXTRA_PERMISSIONS] * *
Result will be delivered via [Activity.onActivityResult] with `String[]` * [EXTRA_PERMISSIONS] and `int[]` [EXTRA_PERMISSION_GRANT_RESULTS], similar to * [Activity.onRequestPermissionsResult] * * @hide */ @SystemApi @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_HEALTH_PERMISSIONS = "android.health.connect.action.REQUEST_HEALTH_PERMISSIONS"; /** * Activity action: Launch UI to health connect home settings screen. * *
shows a list of recent apps that accessed (e.g. read/write) health data and allows the * user to access health permissions and health data. * * @hide */ @SystemApi @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_HEALTH_HOME_SETTINGS = "android.health.connect.action.HEALTH_HOME_SETTINGS"; /** * Activity action: Launch UI to show and manage (e.g. delete/export) health data. * *
shows a list of health data categories and actions to manage (e.g. delete/export) health * data. * * @hide */ @SystemApi @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_HEALTH_DATA = "android.health.connect.action.MANAGE_HEALTH_DATA"; /** * Activity action: Display information regarding migration - e.g. asking the user to take some * action (e.g. update the system) so that migration can take place. * *
Note: Callers of the migration APIs must handle this intent. * * @hide */ @SystemApi @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SHOW_MIGRATION_INFO = "android.health.connect.action.SHOW_MIGRATION_INFO"; /** * Broadcast Action: Health Connect is ready to accept migrated data. * *
This broadcast is explicitly sent to Health Connect migration aware * applications to prompt them to start/continue HC data migration. Migration aware applications * are those that both hold {@code android.permission.MIGRATE_HEALTH_CONNECT_DATA} and handle * {@code android.health.connect.action.SHOW_MIGRATION_INFO}. * *
This is a protected intent that can only be sent by the system. * * @hide */ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) @SystemApi public static final String ACTION_HEALTH_CONNECT_MIGRATION_READY = "android.health.connect.action.HEALTH_CONNECT_MIGRATION_READY"; /** * Unknown download state considered to be the default download state. * *
See also {@link #updateDataDownloadState} * * @hide */ @SystemApi public static final int DATA_DOWNLOAD_STATE_UNKNOWN = 0; /** * Indicates that the download has started. * *
See also {@link #updateDataDownloadState} * * @hide */ @SystemApi public static final int DATA_DOWNLOAD_STARTED = 1; /** * Indicates that the download is being retried. * *
See also {@link #updateDataDownloadState} * * @hide */ @SystemApi public static final int DATA_DOWNLOAD_RETRY = 2; /** * Indicates that the download has failed. * *
See also {@link #updateDataDownloadState} * * @hide */ @SystemApi public static final int DATA_DOWNLOAD_FAILED = 3; /** * Indicates that the download has completed. * *
See also {@link HealthConnectManager#updateDataDownloadState} * * @hide */ @SystemApi public static final int DATA_DOWNLOAD_COMPLETE = 4; /** * Unknown error during the last data export. * * @hide */ @FlaggedApi(FLAG_EXPORT_IMPORT) public static final int DATA_EXPORT_ERROR_UNKNOWN = 0; /** * No error during the last data export. * * @hide */ @FlaggedApi(FLAG_EXPORT_IMPORT) public static final int DATA_EXPORT_ERROR_NONE = 1; /** * Indicates that the last export failed because we lost access to the export file location. * * @hide */ @FlaggedApi(FLAG_EXPORT_IMPORT) public static final int DATA_EXPORT_LOST_FILE_ACCESS = 2; /** * Activity action: Launch activity exported by client application that handles onboarding to * Health Connect. * *
Health Connect will invoke this intent whenever the user attempts to connect an app that * has exported an activity that responds to this intent. The launched activity is responsible * for making permission requests and any other prerequisites for connecting to Health Connect. * *
Applications exporting an activity that is launched by this intent must also
* guard it with {@link HealthPermissions#START_ONBOARDING} so that only the system can launch
* it.
*
* @hide
*/
@SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SHOW_ONBOARDING =
"android.health.connect.action.SHOW_ONBOARDING";
private static final String TAG = "HealthConnectManager";
private static final String HEALTH_PERMISSION_PREFIX = "android.permission.health.";
@SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
private static volatile Set Note: This API sets {@code PackageManager.FLAG_PERMISSION_USER_SET}.
*
* @hide
*/
@RequiresPermission(MANAGE_HEALTH_PERMISSIONS)
@UserHandleAware
public void grantHealthPermission(@NonNull String packageName, @NonNull String permissionName) {
try {
mService.grantHealthPermission(packageName, permissionName, mContext.getUser());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Revoke a health permission that was previously granted by {@link
* #grantHealthPermission(String, String)} The permission must have been requested by the
* application. If the application is not allowed to hold the permission, a {@link
* java.lang.SecurityException} is thrown. If the package or permission is invalid, a {@link
* java.lang.IllegalArgumentException} is thrown.
*
* Note: This API sets {@code PackageManager.FLAG_PERMISSION_USER_SET} or {@code
* PackageManager.FLAG_PERMISSION_USER_FIXED} based on the number of revocations of a particular
* permission for a package.
*
* @hide
*/
@SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression
@RequiresPermission(MANAGE_HEALTH_PERMISSIONS)
@UserHandleAware
public void revokeHealthPermission(
@NonNull String packageName, @NonNull String permissionName, @Nullable String reason) {
try {
mService.revokeHealthPermission(
packageName, permissionName, reason, mContext.getUser());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Revokes all health permissions that were previously granted by {@link
* #grantHealthPermission(String, String)} If the package is invalid, a {@link
* java.lang.IllegalArgumentException} is thrown.
*
* @hide
*/
@SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression
@RequiresPermission(MANAGE_HEALTH_PERMISSIONS)
@UserHandleAware
public void revokeAllHealthPermissions(@NonNull String packageName, @Nullable String reason) {
try {
mService.revokeAllHealthPermissions(packageName, reason, mContext.getUser());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns a list of health permissions that were previously granted by {@link
* #grantHealthPermission(String, String)}.
*
* @hide
*/
@RequiresPermission(MANAGE_HEALTH_PERMISSIONS)
@UserHandleAware
public List This is equivalent to calling {@link PackageManager#getPermissionFlags(String, String,
* UserHandle)} for each provided permission except it throws an exception for non-Health or
* undeclared permissions. Flag masks listed in {@link PackageManager#MASK_PERMISSION_FLAGS_ALL}
* can be used to check the flag values.
*
* Returned flags for invalid, non-Health or undeclared permissions are equal to zero.
*
* @return a map which contains all requested permissions as keys and corresponding flags as
* values.
* @throws IllegalArgumentException if the package doesn't exist, any of the permissions are not
* Health permissions or not declared by the app.
* @throws NullPointerException if any of the arguments is {@code null}.
* @throws SecurityException if the caller doesn't possess {@code
* android.permission.MANAGE_HEALTH_PERMISSIONS}.
* @hide
*/
@RequiresPermission(MANAGE_HEALTH_PERMISSIONS)
@UserHandleAware
public Map Note:
* This type is embedded in the {@link AggregationType} as {@link AggregationType} are
* typed in nature.
* Only {@link AggregationType}s that are of same type T can be queried together
* @param request request for different aggregation.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @see AggregateRecordsResponse#get
*/
@NonNull
@SuppressWarnings("unchecked")
public Note:
* This type is embedded in the {@link AggregationType} as {@link AggregationType} are
* typed in nature.
* Only {@link AggregationType}s that are of same type T can be queried together
* @param request request for different aggregation.
* @param duration Duration on which to group by results
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @see HealthConnectManager#aggregateGroupByPeriod
*/
@SuppressWarnings("unchecked")
public Note:
* This type is embedded in the {@link AggregationType} as {@link AggregationType} are
* typed in nature.
* Only {@link AggregationType}s that are of same type T can be queried together
* @param request Request for different aggregation.
* @param period Period on which to group by results
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @see AggregateRecordsGroupedByPeriodResponse#get
* @see HealthConnectManager#aggregateGroupByDuration
*/
@SuppressWarnings("unchecked")
public Deletions are performed in a transaction i.e. either all will be deleted or none
*
* @param request Request based on which to perform delete operation
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @hide
*/
@SystemApi
@RequiresPermission(MANAGE_HEALTH_PERMISSIONS)
public void deleteRecords(
@NonNull DeleteUsingFiltersRequest request,
@NonNull Executor executor,
@NonNull OutcomeReceiver Deletions are performed in a transaction i.e. either all will be deleted or none
*
* @param recordIds recordIds on which to perform delete operation.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @throws IllegalArgumentException if {@code recordIds is empty}
*/
public void deleteRecords(
@NonNull List Deletions are performed in a transaction i.e. either all will be deleted or none
*
* @param recordType recordType to perform delete operation on.
* @param timeRangeFilter time filter based on which to delete the records.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
*/
public void deleteRecords(
@NonNull Class extends Record> recordType,
@NonNull TimeRangeFilter timeRangeFilter,
@NonNull Executor executor,
@NonNull OutcomeReceiver Tokens from this request are to be passed to {HealthConnectManager#getChangeLogs}
*
* @param request A request to get changelog token
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
*/
public void getChangeLogToken(
@NonNull ChangeLogTokenRequest request,
@NonNull Executor executor,
@NonNull OutcomeReceiver If you are calling this function for the first time after a user unlock, this might take
* some time so consider calling this on a thread.
*
* @return Auto delete period in days, 0 is returned if auto delete period is not set.
* @throws RuntimeException for internal errors
* @hide
*/
@SystemApi
@RequiresPermission(MANAGE_HEALTH_DATA_PERMISSION)
@IntRange(from = 0, to = 7300)
public int getRecordRetentionPeriodInDays() {
try {
return mService.getRecordRetentionPeriodInDays(mContext.getUser());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets auto delete period (for all the records to be automatically deleted) for this user.
*
* Note: The max value of auto delete period can be 7300 i.e. ~20 years
*
* @param days Auto period to be set in days. Use 0 to unset this value.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @throws RuntimeException for internal errors
* @throws IllegalArgumentException if {@code days} is not between 0 and 7300
* @hide
*/
@SystemApi
@RequiresPermission(MANAGE_HEALTH_DATA_PERMISSION)
public void setRecordRetentionPeriodInDays(
@IntRange(from = 0, to = 7300) int days,
@NonNull Executor executor,
@NonNull OutcomeReceiver Number of records returned by this API will depend based on below factors:
*
* When an app with read permission allowed calls the API from background then it will be
* able to read only its own inserted records and will not get records inserted by other apps.
* This may be less than the total records present for the record type.
*
* When an app with read permission allowed calls the API from foreground then it will be
* able to read all records for the record type.
*
* App with only write permission but no read permission allowed will be able to read only
* its own inserted records both when in foreground or background.
*
* An app without both read and write permissions will not be able to read any record and the
* API will throw Security Exception.
*
* @param request Read request based on {@link ReadRecordsRequestUsingFilters} or {@link
* ReadRecordsRequestUsingIds}
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @throws IllegalArgumentException if request page size set is more than 5000 in {@link
* ReadRecordsRequestUsingFilters}
* @throws SecurityException if app without read or write permission tries to read.
*/
public In case the input record to be updated does not exist in the database or the caller is not
* the owner of the record then {@link HealthConnectException#ERROR_INVALID_ARGUMENT} will be
* thrown.
*
* @param records list of records to be updated.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @throws IllegalArgumentException if at least one of the records is missing both
* ClientRecordID and UUID.
*/
public void updateRecords(
@NonNull List The staged data will later be restored (integrated) into the existing Health Connect data.
* Any existing data will not be affected by the staged data.
*
* The file names passed should be the same as the ones on the original device that were
* backed up or are being transferred directly.
*
* If a file already exists in the staged data then it will be replaced. However, note that
* staging data is a one time process. And if the staged data has already been processed then
* any attempt to stage data again will be silently ignored.
*
* The caller is responsible for closing the original file descriptors. The file descriptors
* are duplicated and the originals may be closed by the application at any time after this API
* returns.
*
* The caller should update the data download states using {@link #updateDataDownloadState}
* before calling this API.
*
* @param pfdsByFileName The map of file names and their {@link ParcelFileDescriptor}s.
* @param executor The {@link Executor} on which to invoke the callback.
* @param callback The callback which will receive the outcome of this call.
* @hide
*/
@SystemApi
@UserHandleAware
@RequiresPermission(Manifest.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA)
public void stageAllHealthConnectRemoteData(
@NonNull Map The shared data must later be sent for Backup to cloud or another device.
*
* We are responsible for closing the original file descriptors. The caller must not close
* the FD before that.
*
* @param pfdsByFileName The map of file names and their {@link ParcelFileDescriptor}s.
* @hide
*/
public void getAllDataForBackup(@NonNull Map This deletes only the staged data leaving any other Health Connect data untouched.
*
* @hide
*/
@TestApi
@UserHandleAware
public void deleteAllStagedRemoteData() throws NullPointerException {
try {
mService.deleteAllStagedRemoteData(mContext.getUser());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Updates the download state of the Health Connect data.
*
* The data should've been downloaded and the corresponding download states updated before
* the app calls {@link #stageAllHealthConnectRemoteData}. Once {@link
* #stageAllHealthConnectRemoteData} has been called the downloaded state becomes {@link
* #DATA_DOWNLOAD_COMPLETE} and future attempts to update the download state are ignored.
*
* The only valid order of state transition are:
*
* Note that it's okay if some states are missing in of the sequences above but the order has
* to be one of the above.
*
* Only one app will have the permission to call this API so it is assured that no one else
* will be able to update this state.
*
* @param downloadState The download state which needs to be purely from {@link
* DataDownloadState}
* @hide
*/
@SystemApi
@UserHandleAware
@RequiresPermission(Manifest.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA)
public void updateDataDownloadState(@DataDownloadState int downloadState) {
try {
mService.updateDataDownloadState(downloadState);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Asynchronously returns the current UI state of Health Connect as it goes through the
* Data-Migration process. In case there was an error reading the data on the disk the error
* will be returned in the callback.
*
* See also {@link HealthConnectMigrationUiState} object describing the HealthConnect UI
* state.
*
* @param executor The {@link Executor} on which to invoke the callback.
* @param callback The callback which will receive the current {@link
* HealthConnectMigrationUiState} or the {@link HealthConnectException}.
* @hide
*/
@UserHandleAware
@RequiresPermission(MANAGE_HEALTH_DATA_PERMISSION)
@NonNull
public void getHealthConnectMigrationUiState(
@NonNull Executor executor,
@NonNull
OutcomeReceiver See also {@link HealthConnectDataState} object describing the HealthConnect state.
*
* @param executor The {@link Executor} on which to invoke the callback.
* @param callback The callback which will receive the current {@link HealthConnectDataState} or
* the {@link HealthConnectException}.
* @hide
*/
@SystemApi
@UserHandleAware
@RequiresPermission(
anyOf = {
MANAGE_HEALTH_DATA_PERMISSION,
Manifest.permission.MIGRATE_HEALTH_CONNECT_DATA
})
@NonNull
public void getHealthConnectDataState(
@NonNull Executor executor,
@NonNull OutcomeReceiver If you are calling this function for the first time after a user unlock, this might take
* some time so consider calling this on a thread.
*
* @return Period between scheduled exports in days, 0 is returned if period between scheduled
* exports is not set.
* @throws RuntimeException for internal errors
* @hide
*/
@WorkerThread
@RequiresPermission(MANAGE_HEALTH_DATA_PERMISSION)
@IntRange(from = 0, to = 30)
public int getScheduledExportPeriodInDays() {
try {
return mService.getScheduledExportPeriodInDays(mContext.getUser());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Queries the document providers available to be used for export/import.
*
* @throws RuntimeException for internal errors
* @hide
*/
@FlaggedApi(FLAG_EXPORT_IMPORT)
@WorkerThread
@RequiresPermission(MANAGE_HEALTH_DATA_PERMISSION)
public void queryDocumentProviders(
@NonNull Executor executor,
@NonNull
OutcomeReceiver Note: If we, for some reason, fail to retrieve these, we return an empty set rather
* than crashing the device. This means the health permissions infra will be inactive.
*
* @hide
*/
@NonNull
@SystemApi
public static Set Number of resources returned by this API will depend based on below factors:
*
* , HealthConnectException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
try {
mService.queryAccessLogs(
mContext.getPackageName(),
new IAccessLogsResponseCallback.Stub() {
@Override
public void onResult(AccessLogsResponseParcel parcel) {
Binder.clearCallingIdentity();
executor.execute(() -> callback.onResult(parcel.getAccessLogs()));
}
@Override
public void onError(HealthConnectExceptionParcel exception) {
returnError(executor, exception, callback);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* API to read records based on {@link ReadRecordsRequestUsingFilters} or {@link
* ReadRecordsRequestUsingIds}
*
*
*
*
* , HealthConnectException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
Objects.requireNonNull(recordTypes);
if (recordTypes.isEmpty()) {
throw new IllegalArgumentException("Record types list can not be empty");
}
try {
mService.getActivityDates(
new ActivityDatesRequestParcel(recordTypes),
new IActivityDatesResponseCallback.Stub() {
@Override
public void onResult(ActivityDatesResponseParcel parcel) {
Binder.clearCallingIdentity();
executor.execute(() -> callback.onResult(parcel.getDates()));
}
@Override
public void onError(HealthConnectExceptionParcel exception) {
returnError(executor, exception, callback);
}
});
} catch (RemoteException exception) {
exception.rethrowFromSystemServer();
}
}
/**
* Marks the start of the migration and block API calls.
*
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @hide
*/
@RequiresPermission(Manifest.permission.MIGRATE_HEALTH_CONNECT_DATA)
@SystemApi
public void startMigration(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver
, HealthConnectException>
callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
try {
mService.queryDocumentProviders(
mContext.getUser(),
new IQueryDocumentProvidersCallback.Stub() {
@Override
public void onResult(List
*
*
* @param ids Identifiers on which to perform read operation.
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive result of performing this operation.
* @throws IllegalArgumentException if {@code ids} is empty or its size is more than 5000.
*/
@FlaggedApi(FLAG_PERSONAL_HEALTH_RECORD)
public void readMedicalResources(
@NonNull List, HealthConnectException> callback) {
Objects.requireNonNull(ids);
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
if (ids.size() >= MAXIMUM_PAGE_SIZE) {
throw new IllegalArgumentException("Maximum allowed pageSize is " + MAXIMUM_PAGE_SIZE);
}
throw new UnsupportedOperationException("Not implemented");
}
}