EAS Config Plugin to add `android:exported="true"` to Manifest?

Recently I changed my targetSdkVersion and compileSdkVersion to 31.

I tried to submit my new .abb. While I had no errors in build, I am having the following error in submit:

[logs] [!] Google Api Error: Invalid request - You uploaded an APK or Android App Bundle which has an activity, activity alias, service or broadcast receiver with intent filter, but without 'android:exported' property set. This file can't be installed on Android 12 or higher. See: developer.android.com/about/versions/12/behavior-changes-12#exported
[logs] fastlane supply failed
[logs] Failed to submit the app

Researching about this issue (You uploaded an APK or Android App Bundle which has an activity, activity alias, service or broadcast receiver with intent filter, but without the 'an - Stack Overflow), seems that I need to add android:exported="true" to the android manifest with an EAS Config Plugin.

There is the withAndroidManifest, but I ain’t sure where and how I can add this attribute.

Hi @srbrahma

What dependencies do you have installed? If I understand correctly it could be caused by one of them. I suspect you could use patch-package to fix this if so.

Hi @wodin

"dependencies": {
    "@ahive/shared": "workspace:^",
    "@babel/runtime": "7.17.7",
    "@expo-google-fonts/dm-sans": "0.2.2",
    "@expo-google-fonts/inter": "0.2.2",
    "@expo-google-fonts/mada": "^0.2.2",
    "@expo-google-fonts/roboto": "0.2.2",
    "@react-native-async-storage/async-storage": "1.15.17",
    "@react-native-community/datetimepicker": "4.0.1",
    "@react-navigation/bottom-tabs": "6.2.0",
    "@react-navigation/material-top-tabs": "6.1.1",
    "@react-navigation/native": "6.0.8",
    "@react-navigation/stack": "6.1.1",
    "@sindresorhus/is": "4.6.0",
    "@stripe/stripe-react-native": "^0.5.0",
    "@typesaurus/react": "5.0.0-alpha.4",
    "dayjs": "1.11.0",
    "expo": "44.0.6",
    "expo-app-loading": "1.3.0",
    "expo-application": "4.0.2",
    "expo-auth-session": "3.5.0",
    "expo-constants": "13.0.2",
    "expo-dev-client": "0.8.4",
    "expo-font-loader": "0.1.6",
    "expo-image-picker": "12.0.2",
    "expo-mail-composer": "11.1.1",
    "expo-navigation-bar": "1.1.2",
    "expo-notifications": "0.14.1",
    "expo-random": "12.1.2",
    "expo-status-bar": "1.2.0",
    "expo-system-ui": "~1.1.0",
    "firebase": "8.10.1",
    "firebase-functions-extended-client": "5.5.1",
    "fractional-indexing": "2.0.0",
    "lodash": "4.17.21",
    "pagescrollview": "1.3.2",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-hook-form": "7.28.0",
    "react-hooks-global-state": "1.0.2",
    "react-native": "0.64.3",
    "react-native-bouncy-checkbox": "2.1.10",
    "react-native-bouncy-checkbox-group": "npm:react-native-bouncy-checkbox-group-srbrahma@0.1.1",
    "react-native-dialog": "9.2.1",
    "react-native-draggable-flatlist": "3.0.7",
    "react-native-gesture-handler": "2.2.0",
    "react-native-gev": "^0.45.6",
    "react-native-mask-text": "0.7.0",
    "react-native-modal-datetime-picker": "10.2.0",
    "react-native-pager-view": "5.4.9",
    "react-native-payment-icons": "1.0.11",
    "react-native-reanimated": "2.3.1",
    "react-native-safe-area-context": "3.3.2",
    "react-native-screens": "3.10.2",
    "react-native-shadow-2": "6.0.3",
    "react-native-size-matters": "0.4.0",
    "react-native-svg": "^12.3.0",
    "react-native-tab-view": "3.1.1",
    "react-native-web": "0.17.1",
    "ts-retry-promise": "0.6.1",
    "typesaurus": "npm:typesaurus-srbrahma@^1.0.0"
  },
  "devDependencies": {
    "@babel/core": "7.17.7",
    "@babel/preset-typescript": "^7.16.7",
    "@types/async-retry": "^1.4.3",
    "@types/deep-equal": "^1.0.1",
    "@types/lodash": "^4.14.179",
    "@types/react": "~17.0.38",
    "@types/react-native": "~0.64.23",
    "@types/react-router-native": "^5.1.3",
    "babel": "^6.23.0",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.2.3",
    "babel-plugin-module-resolver": "4.1.0",
    "eas-cli": "^0.48.2",
    "eslint-config-gev": "^2.33.0",
    "expo-cli": "5.3.0",
    "typescript": "^4.6.2"
  },

I’ve tinkered a bit and made this plugin to add the android:exported to the Manifest MainActivity. Unsure if it will work, building the new bundle and will soon try to submit it again:

const {
  withPlugins,
  withAndroidManifest,
  AndroidConfig,
} = require('@expo/config-plugins');

const { getMainActivityOrThrow } = AndroidConfig.Manifest


const withAndroidExported = (config) => {
  return withAndroidManifest(config, (config) => {
    const activity = getMainActivityOrThrow(config.modResults);
    activity.$['android:exported'] = false;
    return config;
  })
};

module.exports = (config, props) =>
  withPlugins(config, [
    [withAndroidExported, props],
  ]);

Edit: Nope, above didn’t work. Don’t know how to find out what is causing this.

I’ve found that you can test this by creating and running an Android 12 AVD and trying to install the APK onto it:

eas build -p android --profile preview --local
[...]
adb install build-1649066639796.apk
Performing Streamed Install
adb: failed to install build-1649066639796.apk: Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl984449092.tmp/base.apk (at Binary XML file line #124): com.example.androidExportedTest.MainActivity: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present]

It points to line “#124” of the binary AndroidManifest.xml

There are many ways to decode the binary XML, but unfortunately I don’t know if there’s a way to find out exactly which line is #124 in the binary version.

e.g. apktool d build-1649066639796.apk

Could you try building a newly created Expo SDK 44 app with the Android SDK 31 changes and see if it installs on Android 12? If it does I think comparing the decoded AndroidManifest.xml files between this app and the real app could be enlightening.

If it also fails with a newly created Expo app, then I think it’s worth creating an issue on GitHub.

The error message does mention com.example.androidExportedTest.MainActivity. If I look in the decoded AndroidManifest.xml from an APK with all of your dependencies (except the @ahive/shared one I couldn’t install) there is an activity that looks like this:

    <activity android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
      android:label="@string/app_name"
      android:launchMode="singleTask"
      android:name="com.example.androidExportedTest.MainActivity"
      android:screenOrientation="portrait"
      android:theme="@style/Theme.App.SplashScreen"
      android:windowSoftInputMode="adjustResize">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
      <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="com.example.androidExportedTest"/>
        <data android:scheme="exp+androidexportedtest"/>
      </intent-filter>
    </activity>

So it might be that you just need to add the android:export="true" there.

But I think it’s worth trying the newly created app first to see if that works.