diff --git a/firebase-sessions/test-app/src/main/AndroidManifest.xml b/firebase-sessions/test-app/src/main/AndroidManifest.xml
index 9965842a01e..8d2011ca18e 100644
--- a/firebase-sessions/test-app/src/main/AndroidManifest.xml
+++ b/firebase-sessions/test-app/src/main/AndroidManifest.xml
@@ -45,8 +45,8 @@
+ android:name="sessions_sampling_percentage"
+ android:value="0.01" />
+
+
+
+
= Build.VERSION_CODES.P) Application.getProcessName() else "unknown"
-
private fun logProcessDetails() {
val pid = android.os.Process.myPid()
val uid = android.os.Process.myUid()
val activity = javaClass.name
- val process = getProcessName()
- Log.i(TAG, "activity: $activity process: $process, pid: $pid, uid: $uid")
+ Log.i(TAG, "activity: $activity process: $myProcessName, pid: $pid, uid: $uid")
}
private fun logFirebaseDetails() {
@@ -85,15 +80,11 @@ open class BaseActivity : AppCompatActivity() {
val defaultFirebaseApp = FirebaseApp.getInstance()
Log.i(
TAG,
- "activity: $activity firebase: ${defaultFirebaseApp.name} appsCount: ${firebaseApps.count()}"
+ "activity: $activity firebase: ${defaultFirebaseApp.name} appsCount: ${firebaseApps.count()}",
)
}
private fun setProcessAttribute() {
- FirebasePerformance.getInstance().putAttribute("process_name", getProcessName())
- }
-
- companion object {
- val TAG = "BaseActivity"
+ FirebasePerformance.getInstance().putAttribute("process_name", myProcessName)
}
}
diff --git a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/CrashBroadcastReceiver.kt b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/CrashBroadcastReceiver.kt
index 89d2f03f1ce..203ab0416d1 100644
--- a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/CrashBroadcastReceiver.kt
+++ b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/CrashBroadcastReceiver.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast
+import com.google.firebase.testing.sessions.TestApplication.Companion.TAG
class CrashBroadcastReceiver : BroadcastReceiver() {
@@ -42,7 +43,6 @@ class CrashBroadcastReceiver : BroadcastReceiver() {
}
companion object {
- val TAG = "CrashBroadcastReceiver"
val CRASH_ACTION = "com.google.firebase.testing.sessions.CrashBroadcastReceiver.CRASH_ACTION"
val TOAST_ACTION = "com.google.firebase.testing.sessions.CrashBroadcastReceiver.TOAST_ACTION"
}
diff --git a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/FirstFragment.kt b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/FirstFragment.kt
index c151b56a68f..ebe54414661 100644
--- a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/FirstFragment.kt
+++ b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/FirstFragment.kt
@@ -16,7 +16,6 @@
package com.google.firebase.testing.sessions
-import android.app.Application
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
@@ -31,6 +30,7 @@ import androidx.lifecycle.lifecycleScope
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.perf.FirebasePerformance
import com.google.firebase.perf.trace
+import com.google.firebase.testing.sessions.TestApplication.Companion.myProcessName
import com.google.firebase.testing.sessions.databinding.FragmentFirstBinding
import java.net.HttpURLConnection
import java.net.URL
@@ -129,7 +129,7 @@ class FirstFragment : Fragment() {
intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
- binding.processName.text = getProcessName()
+ binding.processName.text = myProcessName
}
override fun onResume() {
@@ -152,9 +152,5 @@ class FirstFragment : Fragment() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(Date())
else "unknown"
-
- fun getProcessName(): String =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) Application.getProcessName()
- else "unknown"
}
}
diff --git a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/ForegroundService.kt b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/ForegroundService.kt
index f616a0a54a4..a17511c4740 100644
--- a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/ForegroundService.kt
+++ b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/ForegroundService.kt
@@ -29,6 +29,7 @@ import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.google.firebase.FirebaseApp
+import com.google.firebase.testing.sessions.TestApplication.Companion.TAG
class ForegroundService : Service() {
private val CHANNEL_ID = "CrashForegroundService"
@@ -104,10 +105,8 @@ class ForegroundService : Service() {
}
companion object {
- val TAG = "WidgetForegroundService"
-
fun startService(context: Context, message: String) {
- Log.i(TAG, "Starting foreground serice")
+ Log.i(TAG, "Starting foreground service")
ContextCompat.startForegroundService(
context,
Intent(context, ForegroundService::class.java).putExtra("inputExtra", message),
@@ -115,7 +114,7 @@ class ForegroundService : Service() {
}
fun stopService(context: Context) {
- Log.i(TAG, "Stopping serice")
+ Log.i(TAG, "Stopping service")
context.stopService(Intent(context, ForegroundService::class.java))
}
}
diff --git a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/MyServiceA.kt b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/MyServiceA.kt
new file mode 100644
index 00000000000..b35c853bfaf
--- /dev/null
+++ b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/MyServiceA.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.firebase.testing.sessions
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import com.google.firebase.testing.sessions.TestApplication.Companion.TAG
+import com.google.firebase.testing.sessions.TestApplication.Companion.myProcessName
+import kotlin.system.exitProcess
+
+class MyServiceA : Service() {
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.i(TAG, "Service A action: ${intent?.action} on process: $myProcessName")
+
+ // Send actions from adb shell this way, so it can start the process if needed:
+ // am startservice -n com.google.firebase.testing.sessions/.MyServiceA -a PING
+ when (intent?.action) {
+ "PING" -> ping()
+ "CRASH" -> crash()
+ "KILL" -> kill()
+ "SESSION" -> session()
+ }
+
+ return START_STICKY
+ }
+
+ private fun ping() {
+ repeat(7) { Log.i(TAG, "*** pong ***") }
+ }
+
+ private fun crash() {
+ Log.i(TAG, "crashing")
+ throw IndexOutOfBoundsException("crash service a")
+ }
+
+ private fun kill() {
+ Log.i(TAG, "killing process $myProcessName")
+ exitProcess(0)
+ }
+
+ private fun session() {
+ Log.i(
+ TAG,
+ "service a, session id: ${TestApplication.sessionSubscriber.sessionDetails?.sessionId}",
+ )
+ }
+
+ override fun onBind(intent: Intent?): IBinder? = null
+}
diff --git a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/MyServiceB.kt b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/MyServiceB.kt
new file mode 100644
index 00000000000..cb9791796b0
--- /dev/null
+++ b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/MyServiceB.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.firebase.testing.sessions
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import com.google.firebase.testing.sessions.TestApplication.Companion.TAG
+import com.google.firebase.testing.sessions.TestApplication.Companion.myProcessName
+import kotlin.system.exitProcess
+
+class MyServiceB : Service() {
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.i(TAG, "Service B action: ${intent?.action} on process: $myProcessName")
+
+ when (intent?.action) {
+ "PING" -> ping()
+ "CRASH" -> crash()
+ "KILL" -> kill()
+ }
+
+ return START_STICKY
+ }
+
+ private fun ping() {
+ repeat(7) { Log.i(TAG, "*** hello ***") }
+ Log.i(TAG, "session id: ${TestApplication.sessionSubscriber.sessionDetails?.sessionId}")
+ }
+
+ private fun crash() {
+ Log.i(TAG, "crashing")
+ throw IllegalStateException("crash in service b")
+ }
+
+ private fun kill() {
+ Log.i(TAG, "killing process $myProcessName")
+ exitProcess(0)
+ }
+
+ override fun onBind(intent: Intent?): IBinder? = null
+}
diff --git a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/SecondActivity.kt b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/SecondActivity.kt
index 6c2fd3c06b0..434ff1dec08 100644
--- a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/SecondActivity.kt
+++ b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/SecondActivity.kt
@@ -25,6 +25,7 @@ import android.widget.Button
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import com.google.firebase.perf.FirebasePerformance
+import com.google.firebase.testing.sessions.TestApplication.Companion.myProcessName
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -56,7 +57,7 @@ class SecondActivity : BaseActivity() {
.killBackgroundProcesses("com.google.firebase.testing.sessions")
}
}
- findViewById(R.id.process_name_second).text = getProcessName()
+ findViewById(R.id.process_name_second).text = myProcessName
}
override fun onResume() {
diff --git a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/TestApplication.kt b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/TestApplication.kt
index 10a95261fa8..f8b8dec2cc7 100644
--- a/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/TestApplication.kt
+++ b/firebase-sessions/test-app/src/main/kotlin/com/google/firebase/testing/sessions/TestApplication.kt
@@ -17,21 +17,29 @@
package com.google.firebase.testing.sessions
import android.annotation.SuppressLint
+import android.app.Application
import android.content.IntentFilter
import android.os.Build
import android.os.Handler
import android.os.Looper
+import android.util.Log
import android.widget.TextView
import androidx.multidex.MultiDexApplication
+import com.google.firebase.FirebaseApp
import com.google.firebase.sessions.api.FirebaseSessionsDependencies
import com.google.firebase.sessions.api.SessionSubscriber
+import java.io.File
class TestApplication : MultiDexApplication() {
private val broadcastReceiver = CrashBroadcastReceiver()
+ @SuppressLint("UnspecifiedRegisterReceiverFlag")
override fun onCreate() {
super.onCreate()
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Log.i(TAG, "TestApplication created on process: $myProcessName")
+ FirebaseApp.initializeApp(this)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(
broadcastReceiver,
IntentFilter(CrashBroadcastReceiver.CRASH_ACTION),
@@ -48,6 +56,7 @@ class TestApplication : MultiDexApplication() {
}
}
+ @SuppressLint("DiscouragedApi")
class FakeSessionSubscriber : SessionSubscriber {
override val isDataCollectionEnabled = true
override val sessionSubscriberName = SessionSubscriber.Name.MATT_SAYS_HI
@@ -78,8 +87,19 @@ class TestApplication : MultiDexApplication() {
@SuppressLint("DiscouragedApi")
companion object {
+ const val TAG = "SessionsTestApp"
+
val sessionSubscriber = FakeSessionSubscriber()
+ val myProcessName: String =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) Application.getProcessName()
+ else
+ try {
+ File("/proc/self/cmdline").readText().substringBefore('\u0000').trim()
+ } catch (_: Exception) {
+ null
+ } ?: "unknown"
+
init {
FirebaseSessionsDependencies.addDependency(SessionSubscriber.Name.MATT_SAYS_HI)
FirebaseSessionsDependencies.register(sessionSubscriber)