Приемники вещания не работают в Android 6.0 Marshmallow

Я только что обновил свой Nexus 5 до Android 6, до сих пор мое приложение работало нормально, но теперь приемники вещания не работают. Что-то изменилось в новой версии? Вот код, который я пробовал, который работал в предыдущих версиях, но не в зефире -

Android Манифест

    <intent-filter >
        <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
    </intent-filter>
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<uses-permission android:name="android.permission.READ_SMS" ></uses-permission>

Приемник вещания

public String TAG ="someClass";
private static String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();
    if (action.equalsIgnoreCase(ACTION_SMS_RECEIVED)) {
        Log.d(TAG, "Received...");
    }
}

обслуживание

Broadcast_receiver broadcast_receiver = new Broadcast_receiver();
IntentFilter filter1 = new IntentFilter();
filter1.addAction("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(broadcast_receiver, filter1);

Аналогично, приемник вещания для PHONE_STATE также не работает.

3 ответа

Решение

Целевой уровень API вашего приложения - 23, то есть Android M (6.0). В Android M произошли огромные изменения, связанные с разрешениями пользователей. Вот хорошая статья, объясняющая изменения.

Как указано в Android - Запрос разрешений

Начиная с Android 6.0 (уровень API 23), пользователи предоставляют разрешения приложениям во время работы приложения, а не при установке приложения... Пользователь может отозвать разрешения в любое время...

Также заявлено, что:

Системные разрешения делятся на две категории, обычные и опасные:

  1. Обычные разрешения напрямую не влияют на конфиденциальность пользователя. Если ваше приложение указывает нормальное разрешение в своем манифесте, система автоматически предоставляет разрешение

  2. Опасные разрешения могут дать приложению доступ к конфиденциальным данным пользователя. Если вы указываете опасное разрешение, пользователь должен явно дать согласие на ваше приложение

Вот полные списки Опасных разрешений и Нормальных разрешений

Все это в основном означает, что вам нужно вручную запрашивать любое опасное разрешение, когда оно действительно необходимо.

Поскольку в вашем коде это может понадобиться несколько раз, вы можете создать метод многократного использования, который проверяет, было ли уже предоставлено конкретное разрешение, а если нет - запрашивать его у пользователя.

Вот пример:

Джава

public class PermissionManager {
    //A method that can be called from any Activity, to check for specific permission
    public static void check(Activity activity, String permission, int requestCode){
        //If requested permission isn't Granted yet 
        if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
            //Request permission from user
            ActivityCompat.requestPermissions(activity,new String[]{permission},requestCode);
        }
    }
}

Котлин

object PermissionManager {
    //A method that can be called from any Activity, to check for specific permission
    fun check(activity: Activity, permission: String, requestCode: Int) {
        //If requested permission isn't Granted yet
        if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
            //Request permission from user
            ActivityCompat.requestPermissions(activity, arrayOf(permission), requestCode)
        }
    }
}

Использование:

Джава

//Inside your activity:
//1. Define static constant for each permission request
public static final int REQUEST_CODE_FOR_SMS=1;
//2. When needed (for example inside .onStart event) use method PermissionManager.check for requested permission 
@Override
protected void onStart() {
    super.onStart();
    PermissionManager.check(this, Manifest.permission.RECEIVE_SMS, REQUEST_CODE_FOR_SMS);
}
//3. Handle User's response for your permission request
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if(requestCode==REQUEST_CODE_FOR_SMS){//response for SMS permission request
        if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
            //What to do if User allowed SMS permission
        }else{
            //What to do if user disallowed requested SMS permission
        }
    }
}

Котлин

//Inside your activity:
//1. Define static constant for each permission request
val REQUEST_CODE_FOR_SMS = 1
//2. When needed (for example inside .onStart event) use method PermissionManager.check for requested permission
override fun onStart() {
    super.onStart()
    PermissionManager.check(this, Manifest.permission.RECEIVE_SMS, REQUEST_CODE_FOR_SMS)
}

//3. Handle User's response for your permission request
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
    if (requestCode == REQUEST_CODE_FOR_SMS) {//response for SMS permission request
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //What to do if User allowed SMS permission
        } else {
            //What to do if user disallowed requested SMS permission
        }
    }
}

Замечания:

  1. Если вам нужно использовать PermissionManager.check внутри Fragment Например, используйте: getActivity() в качестве первого параметра.

  2. Ты можешь использовать checkSelfPermission внутри Service экземпляр, чтобы проверить, если какое-то разрешение уже предоставлено, но не requestPermissions просить это. Так как checkSelfPermission может быть использован для любого Context, но requestPermissions только для Activity

Зефир блокирует опасные разрешения.

Это не относится к перечисленному сценарию, но может помочь кому-то еще. Я продолжал приходить к этому ТАК за тем, почему некоторые из наших приемников вещания не работали. У нас есть настраиваемая настройка разрешения и было android:protectionLevel="dangerous", Изменил это на android:protectionLevel= "signature"и все начало работать.

Другие вопросы по тегам