Skip to content

Make the Sessions test app more convenient to test #7054

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 20, 2025
Merged
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
16 changes: 14 additions & 2 deletions firebase-sessions/test-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@

<!-- Override value to 1.0 to always have a fireperf verbose session -->
<meta-data
android:name="sessions_sampling_percentage"
android:value="0.01" />
android:name="sessions_sampling_percentage"
android:value="0.01" />

<meta-data
android:name="firebase_performance_logcat_enabled"
Expand Down Expand Up @@ -74,6 +74,18 @@
android:resource="@xml/homescreen_widget" />
</receiver>

<service
android:exported="true"
android:name=".MyServiceA"
android:process=":a"
tools:ignore="ExportedService" />

<service
android:exported="true"
android:name=".MyServiceB"
android:process=":b"
tools:ignore="ExportedService" />

<service
android:enabled="true"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,48 +18,47 @@ package com.google.firebase.testing.sessions

import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo
import android.app.Application
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.FirebaseApp
import com.google.firebase.perf.FirebasePerformance
import com.google.firebase.testing.sessions.TestApplication.Companion.TAG
import com.google.firebase.testing.sessions.TestApplication.Companion.myProcessName

open class BaseActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
FirebaseApp.initializeApp(this)
setProcessAttribute()
logProcessDetails()
logFirebaseDetails()
Log.i(TAG, "onCreate - ${getProcessName()} - ${getImportance()}")
Log.i(TAG, "onCreate - $myProcessName - ${getImportance()}")
}

override fun onPause() {
super.onPause()
Log.i(TAG, "onPause - ${getProcessName()} - ${getImportance()}")
Log.i(TAG, "onPause - $myProcessName - ${getImportance()}")
}

override fun onStop() {
super.onStop()
Log.i(TAG, "onStop - ${getProcessName()} - ${getImportance()}")
Log.i(TAG, "onStop - $myProcessName - ${getImportance()}")
}

override fun onResume() {
super.onResume()
Log.i(TAG, "onResume - ${getProcessName()} - ${getImportance()}")
Log.i(TAG, "onResume - $myProcessName - ${getImportance()}")
}

override fun onStart() {
super.onStart()
Log.i(TAG, "onStart - ${getProcessName()} - ${getImportance()}")
Log.i(TAG, "onStart - $myProcessName - ${getImportance()}")
}

override fun onDestroy() {
super.onDestroy()
Log.i(TAG, "onDestroy - ${getProcessName()} - ${getImportance()}")
Log.i(TAG, "onDestroy - $myProcessName - ${getImportance()}")
}

private fun getImportance(): Int {
Expand All @@ -68,15 +67,11 @@ open class BaseActivity : AppCompatActivity() {
return processInfo.importance
}

protected fun getProcessName(): String =
if (Build.VERSION.SDK_INT >= 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() {
Expand All @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() {

Expand All @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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() {
Expand All @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -104,18 +105,16 @@ 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),
)
}

fun stopService(context: Context) {
Log.i(TAG, "Stopping serice")
Log.i(TAG, "Stopping service")
context.stopService(Intent(context, ForegroundService::class.java))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -56,7 +57,7 @@ class SecondActivity : BaseActivity() {
.killBackgroundProcesses("com.google.firebase.testing.sessions")
}
}
findViewById<TextView>(R.id.process_name_second).text = getProcessName()
findViewById<TextView>(R.id.process_name_second).text = myProcessName
}

override fun onResume() {
Expand Down
Loading
Loading