GithubHelp home page GithubHelp logo

Comments (12)

davidgyoung avatar davidgyoung commented on June 11, 2024 1

@rahul-kumawat-vts can you please share two things:

  1. Any entry in your AndroidManifest.xml that refers to "BeaconService". Below is what it might look like:
        <service 
            android:name=".service.BeaconService"
            android:enabled="true"
            android:exported="false"
            android:isolatedProcess="false"
            android:foregroundServiceType="location"
            android:label="beacon"            
            />
  1. The code that configures the foreground service and starts ranging or monitoring. Below is what the official Kotlin reference app does to set this up to give you a clue what we are looking to see. The key part is the code that calls enableForegroundServiceScanning and then startRangingBeacons or startMonitoring.
        val builder = Notification.Builder(this, "BeaconReferenceApp")
        builder.setSmallIcon(R.drawable.ic_launcher_background)
        builder.setContentTitle("Scanning for Beacons")
        val intent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(
                this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT + PendingIntent.FLAG_IMMUTABLE
        )
        builder.setContentIntent(pendingIntent);
        val channel =  NotificationChannel("beacon-ref-notification-id",
            "My Notification Name", NotificationManager.IMPORTANCE_DEFAULT)
        channel.setDescription("My Notification Channel Description")
        val notificationManager =  getSystemService(
                Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel);
        builder.setChannelId(channel.getId());
        Log.d(TAG, "Calling enableForegroundServiceScanning")
        BeaconManager.getInstanceForApplication(this).enableForegroundServiceScanning(builder.build(), 456);
        Log.d(TAG, "Back from  enableForegroundServiceScanning")

        beaconManager.startMonitoring(region)
        beaconManager.startRangingBeacons(region)

from android-beacon-library.

davidgyoung avatar davidgyoung commented on June 11, 2024

Thanks for this report.

We added the code below to prevent that in #1162 released in October in 2.20.1, but it looks like it will not work in your case:

            try {
                this.startForeground(notificationId, notification);
            }
            catch (SecurityException exception) {
                // https://issuetracker.google.com/issues/294408576
                LogManager.w(TAG, "Call to service startForeground() threw a SecurityException.  The Foreground Service for beacon scanning may have started anyway, but this behavior might change in  different conditions or a future Android version.");
            }

The reason is because you are getting a android.app.ForegroundServiceStartNotAllowedException which does not inherit from SecurityException, it inherits from IllegalStateException.

We probably ned to add another catch here for that exception. This will prevent the crash reports, but it won't change the fact that we won't be able to start the foreground service in these cases.

@stephenruda any thoughts here?

from android-beacon-library.

stephenruda avatar stephenruda commented on June 11, 2024

Hi! Yes I have some thoughts.

ForegroundServiceStartNotAllowedException actually dates back to Android 12. After re-reading the Android 14 docs, I don't think there was anything new in Android 14 that would have caused this exception that wasn't already happening in Android 12 and 13.
This exception is actually a child of ServiceStartNotAllowedException which you are already catching here.
Catching the exception using the parent class should be sufficient in Java. It should catch both the ServiceStartNotAllowedException and ForegroundServiceStartNotAllowedException.

My understanding was that this exception was only ever thrown in calls to startForegroundService() and not calls to startForeground() like we are seeing in the mentioned stacktrace.

I have other foreground services in my app (unrelated to the android-beacon-library) that catches this exception. Just like in the BeaconManager, I am catching this exception on startForegroundService() and not on startForeground(). I have never had an issue with it.

I finally just shipped my Android 14 changes for my app last week. I have not noticed this exception yet in my app but I will keep my eye out for it.

My conclusion would be...

  1. Maybe something has changed in Android 14 and now this exception is being thrown from calls to startForeground() as well as startForegroundService(). I haven't experienced that yet myself so maybe we should wait until we can collect more data. My app has 4 other foreground services alongside the BeaconService so if this is the case it shouldn't take me too long before I see it. If that ends up being the case, we will have to fallback to the job scheduler just like we would when catching the exception from the BeaconManager. I don't think we could just "eat" the error and act like nothing happened like we do with the SecurityException.
  2. Maybe this user has the library implemented in a unique way. For example, maybe they are calling startForegroundService() themselves rather than allowing the BeaconManager to do it for them? If this is something they can recreate consistently, it would be great to see some code or have them recreate the problem in the reference application.

from android-beacon-library.

stephenruda avatar stephenruda commented on June 11, 2024

Looking a bit more into this issue, my leading theory is that there is something new in Android 14 that is causing this.

Because the BeaconService is sticky, it will attempt to restart itself if it is ever killed. This is likely why we are seeing calls to startForeground() without any calls from the BeaconManager.
The docs say that it does not restrict restarting a sticky foreground service. Maybe that is an undocumented change or bug in Android 14?

Finally, the reason that I have not seen this in my app is due to the fact that I force users to turn off battery optimizations for my app prior to using my foreground services. This allows me to start foreground services from the background and therefore my app would never throw an exception when this scenario occurs.

I am not suggesting that the Beacon Library should force users to turn this setting off - I am just explaining why I haven't seen the exception in my app.

Ultimately, what do we do about this issue? I am not really sure. To me it does feel like an Android 14 bug but I doubt that it will be fixed any time soon. I suppose falling back to the job scheduler is better than crashing? It would be nice if we could get more data/reports from other users to see how often this is occurring. Like I mentioned, it will not occur in my app so I don't have a good way to gauge how common the issue is.

from android-beacon-library.

davidgyoung avatar davidgyoung commented on June 11, 2024

Yes, I agree that falling back to the JobScheduler is probably the solution in this case, but it would be good to understand the specific sequence that triggers it. @stephenruda do you think you might be able to re-enable background optimizations on your app on a test phone and let it run for a day or so to see if you can reproduce a crash?

I am going to try running the reference app on the latest Android 14 to see if I can reproduce, but I may not be able to, because I suspect the foreground service will just keep running for days. I wonder if I can force a crash of the foreground service somehow to test your sticky restart theory.

from android-beacon-library.

stephenruda avatar stephenruda commented on June 11, 2024

Yes, I can do that.

I am unsure how to consistently reproduce the problem. I don't think there is a way we can manually trigger 'killing' the service in the same way that the OS does. I think the OS kills the service when system resources get low. As you suggested, it might take some time before we actually see the problem occur.

from android-beacon-library.

davidgyoung avatar davidgyoung commented on June 11, 2024

I was unable to reproduce the problem running the Kotlin reference app (configured to use a foreground service) on a Pixel 6a on the latest Android 14 (Security update January 5, 2014), then crashing the app connected to adb on my workstation with this command:

adb shell am crash org.altbeacon.beaconreference

The app crashed, then restarted as expected, and the foreground service started successfully as shown in the logs below.

2024-01-20 23:04:51.338 24146-24146 AndroidRuntime          org.altbeacon.beaconreference        D  Shutting down VM
2024-01-20 23:04:51.342 24146-24146 AndroidRuntime          org.altbeacon.beaconreference        E  FATAL EXCEPTION: main
                                                                                                    Process: org.altbeacon.beaconreference, PID: 24146
                                                                                                    android.app.RemoteServiceException$CrashedByAdbException: shell-induced crash
                                                                                                    	at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2115)
                                                                                                    	at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException(Unknown Source:0)
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2394)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:205)
                                                                                                    	at android.os.Looper.loop(Looper.java:294)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8248)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
2024-01-20 23:04:51.371 24146-24146 Process                 org.altbeacon.beaconreference        I  Sending signal. PID: 24146 SIG: 9
---------------------------- PROCESS ENDED (24146) for package org.altbeacon.beaconreference ----------------------------
---------------------------- PROCESS STARTED (24626) for package org.altbeacon.beaconreference ----------------------------
2024-01-20 23:04:51.510 24626-24626 Compatibil...geReporter org.altbeacon.beaconreference        D  Compat change id reported: 242716250; UID 10244; state: ENABLED
2024-01-20 23:04:51.520 24626-24626 nativeloader            org.altbeacon.beaconreference        D  Configuring clns-4 for other apk /data/app/~~Jv2bdx7ebRgX_okn7mVOOw==/org.altbeacon.beaconreference-fJ-Z1Z_BvOyOuJs1M4fQaA==/base.apk. target_sdk_version=34, uses_libraries=, library_path=/data/app/~~Jv2bdx7ebRgX_okn7mVOOw==/org.altbeacon.beaconreference-fJ-Z1Z_BvOyOuJs1M4fQaA==/lib/arm64, permitted_path=/data:/mnt/expand:/data/user/0/org.altbeacon.beaconreference
2024-01-20 23:04:51.529 24626-24626 GraphicsEnvironment     org.altbeacon.beaconreference        V  Currently set values for:
2024-01-20 23:04:51.529 24626-24626 GraphicsEnvironment     org.altbeacon.beaconreference        V    angle_gl_driver_selection_pkgs=[]
2024-01-20 23:04:51.529 24626-24626 GraphicsEnvironment     org.altbeacon.beaconreference        V    angle_gl_driver_selection_values=[]
2024-01-20 23:04:51.529 24626-24626 GraphicsEnvironment     org.altbeacon.beaconreference        V  ANGLE GameManagerService for org.altbeacon.beaconreference: false
2024-01-20 23:04:51.530 24626-24626 GraphicsEnvironment     org.altbeacon.beaconreference        V  org.altbeacon.beaconreference is not listed in per-application setting
2024-01-20 23:04:51.530 24626-24626 GraphicsEnvironment     org.altbeacon.beaconreference        V  Neither updatable production driver nor prerelease driver is supported.
2024-01-20 23:04:51.535 24626-24626 BeaconParser            org.altbeacon.beaconreference        D  Parsing beacon layout: m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24
2024-01-20 23:04:51.546 24626-24626 BeaconManager           org.altbeacon.beaconreference        I  BeaconManager started up on pid 24626 named 'org.altbeacon.beaconreference' for application package 'org.altbeacon.beaconreference'.  isMainProcess=true
2

from android-beacon-library.

davidgyoung avatar davidgyoung commented on June 11, 2024

App also restarted fine automatically after reboot with foreground service

from android-beacon-library.

rahul-kumawat-vts avatar rahul-kumawat-vts commented on June 11, 2024

@davidgyoung We got so many crash on over firebase. i have tried to replicate but not able to replicate.

In our app we are running a service to handle the beacon. Please check the code we are using in our app.

<service
  android:name=".MyAppBeaconService"
  android:enabled="true"
  android:exported="false"
  android:foregroundServiceType="location" />




import android.app.*
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.IBinder
import android.os.RemoteException
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.AndroidEntryPoint
import org.altbeacon.beacon.*
import timber.log.Timber


@AndroidEntryPoint
class MyAppBeaconService : Service(), InternalBeaconConsumer, MonitorNotifier, RangeNotifier {
    
    private var beaconManager: BeaconManager? = null
    private val region = Region("backgroundRegion", null, null, null)

    private var lastShownNotificationId = 0
    private var builder: NotificationCompat.Builder? = null


    companion object {
        const val IBEACON_LAYOUT = "IBEACON_LAYOUT_ACTUAL_VALUE"
        const val EDDYSTONE_UID_LAYOUT = BeaconParser.EDDYSTONE_UID_LAYOUT
        const val EDDYSTONE_URL_LAYOUT = BeaconParser.EDDYSTONE_URL_LAYOUT
        const val EDDYSTONE_TLM_LAYOUT = BeaconParser.EDDYSTONE_TLM_LAYOUT
    }

    override fun onCreate() {
        super.onCreate()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startBeaconForegroundService(NOTIFICATION_ID)
        }
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Timber.d("MyAppBeaconService onStartCommand")
        if (intent?.action == STOP_BEACON_FOREGROUND_ACTION) {
            stopForeground(true)
            stopSelf()
        } else {
            try {
                beaconManager = BeaconManager.getInstanceForApplication(this)
                beaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout(IBEACON_LAYOUT))
                beaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout(EDDYSTONE_UID_LAYOUT))
                beaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout(EDDYSTONE_URL_LAYOUT))
                beaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout(EDDYSTONE_TLM_LAYOUT))

                beaconManager?.backgroundBetweenScanPeriod = 1000L
                beaconManager?.foregroundBetweenScanPeriod = 1000L
                beaconManager?.enableForegroundServiceScanning(builder?.build(), NOTIFICATION_ID)
                beaconManager?.setEnableScheduledScanJobs(false)
                beaconManager?.backgroundScanPeriod = 1100L
                beaconManager?.bindInternal(this)
                beaconManager?.updateScanPeriods()

            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        // start MyAppBeaconService if not running.
        startBeaconService()
        super.onTaskRemoved(rootIntent)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun startBeaconForegroundService(notificationId: Int) {
        builder = getNotificationBuilder("CHANNEL_ID",
            NotificationManagerCompat.IMPORTANCE_LOW)
        builder?.let { builder ->
            builder.setOngoing(true)
                .setContentTitle(applicationContext.resources.getString(R.string.background_service_message))
                .setSmallIcon(R.drawable.ic_icon)
                .setPriority(NotificationManager.IMPORTANCE_MAX)
                .setCategory(Notification.CATEGORY_SERVICE)
                .addAction(1001, applicationContext.resources.getString(R.string.action),
                    beaconStopPendingIntent())
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                builder.foregroundServiceBehavior = Notification.FOREGROUND_SERVICE_IMMEDIATE
            }
            val notification = builder.build()
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                    this.startForeground(notificationId, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION)
                } else {
                    this.startForeground(notificationId, notification)
                }
            } catch (e: Exception) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                    && e is ForegroundServiceStartNotAllowedException) {
                    Timber.d("MyAppBeaconService - startBeaconForegroundService - ForegroundServiceStartNotAllowedException: ${e.message}")
                }
                e.printStackTrace()
            }
            if (notificationId != lastShownNotificationId) {
                // Cancel previous notification
                val nm = this.getSystemService(Activity.NOTIFICATION_SERVICE) as NotificationManager
                nm.cancel(lastShownNotificationId)
            }
            lastShownNotificationId = notificationId
        }
    }

    @RequiresApi(Build.VERSION_CODES.O)
    fun getNotificationBuilder(channelId: String, importance: Int): NotificationCompat.Builder {
        prepareChannel(channelId, importance)
        return NotificationCompat.Builder(this, channelId)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun prepareChannel(id: String, importance: Int) {
        val appName = resources.getString(R.string.app_name)
        val notificationManager = this.getSystemService(Activity.NOTIFICATION_SERVICE) as NotificationManager
        var notificationChannel = notificationManager.getNotificationChannel(id)
        if (notificationChannel == null) {
            notificationChannel = NotificationChannel(id, appName, importance)
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }

    private fun beaconStopPendingIntent(): PendingIntent {
        val intent = Intent(this, this.javaClass)
        intent.action = Constants.STOP_BEACON_FOREGROUND_ACTION
        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT)
    }

    override fun didRangeBeaconsInRegion(beacons: Collection<Beacon>, region: Region) {
        //Timber.d(" MyAppBeaconService - didRangeBeaconsInRegion - $beacons , Size - ${beacons.size}")

    }

    override fun onDestroy() {
        super.onDestroy()
        Timber.d("MyAppBeaconService, onDestroy ")

        beaconManager?.stopRangingBeacons(this.region)
        beaconManager?.removeAllMonitorNotifiers()
        beaconManager?.removeAllRangeNotifiers()
        beaconManager?.unbindInternal(this)
        beaconManager?.disableForegroundServiceScanning()
    }

    override fun didDetermineStateForRegion(state: Int, region: Region) {
        beaconManager?.startRangingBeacons(region)
        Timber.d("MyAppBeaconService - didDetermineStateForRegion - I have just switched from seeing/not seeing beacons: $state, region: $region")
    }

    override fun didEnterRegion(region: Region) {
        beaconManager?.startRangingBeacons(region)
        Timber.d("MyAppBeaconService - didEnterRegion - I just saw an beacon for the first time!")
    }

    override fun didExitRegion(region: Region) {
        try {
            beaconManager?.stopRangingBeacons(region)
        } catch (e: RemoteException) {
            Timber.d("MyAppBeaconService - didExitRegion - RemoteException: ${e.printStackTrace()}")
            e.printStackTrace()
        }
        Timber.d("MyAppBeaconService - didExitRegion - I no longer see an beacon")
    }

    override fun onBeaconServiceConnect() {
        Timber.d("MyAppBeaconService - onBeaconServiceConnect - beacon service connect")
        beaconManager?.removeAllMonitorNotifiers()
        beaconManager?.removeAllRangeNotifiers()
        beaconManager?.addMonitorNotifier(this)
        beaconManager?.addRangeNotifier(this)
        try {
            beaconManager?.startMonitoring(region)
            beaconManager?.startRangingBeacons(region)
        } catch (e: RemoteException) {
            e.printStackTrace()
            Timber.d("MyAppBeaconService - onBeaconServiceConnect - errStartMonitoringRanging: ${e.message}, exception: ${e.printStackTrace()}")
        }
    }

    private fun startBeaconService() {
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context.startForegroundService(Intent(context, MyAppBeaconService::class.java))
            } else {
                context.startService(Intent(context, MyAppBeaconService::class.java))
            }
        } catch (exception: Exception) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                && exception is ForegroundServiceStartNotAllowedException) {
                Timber.d("MyAppBeaconService - startBeaconService - ForegroundServiceStartNotAllowedException: ${exception.message}")
            }
        }
    }
}



from android-beacon-library.

davidgyoung avatar davidgyoung commented on June 11, 2024

from android-beacon-library.

stephenruda avatar stephenruda commented on June 11, 2024

Over the weekend, I was not able to reproduce the crash in my app after re-enabling battery optimizations.

I agree that this is an interesting approach. I have never done anything exactly like this. In my app, I do have instances where I start a foreground service from within another foreground service. My understanding of foreground services is that it should work. But there are so many intricacies and every new Android update adds more.

I am having a hard time wrapping my head around every possible scenario and how this situation might have occurred.

My best guess is that you are trying to start the BeaconService when it is not allowed - just as the exception states. You cannot start a foreground service while the app is in the background.
Is it possible that you are trying to start the service after sending the STOP_BEACON_FOREGROUND_ACTION flag?

You are also catching a lot of exceptions in the MyAppBeaconService. It is possible that you catch an exception within this service so it never makes it into the foreground - but then you try starting the BeaconService which will fail because nothing was in the foreground?

Here are some links that might be helpful:
https://developer.android.com/develop/background-work/services/foreground-services#bg-access-restrictions
https://developer.android.com/develop/background-work/services/foreground-services#wiu-restrictions
https://developer.android.com/develop/background-work/services/fg-service-types#location

Due to the amount of crashes you are seeing, I am guessing this issue is related to your unique set up.

from android-beacon-library.

davidgyoung avatar davidgyoung commented on June 11, 2024

@rahul-kumawat-vts, as suggested by @stephenruda, I suspect that the core issue here may be that Android 14 simply does not support your unusual foreground service use case. I would suggest you do one of two things:

  1. Remove the catch of the exceptions inside startBeaconForegroundService method and see if you get exception reports (indicating your foreground service is also being blocked from starting by Android.)
  • or -
  1. Add a new member variable to MyForegroundService called foregroundServiceStartFailed that gets set to true if any of the above exceptions inside startBeaconForegroundService get caught, and if this flag is true, do not do any of the call any of the BeaconManager setup which causes a secondary exception to crash the app. Then see if the crash reports go away.

If you choose option (2) above, I'd suggest you also add something to send stack traces or more detailed logs to a server so you can debug why your foreground service is not being allowed by Andorid to start, as option 2 may otherwise make your service start failures silent.

I am going to close this issue for now, as I do not believe there is anything the library can do to resolve these Android 14 restrictions. However, if you find evidence to the contrary in the above tests, feel free to re-open this issue.

from android-beacon-library.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.