Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SecurityException thrown when calling WifiManager startScan

I'm using a PendingIntent launched by AlarmManager (with setRepeating) to start wifi scans (using IntentService) every few minutes. On most devices and in most cases, there is no problem with that. However, on several devices I get the following error (Couldn't reproduce the error on any test device. This is a crash log from a user's device):

java.lang.RuntimeException: Unable to start service com.myapp.android.service.MyService@44a9701 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)
       ...

I'm creating the PendingIntent from my app so I see no reason for the SecurityException thrown from WifiManager (Especially since this happens rarely).

The IntentService launched from the PendingIntent code is as follows:

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

boolean ok = mWifiManager.startScan();

Any ideas on what might be causing this?

like image 491
Muzikant Avatar asked Jul 22 '15 15:07

Muzikant


1 Answers

This is happening because of the new app permissions for android m. See the comment above the source code of wifimanager's 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;
        }
    }

Hence, you will have to ask the user for permissions on runtime. Put these permissions in your manifest-

  <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"/>

From api 23 onwards you require a permission to access user location to use it. I suggest you use a permissions check based on the api level and start intent only if the permissions have been granted. Something like this-

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);
      }
}

Further to check results-

@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 is a signature level permission. Just add this android:protectionLevel="signature" in your manifest .

For more details you can check this

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

<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" android:protectionLevel="signature"/>
like image 105
Vaibhav Sharma Avatar answered Oct 27 '22 21:10

Vaibhav Sharma