I'm trying to get the new ConnectivityManager.bindProcessToNetwork(Network) using ConnectivityManager.requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)
The reason is to force the app to call the future request in some specific network, which doesn't have a internet connectivity (it's a local hardware communication network). At this point, the system is sending the requests over 3G/4G network and never reach the desired Wifi network, because this network doesn't respond the connectivity check that android call.
When I call the requestNetwork method, I receive the following error:
java.lang.SecurityException: com.xyz.app was not granted either of these permissions: android.permission.CHANGE_NETWORK_STATE, android.permission.WRITE_SETTINGS.
I try to call the new method to request permission available in Android 6.0:
requestPermissions(new String[]{Manifest.permission.CHANGE_NETWORK_STATE, Manifest.permission.WRITE_SETTINGS}, PERMISSIONS_REQUEST_WIFI);
But the callback is always PackageManager.PERMISSION_DENIED.
I put both of these permissions in the AndroidManifest.xml, without success.
Notice: The Manifest.permission.WRITE_SETTINGS is not in the Permissions Groups.
I'm not sure if this was intended by Google, but the following is the behavior I'm seeing:
CHANGE_NETWORK_STATE seems to always be denied (as noted in the comments, its a signature permission) but it also doesn't seem to matter. My ConnectivityManager network requests all seem to be gated by WRITE_SETTINGS only - so if you have WRITE_SETTINGS you don't need CHANGE_NETWORK_STATE.
As noted in comments, you do this differently than other permissions, using:
Intent goToSettings = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
goToSettings.setData(Uri.parse("package:" + Context.getPackageName()));
startActivity(goToSettings);
And after that, my ConnectivityManager network requests were peachy.
To check if the permission is already granted before calling the ACTION_MANAGE_WRITE_SETTINGS activity, this answer has the solution using
Settings.System.canWrite(Context)
Can't get WRITE_SETTINGS permission
UPDATE: as of Android 6.0.1, CHANGE_NETWORK_STATE is auto granted when requested in your manifest file. The above WRITE_SETTINGS checks are only required for 6.0
This was an Android 6.0 bug. It's fixed in Android 6.0.1, requestNetwork() can be called if you request CHANGE_NETWORK_STATE in the manifest. No need to call requestPermissions(), it's a normal permission.
Related
I am trying to upgrade my app's targetSDK to above 23 and I've run into a small problem. I have an activity that binds traffic to Wifi (to measure the network speed to the router, even if the router is not connected to the internet). In order for that to happen my app needs the CHANGE_NETWORK_STATE permission. That permission is usually granted directly if declared in the manifest. On Android 6.0 (exact, this was fixed in 6.0.1 IIRC) CHANGE_NETWORK_STATE is broken and won't be granted so you need the WRITE_SETTINGS permission instead. I've implemented a way for Android 6.0 Users to grant that permission but when I want to test my Activity using espresso I am unable to do so. Permissions are granted to tests by adding something like
#Rule
public GrantPermissionRule runtimePermissionRule = GrantPermissionRule.grant(Manifest.permission.CHANGE_NETWORK_STATE);
to the TestCase. That worked in other places in the app but for this I get
junit.framework.AssertionFailedError: Failed to grant permissions, see logcat for details in my test results. In logcat I find E/GrantPermissionCallable: Permission: android.permission.WRITE_SETTINGS cannot be granted! or the same with CHANGE_NETWORK_STATE, I've tried granting both and they both don't work. Is there any other way for me to grant the permission in the testing environment? Or am I unable to test this activity on 6.0 devices from now on?
I managed to get around this by granting the permission using UiAutomator and the shell appops command:
Instrumentation instrumentation = getInstrumentation();
UiDevice device = UiDevice.getInstance(instrumentation);
String targetPackageName = instrumentation.getTargetContext().getPackageName();
if (Build.VERSION.SDK_INT >= 23) {
String shellCommand = String.format("appops set %s WRITE_SETTINGS allow", targetPackageName);
device.executeShellCommand(shellCommand);
}
Because WRITE_SETTINGS is a sensitive permission, you won't be able to grant it using GrantPermissionRule in API 23. You will likely end up needing to use UIAutomator in your tests to select the appropriate response in the permissions management screen.
While developing an app where I scan the WiFi, I found that it does not work if I turn off the location service on my phone. I have provided the app with all the necessary permissions. - ACCESS_NETWORK_STATE, ACCESS_WIFI_STATE, ACCESS_COARSE_LOCATION.
This is my code:
WifiManager manager= (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
manager.startScan();
In the receiver:
int found = manager.getScanResults().size();
This question answers part of the problem.
Wifi scan results broadcast receiver not working
My questions are:
Is there a way for the app to list the Wifi access points if the location service is turned off?
If location service is absolutely necessary, is there a way for the app to turn on the location service while the app scans the wifi access points?
The only way to get the scanResult without GPS turned on is to set the app's targetSDKversion to 21 or lower.
This will work even on Lolipop and above.
Android 8.0 and Android 8.1:
A successful call to WifiManager.getScanResults() requires any one of the following permissions:
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
CHANGE_WIFI_STATE
If the calling app does not have any of these permissions, the call fails with a SecurityException.
Android 9 and later:
A successful call to WifiManager.startScan() requires all of the following conditions to be met:
Your app has the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission.
Your app has the CHANGE_WIFI_STATE permission.
Location services are enabled on the device (under Settings > Location).
To successfully call WifiManager.getScanResults() ensure all of the following conditions are met:
Your app has the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission.
Your app has the ACCESS_WIFI_STATE permission.
Location services are enabled on the device (under Settings > Location).
If the calling app doesn't meet all of these requirements, the call fails with a SecurityException.
This is from https://developer.android.com/guide/topics/connectivity/wifi-scan Google Documentation.
Probably they needed the "Location services are enabled on the device" requirement for Android 6.+ because it is the version this permission restrictions first revealed, but they don't seem to had this requirement in documentations since no one has answered this question until now.
Is there a way for the app to list the Wifi access points if the
location service is turned off?
Yes, only system apps can get scan results without the location with the following permission:
android.permission.PEERS_MAC_ADDRESS
permission
Code:
I use the following code taken from here with a target API level 23 (and minimum API level 18).
private final BroadcastReceiver mReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action))
{
bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
deviceNameTextView.setText(bluetoothDevice.getName());
}
}
};
On a button pressed event I call:
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
mBluetoothAdapter.startDiscovery(); // was initialized successsfully
My AndroidManifest.xml contains:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
Devices:
Samsung Galaxy S III (API level 18)
Sony Xperia Z3 (API level 23)
Facts:
both devices are running the exact same code
device 1 can discover device 2 (and any other Bluetooth device)
device 2 cannot discover device 1 (or any other Bluetooth device)
both devices are discoverable
tested discoverability with the standard system dialog for pairing for both devices successfully
both devices were unpaired at all times (I don't want to pair)
No exceptions are thrown.
What is wrong?
Update 1:
Since API level 23 permissions may have to be requested at run time. Yvette pointed me to that, thank you! Unfortunately it didn't solve my problem.
What speaks against her theory is the following:
mBluetoothAdapter.startDiscovery() returns true, which means success (see here).
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity , Manifest.permission.BLUETOOTH_ADMIN);
if(permissionCheck == PackageManager.PERMISSION_GRANTED)
Log.i("info", "Permission granted!");
else
Log.i("info", "Permission not granted!");
Running this code with BLUETOOTH_ADMIN and BLUETOOTH returns both times:
Permission granted!
When doing some research, I found the following article from the official documentation regarding changes in Android 6.0 (API level 23).
To access the hardware identifiers of nearby external devices via
Bluetooth and Wi-Fi scans, your app must now have the
ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions:
- WifiManager.getScanResults()
- BluetoothDevice.ACTION_FOUND
- BluetoothLeScanner.startScan()
So, I was missing the permissions ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION all along. But just adding them in the AndroidManifest.xml file is not enough. You have to request those privileges at run time like Yvette suggested.
You can find here how you can do that or just use this piece of code I wrote to get the permissions needed for Bluetooth discovery.
final int CODE = 5; // app defined constant used for onRequestPermissionsResult
String[] permissionsToRequest =
{
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.BLUETOOTH,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
boolean allPermissionsGranted = true;
for(String permission : permissionsToRequest)
{
allPermissionsGranted = allPermissionsGranted && (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED);
}
if(!allPermissionsGranted)
{
ActivityCompat.requestPermissions(this, permissionsToRequest, CODE);
}
mBluetoothAdapter.startDiscovery();
This code is assuming that the user grants the permissions (for simplicity). If you want your app to behave differently when the permissions are not granted see "Handle the permissions request response" in this article.
Sounds like you're not managing runtime permissions for sdk 23 and higher. The phone with the sdk of 23 will either silently ignore any requests that require runtime permissions or crash.
Also see in the docs System Permissions.
If your app lists normal permissions in its manifest (that is, permissions that don't pose much risk to the user's privacy or the device's operation), the system automatically grants those permissions. If your app lists dangerous permissions in its manifest (that is, permissions that could potentially affect the user's privacy or the device's normal operation), the system asks the user to explicitly grant those permissions. The way Android makes the requests depends on the system version, and the system version targeted by your app:
If the device is running Android 6.0 (API level 23) or higher, and the app's targetSdkVersion is 23 or higher, the app requests permissions from the user at run-time. The user can revoke the permissions at any time, so the app needs to check whether it has the permissions every time it runs. For more information about requesting permissions in your app, see the Working with System Permissions training guide.
If the device is running Android 5.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions when the user installs the app. If you add a new permission to an updated version of the app, the system asks the user to grant that permission when the user updates the app. Once the user installs the app, the only way they can revoke the permission is by uninstalling the app.
Often times a permission failure will result in a SecurityException being thrown back to the application. However, this is not guaranteed to occur everywhere. For example, the sendBroadcast(Intent) method checks permissions as data is being delivered to each receiver, after the method call has returned, so you will not receive an exception if there are permission failures. In almost all cases, however, a permission failure will be printed to the system log.
The permissions provided by the Android system can be found at Manifest.permission. Any application may also define and enforce its own permissions, so this is not a comprehensive list of all possible permissions.
A particular permission may be enforced at a number of places during
your program's operation:
At the time of a call into the system, to prevent an application from
executing certain functions.
When starting an activity, to prevent
applications from launching activities of other applications.
Both
sending and receiving broadcasts, to control who can receive your
broadcast or who can send a broadcast to you.
When accessing and
operating on a content provider.
Binding to or starting a service.
As for the app crashing:
Everything every Android Developer must know about new Android's Runtime Permission
Next question in your head right now. So will my application crash?
Such a kindness sent from god delivered through the Android team. When we call a function that requires a permission user revoked on application with targetSdkVersion less than 23, no any Exception will be thrown. Instead it will just simply do nothing. For the function that return value, it will return either null or 0 depends on the case.
But don't be too happy. Although application would not be crashed from calling a function. It may still can crash from what that application does next with those returned value.
Some more details in these answers Require dangerous permissions during installation When asking for runtime permission for location
Is it necessary to use "ACCESS_NETWORK_STATE" permission to work "CONNECTIVITY_CHANGE" reciver?
When I test it with "INTERNET" permission and not "ACCESS_NETWORK_STATE" works like a charm.
Any idea or comment will be appreciated.
android.permission.INTERNET
Allows applications to open network sockets
&&
android.permission.ACCESS_NETWORK_STATE
Allows applications to access information about networks
It do requires the caller to hold the permission ACCESS_NETWORK_STATE
I have a (FOSS) app out there which can, among other features, enable and disable Wifi.
AndroidManifest.xml contains android.permission.CHANGE_WIFI_STATE, unit tests on the emulator pass and the feature works on a real device, a HTC Desire running 2.2.2.
The SDK versions are android:minSdkVersion="7" and android:targetSdkVersion="10", so I can't say about newer releases of Android.
I have received one single crash report:
java.lang.SecurityException: Permission Denial: writing com.android.providers.settings.SettingsProvider
uri content://settings/secure from pid=6191,
uid=10114 requires android.permission.WRITE_SETTINGS
Is android.permission.WRITE_SETTINGS possibly required on recent versions of Android? The reference says the permission exists since API 1, so I'd be surprised why it wasn't on older releases.
The user message is weird, it just says "lies", so I am unsure if I should just follow this report and add android.permission.WRITE_SETTINGS.
Any thoughts?
Cheers,
Torsten
That's weird, cause I needed to check exactly the same issue, so i just wrote a few lines of code doing exactly just that : enable/disable wifi and bluetooth.
My conclusion: you dont need the WRITE_SETTINGS permission to toggle the wifi, nor the bluetooth for that matter.
wifi = access_wifi_state + change_wifi_state
BT = bluetooth + bluetooth_admin
(with 4.2.2)
Check whether you were trying something else too, which may trigger the exception ?