Found 4 unused Android permissions in my AAB right before Play Store submission — turned out to be expo-screen-capture
Almost shipped my note-taking app to Google Play with 4 sensitive permissions I didn't need. Sharing in case it saves someone else the same surprise.
What happened
I was reviewing the merged AndroidManifest.xml before submitting my AAB for closed testing, and noticed 4 permissions I never asked for:
- READ_MEDIA_IMAGES
- DETECT_SCREEN_CAPTURE
- READ_EXTERNAL_STORAGE
- WRITE_EXTERNAL_STORAGE
For a local-first note-taking app, none of these made sense. Google Play also flags some of these as sensitive in the Data Safety form, so leaving them in would have meant explaining usage I couldn't justify.
Where they came from
Traced it back to expo-screen-capture in package.json. The library declares these permissions in its own AndroidManifest.xml, and the Manifest Merger pulls them into the final app manifest — even though I had zero imports or calls to it in my own code.
The fix
Added tools:node="remove" to my app's AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" tools:node="remove" />
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" tools:node="remove" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />
Rebuilt the AAB, checked the merged manifest, all 4 gone. App still works fine.
Lesson
If you have a managed Expo project and you're about to submit to Play Store, it's worth checking what permissions actually end up in your final merged manifest, not just what you think you declared. Transitive permissions from libraries you're not even using is a real thing.
Yes, the cleaner fix is to just remove the library from package.json — I'll do that in the next release. The tools:node="remove" approach is the fast unblock when you're about to ship.