This guide is compatible with Health Connect version 1.1.0-beta02.
To write medical data using Personal Health Records (PHR) in Health Connect, follow these steps:
- Check for feature availability
- Request write permissions
- Create a data source (
MedicalDataSource
) - Write a medical resource (
MedicalResource
)
Feature availability
To determine whether a user's device supports PHR on Health Connect,
check the availability of FEATURE_PERSONAL_HEALTH_RECORD
on the client:
if (healthConnectClient
.features
.getFeatureStatus(
HealthConnectFeatures.FEATURE_PERSONAL_HEALTH_RECORD
) == HealthConnectFeatures.FEATURE_STATUS_AVAILABLE) {
// Feature is available
} else {
// Feature isn't available
}
See Check for feature availability to learn more.
Required permissions
Writing medical data is protected by the following permission:
android.permission.health.WRITE_MEDICAL_DATA
Declare these permissions in the Play Console for your app, as well as in your app's manifest:
<application>
<uses-permission
android:name="android.permission.health.WRITE_MEDICAL_DATA" />
</application>
You are responsible for declaring all the appropriate permissions you intend to use in your devices and apps. You should also check that each permission has been granted by the user before use.
Request permissions from the user
After creating a client instance, your app needs to request permissions from the user. Users must be allowed to grant or deny permissions at any time.
To do so, create a set of permissions for the required data types. Make sure that the permissions in the set are declared in your Android manifest first.
// Create a set of permissions for required data types
import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_WRITE_MEDICAL_DATA
val PERMISSIONS =
setOf(
PERMISSION_WRITE_MEDICAL_DATA
)
Use getGrantedPermissions
to see if your app already has the
required permissions granted. If not, use
createRequestPermissionResultContract
to request
those permissions. This displays the Health Connect permissions screen.
// Create the permissions launcher
val requestPermissionActivityContract = PermissionController.createRequestPermissionResultContract()
val requestPermissions = registerForActivityResult(requestPermissionActivityContract) { granted ->
if (granted.containsAll(PERMISSIONS)) {
// Permissions successfully granted
} else {
// Lack of required permissions
}
}
suspend fun checkPermissionsAndRun(healthConnectClient: HealthConnectClient) {
val granted = healthConnectClient.permissionController.getGrantedPermissions()
if (granted.containsAll(PERMISSIONS)) {
// Permissions already granted; proceed with inserting or reading data
} else {
requestPermissions.launch(PERMISSIONS)
}
}
Because users can grant or revoke permissions at any time, your app needs to periodically check for granted permissions and handle scenarios where permission is lost.
Data Sources
A MedicalDataSource
in Health Connect represents a
user-facing source of data, such as a healthcare organization, a hospital, or an
API.
Medical records stored in Health Connect are organized into a
MedicalDataSource
. This allows separation of medical records for the same
individual that come from different sources such as APIs or healthcare systems.
If all records originate from the same source, a writing app need only create
one MedicalDataSource
. If records originate from multiple sources, an app can
still create a single MedicalDataSource
if that data is reconciled and all
records have a unique combination of FHIR resource type and FHIR resource ID.
Otherwise, a MedicalDataSource
should be created for each data source.
All medical records must be associated with a MedicalDataSource
, so this must
be created before writing the resources.
Properties of MedicalDataSource
:
- Display Name (required) - User facing display name for the data source, uniquely identified per writing app.
FHIR Base URI (required) - For data coming from a FHIR server this should be the FHIR base URL (for example,
https://example.com/fhir/
). Multiple data sources can be associated with the same FHIR base URL.If the data is generated by an app without an FHIR URL, this should be a unique and understandable URI defined by the app (for example,
myapp://..
) that points to the source of the data.As an example, if a client app supports deep linking, this deeplink could be used as the FHIR Base URI. The maximum length for the URI is 2000 characters.
Package name (populated automatically) - The app writing the data.
FHIR Version (required) - The FHIR version. Must be a supported version.
Create a MedicalDataSource record
Create a record for each healthcare organization or entity your app is linked to.
// Create a `MedicalDataSource`
// Note that `displayName` must be unique across `MedicalDataSource`s
// Each `MedicalDataSource` is assigned an `id` by the system on creation
val medicalDataSource: MedicalDataSource =
healthConnectClient.createMedicalDataSource(
CreateMedicalDataSourceRequest(
fhirBaseUri = Uri.parse("https://fhir.com/oauth/api/FHIR/R4/"),
displayName = "Test Data Source",
fhirVersion = FhirVersion(4, 0, 1)
)
)
Delete a MedicalDataSource record
The previous example returns an id
by the system on creation. If you need to
delete the MedicalDataSource
record, reference that same id
:
// Delete the `MedicalDataSource` that has the specified `id`
healthConnectClient.deleteMedicalDataSourceWithData(medicalDataSource.id)
Medical resources
A MedicalResource
in Health Connect represents an FHIR
resource (which contains a medical record), along with metadata.
Properties of MedicalResource
:
- DataSourceId (required) - Data source as described for a
MedicalDataSource
. - FHIR Version (required) - The FHIR version. Must be a supported version.
- FHIR Resource (required) The JSON-encoded FHIR resource instance.
- Medical Resource type (populated automatically) - The user-facing category of the resource, mapping to user-facing permissions.
Prepare FHIR resources in JSON
Prior to writing medical resources to Health Connect, prepare your FHIR resource records in JSON. Store each JSON in its own variable for inserting as a medical resource.
If you need help with the FHIR JSON format, refer to the example data provided by the HL7 organization.
Insert or update MedicalResource records
Use UpsertMedicalResourceRequest
to insert new or update existing
MedicalResource
records for a MedicalDataSource
:
// Insert `MedicalResource`s into the `MedicalDataSource`
val medicalResources: List<MedicalResource> =
healthConnectClient.upsertMedicalResources(
listOf(
UpsertMedicalResourceRequest(
medicalDataSource.id,
medicalDataSource.fhirVersion,
medicationJsonToInsert // a valid FHIR json string
)
)
)
// Update `MedicalResource`s in the `MedicalDataSource`
val updatedMedicalResources: List<MedicalResource> =
healthConnectClient.upsertMedicalResources(
listOf(
UpsertMedicalResourceRequest(
medicalDataSource.id,
medicalDataSource.fhirVersion,
// a valid FHIR json string
// if this resource has the same type and ID as in `medicationJsonToInsert`,
// this `upsertMedicalResources()` call will update the previously inserted
// `MedicalResource`
updatedMedicationJsonToInsert
)
)
)
Example FHIR resource
In the previous example, the variable medicationJsonToInsert
represented a
valid FHIR JSON string.
Here is an example of what that JSON might look like, using AllergyIntolerance
as the FHIR resource type, which would map to the Medical Resource Type of
FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE
in PHR:
{
"resourceType": "AllergyIntolerance",
"id": "allergyintolerance-1",
"criticality": "high",
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "91936005",
"display": "Penicillin allergy"
}
],
"text": "Penicillin allergy"
},
"recordedDate": "2020-10-09T14:58:00+00:00",
"asserter": {
"reference": "Patient/patient-1"
},
"lastOccurrence": "2020-10-09",
"patient": {
"reference": "Patient/patient-1",
"display": "B., Alex"
}
...
}
Delete a MedicalResource record
MedicalResource
records may be deleted by ID:
// Delete `MedicalResource`s matching the specified `dataSourceId`, `type` and `fhirResourceId`
healthConnectClient.deleteMedicalResources(
medicalResources.map { medicalResource: MedicalResource ->
MedicalResourceId(
dataSourceId = medicalDataSource.id,
fhirResourceType = medicalResource.id.fhirResourceType,
fhirResourceId = medicalResource.id.fhirResourceId
)
}
)
Or they can be deleted by medicalResourceType
:
// Delete all `MedicalResource`s that are in any pair of provided `dataSourceIds` and
// `medicalResourceTypes`
healthConnectClient.deleteMedicalResources(
DeleteMedicalResourcesRequest(
dataSourceIds = setOf(medicalDataSource.id),
medicalResourceTypes = setOf(MEDICAL_RESOURCE_TYPE_MEDICATIONS)
)
)