This repo contains code and other resources for dynamically analyzing Android apps, especially, for checking their compliance with Global Privacy Control (GPC). GPC is a privacy preference signal for opting out from ad tracking. Apps are required to respect GPC signals per the California Consumer Privacy Act (CCPA) and other privacy laws.
The code and all other resources in this repo are developed and maintained by Sage Altman (@sagealtman), Nishant Aggarwal (@n-aggarwal), Zachary Liu (@zatchliu) and Sebastian Zimmeck (@SebastianZimmeck) of the privacy-tech-lab and Konrad Kollnig (@kasnder) of the Law and Tech Lab of Maastricht University. Wesley Tan (@wesley-tan), Samir Cerrato (@samir-cerrato), Eliza Kuller (@ekuller) and Bella Tassone (@bella-tassone) contributed earlier.
1. Research Publications
2. Repo Overview
3. Scripts
4. Apps CSV
5. Thank You!
For setup instructions, see also our Artifact Appendix.
Sebastian Zimmeck, Nishant Aggarwal, Zachary Liu, Sage Altman and Konrad Kollnig, Exercising the CCPA Opt-out Right on Android: Legally Mandated but Practically Challenging, 26th Privacy Enhancing Technologies Symposium (PETS), Calgary, Canada, July 2026, BibTeX
You can find a complete list of our GPC research publications in the GPC OptMeowt repo.
This repo contains the following resources:
scripts: Code for dynamically intercepting and analyzing network trafficapp_csv: App lists sorted by Google Play Store categories
The scripts can be used in conjunction with mitmproxy SOCKS5 mode to intercept network traffic and perform dynamic privacy analysis on Android apps. The following is a guide on how to run the analysis. To begin you should clone the repo.
- Frida Server on phone
- App for SOCKS Proxy on phone
- MITM Proxy on computer
- Mullvad VPN Account and set up on computer (optional if for different analysis regions)
- MITM Proxy installed and set up on computer
- ADB Installed and set up on computer
- Rooted phone set up with the appropriate Magisk Modules
- USB Debugging is enabled on the phone
Once the repo is cloned, you will see there is a file called scripts/app-list.txt. In that file you should enter the list of package names that you want to test. Each package name should be on a new line. Make sure to add an empty line after your last package name.
The next step is to upload the actual apk files. You will see a folder called apps. This is where all the apks should be uploaded. It is recommended that the apks are deleted (or not staged) prior to any commit.
Now we can move on to setting up the capture infrastructure. To begin, you will need a usb type-c to type-c cable. Use it to connect the phone to the computer (via the Android Debug Bridge (ADB)). Make sure the phone is connected by running the command adb devices. If you see a device in the list, you should be ready. If not, try detaching and reconnecting the usb connection.
Before you begin, make sure that your setup has followed all instructions so far and all the pre-requisites have been satisfied. If so, we are now ready to start the captures. The following is a list of steps in order that explicitly tell you how to run it.
-
Reboot the phone:
adb reboot -
Turn on Data Saver and Turn off Private DNS on the phone; both settings are in
Settings -> Networks -
Turn on Frida
adb shell su -c /data/local/tmp/frida-server & -
Now connect the phone to the computer using SOCKS Proxy. To do so first find the IP address of the computer (for MacBooks, it is in
Settings -> Wifi -> Details). Now set it to be the IP address of the SOCKS Proxy on the phone. Set the port to 8889. Turn on the SOCKS Proxy Connection. -
If you want to analyze apps' traffic from a different region than the one you are located in, turn on Mullvad VPN on the computer and connect to the region of your choice.
-
Navigate to the
scriptsdirectory in thegpc-androidrepo and run the commandbash multi-app-automation-script.sh
The output files (.mitm and .har files) are stored in the mitm-captures folder. These files will not be uploaded to the remote repo. If you do want to upload them, please modify the .gitignore files accordingly.
Frida is the most likely component to break the testing framework. The reason is that new OTA Android updates can break its functionality. To fix the issue, you can try a couple of things:
- Install the latest version of Frida server on android (arm64)
- Make sure the Frida client and Frida server are the same version
- Check the Frida GitHub repo for relevant information on the issue you may be facing
An error that you may encounter when running the Frida server is the following:
- {"type":"error","description":"Error: Unable to perform state transition; please file a bug","stack":"Error: Unable to perform state transition; please file a bug\n at bt (frida/node*modules/frida-java-bridge/lib/android.js:578:1)\n at frida/node_modules/frida-java-bridge/lib/class-model.js:112:1\n at Function.build (frida/node_modules/frida-java-bridge/lib/class-model.js:7:1)\n at k._make (frida/node_modules/frida-java-bridge/lib/class-factory.js:168:1)\n at k.use (frida/node_modules/frida-java-bridge/lib/class-factory.js:62:1)\n at frida/node_modules/frida-java-bridge/index.js:224:1\n at c.perform (frida/node_modules/frida-java-bridge/lib/vm.js:12:1)\n at _performPendingVmOpsWhenReady (frida/node_modules/frida-java-bridge/index.js:223:1)\n at *.perform (frida/node_modules/frida-java-bridge/index.js:204:1)\n at /internal-agent.js:490:6","fileName":"frida/node_modules/frida-java-bridge/lib/android.js","lineNumber":578,"columnNumber":1}
To resolve this issue, you can delete the Android runtime library. Steps to uninstall it are
-
Run
adb shell -
Run
pm uninstall com.google.android.art -
Exit the shell with CTRL + D and then reboot your phone
adb reboot
The path the network traffic takes in this setup is somewhat complicated. As such there could be several different places where the issue could be arising from:
- SOCKS Proxy: The SOCKS Proxy connects the phone to the computer. To make sure the connection is setup properly, first check the IP address of the computer you are connecting the device to. Now set that to the IP address to be reached in the SOCKS Proxy. But this alone is not enough. You also need to make sure that the port that you are sending the data to is listening for incoming data. This would, for us, be MITM Proxy. The port we are using is 8889. Additionally, if SocksDroid is directing traffic to what seems like the proper port, but it is not being picked up by MITM, it is possible that the port on the Android device is not properly connected to the respective port on the computer. Running
adb reverse tcp:8889 tcp:8889and ensuring SocksDroid is set with Server IP: 127.0.0.1 and Server Port: 8889 may resolve this issue. - MITM Proxy: The MITM Proxy is the next stage in the network transfer process. To make sure it is set up correctly make sure that MITM is up to date and running on the same port as defined in the SOCKS Proxy. For us, this should be 8889.
- Mullvad VPN: The last step in our network transfer process is the Mullvad VPN, assuming you are using it. We have almost never encountered issues with it, but in case you have a connection issue, it may be better to just test the setup without the VPN running to eliminate a potential reason for failure. Additionally, it may take a variable amount of time for the phone to connect to the MITM Proxy (this will be shown by "client connect" and "server connect" messages in the terminal). In this case, wait until the connection registers before attempting to analyze traffic.
The process for capturing traffic manually is similar to the setup above but has some key differences. First follow the instructions under "Setting up the Capture Infrastructure," as well as those under "Running the Analysis" above through step 5 (turning on Mullvad VPN). Additionally, you will need the app whose traffic you wish to analyze installed on your phone. Proceed with the following steps:
-
Edit the paths given in
save_flows.py(in thescriptsdirectory) to ensure the script has a valid path to save to. Generally, this path should be./mitm-captures/$APP_PACKAGE_NAME/not_opted_out.json(or.../opted_out.json). The JSON files do not need to exist beforehand (and in fact should not exist), but the directories parenting them must be present for the script to function. -
Then run the following three commands in your terminal in sequence.
Clear any data on the android device associated with the given app
adb shell su -c pm clear $APP_PACKAGE_NAMEBegin capturing network traffic from the device
mitmdump --mode socks5 -s ./scripts/save_flows.pyOpen the app with the necessary Frida script running
frida -U -l ./scripts/frida-script.js -f $APP_PACKAGE_NAME -
In order to stop saving traffic to the path listed in
save_flows.py, you can press CTRL+C at any time to quit mitmproxy and CTRL+C along withexitto end the Frida script.
This capture method is used to directly examine the traffic an app is sending before and after an in-app privacy toggle is switched on. It was run on a Google Pixel 6a. These instructions should also generally work with other Android phones, but you would need to download versions of the respective software compatible with your device and OS. Most recent versions tested are as follows:
- Pixel 6a
- Android 16 (Build Number BP2A.250705.008)
- MITMProxy v12.2.1
- Frida v17.5.1
- SocksDroid v1.0.4
The apps_csv directory contains a collection of CSV files, each representing a category of apps on the Google Play Store. Each file contains a list of the top 40 free apps for a category.
The directory contains the following files:
- Multiple CSV files with the naming convention
apps_<CATEGORY>.csvwhere CATEGORY is the app category name from the Google Play Store, for exampleapps_ART-AND-DESIGN.csv - A JavaScript file,
trial-play-scraper.js, which can be used to scrape app data from the Google Play Store - A bash shell script
play-store-downloader.sh, which reads a CSV file and downloads the corresponding apps
Each CSV file contains the following columns:
APP_ID: the unique ID of the app on the Google Play StoreTITLE: the title of the appDEVELOPER: the developer of the appSCORE: the score of the app on the Google Play Store
-
Clone this repo to your local machine with
git clone https://github.com/privacy-tech-lab/gpc-android.gitThen, navigate to the
app_csvdirectory. -
To scrape app metadata from the Google Play Store for a particular category, first make sure you have Node.js installed.
-
Then, run the
trial-play-scraper.jsscript withnode trial-play-scraper.js -
To download APKs from the Google Play Store with the
play-store-downloader.shrunchmod +x play-store-downloader.sh ./play-store-downloader.sh
Before running the downloader script replace
[email protected]andpasswordin theplay-store-downloader.shscript with your Google Play Store email and password, respectively. Then, give the script execution permissions and run it. Doing so will download all the apps listed in theapps-ART_AND_DESIGN.csvfile. To download apps from a different category, replace "apps-ART_AND_DESIGN.csv" with the desired CSV file name in the script. -
If downloading apps with the
play-store-downloader.shfails, you can also use Raccoon as follows:- Make sure to have a US-based IP address (e.g., via a VPN)
- Set up an account with Google's US Play Store
- Get Raccoon and a Raccoon Premium license. Use Raccoon's DummyDroid to extract the configuration from a real Android device
- Choose "Import Apps" in Raccoon and paste all apps' links in there (e.g., market://details?id=com.fishbrain.app)
- Sit and wait ...
-
If downloading apps with the two previous methods fails, you can also try the
google-playmethod through apkeep.
We would like to thank our supporters!
Major financial support provided by the National Science Foundation.
Additional financial support provided by the Alfred P. Sloan Foundation, Wesleyan University, and the Anil Fernando Endowment.
Conclusions reached or positions taken are our own and not necessarily those of our financial supporters, its trustees, officers, or staff.

