Skip to content

Commit 1db5fe6

Browse files
abdulraqeeb33AR Abdul Azeezcursoragent
authored
fix: [SDK-5167] split FID census into flag= and reg= (#2733)
Co-authored-by: AR Abdul Azeez <abdul@onesignal.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent a8bafc0 commit 1db5fe6

5 files changed

Lines changed: 81 additions & 6 deletions

File tree

OneSignalSDK/onesignal/core/consumer-rules.pro

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,12 @@
3737

3838
# Fallback "misconfigured" managers are reflectively constructed when an optional module is absent.
3939
-keepclassmembers @com.onesignal.core.internal.minification.KeepStub class * { <init>(...); }
40+
41+
# FidEnv looks these up by name. Core has no compile-time call, so R8 would strip them.
42+
-keepclassmembers class com.google.firebase.messaging.FirebaseMessaging {
43+
*** register();
44+
}
45+
-keepclassmembers class com.google.firebase.FirebaseApp {
46+
*** getApps(android.content.Context);
47+
*** getName();
48+
}

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/device/impl/FidEnv.kt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal data class FidEnvSnapshot(
1818
val googleServices: Boolean,
1919
val agpVersion: String?,
2020
val fidFlag: Boolean,
21+
val fidRegisterApi: Boolean,
2122
val defaultFirebaseApp: Boolean,
2223
val firebaseInitProvider: Boolean,
2324
val minSdk: Int?,
@@ -29,6 +30,7 @@ internal data class FidEnvSnapshot(
2930
"gs=${googleServices.toBit()}",
3031
"agp=${agpVersion.sanitized()}",
3132
"flag=${fidFlag.toBit()}",
33+
"reg=${fidRegisterApi.toBit()}",
3234
"def=${defaultFirebaseApp.toBit()}",
3335
"prov=${firebaseInitProvider.toBit()}",
3436
"min=${minSdk?.toString() ?: "-"}",
@@ -69,6 +71,16 @@ internal fun dashboardSenderForCensus(
6971
): String? =
7072
if (hydrated && senderAppId == appId) sender else null
7173

74+
@Suppress("TooGenericExceptionCaught")
75+
internal fun hasPublicNoArgMethod(className: String, methodName: String): Boolean {
76+
return try {
77+
Class.forName(className).getMethod(methodName)
78+
true
79+
} catch (_: Throwable) {
80+
false
81+
}
82+
}
83+
7284
internal class AndroidFidEnvReader(
7385
private val context: Context,
7486
) {
@@ -89,7 +101,8 @@ internal class AndroidFidEnvReader(
89101
return FidEnvSnapshot(
90102
googleServices = !googleAppId.isNullOrBlank(),
91103
agpVersion = readApkEntry(AGP_METADATA_PATH)?.let { parseAgpVersion(it) },
92-
fidFlag = AndroidUtils.getManifestMetaBoolean(context, FID_FLAG),
104+
fidFlag = fidFlagFromMetadata(),
105+
fidRegisterApi = hasFirebaseMessagingRegisterApi(),
93106
defaultFirebaseApp = false,
94107
firebaseInitProvider = hasFirebaseInitProvider(),
95108
minSdk = minSdk(),
@@ -127,6 +140,24 @@ internal class AndroidFidEnvReader(
127140
}
128141
}
129142

143+
// 25.1+ exposes register(); older libraries ignore the metadata and keep getToken().
144+
private fun hasFirebaseMessagingRegisterApi(): Boolean =
145+
hasPublicNoArgMethod(FIREBASE_MESSAGING, "register")
146+
147+
@Suppress("TooGenericExceptionCaught")
148+
private fun fidFlagFromMetadata(): Boolean {
149+
return try {
150+
val meta =
151+
context.packageManager.getApplicationInfo(
152+
context.packageName,
153+
PackageManager.GET_META_DATA,
154+
).metaData ?: return false
155+
meta.containsKey(FID_FLAG) && meta.getBoolean(FID_FLAG)
156+
} catch (_: Throwable) {
157+
false
158+
}
159+
}
160+
130161
@Suppress("TooGenericExceptionCaught", "UNCHECKED_CAST")
131162
private fun hasDefaultFirebaseApp(): Boolean {
132163
return try {
@@ -170,6 +201,7 @@ internal class AndroidFidEnvReader(
170201
private const val FIREBASE_INIT_PROVIDER = "com.google.firebase.provider.FirebaseInitProvider"
171202
private const val AGP_METADATA_PATH = "META-INF/com/android/build/gradle/app-metadata.properties"
172203
private const val FIREBASE_APP = "com.google.firebase.FirebaseApp"
204+
private const val FIREBASE_MESSAGING = "com.google.firebase.messaging.FirebaseMessaging"
173205
private const val DEFAULT_APP_NAME = "[DEFAULT]"
174206
}
175207
}

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/device/FidEnvReaderTests.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.onesignal.core.internal.device
22

33
import android.content.Context
44
import android.os.Build
5+
import android.os.Bundle
56
import androidx.test.core.app.ApplicationProvider
67
import br.com.colman.kotest.android.extensions.robolectric.RobolectricTest
78
import com.onesignal.common.AndroidUtils
@@ -34,6 +35,7 @@ class FidEnvReaderTests : FunSpec({
3435

3536
snapshot.googleServices shouldBe false
3637
snapshot.fidFlag shouldBe false
38+
snapshot.fidRegisterApi shouldBe false
3739
snapshot.defaultFirebaseApp shouldBe false
3840
snapshot.firebaseInitProvider shouldBe false
3941
snapshot.senderMatch shouldBe null
@@ -42,11 +44,20 @@ class FidEnvReaderTests : FunSpec({
4244
snapshot.agpVersion shouldBe null
4345
}
4446

47+
test("manifest flag and register API are reported independently") {
48+
val info = context.applicationInfo
49+
if (info.metaData == null) info.metaData = Bundle()
50+
info.metaData.putBoolean("firebase_messaging_installation_id_enabled", true)
51+
52+
val snapshot = AndroidFidEnvReader(context).collect(null)
53+
snapshot.fidFlag shouldBe true
54+
snapshot.fidRegisterApi shouldBe false
55+
}
56+
4557
test("gs and sender match follow google-services string resources") {
4658
mockkObject(AndroidUtils)
4759
every { AndroidUtils.getResourceString(context, "google_app_id", null) } returns "1:388536902528:android:abc"
4860
every { AndroidUtils.getResourceString(context, "gcm_defaultSenderId", null) } returns "388536902528"
49-
every { AndroidUtils.getManifestMetaBoolean(context, any()) } returns false
5061

5162
val snapshot = AndroidFidEnvReader(context).collect("388536902528")
5263

@@ -58,7 +69,6 @@ class FidEnvReaderTests : FunSpec({
5869
mockkObject(AndroidUtils)
5970
every { AndroidUtils.getResourceString(context, "google_app_id", null) } returns "1:1:android:abc"
6071
every { AndroidUtils.getResourceString(context, "gcm_defaultSenderId", null) } returns "111"
61-
every { AndroidUtils.getManifestMetaBoolean(context, any()) } returns false
6272

6373
val snapshot = AndroidFidEnvReader(context).collect("999")
6474

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/device/FidEnvTests.kt

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.onesignal.core.internal.device
22

33
import com.onesignal.core.internal.device.impl.FidEnvSnapshot
44
import com.onesignal.core.internal.device.impl.dashboardSenderForCensus
5+
import com.onesignal.core.internal.device.impl.hasPublicNoArgMethod
56
import com.onesignal.core.internal.device.impl.parseAgpVersion
67
import com.onesignal.core.internal.device.impl.sanitizeToken
78
import com.onesignal.core.internal.device.impl.senderMatch
@@ -15,14 +16,15 @@ class FidEnvTests : FunSpec({
1516
googleServices = true,
1617
agpVersion = "8.8.2",
1718
fidFlag = false,
19+
fidRegisterApi = true,
1820
defaultFirebaseApp = true,
1921
firebaseInitProvider = true,
2022
minSdk = 24,
2123
targetSdk = 35,
2224
senderMatch = true,
2325
).toHeaderValue()
2426

25-
header shouldBe "gs=1;agp=8.8.2;flag=0;def=1;prov=1;min=24;tgt=35;snd=1"
27+
header shouldBe "gs=1;agp=8.8.2;flag=0;reg=1;def=1;prov=1;min=24;tgt=35;snd=1"
2628
}
2729

2830
test("header uses dashes for unknown optional fields") {
@@ -31,14 +33,15 @@ class FidEnvTests : FunSpec({
3133
googleServices = false,
3234
agpVersion = null,
3335
fidFlag = false,
36+
fidRegisterApi = false,
3437
defaultFirebaseApp = false,
3538
firebaseInitProvider = false,
3639
minSdk = null,
3740
targetSdk = null,
3841
senderMatch = null,
3942
).toHeaderValue()
4043

41-
header shouldBe "gs=0;agp=-;flag=0;def=0;prov=0;min=-;tgt=-;snd=-"
44+
header shouldBe "gs=0;agp=-;flag=0;reg=0;def=0;prov=0;min=-;tgt=-;snd=-"
4245
}
4346

4447
test("sanitizeToken strips header-unsafe characters and caps length") {
@@ -96,4 +99,25 @@ class FidEnvTests : FunSpec({
9699
sender = "111",
97100
) shouldBe "111"
98101
}
102+
103+
test("hasPublicNoArgMethod is true when the class exposes the method") {
104+
hasPublicNoArgMethod(DummyWithRegister::class.java.name, "register") shouldBe true
105+
}
106+
107+
test("hasPublicNoArgMethod is false when the class or method is missing") {
108+
hasPublicNoArgMethod(DummyWithoutRegister::class.java.name, "register") shouldBe false
109+
hasPublicNoArgMethod(DummyWithRegisterArg::class.java.name, "register") shouldBe false
110+
hasPublicNoArgMethod("com.onesignal.missing.FirebaseMessaging", "register") shouldBe false
111+
}
99112
})
113+
114+
private class DummyWithRegister {
115+
fun register() {}
116+
}
117+
118+
private class DummyWithRegisterArg {
119+
@Suppress("UNUSED_PARAMETER")
120+
fun register(unused: String) {}
121+
}
122+
123+
private class DummyWithoutRegister

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/http/HttpClientTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,6 @@ internal class FakeFidEnv(
320320
override fun headerValue(): String = value
321321

322322
companion object {
323-
const val VALUE = "gs=0;agp=8.8.2;flag=0;def=0;prov=0;min=21;tgt=34;snd=-"
323+
const val VALUE = "gs=0;agp=8.8.2;flag=0;reg=0;def=0;prov=0;min=21;tgt=34;snd=-"
324324
}
325325
}

0 commit comments

Comments
 (0)