Write medical data

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:

  1. Check for feature availability
  2. Request write permissions
  3. Create a data source (MedicalDataSource)
  4. 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)
    )
)