【Android 11 framework学习之wifi 扫描】

wifi扫描时序图如下:
scan chart

setting app调用WifiTracker.resumeScanning,看功能规范什么时候有扫描的需求,例如加入network中时会扫描

/*** com.android.settings.wifi.WifiSettings.handleAddNetworkRequest ***/

void handleAddNetworkRequest(int result, Intent data) {
    if (result == Activity.RESULT_OK) {
        handleAddNetworkRequestSubmitEvent(data);
    }
    mWifiTracker.resumeScanning();
}

调用内部类的resume方法

/*** com.android.settingslib.wifi.WifiTracker.resumeScanning ***/

public void resumeScanning() {
    synchronized (mLock) {
        if (mScanner == null) {
            mScanner = new Scanner();
        }

        if (isWifiEnabled()) {
            mScanner.resume();
        }
    }
}

resume函数会发送MSG_SCAN消息

/*** com.android.settingslib.wifi.WifiTracker.Scanner.resume ***/

void resume() {
    ...
    if (!hasMessages(MSG_SCAN)) {
        sendEmptyMessage(MSG_SCAN);
    }
}

处理MSG_SCAN消息,调用WifiManager的startScan函数,sendEmptyMessageDelayed(step7)

/*** com.android.settingslib.wifi.WifiTracker.Scanner.handleMessage ***/

public void handleMessage(Message message) {
    if (message.what != MSG_SCAN) return;
    if (mWifiManager.startScan()) {
        mRetry = 0;
        } else if (++mRetry >= 3) {
            mRetry = 0;
            if (mContext != null) {
                Toast.makeText(mContext, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
            }
            return;
        }
        sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
    }

调用到WifiServiceImpl.startScan

/*** android.net.wifi.WifiManager.startScan ***/

public boolean startScan(WorkSource workSource) {
    try {
        String packageName = mContext.getOpPackageName();
        return mService.startScan(packageName);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

调用到ScanRequestProxy.startScan

/*** com.android.server.wifi.WifiServiceImpl.startScan ***/

public boolean startScan(String packageName, String featureId) {
    ...
    boolean scanSuccess = mWifiThreadRunner.call(() -> 
            mScanRequestProxy.startScan(callingUid, packageName), null);
    ...
}

先获取扫描的设置,然后再开始扫描。

/*** com.android.server.wifi.ScanRequestProxy.startScan ***/

public boolean startScan(int callingUid, String packageName) {
    if (!mScanningEnabled || !retrieveWifiScannerIfNesessary()) {
        ...
    }
    ...
    // Create the scan settings.
    WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
    // Scan requests from apps with network settings will be of high accuracy type.
    if (fromSettingsOrSetupWizard) {
        settings.type = WifiScanner.TYPE_HIGH_ACCURACY;
    }
    ...
    mWifiScanner.startScan(settings, new ScanRequestProxyScanListener(), workSource);
    mIsScanProcessingComplete = false;
    return true;
}

这是和WifiManager一样的一个代理类,发送CMD_START_SINGLE_SCAN消息

/*** android.net.wifi.WifiScanner.startScan ***/

public void startScan(ScanSettings settings, @Nullable @CallbackExecutor Executor executor, ScanListener listener, WorkSource workSource) {
     ...
     mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
 }

这里用到了异步通道AsyncChannel跨进程与系统服务进行跨进程通信,跨进程通信的系统服务WifiScanningServiceImpl是IWifiScanner的实现类。在WifiScanningServiceImpl中对异步通道过来的消息是由其内部类ClientHandler来处理,所以看ClientHandler.handleMessage

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.ClientHandler.handleMessage ***/

public void handleMessage(Message msg) {
...
switch (msg.what) {
    ...
    case WifiScanner.CMD_START_SINGLE_SCAN:
    case WifiScanner.CMD_STOP_SINGLE_SCAN:
        mSingleScanStateMachine.sendMessage(Message.obtain(msg));
        break;
    ...
}}

ClientHandler在收到CMD_START_SINGLE_SCAN消息后直接给WifiScannerServiceImpl内部状态机WifiSingleScanStateMachine发送了消息,状态机这里之前有详细分析过,直接看DriverStartedState对消息的处理。
WifiSingleScanStateMachine状态机中状态的切换其实就是在ScanningState和IdleState之间切换。在向下发送扫描指令后,会切换到ScanningState,在接收到有扫描结果的消息后会切换到IdleState,所以其实不管当前状态是否为ScanningState,都会调用到tryToStartNewScan方法,这个方法才是真正开启扫描的方法

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine.DriverStartedState.processMessage ***/

public boolean processMessage(Message msg) {
    switch (msg.what) {
        ...
        case WifiScanner.CMD_START_SINGLE_SCAN:
            ...
            if (getCurrentState() == mScanningState) {
                if (activeScanSatisfies(scanSettings)) {
                    mActiveScans.addRequest(ci, handler, workSource, scanSettings);
                } else {
                    mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                }
            } else {
                mPendingScans.addRequest(ci, handler, workSource, scanSettings);
                tryToStartNewScan();
            }
            ...
}

前面部分就是设置一些扫描的信道个数,扫描周期,和隐藏列表等省略不写,面主要的是调用ScannerImplsTracker.startSingleScan往下走进行扫描的。

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine.tryToStartNewScan ***/

void tryToStartNewScan() {
    ...
    if (mScannerImplsTracker.startSingleScan(settings, this)) {
        ...
    } else {...}
}

这个方法中其实就是看系统有几种WifiScannerImpl类的实现方式,分别调用他们的startSingleScan方法。

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine.ScannerImplsTracker.startSingleScan ***/

public boolean startSingleScan(WifiNative.ScanSettings scanSettings) {
    ...
    WifiScannerImpl impl = entry.getValue();
    boolean success = impl.startSingleScan();
    ...
}

在安卓11源码中,有一个子类WificondScannerImpl继承了WifiScannerImpl,再分析WificondScannerImpl.startSingleScan

/*** com.android.server.wifi.scanner.WifiScannerImpl.startSingleScan ***/

//父类声明
public abstract boolean startSingleScan(WifiNative.ScanSettings settings,
        WifiNative.ScanEventHandler eventHandler);

前面添加扫描的频段和隐藏的热点省略,重要的是后面调用的WifiNative.scan方法

/*** com.android.server.wifi.scanner.WifiCondScannerImpl.startSingleScan ***/

public boolean startSingleScan(WifiNative.ScanSettings settings,
        WifiNative.ScanEventHandler eventHandler) {
        ...
        success = mWifiNative.scan(mIfaceName, settings.scanType, freqs, hiddenNetworkSSIDSet);
        ...
    }
}

对隐藏热点再次处理后,调用了WifiNl80211Manager.startScan,mWifiCondManager是WifiNl80211Manager类的实例化

/*** com.android.server.wifi.WifiNative.scan ***/

public boolean scan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, Set<Integer> freqs,
        List<String> hiddenNetworkSSIDs) {
    ...
    mIfaceNameforPartialScanResult = ifaceName;
    boolean scanRequested = mWifiCondManager.startScan(ifaceName, scanType, freqs, hiddenNetworkSsidsArrays);
    ...
    return scanRequested;
}

方法前面部分确定了扫描类型、通道的扫描频率和隐藏热点省略,然后调用了IWifiScannerImpl接口的scan方法。IWifiScannerImpl接口为一个AIDL,其实现在底层的C++代码中。到这里整个扫描流程都分析完了。

/*** android.net.wifi.nl80211.WifiNl80211Manager.startScan ***/

public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
       @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) {
   IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
   ...
   try {
       return scannerImpl.scan(settings);
   } catch (RemoteException e1) {
       Log.e(TAG, "Failed to request scan due to remote exception");
   }
   return false;
}

下面继续分析如何上报扫描结果
在开启wifi时,会调用WifiNative中的setupInterfaceForClientInScanMode()方法,向WifiNl80211Manager注册了NormalScanEventCallback回调类

/*** com.android.server.wifi.WifiNative.setupInterfaceForClientInScanMode ***/

public String setupInterfaceForClientInScanMode(
       @NonNull InterfaceCallback interfaceCallback) {
   synchronized (mLock) {
       ...
       if (!mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,
               new NormalScanEventCallback(iface.name),
               new PnoScanEventCallback(iface.name))) {
           Log.e(TAG, "Failed to setup iface in wificond=" + iface.name);
           teardownInterface(iface.name);
           mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
           return null;
       }
      ...
       return iface.name;
   }
}

再看WifiNl80211Manager.setupInterfaceForClientMode方法,可以看到,调用了IWifiScannerImpl接口的实现类中注册了回调函数,之前有提到过此接口的实现在下面C++代码中,这里不往下分析。
那么在扫描成功有扫描结果后,变回调用onScanResultReady回调方法,方法中通过WifiMonitor.broadcastScanResultEvent对外发送了监听消息,监听对应消息的对象都能收到此消息。

/*** com.android.server.wifi.WifiNative.NormalScanEventCallback ***/

private class NormalScanEventCallback implements WifiNl80211Manager.ScanEventCallback {
   private String mIfaceName;
   NormalScanEventCallback(String ifaceName) {
       mIfaceName = ifaceName;
   }
   @Override
   public void onScanResultReady() {
       Log.d(TAG, "Scan result ready event");
       mWifiMonitor.broadcastScanResultEvent(mIfaceName);
   }
   @Override
   public void onScanFailed() {
       Log.d(TAG, "Scan failed event");
       mWifiMonitor.broadcastScanFailedEvent(mIfaceName);
   }
}

再来看WifiMonitor.broadcastScanResultEvent方法。这时,需要知道哪些类向WifiMonitor注册监听了SCAN_RESULTS_EVENT消息

/*** com.android.server.wifi.WifiMonitor.broadcastScanResultEvent ***/

public void broadcastScanResultEvent(String iface) {
   sendMessage(iface, SCAN_RESULTS_EVENT);
}

回到WificondScannerImpl也就是WifiScannerImpl的子类,其构造方法如下。可以看到是WificondScannerImpl注册监听了此消息

/*** com.android.server.wifi.scanner.WificondScannerImpl.WificondScannerImpl ***/

public WificondScannerImpl(Context context, String ifaceName, WifiNative wifiNative,
                          WifiMonitor wifiMonitor, ChannelHelper channelHelper,
                          Looper looper, Clock clock) {
    ...
    mEventHandler = new Handler(looper, this);
    mClock = clock;
    wifiMonitor.registerHandler(getIfaceName(),
            WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
    wifiMonitor.registerHandler(getIfaceName(),
            WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler);
    wifiMonitor.registerHandler(getIfaceName(),
            WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
}

那么在看其是如何处理SCAN_RESULTS_EVENT消息的。首先调用了cancelScanTimeout取消了在AlarmManager中注册的定时任务,然后进入pollLatestScanData方法中

/*** com.android.server.wifi.scanner.WificondScannerImpl.handleMessage ***/

public boolean handleMessage(Message msg) {
   switch(msg.what) {
       ...
       case WifiMonitor.SCAN_RESULTS_EVENT:
            boolean isPartialScanResultas = (msg.arg1 == WifiScanner.ON_PARTIAL_SCAN_RESULTS);
           cancelScanTimeout();
           pollLatestScanData(isPartialScanResultas);
           break;
       default:
           // ignore unknown event
   }
   return true;
}

这个方法主要是从HAL层拉去新扫描的数据,然后处理后调用回调通知上层。在前面调用WificondScannerImpl中的startSingleScan方法时,将一个LastScanSettings对象赋值给了mLastScanSettings,在mLastScanSettings内部有一个WifiNative.ScanEventHandler类的回调对象,其实现类为WifiScanningServiceImpl中的ScanEventHandler。这里在处理完扫描结果后,调用了WifiNative.ScanEventHandler类的回调对象的onScanStatus方法

/*** com.android.server.wifi.scanner.WificondScannerImpl.pollLatestScanData ***/

private void pollLatestScanData(boolean isPartial) {
   synchronized (mSettingsLock) {
        ...
        /*到这里其实扫描已经完成,然后扫描结果还存在hal层,这部分省略的代码其实就是从hal层取扫描结果然后处理成java层需要的样子*/
       if (mLastScanSettings.singleScanEventHandler != null) {
           if (mLastScanSettings.reportSingleScanFullResults) {
               for (ScanResult scanResult : singleScanResults) {
                   // ignore buckets scanned since there is only one bucket for a single scan
                   mLastScanSettings.singleScanEventHandler.onFullScanResult(scanResult,
                           /* bucketsScanned */ 0);
               }
           }
           Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR);
           mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0,
                   getBandScanned(mLastScanSettings.singleScanFreqs),
                   singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
           mLastScanSettings.singleScanEventHandler
                   .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
       }
       mLastScanSettings = null;
   }
}

所以这里我们直接看WifiScanningServiceImpl中的ScanEventHandler.onScanStatus。扫描成功的话在pollLatestScanData会发送WIFI_SCAN_RESULTS_AVAILABLE消息,然后调用reportScanStatusForImpl

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine.ScannerImplsTracker.ScanEventHandler.onScanState ***/

public void onScanStatus(int event) {
   switch (event) {
       case WifiNative.WIFI_SCAN_RESULTS_AVAILABLE:
       case WifiNative.WIFI_SCAN_THRESHOLD_NUM_SCANS:
       case WifiNative.WIFI_SCAN_THRESHOLD_PERCENT:
           reportScanStatusForImpl(mImplIfaceName, STATUS_SUCCEEDED, WifiScanner.ON_COMPLETE_SCAN_RESULTS);
           break;
       case WifiNative.WIFI_SCAN_FAILED:
           reportScanStatusForImpl(mImplIfaceName, STATUS_FAILED), WifiScanner.ON_PARTIAL_SCAN_RESULTS;
           break;
       default:
           Log.e(TAG, "Unknown scan status event: " + event);
           break;
   }
}

这里其实做的操作是检验是否所有的wifi芯片开启的接口扫描都是成功了,这里就先认为是都成功了,其实也会都成功,那么会继续给状态机发送CMD_SCAN_RESULTS_AVAILABLE消息

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine.ScannerImplsTracker.reportScanStatusForImpl ***/

private void reportScanStatusForImpl(@NonNull String implIfaceName, int newStatus, int isPartialScanResult) {
    ...
    Integer currentStatus = mStatusPerImpl.get(implIfaceName);
    if (currentStatus != null && currentStatus == STATUS_PENDING) {
        mStatusPerImpl.put(implIfaceName, newStatus);
    }
    // Now check if all the scanner impls scan status is available.
    int consolidatedStatus = getConsolidatedStatus();
    if (consolidatedStatus == STATUS_SUCCEEDED) {
        sendMessage(CMD_SCAN_RESULTS_AVAILABLE, WifiScanner.ON_COMPLETE_SCAN_RESULTS);
    } else if (consolidatedStatus == STATUS_FAILED) {
        sendMessage(CMD_SCAN_FAILED);
    }
}

这时候根据前面的分析内容,状态机正处于ScanningState状态,那么转入ScanningState状态的处理方法

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine.ScanningState.processMessage ***/

public boolean processMessage(Message msg) {
    switch (msg.what) {
        case CMD_SCAN_RESULTS_AVAILABLE:
            ScanData latestScanResults =
                    mScannerImplsTracker.getLatestSingleScanResults();
            if (latestScanResults != null) {
                ...
                reportScanResults(latestScanResults, WifiScanner.ON_COMPLETE_SCAN_RESULTS);
                mActiveScans.clear();
            } else {
                Log.e(TAG, "latest scan results null unexpectedly");
            }
            transitionTo(mIdleState);
            return HANDLED;
        ...
    }
}

这里主要关注mSingleScanListeners变量中存的回调对象,会通过异步通道向WifiScanner发送CMD_SCAN_RESULT消息。那我们需要关注在什么时候向mSingleScanListeners变量中注册了回调对象。可以回看前面的分析,step7在开启扫描的时候调用了ScanRequestProxy.startScan,在此方法中第一行就调用了retrieveWifiScannerIfNecessary,在这个里面注册回调对象,转入分析ScanRequestProxy.retrieveWifiScannerIfNecessary

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine.reportScanResults ***/

	void reportScanResults(@NonNull ScanData results, int partialScanResultsFlag) {
	    ...
	    for (RequestInfo<Void> entry : mSingleScanListeners) {
	        logCallback("singleScanResults",  entry.clientInfo, entry.handlerId,
	                describeForLog(allResults));
	        entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, partialScanResultsFlag, parcelableAllResults);
	    }
	    ...
	}

可以看到将一个监听类注册到了WifiScanner中,并且记住这里的回调类是GlobalScanListener

/*** com.android.server.wifi.scanner.ScanRequestProxy.retrieveWifiScannerIfNecessary ***/

private boolean retrieveWifiScannerIfNecessary() {
     if (mWifiScanner == null) {
         ...
         if (mWifiScanner != null) {
             mWifiScanner.registerScanListener(
                     new HandlerExecutor(mHandler), new GlobalScanListener());
         }
     }
     return mWifiScanner != null;
}

WifiScanner通过异步通道向WifiScanningServiceImpl发送了CMD_REGISTER_SCAN_LISTENER消息

/*** android.net.wifi.WifiScanner.registerScanListener ***/

public void registerScanListener(@NonNull @CallbackExecutor Executor executor,
       @NonNull ScanListener listener) {
    ...
    int key = addListener(listener, executor);
    if (key == INVALID_KEY) return;
    validateChannel();
    mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key);
}

这时候可以看到,向mSingleScanListeners添加了对应的回调信息

/*** com.android.server.wifi.scanner.WifiScanningServiceImpl.ClientHandler.handleMessage ***/

public void handleMessage(Message msg) {
	...
switch (msg.what) {
    ...
	case WifiScanner.CMD_REGISTER_SCAN_LISTENER:
	    logScanRequest("registerScanListener", ci, msg.arg2, null, null, null);
	    mSingleScanListeners.addRequest(ci, msg.arg2, null, null);
	    replySucceeded(msg);
	    break;
}
...
}

再次回到前面分析的reportScanResults方法,遍历了所有mSingleScanListeners中的回调,并发送CMD_SCAN_RESULT消息给WifiScanner。再看WifiScanner是如何处理CMD_SCAN_RESULT消息的。

/*** android.net.wifi.WifiScanner.ServiceHandler.handleMessage ***/

public void handleMessage(Message msg) {
	...
    switch (msg.what) {
        ...
        case CMD_SCAN_RESULT: {
            ScanListener scanListener = (ScanListener) listener;
            ParcelableScanData parcelableScanData = (ParcelableScanData) msg.obj;
            Binder.clearCallingIdentity();
            executor.execute(() -> scanListener.onResults(parcelableScanData.getResults()));
        } break;
    }
    ...
}

根据WifiScanningServiceImpl返回过来的回调信息,找出对应的回调对象listener,然后调用其onResults方法。
由于在前面注册的回调类是GlobalScanListener,所以直接看GlobalScanListener.onResults

/*** com.android.server.wifi.scanner.ScanRequestProxy.GlobalScanListener.onResults ***/

public void onResults(WifiScanner.ScanData[] scanDatas) {
   ...
   if (WifiScanner.isFullBandScan(scanData.getBandScanned(), false)) {
       // Store the last scan results & send out the scan completion broadcast.
       mLastScanResults.clear();
       mLastScanResults.addAll(Arrays.asList(scanResults));
       sendScanResultBroadcast(true);
       sendScanResultsAvailableToCallbacks();
   }
}

在判断为全频段扫描后,直接调用sendScanResultBroadcast对外发送广播。

/*** com.android.server.wifi.scanner.ScanRequestProxy.sendScanResultBroadcast ***/

private void sendScanResultBroadcast(boolean scanSucceeded) {
   Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
   intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
   mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}

当传入的参数scanSucceeded为false时,则表示没有新的扫描结果,所以一般获取EXTRA_RESULTS_UPDATED值为true时才去拿扫描结果数据。到这里整个扫描开始到有新的扫描结果然后对上发送广播的过程全部分析完了。

参考:
https://blog.csdn.net/qq_43804080/article/details/103499015
https://blog.csdn.net/qq_39036223/article/details/125167281

  • 40
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要设置WiFi静态模式,您需要在Android 10的framework代码中进行以下步骤: 1. 打开WifiManager.java文件并添加以下代码: ``` /** * Set the IP address, gateway, and subnet mask for a static IP configuration. * * @param ipAddress the IP address in integer form * @param gateway the gateway in integer form * @param subnetMask the subnet mask in integer form * @param dns the DNS server addresses in integer form * @return {@code true} if the operation succeeded, {@code false} otherwise * @hide */ public boolean setWifiStaticIpConfiguration(int ipAddress, int gateway, int subnetMask, int[] dns) { WifiConfiguration config = getCurrentWifiConfiguration(); if (config == null) { return false; } config.setIpAssignment(IpAssignment.STATIC); config.setIpAddress(InetAddress.getByAddress(intToByteArray(ipAddress)).getHostAddress()); config.setGateway(InetAddress.getByAddress(intToByteArray(gateway)).getHostAddress()); config.setNetmask(InetAddress.getByAddress(intToByteArray(subnetMask)).getHostAddress()); config.setDns(dns); return saveWifiConfiguration(config); } private byte[] intToByteArray(int value) { return new byte[] { (byte)(value >>> 24), (byte)(value >>> 16), (byte)(value >>> 8), (byte)value}; } ``` 2. 在WifiConfiguration.java文件中,添加以下代码: ``` /** * Set the IP assignment mode. * * @param ipAssignment the IP assignment mode to set * @hide */ public void setIpAssignment(IpAssignment ipAssignment) { mIpAssignment = ipAssignment; } /** * Set the IP address. * * @param ipAddress the IP address to set * @hide */ public void setIpAddress(String ipAddress) { mIpAddress = ipAddress; } /** * Set the gateway. * * @param gateway the gateway to set * @hide */ public void setGateway(String gateway) { mGateway = gateway; } /** * Set the subnet mask. * * @param netmask the subnet mask to set * @hide */ public void setNetmask(String netmask) { mNetmask = netmask; } /** * Set the DNS server addresses. * * @param dns the DNS server addresses to set * @hide */ public void setDns(int[] dns) { mDnsServers.clear(); for (int i = 0; i < dns.length; i++) { mDnsServers.add(InetAddress.getByAddress(intToByteArray(dns[i]))); } } ``` 3. 最后,在Settings app中添加WiFi的静态IP配置。打开WifiSettings.java文件并添加以下代码: ``` /** * Show the dialog for configuring a static IP address. * * @param config the WifiConfiguration to configure * @hide */ public void showWifiStaticIpConfigDialog(WifiConfiguration config) { StaticIpConfigDialogFragment fragment = StaticIpConfigDialogFragment.newInstance(config); fragment.show(getFragmentManager(), StaticIpConfigDialogFragment.TAG); } ``` 这些代码将允许您在Android 10中设置WiFi的静态IP地址。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值