web-dev-qa-db-ja.com

Android M)でモバイルデータがオン(接続あり)の場合でも、WiFi(接続なし)でリクエストを送信します

インターネットに接続せずにUDPパケットをWiFiモジュール(独自のAPに付属)に送信する必要がありますが、モバイルをAPに接続すると、Androidは、モバイルデータインターフェイスでパケットをリダイレクトします。インターネットに接続しています。

以下のコードを使用して仕事をしましたが、Android Mでは機能しないようです。

_@TargetApi(Build.VERSION_CODES.Lollipop)
private void setWifiInterfaceAsDefault() {
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkRequest.Builder builder = new NetworkRequest.Builder();
    NetworkRequest networkRequest= builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .build();

    connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback());
}
_

私も追加しました

_<uses-permission Android:name="Android.permission.CHANGE_NETWORK_STATE" />
<uses-permission Android:name="Android.permission.WRITE_SETTINGS" />
_

androidManifest.xmlで、Settings.System.canWrite(this)trueを返すことを確認しましたが、それでも何も返されません。

前もって感謝します。

17

ConnectivityManager.setProcessDefaultNetwork()を使用してネットワークをバインドすると、ローミングが防止され、完全なTCPアクセスが可能になります。したがって、onAvailable()コールバック内で、接続を開くのではなく、アプリケーションプロセスをそのネットワークにバインドできます。特定のURL。

ConnectivityManager connection_manager = 
(ConnectivityManager) activity.getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkRequest.Builder request = new NetworkRequest.Builder();
request.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);

connection_manager.registerNetworkCallback(request.build(), new NetworkCallback() {

    @Override
    public void onAvailable(Network network) {
        ConnectivityManager.setProcessDefaultNetwork(network);
    }
}

元の回答

9

Stanislav の答えは正しいですが、ロリポップでのみ機能するため不完全です。

選択した特定のネットワークに接続したときにWiFi経由ですべてのネットワーク要求をルーティングできるように、LollipopとMarshmallow以降の完全なソリューションを作成しました。


Kotlin

あなたの活動では、

@RequiresApi(Build.VERSION_CODES.Lollipop)
class RoutingActivity : Activity() {

    private var mConnectivityManager: ConnectivityManager? = null
    private var mNetworkCallback: ConnectivityManager.NetworkCallback? = null
    //...

    override fun onCreate(savedInstanceState: Bundle?) {
        //...
        routeNetworkRequestsThroughWifi("Access-Point-SSID-You-Want-To-Route-Your-Requests")
    }

アプリケーションからの将来のネットワーク要求をWiFi経由でルーティングします(特定のWiFiネットワークにインターネットがなく、モバイルデータにインターネット接続がある場合でも)

/**
 * This method sets a network callback that is listening for network changes and once is
 * connected to the desired WiFi network with the given SSID it will bind to that network.
 *
 * Note: requires Android.permission.INTERNET and Android.permission.CHANGE_NETWORK_STATE in
 * the manifest.
 *
 * @param ssid The name of the WiFi network you want to route your requests
 */
private fun routeNetworkRequestsThroughWifi(ssid: String) {
    mConnectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    // ensure prior network callback is invalidated
    unregisterNetworkCallback(mNetworkCallback)

    // new NetworkRequest with WiFi transport type
    val request = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .build()

    // network callback to listen for network changes
    mNetworkCallback = object : ConnectivityManager.NetworkCallback() {

        // on new network ready to use
        override fun onAvailable(network: Network) {

            if (getNetworkSsid(this@RoutingActivity).equals(ssid, ignoreCase = false)) {
                releaseNetworkRoute()
                createNetworkRoute(network)

            } else {
                releaseNetworkRoute()
            }
        }
    }
    mConnectivityManager?.requestNetwork(request, mNetworkCallback)
}

ネットワークコールバックの登録を解除する

private fun unregisterNetworkCallback(networkCallback: ConnectivityManager.NetworkCallback?) {
    if (networkCallback != null) {
        try {
            mConnectivityManager?.unregisterNetworkCallback(networkCallback)

        } catch (ignore: Exception) {
        } finally {
            mNetworkCallback = null
        }
    }
}

ネットワークルートを作成する

private fun createNetworkRoute(network: Network): Boolean? {
    var processBoundToNetwork: Boolean? = false
    when {
    // 23 = Marshmallow
        Build.VERSION.SDK_INT >= 23 -> {
            processBoundToNetwork = mConnectivityManager?.bindProcessToNetwork(network)
        }

    // 21..22 = Lollipop
        Build.VERSION.SDK_INT in 21..22 -> {
            processBoundToNetwork = ConnectivityManager.setProcessDefaultNetwork(network)
        }
    }
    return processBoundToNetwork
}

ネットワークルートを解放する

private fun releaseNetworkRoute(): Boolean? {
    var processBoundToNetwork: Boolean? = false
    when {
    // 23 = Marshmallow
        Build.VERSION.SDK_INT >= 23 -> {
            processBoundToNetwork = mConnectivityManager?.bindProcessToNetwork(null)
        }

    // 21..22 = Lollipop
        Build.VERSION.SDK_INT in 21..22 -> {
            processBoundToNetwork = ConnectivityManager.setProcessDefaultNetwork(null)
        }
    }
    return processBoundToNetwork
}

ヘルパー

private fun getNetworkSsid(context: Context?): String {
    // WiFiManager must use application context (not activity context) otherwise a memory leak can occur
    val mWifiManager = context?.applicationContext?.getSystemService(Context.WIFI_SERVICE) as WifiManager
    val wifiInfo: WifiInfo? = mWifiManager.connectionInfo
    if (wifiInfo?.supplicantState == SupplicantState.COMPLETED) {
        return wifiInfo.ssid.removeSurrounding("\"")
    }
    return ""
}
13
Ryan Amaral