调用WifiManager的startScan方法时抛出SecurityException异常

10

我正在使用由AlarmManager启动的PendingIntent(使用setRepeating)每隔几分钟启动wifi扫描(使用IntentService)。在大多数设备和情况下,这没有问题。但是,在一些设备上,我遇到了以下错误(无法在任何测试设备上重现此错误。这是来自用户设备的崩溃日志):

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)
       ...

我从我的应用程序中创建PendingIntent,因此我认为WifiManager引发SecurityException没有理由(尤其是因为这种情况很少发生)。

PendingIntent代码启动的IntentService如下:

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

boolean ok = mWifiManager.startScan();

有什么想法可能会导致这种情况吗?


它说需要android.permission.INTERACT_ACROSS_USERS_FULL或android.permission.INTERACT_ACROSS_USERS权限,你有尝试在清单文件中添加吗? - Berťák
3
@Berťák,这个权限是系统权限,除非是系统应用程序,否则没有应用程序可以请求该权限。此外,对于调用startScan并通过SCAN_RESULTS_AVAILABLE_ACTION广播接收结果并不需要该权限。这就是让它变得奇怪的原因... - Muzikant
似乎您的IntentService在不同的用户上下文中运行。似乎有一个或多个用户登录了您的手机。这让人感到奇怪。 - ShihabSoft
似乎你的安卓系统出了个bug。你尝试在其他设备上测试过吗? - ShihabSoft
@ShihabSoft 我在多个设备上尝试过,但都无法重现此错误(即使在相同类型的设备上也是如此)。 - Muzikant
显示剩余7条评论
3个回答

4
这是由于Android M的新应用程序权限引起的。 请查看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;
        }
    }

因此,您需要在运行时向用户请求权限。将这些权限放入您的清单文件中 -
  <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;
    }
  }

更新:

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

感谢您提供详细的答案,但这并没有解决问题。我知道SDK 23中的权限更改,并且清单具有您提到的权限,但这并不能解释错误“从Android广播请求以用户-1运行,但正在从用户0调用;这需要android.permission.INTERACT_ACROSS_USERS_FULL或android.permission.INTERACT_ACROSS_USERS”。此外,这种情况很少发生,我无法在完全相同的设备和API级别上重现相同的错误。 - Muzikant
还有一件事,你回答中的代码片段引用了 getScanResults,但错误是在 startScan 上收到的。 - Muzikant
@Muzikant,wifimanager在您调用startscan后调用getScanResults获取扫描结果。另外,我已经更新了我的答案。 - Vaibhav Sharma

0

如果你要覆盖

onCreate() 

在你的

IntentService,

然后确保您调用

super.onCreate()

在其中。这似乎很可能是你的问题。


-1

你的问题是你正在从不同的用户调用并要求在不同的用户上运行,这需要 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"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接