Measure Blood Oxygen Level and Heart Rate on Galaxy Watch


Objective

Create a health app for Galaxy Watch, operating on Wear OS powered by Samsung, utilizing Samsung Privileged Health SDK to trigger and obtain results of simultaneous blood oxygen level (SpO2) and heart rate measurements.

Partnership Request

In this Code Lab, you will use a specially prepared mock library. It has limited functionality and uses dummy data instead of real-time data. To get real values, you will need the full version of the Samsung Privileged Health SDK library, which is available to Samsung partners. Apply as a partner by checking out the Partner App Program to get exclusive access to the Samsung Privileged Health SDK.

Overview

Samsung Privileged Health SDK provides means of accessing and tracking health information contained in the health data storage. Its tracking service gives raw and processed sensor data such as accelerometer and body composition data sent by the Samsung BioActive sensor. The latest BioActive sensor of Galaxy Watch runs powerful health sensors such as photoplethysmogram (PPG), electrocardiogram (ECG), bioelectrical impedance analysis (BIA), sweat loss, and SpO2.

See Samsung Privileged Health SDK descriptions for detailed information.

Set up your environment

You will need the following:

  • Galaxy Watch4 or newer
  • Android Studio (latest version recommended)
  • Java SE Development Kit (JDK) 11 or later

Sample Code

Here is a sample code for you to start coding in this Code Lab. Download it and start your learning experience!

Measuring Blood Oxygen Level and Heart Rate Sample Code
(135.23 KB)

Connect your Galaxy Watch to Wi-Fi

  1. Go to Settings > Connection > Wi-Fi and make sure that Wi-Fi is enabled.

  2. From the list of available Wi-Fi networks, choose and connect to the same one as your PC.

Turn on Developer Mode and adjust its settings

  1. On your watch, go to Settings > About watch > Software and tap on Software version 5 times.

  2. Upon successful activation of Developer mode, a toast message will display as on the image below.

  3. Afterwards, Developer options will be visible under Settings.

  4. Tap Developer options and enable the following options:

    • ADB debugging

    • In Developer options find Wireless debugging

    • Turn on Wireless debugging

    • Check Always allow on this network and tap Allow

    • Go back to Developer options and click Turn off automatic Wi-Fi

Connect your Galaxy Watch to Android Studio

  1. Go to Settings > Developer options > Wireless debugging and choose Pair new device.

  2. Take note of the Wi-Fi pairing code, IP address & Port.

  3. In Android Studio, go to Terminal and type:

    adb pair <IP address>:<port> <Wi-Fi pairing code>
    
  4. When prompted, tap Always allow from this computer to allow debugging.

  5. After successfully pairing, type:

    adb connect <IP address of your watch>:<port>
    

    Upon successful connection, you will see the following message in Android Studio’s terminal:

    connected to <IP address of your watch>
    

    Now, you can run the app directly on your watch.

Turn on Developer Mode for Health Platform

This step is only applicable to registered partners of Samsung Privileged Health SDK. You can replace priv-health-tracking-mock-2023.aar in app/libs with the real library to receive real sensor data with the application. This requires Health Platform to be set in developer mode:

  1. On your watch go to Settings > Apps > Health Platform.

  2. Quickly tap Health Platform title for 10 times. This enables developer mode and displays [Dev mode] below the title.

  3. To stop using developer mode, quickly tap Health Platform title for 10 times to disable it.

Start your project

After downloading the sample code containing the project files, in Android Studio click Open to open existing project.

Locate the downloaded Android project from the directory and click OK.

Establish service connection and check capabilities

For the device to track data with the Samsung Privileged Health SDK, it must connect to the service by HealthTrackingService API. After establishing a connection, verify if the required tracker type is available. To check this, get the list of available tracker types and verify that the tracker is on the list. In this Code Lab, you will utilize blood oxygen level and heart rate trackers. The HealthTrackingService API usage is in the table below.

HealthTrackingService

HealthTrackingService initiates a connection to Samsung's health tracking service and provides a HealthTracker instance to track a HealthTrackerType.

public void

connectService()

Establish a connection with Samsung's health tracking service.

public void

disconnectService()

Release a connection for Samsung's health tracking service.

public HealthTrackerCapability

getTrackingCapability()

Provide a HealthTrackerCapability instance to get a supporting health tracker type list.

Initialize multiple trackers

Before the measurement starts, initialize the SpO2 tracker by obtaining the proper health tracker object.

In the ConnectionManager.java file, navigate to initSpO2(), create an oxygen saturation HealthTracker instance, and pass it to the SpO2Listener instance.

  • Get the HealthTracker object using the HealthTrackingService API.
  • Use the HealthTrackerType.SPO2 type as parameter.
HealthTrackingService

HealthTrackingService initiates a connection to Samsung's health tracking service and provides a HealthTracker instance to track a HealthTrackerType.

public HealthTracker

getHealthTracker(HealthTrackerType healthTrackerType)

Provide a HealthTracker instance for the given healthTrackerType.

  • Pass the HealthTracker object to the SpO2Listener instance object.
SpO2Listener

public void

setHealthTracker(HealthTracker tracker)

Set HealthTracker instance for the given tracker.


/*******************************************************************************************
 * [Practice 1] Create blood oxygen level health tracker object
 *  - get health tracker object
 *  - pass it to spO2Listener
 -------------------------------------------------------------------------------------------
 *  - (Hint) Replace TODO 1 with parts of code
 *      (1) get HealthTracker object using healthTrackingService.getHealthTracker()
 *          use HealthTrackerType.SPO2 type as parameter
 *      (2) pass it to spO2Listener using setHealthTracker function
 ******************************************************************************************/

public void initSpO2(SpO2Listener spO2Listener) {
    //"TODO 1 (1)"
    //"TODO 1 (2)"
    setHandlerForBaseListener(spO2Listener);
}
  

Next, in the ConnectionManager.java file, in the initHeartRate() function, create a heart rate HealthTracker instance, and pass it to the HeartRateListener instance.

  • Get the HealthTracker object using the HealthTrackingService API.
  • Use the HealthTrackerType.HEART_RATE type as parameter.
  • Pass the HealthTracker object to the HeartRateListener instance object.
HeartRateListener

public void

setHealthTracker(HealthTracker tracker)

Set HealthTracker instance for the given tracker.


/*******************************************************************************************
 * [Practice 2] Create heart rate health tracker object
 *  - get health tracker object
 *  - pass it to heartRateListener
 -------------------------------------------------------------------------------------------
 *  - (Hint) Replace TODO 2 with parts of code
 *      (1) get HealthTracker object using healthTrackingService.getHealthTracker()
 *          use HealthTrackerType.HEART_RATE type as parameter
 *      (2) pass it to heartRateListener using setHealthTracker function
 ******************************************************************************************/

public void initHeartRate(HeartRateListener heartRateListener) {
    //"TODO 2 (1)"
    //"TODO 2 (2)"
    setHandlerForBaseListener(heartRateListener);
}


Start and stop trackers

For the client app to start obtaining the data through the SDK, set a listener method on HealthTracker. This method will be called every time there is new data. After the measurement completes, the listener has to be disconnected.

To start measurement in the BaseListener.java file, navigate to startTracker() function, and set TrackerEventListener as listener HealthTracker object.

  • Set an event listener on HealthTracker object using HealthTracking API.
  • Use the HealthTracker.TrackerEventListener object instance as parameter.
HealthTracker

HealthTracker enables an application to set an event listener and get tracking data for a specific HealthTrackerType.

public void

setEventListener(HealthTracker.TrackerEventListener listener)

Set an event listener to this HealthTracker instance.


/*******************************************************************************************
 * [Practice 3] Start health tracker by setting event listener
 *  - set event listener on health tracker
 -------------------------------------------------------------------------------------------
 *  - (Hint) Replace TODO 3 with parts of code
 *      set event listener on HealthTracker object using healthTracker.setEventListener()
 *      use trackerEventListener object as parameter
 ******************************************************************************************/

public void startTracker() {
    Log.i(APP_TAG, "startTracker called ");
    Log.d(APP_TAG, "healthTracker: " + healthTracker.toString());
    Log.d(APP_TAG, "trackerEventListener: " + trackerEventListener.toString());
    if (!isHandlerRunning) {
        handler.post(() -> {
            //"TODO 3"
            setHandlerRunning(true);
        });
    }
}


To stop measurement, unset the TrackerEventListener from the HealthTracker object in the stopTracker() function.

  • Unset the event listener on HealthTracker object using HealthTracking API.
HealthTracker

HealthTracker enables an application to set an event listener and get tracking data for a specific HealthTrackerType.

public void

unsetEventListener()

Stop the registered event listener to this HealthTracker instance.


/*******************************************************************************************
 * [Practice 4] Stop health tracker by removing event listener
 *  - unset event listener on health tracker
 -------------------------------------------------------------------------------------------
 *  - (Hint) Replace TODO 4 with parts of code
 *      unset event listener on HealthTracker object using healthTracker.unsetEventListener()
 ******************************************************************************************/

public void stopTracker() {
    Log.i(APP_TAG, "stopTracker called ");
    Log.d(APP_TAG, "healthTracker: " + healthTracker.toString());
    Log.d(APP_TAG, "trackerEventListener: " + trackerEventListener.toString());
    if (isHandlerRunning) {
        //"TODO 4"
        setHandlerRunning(false);

        handler.removeCallbacksAndMessages(null);
    }
}

Process obtained and batching data

The response from the platform will be asynchronous with the results you want to obtain. Follow the steps below to get blood oxygen level and heart rate data.

In the SpO2Listener.java file, navigate to updateSpo2() function, and read SpO2 data from DataPoint:

  • Get the oxygen saturation status using the DataPoint API (key: ValueKey.SpO2Set.STATUS).
  • Get the oxygen saturation value using the DataPoint API (key: ValueKey.SpO2Set.SPO2).
DataPoint

DataPoint provides a map of ValueKeyand value with a timestamp.

public <T>T

getValue(ValueKey<T> type)

Get data value for the given key.

	
/*******************************************************************************************
 * [Practice 5] Read values from DataPoint object
 *  - Get blood oxygen level status
 *  - Get blood oxygen level value
 -------------------------------------------------------------------------------------------
 *  - (Hint) Replace TODO 5 with parts of code
 *      (1) remove SpO2Status.CALCULATING and
 *          set status from 'dataPoint' object using dataPoint.getValue(ValueKey.SpO2Set.STATUS)
 *      (2) set spo2Value from 'dataPoint' object using dataPoint.getValue(ValueKey.SpO2Set.SPO2)
 *          if status is 'SpO2Status.MEASUREMENT_COMPLETED'
 ******************************************************************************************/

public void updateSpo2(DataPoint dataPoint) {

    int status = SpO2Status.CALCULATING; //"TODO 5 (1)"
    int spo2Value = 0;

    //"TODO 5 (2)"

    TrackerDataNotifier.getInstance().notifySpO2TrackerObservers(status, spo2Value);
    Log.d(APP_TAG, dataPoint.toString());
}
	

In the HeartRateListener.java file, navigate to readValuesFromDataPoint() function, and read the heart rate data from DataPoint:

  • Get heart rate status using DataPoint API (key: ValueKey.HeartRateSet.HEART_RATE_STATUS).
  • Get heart rate value using DataPoint API (key: ValueKey.HeartRateSet.HEART_RATE).
  • Get heart rate IBI value using DataPoint API (key: ValueKey.HeartRateSet.IBI_LIST).
  • Get IBI quality using DataPoint API (key: ValueKey.HeartRateSet.IBI_STATUS_LIST).

/*******************************************************************************************
 * [Practice 6] Read values from DataPoint object
 *  - Get heart rate status
 *  - Get heart rate value
 *  - Get heart rate IBI value
 *  - Check retrieved heart rate’s IBI and IBI quality values
 -------------------------------------------------------------------------------------------
 *  - (Hint) Replace TODO 6 with parts of code
 *      (1) set hrData.status from 'dataPoint' object using dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE_STATUS)
 *      (2) set hrData.hr from 'dataPoint' object using dataPoint.getValue(ValueKey.HeartRateSet.HEART_RATE)
 *      (3) set local variable 'List<Integer> hrIbiList' using dataPoint.getValue(ValueKey.HeartRateSet.IBI_LIST)
 *      (4) set local variable 'final List<Integer> hrIbiStatus' using dataPoint.getValue(ValueKey.HeartRateSet.IBI_STATUS_LIST)
 *      (5) set hrData.ibi with the last of 'hrIbiList' values
 *      (6) set hrData.qIbi with the last of 'hrIbiStatus' values
******************************************************************************************/

public void readValuesFromDataPoint(DataPoint dataPoint) {
    HeartRateData hrData = new HeartRateData();
    //"TODO 6 (1)"
    //"TODO 6 (2)"
    //"TODO 6 (3)"
    //"TODO 6 (4)"
    //"TODO 6 (5)" 
    //"TODO 6 (6)"
    TrackerDataNotifier.getInstance().notifyHeartRateTrackerObservers( hrData);
    Log.d(APP_TAG, dataPoint.toString());
}

Run unit tests

For your convenience, you can find an additional Unit Tests package. This lets you verify your code changes even without using a physical watch. See instructions below on how to run unit tests:

  1. Right click on com.samsung.health.multisensortracking (test) and execute Run 'Tests in 'com.samsung.health.multisensortracking'' command.

  1. If you completed all the tasks correctly, you can see that all the unit tests passed successfully.

Run the app

After building the APK, you can run the application on a connected device to see blood oxygen level and heart rate values.

  1. Right after the app is started, it requests for user permission. Allow the app to receive data from the body sensors.

  1. Afterwards, it shows the application's main screen and automatically display the heart rate. To get the blood oxygen level (SpO2) value, tap on the Measure button. To stop the measurement, tap on the Stop button.

  1. Tap on the details label to see more heart rate data.

You're done!

Congratulations! You have successfully achieved the goal of this Code Lab. Now, you can create a health app that measures blood oxygen level and heart rate by yourself! If you're having trouble, you may download this file:

Measuring Blood Oxygen Level and Heart Rate Complete Code
(134.82 KB)

To learn more about Samsung Health, visit:
developer.samsung.com/health