UsageStats
If you are unfamiliar with this artifact, Alex Brignoni explains the UserStats artifact in the blog post here. Located at /data/system/usagestats/ this information can be useful in cases. Up until Android 9 (Pie), this was in XML format, however since Android 10(Q), it is now in a different format. So the tool written by Alex didn't work out for me or my students investigating this artifact a couple of months back.The file name has the same format (unix millisecond time as integer) and below you can see what the new data looks like.
Figure 1 - File 1572840777639 - raw hex view (complete file not shown) |
To test for protocol buffer presence (on windows), you will need to download protoc.exe from here. Run protoc.exe as shown below. Here 1572840777639 is the filename. If you got output, its a protobuf.
W:\usagestats\0\daily>protoc --decode_raw < 1572840777639
1: 1862148
3: 1
4: 1
2 {
1: 74
2: "com.google.android.youtube"
2: "com.google.android.ext.services"
2: "com.android.providers.telephony"
2: "com.android.dynsystem"
2: "com.android.settings.CryptKeeper"
...
...output snipped...
...
22 {
2: 23
4: 60
5: 1249881
7: 23
14: 92830887
15: 23
16: 60
}
OK, so we got some decoded json data back. But it still did not look like anything we are used to seeing (see XML below).
Figure 2 - XML usagestats snippet |
Since Android is open source, so why not peek at the source code of AOSP? To avoid downloading the entire source code, just browse the aosp-mirror on github.
Figure 3 - aosp source code on github |
After a bit of searching, we find the file we are looking for at:
platform_frameworks_base/core/proto/android/server/usagestatsservice.proto
Figure 4 - usagestatsservice.proto file snippet |
- usagestatsservice.proto
- configuration.proto
- privacy.proto
- locale.proto
- rect.proto
- protobuf_descriptor.proto
- window_configuration.proto
Next, we need to transform (google says compile) our .proto files into python libraries. Use protoc.exe to do so. The syntax is :
protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/your_proto_file.proto
Do this for every .proto file. It will generate a .py file for each one. For example, the usagestatsservice.proto compiles to usagestatsservice_pb2.py. Now all that remains is to use these generated python files to read our raw protocol buffer from file. We will need to write some code to do so.
Peeking into the usagestatsservice.proto file, you get some idea of how this might work. I constructed a basic python script to read this (below).
import usagestatsservice_pb2
input_path = "W:\\usagestats\\0\\daily\\1572840777639" stats = usagestatsservice_pb2.IntervalStatsProto() with open (input_path, 'rb') as f: stats.ParseFromString(f.read()) # GET PACKAGES for usagestat in stats.packages: print('package = '+ stats.stringpool.strings[usagestat.package_index - 1]) print(usagestat)
# GET CONFIGURATIONS for conf in stats.configurations: print(conf)
# GET EVENT LOGS for event in stats.event_log: print(event)
input_path = "W:\\usagestats\\0\\daily\\1572840777639" stats = usagestatsservice_pb2.IntervalStatsProto() with open (input_path, 'rb') as f: stats.ParseFromString(f.read()) # GET PACKAGES for usagestat in stats.packages: print('package = '+ stats.stringpool.strings[usagestat.package_index - 1]) print(usagestat)
# GET CONFIGURATIONS for conf in stats.configurations: print(conf)
# GET EVENT LOGS for event in stats.event_log: print(event)
You can check for the existence of a field using the HasField() function. So here is what a package object consists of:
package = com.android.settingsA configuration object consists of:
package_index: 58
last_time_active_ms: 663647
total_time_active_ms: 4897
app_launch_count: 3
last_time_service_used_ms: -1572840673324
last_time_visible_ms: 673237
total_time_visible_ms: 25221
config {
font_scale: 1.0
locales {
language: "en"
country: "US"
}
screen_layout: 268435794
color_mode: 5
touchscreen: 3
keyboard: 2
keyboard_hidden: 1
hard_keyboard_hidden: 1
navigation: 1
navigation_hidden: 2
orientation: 1
screen_width_dp: 411
screen_height_dp: 659
smallest_screen_width_dp: 411
density_dpi: 560
window_configuration {
app_bounds {
right: 1440
bottom: 2392
}
windowing_mode: 1
bounds {
right: 1440
bottom: 2560
}
}
}
last_time_active_ms: 662163
total_time_active_ms: 37
count: 1
An event log object contains:
package = com.google.android.apps.nexuslauncherSo now, our protobuf parsed and file read and interpreted successfully! That's it for now. On to the next artifact..
class = com.google.android.apps.nexuslauncher.NexusLauncherActivity
task root package = com.google.android.apps.nexuslauncher
task root class = com.google.android.apps.nexuslauncher.NexusLauncherActivity
type = MOVE_TO_FOREGROUND
time_ms: 34440