I have an App that listens to android.location.PROVIDERS_CHANGED broadcast to stay aware of the phone's current location capabilities.
With Android M and the new App-runtime permissions, it works normally when the location is changed from the system/general panel. But if a user disables the location permission specifically for the App, the same broadcast is not fired.
So far I can test for location access with a regular AlarmManager, but it's quite odd and not very responsive.
Is there any other specific method, ideally a kind of BroadcastReceiver to register that keeps us informed of this location app-permission-change with Android M?
Sorry, but you are not directly notified about permission changes, for locations or other permissions, unless there's some undocumented hack that I'm not aware of.
If the user revokes a permission, your app's process will be terminated. The idea is that you will find out about the revoked permission when your app runs again and you call checkSelfPermission(). That flow is optimized for more conventional cases, where the permission checks are being conducted in an activity opened by the user. It doesn't handle your case very well (though, IMHO, location permissions shouldn't affect the broadcast as you describe).
In the N Developer Preview, you can now set up JobScheduler to monitor a Uri for changes and trigger your job as needed. This is a bit like registering a ContentObserver, except that you don't need the process running all the time — JobScheduler registers the observer and just invokes your JobService as needed. I think that the roster of enabled location sources is found in the Settings provider somewhere; if so, for Android 6.1/7.0/Turbo System 5000/whatever N turns into, you might be able to use JobScheduler to find out about the location source changes, instead of relying on the broadcast.
That doesn't help you for Android 6.0, though. You can use JobScheduler or AlarmManager or something to see if you lost the permission (via checkSelfPermission()), but that's kinda wasteful of battery life (and, as a result, will not work well given Doze mode and possibly app standby).
Related
I have an Android app on Play store for 8 years. Recently Google release Android S or 12 introduce some limit with Foreground service launch restrictions
https://developer.android.com/about/versions/12/behavior-changes-12#foreground-service-launch-restrictions
and
Exact alarm permission
https://developer.android.com/about/versions/12/behavior-changes-12#exact-alarm-permission
In the app I use foreground service and alarm clock to schedule update weather data from the cloud and device sensor and send notification to user, update the widget.
But they said: Exact alarms should only be used for user-facing features so if I continue use those API, it is safe (with Google Play policy)?
I ask this because other solution like sticky notification with foreground service and workmanager not work as my requirements.
if you are testing android 12 then don't forget to add this line to Manifest
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
Yes, the android.permission.SCHEDULE_EXACT_ALARM it's safe to use, on Android 12 this permission is automatically granted by the Android system but on Android 13 you need to check if the user has granted this permission.
So you need to add the permission to the manifest
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
And then you need to check if the permission was granted, if not granted then you need to redirect the user to the Alarms & Reminders page
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val alarmManager = ContextCompat.getSystemService(context, AlarmManager::class.java)
if (alarmManager?.canScheduleExactAlarms() == false) {
Intent().also { intent ->
intent.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
context.startActivity(intent)
}
}
}
Google also suggests that you need to check any changes on this permission by registering a Broadcast Receiver and check the changes on ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED
Google states: "(when your app) requires precisely-timed actions". Your use case is "to schedule update weather data (…) send notification to user". While this might be user-facing, it doesn't seem to require to be precisely on a certain time. I would guess your app doesn't qualify.
The methods requiring the additional permission are currently: setExact(), setExactAndAllowWhileIdle() and setAlarmClock(). Repeating alarms will always be inexact. Seems like getting processing weather data and device sensors is something repetitive anyway.
From what you've mentioned, you're talking about user-facing features.
A hypothetical example of the opposite would be Facebook forcing synchronization of user data at some specific time. That would be bad because it's preferable not to force a schedule on those types of things as it doesn't matter whether it happens at a specific time or a minute later when system resources are not used by some other service.
Also, "should" means it's a recommendation. Facebook can do the above, but it would be a less optimal solution. It's best to leave control over those kinds of services to Android as it would likely do a better job at distributing resources and preventing lag. So in other words, you not listening to their recommendation won't get your app removed from the app store or something like that.
Also, the paragraph you quoted from the second link, has a link to examples of acceptable use cases, and it mentions alarm apps. This is likely why your question was downvoted.
effective solution
you need to add the permission to the manifest before <application
I am verifying my location service compatibility with Android Q but I am a little unsure how my app is going to react since in my testing I have seen not difference when granting Background permission vs Only while app is running.
Coming off this statement from the Q migration documentation
An app is considered to be in the background unless one of its
activities is visible or the app is running a foreground service.
Since the location service is a foreground service does the difference in permission even matter in this case?
Well it's a bit of a tricky question.
When running location foreground service on Q you need "while app running" permission + to declare in the manifest this foreground service is of type location. Your app has no need of the background permission.
If your app do asks for background permission, the user can get suspicious and reject any location permission :( So it is not recommended to ask the user for permissions you don't need.
highly recommend to watch this video from the google IO: Updating Your Apps for Location Permission Changes in Android Q (Google I/O'19)
You can read more about t here: documantation
And you can see googles example project on GitHub for location updates on Q with foreground service here :LocationUpdatesForegroundService
You just need run foreground service instead of background. Otherwise your app will crash during background services start when there is no activity on the foreground
I have developed a Android hybrid app that amongst other activities tracks the users location and displays it to the user showing where they have been recently. This works by invoking LocationServices.FusedLocationApi.requestLocationUpdates at a 10 second update rate.
Obviously this requires location permission and for ANDROID Marshmallow and above I check and request the necessary location privileges from the user before requesting location updates.
I have read many other StackOverflow question about the same topic but I have not found a satisfactory answer about preventing Android from terminating the app if location permission is revoked for the app by the user after initially granting it.
I would be more than happy for the app to stop displaying the user's location and track, after all the user has asked for it to be stopped, but I am far less happy for the app to be terminated, as I said earlier the app does many other things for the user whilst running in the background.
As far as I understand there is no callback that can be used to allow me to remove the location updates as soon as the location permission is revoked. How could I change my strategy to avoid the app crashing?
It is not that I am expecting users to do this very often, but I think it good practice to make the app as bulletproof as possible. Please do not remind me that Android can terminate any app in the background as that is not my question here.
Do I need to adopt a singular rather than a periodic approach to requesting the location so that I can re-check the permission exists each time.
Or is it better to try and catch the error. If so where do I do this? Would it be inside onLocationChanged?
What I mean by app crash is that the process is terminated (I have checked and this actually happens)
That's perfectly normal, when the user revokes a runtime permission.
What I would like to do is prevent the process terminating if there is any way I can do this.
Sorry, that is not possible.
Since I suspect that few users even know that they can revoke runtime permissions, this is not going to be a common occurrence, IMHO.
Blockquote
however why did the Android developers not just cause a callback to be generated to allow the app code base to react
You could register for the LocationManager.PROVIDERS_CHANGED_ACTION in the BroadcastReceiver.
When it is hit, you can check if the provider is still enabled and use that accordingly to prevent your app from crashing when someone revokes the location permission.
LocationManager locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
boolean enabled = locationManager != null && locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
I am implementing a service that uses LocationManager to get and utilize the tablet location. This service is start and stop from an activity.
The latest Android requires that permissions are requested on runtime. Now I have managed to do this on an activity by using requestPermission in onCreate , checkSelfPermission everytime I use some Location manager function, and adding the requestPermission function and overriding the onRequestPermissionResult.
It works great.
Now for my service I need to do the same, but these functions seems to work only for activities. How can I activate permissions in a Service?
just in case, I have already asked for permissions in the activity that starts and stops the services
How can I activate permissions in a Service?
You don't. You activate (i.e., request) permissions from an activity. That is not negotiable.
Ideally, you request permissions before the activity starts the service or does something that will eventually cause the service to start (e.g., schedules the job with JobScheduler).
If you determine that your service no longer has the necessary permissions — perhaps the user revoked them from Settings — you could raise a Notification that leads the user to an activity where you re-request the permissions.
It is technically possible for a service to start an activity which requests the permissions. Usually, this is not a good idea, as you may not know what the user is doing at that moment, and the user may be unhappy to have you interrupt them with this permission request.
How can I activate permissions in a Service?
You can't request for permissions from services. Permissions should be asked explicitly which should be visible to the user in UI. However you can ask permission from activity and, if succeed, you can access those resources until user again turned off permission for your app.
how can you "transfer" these permissions to the service?
Permission is assigned for the entire app, so you don't need to transfer it from one activity to another or from one activity to service. Once you get a permission in an Activity, that permission is assigned to the entire app and your services can access the resources then after. cheers :)
I'm looking into porting some existing code to take Android M's new way of dealing with permissions into consideration. However the permission API needs to have an activity associated with it (for example the requestPermissions() method's first parameter is an activity).
So how should a service that needs to check if a permissions has been granted and request for permissions use this new API if the service doesn't have an activity?
Is it possible for the service to create a dummy invisible activity just for use with the permissions API? (if its possible I don't like the thought of doing that anyway though).
Or suppose its not a service but a model class that needs to perform a permissions check, in MVC a model shouldn't have any knowledge of the Vs and Cs and yet now either it has to in order to know which Activity to use with the permission API. Or potentially lots of code might have to migrate from model code into Activity code.
Any thoughts on how to migrate non activity based code that needs to check/prompt for permissions over to Android 6.0?
Update: I left out an important piece of information - this is code that is pre-installed (our company provides code that device manufacture's place in rom) and often may be run at device boot time and run in the background. Therefore the usual situation of a user being prompted for permission when they launch the app or later (and there therefore being an activity at that point) does not necessarily apply.
So how should a service that needs to check if a permissions has been granted and request for permissions use this new API if the service doesn't have an activity?
There is almost always an activity, except for pre-installed apps and plugins for other apps. Otherwise, your service is unlikely to ever run, as nothing will have used an explicit Intent to start up one of your app's components, so it will remain in the stopped state.
For the ~99.9% of Android apps that have an activity already, if the permissions are needed for the whole operation of the app, request them on first run. As Snild Dolkow notes, if the user later revokes the permission through Settings, you can detect that without an activity, then use other UI options (e.g., Notification, app widget) to let the user know that operation is suspended until they grant you the permissions again, which they would then do through your activity.
Is it possible for the service to create a dummy invisible activity just for use with the permissions API?
Presumably you can have a Theme.NoDisplay activity use requestPermissions(). However, from the user's standpoint, it will not make much sense, unless there's some alternative UI (app widget?) that they are interacting with. Popping up a permission dialog out of nowhere is unlikely to make you popular.
UPDATE 2019-06-15: Note that Android Q bans services popping up activities frmo the background. Please use a notification instead.
in MVC a model shouldn't have any knowledge of the Vs and Cs and yet now either it has to in order to know which Activity to use with the permission API
Do not touch the models until you have requested the permission, and gracefully fail if the permission is revoked. You already have to gracefully fail in other circumstances (out of disk space, no Internet connection, etc.), so a revoked permission should be handled in much the same way.
using this new 6.0 API seems like an recipe for bad design and tight coupling
You are welcome to your opinion. Based on what I have read, the Android engineers believe that asking the user for permissions is part of the user experience and is best handled at the UI layer as a result.
Again: the vast majority of Android apps will not have a problem with this, as they have a user interface. Apps that do not have a user interface and need dangerous permissions are in for some amount of rework.
this is code that is pre-installed (our company provides code that device manufacture's place in rom) and often may be run at device boot time
First, please understand that this is so far from normal that you can't even see normal from where you are due to the curvature of the Earth. :-) You can't really complain that Google did not optimize this particular scenario.
As I understand it, even system apps should be asking for runtime permissions. The Camera app did, for example, on the 6.0 preview. That being said, there's gotta be some database on the device somewhere that is tracking what has been granted, and presumably there is some way to pre-populate it. However, the user could still revoke it from Settings, presumably. But, the manufacturer could pull some stunts (e.g., messing with the Settings app) to possibly even preclude that scenario. I'd be looking in the same area as "how do I get it so my app cannot be force-stopped?" that device manufacturers can do.
Your alternatives would be to get rid of the dangerous permissions or to migrate your app off the SDK and into a standard Linux binary that would be run as part of the boot process and be put into a Linux user group that has access to the stuff that you need.
Ask for it when the user enables whatever feature your service provides. They'll be in one of your activities at the time. Yes, it means that your activities need knowledge of what permissions your services will require.
The service can always check for the permission by itself, though, since checkSelfPermission() is available in all Context instances. So you don't need an activity for that.
I guess an alternative would be to have your service post a notification saying "feature X requires you to approve more permissions". Actually, that may be a good idea regardless, in case the user goes into settings and revokes any permissions after the fact. That notification would then take the user to some activity with an "enable feature X" button/checkbox -- ask for the permission when that is selected.
You can send a notification. Look this library to manage the permissions: permission library