Android — Отказано в разрешении при привязке сокета мобильной сети (OkHttp.SocketFactory(network.SocketFactory))

#android #retrofit2 #okhttp

Вопрос:

Я ищу вашу помощь / идеи, связанные с проблемой ниже:

Чего я хочу достичь:

  • маршрутизация запросов через мобильную сеть на Android с использованием Retrofit2, OktHttp

Что я сделал:

  • получен необходимый экземпляр сети с помощью ConnectivityManager
  • установите SocketFactory для OkHttpClient, чтобы использовать тот, который находится в сетевом экземпляре
  • укажите эти разрешения в AndroidManifest.xml.:
 lt;uses-permission android:name="android.permission.INTERNET"/gt; lt;uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/gt; lt;uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/gt; lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/gt; lt;uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/gt;  

Пример кода:

 val network: Network = connectivityManager.allNetworks.first {  connectivityManager.getNetworkCapabilities(it)?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true }  val okHttpClient = OkHttpClient.Builder()  .socketFactory(network.socketFactory)  .build()  

Результат:

  • возникает исключение: java.net.SocketException: Ошибка привязки сокета к сети 505: EACCES (отказано в разрешении)

Примечания / Отказ от ответственности:

  • вышесказанное работает для Wi-Fi
  • Я знаю, что это плохая практика: это не поведение приложения по умолчанию. Пользователи могут зарегистрироваться и отказаться в любое время. Есть конкретный случай использования, в котором это имеет смысл.

Если у вас есть какие-либо идеи о том, что я делаю неправильно, или почему это не работает для мобильных сетей, пожалуйста, дайте мне знать.

Ответ №1:

Мне удалось выяснить, почему я поступил неправильно.

В двух словах:

  • вместо ConnectivityManager.getAllNetworks() используйте ConnectivityManager.requestNetwork(), указав сведения о сети и используйте полученную.
 val cellularRequest = NetworkRequest.Builder()  .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)  .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)  .build()  connectivityManager.requestNetwork(cellularRequest, object :  ConnectivityManager.NetworkCallback() {  override fun onAvailable(network: Network) {  // use the network...  }  })  

Длинная история:
При тестировании у меня был включен Wi-Fi и мобильные данные.
Из документов о ConnectivityManager.getAllNetworks():

  • «Возвращает массив всей сети, отслеживаемой в настоящее время платформой».
  • Я думаю, что ключевое слово здесь-это та часть, которую я выделил (интерфейс мобильной сети передачи данных не будет отслеживаться с самого начала, он не появится в списке. Когда я увидел тот, у которого есть возможности СОТОВОЙ связи, я подумал, что это тот, кого я ищу. Я ошибся. Это другой интерфейс. Я не заметил, что у него нет возможности доступа в Интернет.)
  • по умолчанию он вернул 2 сетевых экземпляра, см. Ниже:
Сетевой идентификатор Возможности
525 Транспортировка:
Возможности СОТОВОЙ связи: IMSamp;NOT_METEREDamp;НАДЕЖНЫЙamp;NOT_VPNamp;ПРОВЕРЕННЫЙamp;NOT_ROAMINGamp;ПЕРЕДНИЙ ПЛАНamp;NOT_CONGESTEDamp;NOT_SUSPENDED
767 Транспорт:
Возможности WI-FI: NOT_METEREDamp;INTERNETamp;NOT_RESTRICTEDamp;TRUSTEDamp;NOT_VPNamp;VALIDATEDamp;NOT_ROAMINGamp;FOREGROUNDamp;NOT_CONGESTEDamp;NOT_SUSPENDED

Обратите внимание, что СОТОВАЯ связь (идентификатор: 525) не имеет возможностей Интернета и имеет IMS (который, если я правильно понял из документов, относится к сети GSM). Когда я попытался привязать сокет к этой сети, это, очевидно, не удалось.
Затем я вызвал метод requestNetwork() и заметил, что получил новый (3-й) сетевой экземпляр:

Сетевой идентификатор Возможности
770 Транспортные средства:
Возможности СОТОВОЙ связи: SUPLamp;INTERNETamp;NOT_RESTRICTEDamp;TRUSTEDamp;NOT_VPNamp;VALIDATEDamp;NOT_ROAMINGamp;FOREGROUNDamp;NOT_CONGESTEDamp;NOT_SUSPENDED

Используя это, привязка работает без проблем.