Android beacon library how to turn foreground service to background mode? - android

What I want to achieve is, when user enter geofencing, the beacons foreground service will start to run and after one beacon detected, I will kill this foreground service and start to run it on the background just like the sample code on android-beacon-library-reference library.
private fun monitorBeacons(startForegroundService: Boolean) {
var beaconManager = WolApp.appContext?.beaconManager
if (beaconManager == null) {
WolApp.appContext?.beaconManager = BeaconManager.getInstanceForApplication(WolApp.appContext!!)
beaconManager = WolApp.appContext?.beaconManager
beaconManager?.backgroundMode = true
beaconManager?.beaconParsers?.clear()
beaconManager?.beaconParsers?.add(BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"))
beaconManager?.removeAllMonitorNotifiers()
}
if (startForegroundService) {
setupForegroundNotificationService(WolApp.appContext!!)
} else {
WolApp.appContext?.regionBootstrap?.disable()
WolApp.appContext?.regionBootstrap = null
try {
WolApp.appContext?.beaconManager?.disableForegroundServiceScanning()
} catch (e: IllegalStateException) {}
if (beaconManager?.scheduledScanJobsEnabled == false) {
beaconManager.setEnableScheduledScanJobs(true)
beaconManager.backgroundBetweenScanPeriod = BeaconManager.DEFAULT_BACKGROUND_BETWEEN_SCAN_PERIOD
beaconManager.backgroundScanPeriod = BeaconManager.DEFAULT_BACKGROUND_SCAN_PERIOD
}
}
if (WolApp.appContext?.regionBootstrap == null) {
WolApp.appContext?.regionBootstrap = RegionBootstrap(WolApp.appContext!!, regions)//regions are some iBeacon regions
}
if (!startForegroundService) {
WolApp.appContext?.backgroundPowerSaver = BackgroundPowerSaver(WolApp.appContext!!)
}
}
For setupForegroundNotificationService method is same with android-beacon-library-reference library.
I'm not quite sure if I'm doing this right or wrong, can anyone help, please?

It is a little bit tricky to switch a foreground service on or off because you are trying to change the behavior of multiple threads of execution that are already running behind the scenes in existing services.
The key thing missing from the code shown is that you must also make sure you have stopped the library from scanning before you can switch. This is complex because it is asynchronous -- it takes time for the scanner to shut down its threads.
If using regionBootstrap, the call to regionBootstrap.disable() does this. (You can also use beaconManager.unbind(...) if not using regionBootstrap). But the problem is those APIs do not give you a callback when the scaning service is fully shut down. And restarting it again before it is shut down can cause problems. I do not have a great suggestion here , other than perhaps using a timer -- say one second between stop and start?
You might also want to look at this discussion of a similar setup:
https://github.com/AltBeacon/android-beacon-library/issues/845

Related

What is the best way to execute asynchronous code inside CameraX analyze()?

I'm using CameraX's image analysis use case that keeps calling the analyze() method in my custom Analyzer class. Inside analyze(), before doing anything else, I need to send a request to a connected device and wait for its response; the latency is very low and I'm already doing it synchronously with no issues, but I was told it's better to make it asynchronous just in case the device responds too slowly.
I know that MLKit's process() returns a Task<List<T>> and I already call onSuccessListener { } on it, so I was wondering if I can use a similar approach (I can't return a Task<T> from my function, how do I create one?). Otherwise would you suggest threads, or coroutines, or something else?
Edit: below there's a simplified example of what I'm trying to do. For a given frame sent by the camera I just need to perform only the current analysis in line, then I return so that analyze() will be called again with the next frame, on which it will perform the next analysis.
It might look hacky but it's for an app that continuously runs in foreground on a single-purpose device (let's call it Dev A) with no user interaction provided by touch or other conventional means, so it needs some kind of trigger to start doing what is required.
The trigger might as well be when the first image analysis in line is successful, but running MLKit or TFLite models from real time camera feed all day long makes Dev A overheat excessively. The best solution so far seems to be waiting for the trigger to come from an external device (Dev B) that operates independently.
Since Dev B may respond with some delay I need to communicate with it asynchronously, hence the reason for the question in the first place. While there are certainly several architectural nuances to discuss, the current root of the problem is that I can't decide (or rather I don't know) how to handle the repeating "connection" with Dev B in a non-blocking way.
I mean, can I just treat this issue like any other case where multithreading is needed, or the fact that the camera is involved might pose additional threats? The backpressure strategy is set to STRATEGY_KEEP_ONLY_LATEST, so in theory if the current call to analyze() hasn't finished yet the new frames are dropped and nothing bad happens even if inside the method I'm still waiting for the async call to Dev B to finish, or am I missing something?
var connected = false
lateinit var result: Boolean
var analysis1 = true
var analysis2 = true
override fun analyze() {
if (!connected) {
result = connectToDevice() // needs to be async
connected = true
}
// need positive result to proceed, otherwise start over
if (!result) {
connected = false
return
}
if (analysis1) {
// perform analysis #1...
analysis1 = false
// when an analysis is done, exit early and perform next analysis on next frame
return
}
if (analysis2) {
// perform analysis #2...
analysis2 = false
// same as above
return
}
// when all analyses are done, reset all flags to start over
connected = false
analysis1 = true
analysis2 = true
}

Android - how to solve long server response waiting time?

Do you have any ideas on how to solve in an android long time of waiting on server response?
I am making a request to the API saving profile.
This request is fast
But the server is processing it rather long 10sec to 3min (like scanning cheap flight on some tickets sites)
After the response, I need to redirect the user to a confirmation screen or home screen of the app.
I solved this by setting the longer timeout to 45 sec and then always redirecting to confirmation + alert that it takes longer.
On the home screen, I am displaying "Processing..." label until the server finishes
But this solution has some problems like:
what about user going out by home button if it takes longer, or switching applications, and if just display goes to sleep while untouched more than 30sec? Then activity/fragment is recreated and response seems to not arrive.
I consider adding push notification when processing is done this could help a little. Is there any way to solve such an issue? Maybe some background Service? But isn't Android Services deprecated? I think only Foreground Services are valid to use, or maybe new WorkManagers (but this doesn't seem to fit this scenario). And how from then wakeup screen and move it to the next page.
Code sample:
// Fragment
viewModel.saveData(data)
// View Model
fun saveData(data: Data) : LiveData<Resource<DataResponse>> {
_dataEvent.postValue(Event(Resource.loading(null)))
val apiSource = dataRepo.saveData(data)
_dataEvent.addSource(apiSource) { resource ->
_dataEvent.removeSource(apiSource)
val resource = resource ?: Resource.error(null, null)
_dataEvent.postValue(Event(resource))
}
return apiSource
}
// Observing Data Event
viewModel.dataEvent.observe(this,
Observer { event ->
if(event?.peekContent()?.status == Resource.Status.LOADING) {
showProgressAlert(context)
event.getContentIfNotHandled() // consume loading event
}
val resource = event?.getContentIfNotHandled()
if(resource != null) {
hideProgressAlert()
if (resource.status == Resource.Status.SUCCESS) {
showSuccessAlert(context)
navigateToConfirmPage()
} else if (resource.status == Resource.Status.ERROR) {
if (throwable is SocketTimeoutException) {
showTimeoutAlert(context)
navigateToConfirmPage()
} else {
showErrorAlert(context)
}
}
}
If this request is taking a long time then you should perform it in a background thread, Since android oreo background services became very restricted if you want to implement it you can use JobIntentService or JobScheduler or make it a ForegroundService but in this case you will have to show a notification to the user while the service is running, You can read more about it here https://developer.android.com/about/versions/oreo/background
Another approach is to use RXjava which handles threading and perform background services very smoothly

Android beacon library oscillates between on and off with a fast pace

I used the android beacon library to do the following action:
I switched on and off with a fast pace. On and off, on and off, and so on for 8-9 times.
However, the beacon then lost the signal for about 10 seconds and then the signal started to be received again.
Also, I tried an Android API function, "lescan", which resulted in the same situation.
Does anyone know why this happens?
MY testing device is:
HUAWEI P20 Pro 8.1
Samsung S6 7.0
override fun onResume() {
beaconManager = BeaconManager.getInstanceForApplication(this)
beaconManager.getBeaconParsers().add(BeaconParser().
setBeaconLayout(IBEACON_LAYOUT))
beaconManager.getBeaconParsers().add(BeaconParser().
setBeaconLayout(EDDYSTONE_UID_LAYOUT))
beaconManager.getBeaconParsers().add(BeaconParser().
setBeaconLayout(EDDYSTONE_URL_LAYOUT))
beaconManager.getBeaconParsers().add(BeaconParser().
setBeaconLayout(EDDYSTONE_TLM_LAYOUT))
beaconManager.bind(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val permissions = ArrayList<String>()
if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)) permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION)
if (permissions.size != 0) {
ActivityCompat.requestPermissions(this, permissions.toTypedArray(), 100)
}
}
}
override fun onBeaconServiceConnect() {
beaconManager.addRangeNotifier{ beacons,region ->
Log.d("addRangeNotifier",beacons.size.toString())
}
try {
beaconManager.startRangingBeaconsInRegion(Region("com.gigabyte.testkotlin", null, null, null))
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onPause() {
super.onPause()
beaconManager.unbind(this)
}
It's hard to say exactly what you are witnessing without seeing exact code to reproduce, but turning scanning on and off quickly is not necessarily a problem on all devices.
By default, the Android Beacon Library uses a foreground scan period of 1100 ms and a between scan period of 0ms, so it effectively turns scanning on and off 9 times in just over 10 seconds -- similar to what you describe.
I have never noticed these symptoms in normal use of the library on Samsung devices or the Huawei P9, so something else must be triggering this behaviour in your test case.
EDIT: The posted code indicates that the activity itself is what is started and stopped rapidly, and because it binds and unbinds to the beaconManager as it starts and stops, it also starts and stops the Android service that scans for beacons. These are heavy weight data structures that are not designed to be started and stopped rapidly. Short answer: don't do this. If you really need to start and stop your activity rapidly, bind to the beaconManager outside the activity lifcycle, perhaps only one at app startup in the onCreate method of a custom Android Application class.

Uploading files in an Android service

What is the best way of letting an Android app upload a potentially large file to a server at a request from the user?
I'm currently using an IntentService, in which I call startForeground and update the notification progress periodically, but the service randomly gets killed by the system after about a minute or so.
Here is the relevant code from onHandleIntent :
class BeamService extends IntentService("SSH Beam") {
override def onHandleIntent(intent: Intent) = {
...
// Start the notification
startForeground(0,
builder
.setTicker("Starting transfer")
.setContentTitle(filename)
.setContentText("Starting transfer")
.setOngoing(true).build
)
// Create the session and the monitor
val session = server.createSession(auth)
implicit val monitor = Monitor(filename, size)
// Send the file
try {
session.connect
session.cd(destination)
session.put(filename, is)
} catch {
case e: Throwable => {
notificationManager.notify(0,
builder.setProgress(0, 0, false)
.setTicker("Transfer failed")
.setContentText(e.getMessage)
.build
)
e.printStackTrace
}
} finally {
session.disconnect
is.close
}
stopForeground(false)
}
}
I found out how to implement that properly :
Do not use the notify method from the NotificationManager while you are in foreground. According to this, you have to use startForeground again if you want to update the notification. (This was causing my service to get killed by the system)
There's a weird quirk in startForeground that makes it not show notifications if the ID is 0.
Finally, I should have thought about it, but this question gives a nice in-depth answer on how to check if the service is indeed in foreground.
(The fantastic flying network monitor from the System Monitor app also helped me a lot to check if the upload was still running while I was starting other apps to try and trigger the "service died" message)
There are a few reasons to use startForeground, but I can't for the life of me think of a reason to use startForeground on an IntentService! An IntentService should be used to do long-running tasks on a background thread, without interruption, from which you want persistent results.

How to set a "control panel" for my Service in Android?

I noticed that some running services have a Settings rather than Stop button in the details page of Running services in the system Settings app. I want to setup my own service to work like this.
After digging into the source code of Settings app, I found a clue:
ActivityManager.getRunningServiceControlPanel()
Returns a PendingIntent you can start to show a control panel for the given running service. If the service does not have a control panel, null is returned.
My question is: how can I set a control panel for my own service?
In case anyone's curious, I found the code behind this feature. The service's description and configuration intent can be set during a service binding, if and only if the caller is running as SYSTEM.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2.1_r1/com/android/server/am/ActivityManagerService.java#11651
int clientLabel = 0;
PendingIntent clientIntent = null;
if (callerApp.info.uid == Process.SYSTEM_UID) {
// Hacky kind of thing -- allow system stuff to tell us
// what they are, so we can report this elsewhere for
// others to know why certain services are running.
try {
clientIntent = (PendingIntent)service.getParcelableExtra(
Intent.EXTRA_CLIENT_INTENT);
} catch (RuntimeException e) {
}
if (clientIntent != null) {
clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
if (clientLabel != 0) {
// There are no useful extras in the intent, trash them.
// System code calling with this stuff just needs to know
// this will happen.
service = service.cloneFilter();
}
}
}
This code was moved at some point, but still exists in KitKat, unchanged.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/com/android/server/am/ActiveServices.java#665

Categories

Resources