2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-23 10:27:57 +00:00
kdeconnect-android/src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.java

97 lines
3.6 KiB
Java
Raw Normal View History

package org.kde.kdeconnect.Helpers;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.preference.PreferenceManager;
import android.text.TextUtils;
Automatically reconnect to trusted networks in the background on API 29+ ## Problem On Android 10 (API 29) or later, when a trusted network has been configured, if the mobile device rejoins the network while KDE Connect is running in the background, it doesn't reconnect to any paired devices in the trusted network. It only reconnects when the user brings the app to the foreground. ## Cause Android 10 introduced [a separate permission for background location access](https://developer.android.com/about/versions/10/privacy/changes#app-access-device-location). When KDE Connect is running in the background with API 29+, [`TrustedNetworkHelper.currentSSID` fails to get the SSID](https://invent.kde.org/coskomh/kdeconnect-android/-/blob/d22967f4752d9590d05551279269d6a0f5ec5470/src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.java#L77) because it doesn't have the required permission. This prevents KDE Connect from verifying whether a network is trusted. ## Solution Make KDE Connect request for background location access permission. To request for background location access, an app must [declare it in the manifest](https://developer.android.com/training/location/permissions#background), or else the option to to enable background location access won't appear in settings. As a side note, the permission request dialog in `TrustedNetworksActivity` doesn't require changes because after Android 11, the option to allow background location no longer shows up in a dialog. [It has to be manually enabled in settings.](https://developer.android.com/training/location/permissions#background-dialog-target-sdk-version) ## Test Plan ### Before: On Android 10 or later, configure a trusted network and pair with a device in it. Let KDE Connect run in the background. Disconnect from the trusted network and then rejoin. KDE Connect won't automatically reconnect to the paired device. ### After: Do the same steps as above, except after configuring the trust network, go into settings and allow KDE Connect to access location all the time. After KDE Connect rejoins the trusted network, it should automatically reconnect to the paired device. ![kdeconnect_location_permission_settings](/uploads/c7d03de9f1fb1502d7036f45ccb811a0/kdeconnect_location_permission_settings.png)
2022-03-01 22:08:54 +00:00
import android.util.Log;
import androidx.core.content.ContextCompat;
2020-07-10 08:21:25 +05:30
import org.apache.commons.lang3.ArrayUtils;
import org.kde.kdeconnect_tp.R;
2020-07-10 08:21:25 +05:30
import java.util.List;
public class TrustedNetworkHelper {
private static final String KEY_CUSTOM_TRUSTED_NETWORKS = "trusted_network_preference";
private static final String KEY_CUSTOM_TRUST_ALL_NETWORKS = "trust_all_network_preference";
private static final String NETWORK_SSID_DELIMITER = "#_#";
private static final String NOT_AVAILABLE_SSID_RESULT = "<unknown ssid>";
public static final String ACCESS_WIFI_NETWORKS_PERMISSION = Build.VERSION.SDK_INT >= 33? Manifest.permission.NEARBY_WIFI_DEVICES : Manifest.permission.ACCESS_FINE_LOCATION;
public static final int ACCESS_WIFI_NETWORKS_PERMISSION_EXPLANATION = Build.VERSION.SDK_INT >= 33? R.string.wifi_permission_needed_desc : R.string.location_permission_needed_desc;
private final Context context;
public TrustedNetworkHelper(Context context) {
this.context = context;
}
2020-07-10 08:21:25 +05:30
public String[] read() {
String serializeTrustedNetwork = PreferenceManager.getDefaultSharedPreferences(context).getString(
KEY_CUSTOM_TRUSTED_NETWORKS, "");
if (serializeTrustedNetwork.isEmpty())
2020-07-10 08:21:25 +05:30
return ArrayUtils.EMPTY_STRING_ARRAY;
return serializeTrustedNetwork.split(NETWORK_SSID_DELIMITER);
}
public void update(List<String> trustedNetworks) {
String serialized = TextUtils.join(NETWORK_SSID_DELIMITER, trustedNetworks);
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
KEY_CUSTOM_TRUSTED_NETWORKS, serialized).apply();
}
public boolean allAllowed() {
if (!hasPermissions()) {
return true;
}
return PreferenceManager
.getDefaultSharedPreferences(context)
.getBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, Boolean.TRUE);
}
public void allAllowed(boolean isChecked) {
PreferenceManager
.getDefaultSharedPreferences(context)
.edit()
.putBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, isChecked)
.apply();
}
public boolean hasPermissions() {
int result = ContextCompat.checkSelfPermission(context, ACCESS_WIFI_NETWORKS_PERMISSION);
return (result == PackageManager.PERMISSION_GRANTED);
}
public String currentSSID() {
WifiManager wifiManager = ContextCompat.getSystemService(context.getApplicationContext(),
WifiManager.class);
if (wifiManager == null) return "";
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) {
return "";
}
String ssid = wifiInfo.getSSID();
if (ssid.equalsIgnoreCase(NOT_AVAILABLE_SSID_RESULT)){
Automatically reconnect to trusted networks in the background on API 29+ ## Problem On Android 10 (API 29) or later, when a trusted network has been configured, if the mobile device rejoins the network while KDE Connect is running in the background, it doesn't reconnect to any paired devices in the trusted network. It only reconnects when the user brings the app to the foreground. ## Cause Android 10 introduced [a separate permission for background location access](https://developer.android.com/about/versions/10/privacy/changes#app-access-device-location). When KDE Connect is running in the background with API 29+, [`TrustedNetworkHelper.currentSSID` fails to get the SSID](https://invent.kde.org/coskomh/kdeconnect-android/-/blob/d22967f4752d9590d05551279269d6a0f5ec5470/src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.java#L77) because it doesn't have the required permission. This prevents KDE Connect from verifying whether a network is trusted. ## Solution Make KDE Connect request for background location access permission. To request for background location access, an app must [declare it in the manifest](https://developer.android.com/training/location/permissions#background), or else the option to to enable background location access won't appear in settings. As a side note, the permission request dialog in `TrustedNetworksActivity` doesn't require changes because after Android 11, the option to allow background location no longer shows up in a dialog. [It has to be manually enabled in settings.](https://developer.android.com/training/location/permissions#background-dialog-target-sdk-version) ## Test Plan ### Before: On Android 10 or later, configure a trusted network and pair with a device in it. Let KDE Connect run in the background. Disconnect from the trusted network and then rejoin. KDE Connect won't automatically reconnect to the paired device. ### After: Do the same steps as above, except after configuring the trust network, go into settings and allow KDE Connect to access location all the time. After KDE Connect rejoins the trusted network, it should automatically reconnect to the paired device. ![kdeconnect_location_permission_settings](/uploads/c7d03de9f1fb1502d7036f45ccb811a0/kdeconnect_location_permission_settings.png)
2022-03-01 22:08:54 +00:00
Log.d("TrustedNetworkHelper", "Current SSID is unknown");
return "";
}
return ssid;
}
2020-01-05 17:27:16 +01:00
public static boolean isTrustedNetwork(Context context) {
TrustedNetworkHelper trustedNetworkHelper = new TrustedNetworkHelper(context);
if (trustedNetworkHelper.allAllowed()){
2020-01-05 17:27:16 +01:00
return true;
}
2020-07-10 08:21:25 +05:30
return ArrayUtils.contains(trustedNetworkHelper.read(), trustedNetworkHelper.currentSSID());
}
}