Soulesidibe     About     Feed

Android 6.0 Marshmallow est la! (Part 2)

L’un des changements les plus importants de la release 6.0 est le nouveau mode de fonctionnement des permissions. A partir de Android Marshmallow, lors de l’installation d’une nouvelle application sur le playstore, le user n’aura plus à autoriser une liste enorme de permissions. Les demandes d’autorisations se feront au runtime et au besoin. Exemple, l’application a besoin d’écrire sur la mémoire externe, il lui suffira de faire la demande à l’utilisateur via une AlertDialog gérée par le systeme et il pourra accepter ou pas la demande du développeur. Côte client, c’est très intéressant vu qu’on a la main sur qui a droit de faire quoi.
Côte développeur, en fonction des categories des permissions qu’on a defini dans le Manifest, on aura plus ou moins du boulot pour rendre compatible nos applications. On verra ensemble les etapes permettant de rendre compatible nos apps sans trop de difficultés.

Les permissions

Je ne vais pas revenir sur l’importance des permissions. Parcontre, il est important de connaitre les différents groupes de permissions. La raison est simple. Avec Android 6.0 quand on fait la demande d’une permission, le syteme nous donne acces au groupe ou pas. Ce qui permet au user de ne pas autoriser un par un une liste de permissions appartenant au même groupe. Prenons le cas d’une application souhaitant gérer nos contacts. Pour récuperer la liste des contacts, elle a besoin de la permission de lecture des contacts donc elle demande l’accès a la permission READ_CONTACTS. A un certain moment, elle veut modifier un contact, elle a besoin pour cela de la permission d’écriture. Elle demande le droit d’accès à la permission WRITE_CONTACTS et comme elle avait déjà l’accès au groupe CONTACTS, elle a automatiquement le droit de modifier. Le user n’a pas à autoriser une seconde fois. Ceci simplifie grandement les choses pour le user mais aussi pour le developpeur.

Les groupes de permissions

permissions groups

Comment ca marche

En théorie

Tout d’abord, avec la multitude de versions d’android disponible sur le marché, nous développeurs devons penser à la retro compatibilité de nos apps(oui rien de bien nouveau…). La déclaration des permissions utilisées dans le manifest ne change pas rien que pour éviter d’avoir des SecurityException pour les versions avant la 6.0.

A l’installation d’une application une liste limitée de permissionq est granted à l’application. Le développeur n’aura pas à faire la demande pour avoir l’accès. Il s’agit des permissions dont le niveau protection est PROTECTION_NORMAL comme par exemple la permission internet. Evidemment, on a aussi les permissions PROTECTION_DANGEROUS qui sont accessibles qu’après autorisation du user au runtime. Comme récupèrer les contacts. Pour plus d’infos sur les niveaux de protection voir la doc offcicielle. La liste des permissions et leur niveau de protection y est dispo!

Quand l’app a besoin d’effectuer une action qui requiert une autorisation, il fait la demande au systéme qui affiche ou pas une Dialog(en fonction qu’il est deja l’autorisation ou pas) à l’utilisateur qui peut autoriser ou pas la demande d’accès. Le systéme renvoie un callback à l’app pour lui notifier de la réponse du user. Ce nouveau modèle change le mode de fonctionnement de nos apps et voici quelques points à prendre en compte:

En pratique

Voici un petit projet pour illustrer un peu le concept. C’est une app qui affiche dans le logcat le nom du user du device. Simple. Pour lancer le projet, il faut deja mettre à jour le SDK pour récuperer android 6.0. Puis creer un AVD tournant sous Marshmallow. Le projet est disponible sur GitHub

Pour afficher le profile du user, on utilise le ContentProvider qui expose tout ce qui est en rapporte avec les contacts:

// Sets the columns to retrieve for the user profile
	String[] mProjection = new String[]{
		ContactsContract.Profile.DISPLAY_NAME_PRIMARY,
	};

	// Retrieves the profile from the Contacts Provider
	mProfileCursor = getContentResolver().query(
	        ContactsContract.Profile.CONTENT_URI,
	        mProjection,
	        null,
	        null,
	        null);

	if (mProfileCursor == null) {
	    return;
	}
	mProfileCursor.moveToFirst();
	while (!mProfileCursor.isAfterLast()) {
	    Log.d("Permission Test", "Display Name >> " + mProfileCursor.getString(0));
	    mProfileCursor.moveToNext();
	}

Pas la peine de revenir sur cette partie. Ce n’est pas le but. Ce qui est important ici c’est qu’il faut avoir la permission android.permission.READ_CONTACTS pour pouvoir lire le profil du user. Donc pour ne pas avoir de SecurityException, il suffit de rajouter <uses-permission android:name="android.permission.READ_CONTACTS" /> au niveau du manifest. La tous les devices sous android API 22 et above marcheront. Maintenant ce n’est pas le cas pour l’API 23 vu que la permission READ_CONTACTS est considerée comme PROTECTION_DANGEROUS. L’app doit demander la permission au runtime avant de pouvoir proceder au traitement. Ca nous fait du code de ce type à avoir un peu partout dans notre code :

if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
    //permission granted at install time        
}else{
	//Check if permission granted
}

Ce qui n’est pas top pour la codebase de notre projet. Mais avec la support library (23.0.0) on n’a pas a vérifier cela. On pourra directement vérifier si on a le droit de lire les contacts et la support library s’occupera de tester si on est sous android 6 ou pas. Pour verifier une permission, le code sera le même à chaque fois que l’on desire checker une permission:

if(checkSelfPermission(Manifest.permission.READ_CONTACTS)
    != PackageManager.PERMISSION_GRANTED) {
    if (shouldShowRequestPermissionRationale
    	(Manifest.permission.READ_CONTACTS)) {
    	//Explain why read contact permission is required
    }

    requestPermissions(
    	new String[]{Manifest.permission.READ_CONTACTS},
		PERMISSION_REQUEST_CONTACTS
	);
} else {
	//Permission read contact granted
    getProfile();
}

Detaillons un peu ce code: checkSelfPermission(Manifest.permission.READ_CONTACTS) permet de vérifier si on a la permission READ_CONTACTS. Si c’est le cas la méthode retourne un PERMISSION_GRANTED. On peut proceder à la lecture des informations du contact sinon un PERMISSION_DENIED nous oblige à faire une demande au syteme via le methode requestPermissions(String[], int). Le syteme affiche donc une AlertDialog au user lui demandant d’autoriser ou pas la demande: Permission request

Le resulat de la demande sera traité dans le onRequestPermissionsResult :

if (requestCode == PERMISSION_REQUEST_CONTACTS) {
    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // Permission has been granted. 
        getProfile();
    } else {
        // Permission request was denied.
    }
}

La constante PERMISSION_REQUEST_CONTACTS est fournie dans la methode requestPermissions et permet d’identifer la demande.
Il peut arriver que le user deny notre demande. En plus de désactiver la fonctionnalité dont dépend cette permission, il faut pouvoir expliquer au user le pourquoi de cette permission. La méthode shouldShowRequestPermissionRationale() nous indique le bon moment pour faire cela.
Les methodes requestPermissions(), onRequestPermissionsResult(), shouldShowRequestPermissionRationale() ont été ajoutées à android 6.0 donc ne sont pas dispo pour 5.x et les autres. Mais encore ici, la support library nous gére cela. Il suffit d’utiliser AppCompatActivity ou Fragment de la support-v4 pour y avoir accès.

Mettre à jour une application existente

Le cas traité plus haut concerne plus une nouvelle app. Dans le cas d’une application qui target android 5.x, cela peut être un peu moins simple à gérer. Mais en suivant les étapes suivantes, on peut le s’en sortir relativement bien.

Tout d’abord il faut identifier au niveau du manifest toutes les permissions déclarées. C’est aussi locasion de voir la pertinence de ces permissions pour l’app. Il est courant de déclarer une tonne de permissions dont on n’a pas le besoin. Ci-dessous la liste des permissions de niveau normal et dangereux. Cela vous permettra d’ientifier assez vite quelles permissions devront être revues côté code pour prendre en compte android 6.0. Après, il suffira de dérouler ce qui a été expliqué plus haut et tout devrait bien se passer :).

Liste des permissions de niveau PROTECTION_NORMAL

Ces permissions peuvent être utilisées sans procéder à la demande au runtime. Elle seront granted automatiquement à l’installation de l’app:

Liste des permissions de niveau PROTECTION_DANGEROUS

La liste des permissions que vous devez obligatoirement vérifier au runtime quand votre app tourne sous android 6.0 et plus

PROTECTION_DANGEROUS

Bonus

Quelques liens et videos utils pour aller en profondeur et surtout mieux comprendre le concept:

C’est tout pour les permissions. C’est un sujet assez important à ne pas négliger si on souhaite que notre app supporte android 6.0. Il faut prendre le temps de comprendre les choses et de les tester. Je suis ouvert à toutes questions, suggestions en commentaire ou sur twitter(@soulesidibe).

comments powered by Disqus