1

I have an Android Wear OS emergency app, which monitors the user and looks out for emergency events. When the app is closed, I use a foreground service, to keep monitoring in the background. This works fine and the app registers the event and both vibrates and makes a ping sound. When the app registers the emergency event, even if the app is in the background and the screen is black, I want the app to do one of the following:

  1. Preferably: Open the apps main activity where the user can take action
  2. Alternatively: Wake up the screen and show a notification, so the user knows the event has been identified

I have tried a bunch of different approaches and I'm aware this is difficult on purpose, with the hardening of restrictions. But surely it is possible.

My first approach is this:

private fun openApp() {
    AppLogger.d(TAG, "Open App Intent")

    val notificationManager = this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager

    val saveRequestOptionsBundle: Bundle? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
        val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
        } else {
            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
        }

        ActivityOptions.makeBasic().apply {
            pendingIntentCreatorBackgroundActivityStartMode = mode
        }.toBundle()
    } else {
        null
    }

    val openAppIntent = Intent(this, MainActivity::class.java)
    val openAppPendingIntent = PendingIntent.getActivity(
        this,
        1,
        openAppIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
        saveRequestOptionsBundle
    )

    val notification = NotificationCompat.Builder(this, getString(R.string.emergency_notification_channel_id))
        .setContentTitle(getString(R.string.emergency_notification_title))
        .setFullScreenIntent(openAppPendingIntent, true)
        .setContentText(getString(R.string.event))
        .setCategory(NotificationCompat.CATEGORY_ALARM)
        .setPriority(NotificationCompat.PRIORITY_MAX)
        .setDefaults(NotificationCompat.DEFAULT_ALL)
        .setContentIntent(openAppPendingIntent)
        .setSmallIcon(R.drawable.ic_app_logo)
        .setAutoCancel(false)
        .build()

    notificationManager.notify(11, notification)
}

My second approach was this:

private fun openApp1() {
    Log.d(TAG, "Open App Intent")

    val intent = Intent(this, MainActivity::class.java).apply {
        addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TOP)
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { // API 36+
        val activityOptions = ActivityOptions.makeBasic().apply {
            pendingIntentCreatorBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
        }

        val launchActivityPendingIntent: PendingIntent = PendingIntent.getActivity(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
            activityOptions.toBundle()
        )

        try {
            launchActivityPendingIntent.send()
        } catch (e: PendingIntent.CanceledException) {
            AppLogger.e(TAG, "PendingIntent was cancelled", e)
        }

    } else {
        startActivity(intent)
    }
}

Both approaches gave me the error:

Background activity launch blocked! [callingPackage: com.mypackage; callingPackageTargetSdk: 36; callingUid: 10251; callingPid: -1; appSwitchState: 1; callingUidHasAnyVisibleWindow: false; callingUidProcState: FOREGROUND_SERVICE; isCallingUidPersistentSystemProcess: false; forcedBalByPiSender: BackgroundStartPrivileges[allowsBackgroundActivityStarts=true, allowsBackgroundForegroundServiceStarts=true, originatingToken=android.os.Binder@8]; intent: Intent { cmp=com.mypackage.MainActivity }; callerApp: null; balAllowedByPiCreator: BSP.ALLOW_BAL; balAllowedByPiCreatorWithHardening: BSP.ALLOW_BAL; resultIfPiCreatorAllowsBal: BAL_BLOCK; hasRealCaller: true; isCallForResult: false; isPendingIntent: true; autoOptInReason: null; realCallingPackage: com.samsung.android.wearable.sysui; realCallingPackageTargetSdk: 33; realCallingUid: 10; realCallingPid: 1; realCallingUidHasAnyVisibleWindow: false; realCallingUidProcState: BOUND_FOREGROUND_SERVICE; isRealCallingUidPersistentSystemProcess: false; originatingPendingIntent: PendingIntentRecord{13ca919 com.mypackage startActivity (allowlist: 87:+30s0ms/0/NOTIFICATION_SERVICE/NotificationManagerService)}; realCallerApp: ProcessRecord{55c 12:com.samsung.android.wearable.sysui/u0}; realInVisibleTask: false; balAllowedByPiSender: BSP.ALLOW_BAL; resultIfPiSenderAllowsBal: BAL_BLOCK]

I have all of the permissions:

  • USE_FULL_SCREEN_INTENT
  • SYSTEM_ALERT_WINDOW
  • POST_NOTIFICATIONS

My mainactivity in manifest has these:

  • android:launchMode="singleTask"
  • android:turnScreenOn="true"
  • android:showWhenLocked="true"

And my notification channel looks like this:

// Emergency Channel
    String emergency_channel_id = getString(R.string.emergency_notification_channel_id);
    CharSequence emergency_name = getString(R.string.emergency_notification_channel_name);
    String emergency_description = getString(R.string.emergency_notification_channel_description);

    NotificationChannel emergency_channel = new NotificationChannel(
            emergency_channel_id,
            emergency_name,
            NotificationManager.IMPORTANCE_HIGH
    );
    emergency_channel.setDescription(emergency_description);
    emergency_channel.enableLights(true);
    emergency_channel.setLightColor(Color.RED);
    emergency_channel.enableVibration(true);
    emergency_channel.setVibrationPattern(new long[]{0, 500, 200, 500, 200, 500});
    emergency_channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
    emergency_channel.setBypassDnd(true);
    emergency_channel.setShowBadge(true);
    AudioAttributes audioAttributes = new AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
            .setUsage(AudioAttributes.USAGE_ALARM)
            .build();
    emergency_channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM), audioAttributes);
    notificationManager.createNotificationChannel(emergency_channel);

I've gone through a lot of different StackOverflow questions and official Android update docs, but nothing seems to get past this BAL_BLOCK.

Have anybody figured it out or know the best practice for this?

1 Answer 1

0
val launchIntent = Intent(applicationContext, MainActivity::class.java)
launchIntent
     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(launchIntent)

i have only this in my service and it works. i have watches from api 34 to 36. let me know if you have issues. maybe remove all the other stuff from your code ;-)

if i remember correctly

SYSTEM_ALERT_WINDOW

is the crucial permission here.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.