When I was working on creating a tello game, I faced the problem of permissions in android 6 and above. All permissions were requested when the app launches the first time instead of when the user downloads the app. Not only that, but the permissions are requested individually and not altogether like it used to be, which showed me some permissions we didn’t even notice were there. After some research, I found some answers and problems. I hope that the following article will bring them closer.
Starting on Android 6, user permissions are requested during run time rather than during installation. According to Google, the change streamlines the installation and update process and allows fine tuning of the app’s functionalities: since the permissions are requested individually, the user can allow some and deny others. For example: an user can grant camera permission but deny GPS and the app may function seamlessly. Before the update, given that all the permissions were displayed grouped before installation, denying them would cancel the installation.
The Android Support Library provides us with methods of checking, requesting and dealing with permissions request responses. You can check more about it here. In order to fully understand the problem, we must learn a bit more about Android permissions, starting with protection levels.
Android system permission are divided into two protection levels: normal and dangerous.
Normal permissions are automatically granted without prompt, while dangerous permissions require user authorization via dedicated prompt.
Considering that the list of Android permissions might be quiet long, they have been grouped into Permission Groups based on which data or resource it requests access for.
Let’s take the “Contacts” permission group as an example. It contains three permissions: READ_CONTACTS, WRITE_CONTACTS and GET_ACCOUNTS. As you can see, all the permissions are directly related to the user contacts. Groups are extremely handy because if your app needs those three permissions, we don’t need to request each one individually. When we request any permission in the “Contacts” group, we are actually requesting a group permission. Given that, if an app is running on Android Marshmallow or above requests the READ_CONTACTS permission, the user grants it and later on it requests the WRITE_CONTACTS permission, no prompt will be shown to the user on the latter, because he/she already granted permission to the “Contacts” group. Hence the WRITE_CONTACTS permission shall be automatically granted.
Knowing what permissions are doing in android, we can understand how Unity handles permissions. One of the most common searches about Unity and Android permissions is “why is my app requesting permission to make and manage phone calls if I don’t have that on AndroidManifest.xml file?” and that’s exactly how we started our search. After some reading, we found out the READ_PHONE_STATE permission is the source of the problem.
We thought “we don’t need to read the phone state, why is that permission even in the AndroidManifest?” and proceeded to delete the permission request. To our surprise, even after deleting the entry, our app still requested the permission. We thought we were crazy for a second and started to dig in all the AndroidManifest files we have in our project (plugins) looking for that entry, but we couldn’t find it.
Then, after some more tests and research, we found the villain: SystemInfo.deviceUniqueIdentifier. This simple example shows us how Unity sometimes might be a bit too nosy and adds some permissions to our AndroidManifest on the build process. Some of Unity’s API calls require a permission to be granted to function rightfully. Unity trying to make your life easier, identifies when your application’s source code contains that call and adds the permission request to your AndroidManifest.
The deviceUniqueIdentifier call does exactly what it says: it returns the device’s unique identifier. This identifier is often used as an ID for guest users so the game can save its progress on the server, even though the user didn’t create an username. For example, this allows users to keep their progress after deleting the app from their phone. Given the premise that every phone has an unique, non-mutable identifier.
The problem lays on how the engine gets that identifier. There is no guaranteed way of getting a phone’s unique ID, but there are some solutions close enough and the IMEI is the most famous and used one. The IMEI is an — usually — unique identifier used to identify most mobile and satellite phones in the world and is generally used to block stolen phones around the globe.
The root of the problems is that to access the phone’s IMEI on an Android phone, you need the READ_PHONE_STATEpermission, which is part of the PHONE Permission Group. Now try to guess, how a request to that permission group is described. You’re right: “Allow this app to make and manage phone calls?”. That’s right. Just to access the IMEI, you must request permission to the entire PHONE group, which includes making and managing phone calls. It sounds like an overkill, but that’s how Android sets it up. Also Unity, trying to help us, automatically adds the permission when the deviceUniqueIdentifier call is present in code.
During my reseach about Android permissions, we noticed that some of the games and apps I played already made use of the runtime permissions, but on a less intrusive way. They only requested a permission when it was really necessary.After some digging, I found out that Unity doesn’t provide a way of doing that natively. The engine simply requests every single permission when the app starts and there is no way you can control that.
Since Unity lacks of a solution for this problem, we cought an alternative solution. We can’t control when Unity requests the permission, but we can skip permissions dialog adding the following line to the AndroidManifest.xml file:
<meta-data android:name=“unityplayer.SkipPermissionsDialog” android:value=“true”\\>
For now, we need to wait for official solution from Unity Technology: an API that implements new permission model, giving us control over runtime permission requests.
To help us create more free stuff, consider leaving a donation through Patreon where you can vote for the next asset pack and get access to future packs & articles earlier. Sharing knowledge is our passion. Unfortunately, keeping a team of professionals costs a lot that’s why we are constantly taking on new opportunities.
We’ll be publishing any news on the following outlets:
Thanks for reading!