Android WifiManager getScanResult complains Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission although declared permission
Asked Answered
V

3

6

I am developing an App to check Wifi points.

I am getting error "java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results" at wifiManager.getScanResults() even though I already declared those permissions.

main activity

public class MainActivity extends AppCompatActivity {

WifiManager wifiManager;
String[] wifis;
WifiReceiver wifiReceiver;

ListView wifiListView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    wifiListView = (ListView) findViewById(R.id.wifi_list);

    wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    wifiReceiver = new WifiReceiver();
    wifiManager.startScan();
}

protected void onPause() {
    unregisterReceiver(wifiReceiver);
    super.onPause();
}

protected void onResume() {
    registerReceiver(wifiReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
    super.onResume();
}

private class WifiReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        List<ScanResult> wifiScanList = wifiManager.getScanResults();
        wifis = new String[wifiScanList.size()];

        for (int i = 0; i < wifiScanList.size(); i++) {
            wifis[i] = wifiScanList.get(i).toString();
        }

        wifiListView.setAdapter(new ArrayAdapter<>(getApplicationContext(), android.R.layout.simple_list_item_1, wifis));
    }
}

manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sample">

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

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

I am on SDK 6.0

I observed similar question, but solution does not apply since I already declared permission.

Anyone know what might be problem? Thank you.

Vogel answered 16/11, 2015 at 8:5 Comment(4)
Try restarting your IDE.Lobachevsky
tried restarting IDE, uninstall app, clean project, build project, still same errorVogel
Hi @kevin0228ca, I'm not entirely sure if what I say applies to your problem. According to Vogella Android 6.0 Marshmallow (API 23) introduced a new runtime permission model. If your application targets Android 6.0, you must use the new permissions model. So I'm assuming that this new model is based on Requesting Permissions at Run Time may solve the issue you are having. Vogella provide us a link to the official documentationFeatherstone
#33666571Ethos
P
7

In Android M, you need to ask for the permission which is defined as dangerous in PermissionModel to the user before start using each time, it as such:

private boolean mayRequestLocation() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        }
        if (checkSelfPermission(ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
            Snackbar.make(mView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
                    .setAction(android.R.string.ok, new View.OnClickListener() {
                        @Override
                        @TargetApi(Build.VERSION_CODES.M)
                        public void onClick(View v) {
                            requestPermissions(new String[]{ACCESS_FINE_LOCATION}, REQUEST_FINE_LOCATION);
                        }
                    });
        } else {
            requestPermissions(new String[]{ACCESS_FINE_LOCATION}, REQUEST_FINE_LOCATION);
        }
        return false;
    }

Add this to your Activity:

private static final int REQUEST_FINE_LOCATION=0

and load it during runtime with:

loadPermissions(Manifest.permission.ACCESS_FINE_LOCATION,REQUEST_FINE_LOCATION);

To evaluate the results of your permission request, you can override onRequestPermissionsResult method:

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case REQUEST_FINE_LOCATION: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // The requested permission is granted.
            }
            else{
                // The user disallowed the requested permission.
            }
            return;
        }

}
Philip answered 16/11, 2015 at 8:28 Comment(0)
M
1

MADAO is right: you should turn on GPS to get the WIFI access point list.

But I'm not sure about PEERS_MAC_ADDRESS. If you look at the source code (line 957):

/**
 * Return the results of the most recent access point scan, in the form of
 * a list of {@link ScanResult} objects.
 * @return the list of results
 */
public List<ScanResult> getScanResults(String callingPackage) {
    enforceAccessPermission();
    int userId = UserHandle.getCallingUserId();
    int uid = Binder.getCallingUid();
    boolean canReadPeerMacAddresses = checkPeersMacAddress();
    boolean isActiveNetworkScorer =
            NetworkScorerAppManager.isCallerActiveScorer(mContext, uid);
    boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
    long ident = Binder.clearCallingIdentity();
    try {
        if (!canReadPeerMacAddresses && !isActiveNetworkScorer
                && !isLocationEnabled()) {
            return new ArrayList<ScanResult>();
        }
        if (!canReadPeerMacAddresses && !isActiveNetworkScorer
                && !checkCallerCanAccessScanResults(callingPackage, uid)) {
            return new ArrayList<ScanResult>();
        }
        if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
                != AppOpsManager.MODE_ALLOWED) {
            return new ArrayList<ScanResult>();
        }
        if (!isCurrentProfile(userId) && !hasInteractUsersFull) {
            return new ArrayList<ScanResult>();
        }
        return mWifiStateMachine.syncGetScanResultsList();
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

The first if is checking canReadPeerMacAddresses which the code for checkPeersMacAddress() is:

/**
 * Returns true if the caller holds PEERS_MAC_ADDRESS.
 */
private boolean checkPeersMacAddress() {
    return mContext.checkCallingOrSelfPermission(
            android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED;
}

If you add the permission you can bypass if (!canReadPeerMacAddresses && !isActiveNetworkScorer && !isLocationEnabled()) {. I've tested but I cannot get WIFI MAC list by just using the permission and disabling location.

Margarine answered 22/8, 2017 at 10:54 Comment(5)
Are you saying that you have verified that you go this permission and when you call mContext.checkCallingOrSelfPermission( android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED you get true? If so, perhaps the access is blocked by the call to enforceAccessPermission()?Actinopod
No. As I told I've add the permission PEERS_MAC_ADDRESS but did not success in getting access points list without turning on location. Furthermore I make my app a system app but still failure in getting access point list without turning on location.Margarine
The code for enforceAccessPermission() is like below: private void enforceAccessPermission() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, "WifiService"); } And I have the ACCESS_WIFI_STATE permission in manifest.Margarine
I'm just wondering if you can verify in code that those two permission have actually been granted. You may have requested them, but that doesn't mean that something hasn't gone wrong such that the OS refused to grant them.Actinopod
Actually No. I did not debug the Android code itself. But only I've checked its source. And you're right. Maybe the OS or the vendor specific OS may deny the permission. And now I'm very curious about how to get access points without turning on location. I can do anything to the phone: rooting, making system app, etc. If you got any solution please share. TnxMargarine
L
0

ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION is necessary. To get a valid result, you also have to turn on GPS or get a PEERS_MAC_ADDRESS permission like Setting.

Liliuokalani answered 27/1, 2016 at 3:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.