Android Tutorial Runtime Permissions (Android 6.0+ Permissions)

Status
Not open for further replies.
1. Edit: In B4XPages the permission result event signature is: Wait For B4XPage_PermissionResult (Permission As String, Result As Boolean)
2. Important to read: https://www.b4x.com/android/forum/threads/android-jar-targetsdkversion-minsdkversion.87610/#content



If the targetSdkVersion is lower than 23 then the standard permissions system will be used on all devices including Android 6+, however soon all Google Play apps will need to set the targetSdkVersion to 26+.

B4A v6.0 adds support for runtime permissions. The nice thing about runtime permissions is that the user is not asked for any permission when they install your application from Google Play. Instead they will be asked to approve "dangerous" permissions at runtime.

Luckily most permissions are not considered dangerous. You can see the list of permissions that are considered dangerous here: https://developer.android.com/guide/topics/permissions/overview.html#permission-groups



upload_2016-6-8_14-48-39.png


The CheckAndRequest method can only be called from an Activity.
There is another method named Check that only tests whether the permission has already been approved or not. This method can be called from any module.
It might be tempting to first test whether there is a permission and only if there is no permission call CheckAndRequest. However it will just make the program flow more complicated as you will need to deal with all cases anyway.
As a general rule, you shouldn't call RuntimePermissions.Check from an Activity. It will be simpler to always call CheckAndRequest.

Listing the permissions

Not many are aware to the fact that you can see the project permissions by clicking on the List Permissions button that is inside the Logs tab:
A very common mistake is to request a permission at runtime that is not listed in the "permissions dialog". It will not work.
SS-2016-06-08_15.06.00.png


The dangerous permissions are marked with * (in B4A v6+).
You don't need to ask for non-dangerous permissions.

READ_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE

This is the most common dangerous permission. It is added automatically when you use File.DirDefaultExternal or File.DirRootExternal.
However there is a simple workaround for this.

1. Use RuntimePermissions.GetSafeDirDefaultExternal("") instead of File.DirDefaultExternal. The parameter passed is an optional subfolder that will be created under the default folder.

2. Add this code to the manifest editor:
B4X:
AddManifestText(
<uses-permission
  android:name="android.permission.WRITE_EXTERNAL_STORAGE"
  android:maxSdkVersion="19" />
)
The explanation for this is that GetSafeDirDefaultExternal doesn't require any permission on Android 4.4+ (API 19) and requires the WRITE_EXTERNAL_STORAGE on older versions. The code above adds the permission to older devices.

You only need to deal with WRITE_EXTERNAL_STORAGE at runtime if you need access to a folder other than the app's default external folder.

Notes & tips:

- You can only request permissions that were declared in the manifest file (this is usually taken care by the compiler).
- Testing the permissions can be confusing as the user only needs to give permissions once. The solution is to uninstall the app from the device. Click on Ctrl + P (clean project) in the IDE and run again.
- The user actually approves groups of permissions. So if the user has approved the read contacts permission they will not be asked to approve the write contacts permission.
- Once you've uploaded your app to Google Play with targetSdkVersion set to 23 you cannot downgrade the target version back.
- Some Android 4.4 (API 19) devices do not allow access to RuntimePermissions.GetSafeDirDefaultExternal without explicit permission although they should. It it therefore recommended to set android:maxSdkVersion to 19 in the version based permission. It was previously set to 18.
 
Last edited:

scsjc

Well-Known Member
Licensed User
Longtime User
Hello, i'm trying the runtime on 6.0.1,
on this line : rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
get this error :


B4X:
LogCat connected to: 7091585a
--------- beginning of system
--------- beginning of main
** Service (starter) Create **
Error occurred on line: 26 (Starter)
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.ref.WeakReference.get()' on a null object reference
    at anywheresoftware.b4a.objects.RuntimePermissions.CheckAndRequest(RuntimePermissions.java:62)
    at myworldapp.people.com.starter._service_create(starter.java:267)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:703)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:337)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:247)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:134)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:153)
    at myworldapp.people.com.starter.onCreate(starter.java:54)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:3807)
    at android.app.ActivityThread.access$2100(ActivityThread.java:221)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1882)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:158)
    at android.app.ActivityThread.main(ActivityThread.java:7224)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
** Service (starter) Start **

can help me?
thanks!!!!
 

MarcoRome

Expert
Licensed User
Longtime User
Hello, i'm trying the runtime on 6.0.1,
on this line : rp.CheckAndRequest(rp.PERMISSION_WRITE_EXTERNAL_STORAGE)
get this error :


B4X:
LogCat connected to: 7091585a
--------- beginning of system
--------- beginning of main
** Service (starter) Create **
Error occurred on line: 26 (Starter)
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.ref.WeakReference.get()' on a null object reference
    at anywheresoftware.b4a.objects.RuntimePermissions.CheckAndRequest(RuntimePermissions.java:62)
    at myworldapp.people.com.starter._service_create(starter.java:267)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:703)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:337)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:247)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:134)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:153)
    at myworldapp.people.com.starter.onCreate(starter.java:54)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:3807)
    at android.app.ActivityThread.access$2100(ActivityThread.java:221)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1882)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:158)
    at android.app.ActivityThread.main(ActivityThread.java:7224)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
** Service (starter) Start **

can help me?
thanks!!!!
I dont think that you can use this is a service. Try same code in Activity
 

M.LAZ

Active Member
Licensed User
Longtime User
- Once you've uploaded your app to Google Play with targetSdkVersion set to 23 you cannot downgrade the target version back.
that's what happend for me...i set it to 25 and coudn't downgrade it back..
so i used the Runtime Permissions for sdk > 23
every thing is ok now ,, my app works fine
but when i trying upload to google store give me this error message :

Upload failed
Duplicate declarations of permission android.permission.WRITE_EXTERNAL_STORAGE with different maxSdkVersions.

my manifest file :

AddManifestText(
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="25"/>
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>

)


AddManifestText(
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")

SetApplicationAttribute(android:theme, "@android:style/Theme.Holo.Light")
SetActivityAttribute(FastRandomChart, android:screenOrientation, "landscape")
SetActivityAttribute(hba1cChart, android:screenOrientation, "landscape")

'AddPermission(android.permission.READ_EXTERNAL_STORAGE)
RemovePermission (android.permission.READ_PHONE_STATE)
RemovePermission (android.permission.WRITE_SETTINGS)

'SetApplicationAttribute(android:theme, "@android:style/Theme.Holo")
'End of default text.



'************ Google Play Services Base ************
AddApplicationText(
<activity android:name="com.google.android.gms.common.api.GoogleApiActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="false"/>
<meta-data
android:name="com.google.android.gms.version"
android:value="@Integer/google_play_services_version" />
)
'************ Google Play Services Base (end) ************



'************ Firebase Base ************
CreateResourceFromFile("google-services", "google-services.json")
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.WAKE_LOCK)
AddPermission(com.google.android.c2dm.permission.RECEIVE)
AddPermission(${applicationId}.permission.C2D_MESSAGE)
AddManifestText( <permission android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />)
AddApplicationText(
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
android:enabled="true">
<intent-filter>
<action android:name="com.google.android.gms.measurement.UPLOAD"/>
</intent-filter>
</receiver>

<service
android:name="com.google.android.gms.measurement.AppMeasurementService"
android:enabled="true"
android:exported="false"/>
<provider
android:authorities="${applicationId}.firebaseinitprovider"
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:exported="false"
android:initOrder="100" />
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
android:enabled="true">
<intent-filter>
<action android:name="com.google.android.gms.measurement.UPLOAD"/>
</intent-filter>
</receiver>

<service
android:name="com.google.android.gms.measurement.AppMeasurementService"
android:enabled="true"
android:exported="false"/>
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
android:exported="false" />


<service
android:name="com.google.firebase.iid.FirebaseInstanceIdService"
android:exported="true">
<intent-filter android:priority="-500">
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
)
'************ Firebase Base (end) ************

'************ Firebase Notifications ************
AddApplicationText(
<service
android:name="com.google.firebase.messaging.FirebaseMessagingService"
android:exported="true">
<intent-filter android:priority="-500">
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name="anywheresoftware.b4a.objects.FirebaseNotificationsService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
)
'************ Firebase Notifications (end)************

'************ Firebase Analytics ************
AddApplicationText(
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
android:enabled="true">
<intent-filter>
<action android:name="com.google.android.gms.measurement.UPLOAD"/>
</intent-filter>
</receiver>

<service
android:name="com.google.android.gms.measurement.AppMeasurementService"
android:enabled="true"
android:exported="false"/>

<service android:name="com.google.firebase.crash.internal.service.FirebaseCrashReceiverService"
android:process=":background_crash"/>

<service android:name="com.google.firebase.crash.internal.service.FirebaseCrashSenderService"
android:process=":background_crash"/>
)
'************ Firebase Analytics (end) ************

'************ Firebase Ads ************
AddApplicationText(
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />
<activity android:name="com.google.android.gms.ads.purchase.InAppPurchaseActivity"
android:theme="@style/Theme.IAPTheme"/>
)
'************ Firebase Ads (end) ************

'************ Firebase Auth ************
AddApplicationText(
<activity android:name="com.google.android.gms.auth.api.signin.internal.SignInHubActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents="true"
android:exported="false" />

<service
android:name="com.google.android.gms.auth.api.signin.RevocationBoundService"
android:exported="true"
android:permission="com.google.android.gms.auth.api.signin.permission.REVOCATION_NOTIFICATION" />
)
'************ Firebase Auth (end) ************
 

Roberto P.

Well-Known Member
Licensed User
Longtime User
Hi to all,
I have to manage the automatic positioning device with GPS library and I have to be able to activate the positioning enabled automatically with Runtime Permissions, from the application, and without the user's permission.

How can I do it?

Thank you
 

tracklocator

Member
Licensed User
Hello @Erel just started in the app development I am doing the map example and the fine location permissions. I have not had success already the point:

If <user has already approved> Or <older device> Then
Activity_PermissionResult (Permission, True)
Else
ShowDialog
Activity_PermissionsResult (Permission, Dialog result)
End If

The example code I use is as follows:

#Region Project Attributes
#ApplicationLabel: Maps Example
#VersionCode: 1
#VersionName:
'SupportedOrientations possible values: unspecified, landscape or portrait.
#SupportedOrientations: unspecified
#CanInstallToExternalStorage: False
#AdditionalJar: com.google.android.gms:play-services-maps

#End Region

#Region Activity Attributes
#FullScreen: False
#IncludeTitle: True
#End Region

'Activity module
Sub Process_Globals
Dim rp As RuntimePermissions
End Sub

Sub Globals
Private gmap As GoogleMap
Private MapFragment1 As MapFragment
End Sub

Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("1")
If MapFragment1.IsGooglePlayServicesAvailable = False Then
ToastMessageShow("Please install Google Play Services.", True)
End If
End Sub

Sub MapFragment1_Ready
gmap = MapFragment1.GetMap
rp.CheckAndRequest(rp.PERMISSION_ACCESS_FINE_LOCATION)
Dim m1 As Marker = gmap.AddMarker(10, 30, "test")
m1.Snippet = "This is the snippet"
End Sub


Sub Activity_PermissionResult (Permission As String, Result As Boolean)
If Permission = rp.PERMISSION_ACCESS_FINE_LOCATION Then
If Result Then
gmap.MyLocationEnabled = Result
End If
End If
End Sub

'End of example code

The error I have is the following:


** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
java.lang.RuntimeException: java.lang.SecurityException: my location requires permission ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION
at anywheresoftware.b4a.objects.MapFragmentWrapper$1.onMapReady(MapFragmentWrapper.java:196)
at com.google.android.gms.maps.MapFragment$zza$1.zza(Unknown Source)
at com.google.android.gms.maps.internal.zzt$zza.onTransact(Unknown Source)
at android.os.Binder.transact(Binder.java:387)
at zu.a:)com.google.android.gms.DynamiteModulesB:82)
at maps.ad.t$5.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7325)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Caused by: java.lang.SecurityException: my location requires permission ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION
at maps.ad.t.c(Unknown Source)
at xj.onTransact:)com.google.android.gms.DynamiteModulesB:274)
at android.os.Binder.transact(Binder.java:387)
at com.google.android.gms.maps.internal.IGoogleMapDelegate$zza$zza.setMyLocationEnabled(Unknown Source)
at com.google.android.gms.maps.GoogleMap.setMyLocationEnabled(Unknown Source)
at anywheresoftware.b4a.objects.MapFragmentWrapper$1.onMapReady(MapFragmentWrapper.java:183)
... 12 more

I have tried different ways but I still do not make it work.

I hope you can help me

Thanks for your time.
 

hanyelmehy

Active Member
Licensed User
Longtime User
i use targetSdkVersion=22 and add
B4X:
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE
when run app on android 6+ and use File.Copy to RootExternal i get Permission denied
 

hanyelmehy

Active Member
Licensed User
Longtime User
How did you add this permission?
Can you post your manifestcode from the manifesteditor please
i use
B4X:
AddManifestText(
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="22"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
AddPermission("android.permission.WRITE_EXTERNAL_STORAGE")
AddPermission("android.permission.READ_EXTERNAL_STORAGE")
 

DonManfred

Expert
Licensed User
Longtime User
AddPermission("android.permission.WRITE_EXTERNAL_STORAGE")
AddPermission(
"android.permission.READ_EXTERNAL_STORAGE")
The Permissions are not Strings!

Use this permissions.
AddPermission(android.permission.READ_EXTERNAL_STORAGE) ' Allows an application to read from external storage.
AddPermission(android.permission.WRITE_EXTERNAL_STORAGE) ' Allows an application to write to external storage.

See this list for all permissions
https://www.b4x.com/android/forum/threads/addpermission-a-list-of-permissions-in-android.39401/
 

DonManfred

Expert
Licensed User
Longtime User
Status
Not open for further replies.
Top