androidfluttergoogle-playgoogle-play-consoleandroid-app-bundle

Flutter app some devices are excluded by rule in Google Play Console


I am using flutter to release my android app and using this command to upload a build :

flutter build appbundle --obfuscate --release --split-debug-info=/Users/foxtom/StudioProjects/project_name/build --build-name=1.0.0 --build-number=1

And i see this device for example that is excluded in my Play Console Device catalog :

xiaomi_ginkgo_excluded

I would like to understand why it is excluded ?

Here is my manifest file :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myapp.android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application
        android:label="My App Name"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">

        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="ca-app-pub-ejfqifjq"
            />

        <property
            android:name="android.adservices.AD_SERVICES_CONFIG"
            android:resource="@xml/gma_ad_services_config"
            tools:replace="android:resource" />

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->

            <meta-data android:name="flutter_deeplinking_enabled" android:value="false" />

            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="https" android:host="www.mywebsite.com" android:pathPrefix="/any" />
            </intent-filter>

            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>


        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
    <!-- Required to query activities that can process text, see:
         https://developer.android.com/training/package-visibility?hl=en and
         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.

         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>
</manifest>

and my build.gradle file :

plugins {
    id "com.android.application"
    // START: FlutterFire Configuration
    id 'com.google.gms.google-services'
    id 'com.google.firebase.firebase-perf'
    id 'com.google.firebase.crashlytics'
    // END: FlutterFire Configuration
    id "kotlin-android"
    id "dev.flutter.flutter-gradle-plugin"
}

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    namespace "com.myapp_android"
    compileSdk 35
    ndkVersion "27.1.12297006"

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        applicationId "com.myapp.android"
        // You can update the following values to match your application needs.
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
        minSdkVersion 23
        targetSdkVersion 35
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "com.google.android.gms:play-services-ads:23.4.0"
    implementation 'com.giphy.sdk:ui:2.3.15'
}

I hope that if i can find an answer for this device i will find for other devices.

Note : Google Play Support told me to

ask a question since we can't help with technical issues.

EDIT : I have raised a github issue in Flutter repo to see if they have more insights.

The weirdest thing is that i can "Exclude" the devices that are apparently already excluded, but it's like they are considered not excluded... And if i click on "Manage exclusion rules", there are no rules at all obviously...

enter image description here


Solution

  • Do you use Play Integrity API ?

    In my app i had set it up to "Device integrity checks (recommended)" but when I have lowered it to "Basic integrity checks" it didn't exclude anything.

    The funny thing is that Google writes : "Devices excluded based on Play Integrity verdicts aren't shown in the tables below" But this is simply a wrong statement. Give it a try despite the Google sentence.