It's a gzip compressed protocol buffer (protobuf). With decoding tools you can see all the values and types which can be useful. Though without the .proto definition it's a struggle to figure out what it all means.
To decode the request, first get it in the raw form. I do this by exporting a HTTP .trace file and extracting just the body. I've had better luck doing the gzip decompression myself.
Once you have the raw request body, decode it like this:
$ gunzip - < request_body > request_uncompressed.bin
$ protoc --decode_raw < request_uncompressed.bin
Here's a simple CyberChef formula that also decodes it for you: https://gchq.github.io/CyberChef/#recipe=Gunzip()Protobuf_Decode('',false,false)
When it works you'll see the raw protobuf values. They'll look something like this (actual values randomized):
1 {
1: 1
2 {
1 {
1: "_si"
3: 161212808641
}
1 {
1: "_et"
3: 57
}
1 {
1: "_sc"
2: "SomeControllerName"
}
1 {
1: "_o"
2: "auto"
}
2: "_e"
3: 161236824
4: 163120534
}
2 {
1 {
1: "_si"
3: 1358166110
}
1 {
1: "_sc"
2: "SomeControllerName"
}
1 {
1: "_o"
2: "auto"
}
2: "_ab"
3: 161336826
4: 163123680
}
3 {
1: 163129524107
2: "_fi"
4: 1
}
3 {
1: 15514295
2: "_fot"
4: 15514241
}
3 {
1: 1530783276
2: "_sid"
4: 1530783376
}
...
8: "ios"
9: "13.5"
10: "iPhone12,3"
...
Update from @lari: Creating a custom protobuf definition to decode the requests
On Android you can enable verbose logging and see in device logs what Firebase Analytics sends to the servers in its original format. Here's an example:
FA-SVC com.google.android.gms V Uploading data. app, uncompressed size, data: com.my.app, 9332,
batch {
bundle {
protocol_version: 1
platform: android
gmp_version: 46000
config_version: 1679644809123456
gmp_app_id: 1:123456789:android:aaaaaaaaaa
app_id: com.my.app
app_version: 1.0.0
app_version_major: 100
firebase_instance_id: xx_xxxx_xx
upload_timestamp_millis: 1681470819289
start_timestamp_millis: 1681468977430
app_instance_id: f8s9fa09vsa4a4lk2983fsdf
os_version: 9
user_property {
set_timestamp_millis: 1631520687985
name: first_open_time(_fot)
string_value:
int_value: 1631523600000
}
user_property {
set_timestamp_millis: 1681468712345
name: ga_session_id(_sid)
string_value:
int_value: 1681468788
}
event {
name: user_engagement(_e)
timestamp_millis: 1681468977430
previous_timestamp_millis: 1681468884057
param {
name: ga_event_origin(_o)
string_value: auto
}
param {
name: engagement_time_msec(_et)
string_value:
int_value: 90654
}
param {
name: ga_screen_class(_sc)
string_value: MyViewController
}
param {
name: ga_screen_id(_si)
string_value:
int_value: -13918239812398123
}
}
}
}
You can quite easily recreate parts of the .proto definition by comparing this to the encoded version (the one with numbers as keys).
For example:
- the root message is called "batch"
- "1" in batch is "bundle"
- "2" in bundle is "event"
- "2" in event is "name"
and so on...
Based on this, you can create a custom definition, for example:
// app-measurement.proto
syntax = "proto3";
package app_measurement;
message Bundle {
message Event {
string name = 2;
}
repeated Event event = 2;
}
message Batch {
repeated Bundle bundle = 1;
}
And use this to decode the message:
$ protoc --decode=app_measurement.Batch app-measurement.proto < request_uncompressed.bin
You will now see the identified parts replaced with key names from the .proto file while the rest stay as numbers, for example:
bundle {
event {
name: "_e"
1 {
1: "_et"
3: 10856
}
1 {
1: "_o"
2: "auto"
}
3: 1680595820225
4: 1680595807912
}
...
If you need a compiled "descriptor" for some other tool, you can create it with the --descriptor_set_out=
flag:
$ protoc --descriptor_set_out=app-measurement.desc app-measurement.proto
As you'll probably notice, Firebase Analytics also shortens the default event, parameter and user property names. E.g. _e = user_engagement and _o = ga_event_origin. The original names can be seen in the device logs on both Android and iOS.
I have created and published an open-source version of a protocol buffers definition for the app-measurement.com requests and shared it in GitHub: https://github.com/lari/firebase-ga4-app-measurement-protobuf
There's also a blog post with more details: https://larihaataja.com/firebase-ga4-app-measurement-com-calls/