Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,21 @@ jobs:
working-directory: OneSignalSDK
run: |
./gradlew testDebugUnitTest --console=plain --continue
- name: "[Build] Demo app (minified GMS + Huawei release)"
- name: "[Build] Demo app (minified GMS release)"
working-directory: OneSignalSDK
run: |
./gradlew :app:assembleGmsRelease :app:assembleHuaweiRelease --console=plain
./gradlew :app:assembleGmsRelease --console=plain
# Runs against the GMS release seeds emitted by the build above (before the Huawei build
# overwrites them). Fails if a consumer keep protecting a reflectively-referenced member
# silently disappeared -- the class of regression that assemble alone cannot catch.
- name: "[R8] Consumer keep-rules diff gate"
working-directory: OneSignalSDK
run: |
../scripts/r8-keep-check.sh
- name: "[Build] Demo app (minified Huawei release)"
working-directory: OneSignalSDK
run: |
./gradlew :app:assembleHuaweiRelease --console=plain
- name: "[Diff Coverage] Check for bypass"
id: coverage_bypass
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
Expand Down
48 changes: 32 additions & 16 deletions OneSignalSDK/onesignal/core/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
-dontwarn com.onesignal.**

# Amazon ADM is a compileOnly dependency; suppress missing-class warnings when the app omits it.
-dontwarn com.amazon.**

-keepclassmembers class com.onesignal.core.** { *; }

-keepclassmembers class com.onesignal.session.** { *; }

-keepclassmembers class com.onesignal.user.** { *; }

-keepclassmembers class com.onesignal.internal.** { *; }

-keepclassmembers class com.onesignal.debug.** { *; }

-keepclassmembers class com.onesignal.common.** { *; }

# Service implementations are instantiated by reflectively selecting a constructor
# (ServiceRegistrationReflection.resolve -> constructor.newInstance). The classes are retained via
# ::class.java literals in the modules, so only their constructors need protecting here.
-keepclassmembers class com.onesignal.** {
<init>(...);
}

# Models are populated from JSON by matching getter method *names* via reflection
# (Model.initializeFromJson looks up "get<Property>"/"is<Property>"). Getters must survive and keep
# their names, so this must run without allowobfuscation.
-keepclassmembers class * extends com.onesignal.common.modeling.Model {
*** get*();
*** is*();
}

# Enum constants are looked up by name via valueOf/enumValueOf against server-supplied strings
# (e.g. Model.getOptEnumProperty -> ConfigModel.logLevel from remote-params JSON). Their names must
# survive obfuscation, so keep enum fields and the synthetic values()/valueOf(String) methods.
-keepclassmembers enum com.onesignal.** {
<fields>;
public static **[] values();
public static ** valueOf(java.lang.String);
}

# Optional feature modules are loaded by fully-qualified name (OneSignalImp loads them via
# Class.forName(...).newInstance()), so the name must be preserved and a no-arg constructor kept.
-keep class ** implements com.onesignal.common.modules.IModule {
<init>();
}

# Fallback "misconfigured" managers are reflectively constructed when an optional module is absent.
-keepclassmembers @com.onesignal.core.internal.minification.KeepStub class * { <init>(...); }

-keep class ** implements com.onesignal.common.modules.IModule { *; }
14 changes: 12 additions & 2 deletions OneSignalSDK/onesignal/in-app-messages/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
-dontwarn com.onesignal.iam.**
# In-app-messages service implementations are instantiated via reflective constructor selection
# (ServiceRegistrationReflection). Keep only their constructors.
-keepclassmembers class com.onesignal.inAppMessages.** {
<init>(...);
}

-keepclassmembers class com.onesignal.inAppMessages.** { *; }
# IAM display Position is resolved via Position.valueOf(serverString) (WebViewManager) and the
# IllegalArgumentException on a renamed constant is uncaught. Keep enum constant names.
-keepclassmembers enum com.onesignal.inAppMessages.** {
<fields>;
public static **[] values();
public static ** valueOf(java.lang.String);
}
14 changes: 12 additions & 2 deletions OneSignalSDK/onesignal/location/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
-dontwarn com.onesignal.location.**
# Location service implementations are instantiated via reflective constructor selection
# (ServiceRegistrationReflection). Keep only their constructors.
-keepclassmembers class com.onesignal.location.** {
<init>(...);
}

-keepclassmembers class com.onesignal.location.** { *; }
# GoogleApiClientCompatProxy invokes these GMS methods reflectively by string name (for
# compatibility across Google Play services versions), so their names must be preserved.
-keepclassmembers class com.google.android.gms.common.api.GoogleApiClient {
com.google.android.gms.common.ConnectionResult blockingConnect();
void connect();
void disconnect();
}
80 changes: 29 additions & 51 deletions OneSignalSDK/onesignal/notifications/consumer-rules.pro
Original file line number Diff line number Diff line change
@@ -1,72 +1,50 @@
-dontwarn com.onesignal.notification.**

# These 2 methods are called with reflection.
-keep class com.google.android.gms.common.api.GoogleApiClient {
void connect();
void disconnect();
}

# Need to keep as these 2 methods are called with reflection from com.onesignal.PushRegistratorFCM
-keep class com.google.firebase.iid.FirebaseInstanceId {
static com.google.firebase.iid.FirebaseInstanceId getInstance(com.google.firebase.FirebaseApp);
java.lang.String getToken(java.lang.String, java.lang.String);
}

-keep class ** implements com.onesignal.notifications.IPermissionObserver{
void onNotificationPermissionChange(java.lang.Boolean);
}

-keep class ** implements com.onesignal.user.subscriptions.IPushSubscriptionObserver {
void onPushSubscriptionChange(com.onesignal.user.subscriptions.PushSubscriptionChangedState);
}

-keep class ** implements com.onesignal.user.state.IUserStateObserver {
void onUserStateChange(com.onesignal.user.state.UserChangedState);
}

-keep class ** implements com.onesignal.notifications.INotificationServiceExtension{
# The notification service extension class is named in the app's AndroidManifest <meta-data> and is
# reflectively instantiated (NotificationLifecycleService -> Class.forName(name).newInstance()), so the
# class name and its no-arg constructor must be preserved.
-keep class ** implements com.onesignal.notifications.INotificationServiceExtension {
<init>();
void onNotificationReceived(com.onesignal.notifications.INotificationReceivedEvent);
}

-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.AdwHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.ApexHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.AsusHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.DefaultBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.EverythingMeHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.HuaweiHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.LGHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.NewHtcHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.NovaHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.OPPOHomeBader { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.SamsungHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.SonyHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.VivoHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.XiaomiHomeBadger { <init>(...); }
-keep class com.onesignal.notifications.internal.badges.impl.shortcutbadger.impl.ZukHomeBadger { <init>(...); }
# Home-screen badgers are instantiated via newInstance() from a list of class literals
# (ShortcutBadger.initBadger), so each implementation needs its constructor kept.
-keep class * implements com.onesignal.notifications.internal.badges.impl.shortcutbadger.Badger {
<init>(...);
}

# Optional push providers are compileOnly in the SDK and referenced directly by the
# provider-specific registrators. A consuming app only bundles the provider(s) it targets, so the
# others are absent at R8 time: Huawei-only apps exclude com.google.firebase:firebase-messaging
# (removing com.google.firebase.**, referenced from PushRegistratorFCM), just as GMS apps omit HMS
# (com.huawei.**) and ADM (com.amazon.**). Suppress the missing-class errors for all three.
-dontwarn com.huawei.**
-dontwarn com.amazon.**
-dontwarn com.google.firebase.**

# Proguard ends up removing this class even if it is used in AndroidManifest.xml so force keeping it.
-keep public class com.onesignal.notifications.services.ADMMessageHandler {*;}
# ADM handlers are instantiated by name from the app manifest AND their on* lifecycle callbacks
# (onMessage/onRegistered/onRegistrationError/onUnregistered) are invoked by the ADM framework, not
# the SDK, so keep both constructors and those methods. (Amazon-device-only path, untestable in CI.)
-keep public class com.onesignal.notifications.services.ADMMessageHandler { <init>(...); void on*(...); }
-keep public class com.onesignal.notifications.services.ADMMessageHandlerJob { <init>(...); void on*(...); }

-keep public class com.onesignal.notifications.services.ADMMessageHandlerJob {*;}
# Legacy v4 job shim is referenced by name (older scheduled jobs); keep its constructors.
-keep class com.onesignal.JobIntentService$* { <init>(...); }

-keep class com.onesignal.JobIntentService$* {*;}

-keepclassmembers class com.onesignal.notifications.** { *; }
# Notification service implementations are instantiated via reflective constructor selection
# (ServiceRegistrationReflection). Keep only their constructors.
-keepclassmembers class com.onesignal.notifications.** {
<init>(...);
}

# Keep OneSignal WorkManager workers and constructors used for runtime instantiation.
# WorkManager instantiates workers via reflection using the (Context, WorkerParameters) constructor.
-keep class com.onesignal.notifications.internal.** extends androidx.work.ListenableWorker {
public <init>(android.content.Context, androidx.work.WorkerParameters);
}

# WorkManager instantiates InputMerger classes via reflection (InputMerger.fromClassName).
# R8 full mode (AGP 8+) strips no-arg constructors, causing:
# java.lang.NoSuchMethodException: androidx.work.OverwritingInputMerger.<init>()
# WM-WorkerWrapper E Could not create Input Merger androidx.work.OverwritingInputMerger
# Keep all InputMerger subclasses (OverwritingInputMerger, ArrayCreatingInputMerger, etc.)
-keep class * extends androidx.work.InputMerger {
public <init>();
}

5 changes: 4 additions & 1 deletion examples/demo/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ android {
signingConfig = signingConfigs.getByName("debug")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
"proguard-rules.pro",
// Emits R8 seeds/config so CI can diff kept consumer members against a baseline
// and catch a reflectively-referenced keep being silently dropped.
"proguard-r8-report.pro"
)
}
debug {
Expand Down
15 changes: 15 additions & 0 deletions examples/demo/app/proguard-r8-report.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# R8 keep-rules reporting for the consumer keep-rules diff gate.
#
# Why: the OneSignal library modules ship consumer keep rules (consumer-rules.pro) that protect
# members reached only via string-based reflection (e.g. GoogleApiClient.connect/disconnect,
# Model getters, service constructors). Those breakages are invisible at assemble time because R8
# only errors on missing classes, not on renamed/removed methods reached by reflection string.
# Emitting the seeds (members R8 kept) lets CI diff them against a baseline and fail when a
# previously-protected member silently stops being kept.
#
# -printseeds lists exactly which classes/members survived shrinking (the primary diff signal).
# -printconfiguration writes the fully-merged R8 configuration (human-readable secondary signal).
# Paths are relative to the module dir; both land under build/ (gitignored) and are consumed by
# scripts/r8-keep-check.sh after the minified release build.
-printseeds build/outputs/r8-report/seeds.txt
-printconfiguration build/outputs/r8-report/configuration.txt
Loading
Loading