Android permissions — Разрешения в приложениях

Одна из основных проблем безопасности персональных устройств это доступ приложений к данным пользователя. В системе android каждое приложение обязано сообщить какие разрешения ему потребуются.

Если в старых версиях Android пользователь мог видеть какие разрешения требуются приложению только в момент установки и далее не мог ими управлять, то начиная с 6-й версии разработчик приложения должен реализовать запрос разрешения у пользователя самостоятельно, точнее запрос необходимо реализовывать только для «опасных» разрешений.

Ниже приведены списки нормальных и опасных разрешений:

Разрешения из списка «Normal»

ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS

Разрешения из списка «Dangerous»

READ_CALENDAR
WRITE_CALENDAR
CAMERA
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
RECORD_AUDIO
READ_PHONE_STATE
READ_PHONE_NUMBERS 
CALL_PHONE
ANSWER_PHONE_CALLS 
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
BODY_SENSORS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

Давайте рассмотрим на примере, как можно реализовать проверку и запрос. Для начала проверим, есть ли у нас требуемые разрешения. Сделать это можно из любого потомка Context с помощью метода checkSelfPermission

Conext class
Абстрактный метод в классе Context

Довольно удобно использовать метод onCreate вашей Activity допустим добавив в него следующий код:

if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M) {
    boolean permissionGranted = this.checkSelfPermission(
                           Manifest.permission.ACCESS_FINE_LOCATION) ==
                           PackageManager.PERMISSION_GRANTED);
    if (permissionGranted) {
         LOCATION_PERMISSION_GRANTED = true;
    } else {
         requestPermissions(
            new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
            LOCATION_PERMISSION_REQUEST_CODE);
     }
} else {
     // Версия меньше 6-ой, запрос разрешений не требуется
     LOCATION_PERMISSION_GRANTED = true;
}

Или можно реализовать проверку используя статичный метод класса ContextCompat

if(ContextCompat.checkSelfPermission(myActivity, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
    // Permission is not granted
}

Переменную вроде PERMISSION_GRANTED можно вынести в поле класса и в дальнейшем иметь к ней доступ на протяжении всего жизненного процесса Activity

Осталось запросить требуемые разрешения. Тут так-же можно использовать два варианта:

  1. Использовать метод нашей Activity например myActivity.requestPermissions(), данный метод актуален и для объектов класса ActivityCompat
  2. Использовать статичный метод ActivityCompat.requestPermissions(), первым параметром которого будет объект класса Activity. Это удобно если вам нужно реализовать отдельный касс, выполняющий запрос разрешений.
public static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
...
@RequiresApi(api = Build.VERSION_CODES.M)
public void request() {
    this.requestPermissions(new String[]
                               {Manifest.permission.ACCESS_FINE_LOCATION}, 
                            LOCATION_PERMISSION_REQUEST_CODE);
    }

или

ActivityCompat.requestPermissions(myActivity,
                new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                LOCATION_PERMISSION_REQUEST_CODE);

В примерах использована переменная LOCATION_PERMISSION_REQUEST_CODE, она служит для идентификации нашего запроса. Значение равное значению этой переменной получает метод onRequestPermissionsResult() нашей Activity. Осталось только проверить разрешил нам пользователь доступ к той функции которую мы запрашивали или нет:

@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
     if (requestCode == LOCATION_PERMISSION_REQUEST_CODE &&
         grantResults.length == 1) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            LOCATION_PERMISSION_GRANTED = true;
            ...
        } else {
            LOCATION_PERMISSION_GRANTED = false;
            ...
        }
    }
 ...
}

Такой механизм обработки разрешений безусловно более сложен, однако вместе с тем он и более гибкий.

Поделиться этим материалом