SecurityException, вызывается при вызове WifiManager startScan

Я запускаю AlarmManager AlarmManager (setRepeating) для запуска wifi-сканирования (используя IntentService) каждые несколько минут. На большинстве устройств и в большинстве случаев нет проблем с этим. Однако на нескольких устройствах я получаю следующую ошибку (не удалось воспроизвести ошибку на любом тестовом устройстве. Это журнал сбоев с пользовательского устройства):

java.lang.RuntimeException: Unable to start service [email protected] with Intent { act=com.myapp.android.ACTION_PERFORM_WIFI_SCAN flg=0x4 cmp=com.myapp/com.mayapp.android.service.MyService (has extras) }: java.lang.SecurityException: Permission Denial: broadcast from android asks to run as user -1 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL or android.permission.INTERACT_ACROSS_USERS
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3021)
       at android.app.ActivityThread.-wrap17(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1443)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:148)
       at android.app.ActivityThread.main(ActivityThread.java:5415)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:725)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:615)
Caused by: java.lang.SecurityException: Permission Denial: broadcast from android asks to run as user -1 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL or android.permission.INTERACT_ACROSS_USERS
       at android.os.Parcel.readException(Parcel.java:1599)
       at android.os.Parcel.readException(Parcel.java:1552)
       at android.net.wifi.IWifiManager$Stub$Proxy.startScan(IWifiManager.java:1045)
       at android.net.wifi.WifiManager.startScan(WifiManager.java:1088)
       ...

Я создаю PendingIntent из своего приложения, поэтому не вижу причин для SecurityException, выкинутых из WifiManager (тем более, что это происходит редко).

IntentService, запущенный из кода PendingIntent, выглядит следующим образом:

mContext.registerReceiver(mWifiScanReceiver, new IntentFilter(
                    WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

boolean ok = mWifiManager.startScan();

Любые идеи о том, что может быть причиной этого?

Ответ 1

Это происходит из-за новых разрешений приложений для android m. См. Комментарий выше исходного кода wifimanager getScanResults() for api 23 -

/**
     * Return the results of the latest access point scan.
     * @return the list of access points found in the most recent scan. An app must hold
     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
     * in order to get valid results.
     */
public List<ScanResult> getScanResults() {
        try {
            return mService.getScanResults(mContext.getOpPackageName());
        } catch (RemoteException e) {
            return null;
        }
    }

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

  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Начиная с api 23 и далее вам требуется разрешение на доступ к пользовательскому местоположению для его использования. Я предлагаю вам использовать проверку разрешений на основе уровня api и начать выполнение только в том случае, если разрешения предоставлены. Что-то вроде этого -

if (Build.VERSION.SDK_INT >= 23) {
int hasReadLocationPermission = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
      if (hasReadLocationPermission != PackageManager.PERMISSION_GRANTED) {
        if (!ActivityCompat.shouldShowRequestPermissionRationale(HomeActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)) {
          showMessageOKCancel("You need to allow access to GPS",
              new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                  ActivityCompat.requestPermissions(HomeActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, GPS_ENABLE_REQUEST);
                }
              });
          return;
        }
        ActivityCompat.requestPermissions(HomeActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, GPS_ENABLE_REQUEST);
        return;
      }
      if (locationManager != null && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
        gotoGPSEnableScreen();
      } else {
        //Permissions granted and gps is on
        launchService(true);
      }
}

Далее, чтобы проверить результаты -

@Override
  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
      case GPS_ENABLE_REQUEST:
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
          if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            gotoGPSEnableScreen();
          }
        } else {
          launchService(false);
        }
      default:
        return;
    }
  }

UPDATE:

android.permission.INTERACT_ACROSS_USERS_FULL - это разрешение уровня подписей. Просто добавьте этот android: protectionLevel = "подпись" в ваш манифест.

Для получения дополнительной информации вы можете проверить это

http://developer.android.com/guide/topics/manifest/permission-element.html

<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" android:protectionLevel="signature"/>

Ответ 2

Если вы собираетесь переопределить

onCreate() 

в

IntentService,

затем убедитесь, что вы вызываете

super.onCreate()

. Вероятно, это будет вашей проблемой.

Ответ 3

Ваша проблема в том, что вы звоните от другого пользователя и просите запустить у другого пользователя, и для этого требуется android.permission.INTERACT_ACROSS_USERS_FULL, и это разрешение уровня подписки. Просто добавьте этот android:protectionLevel="signature" в ваш манифест.

Для получения дополнительной информации вы можете проверить это

http://developer.android.com/guide/topics/manifest/permission-element.html

<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" android:protectionLevel="signature"/>