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)