Я занимаюсь разработкой приложения, которое управляет подключениями Wi-Fi.Мой сценарий таков: скажем, во всем здании есть сеть Wi-Fi под названием «testing-tls».Мое приложение должно иметь возможность подключаться только к выбранным точкам доступа (на основе BSSID или MAC ID).Для проверки пользователя мы используем механизм TLS-аутентификация (пользовательские сертификаты CA).
Я могу установить соединение через приложение, но не удается, когда я пытаюсь подключиться к другой точке доступа(разные BSSID).Несмотря на то, что я создал конфигурацию Wi-Fi программно, я не могу обновить конфигурацию после первого успешного подключения.Я проверил свое приложение в Орео и Зефир.Но я сталкиваюсь с проблемами в Oreo (не уверен насчет нуги).Я начинаю задумываться, возможно ли вообще обновить конфигурацию после ее создания.
Вот следующие шаги, которые я выполняю:
1) Создайте объект WifiConfiguration
private WifiConfiguration createWifiConfiguration() {
WifiConfiguration config = new WifiConfiguration();
config.SSID = "\"testing-tls\"";
config.priority = 1;
config.status = WifiConfiguration.Status.ENABLED;
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP;
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
config.enterpriseConfig.setIdentity(identityName);
config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
PKCS12ParseInfo parseInfo;
try {
parseInfo = CertificateUtils.parsePKCS12Certificate(
certificateFilePath, identityPassword);
if (parseInfo != null) {
config.enterpriseConfig.setClientKeyEntry(parseInfo.getPrivateKey(),
parseInfo.getCertificate());
return config;
}
return null;
} catch (KeyStoreException | NoSuchAlgorithmException | IOException |
CertificateException | UnrecoverableKeyException | KeyManagementException e1) {
Timber.e("WifiMonitorService, Fail to parse the input certificate: %s", e1.toString());
Toast.makeText(this, "Error occurred", Toast.LENGTH_SHORT).show();
return null;
}
}
2) Попробуйте установить соединение
private void establishWifiConnection(String result) {
Timber.d("WifiMonitorService, establishing WifiConnection");
WifiConfiguration configuration = createWifiConfiguration();
if (configuration != null) {
// result contains a mac id - 00:45:69:c5:34:f2
configuration.BSSID = result;
int networkId = wifiManager.addNetwork(configuration);
if (networkId == -1) {
networkId = getExistingNetworkId(wifiSsid);
// Add a new configuration to the db
if (networkId == -1) {
Timber.e("Couldn't add network with SSID");
Toast.makeText(this, "Wifi configuration error", Toast.LENGTH_SHORT).show();
return;
}
}
Timber.i("WifiMonitorService, # addNetwork returned: %d", networkId);
wifiManager.saveConfiguration();
wifiManager.enableNetwork(networkId, true);
wifiManager.reassociate();
} else {
Toast.makeText(this, "Wifi conf Error occurred", Toast.LENGTH_SHORT).show();
}
}
3) Получить идентификатор выхода из сети, если имеется
private int getExistingNetworkId(String ssid) {
List<WifiConfiguration> configuredNetworks =
wifiManager.getConfiguredNetworks();
if (configuredNetworks != null) {
for (WifiConfiguration existingConfig : configuredNetworks) {
if (existingConfig.SSID.equals("\"testing-tls\"")) {
return existingConfig.networkId;
}
}
}
return -1;
}
Разрешения манифеста следующие:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<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_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<uses-feature android:name="android.hardware.wifi" />
<uses-feature android:name="android.hardware.camera" />
<permission
android:name="android.permission.INTERACT_ACROSS_USERS"
android:protectionLevel="signature" />
Ошибка: я всегда получаю UID 10189 does not have permission to update configuration error
в Oreo
2018-12-28 12:23:44.571 1320-1847/? E/WifiConfigManager: UID 10189 does not have permission to update configuration "testing-tls"WPA_EAP
2018-12-28 12:23:44.571 1320-1847/? I/WifiStateMachine: connectToUserSelectNetwork Allowing uid 10189 with insufficient permissions to connect=1
Расследование
После копания в исходном коде я нашел реализацию метода addOrUpdateNetwork
в WifiConfigManager class.
Реализация addOrUpdateNetwork
, в теге android_8.0.0_r21 (номер сборки OPD1.170816.010) выглядит следующим образом:
- Сначала проверьте, если у нас уже есть сеть с предоставленным идентификатором сети или configKey
- Если существующая сеть не найдена, проверьте конфигурацию и добавьте.
- Если существующая сеть найдена,обновить конфигурацию сети.Перед этим проверьте, есть ли у приложения необходимые разрешения для обновления сети.
AddOrUpdateNetwork
внутренне вызывает функцию с именем canModifyNetwork
:
/**
* Checks if |uid| has permission to modify the provided configuration.
*
* @param config WifiConfiguration object corresponding to the network to be modified.
* @param uid UID of the app requesting the modification.
* @param ignoreLockdown Ignore the configuration lockdown checks for connection attempts.
*/
private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) {
// Passpoint configurations are generated and managed by PasspointManager. They can be
// added by either PasspointNetworkEvaluator (for auto connection) or Settings app
// (for manual connection), and need to be removed once the connection is completed.
// Since it is "owned" by us, so always allow us to modify them.
if (config.isPasspoint() && uid == Process.WIFI_UID) {
return true;
}
// EAP-SIM/AKA/AKA' network needs framework to update the anonymous identity provided
// by authenticator back to the WifiConfiguration object.
// Since it is "owned" by us, so always allow us to modify them.
if (config.enterpriseConfig != null
&& uid == Process.WIFI_UID
&& TelephonyUtil.isSimEapMethod(config.enterpriseConfig.getEapMethod())) {
return true;
}
final DevicePolicyManagerInternal dpmi = LocalServices.getService(
DevicePolicyManagerInternal.class);
final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
// If |uid| corresponds to the device owner, allow all modifications.
if (isUidDeviceOwner) {
return true;
}
final boolean isCreator = (config.creatorUid == uid);
// Check if the |uid| holds the |NETWORK_SETTINGS| permission if the caller asks us to
// bypass the lockdown checks.
if (ignoreLockdown) {
return mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
}
// Check if device has DPM capability. If it has and |dpmi| is still null, then we
// treat this case with suspicion and bail out.
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
&& dpmi == null) {
Log.w(TAG, "Error retrieving DPMI service.");
return false;
}
// WiFi config lockdown related logic. At this point we know uid is NOT a Device Owner.
final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
if (!isConfigEligibleForLockdown) {
return isCreator || mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
}
final ContentResolver resolver = mContext.getContentResolver();
final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
return !isLockdownFeatureEnabled
&& mWifiPermissionsUtil.checkNetworkSettingsPermission(uid);
}
Насколько я вижу, только следующие пользователи имеют доступ для изменения конфигурации сети.
- Системное приложение
- Владелец устройства
- Создатель (По какой-то причине происходит сбой)
Я получаю одинаковое поведение в этих двух телефонах.
- Пиксель 2 (Oreo 8.0.0)
- Samsung J8 (Oreo 8.0.0)
Кроме того, Samsung J8 всегда показывает это предупреждение:
CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certificate path not found
Вопрос:
- Как может быть уже созданная конфигурация Wi-Fiобновляется программно?
- Возможно ли вообще обновить конфигурацию Wi-Fi после ее создания во внутренней базе данных Wi-Fi?
- Обязательно ли отключать Wi-Fi перед обновлением или включением конфигурации?