EAS build requires READ_PHONE_STATE permission on Android

I switched my Android build over from Turtle to eas --local. Google Play now won’t accept the AAB because it requires an additional permission:

Your APK or Android App Bundle is using permissions that require a privacy policy: (android.permission.READ_PHONE_STATE).

I’m using a managed Expo app on SDK 44. I did perform the upgrade to that SDK together with the migration to EAS, so I unfortunately don’t know which change exactly triggered the permission change.

In my config, I have permissions: [], which per the docs, should only request the bare minimum. With Turtle, that seemed to work.

Used Expo packages:

    "expo": "^44.0.3",
    "expo-app-loading": "^1.3.0",
    "expo-asset": "^8.4.5",
    "expo-constants": "^13.0.0",
    "expo-font": "^10.0.4",
    "expo-linking": "^3.0.0",
    "expo-navigation-bar": "^1.1.1",
    "expo-splash-screen": "^0.14.1",
    "expo-status-bar": "^1.2.0",
    "expo-store-review": "^5.1.0",
    "expo-system-ui": "^1.1.0",
eas-cli/0.43.0 linux-x64 node-v16.13.1

Thanks!

I want to add that the generated android/app/src/main/AndroidManifest.xml only contains:

  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
  <uses-permission android:name="android.permission.VIBRATE"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

so I’m struggling to understand where the permission requirement is coming from. I don’t have a lot of experience with Android’s build process though.

android permissions can come from your own android manifest or any library android manifest. so there may be some library outside of those that you listed which pulls in the permission.

the documentation for permissions in app.json applies to the classic build system currently - not eas build. you can add READ_PHONE_STATE to an block list using a config plugin. see this repo for an example: GitHub - brentvatne/blockperm

here is the relevant commit: config · brentvatne/blockperm@4ca2c5b · GitHub

1 Like

Interesting, thanks. I do have some follow-up questions then:

  • If app.json permissions don’t apply to EAS, is the only alternative to eject (or to block individual permissions as you showed)?
  • I tried grep uses-permission $(find node_modules -name AndroidManifest.xml) and got only the permissions that are listed in the generated AndroidManifest.xml for my app, which I pasted above - except VIBRATE, interestingly. From what you’re saying, additional permissions can be requested in other ways?
  • If so, how do I obtain an exhaustive list of permissions effectively required by my build?
  • And related to that, how do I determine which of my dependencies are requesting which additional permissions?
  • If app.json permissions don’t apply to EAS, is the only alternative to eject (or to block individual permissions as you showed)?

the example i provided above shows how to restrict permissions for eas build. if you want to add more permissions, you can still use the permissions key for that. you probably don’t need to do this in most cases because libraries will add their permissions automatically. you can think of the permissions key for eas build as just adding permissions to whatever permissions are automatically added by your libraries.

  • I tried grep uses-permission $(find node_modules -name AndroidManifest.xml) and got only the permissions that are listed in the generated AndroidManifest.xml for my app, which I pasted above - except VIBRATE , interestingly. From what you’re saying, additional permissions can be requested in other ways?

it may also be coming from a config plugin that adds the permission, eg: expo-cellular does this. try searching for READ_PHONE_STATE in your node_modules

  • If so, how do I obtain an exhaustive list of permissions effectively required by my build?

there isn’t a good way to do this without running a build and inspecting the apk. this is a downside of how android permissions work.

  • And related to that, how do I determine which of my dependencies are requesting which additional permissions?

for the same reason as the above, it’s not easy to determine which dependencies are added until you do a build. you can build an apk and run aapt d permissions path/to/app.apk to dump the permissions info once it’s built.

Thanks again!

grep -lri read_phone_state node_modules only gives me Expo’s own internal modules and none of my additional dependencies, so I have the feeling EAS is adding the permission itself.

I was able to work around it by excluding permissions as you demonstrated, but that feels a bit arbitrary. Whenever a dependency changes, it might add a permission that I’m not excluding.

I guess I’ll want to add a post-build step that tests the output of aapt d, but just to confirm: there’s no way to get the effective permissions without doing a full build?

I’m building an aab rather than an apk though. Is there a similar command for that? I know I can unzip the aab but maybe there’s a clean way? I’ll use bundletool for that.

I also see there’s a AndroidConfig.Permissions.withPermissions. Is that for defining only the required permissions or is it equivalent to permissions in my config?

It’s for adding permissions. Not setting the exact set of permissions.

By the way, I’m not sure it’s relevant, but this says that at least up until React Native 0.63, READ_PHONE_STATE was one of the default permissions added (by React Native?). The docs for version 0.64 don’t have that page, though, and the closest I could find does not mention READ_PHONE_STATE. Since Expo SDK 44 uses React Native 0.64.3 and assuming the docs are correct it seems that React Native should not be adding the READ_PHONE_STATE permission, but I’m not sure.

Well, it wasn’t getting added with Turtle, but perhaps that’s why I’d added permissions: [] to my config? (Which, as discussed above, doesn’t apply to EAS.)

permissions: [] did not cause default permissions to be excluded, although based on the RN documentation and what you’ve said it seems that expo build must have removed READ_PHONE_STATE :man_shrugging:

But permissions: [] used to do something, right? This is what the docs say:

To use ONLY the following minimum necessary permissions and none of the extras supported by Expo in a default managed app, set permissions to [] . The minimum necessary permissions do not require a Privacy Policy when uploading to Google Play Store and are:

  • receive data from Internet
  • view network connections
  • full network access
  • change your audio settings
  • prevent device from sleeping

To use ALL permissions supported by Expo by default, do not specify the permissions key.

Yes, right. What I meant was that even if you specified permissions: [] there would still be some default permissions included. i.e. the ones you listed. But of course READ_PHONE_STATE does not appear to be one of those, so I’m still not sure where that comes from.

Getting the same when moving from Turtle to EAS. Will stick with Turtle until bug fixed.

Per @notbrent’s answer, adding this to app.config.js does work:

{
  plugins: [
    [
      AndroidConfig.Permissions.withBlockedPermissions,
      [
        'android.permission.VIBRATE',
        'android.permission.READ_EXTERNAL_STORAGE',
        'android.permission.WRITE_EXTERNAL_STORAGE',
        'android.permission.READ_PHONE_STATE',
        'android.permission.RECORD_AUDIO'
      ]
    ]
  ]
}
2 Likes

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.