Sistema de permissões do Android 6.0

Posted by rlecheta on outubro 02, 2015
Android, Tutorial

Olá pessoal, este post visa explicar um recurso importante do Android 6.0 (Marshmallow) que se você não conhecer pode quebrar seu aplicativo.

A partir do Android 6.0 (API 23) os aplicativos precisam pedir permissões em tempo de execução para utilizar determinadas APIs, como por exemplo: escrever no sdcard, câmera, localização, etc.

Farei um resumo aqui, mas recomendo que você leia a documentação oficial para pegar todos os detalhes:

http://developer.android.com/guide/topics/security/permissions.html

http://developer.android.com/training/permissions/index.html

  • Caso o código seja compilado com a API 22 (targetSdkVersion 22), o comportamento antigo ainda se aplica, ou seja, o usuário precisará aceitar todas as permissões no momento da instalação do aplicativo.
  • Caso o código seja compilado com a API 23 (targetSdkVersion 23), o aplicativo será instalado diretamente sem confirmar as permissões. Porém, em tempo de execução o aplicativo deverá solicitar a permissão do usuário para utilizar as funcionalidades restritas. Somente quando o usuário aceitar a permissão, o aplicativo pode usar a funcionalidade desejada.

As permissões são divididas entre normais e perigosas (dangerous). As permissões normais, exemplo: acessar a internet, bluetooth, etc, podem ser acessadas diretamente sem a confirmação do usuário. Já as permissões perigosas, como escrever no sdcard, acessar a localização, utilizar a câmera, etc, precisam da aprovação do usuário.

Para maiores detalhes sobre a lista de permissões, recomendo ler a documentação oficial:

http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous

Dito isso, vamos partir para a prática. Sempre que precisar utilizar alguma funcionalidade restrita precisamos verificar se o usuário já autorizou o acesso. O código a seguir mostra como validar se o usuário permitiu o aplicativo escrever no cartão de memória.

boolean ok = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;

Caso o retorno seja verdadeiro, podemos escrever um arquivo no cartão de memória, caso contrário precisamos pedir a permissão com o método requestPermissions(activity,permissoes,requestCode). Isso é feito com o seguinte template de código:

 
// Se não possui permissão
if (ContextCompat.checkSelfPermission(thisActivity,WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
      // Verifica se já mostramos o alerta e o usuário negou na 1ª vez.
      if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
           // Caso o usuário tenha negado a permissão anteriormente, e não tenha marcado o check "nunca mais mostre este alerta"
           // Podemos mostrar um alerta explicando para o usuário porque a permissão é importante.
      } else {
          // Solicita a permissão
          ActivityCompat.requestPermissions(thisActivity,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},0);
      }
} else {
      // Tudo OK, podemos prosseguir.
}

Note que este código utiliza as classes ContextCompat e ActivityCompat para manter a compatibilidade, pois estes métodos de solicitar permissões somente existem no Android 6.0.

Depois de solicitar a permissão com o método requestPermissions(activity,permissoes,requestCode), o usuário verá um alerta para aceitar ou não a permissão, conforme a figura abaixo.

18.004

O resultado da decisão do usuário será entregue via o método onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults), que recebe como parâmetro o código da chamada (requestCode), a lista de permissões e a lista de respostas do usuário.

@Override
public void onRequestPermissionsResult(int requestCode,String permissions[], int[] grantResults) {
 switch (requestCode) {
      case 0: {
      if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)      {
           // Usuário aceitou a permissão!
      } else {
           // Usuário negou a permissão.
           // Não podemos utilizar esta funcionalidade.
      }
      return;
     }
   }
}

Basicamente é isso!

Entenda que estas solicitações devem ser feitas a medida que você for usando o aplicativo. Por exemplo, somente quando for necessário obter a localização (GPS) você deverá fazer todo esta verificação. E lembre-se que caso o usuário negue a permissão, você deverá desabilitar as funcionalidades, pois qualquer tentativa de utilizar a API neste caso resultaria em uma SecurityException.

Dadas estas explicações, vamos fazer um exemplo prático.

Se você possui o livro Google Android 4ª edição poderá ver que alguns exemplos podem não funcionar no Android 6.0. Na verdade se você compilar o projeto com targetSdkVersion 22 não teremos problemas, pois neste caso, o controle de permissões será feito no momento da instalação, como era antes. Mas às vezes, ao pegar os exemplos do livro, você pode alterar o código para targetSdkVersion 23 (Android 6.0), e neste caso alguns exemplos podem parar de funcionar devido ao erro de permissão SecurityException .

Para brincar um pouco, abra o projeto HelloContatos do capítulo 23 (Agenda e Contatos) dos exemplos do livro Google Android 4ª edição. Como você deve saber, este exemplo lista os contatos da agenda. Este app inclusive mostra como adicionar alguns contatos na agenda (Donald, Mickey e Pateta).

contatos

Mas agora altere o arquivo app/build.gradle para compilar o projeto com API 23.

Ao executar o projeto em um dispositivo com Android 6.0, o aplicativo vai travar, mostrando a famosa mensagem Force Close.

b

Como de costume, sempre que o aplicativo trava devemos olhar os logs no LogCat.

Neste caso o erro foi uma SecurityException (permissão negada).

Screen Shot 10-02-15 at 12.07 AM

Este erro já era esperado, então vamos corrigí-lo.

Como o projeto que lista os contatos utiliza as permissões READ_CONTACTS e WRITE_CONTACTS, precisamos pedir a permissão do usuário para utilizar a agenda de contatos.

Para facilitar a codificação, criei a classe PermissionUtils (use ela como quiser, é apenas uma sugestão). A formatação do código ficou meio zoada, então faça o download do código-fonte do arquivo aqui. PermissionUtils


<pre>package br.com.livroandroid.contatos;

import android.app.Activity;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;

/**
 * Sistemas de permissão do Android 6.0
 * <p/>
 * http://developer.android.com/preview/features/runtime-permissions.html
 */
public class PermissionUtils {

    /**
     * Solicita as permissões
     */
    public static boolean validate(Activity activity, int requestCode, String... permissions) {
        List<String> list = new ArrayList<String>();
        for (String permission : permissions) {
            // Valida permissão
            boolean ok = ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED;
            if (! ok ) {
                list.add(permission);
            }
        }
        if (list.isEmpty()) {
            // Tudo ok, retorna true
            return true;
        }

        // Lista de permissões que falta acesso.
        String[] newPermissions = new String[list.size()];
        list.toArray(newPermissions);

        // Solicita permissão
        ActivityCompat.requestPermissions(activity, newPermissions, 1);

        return false;
    }
}</pre>

O método validate(…) recebe uma lista de permissões e valida se o usuário já concedeu o acesso. Caso o app ainda não tenha perguntado ao usuário, a acesso é solicitado pelo método requestPermissions(…).

Na MainActivity do projeto HelloContatos, basta adicionar estas linhas de código para validar a permissão e solicitá-la caso necessário:


// Solicita as permissões
 String[] permissoes = new String[]{
 Manifest.permission.READ_CONTACTS,
 Manifest.permission.WRITE_CONTACTS,
 };
 PermissionUtils.validate(this, 0, permissoes);

O código-fonte completo pode ser visto a seguir. Download do fonte: MainActivity

Bom, é isso :-). Se fizermos essa chamada no código vai dar boa. Ao executar o projeto no emulador do Android 6.0, vereremos o seguinte alerta solicitando a permissão do usuário para utilizar a API da agenda de contatos.

alerta

Se o usuário aceitar as permissões, tudo continuará bem e o aplicativo poderá ler os contatos.

Caso contrário, você terá que fazer algumas validações no código (if) para não utilizar as APIs que o usuário não aprovou.

Neste exemplo que fiz, caso o usuário negue a permissão, o aplicativo vai mostrar uma mensagem e sair.

nao

É isso pessoal!

Espero que este post tenha ajudado a entender um pouco sobre a nova API de permissões do Android 6.0.

Lembre-se de atualizar os seus aplicativos e validar no código se as permissões foram concedidas.

E por último, tenha atenção, pois o usuário pode revogar as permissões a qualquer momento, ou seja, ele pode alterar as configurações para permitir ou não o acesso a determinada API.

A figura abaixo mostra as configurações de um aplicativo, mostrando as opções para o usuário revogar as permissões. Portanto, lembre-se de sempre validar se a permissão foi concedida ou não antes de usar as APIs restritas, pois o usuário pode mudar as configurações a qualquer momento.

permi

Links oficiais, recomendado o estudo (leia sobre as boas práticas).

10 Comments to Sistema de permissões do Android 6.0