I have an android system service(not app) which is used by android applications. I want to add a customed permission to this system service, then an application has to have this permission to access my service.
Now, I've added a permision in /frameworks/base/core/res/AndroidManifest.xml, say com.test.testpermission.
But what else should I do to use this permission in the system service?
Inside the service code you need to add permission check into all public API's exposed by the service. To check the permissions you can use this approach:
private boolean checkPermission() {
if (getContext().checkCallingOrSelfPermission("com.test.testpermission") == PackageManager.PERMISSION_GRANTED) {
return true;
}
return false;
}
So, the client app have to have your permission listed in its manifest.
I never tried it by myself but I can see it is used by TelephonyRegistry service for example.
For native code I can see this in camera service:
if (!checkCallingPermission(
String16("android.permission.CAMERA"))) {
const int uid = getCallingUid();
LOGE("Permission Denial: "
"can't use the camera pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
Related
What permission do we need to use instead of WRITE_EXTERNAL_STORAGE in Android 13 app on Android 13 device? I have read through the documentation but not able to get what permission we need to use. The function which needs this is a photo picker which access the images, before it used to ask for permission on Android 12, but once the app was upgraded to Android 13 and used on an Android 13 app, the photo picker stopped working. It uses the permission WRITE_EXTERNAL_STORAGE which is not required anymore. But what needs to be used instead of this?
The function is from a library - react-native-image-crop-picker
#ReactMethod
public void openPicker(final ReadableMap options, final Promise promise) {
final Activity activity = getCurrentActivity();
if (activity == null) {
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
return;
}
setConfiguration(options);
resultCollector.setup(promise, multiple);
initiatePicker(activity);
permissionsCheck(activity, promise, Collections.singletonList(Manifest.permission.WRITE_EXTERNAL_STORAGE), new Callable<Void>() {
#Override
public Void call() {
return null;
}
});
}
Use react-native-permissions for managing the permissions.
The docs are quite simple, kindly go through this.
Some permission changes were required for this in the react-native-image-cropper library - https://github.com/ivpusic/react-native-image-crop-picker/pull/1852
I have issues with my app recently, when it is out of nowhere rejected by Google Play because they found that I'm using background location. But in fact I'm not using this feature. I have only ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions and I'm using FusedLocationProviderClient to get location in my app. This location is requested only by user action inside app, so if its in background, this is never called. I checked merged manifest feature and I tried to find if some of my imported libs are using background location permission, but I didn't find anything. Also I preventively added <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" tools:node="remove"/> to my manifest to block any background location permission requests. I dont have any background services which are working with location at all. The only background service is FirebaseMessagingService for push notifications.
Anyone have this problem recently?
UPDATE:
I checked merged manifest in my app and I couldn't find ACCESS_BACKGROUND_LOCATION permission there. But I found some services which could trigger background location but I'm not sure. They are part of Firebase Crashlytics and they are probably used to send data to Firebase and they could work in a background. But I don't think they are sending any location. Also they are part of firebase plugin which is from Google.
<service
android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
<receiver
android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver"
android:exported="false" />
UPDATE #2:
This is code I'm using to get location.
MainActivity:
/**
* Updating location every second/1 meter
*/
var currLocation: GpsLocation? = null
private var locationManager : LocationManager? = null
private fun initLocationManager() {
if (app.hasLocationPermission){
locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
}
changeLocationUpdaters(true)
}
private fun changeLocationUpdaters(isEnabled: Boolean){
if (ActivityCompat.checkSelfPermission(
this#MainActivity,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(
this#MainActivity,
Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
locationManager?.apply{
if (isEnabled && app.hasLocationPermission){
requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_UPDATE_TIME_INTERVAL, LOCATION_UPDATE_DIST_INTERVAL, this#MainActivity)
requestLocationUpdates(LocationManager.NETWORK_PROVIDER, LOCATION_UPDATE_TIME_INTERVAL, LOCATION_UPDATE_DIST_INTERVAL, this#MainActivity)
} else {
removeUpdates(this#MainActivity)
}
}
} else {
return
}
}
Then removing location updaters when app is in background:
override fun onPause() {
super.onPause()
changeLocationUpdaters(false)
}
override fun onResume() {
super.onResume()
changeLocationUpdaters(true)
}
Then I use FusedLocationProvider inside Fragment to get more accurate location. Its used only by calling function so its not automated like previous one. Its used in GoogleMap classes and also in some onClick events inside app to return current location. There is no service or updater calling it.
private inner class LocationCb(val lp: FusedLocationProviderClient,
val onFailure: (()->Unit)? = null,
val onSuccess: (GpsLocation)->Unit)
: LocationCallback() {
init {
val lr = LocationRequest.create().apply {
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
interval = 200
}
val lsr = LocationSettingsRequest.Builder().run {
addLocationRequest(lr)
build()
}
val check = LocationServices.getSettingsClient(activity!!).checkLocationSettings(lsr)
check.addOnCompleteListener {
try {
check.getResult(ApiException::class.java)
val task = lp.requestLocationUpdates(lr, this, Looper.getMainLooper())
task.addOnFailureListener {
onFailure?.invoke()
}
} catch (e: ApiException) {
when (e.statusCode) {
LocationSettingsStatusCodes.RESOLUTION_REQUIRED-> if(!locationResolutionAsked){
// Location settings are not satisfied. But could be fixed by showing the user a dialog.
try {
// Cast to a resolvable exception.
val re = e as ResolvableApiException
// Show the dialog by calling startResolutionForResult(), and check the result in onActivityResult().
re.startResolutionForResult(mainActivity, MainActivity.REQUEST_LOCATION_SETTINGS)
locationResolutionAsked = true
} catch (e: Exception) {
e.printStackTrace()
}
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE->{
App.warn("Location is not available")
onFailure?.invoke()
}
}
}
}
}
fun cancel(){
lp.removeLocationUpdates(this)
currLocCb = null
}
override fun onLocationResult(lr: LocationResult) {
cancel()
val ll = lr.lastLocation
onSuccess(GpsLocation(ll.longitude, ll.latitude))
}
}
This location provider is cancelled after result is returned so its one-time use only. But Ive added similar cancellation method inside onPause and onStop for Fragment than it is in MainActivity to make sure that its inactive when app is in background.
override fun onStop() {
super.onStop()
currLocCb?.cancel()
}
override fun onPause() {
super.onPause()
currLocCb?.cancel()
}
Merged manifest may not contain all permissions
Unfortunately, not all libraries publish a manifest that contains all necessary <uses-permission> elements. That means, that simply checking your merged AndroidManifest.xml won't help much - you will have to check documentation for each library to find out which permissions it really needs, or just add necessary permissions to your own AndroidManifest.xml preemptively.
Background permission limitation for API 29
You also mentioned that your target SDK is 29. So, according to the official documentation here, you have to set the permission in your AndroidManifest.xml explicitly, if it's needed. Previously, it was granted automatically, if the app had foreground location access (basically, ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION).
On Android 10 (API level 29) and higher, you must declare the
ACCESS_BACKGROUND_LOCATION permission in your app's manifest in order
to request background location access at runtime. On earlier versions
of Android, when your app receives foreground location access, it
automatically receives background location access as well.
So, for older versions, your app was granted ACCESS_BACKGROUND_LOCATION automatically, because it was granted ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION beforehand.
Requesting location in background requires ACCESS_BACKGROUND_LOCATION
Additionally, even if you or any of your libraries do not set ACCESS_BACKGROUND_LOCATION anywhere, the system will still consider that your app is using background location for any situation except:
An activity that belongs to your app is visible.
Your app is running a
foreground service. When a foreground service is running, the system
raises user awareness by showing a persistent notification. Your app
retains access when it's placed in the background, such as when the
user presses the Home button on their device or turns their device's
display off.
Conclusion
What the latter means is that may have a library or libraries that need ACCESS_BACKGROUND_LOCATION, but it's not present in their AndroidManifest.xml for whatever reason. It used to work for API < 29 because your app was granted the permission automatically (due to foreground location permission).
Also, now, the system considers any usage of current location a background location if it's done outside of your visible Activity or not in a Foreground Service. So, make sure that you're not doing so in any part of your app.
Update
Based on your updated question, you are requesting a current location within OnCompleteListener by calling lp.requestLocationUpdates:
...
check.addOnCompleteListener {
try {
check.getResult(ApiException::class.java)
val task = lp.requestLocationUpdates(lr, this, Looper.getMainLooper())
task.addOnFailureListener {
onFailure?.invoke()
}
...
This can be a problem (I cannot be sure because you don't show how the class is used within your app) because the app may go to the background before OnCompleteListener completes, and so the location will be requested in the background.
As stated in the previous section, by doing so the system considers that you need a background location permissions to do so. So, you must unsubscribe your callback OnCompleteListener if your app goes to background.
You could use another version of addOnCompleteListener that also accept your Activity instance as shown here
public Task addOnCompleteListener (Activity activity, OnCompleteListener listener)
In this case, the listener will be automatically removed during Activity.onStop().
First of all, remove completely words ACCESS_BACKGROUND_LOCATION from your manifest. Even with tools:node="remove".
The second: if you haven't added ACCESS_BACKGROUND_LOCATION manually it doesn't mean it is not there - some libraries may have added it for you. Instead of checking your project manifest file - check merged manifest - the usual path to it is: (it may be different in your case if you have flavors)
/project/module/build/intermediates/manifests/full/debug/AndroidManifest.xml.
Check if there is ACCESS_BACKGROUND_LOCATION permission there - if there is - this means that some library added it there. Manually check all the manifests of all the libraries to find out which one has added it there. When you find it - delete it.
If your project heavily depends on the target library - you have another solution - write a disclosure in the app and play store console about why do you need to use background location and show it before the location permission dialog with message that looks like:
We need access to your location in the background to ensure our app can function correctly.
Keep in mind that this message may be not enough descriptive - but testers from google will notify you if it is.
Either way, disclosure is the last chance solution...
If you have no ACCESS_BACKGROUND_LOCATION and you do not use location in foreground service but only inside the app while it is running - write a letter to google support with all your arguments and ask them what exactly causes the rejection issue. Be polite and well-tempered - and it will be resolved. I have had similar issues in the past and contact with their release support always helped.
I really struggle with this since a while :( as I need an solution that works within UNITY3D.
I need to check if the user has given the permission to access the Android device camera (and location on a second level).
Normally the app start by asking for this permissions at launch, but if the user denies the access for the camera I need to know and check that later.
Otherwise the user could hit the camera UI button I made and try to access the camera via webcamtexture... and that leads into a crash of the app.
Since Android API 23 you cannot ignore or already grant permissions by changing the android manifest like I tried after reading several posts about that.
Thank's to everyone who has an idea to solve this.
Check this library: https://github.com/sanukin39/UniAndroidPermission
In that library I got these methods to check and request Permission.
public static void requestPermission(String permissionStr){
if(!hasPermission(permissionStr)) {
UnityPlayer.currentActivity.requestPermissions(new String[]{permissionStr}, 0);
}
}
public static boolean hasPermission(String permissionStr) {
if(Build.VERSION.SDK_INT < 23) {
return true;
}
Context context = UnityPlayer.currentActivity.getApplicationContext();
return context.checkCallingOrSelfPermission(permissionStr) == PackageManager.PERMISSION_GRANTED;
}
Hope it helps:)
I'm developing an app that require some system permissions, however these are no longer granted automatically at installation time on Android Marshmallow.
I would like to request these permissions at runtime and run some kind of automation to grant them without needing a user to tap the 'allow' button when the System permissions Dialog appears.
How can I achieve this? Is there any way to do so in Marshmallow and later versions?
For Marshmallow or later permissions are not granted at install time and must be requested when required at runtime (if not granted previously.)
To do this, you need to run ActivityCompat.requestPermissions() to pop up the systems permissions dialog in your Activity, at the time when the user is undertaking an action that requires additional system permissions.
An example of this for the WRITE_EXTERNAL_STORAGE permission would be:
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
WRITE_EXTERNAL_STORAGE_REQUEST_CODE
);
Note: WRITE_EXTERNAL_STORAGE_REQUEST_CODE is an arbitrary integer constant you should define elsewhere.
The permissions you request should also be declared in your AndroidManifest.xml. In this example the declaration would be:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
In order to handle the system permissions dialog response you will also need to implement onRequestPermissionsResult() in your Activity. For this example the code would be similar to
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String permissions[], #NonNull int[] grantResults) {
if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) {
return; //permission not granted, could also optionally log an error
}
if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
//Do whatever you needed the write permissions for
}
}
If you are automating your app through Espresso, UIAutomator and/or some other UI testing framework you will need to anticipate and click the system dialog during your test, which can be accomplished with the following test code:
private void allowPermissionsIfNeeded() {
if (Build.VERSION.SDK_INT >= 23) {
UiObject allowPermissions = mDevice.findObject(new UiSelector().text("Allow"));
if (allowPermissions.exists()) {
try {
allowPermissions.click();
} catch (UiObjectNotFoundException e) {
Timber.e(e, "There is no permissions dialog to interact with ");
}
}
}
}
A more comprehensive explanation of testing System UI Permissions is available here.
I found out that an simpler way to automate the permission acceptance without using UIAutomator or espresso in a CI scenario is to simply, pre-installing the apk via adb using:
adb install -g {my_apk_file}
The -g flag automatically grants all manifest permissions to the app. Afterwards if you launch your espresso test suite, the ui won't ask you again to grant them.
I am updating my app to work with the new Android Marshmallow permission framework and it looks like it's enough for the user to grant the ACCESS_FINE_LOCATION permission at runtime for the app to work fine. This is what I do:
public static final String[] runtimePermissions = { permission.ACCESS_FINE_LOCATION };
public static final int LOCATION_PERMISSION_IDENTIFIER = 1;
and further down the class:
public static boolean checkConnectionPermission(final Context context) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (context.checkSelfPermission(permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
return true;
}
else {
((Activity) context).requestPermissions(runtimePermissions,
LOCATION_PERMISSION_IDENTIFIER);
return false;
}
}
// since in API levels below M the permission is granted on
// installation,
// it is considered a given that the permission has been granted since
// the
// app is running. So we return true by default
else {
return true;
}
}
I am just concerned that I am overlooking something that could cause trouble in the future (the app is near-production) with Security Exception(s).
I guess my ultimate question is: does granting FINE_LOCATION somehow auto-grant COARSE_LOCATION too?
Sort of already answered here.
If I have ACCESS_FINE_LOCATION already, can I omit ACCESS_COARSE_LOCATION?
Some additional information, you should look at permission group.
https://developer.android.com/preview/features/runtime-permissions.html
They are in the same permission group. If you really want to play safe, just include both in your manifest, and request them on need. It would be transparent to user.
According to this blog post, if you have specified both in the manifest and the user has granted you one, then when you ask for the other it will be automatically granted (since they're both in the same permission group).
That means that if you have been granted COARSE_LOCATION and then ask for FINE_LOCATION you can get it without the user being prompted, but the catch is that you still have to specify both in the manifest.
Apps can actually register for two types of location updates. Fine grained, which uses the Global Positioning System to get the device’s location accurate to within a few meters, vs coarse-grained, which uses the Wifi, Cell towers and other data available to the device to get a rough (accurate to 10s of meters) location of the device.
Choice is yours.