{"id":19081491,"url":"https://github.com/privacy-tech-lab/gpc-android","last_synced_at":"2025-10-26T11:06:07.331Z","repository":{"id":154431643,"uuid":"193385132","full_name":"privacy-tech-lab/gpc-android","owner":"privacy-tech-lab","description":"Code and dynamic analysis scripts for GPC on Android","archived":false,"fork":false,"pushed_at":"2025-06-24T20:29:53.000Z","size":105183,"stargazers_count":5,"open_issues_count":2,"forks_count":2,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-07-25T11:20:58.159Z","etag":null,"topics":["android","code-analysis","dnt","do-not-track","dynamic-analysis","global-privacy-control","gpc","mobile-privacy","opt-out","privacy","privacy-analysis","privacy-enhancing-technologies"],"latest_commit_sha":null,"homepage":"https://privacytechlab.org/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/privacy-tech-lab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":"FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"privacy-tech-lab"}},"created_at":"2019-06-23T19:01:09.000Z","updated_at":"2025-06-24T20:29:57.000Z","dependencies_parsed_at":"2024-11-24T20:21:41.164Z","dependency_job_id":"bd341dbe-e4ce-44e0-b103-8b154527372b","html_url":"https://github.com/privacy-tech-lab/gpc-android","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/privacy-tech-lab/gpc-android","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privacy-tech-lab%2Fgpc-android","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privacy-tech-lab%2Fgpc-android/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privacy-tech-lab%2Fgpc-android/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privacy-tech-lab%2Fgpc-android/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/privacy-tech-lab","download_url":"https://codeload.github.com/privacy-tech-lab/gpc-android/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/privacy-tech-lab%2Fgpc-android/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267007610,"owners_count":24020262,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-25T02:00:09.625Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","code-analysis","dnt","do-not-track","dynamic-analysis","global-privacy-control","gpc","mobile-privacy","opt-out","privacy","privacy-analysis","privacy-enhancing-technologies"],"created_at":"2024-11-09T02:36:18.769Z","updated_at":"2025-10-26T11:06:02.295Z","avatar_url":"https://github.com/privacy-tech-lab.png","language":"JavaScript","funding_links":["https://github.com/sponsors/privacy-tech-lab"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/releases\"\u003e\u003cimg alt=\"GitHub release (latest by date)\" src=\"https://img.shields.io/github/v/release/privacy-tech-lab/gpc-android\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/releases\"\u003e\u003cimg alt=\"GitHub Release Date\" src=\"https://img.shields.io/github/release-date/privacy-tech-lab/gpc-android\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/commits/main\"\u003e\u003cimg alt=\"GitHub last commit\" src=\"https://img.shields.io/github/last-commit/privacy-tech-lab/gpc-android\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/issues\"\u003e\u003cimg alt=\"GitHub issues\" src=\"https://img.shields.io/github/issues-raw/privacy-tech-lab/gpc-android\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/issues?q=is%3Aissue+is%3Aclosed\"\u003e\u003cimg alt=\"GitHub closed issues\" src=\"https://img.shields.io/github/issues-closed-raw/privacy-tech-lab/gpc-android\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/blob/main/LICENSE.md\"\u003e\u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/privacy-tech-lab/gpc-android\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/watchers\"\u003e\u003cimg alt=\"GitHub watchers\" src=\"https://img.shields.io/github/watchers/privacy-tech-lab/gpc-android?style=social\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/stargazers\"\u003e\u003cimg alt=\"GitHub Repo stars\" src=\"https://img.shields.io/github/stars/privacy-tech-lab/gpc-android?style=social\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/privacy-tech-lab/gpc-android/network/members\"\u003e\u003cimg alt=\"GitHub forks\" src=\"https://img.shields.io/github/forks/privacy-tech-lab/gpc-android?style=social\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/sponsors/privacy-tech-lab\"\u003e\u003cimg alt=\"GitHub sponsors\" src=\"https://img.shields.io/static/v1?label=Sponsor\u0026message=%E2%9D%A4\u0026logo=GitHub\u0026color=%23fe8e86\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://privacytechlab.org/\"\u003e\u003cimg src=\"./wifi.svg\" width=\"200px\" height=\"200px\" alt=\"GPC Android Image\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# GPC Android\n\nThis repo contains code and other resources for dynamically analyzing Android apps, especially, for checking their compliance with [Global Privacy Control (GPC)](https://globalprivacycontrol.org/). 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.\n\nThe code and all other resources in this repo are developed and maintained by **Nishant Aggarwal (@n-aggarwal)**, **Zachary Liu (@zatchliu)**, **Samir Cerrato (@samir-cerrato)** and **Sebastian Zimmeck (@SebastianZimmeck)** of the [privacy-tech-lab](https://privacytechlab.org/) and **Konrad Kollnig (@kasnder)** of the [Law and Tech Lab of Maastricht University](https://www.maastrichtuniversity.nl/about-um/faculties/law/research/law-and-tech-lab). Wesley Tan (@wesley-tan) contributed earlier.\n\n[1. Research Publications](#1-research-publications)  \n[2. Repo Overview](#2-repo-overview)  \n[3. GPC Android App](#3-gpc-android-app)  \n[4. Scripts](#4-scripts)  \n[5. Apps CSV](#5-apps-csv)  \n[6. Thank You!](#6-thank-you)\n\n## 1. Research Publications\n\nSebastian Zimmeck, Nishant Aggarwal, Zachary Liu and Konrad Kollnig, [From Ad Identifiers to Global Privacy Control: The Status Quo and Future of Opting Out of Ad Tracking on Android](https://arxiv.org/abs/2407.14938), Under Review, [BibTeX]().\n\nIf you are using code or other resources from this repo, please cite the above paper.\n\nYou can find a complete list of our GPC research publications in the [GPC OptMeowt repo](https://github.com/privacy-tech-lab/gpc-optmeowt?tab=readme-ov-file#1-research-publications).\n\n## 2. Repo Overview\n\nThis repo contains the following resources:\n\n- `gpc-android-app`: GPC Android app written in Java\n- `scripts`: Code for dynamically intercepting and analyzing network traffic\n- `app_csv`: App lists sorted by Google Play Store categories\n\n## 3. GPC Android App\n\nThe `gpc-android-app` directory contains the code for an app with the following features:\n\n1. Directing people to the AdID setting, where they can disable tracking, which, in our view, is equivalent to turning on GPC\n2. Directing people to DuckDuckGo or Brave, two browsers with GPC enabled\n\nYou can run the app by cloning this repo and running it in [Android Studio](https://developer.android.com/studio).\n\nAlso, check out [Konrad's GPC Android app](https://github.com/TrackerControl/gpc_android)!\n\n## 4. Scripts\n\nThe scripts can be used in conjunction with [mitmproxy](https://mitmproxy.org/) SOCKS5 mode to intercept network traffic and perform dynamic privacy analysis on Android apps. This following is a guide on how to run captures using the gpc-android framework. To begin you should clone the repo.\n\n### Dependencies (Prerequisites)\n1. Frida Server on Test Device\n2. App for SOCKS Proxy on Test Device\n3. MITM Proxy in laptop\n4. Mullvad VPN Account (and set up in laptop)\n5. MITM Proxy installed and setup in laptop\n6. ADB Installed and set up on the laptop\n7. Rooted Test device set up with the appropriate Magisk Modules \n8. USB Debugging is enabled in the test device\n\n### Apps to Test\n\nOnce 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.\n\nThe 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 highly recommended that the apks are deleted (or not staged) prior to any commit.   \n\n### Setting up the capture infrastructure\n\nNow we can move on to setting up the capture infrastructure. To begin, you will need a usb type-c to type-c cord. Use this to connect the test device to the laptop (via adb). Make sure the device is connected by running the command `adb devices`. If you see a device in the list, you should be ready. If not, simply try detaching and reconnecting the usb connection.\n\n### Running the captures\n\nBefore you begin, make sure that your setup has followed all instructions till here 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.\n\n1.  Reboot the test device: \n```\nadb reboot \n```\n2. Turn on Data Saver and Turn off Private DNS on the Test Device; both the settings can be found in `Settings -\u003e Networks`\n3. Turn on the Frida\n```\nadb shell su -c /data/local/tmp/frida-server \u0026\n```\n4. Now connect the test device to the laptop using SOCKS Proxy. To do so first find the IP address of the laptop (for Macbooks, this can be found in `Settings -\u003e Wifi -\u003e Details`. Now set this to be the IP address of the SOCKS Proxy on the test device. Set the port to 8889. Turn on the SOCKS Proxy Connection.\n5. Turn on Mullvad VPN on the laptop and make sure, that you are connected to a server in California.\n6. Navigate to the `scripts` directory in the `gpc-android` repo and run the command \n```\nbash multi-app-automation-script.sh\n```\n\n### Output files\n\nThe output file (.mitm and .har files) are stored in the `mitm-captures` folder. These files will not be uploaded to the remote. If you do want to upload them, please modify the .gitignore files accordingly.\n\n### Troubleshooting\n\n#### Frida \n\nFrida is the most likely component to break the testing framework. This is because new OTA Android updates can break its functionality. To fix the issue, you can try a couple of things:\n- Install the latest version of frida server on android (arm64)\n- Make sure the frida client and frida server are the same version\n- Check the frida github for relevant information on the issue you may be facing\n\nAn error you are likely to encounter when running the Frida server is the following:\n\n- {\"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}\n\nTo resolve this issue, you can delete the Android run time library. Steps to uninstall run time:\n\n1. Run \"adb shell\"\n```\nadb shell\n```\n3. Run \"pm uninstall com.google.android.art\"\n```\npm uninstall com.google.android.art\n```\n2. Exit the shell with CTRL + D and then reboot your test device\n```\nadb reboot \n```\n\n#### Network Connection\n\nThe 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:\n- **The SOCKS Proxy:** The SOCKS Proxy connects the (Android) phone to the laptop. To make sure the connection is setup properly, first check the IP address of the laptop you are connecting the device to. Now set that to the IP address of to be reached in in 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.\n- **MITM Proxy:** The MITM Proxy is the next stage in the network transfer process. To make sure this 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.\n- **Mullvad VPN:** The last step in our network transfer process is the Mullvad VPN. I 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 on potential point of issue. \n\n### Procedure for Manual Traffic Analysis\n\nThe process for capturing traffic manually is similar to the setup above, but has some key differences in method. In order to do this, first follow the instructions under \"Setting up the capture infrastructure,\" as well as those under \"Running the captures\" above through step 5 (turning on Mullvad VPN). Additionally, you will need the app whose traffic you wish to analyze installed on your android device. Proceed to the following steps:\n\n  1. Edit the paths given in `save_flows.py` (in the `scripts` directory) to ensure the script has a valid path to save to. Generally, this path should be `./mitm-captures/$APP_PACKAGE_NAME/not_opted_out.jsonl` (or `.../opted_out.jsonl`).\n  2.  Then run the following three commands in your terminal in sequence. This clears any data on the android device associated with the given app, begins capturing network traffic from the device, and opens the app with the necessary Frida script running.\n \n    adb shell su -c pm clear $APP_PACKAGE_NAME \n\n    mitmdump --mode socks5  -s ./scripts/save_flows.py \n\n    frida -U -l ./scripts/frida-script.js -f $APP_PACKAGE_NAME \n  3. 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 with `exit` to end the Frida script.\n\nThis capture method is used to directly examine the traffic an app is sending before and after an in-app privacy toggle is switched on. \n\n## 5. Apps CSV\n\nThe `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.\n\n### 5.1 Directory Contents\n\nThe directory contains the following files:\n\n- Multiple CSV files with the naming convention `apps_\u003cCATEGORY\u003e.csv` where CATEGORY is the app category name from the Google Play Store, for example `apps_ART-AND-DESIGN.csv`\n- A JavaScript file, `trial-play-scraper.js`, which can be used to scrape app data from the Google Play Store\n- A bash shell script `play-store-downloader.sh`, which reads a CSV file and downloads the corresponding apps\n\nEach CSV file contains the following columns:\n\n- `APP_ID`: the unique ID of the app on the Google Play Store\n- `TITLE`: the title of the app\n- `DEVELOPER`: the developer of the app\n- `SCORE`: the score of the app on the Google Play Store\n\n### 5.2 How to Use\n\n1. Clone this repo to your local machine with:\n\n   ```bash\n   git clone https://github.com/privacy-tech-lab/gpc-android.git\n   ```\n\n   Then, navigate to the `app_csv` directory.\n\n2. To scrape app metadata from the Google Play Store for a particular category, first make sure you have [Node.js](https://nodejs.org/en) installed.\n\n3. Then, run the `trial-play-scraper.js` script with:\n\n   ```bash\n   node trial-play-scraper.js\n   ```\n\n4. To download APKs from the Google Play Store with the `play-store-downloader.sh` run:\n\n   ```bash\n   chmod +x play-store-downloader.sh\n   ./play-store-downloader.sh\n   ```\n\n   Before running the downloader script replace `email@gmail.com` and `password` in the `play-store-downloader.sh` script 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 the `apps-ART_AND_DESIGN.csv` file. To download apps from a different category, replace \"apps-ART_AND_DESIGN.csv\" with the desired CSV file name in the script.\n\n5. If downloading apps with the `play-store-downloader.sh` fails, you can also use [Raccoon](https://raccoon.onyxbits.de/) as follows:\n\n   1. Make sure to have a US-based IP address (e.g., via a VPN)\n   2. Set up an account with Google's US Play Store\n   3. Get Raccoon and a Raccoon Premium license. Use Raccoon's DummyDroid to extract the configuration from a real Android device\n   4. Choose \"Import Apps\" in Raccoon and paste all apps' links in there (e.g., \u003cmarket://details?id=com.fishbrain.app\u003e)\n   5. Sit and wait ...\n\n6. If downloading apps with the two previous methods fails, you can also try the `google-play` method through [apkeep](https://github.com/EFForg/apkeep).\n\n\n## 6. Thank You!\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eWe would like to thank our supporters!\u003c/strong\u003e\u003c/p\u003e\u003cbr\u003e\n\n\u003cp align=\"center\"\u003eMajor financial support provided by the National Science Foundation.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://nsf.gov/awardsearch/showAward?AWD_ID=2055196\"\u003e\n    \u003cimg class=\"img-fluid\" src=\"./nsf.png\" height=\"100px\" alt=\"National Science Foundation Logo\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003eAdditional financial support provided by the Alfred P. Sloan Foundation, Wesleyan University, and the Anil Fernando Endowment.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://sloan.org/grant-detail/9631\"\u003e\n    \u003cimg class=\"img-fluid\" src=\"./sloan_logo.jpg\" height=\"70px\" alt=\"Sloan Foundation Logo\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.wesleyan.edu/mathcs/cs/index.html\"\u003e\n    \u003cimg class=\"img-fluid\" src=\"./wesleyan_shield.png\" height=\"70px\" alt=\"Wesleyan University Logo\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003eConclusions reached or positions taken are our own and not necessarily those of our financial supporters, its trustees, officers, or staff.\u003c/p\u003e\n\n##\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://privacytechlab.org/\"\u003e\u003cimg align=\"center\" src=\"./plt_logo.png\" width=\"auto\" height=\"200px\" alt=\"privacy-tech-lab logo\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.maastrichtuniversity.nl/about-um/faculties/law/research/law-and-tech-lab\"\u003e\u003cimg align=\"center\" src=\"./maastricht_law_tech.jpg\" width=\"auto\" height=\"100px\" alt=\"Logo of Maastricht University Law and Tech Lab\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprivacy-tech-lab%2Fgpc-android","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprivacy-tech-lab%2Fgpc-android","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprivacy-tech-lab%2Fgpc-android/lists"}