The State of Android Health Data (Part 1) – Garmin

One researcher was injured during the making of this blog post.

The public images I release have constantly evolved over the past two years, and this is due to feedback from those that use them. But it is also due to something else: before I start working on them I usually ask a few people what they would like to see in the data that would be beneficial. I already check the respective app stores, blogs, app rankings, and see the chatter in DFIR Discord, but that only gets me so far. Asking around is a great way for me to understand needs and wants, and it makes the data sets better.

Android 11 was no different and one of the requests was for health data, which is something that had not been in a previous Android image. I had included health data in the iOS 13 image, and it was fairly straight forward because AppleWatch is the de facto activity tracker for iOS (Fitbit was included to mix things up a bit). When I started to look into Android smart watches and activity trackers, things got messy.

Generally speaking, health data in the Android ecosystem is all over the place. One of the key components of health data is the hardware used to collect it. Previously, the Android smart watch/activity tracker landscape was extremely fragmented, with various OEMs using their own hardware and operating systems. However, that changed this week at Google I/O. Wear (formally Wear OS) will soon be running on Fitbit hardware and Samsung Galaxy smart watches. This does streamline Android smart watches/activity trackers a bit, but there are still some major non-Wear hardware in the market, namely Garmin and OnePlus, both of which are running proprietary real-time operating system (RTOS) on proprietary hardware. Fortunately, there have been a few looks into Wear OS, which may give some insight in to Wear and give examiners a head start when these new devices come to market.

Additionally, there are 3rd party app developers in the Android ecosystem that don’t create hardware but do create apps that collect health data: Strava, Under Armor (Map My Fitness), Nike, My Fitness Pal, and the list goes on. These apps rely on other OEM hardware, whether a wearable or the phone itself, to feed them data as they have no app-specific hardware to collect health data.

Getting all of these apps in to one single post would be insane; it would be way too long and no one would read it. So, this will be the first in a series of blog posts about the state of health data in Android, and to kick things off, this article will discuss what is arguably the OG of health data wearable hardware, Garmin.

A quick note: the aim of this series of blog posts is not to assess the accuracy of the associated wearable, but to detail what data is available on the paired Android device. Accuracy would be a whole other series of posts itself. I will, however, address data on the wearable, if available.

In The Beginning

Garmin has existed as a company since 1989. Initially called ProNav, the first products the company produced were GPS devices for boats, but hit pay-dirt when they landed the U.S. Army as one of their customers. Over the years Garmin has released a multitude of products to include automotive, marine, aircraft, and handheld GPS units, animal trackers, fish finders, a mobile phone, and fitness trackers. Personally, I used Garmin GPS handheld and automotive devices in the early and mid-2000’s in the age before Google Maps and smart phones (I hated printing things from MapQuest).

The Garmin Forerunner 101 was Garmin’s first “fitness tracker” before fitness trackers were a thing, and was released in 2003 along with its more expensive sibling, the Forerunner 201, Both devices were intended for distance runners. They could track distance, pace, and could keep track of a runner’s route, which could come in handy if a runner got lost during their run (both could direct a runner back to their starting point by reversing the GPS coordinates). The 201 had the additional functionality of being able to connect to a computer. While these devices were not “activity trackers” by today’s standards (no step count, heart rate, pulse ox, sleep, etc.), they were designed to collect certain telemetry about a user during a workout.

Garmin continued to introduce similar wearables into the market, but remained focused on the athletic segment. In 2011 Garmin Connect landed, which provided a way for Garmin customers to access the data being collected by their wearables. In 2014 they released their first “activity tracker” aimed at the masses, the Vivo, and in 2016 Garmin introduced their own app store, Garmin IQ, for their wearables. Garmin currently has several lines of wearables: the aforementioned Vivo and Forerunner lines, fenix, Venu, and Instinct. Each line is aimed at different types of customers depending on intended activity.

The Warm Up

The aforementioned Garmin Connect app is where the magic happens; it keeps track of connected Garmin devices and the activity data they collect. Opening the app lands the user on the “My Day” screen, which can be thought of as a dashboard. See Figure 1.

Figure 1.png
Figure 1.  My Day.

This screen is busy. The screen has cards for heart rate (can be real-time depending on the wearable), steps taken, floors climbed, calories burned, respiration rate, sleep, and any workouts that have occurred during the day. The screen can be edited to include/remove things such as weight, hydration, Body Battery (a measure of recovery rate – similar to Whoop), PulseOx, and Stress (heart rate variability). Not all of Garmin’s wearables can collect each data type, so, depending on the wearable’s hardware, some of these cards may be empty. During testing, I used a Forerunner 35 and Vivoactive 4, the latter being able to collect each data point.

At the top of Figure 1 you can see my test account icon (the cassette tape in the blue box) and the connection status with the wearable (red box). Across the bottom are virtual buttons (yellow box). The buttons are (left to right) My Day, Challenges (exactly what it sounds like), Calendar (activity history in a calendar format), Newsfeed (past activities in a feed format), and Notifications (self-explanatory). See Figure 2 through 5.

Figure 2.png
Figure 2.  Challenges.
Figure 3.png
Figure 3.  Calendar.
Figure 4.png
Figure 4.  News Feed.
Figure 5.png
Figure 5.  Notifications.  All quiet here.

There are a multitude of settings in Garmin Connect, but I do want to cover two notable ones that may be beneficial from an investigative standpoint. The settings can be accessed by pressing the hamburger icon in the upper left corner of the app UI (Figure 6).

Figure 6.png
Figure 6.  The hamburger.

Then proceed to “User Settings.” See Figure 7.

Figure 7.png
Figure 7.  User Settings.

The first notable setting is the Weather Location (shown in Figure 7-A). As examiners/investigators, we sometimes use data from weather functions to help put a device in a particular location, and in Garmin Connect, a user can determine what data this function uses. The Weather Location feature can be set one of two ways, either by leveraging the location of the phone, or from a user-defined location (a static location). It is important to understand the setting of this feature as it can affect some of the location data reported by the app.  Figure 7-A shows the app’s guidance with regards to how this setting is applied to the weather widget, but it also affects data related to workouts, which is explained later.

The second notable setting deals with custom stride lengths (shown in Figure 7-B). In Garmin Connect a user can set a custom stride length for both walking and running. The app’s guidance says a user will get more accurate distance tracking if custom stride lengths are set, but I found during testing that the location tracking was accurate without setting them. As with the Weather Location feature, a custom stride length set by a user can affect location and distance data collected by the associated wearable, and thus, the data reported by the app.

Figure 7-A.png
Figure 7-A.  Weather Locations settings.
Figure 7-B.png
Figure 7-B.  Custom Stride Length settings.

That’s it for the user-facing portion of the app. While there are many aspects of Garmin Connect, the dashboard, calendar, and newsfeed are the key components for activity data.

The Workout

Before I start, a few housekeeping notes on the devices used for testing. I used my Pixel 3 running Android 11, which was running updated patches in December 2020 and March/April 2021. The Garmin Connect data generated and displayed in this article (the most recent data) comes from version 4.42 of the app. The wearable I used in the latest round of testing was the vivoactive 4, running OS versions 5.60 and 5.80. I had planned on skipping software updates (I skipped 5.73 and 5.75) but was hassled so much that I relented and updated to 5.80.

Garmin Connect retains a lot of data. A. Lot. I generated test data for this blog post in two waves. The first wave was in December and resulted in injury (the GIF at the beginning was a joke…but wasn’t – I actually did pull a hammy a couple of weeks before Christmas), and the second wave occurred in the month leading up to this article. The second wave of test data occurred after I had re-imaged the Android phone. In between those waves was the iOS 14 image, which also had Garmin data in it. The surprise here was that Garmin Connect contained data from both waves of data generation in Android AND the data generated during the iOS 14 image creation. So, Garmin is keeping data server-side, which can be good from an examiner’s standpoint (more on this later).

The Garmin Connect folder is com.garmin.android.apps.connect, and it resides in /data/data. An examiner’s ability to get to this data depends on the capabilities of the tool(s) being used or the status of the phone (i.e. is it rooted). A file system extraction, at a minimum, is needed as Garmin data is not present in an ADB backup. The first question to answer is what wearable was paired with the device, and to answer that we turn to the file gcm_cache, which is found in the /databases folder. The table of interest is devices. See Figure 8.

Figure 8.png
Figure 8.  Devices.

The first wearable is a Garmin Forerunner 35 (red box), which was used while creating the iOS 14 image and the first wave of Android data generation in December of 2020. After I re-imaged the Pixel 3 in March of 2021, the Forerunner 35 was never paired with the phone, yet it has an entry in this database table. In the Garmin Connect world, devices are associated with accounts and since Garmin is keeping data on the backend, the Forerunner 35 information was pushed down to this phone when I signed into the Garmin Connect app. This can be helpful in the event an examiner suspects a second device may be in use, if expected data points are missing, or if an examiner is unaware of an additional device(s).

The second device, the vivoactive 4, is seen in the blue box, and was the device used in the most recent wave of data generation. The identifying data available for each device is comprehensive. The available data I observed is as follows:

ID number (column unit_id)
Product Number
Bluetooth MAC address (column mac_address)
Display & Friendly Names
Connection Type (Bluetooth or Wi-Fi)
Last Connection Time (Unix Epoch – will be “null” if device never paired with the phone)
Software Version
URL for Stock Photo of Wearable
Device Category(ies)
SKU
Part Number

See Figures 9, 10 & 11.

Figure 9.png
Figure 9.
Figure 10.png
Figure 10.
Figure 11.png
Figure 12.

In the same database is the table json. This table can contain a lot of information depending on the paired wearable. See Figure 12.

Figure 12.png
Figure 12.  json.

The column concept_name (red box) indicates the category of data that is found in corresponding cell in the cached_val column (blue box), which contains the json data. For example, the value USER_SETTINGS in the in the concept_column has a json blob that contains quite a bit of data related to the settings in the app:

Garmin Connect ID
Provided birthdate
First Day of the Week
Gender
Handedness (right or left – indicates on which hand the wearable is worn)
Heart rate format (bpm)
Hydration measurement (cups/liters)
Height (in meters)
Power Measurement (Battery)
Time format (12 hr or 24 hr)
Weather location (is null if phone location is chosen)
Weight (in grams)
Sleep time (in seconds from midnight – wake and bedtime)

This data was present regardless of which wearable was being used. The remaining values, however, were not. The vivoactive 4 had additional capabilities that the Forerunner 35 did not. This is a good example of how hardware can affect what data is present.

The remaining values seen in the concept_name column are pre-pended with “REAL_TIME” and are real-time readings from the vivoactive 4. The values and description are seen are:

REAL_TIME_HEART_RATE
REAL_TIME_CALORIES
REAL_TIME_RESPIRATION
REAL_TIME_INTENSITY_MINUTES
REAL_TIME_FLOORS
REAL_TIME_STEPS
REAL_TIME_BODY_BATTERY (recovery level – similar to Whoop)

“Real time” can be a bit deceptive depending on the sync settings set by the user. By default, both wearables sync’d periodically (“Smart”), or when I opened the app, which automatically triggered a sync; however, I have seen a discrepancy of sync’ing in REAL_TIME values up to 19 hours! Fortunately, each real time value includes a “last update” timestamp in Unix Epoch, which can also give a good indication of the last time the wearable was sync’d with the device. As an example, REAL_TIME_HEART_RATE is seen in Figure 13.

Figure 13.png
Figure 13.  My heart rate.  Real time.

The next table is json_activities. See Figure 14.

Figure 14.png
Figure 14.  json_activities.

This table tracks “activities” or “exercises.”  These are exercises that are actively tracked by the wearable either by passively detecting the exercise (via setting is called “Auto Activity Start” – it was off by default for my devices) or by the user initiating an exercise. The entries here grouped with each group representing a single exercise session. The values in the data_type column that represent a single exercise session are:

ACTIVITY_DEVICE
ACTIVITY_WEATHER
ACTIVITY_DETAILS
ACTIVITY_CHARTS

The corresponding values in the cached_val column contain (surprise) json data. ACTIVITY_DEVICE contains data about the wearable used to track the activity, and includes many of the values seen in the devices table (Figures 8, 9, & 10), so I will not show it here. ACTIVITY_WEATHER gives the weather conditions at the time the activity was started. See Figure 15.

Figure 15.png
Figure 15.  ACTIVITY_WEATHER.

Much of this data is self-explanatory, but I do want to highlight the “weatherStationDTO” value (blue box). These values represent Surface Weather Observation Station identifiers. In Figure 14, you can see the value is KTTA, which corresponds to the Raleigh Exec Jetport at Sanford-Lee County Airport. This was the closest observation station to me at the time this particular exercise was started. If you have a value, but the name field is blank, you can always search for values on the FAA website, which you can find here. Keep in mind that if a user has chosen a preset location for weather conditions instead of using the phone’s actual location, this value may not be accurate. Also note, the GPS coordinates seen in Figure 15 are for the observation station, not the location of the exercise.

Figure 16 shows the details of the activity.

Figure 16.png
Figure 16.  ACTIVITY_DETAILS (combined).

There is a lot of json data here, but some of the notable ones include the title (Holly Springs Running), the type of activity (running), location, name of the account (This Is DFIR), and a summary of the activity (seen on the right in Figure 16). Some notes about the values seen in the summary: speed is stored in meters per second, distance (horizontal and vertical) is stored in meters, time values are stored in seconds, cadence is steps per minute, heart rate is beats per minute, and water is stored in milliliters.

The next part, ACTIVITY_CHARTS, gives a granular look at the activity. See Figure 17.

Figure 17.png
Figure 17.  ACTIVITY_CHARTS.

The json data comes in two batches, the first batch contains metric data at various points during the exercise as seen above. Based on my testing, a new set of metrics are generated every 3 to 8 seconds, but I have not found any consistency, so mileage may vary. I suspect it may be based on the type of exercise and speed, but have nothing concrete to base it on. Each entry in this first batch contains the same data types, and while I was unable to determine every data value, some could be determined based on test data. See Figure 18.

Figure 18.png
Figure 18.  Metrics.

The next file is notification-database, and it is, as the name suggests, a database that contains information about notifications. There are only three tables in this database, with the table of interest being notification_info. See Figure 19.

Figure 19.png
Figure 19.  Notifications.

This database tracks notifications that are sent to the paired wearable. There are several aspects of a notification documented here. In Figure 18 we have the notification status (red box) timestamp of the notification (blue box), title of the notification (green box), and the message contained in the notification (purple box). Figure 20 shows the notification tray from the paired phone.

Figure 20.png
Figure 20. Notification Tray.

Figure 21 shows additional information in the table. A notification can have a “positive” or “negative” action that can be taken against it (blue and red boxes, respectively). If there is a phone number associated with the notification, such as when a SMS message is received by the paired phone, then it will be appear in the column phoneNumber (green box).

Figure 21.png
Figure 21.  Actions.

Scrolling further in the table finds the data seen in Figure 22, which includes the package name of the app that generated the notification (column packageName) and the type of notification (column type). The columns postTime and when are interesting in that I found the times to be relatively similar to when the timestamp seen Figure 19 (column statusTimestamp in the blue box).

Figure 22.png
Figure 22.  Packages, types, and other time stamps.

The next database is cache-database. There is a substantial amount of data in this database. Let’s take a look at the table sleep_detail. See Figure 23.

Figure 23.png
Figure 23.  Sleep.

There is a sizable amount of data in this table. In the green box the aggregate sleep time (more on that in a moment) is documented in seconds. In the red box is the start and end of sleep in Unix Epoch in UTC.  In the blue box is the start of sleep and end of sleep is documented in Unix Epoch in local time. Note that the local times are what appear to the user via the app UI. As an example, the entry in row 15 in Figure 23 (purple box) is seen in the UI in Figure 24 (red box).

Figure 24.png
Figure 24.  Sleep on 2021-05-15.

Notice the different categories of sleep in Figure 24 (awake, light, REM, and deep). Further in to the table are the amount of sleep (in seconds) for each category. See the red box in Figure 25. Also note the pulse ox percentage values in the blue box.

Figure 25.png
Figure 25.  Amount of sleep per category.

Figure 26 shows the average, lowest, and highest respiration values (breaths per minute) during the sleep period.

Figure 26.png
Figure 26.  Sleep respiration.

The next table is activity_details. This table documents all of the exercises logged in the app with the wearable. There are several metrics documented. See Figures 27 through 29.

Figure 27.png
Figure 27.  Activity Details (Part 1).
Figure 28.png
Figure 28.  Activity Details (Part 2).
Figure 29.png
Figure 29.  Activity Details (Part 3).

The good news about most of the data in this table is that it is self-explanatory. Some of the notable entries include:

Activity Name
Start Time in Local and GMT
Activity Type
Distance (meters)
Duration (seconds)
Moving duration (seconds)
Elevation Gain and Loss (meters)
Average and Max speeds (meters per second)
Start Lat and Long
Average and Max Heart rate
Average and Max Cadence (steps per minute)

Note that the table activity_summaries contains much of the same data as activity_details but there are some differences. The first being that the data is stored in json. Second, it also contains activity data that has been sync’d with Garmin, but was not recorded on the wearable. So, for example, I had activity data in this table going back to October of 2020 (during the Android 11 image data generation period). Third, my most recent activity (a running workout on 2021-05-15) did not appear in this table, but was present in the activity_details table. Many of the data points I highlighted in activity_details also appear in the json for activity_summaries. Figure 30 shows some of the database entries.

Figure 30.png
Figure 30.  activity_summaries in json.

That wraps the main parts of Garmin Connect. I was not in a position to test out other components of the app such as badge challenges and Live Track, which sends real-time activity data to friends during an exercise. I also took a look at Connect IQ, and there wasn’t much in the way of useful data in it as far as my activities were concerned. But…

What About the Wearable???

As it turns out, both the Forerunner 35 and vivoactive 4 could be accessed by connecting them to my workstation. Figure 31 shows Windows Explorer after I connected the vivoactive 4.

Figure 31.png
Figure 31.  vivoactive 4 after connection to my workstation.

The folder “GARMIN” is the folder of most interest. See Figure 32.

Figure 32.png
Figure 32.  GARMIN folder.

A sizable amount of the files in this directory (and sub-directories) are .FIT (Flexible and Interoperable Data Transfer) files. I won’t get into .FIT files here, but just know that it is one file type (another being .tcx files) used to store health/workout data, and can be used to port data between apps (e.g. port data from Garmin to Strava). If you are interested in reading more about .FIT files, you can do so here. On the vivoactive 4, .FIT files were also used to store other data. There multiple files on the device, but there are two I want to highlight.  The first is the file Device.fit which sits in the root of the Garmin folder. I used the website Fit File Viewer, which is found here to decode the file. See Figure 33.

Figure 33.png
Figure 33.  Device.fit decoded.

Values within the .FIT file can be viewed in the web browser, or downloaded as a .csv file. One value, in the red box in Figure 33, is the device serial number, which can be linked back to the unit_id value seen in Figure 8.  This could be useful in the event a wearable is recovered and the paired phone is either missing or inaccessible.  In which case, that small bit of information could possibly be used to get information from Garmin via legal process.

The second file I want to highlight is actually a series of .FIT files that are found in the .\GARMIN\Activity folder. See Figure 34.

Figure 34.png
Figure 34.  Activities.

Each of the files seen in Figure 34 represent activity sessions that were recorded on the vivoactive 4. These files contain GPS, elevation, speed (pace), heart rate, and cadence data associated with the sessions. I used the utility GPXSee to view the file contents. See Figure 35.

Figure 35.png
Figure 35.  2021-05-15-12-44-19.fit (heart rate shown at bottom).

GPXSee can be found here. Another option to view these .FIT files is Golden Cheetah, which can be found here.

There are plenty of research papers that have been written about Garmin wearables and what data is available on them.  If you’re interested, a quick Google search should get you started.

Last Lap

I do want to address one final thing: Garmin takeout data (or whatever it is they call it). I requested Garmin send me a copy of all the data it had on me, and, surprise, much of the data I have discussed in this article is also in Garmin’s possession. This includes account information, sleep information, workout information, and metric summaries. Much of the data comes in json format, but some of the workout data comes as .FIT files. As an example, the account information, historical sleep information, associated device information,  and historical activity information from the takeout data are shown in Figures 36, 37, 38, and 39, respectively.

Figure 36.png
Figure 36.  Acount information from Garmin takeout.
Figure 37.png
Figure 37.  Historical sleep information from Garmin takeout.
Figure 38.png
Figure 38.  Devices associated with the account.
Figure 39.png
Figure 39.  Historical activity…in a messy json blob.

The Cool Down

Garmin has been in the health data wearables space in some fashion for almost 20 years, and they do not appear to be leaving any time soon. They have a lot of experience collecting health data, and it shows. Garmin Connect on Android stores a lot of data about a user, and can give examiners and investigators insight about a user’s movement and sleep. Additionally, Garmin keeps a lot of data stored server-side, so, even if an examiner isn’t able to determine much from the Garmin Connect app, a legal process of some sort (depending on jurisdictional requirements) to Garmin may get what is needed during an investigation.

Stay tuned for the next installment.  🙂

One thought on “The State of Android Health Data (Part 1) – Garmin

Leave a Reply