Troubleshooting Android
Common issues on Android.
Activate logging when troubleshooting
You can activate WonderPush logs by adding a single line in your build config:
buildConfigField 'boolean', 'WONDERPUSH_LOGGING', 'true'
Our logs report pretty much the entire activity of our SDK. They are all prefixed with e with the l so they're easy to filter in and out.
Don't forget to turn them off in your release build.
Make sure you use minimum required versions
If you keep your application up-to-date with the latest Android Studio, Gradle and Gradle plugin, then you are probably already complying with these requirements, but let's be explicit to ensure success.
- Make sure to use the latest
targetSdkVersion
, 31 minimum, per Google Play requirement - Make sure to use the latest
compileSdkVersion
, 31 minimum. - If you are still not using legacy support libraries instead of AndroidX, make sure you use appropriate version, matching
compileSdkVersion
. - Make sure to use
minSdkVersion 21
minimum.
This translates to the following in your app/build.gradle
file:
android {
# Use 30 minimum.
# Using the latest is encouraged by Android Studio
# Cannot be lower than targetSdkVersion
compileSdkVersion 31
defaultConfig {
# Use 31 minimum per Google Play requirement: https://support.google.com/googleplay/android-developer/answer/11926878
# using the latest is encouraged by Android Studio
targetSdkVersion 31
minSdkVersion 21
}
}
“uses-sdk:minSdkVersion 16 cannot be smaller than version 21” error message
We have raised the required minSdkVersion
to 21, released in 2014 and covers 95% of Android devices.
Make sure your application uses at least 21
in your app/build.gradle
, otherwise you'll see the following build error:
Manifest merger failed : uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared in library [com.wonderpush:wonderpush-android-sdk:4.0.1] [...]/.gradle/caches/transforms-2/files-2.1/110f18d74c064672b3bb9b02e46b3f51/jetified-wonderpush-android-sdk-4.0.1/AndroidManifest.xml as the library might be using APIs not available in 16
Suggestion: use a compatible library with a minSdk of at most 16,
or increase this project's minSdk version to at least 21,
or use tools:overrideLibrary="com.wonderpush.sdk" to force usage (may lead to runtime failures)
Trouble receiving notifications
First thing, make sure that you are in the targeted audience by double checking that your installation is in the chosen segment. Look at the installation's tags, properties and events from the dashboard.
Make sure you don't have tons of notifications already displayed, as some devices will not show new notifications.
Freshly started emulator
If you have started your emulator from a save state, FCM somehow does not restores it's connection to Google's backend. The result is that you do not receive notifications for ~15 minutes.
You can fix it by running the following command:
adb shell su root pkill gms
If the above does not fix the issue, you can force your emulator to perform a cold boot by finding your device in the list of the Device Manager tool window of Android Studio, tuck on on right side of the window, clicking the ⋮ icon to reveal a menu and choose Cold Boot Now.
After stopping the app in Android Studio
When developing you might click on the red square Stop button in Android Studio. This kills the application.
Once the app is killed, you will see a log like this in logcat: W/GCM: broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE flg=0x10000000 pkg=com.your.package (has extras) }
.
This is consistent with Force quitting the application and it is a feature of GCM/FCM.
Simply restart your application, then swipe it up in the recent tasks.
You can make sure your application is no longer running by using the following command:
adb shell ps | grep -F com.your.package
If necessary, after closing all your activities, you can stop your app cleanly by using the following command:
adb shell am kill com.your.package
Huawei devices
If you are testing push notifications with Huawei devices, make sure you have added Huawei Mobile Support or you will likely not receive notifications.
Many notifications shown, new ones not showing
If you have already around 50 notifications, you might see the following message in logcat:
E/NotificationService : Package has already posted or enqueued 50 notifications. Not showing more. package=com.myapp
Simply remove the older notifications from your application to make room, newly received notification will now be able to be displayed properly.
Recently upgraded to our Android SDK v4?
Do not forget to add the FCM compatibility module when upgrading to our Android SDK v4.
Otherwise you will see the following error in your logs:
E/WonderPush.Push: Cannot refresh push subscription, no push service available
See the Upgrading to Android SDK v4 guide for resolution.
Network firewalls
If your device is connected to the Internet via an enterprise network, chances are you are behind a Firewall. Firebase Cloud Messaging, the technology behind Android push notifications, requires ports 5228
, 5229
and 5230
to be open for notifications to be delivered. More information.
Power management
In low battery or battery saver modes, notifications can be delayed.
Devices with stamina or ultra power mode enabled drop push notifications altogether to improve battery life even further. Please disable such mode if you want to receive notifications.
The app must not have been force quit or killed.
Note that some devices automatically kill applictions after some time spent in the background as a mean to extend battery life. This can be easily checked if you see W/GCM: broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE flg=0x10000000 pkg=com.your.package (has extras) }
logs in logcat when the device receives a push notification. This indicates that FCM will not wake the app up to let it display the appropriate notification, and thus while delivered properly, the device drops the notification.
Adaptive battery may delay notifications for some apps. You can disable it in the Settings / Battery / Adaptive Battery screen.
App Standby Buckets further affect the application's ability to consume power. In the Settings / Developer Options / Apps (before last section) / Standby apps screen, you can read the current app's state. Read more about app standby buckets on App Standby Buckets and Power management restrictions in the Android documentation.
You can explicitly exempt an application from the Settings / Apps & notifications / Advanced / Special app access / Battery optimization screen, select All apps in the dropdown above the list, click on your app, and choose Don't optimize in the popup.
The most effective way to avoid the restrictive buckets is simply to open the app often. Note that if you recently opened the application to check something like whether you actually subscribed to that event you expected a notification for, you have hence influenced the app standby bucket and just gave it more quota on power usage.
Multiple FirebaseMessagingService
FirebaseMessagingService
If you have multiple dependencies that implement push notifications (even help desks) or background cloud messaging (typically for synchronization purposes), chances are that your application has multiple registered FirebaseMessagingService
services. Unfortunately only one of these will be used and you will not see any compilation warnings.
In order to check if this is the case, you can open the app/build/intermediates/merged_manifests/debug/AndroidManifest.xml
file for mentions of <action android:name="com.google.firebase.MESSAGING_EVENT" />
.
You should only see the following entries:
<service android:name="com.wonderpush.sdk.push.fcm.FirebaseMessagingService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service android:name="com.google.firebase.messaging.FirebaseMessagingService" android:directBootAware="true" android:exported="false">
<intent-filter android:priority="-500">
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
The easy solution is to only use WonderPush as push notification provider. Make sure to remove your old push provider.
If you cannot or do not wish to remove the other dependency, like a help desk dependency that helps you chat with your end-users.
You will then need to implement your own FirebaseMessagingService
and in turn call the onNewToken(Context context, String token)
and onMessageReceived(Context context,RemoteMessage message)
static methods on our class com.wonderpush.sdk.push.fcm.FirebaseMessagingService
, passing getApplicationContext()
in the first argument. Do not forget to do likewise for the other dependency too.
Here is an example code:
<!-- Add the following inside your <application> tag -->
<service android:name=".WrapperFirebaseMessagingService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
package COM.YOUR.PACKAGE; // TODO: Put your application package here
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.util.Log;
import androidx.annotation.NonNull;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
/**
* This class wraps multiple FirebaseMessagingService implementations
* and makes sure each will be called appropriately.
*/
public class WrapperFirebaseMessagingService extends FirebaseMessagingService {
private List<FirebaseMessagingService> messagingServices = new ArrayList<>();
public WrapperFirebaseMessagingService() {
// TODO: Add all the FirebaseMessagingServices you need here
messagingServices.add(new com.wonderpush.sdk.push.fcm.FirebaseMessagingService());
}
//
// Forward FirebaseMessagingService methods
//
// WonderPush needs this method to be forwarded
@Override
public void onNewToken(String s) {
forward(service -> {
service.onNewToken(s);
});
}
// WonderPush needs this method to be forwarded
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
forward(service -> {
service.onMessageReceived(remoteMessage);
});
}
@Override
public void onDeletedMessages() {
forward(service -> {
service.onDeletedMessages();
});
}
@Override
public void onMessageSent(@NonNull String s) {
forward(service -> {
service.onMessageSent(s);
});
}
@Override
public void onSendError(@NonNull String s, @NonNull Exception e) {
forward(service -> {
service.onSendError(s, e);
});
}
//
// Forward Service methods
//
@Override
public void onCreate() {
forward(service -> {
service.onCreate();
});
}
@Override
public void onStart(Intent intent, int startId) {
forward(service -> {
service.onStart(intent, startId);
});
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
forward(service -> {
service.onConfigurationChanged(newConfig);
});
}
@Override
public void onLowMemory() {
forward(service -> {
service.onLowMemory();
});
}
@Override
public void onTrimMemory(int level) {
forward(service -> {
service.onTrimMemory(level);
});
}
@Override
public boolean onUnbind(Intent intent) {
final AtomicBoolean rtn = new AtomicBoolean(false);
forward(service -> {
if (service.onUnbind(intent)) {
rtn.set(true);
}
});
return rtn.get();
}
@Override
public void onRebind(Intent intent) {
forward(service -> {
service.onRebind(intent);
});
}
@Override
public void onTaskRemoved(Intent rootIntent) {
forward(service -> {
service.onTaskRemoved(rootIntent);
});
}
//
// Wrapping code
//
private void forward(Forwarder action) {
for (FirebaseMessagingService service : messagingServices) {
action.run(service);
}
}
interface Forwarder {
void run(FirebaseMessagingService service);
}
// Give the context to wrapped services or they won't be able to call getApplicationContext() and the like,
// and they will hence likely fail.
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
// Accessing protected method Landroid/app/Service;->attachBaseContext(Landroid/content/Context;)V,public-api,sdk,system-api,test-api
Method method;
try {
method = Service.class.getDeclaredMethod("attachBaseContext", Context.class);
} catch (NoSuchMethodException ex) {
Log.e(this.getClass().getSimpleName(), "Failed to reflect Service.attachBaseContext", ex);
return;
}
boolean wasAccessible = method.isAccessible();
if (!wasAccessible) method.setAccessible(true);
// Forward attachBaseContext to the wrapped services too
forward(service -> {
try {
method.invoke(service, newBase);
} catch (IllegalAccessException ex) {
Log.e(this.getClass().getSimpleName(), "Failed to invoke Service.attachBaseContext for " + service, ex);
} catch (InvocationTargetException ex) {
Log.e(this.getClass().getSimpleName(), "Got an error while invoking Service.attachBaseContext for " + service, ex);
}
});
if (!wasAccessible) method.setAccessible(false);
}
}
dependencies {
// You may need to add the following dependencies
//implementation 'com.google.firebase:firebase-messaging'
//implementation 'com.wonderpush:wonderpush-android-sdk-fcm:1.0.5'
}
Build error mentioning AndroidX
You have recently upgraded our Android SDK to v4 but your application does not use AndroidX and you get the following Gradle build errors:
This project uses AndroidX dependencies, but the 'android.useAndroidX' property is not enabled. Set this property to true in the gradle.properties file and retry.
The following AndroidX dependencies are detected: androidx.collection:collection:1.0.0, androidx.cardview:cardview:1.0.0, androidx.versionedparcelable:versionedparcelable:1.0.0, androidx.core:core:1.0.0, androidx.localbroadcastmanager:localbroadcastmanager:1.0.0, androidx.lifecycle:lifecycle-common:2.0.0, androidx.arch.core:core-common:2.0.0, androidx.constraintlayout:constraintlayout:1.1.3, androidx.constraintlayout:constraintlayout-solver:1.1.3, androidx.lifecycle:lifecycle-runtime:2.0.0, androidx.annotation:annotation:1.0.0
Our SDK now depends on AndroidX to benefit from more modular dependencies.
Simply follow Android's Migrating to AndroidX guide.
Migrating is as easy as clicking on the Refactor > Migrate to AndroidX menu entry in Android Studio.
All com.android.support libraries must use the exact same version specification
You are still using our Android SDK v3.x. We have released our Android SDK v4.
Please upgrade using our Upgrading to Android SDK v4 guide.
If you see the error All com.android.support libraries must use the exact same version specification
, add the following in your app/build.gradle
file:
dependencies {
# Make sure you use a version matching your compileSdkVersion, 26.0.2 minimum
# Use the highest version you see in the error message
implementation 'com.android.support:support-v4:+'
}
Duplicate class after upgrading to SDK v4.2.0+
If you see one or multiple errors mentioning something like:
Duplicate class com.google.android.play.core.common.IntentSenderForResultStarter found in modules jetified-core-1.10.0-runtime (com.google.android.play:core:1.10.0) and jetified-core-common-2.0.0-runtime (com.google.android.play:core-common:2.0.0)
You likely are still using the non modular version of the Google Play Services library. Look for dependencies starting with com.google.android.play:
and using the major version 1.
You should update your dependency as outlined in this article: Migration from the Play Core Java and Kotlin Library
If you have no such direct dependency, run the following command to discover which of your direct dependency is indirectly depending on the offending module:
./gradlew --no-daemon app:dependencies --configuration releaseRuntimeClasspath
It outputs a dependency tree that will help you walk up to one of your direct dependencies. Check if an update exists for it.
If not you can still do things like or excluding an indirect dependency or forcing the resolved version of a library. Proceed with caution, it's not always easy to do. Here are examples:
// Excluding an indirect dependency
implementation('group.of.offending.dependency:module.of.offending.dependency:1.+') {
exclude group: 'com.google.android.play', module: 'core'
}
// Overriding the resolved dependency version
configurations.all {
resolutionStrategy {
force 'com.google.android.play:core-common:2.0.0'
force 'com.google.android.play:review:2.0.0'
}
}
Could not initialize WonderPush
If you see the following log:
WonderPush: Could not initialize WonderPush using the initializer class, BuildConfig options or manifest <meta-data> options!
WonderPush: No BuildConfig class found. You probably need to give the value of your gradle defaultConfig.applicationId as the a "wonderpush_buildConfigPackage" string resource or a "com.wonderpush.sdk.buildConfigPackage" manifest <meta-data>.
then you will need to tell the SDK where to find the BuildConfig class.
Firebase fails to initialize. My installation is opt-out.
If you see one of the following errors:
java.lang.IllegalArgumentException: Please set your project ID. A valid Firebase project ID is required to communicate with Firebase server APIs: It identifies your project with Google.
Could not get Firebase InstanceId
java.io.IOException: FIS_AUTH_ERROR
You must use the WonderPush SDK FCM module version 1.0.1 at least.
Edit your app/build.gradle
to add:
dependencies {
implementation 'com.wonderpush:wonderpush-android-sdk-fcm:[1.0.1,2)'
}
Huawei: My installation is opt-out
If you see the following error in your device logs:
E WonderPush.Push.HCM.HCMPushService: Could not get HMS InstanceId
E WonderPush.Push.HCM.HCMPushService: com.huawei.hms.common.ApiException: 907135701: scope list empty
then you probably have a mismatch between the signing configuration and the SHA-256 certificate fingerprint configured in AppGallery Connect.
Check your app/build.gradle
file:
android {
signingConfigs {
mySignConfig { // <- note the actual name you have, and use it below
// [...]
}
}
// Make sure to setup the signingConfig in all buildTypes.
// You are most likely missing the debug build type.
buildTypes {
debug {
signingConfig signingConfigs.mySignConfig // <- use the actual name of one of your signingConfigs
// [...]
}
release {
signingConfig signingConfigs.mySignConfig // <- use the actual name of one of your signingConfigs
// [...]
}
}
}
You can also add more SHA-256 certificate fingerprints in AppGallery Connect. Follow Huawei Push Kit / Preparations / 1.3 Generating a Signing Certificate Fingerprint documentation on how to proceed.
Installations have no push token, in my application uses MultiDex
You should see an error in the logs saying that we could not initialize Firebase due to the following error:
java.lang.NoClassDefFoundError: class com.google.firebase.iid.* is not an found
Then you are probably developing a MultiDex-enabled application.
This is due to the Firebase classes not being included in the primary dex.
The simplest solution we found is to bump our dependency of the Firebase Cloud Messaging dependency.
In your app/build.gradle
file, simply add this new dependency new to our SDK's FCM module:
implementation 'com.google.firebase:firebase-messaging:20.3.0'
I do not see any logs in logcat
If you want to see more logs from WonderPush, you can call the WonderPush.setLogging(true);
method, or better, add this buildConfigField to grab the logs of the SDK before your Application class is ever run.
Sometimes your project can be built with extra "optimizations", that remove all logging, hence your application is completely mute in logcat. We do not recommend using such a configuration. This happens in builds using minifyEnabled true
, where ProGuard is used.
Check that you do not have the following ProGuard rules in your project, typically in your app/proguard-rules.txt
:
# This deactivates all logging
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
public static *** wtf(...);
public static *** println(...);
}
# The following seems simpler, but has other dangerously side effects too.
# Remove this immediately if you ever find it in your codebase!
-assumenosideeffects class android.util.Log {
public *;
}
If you find these rules, remove them and the logs will start flowing again.
This can happen too if you use the proguard-android-optimize.txt
rules, instead of the regular proguard-android.txt
ones, as they add the above -assumenosideeffects
rules.
Leaked GCP API Keys
If you have this kind of message, it's because when the WonderPush SDK calls Firebase to get a push token, Google notes the API Key used, and after running an analysis, considers that it has leaked.
What should I do?
You should follow the recommendations in the linked Google Help Center article named Remediation for Exposed GCP API keys.
- Make sure to use your own Firebase account.
- Generate a new API Key
- Apply restrictions on the new API Key
- Edit your
google-services.json
file to use the new API Key. This step must be done each time you download thegoogle-services.json
file as Firebase will not update it server-side. Note that you must not delete the previous key or your current application installations on your users' devices will stop working.
Note that you may still see the warning after you have restricted the API Key.
Here are the explanations from a Google Security expert.
Here are recommendations on Securing an API Key.
It's interesting to note that Firebase declares that the API Key is a public information in the first place, here and there.
Updated 8 months ago