1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.googlecode.android_scripting.facade.wifi; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 20 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 21 import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ; 22 import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ; 23 24 import static com.googlecode.android_scripting.jsonrpc.JsonBuilder.build; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.app.Service; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.net.ConnectivityManager; 34 import android.net.ConnectivityManager.NetworkCallback; 35 import android.net.DhcpInfo; 36 import android.net.MacAddress; 37 import android.net.Network; 38 import android.net.NetworkCapabilities; 39 import android.net.NetworkInfo; 40 import android.net.NetworkInfo.DetailedState; 41 import android.net.NetworkRequest; 42 import android.net.NetworkSpecifier; 43 import android.net.Uri; 44 import android.net.wifi.CoexUnsafeChannel; 45 import android.net.wifi.EasyConnectStatusCallback; 46 import android.net.wifi.ScanResult; 47 import android.net.wifi.SoftApCapability; 48 import android.net.wifi.SoftApConfiguration; 49 import android.net.wifi.SoftApInfo; 50 import android.net.wifi.WifiClient; 51 import android.net.wifi.WifiConfiguration; 52 import android.net.wifi.WifiConfiguration.AuthAlgorithm; 53 import android.net.wifi.WifiConfiguration.KeyMgmt; 54 import android.net.wifi.WifiEnterpriseConfig; 55 import android.net.wifi.WifiInfo; 56 import android.net.wifi.WifiManager; 57 import android.net.wifi.WifiManager.NetworkRequestMatchCallback; 58 import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback; 59 import android.net.wifi.WifiManager.SubsystemRestartTrackingCallback; 60 import android.net.wifi.WifiManager.WifiLock; 61 import android.net.wifi.WifiNetworkSpecifier; 62 import android.net.wifi.WifiNetworkSuggestion; 63 import android.net.wifi.WpsInfo; 64 import android.net.wifi.hotspot2.ConfigParser; 65 import android.net.wifi.hotspot2.OsuProvider; 66 import android.net.wifi.hotspot2.PasspointConfiguration; 67 import android.net.wifi.hotspot2.ProvisioningCallback; 68 import android.os.Bundle; 69 import android.os.Handler; 70 import android.os.HandlerExecutor; 71 import android.os.HandlerThread; 72 import android.os.PatternMatcher; 73 import android.os.connectivity.WifiActivityEnergyInfo; 74 import android.provider.Settings.SettingNotFoundException; 75 import android.text.TextUtils; 76 import android.util.Base64; 77 import android.util.SparseArray; 78 import android.util.SparseIntArray; 79 80 81 import com.android.internal.annotations.GuardedBy; 82 import com.android.modules.utils.build.SdkLevel; 83 84 import com.googlecode.android_scripting.Log; 85 import com.googlecode.android_scripting.facade.EventFacade; 86 import com.googlecode.android_scripting.facade.FacadeManager; 87 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 88 import com.googlecode.android_scripting.rpc.Rpc; 89 import com.googlecode.android_scripting.rpc.RpcOptional; 90 import com.googlecode.android_scripting.rpc.RpcParameter; 91 92 import org.json.JSONArray; 93 import org.json.JSONException; 94 import org.json.JSONObject; 95 96 import java.io.ByteArrayInputStream; 97 import java.io.ByteArrayOutputStream; 98 import java.io.IOException; 99 import java.io.InputStream; 100 import java.io.ObjectOutput; 101 import java.io.ObjectOutputStream; 102 import java.security.GeneralSecurityException; 103 import java.security.KeyFactory; 104 import java.security.NoSuchAlgorithmException; 105 import java.security.PrivateKey; 106 import java.security.PublicKey; 107 import java.security.cert.CertificateException; 108 import java.security.cert.CertificateFactory; 109 import java.security.cert.X509Certificate; 110 import java.security.spec.InvalidKeySpecException; 111 import java.security.spec.PKCS8EncodedKeySpec; 112 import java.security.spec.X509EncodedKeySpec; 113 import java.util.ArrayList; 114 import java.util.Arrays; 115 import java.util.Collections; 116 import java.util.HashMap; 117 import java.util.List; 118 import java.util.Map; 119 import java.util.concurrent.CountDownLatch; 120 import java.util.concurrent.Executor; 121 import java.util.concurrent.TimeUnit; 122 123 /** 124 * WifiManager functions. 125 */ 126 // TODO: make methods handle various wifi states properly 127 // e.g. wifi connection result will be null when flight mode is on 128 public class WifiManagerFacade extends RpcReceiver { 129 private static final String mEventType = "WifiManager"; 130 // MIME type for passpoint config. 131 private static final String TYPE_WIFICONFIG = "application/x-wifi-config"; 132 private static final int TIMEOUT_MILLIS = 5000; 133 134 private final Service mService; 135 private final WifiManager mWifi; 136 private final ConnectivityManager mCm; 137 private final EventFacade mEventFacade; 138 139 private final IntentFilter mScanFilter; 140 private final IntentFilter mStateChangeFilter; 141 private final IntentFilter mTetherFilter; 142 private final IntentFilter mNetworkSuggestionStateChangeFilter; 143 private final WifiScanReceiver mScanResultsAvailableReceiver; 144 private final WifiScanResultsReceiver mWifiScanResultsReceiver; 145 private final WifiStateChangeReceiver mStateChangeReceiver; 146 private final WifiNetworkSuggestionStateChangeReceiver mNetworkSuggestionStateChangeReceiver; 147 private SubsystemRestartTrackingCallbackFacade mSubsystemRestartTrackingCallback = null; 148 private final HandlerThread mCallbackHandlerThread; 149 private final Object mCallbackLock = new Object(); 150 private boolean mTrackingWifiStateChange; 151 private boolean mTrackingTetherStateChange; 152 private boolean mTrackingNetworkSuggestionStateChange; 153 @GuardedBy("mCallbackLock") 154 private NetworkRequestUserSelectionCallback mNetworkRequestUserSelectionCallback; 155 private final SparseArray<SoftApCallbackImp> mSoftapCallbacks; 156 // This is null if SdkLevel is not at least S 157 @Nullable private WifiManager.CoexCallback mCoexCallback; 158 159 private final BroadcastReceiver mTetherStateReceiver = new BroadcastReceiver() { 160 @Override 161 public void onReceive(Context context, Intent intent) { 162 String action = intent.getAction(); 163 if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) { 164 Log.d("Wifi AP state changed."); 165 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 166 WifiManager.WIFI_AP_STATE_FAILED); 167 if (state == WifiManager.WIFI_AP_STATE_ENABLED) { 168 mEventFacade.postEvent("WifiManagerApEnabled", null); 169 } else if (state == WifiManager.WIFI_AP_STATE_DISABLED) { 170 mEventFacade.postEvent("WifiManagerApDisabled", null); 171 } 172 } else if (ConnectivityManager.ACTION_TETHER_STATE_CHANGED.equals(action)) { 173 Log.d("Tether state changed."); 174 ArrayList<String> available = intent.getStringArrayListExtra( 175 ConnectivityManager.EXTRA_AVAILABLE_TETHER); 176 ArrayList<String> active = intent.getStringArrayListExtra( 177 ConnectivityManager.EXTRA_ACTIVE_TETHER); 178 ArrayList<String> errored = intent.getStringArrayListExtra( 179 ConnectivityManager.EXTRA_ERRORED_TETHER); 180 Bundle msg = new Bundle(); 181 msg.putStringArrayList("AVAILABLE_TETHER", available); 182 msg.putStringArrayList("ACTIVE_TETHER", active); 183 msg.putStringArrayList("ERRORED_TETHER", errored); 184 mEventFacade.postEvent("TetherStateChanged", msg); 185 } 186 } 187 }; 188 189 private final NetworkRequestMatchCallback mNetworkRequestMatchCallback = 190 new NetworkRequestMatchCallback() { 191 private static final String EVENT_TAG = mEventType + "NetworkRequestMatchCallback"; 192 193 @Override 194 public void onUserSelectionCallbackRegistration( 195 NetworkRequestUserSelectionCallback userSelectionCallback) { 196 synchronized (mCallbackLock) { 197 mNetworkRequestUserSelectionCallback = userSelectionCallback; 198 } 199 } 200 201 @Override 202 public void onAbort() { 203 mEventFacade.postEvent(EVENT_TAG + "OnAbort", null); 204 } 205 206 @Override 207 public void onMatch(List<ScanResult> scanResults) { 208 mEventFacade.postEvent(EVENT_TAG + "OnMatch", scanResults); 209 } 210 211 @Override 212 public void onUserSelectionConnectSuccess(WifiConfiguration wifiConfiguration) { 213 mEventFacade.postEvent(EVENT_TAG + "OnUserSelectionConnectSuccess", 214 wifiConfiguration); 215 } 216 217 @Override 218 public void onUserSelectionConnectFailure(WifiConfiguration wifiConfiguration) { 219 mEventFacade.postEvent(EVENT_TAG + "OnUserSelectionConnectFailure", 220 wifiConfiguration); 221 } 222 }; 223 224 private static class SoftApCallbackImp implements WifiManager.SoftApCallback { 225 // A monotonic increasing counter for softap callback ids. 226 private static int sCount = 0; 227 228 private final int mId; 229 private final EventFacade mEventFacade; 230 private final String mEventStr; 231 SoftApCallbackImp(EventFacade eventFacade)232 SoftApCallbackImp(EventFacade eventFacade) { 233 sCount++; 234 mId = sCount; 235 mEventFacade = eventFacade; 236 mEventStr = mEventType + "SoftApCallback-" + mId + "-"; 237 } 238 239 @Override onStateChanged(int state, int failureReason)240 public void onStateChanged(int state, int failureReason) { 241 Bundle msg = new Bundle(); 242 msg.putInt("State", state); 243 msg.putInt("FailureReason", failureReason); 244 mEventFacade.postEvent(mEventStr + "OnStateChanged", msg); 245 } 246 247 @Override onConnectedClientsChanged(List<WifiClient> clients)248 public void onConnectedClientsChanged(List<WifiClient> clients) { 249 ArrayList<MacAddress> macAddresses = new ArrayList<>(); 250 clients.forEach(x -> macAddresses.add(x.getMacAddress())); 251 Bundle msg = new Bundle(); 252 msg.putInt("NumClients", clients.size()); 253 msg.putParcelableArrayList("MacAddresses", macAddresses); 254 mEventFacade.postEvent(mEventStr + "OnNumClientsChanged", msg); 255 mEventFacade.postEvent(mEventStr + "OnConnectedClientsChanged", clients); 256 } 257 258 @Override onConnectedClientsChanged(SoftApInfo info, List<WifiClient> clients)259 public void onConnectedClientsChanged(SoftApInfo info, List<WifiClient> clients) { 260 ArrayList<MacAddress> macAddresses = new ArrayList<>(); 261 clients.forEach(x -> macAddresses.add(x.getMacAddress())); 262 Bundle msg = new Bundle(); 263 msg.putParcelable("Info", info); 264 msg.putParcelableArrayList("ClientsMacAddress", macAddresses); 265 mEventFacade.postEvent(mEventStr + "OnConnectedClientsChangedWithInfo", msg); 266 } 267 268 @Override onInfoChanged(SoftApInfo softApInfo)269 public void onInfoChanged(SoftApInfo softApInfo) { 270 mEventFacade.postEvent(mEventStr + "OnInfoChanged", softApInfo); 271 } 272 273 @Override onInfoChanged(List<SoftApInfo> infos)274 public void onInfoChanged(List<SoftApInfo> infos) { 275 mEventFacade.postEvent(mEventStr + "OnInfoListChanged", infos); 276 } 277 278 @Override onCapabilityChanged(SoftApCapability softApCapability)279 public void onCapabilityChanged(SoftApCapability softApCapability) { 280 mEventFacade.postEvent(mEventStr + "OnCapabilityChanged", softApCapability); 281 } 282 283 @Override onBlockedClientConnecting(WifiClient client, int blockedReason)284 public void onBlockedClientConnecting(WifiClient client, int blockedReason) { 285 Bundle msg = new Bundle(); 286 msg.putString("WifiClient", client.getMacAddress().toString()); 287 msg.putInt("BlockedReason", blockedReason); 288 mEventFacade.postEvent(mEventStr + "OnBlockedClientConnecting", msg); 289 } 290 }; 291 292 private static class CoexCallbackImpl extends WifiManager.CoexCallback { 293 private final EventFacade mEventFacade; 294 private final String mEventStr; 295 CoexCallbackImpl(EventFacade eventFacade)296 CoexCallbackImpl(EventFacade eventFacade) { 297 mEventFacade = eventFacade; 298 mEventStr = mEventType + "CoexCallback"; 299 } 300 301 @Override onCoexUnsafeChannelsChanged( @onNull List<CoexUnsafeChannel> unsafeChannels, int restrictions)302 public void onCoexUnsafeChannelsChanged( 303 @NonNull List<CoexUnsafeChannel> unsafeChannels, int restrictions) { 304 Bundle event = new Bundle(); 305 try { 306 event.putString("KEY_COEX_UNSAFE_CHANNELS", 307 coexUnsafeChannelsToJson(unsafeChannels).toString()); 308 event.putString("KEY_COEX_RESTRICTIONS", 309 coexRestrictionsToJson(restrictions).toString()); 310 mEventFacade.postEvent(mEventStr + "#onCoexUnsafeChannelsChanged", event); 311 } catch (JSONException e) { 312 Log.e("Failed to post event for onCoexUnsafeChannelsChanged: " + e); 313 } 314 } 315 }; 316 317 private WifiLock mLock = null; 318 private boolean mIsConnected = false; 319 WifiManagerFacade(FacadeManager manager)320 public WifiManagerFacade(FacadeManager manager) { 321 super(manager); 322 mService = manager.getService(); 323 mWifi = (WifiManager) mService.getSystemService(Context.WIFI_SERVICE); 324 mCm = (ConnectivityManager) mService.getSystemService(Context.CONNECTIVITY_SERVICE); 325 mEventFacade = manager.getReceiver(EventFacade.class); 326 mCallbackHandlerThread = new HandlerThread("WifiManagerFacade"); 327 328 mScanFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 329 mStateChangeFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION); 330 mStateChangeFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); 331 mStateChangeFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); 332 mStateChangeFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 333 mStateChangeFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1); 334 335 mTetherFilter = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 336 mTetherFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); 337 338 mNetworkSuggestionStateChangeFilter = new IntentFilter( 339 WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION); 340 341 mScanResultsAvailableReceiver = new WifiScanReceiver(mEventFacade); 342 mWifiScanResultsReceiver = new WifiScanResultsReceiver(mEventFacade); 343 mStateChangeReceiver = new WifiStateChangeReceiver(); 344 mNetworkSuggestionStateChangeReceiver = new WifiNetworkSuggestionStateChangeReceiver(); 345 mTrackingWifiStateChange = false; 346 mTrackingTetherStateChange = false; 347 mTrackingNetworkSuggestionStateChange = false; 348 mCallbackHandlerThread.start(); 349 mSoftapCallbacks = new SparseArray<>(); 350 if (SdkLevel.isAtLeastS()) { 351 mCoexCallback = new CoexCallbackImpl(mEventFacade); 352 } 353 } 354 makeLock(int wifiMode)355 private void makeLock(int wifiMode) { 356 if (mLock == null) { 357 mLock = mWifi.createWifiLock(wifiMode, "sl4a"); 358 mLock.acquire(); 359 } 360 } 361 362 /** 363 * Handle Broadcast receiver for Scan Result 364 * 365 * @parm eventFacade Object of EventFacade 366 */ 367 class WifiScanReceiver extends BroadcastReceiver { 368 private final EventFacade mEventFacade; 369 WifiScanReceiver(EventFacade eventFacade)370 WifiScanReceiver(EventFacade eventFacade) { 371 mEventFacade = eventFacade; 372 } 373 374 @Override onReceive(Context c, Intent intent)375 public void onReceive(Context c, Intent intent) { 376 String action = intent.getAction(); 377 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 378 if (!intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)) { 379 Log.w("Wifi connection scan failed, ignoring."); 380 mEventFacade.postEvent(mEventType + "ScanFailure", null); 381 } else { 382 Bundle mResults = new Bundle(); 383 Log.d("Wifi connection scan finished, results available."); 384 mResults.putLong("Timestamp", System.currentTimeMillis() / 1000); 385 mEventFacade.postEvent(mEventType + "ScanResultsAvailable", mResults); 386 } 387 mService.unregisterReceiver(mScanResultsAvailableReceiver); 388 } 389 } 390 } 391 392 class WifiScanResultsReceiver extends WifiManager.ScanResultsCallback { 393 private final EventFacade mEventFacade; 394 WifiScanResultsReceiver(EventFacade eventFacade)395 WifiScanResultsReceiver(EventFacade eventFacade) { 396 mEventFacade = eventFacade; 397 } 398 @Override onScanResultsAvailable()399 public void onScanResultsAvailable() { 400 Bundle mResults = new Bundle(); 401 Log.d("Wifi connection scan finished, results available."); 402 mResults.putLong("Timestamp", System.currentTimeMillis() / 1000); 403 mEventFacade.postEvent(mEventType + "ScanResultsCallbackOnSuccess", mResults); 404 mWifi.unregisterScanResultsCallback(mWifiScanResultsReceiver); 405 } 406 } 407 408 class WifiActionListener implements WifiManager.ActionListener { 409 private final EventFacade mEventFacade; 410 private final String TAG; 411 WifiActionListener(EventFacade eventFacade, String tag)412 public WifiActionListener(EventFacade eventFacade, String tag) { 413 mEventFacade = eventFacade; 414 this.TAG = tag; 415 } 416 417 @Override onSuccess()418 public void onSuccess() { 419 Log.d("WifiActionListener onSuccess called for " + mEventType + TAG + "OnSuccess"); 420 mEventFacade.postEvent(mEventType + TAG + "OnSuccess", null); 421 } 422 423 @Override onFailure(int reason)424 public void onFailure(int reason) { 425 Log.d("WifiActionListener onFailure called for" + mEventType); 426 Bundle msg = new Bundle(); 427 msg.putInt("reason", reason); 428 mEventFacade.postEvent(mEventType + TAG + "OnFailure", msg); 429 } 430 } 431 432 public class WifiStateChangeReceiver extends BroadcastReceiver { 433 String mCachedWifiInfo = ""; 434 435 /** 436 * When a peer to peer request is active, WifiManager.getConnectionInfo() returns 437 * the peer to peer connection details. Hence use networking API's to retrieve the 438 * internet connection details. 439 * 440 * But on Android R, we will need to fallback to the legacy getConnectionInfo() API since 441 * WifiInfo doesn't implement TransportInfo. 442 */ getInternetConnectivityWifiInfo()443 private WifiInfo getInternetConnectivityWifiInfo() { 444 if (!SdkLevel.isAtLeastS()) { 445 return mWifi.getConnectionInfo(); 446 } 447 // TODO (b/156867433): We need a location sensitive synchronous API proposed 448 // in aosp/1629501. 449 final CountDownLatch waitForNetwork = new CountDownLatch(1); 450 final class AnswerBox { 451 public WifiInfo wifiInfo; 452 } 453 final AnswerBox answerBox = new AnswerBox(); 454 final NetworkCallback networkCallback = 455 new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) { 456 @Override 457 public void onCapabilitiesChanged(@NonNull Network network, 458 @NonNull NetworkCapabilities networkCapabilities) { 459 answerBox.wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo(); 460 waitForNetwork.countDown(); 461 } 462 }; 463 mCm.registerNetworkCallback( 464 new NetworkRequest.Builder() 465 .addTransportType(TRANSPORT_WIFI) 466 .addCapability(NET_CAPABILITY_INTERNET) 467 .build(), networkCallback); 468 try { 469 if (!waitForNetwork.await(5, TimeUnit.SECONDS)) { 470 Log.e("Timed out waiting for network to connect"); 471 return null; 472 } 473 return answerBox.wifiInfo; 474 } catch (InterruptedException e) { 475 Log.e("Waiting for onAvailable failed", e); 476 return null; 477 } finally { 478 mCm.unregisterNetworkCallback(networkCallback); 479 } 480 } 481 482 @Override onReceive(Context context, Intent intent)483 public void onReceive(Context context, Intent intent) { 484 String action = intent.getAction(); 485 if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 486 Log.d("Wifi network state changed."); 487 NetworkInfo nInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 488 Log.d("NetworkInfo " + nInfo); 489 // If network info is of type wifi, send wifi events. 490 if (nInfo.getType() == ConnectivityManager.TYPE_WIFI) { 491 if (nInfo.getDetailedState().equals(DetailedState.CONNECTED)) { 492 WifiInfo wInfo = getInternetConnectivityWifiInfo(); 493 if (wInfo == null) { 494 Log.e("Failed to get WifiInfo for internet connection. " 495 + "Not sending wifi network connection event"); 496 return; 497 } 498 String bssid = wInfo.getBSSID(); 499 if (bssid != null && !mCachedWifiInfo.equals(wInfo.toString())) { 500 Log.d("WifiNetworkConnected"); 501 mEventFacade.postEvent("WifiNetworkConnected", wInfo); 502 } 503 mCachedWifiInfo = wInfo.toString(); 504 } else { 505 if (nInfo.getDetailedState().equals(DetailedState.DISCONNECTED)) { 506 if (!mCachedWifiInfo.equals("")) { 507 mCachedWifiInfo = ""; 508 mEventFacade.postEvent("WifiNetworkDisconnected", null); 509 } 510 } 511 } 512 } 513 } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) { 514 Log.d("Supplicant connection state changed."); 515 mIsConnected = intent 516 .getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false); 517 Bundle msg = new Bundle(); 518 msg.putBoolean("Connected", mIsConnected); 519 mEventFacade.postEvent("SupplicantConnectionChanged", msg); 520 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 521 int state = intent.getIntExtra( 522 WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED); 523 Log.d("Wifi state changed to " + state); 524 boolean enabled; 525 if (state == WifiManager.WIFI_STATE_DISABLED) { 526 enabled = false; 527 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 528 enabled = true; 529 } else { 530 // we only care about enabled/disabled. 531 Log.v("Ignoring intermediate wifi state change event..."); 532 return; 533 } 534 Bundle msg = new Bundle(); 535 msg.putBoolean("enabled", enabled); 536 mEventFacade.postEvent("WifiStateChanged", msg); 537 } 538 } 539 } 540 541 public class WifiNetworkSuggestionStateChangeReceiver extends BroadcastReceiver { 542 @Override onReceive(Context context, Intent intent)543 public void onReceive(Context context, Intent intent) { 544 String action = intent.getAction(); 545 if (action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) { 546 WifiNetworkSuggestion networkSuggestion = 547 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_SUGGESTION); 548 mEventFacade.postEvent( 549 "WifiNetworkSuggestionPostConnection", 550 networkSuggestion.wifiConfiguration.SSID); 551 } 552 } 553 } 554 555 public class WifiWpsCallback extends WifiManager.WpsCallback { 556 private static final String tag = "WifiWps"; 557 558 @Override onStarted(String pin)559 public void onStarted(String pin) { 560 Bundle msg = new Bundle(); 561 msg.putString("pin", pin); 562 mEventFacade.postEvent(tag + "OnStarted", msg); 563 } 564 565 @Override onSucceeded()566 public void onSucceeded() { 567 Log.d("Wps op succeeded"); 568 mEventFacade.postEvent(tag + "OnSucceeded", null); 569 } 570 571 @Override onFailed(int reason)572 public void onFailed(int reason) { 573 Bundle msg = new Bundle(); 574 msg.putInt("reason", reason); 575 mEventFacade.postEvent(tag + "OnFailed", msg); 576 } 577 } 578 applyingkeyMgmt(WifiConfiguration config, ScanResult result)579 private void applyingkeyMgmt(WifiConfiguration config, ScanResult result) { 580 if (result.capabilities.contains("WEP")) { 581 config.allowedKeyManagement.set(KeyMgmt.NONE); 582 config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN); 583 config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED); 584 } else if (result.capabilities.contains("PSK")) { 585 config.allowedKeyManagement.set(KeyMgmt.WPA_PSK); 586 } else if (result.capabilities.contains("EAP")) { 587 // this is probably wrong, as we don't have a way to enter the enterprise config 588 config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); 589 config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); 590 } else { 591 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 592 } 593 } 594 genWifiConfig(JSONObject j)595 private WifiConfiguration genWifiConfig(JSONObject j) throws JSONException { 596 if (j == null) { 597 return null; 598 } 599 WifiConfiguration config = new WifiConfiguration(); 600 if (j.has("SSID")) { 601 config.SSID = "\"" + j.getString("SSID") + "\""; 602 } else if (j.has("ssid")) { 603 config.SSID = "\"" + j.getString("ssid") + "\""; 604 } 605 if (j.has("password")) { 606 String security; 607 608 // Check if new security type SAE (WPA3) is present. Default to PSK 609 if (j.has("security")) { 610 if (TextUtils.equals(j.getString("security"), "SAE")) { 611 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); 612 } else { 613 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 614 } 615 } else { 616 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 617 } 618 config.preSharedKey = "\"" + j.getString("password") + "\""; 619 } else if (j.has("preSharedKey")) { 620 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 621 config.preSharedKey = j.getString("preSharedKey"); 622 } else { 623 if (j.has("security")) { 624 if (TextUtils.equals(j.getString("security"), "OWE")) { 625 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); 626 } else { 627 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); 628 } 629 } else { 630 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); 631 } 632 } 633 if (j.has("BSSID")) { 634 config.BSSID = j.getString("BSSID"); 635 } 636 if (j.has("hiddenSSID")) { 637 config.hiddenSSID = j.getBoolean("hiddenSSID"); 638 } 639 if (j.has("priority")) { 640 config.priority = j.getInt("priority"); 641 } 642 if (j.has("apBand")) { 643 config.apBand = j.getInt("apBand"); 644 } 645 if (j.has("wepKeys")) { 646 // Looks like we only support static WEP. 647 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP); 648 JSONArray keys = j.getJSONArray("wepKeys"); 649 String[] wepKeys = new String[keys.length()]; 650 for (int i = 0; i < keys.length(); i++) { 651 wepKeys[i] = keys.getString(i); 652 } 653 config.wepKeys = wepKeys; 654 } 655 if (j.has("wepTxKeyIndex")) { 656 config.wepTxKeyIndex = j.getInt("wepTxKeyIndex"); 657 } 658 if (j.has("meteredOverride")) { 659 config.meteredOverride = j.getInt("meteredOverride"); 660 } 661 if (j.has("macRand")) { 662 config.macRandomizationSetting = j.getInt("macRand"); 663 } 664 if (j.has("carrierId")) { 665 config.carrierId = j.getInt("carrierId"); 666 } 667 return config; 668 } 669 genWifiEnterpriseConfig(JSONObject j)670 private static WifiEnterpriseConfig genWifiEnterpriseConfig(JSONObject j) throws JSONException, 671 GeneralSecurityException { 672 WifiEnterpriseConfig eConfig = new WifiEnterpriseConfig(); 673 if (j.has(WifiEnterpriseConfig.EAP_KEY)) { 674 int eap = j.getInt(WifiEnterpriseConfig.EAP_KEY); 675 eConfig.setEapMethod(eap); 676 } 677 if (j.has(WifiEnterpriseConfig.PHASE2_KEY)) { 678 int p2Method = j.getInt(WifiEnterpriseConfig.PHASE2_KEY); 679 eConfig.setPhase2Method(p2Method); 680 } 681 if (j.has(WifiEnterpriseConfig.CA_CERT_KEY)) { 682 String certStr = j.getString(WifiEnterpriseConfig.CA_CERT_KEY); 683 Log.v("CA Cert String is " + certStr); 684 eConfig.setCaCertificate(strToX509Cert(certStr)); 685 } 686 if (j.has(WifiEnterpriseConfig.CLIENT_CERT_KEY) 687 && j.has(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY)) { 688 String certStr = j.getString(WifiEnterpriseConfig.CLIENT_CERT_KEY); 689 String keyStr = j.getString(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY); 690 Log.v("Client Cert String is " + certStr); 691 Log.v("Client Key String is " + keyStr); 692 X509Certificate cert = strToX509Cert(certStr); 693 String certAlgo = "RSA"; 694 if (j.has("cert_algo")) { 695 certAlgo = j.getString("cert_algo"); 696 } 697 PrivateKey privKey = strToPrivateKey(keyStr, certAlgo); 698 Log.v("Cert is " + cert); 699 Log.v("Private Key is " + privKey); 700 eConfig.setClientKeyEntry(privKey, cert); 701 } 702 if (j.has(WifiEnterpriseConfig.IDENTITY_KEY)) { 703 String identity = j.getString(WifiEnterpriseConfig.IDENTITY_KEY); 704 Log.v("Setting identity to " + identity); 705 eConfig.setIdentity(identity); 706 } 707 if (j.has(WifiEnterpriseConfig.PASSWORD_KEY)) { 708 String pwd = j.getString(WifiEnterpriseConfig.PASSWORD_KEY); 709 Log.v("Setting password to " + pwd); 710 eConfig.setPassword(pwd); 711 } 712 if (j.has(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY)) { 713 String altSub = j.getString(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY); 714 Log.v("Setting Alt Subject to " + altSub); 715 eConfig.setAltSubjectMatch(altSub); 716 } 717 if (j.has(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY)) { 718 String domSuffix = j.getString(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY); 719 Log.v("Setting Domain Suffix Match to " + domSuffix); 720 eConfig.setDomainSuffixMatch(domSuffix); 721 } 722 if (j.has(WifiEnterpriseConfig.REALM_KEY)) { 723 String realm = j.getString(WifiEnterpriseConfig.REALM_KEY); 724 Log.v("Setting Domain Suffix Match to " + realm); 725 eConfig.setRealm(realm); 726 } 727 if (j.has(WifiEnterpriseConfig.OCSP)) { 728 int ocsp = j.getInt(WifiEnterpriseConfig.OCSP); 729 Log.v("Setting OCSP to " + ocsp); 730 eConfig.setOcsp(ocsp); 731 } 732 return eConfig; 733 } 734 genWifiConfigWithEnterpriseConfig(JSONObject j)735 private WifiConfiguration genWifiConfigWithEnterpriseConfig(JSONObject j) throws JSONException, 736 GeneralSecurityException { 737 if (j == null) { 738 return null; 739 } 740 WifiConfiguration config = new WifiConfiguration(); 741 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); 742 743 if (j.has("security")) { 744 if (TextUtils.equals(j.getString("security"), "SUITE_B_192")) { 745 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); 746 } 747 } 748 749 if (j.has("SSID")) { 750 config.SSID = "\"" + j.getString("SSID") + "\""; 751 } else if (j.has("ssid")) { 752 config.SSID = "\"" + j.getString("ssid") + "\""; 753 } 754 if (j.has("FQDN")) { 755 config.FQDN = j.getString("FQDN"); 756 } 757 if (j.has("providerFriendlyName")) { 758 config.providerFriendlyName = j.getString("providerFriendlyName"); 759 } 760 if (j.has("roamingConsortiumIds")) { 761 JSONArray ids = j.getJSONArray("roamingConsortiumIds"); 762 long[] rIds = new long[ids.length()]; 763 for (int i = 0; i < ids.length(); i++) { 764 rIds[i] = ids.getLong(i); 765 } 766 config.roamingConsortiumIds = rIds; 767 } 768 if (j.has("carrierId")) { 769 config.carrierId = j.getInt("carrierId"); 770 } 771 config.enterpriseConfig = genWifiEnterpriseConfig(j); 772 return config; 773 } 774 775 /** 776 * Generate {@link WifiNetworkSpecifier} from the specified json. 777 */ genWifiNetworkSpecifier(JSONObject j)778 public static NetworkSpecifier genWifiNetworkSpecifier(JSONObject j) throws JSONException, 779 GeneralSecurityException { 780 if (j == null) { 781 return null; 782 } 783 WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder(); 784 if (j.has("SSID")) { 785 builder = builder.setSsid(j.getString("SSID")); 786 } else if (j.has("ssidPattern")) { 787 builder = builder.setSsidPattern( 788 new PatternMatcher(j.getString("ssidPattern"), 789 PatternMatcher.PATTERN_ADVANCED_GLOB)); 790 } 791 if (j.has("BSSID")) { 792 builder = builder.setBssid(MacAddress.fromString(j.getString("BSSID"))); 793 } else if (j.has("bssidPattern")) { 794 builder = builder.setBssidPattern( 795 MacAddress.fromString(j.getJSONArray("bssidPattern").getString(0)), 796 MacAddress.fromString(j.getJSONArray("bssidPattern").getString(1))); 797 } 798 if (j.has("hiddenSSID")) { 799 builder = builder.setIsHiddenSsid(j.getBoolean("hiddenSSID")); 800 } 801 if (j.has("isEnhancedOpen")) { 802 builder = builder.setIsEnhancedOpen(j.getBoolean("isEnhancedOpen")); 803 } 804 boolean isWpa3 = false; 805 if (j.has("isWpa3") && j.getBoolean("isWpa3")) { 806 isWpa3 = true; 807 } 808 if (j.has("password") && !j.has(WifiEnterpriseConfig.EAP_KEY)) { 809 if (!isWpa3) { 810 builder = builder.setWpa2Passphrase(j.getString("password")); 811 } else { 812 builder = builder.setWpa3Passphrase(j.getString("password")); 813 } 814 } 815 if (j.has(WifiEnterpriseConfig.EAP_KEY)) { 816 if (!isWpa3) { 817 builder = builder.setWpa2EnterpriseConfig(genWifiEnterpriseConfig(j)); 818 } else { 819 builder = builder.setWpa3EnterpriseConfig(genWifiEnterpriseConfig(j)); 820 } 821 } 822 return builder.build(); 823 } 824 genWifiNetworkSuggestion(JSONObject j)825 private WifiNetworkSuggestion genWifiNetworkSuggestion(JSONObject j) throws JSONException, 826 GeneralSecurityException, IOException { 827 if (j == null) { 828 return null; 829 } 830 WifiNetworkSuggestion.Builder builder = new WifiNetworkSuggestion.Builder(); 831 if (j.has("isAppInteractionRequired")) { 832 builder = builder.setIsAppInteractionRequired(j.getBoolean("isAppInteractionRequired")); 833 } 834 if (j.has("isUserInteractionRequired")) { 835 builder = builder.setIsUserInteractionRequired( 836 j.getBoolean("isUserInteractionRequired")); 837 } 838 if (j.has("isMetered")) { 839 builder = builder.setIsMetered(j.getBoolean("isMetered")); 840 } 841 if (j.has("priority")) { 842 builder = builder.setPriority(j.getInt("priority")); 843 } 844 if (j.has("carrierId")) { 845 builder.setCarrierId(j.getInt("carrierId")); 846 } 847 if (j.has("enableAutojoin")) { 848 builder.setIsInitialAutojoinEnabled(j.getBoolean("enableAutojoin")); 849 } 850 if (j.has("untrusted")) { 851 builder.setUntrusted(j.getBoolean("untrusted")); 852 } 853 if (j.has("profile")) { 854 builder = builder.setPasspointConfig(genWifiPasspointConfig(j)); 855 } else { 856 if (j.has("SSID")) { 857 builder = builder.setSsid(j.getString("SSID")); 858 } 859 if (j.has("BSSID")) { 860 builder = builder.setBssid(MacAddress.fromString(j.getString("BSSID"))); 861 } 862 if (j.has("hiddenSSID")) { 863 builder = builder.setIsHiddenSsid(j.getBoolean("hiddenSSID")); 864 } 865 if (j.has("isEnhancedOpen")) { 866 builder = builder.setIsEnhancedOpen(j.getBoolean("isEnhancedOpen")); 867 } 868 boolean isWpa3 = false; 869 if (j.has("isWpa3") && j.getBoolean("isWpa3")) { 870 isWpa3 = true; 871 } 872 if (j.has("password") && !j.has(WifiEnterpriseConfig.EAP_KEY)) { 873 if (!isWpa3) { 874 builder = builder.setWpa2Passphrase(j.getString("password")); 875 } else { 876 builder = builder.setWpa3Passphrase(j.getString("password")); 877 } 878 } 879 if (j.has(WifiEnterpriseConfig.EAP_KEY)) { 880 if (!isWpa3) { 881 builder = builder.setWpa2EnterpriseConfig(genWifiEnterpriseConfig(j)); 882 } else { 883 builder = builder.setWpa3EnterpriseConfig(genWifiEnterpriseConfig(j)); 884 } 885 } 886 } 887 if (j.has("enhancedMacRandomizationEnabled") 888 && j.getBoolean("enhancedMacRandomizationEnabled")) { 889 builder = builder.setMacRandomizationSetting( 890 WifiNetworkSuggestion.RANDOMIZATION_NON_PERSISTENT); 891 } 892 893 return builder.build(); 894 } 895 genWifiNetworkSuggestions( JSONArray jsonNetworkSuggestionsArray)896 private List<WifiNetworkSuggestion> genWifiNetworkSuggestions( 897 JSONArray jsonNetworkSuggestionsArray) throws JSONException, GeneralSecurityException, 898 IOException { 899 if (jsonNetworkSuggestionsArray == null) { 900 return null; 901 } 902 List<WifiNetworkSuggestion> networkSuggestions = new ArrayList<>(); 903 for (int i = 0; i < jsonNetworkSuggestionsArray.length(); i++) { 904 networkSuggestions.add( 905 genWifiNetworkSuggestion(jsonNetworkSuggestionsArray.getJSONObject(i))); 906 } 907 return networkSuggestions; 908 } 909 matchScanResult(ScanResult result, String id)910 private boolean matchScanResult(ScanResult result, String id) { 911 if (result.BSSID.equals(id) || result.SSID.equals(id)) { 912 return true; 913 } 914 return false; 915 } 916 parseWpsInfo(String infoStr)917 private WpsInfo parseWpsInfo(String infoStr) throws JSONException { 918 if (infoStr == null) { 919 return null; 920 } 921 JSONObject j = new JSONObject(infoStr); 922 WpsInfo info = new WpsInfo(); 923 if (j.has("setup")) { 924 info.setup = j.getInt("setup"); 925 } 926 if (j.has("BSSID")) { 927 info.BSSID = j.getString("BSSID"); 928 } 929 if (j.has("pin")) { 930 info.pin = j.getString("pin"); 931 } 932 return info; 933 } 934 base64StrToBytes(String input)935 private static byte[] base64StrToBytes(String input) { 936 return Base64.decode(input, Base64.DEFAULT); 937 } 938 strToX509Cert(String certStr)939 private static X509Certificate strToX509Cert(String certStr) throws CertificateException { 940 byte[] certBytes = base64StrToBytes(certStr); 941 InputStream certStream = new ByteArrayInputStream(certBytes); 942 CertificateFactory cf = CertificateFactory.getInstance("X509"); 943 return (X509Certificate) cf.generateCertificate(certStream); 944 } 945 strToPrivateKey(String key, String algo)946 private static PrivateKey strToPrivateKey(String key, String algo) 947 throws NoSuchAlgorithmException, InvalidKeySpecException { 948 byte[] keyBytes = base64StrToBytes(key); 949 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 950 KeyFactory fact = KeyFactory.getInstance(algo); 951 PrivateKey priv = fact.generatePrivate(keySpec); 952 return priv; 953 } 954 strToPublicKey(String key)955 private PublicKey strToPublicKey(String key) throws NoSuchAlgorithmException, 956 InvalidKeySpecException { 957 byte[] keyBytes = base64StrToBytes(key); 958 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 959 KeyFactory fact = KeyFactory.getInstance("RSA"); 960 PublicKey pub = fact.generatePublic(keySpec); 961 return pub; 962 } 963 wifiConfigurationFromScanResult(ScanResult result)964 private WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) { 965 if (result == null) 966 return null; 967 WifiConfiguration config = new WifiConfiguration(); 968 config.SSID = "\"" + result.SSID + "\""; 969 applyingkeyMgmt(config, result); 970 config.BSSID = result.BSSID; 971 return config; 972 } 973 974 @Rpc(description = "test.") wifiTest( @pcParametername = "certString") String certString)975 public String wifiTest( 976 @RpcParameter(name = "certString") String certString) throws CertificateException, IOException { 977 // TODO(angli): Make this work. Convert a X509Certificate back to a string. 978 X509Certificate caCert = strToX509Cert(certString); 979 caCert.getEncoded(); 980 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 981 ObjectOutput out = new ObjectOutputStream(bos); 982 out.writeObject(caCert); 983 byte[] data = bos.toByteArray(); 984 bos.close(); 985 return Base64.encodeToString(data, Base64.DEFAULT); 986 } 987 988 @Rpc(description = "Add a network.") 989 @Deprecated wifiAddNetwork(@pcParametername = "wifiConfig") JSONObject wifiConfig)990 public Integer wifiAddNetwork(@RpcParameter(name = "wifiConfig") JSONObject wifiConfig) 991 throws JSONException { 992 return wifiaddOrUpdateNetwork(wifiConfig); 993 } 994 995 @Rpc(description = "Add or update a network.") wifiaddOrUpdateNetwork(@pcParametername = "wifiConfig") JSONObject wifiConfig)996 public Integer wifiaddOrUpdateNetwork(@RpcParameter(name = "wifiConfig") JSONObject wifiConfig) 997 throws JSONException { 998 return mWifi.addNetwork(genWifiConfig(wifiConfig)); 999 } 1000 1001 @Rpc(description = "Cancel Wi-fi Protected Setup.") wifiCancelWps()1002 public void wifiCancelWps() throws JSONException { 1003 WifiWpsCallback listener = new WifiWpsCallback(); 1004 mWifi.cancelWps(listener); 1005 } 1006 1007 @Rpc(description = "Checks Wifi state.", returns = "True if Wifi is enabled.") wifiCheckState()1008 public Boolean wifiCheckState() { 1009 return mWifi.getWifiState() == WifiManager.WIFI_STATE_ENABLED; 1010 } 1011 1012 /** 1013 * @deprecated Use {@link #wifiConnectByConfig(config)} instead. 1014 */ 1015 @Rpc(description = "Connects to the network with the given configuration") 1016 @Deprecated wifiConnect(@pcParametername = "config") JSONObject config)1017 public Boolean wifiConnect(@RpcParameter(name = "config") JSONObject config) 1018 throws JSONException { 1019 try { 1020 wifiConnectByConfig(config); 1021 } catch (GeneralSecurityException e) { 1022 String msg = "Caught GeneralSecurityException with the provided" 1023 + "configuration"; 1024 throw new RuntimeException(msg); 1025 } 1026 return true; 1027 } 1028 1029 /** 1030 * @deprecated Use {@link #wifiConnectByConfig(config)} instead. 1031 */ 1032 @Rpc(description = "Connects to the network with the given configuration") 1033 @Deprecated wifiEnterpriseConnect(@pcParametername = "config") JSONObject config)1034 public Boolean wifiEnterpriseConnect(@RpcParameter(name = "config") 1035 JSONObject config) throws JSONException, GeneralSecurityException { 1036 try { 1037 wifiConnectByConfig(config); 1038 } catch (GeneralSecurityException e) { 1039 throw e; 1040 } 1041 return true; 1042 } 1043 1044 /** 1045 * Connects to a wifi network using configuration. 1046 * @param config JSONObject Dictionary of wifi connection parameters 1047 * @throws JSONException 1048 * @throws GeneralSecurityException 1049 */ 1050 @Rpc(description = "Connects to the network with the given configuration") wifiConnectByConfig(@pcParametername = "config") JSONObject config)1051 public void wifiConnectByConfig(@RpcParameter(name = "config") JSONObject config) 1052 throws JSONException, GeneralSecurityException { 1053 WifiConfiguration wifiConfig; 1054 WifiActionListener listener; 1055 // Check if this is 802.1x or 802.11x config. 1056 if (config.has(WifiEnterpriseConfig.EAP_KEY)) { 1057 wifiConfig = genWifiConfigWithEnterpriseConfig(config); 1058 } else { 1059 wifiConfig = genWifiConfig(config); 1060 } 1061 listener = new WifiActionListener(mEventFacade, 1062 WifiConstants.WIFI_CONNECT_BY_CONFIG_CALLBACK); 1063 mWifi.connect(wifiConfig, listener); 1064 } 1065 1066 /** 1067 * Gets the Wi-Fi factory MAC addresses. 1068 * @return An array of String represnting Wi-Fi MAC addresses, 1069 * Or an empty Srting if failed. 1070 */ 1071 @Rpc(description = "Gets the Wi-Fi factory MAC addresses", returns = "An array of String, representing the MAC address") wifigetFactorymacAddresses()1072 public String[] wifigetFactorymacAddresses(){ 1073 return mWifi.getFactoryMacAddresses(); 1074 } 1075 1076 @Rpc(description = "Gets the randomized MAC address", returns = "A MAC address or null") wifigetRandomizedMacAddress(@pcParametername = "config") JSONObject config)1077 public MacAddress wifigetRandomizedMacAddress(@RpcParameter(name = "config") JSONObject config) 1078 throws JSONException{ 1079 List<WifiConfiguration> configList = mWifi.getConfiguredNetworks(); 1080 for(WifiConfiguration WifiConfig : configList){ 1081 String ssid = WifiConfig.SSID; 1082 ssid = ssid.replace("\"", ""); 1083 if (ssid.equals(config.getString("SSID"))){ 1084 return WifiConfig.getRandomizedMacAddress(); 1085 } 1086 } 1087 Log.d("Did not find a matching object in wifiManager."); 1088 return null; 1089 } 1090 /** 1091 * Generate a Passpoint configuration from JSON config. 1092 * @param config JSON config containing base64 encoded Passpoint profile 1093 */ 1094 @Rpc(description = "Generate Passpoint configuration", returns = "PasspointConfiguration object") genWifiPasspointConfig(@pcParameter name = "config") JSONObject config)1095 public PasspointConfiguration genWifiPasspointConfig(@RpcParameter( 1096 name = "config") JSONObject config) 1097 throws JSONException,CertificateException, IOException { 1098 String profileStr = ""; 1099 if (config == null) { 1100 return null; 1101 } 1102 if (config.has("profile")) { 1103 profileStr = config.getString("profile"); 1104 } 1105 return ConfigParser.parsePasspointConfig(TYPE_WIFICONFIG, 1106 profileStr.getBytes()); 1107 } 1108 1109 /** 1110 * Add or update a Passpoint configuration. 1111 * @param config base64 encoded message containing Passpoint profile 1112 * @throws JSONException 1113 */ 1114 @Rpc(description = "Add or update a Passpoint configuration") addUpdatePasspointConfig(@pcParameter name = "config") JSONObject config)1115 public void addUpdatePasspointConfig(@RpcParameter( 1116 name = "config") JSONObject config) 1117 throws JSONException,CertificateException, IOException { 1118 PasspointConfiguration passpointConfig = genWifiPasspointConfig(config); 1119 mWifi.addOrUpdatePasspointConfiguration(passpointConfig); 1120 } 1121 1122 /** 1123 * Remove a Passpoint configuration. 1124 * @param fqdn The FQDN of the passpoint configuration to be removed 1125 * @return true on success; false otherwise 1126 */ 1127 @Rpc(description = "Remove a Passpoint configuration") removePasspointConfig( @pcParametername = "fqdn") String fqdn)1128 public void removePasspointConfig( 1129 @RpcParameter(name = "fqdn") String fqdn) { 1130 mWifi.removePasspointConfiguration(fqdn); 1131 } 1132 1133 /** 1134 * Get list of Passpoint configurations. 1135 * @return A list of FQDNs of the Passpoint configurations 1136 */ 1137 @Rpc(description = "Return the list of installed Passpoint configurations", returns = "A list of Passpoint configurations") getPasspointConfigs()1138 public List<String> getPasspointConfigs() { 1139 List<String> fqdnList = new ArrayList<String>(); 1140 for(PasspointConfiguration passpoint : 1141 mWifi.getPasspointConfigurations()) { 1142 fqdnList.add(passpoint.getHomeSp().getFqdn()); 1143 } 1144 return fqdnList; 1145 } 1146 1147 private class ProvisioningCallbackFacade extends ProvisioningCallback { 1148 private final EventFacade mEventFacade; 1149 ProvisioningCallbackFacade(EventFacade eventFacade)1150 ProvisioningCallbackFacade(EventFacade eventFacade) { 1151 mEventFacade = eventFacade; 1152 } 1153 1154 @Override onProvisioningFailure(int status)1155 public void onProvisioningFailure(int status) { 1156 Log.v("Provisioning Failure " + status); 1157 Bundle msg = new Bundle(); 1158 msg.putString("tag", "failure"); 1159 msg.putInt("reason", status); 1160 mEventFacade.postEvent("onProvisioningCallback", msg); 1161 } 1162 1163 @Override onProvisioningStatus(int status)1164 public void onProvisioningStatus(int status) { 1165 Log.v("Provisioning status " + status); 1166 Bundle msg = new Bundle(); 1167 msg.putString("tag", "status"); 1168 msg.putInt("status", status); 1169 mEventFacade.postEvent("onProvisioningCallback", msg); 1170 } 1171 1172 @Override onProvisioningComplete()1173 public void onProvisioningComplete() { 1174 Log.v("Provisioning Complete"); 1175 Bundle msg = new Bundle(); 1176 msg.putString("tag", "success"); 1177 mEventFacade.postEvent("onProvisioningCallback", msg); 1178 } 1179 } 1180 1181 private class SubsystemRestartTrackingCallbackFacade extends SubsystemRestartTrackingCallback { 1182 private final EventFacade mEventFacade; 1183 SubsystemRestartTrackingCallbackFacade(EventFacade eventFacade)1184 SubsystemRestartTrackingCallbackFacade(EventFacade eventFacade) { 1185 super(); 1186 mEventFacade = eventFacade; 1187 } 1188 1189 @Override onSubsystemRestarting()1190 public void onSubsystemRestarting() { 1191 Log.v("onSubsystemRestarting"); 1192 mEventFacade.postEvent("WifiSubsystemRestarting", null); 1193 } 1194 1195 @Override onSubsystemRestarted()1196 public void onSubsystemRestarted() { 1197 Log.v("onSubsystemRestarted"); 1198 mEventFacade.postEvent("WifiSubsystemRestarted", null); 1199 } 1200 } 1201 buildTestOsuProvider(JSONObject config)1202 private OsuProvider buildTestOsuProvider(JSONObject config) { 1203 String osuServiceDescription = "Google Passpoint Test Service"; 1204 List<Integer> osuMethodList = 1205 Arrays.asList(OsuProvider.METHOD_SOAP_XML_SPP); 1206 1207 try { 1208 if (!config.has("osuSSID")) { 1209 Log.e("missing osuSSID from the config"); 1210 return null; 1211 } 1212 String osuSsid = config.getString("osuSSID"); 1213 1214 if (!config.has("osuUri")) { 1215 Log.e("missing osuUri from the config"); 1216 return null; 1217 } 1218 Uri osuServerUri = Uri.parse(config.getString("osuUri")); 1219 1220 Log.v("OSU Server URI " + osuServerUri.toString()); 1221 if (!config.has("osuFriendlyName")) { 1222 Log.e("missing osuFriendlyName from the config"); 1223 return null; 1224 } 1225 String osuFriendlyName = config.getString("osuFriendlyName"); 1226 1227 if (config.has("description")) { 1228 osuServiceDescription = config.getString("description"); 1229 } 1230 Map<String, String> osuFriendlyNames = new HashMap<>(); 1231 osuFriendlyNames.put("eng", osuFriendlyName); 1232 return new OsuProvider(osuSsid, osuFriendlyNames, osuServiceDescription, 1233 osuServerUri, null, osuMethodList); 1234 } catch (JSONException e) { 1235 Log.e("JSON Parsing error: " + e); 1236 return null; 1237 } 1238 } 1239 1240 /** 1241 * Start subscription provisioning 1242 */ 1243 @Rpc(description = "Starts subscription provisioning flow") startSubscriptionProvisioning( @pcParametername = "configJson") JSONObject configJson)1244 public void startSubscriptionProvisioning( 1245 @RpcParameter(name = "configJson") JSONObject configJson) { 1246 ProvisioningCallback callback = new ProvisioningCallbackFacade(mEventFacade); 1247 mWifi.startSubscriptionProvisioning(buildTestOsuProvider(configJson), 1248 mService.getMainExecutor(), callback); 1249 } 1250 1251 /** 1252 * Connects to a wifi network using networkId. 1253 * @param networkId the network identity for the network in the supplicant 1254 */ 1255 @Rpc(description = "Connects to the network with the given networkId") wifiConnectByNetworkId( @pcParametername = "networkId") Integer networkId)1256 public void wifiConnectByNetworkId( 1257 @RpcParameter(name = "networkId") Integer networkId) { 1258 WifiActionListener listener; 1259 listener = new WifiActionListener(mEventFacade, 1260 WifiConstants.WIFI_CONNECT_BY_NETID_CALLBACK); 1261 mWifi.connect(networkId, listener); 1262 } 1263 1264 @Rpc(description = "Disconnects from the currently active access point.", returns = "True if the operation succeeded.") wifiDisconnect()1265 public Boolean wifiDisconnect() { 1266 return mWifi.disconnect(); 1267 } 1268 1269 @Rpc(description = "Enable/disable autojoin scan and switch network when connected.") wifiSetEnableAutoJoinWhenAssociated(@pcParametername = "enable") Boolean enable)1270 public Boolean wifiSetEnableAutoJoinWhenAssociated(@RpcParameter(name = "enable") Boolean enable) { 1271 return mWifi.setEnableAutoJoinWhenAssociated(enable); 1272 } 1273 1274 @Rpc(description = "Enable a configured network. Initiate a connection if disableOthers is true", returns = "True if the operation succeeded.") wifiEnableNetwork(@pcParametername = "netId") Integer netId, @RpcParameter(name = "disableOthers") Boolean disableOthers)1275 public Boolean wifiEnableNetwork(@RpcParameter(name = "netId") Integer netId, 1276 @RpcParameter(name = "disableOthers") Boolean disableOthers) { 1277 return mWifi.enableNetwork(netId, disableOthers); 1278 } 1279 1280 @Rpc(description = "Enable WiFi verbose logging.") wifiEnableVerboseLogging(@pcParametername = "level") Integer level)1281 public void wifiEnableVerboseLogging(@RpcParameter(name = "level") Integer level) { 1282 mWifi.setVerboseLoggingEnabled(level > 0); 1283 } 1284 1285 @Rpc(description = "Resets all WifiManager settings.") wifiFactoryReset()1286 public void wifiFactoryReset() { 1287 mWifi.factoryReset(); 1288 } 1289 1290 /** 1291 * Forget a wifi network by networkId. 1292 * 1293 * @param networkId Id of wifi network 1294 */ 1295 @Rpc(description = "Forget a wifi network by networkId") wifiForgetNetwork(@pcParametername = "wifiSSID") Integer networkId)1296 public void wifiForgetNetwork(@RpcParameter(name = "wifiSSID") Integer networkId) { 1297 WifiActionListener listener = new WifiActionListener(mEventFacade, 1298 WifiConstants.WIFI_FORGET_NETWORK_CALLBACK); 1299 mWifi.forget(networkId, listener); 1300 } 1301 1302 /** 1303 * User disconnect network. 1304 * 1305 * @param ssid SSID of wifi network 1306 */ 1307 @Rpc(description = "Disconnect a wifi network by SSID") wifiUserDisconnectNetwork(@pcParametername = "ssid") String ssid)1308 public void wifiUserDisconnectNetwork(@RpcParameter(name = "ssid") String ssid) { 1309 mWifi.disableEphemeralNetwork("\"" + ssid + "\""); 1310 mWifi.disconnect(); 1311 } 1312 1313 /** 1314 * User disconnect passpoint network. 1315 * 1316 * @param fqdn FQDN of the passpoint network 1317 */ 1318 @Rpc(description = "Disconnect a wifi network by FQDN") wifiUserDisconnectPasspointNetwork(@pcParametername = "fqdn") String fqdn)1319 public void wifiUserDisconnectPasspointNetwork(@RpcParameter(name = "fqdn") String fqdn) { 1320 mWifi.disableEphemeralNetwork(fqdn); 1321 mWifi.disconnect(); 1322 } 1323 1324 /** 1325 * Get SoftAp Configuration with SoftApConfiguration. 1326 */ 1327 @Rpc(description = "Gets the Wi-Fi AP Configuration.") wifiGetApConfiguration()1328 public SoftApConfiguration wifiGetApConfiguration() { 1329 return mWifi.getSoftApConfiguration(); 1330 } 1331 1332 /** 1333 * Get SoftAp Configuration with WifiConfiguration. 1334 * 1335 * Used to test deprecated API to check backward compatible 1336 */ 1337 @Rpc(description = "Gets the Wi-Fi AP Configuration with WifiConfiguration.") wifiGetApConfigurationWithWifiConfiguration()1338 public WifiConfiguration wifiGetApConfigurationWithWifiConfiguration() { 1339 return mWifi.getWifiApConfiguration(); 1340 } 1341 1342 @Rpc(description = "Return a list of all the configured wifi networks.") wifiGetConfiguredNetworks()1343 public List<WifiConfiguration> wifiGetConfiguredNetworks() { 1344 return mWifi.getConfiguredNetworks(); 1345 } 1346 1347 @Rpc(description = "Returns information about the currently active access point.") wifiGetConnectionInfo()1348 public WifiInfo wifiGetConnectionInfo() { 1349 return mWifi.getConnectionInfo(); 1350 } 1351 1352 /** 1353 * Check if wifi network is temporary disabled. 1354 * @param config JSONObject Dictionary of wifi connection parameters. 1355 * @return True if network is disabled temporarily, False if not. 1356 */ 1357 @Rpc(description = "Check if network is temporary disabled") wifiIsNetworkTemporaryDisabledForNetwork( @pcParametername = "config") JSONObject config)1358 public boolean wifiIsNetworkTemporaryDisabledForNetwork( 1359 @RpcParameter(name = "config") JSONObject config) 1360 throws JSONException, GeneralSecurityException { 1361 WifiConfiguration wifiConfig; 1362 if (config.has(WifiEnterpriseConfig.EAP_KEY)) { 1363 wifiConfig = genWifiConfigWithEnterpriseConfig(config); 1364 } else { 1365 wifiConfig = genWifiConfig(config); 1366 } 1367 List<WifiConfiguration> wifiConfigList = wifiGetConfiguredNetworks(); 1368 for (WifiConfiguration conf : wifiConfigList) { 1369 if (conf.getSsidAndSecurityTypeString().equals( 1370 wifiConfig.getSsidAndSecurityTypeString())) { 1371 Log.d("Found matching config in the configured networks."); 1372 return conf.getNetworkSelectionStatus().isNetworkTemporaryDisabled(); 1373 } 1374 } 1375 Log.d("Wifi config is not in list of configured wifi networks."); 1376 return false; 1377 } 1378 1379 /** 1380 * Get wifi standard for wifi connection. 1381 */ 1382 @Rpc(description = "Return connection WiFi standard") wifiGetConnectionStandard()1383 public Integer wifiGetConnectionStandard() { 1384 return mWifi.getConnectionInfo().getWifiStandard(); 1385 } 1386 1387 @Rpc(description = "Returns wifi activity and energy usage info.") wifiGetControllerActivityEnergyInfo()1388 public WifiActivityEnergyInfo wifiGetControllerActivityEnergyInfo() { 1389 WifiActivityEnergyInfo[] mutable = {null}; 1390 CountDownLatch latch = new CountDownLatch(1); 1391 mWifi.getWifiActivityEnergyInfoAsync(new Executor() { 1392 @Override 1393 public void execute(Runnable runnable) { 1394 runnable.run(); 1395 } 1396 }, info -> { 1397 mutable[0] = info; 1398 latch.countDown(); 1399 }); 1400 boolean completedSuccessfully = false; 1401 try { 1402 completedSuccessfully = latch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); 1403 } catch (InterruptedException e) { 1404 Log.w("Interrupted while awaiting for WifiManager.getWifiActivityEnergyInfoAsync()"); 1405 } 1406 if (completedSuccessfully) { 1407 return mutable[0]; 1408 } else { 1409 Log.w("WifiManager.getWifiActivityEnergyInfoAsync() timed out after " 1410 + TIMEOUT_MILLIS + " milliseconds"); 1411 return null; 1412 } 1413 } 1414 1415 @Rpc(description = "Get the country code used by WiFi.") wifiGetCountryCode()1416 public String wifiGetCountryCode() { 1417 return mWifi.getCountryCode(); 1418 } 1419 1420 @Rpc(description = "Get the current network.") wifiGetCurrentNetwork()1421 public Network wifiGetCurrentNetwork() { 1422 return mWifi.getCurrentNetwork(); 1423 } 1424 1425 @Rpc(description = "Get the info from last successful DHCP request.") wifiGetDhcpInfo()1426 public DhcpInfo wifiGetDhcpInfo() { 1427 return mWifi.getDhcpInfo(); 1428 } 1429 1430 @Rpc(description = "Get setting for Framework layer autojoin enable status.") wifiGetEnableAutoJoinWhenAssociated()1431 public Boolean wifiGetEnableAutoJoinWhenAssociated() { 1432 return mWifi.getEnableAutoJoinWhenAssociated(); 1433 } 1434 1435 @Rpc(description = "Get privileged configured networks.") wifiGetPrivilegedConfiguredNetworks()1436 public List<WifiConfiguration> wifiGetPrivilegedConfiguredNetworks() { 1437 return mWifi.getPrivilegedConfiguredNetworks(); 1438 } 1439 1440 @Rpc(description = "Returns the list of access points found during the most recent Wifi scan.") wifiGetScanResults()1441 public List<ScanResult> wifiGetScanResults() { 1442 return mWifi.getScanResults(); 1443 } 1444 1445 @Rpc(description = "Get the current level of WiFi verbose logging.") wifiGetVerboseLoggingLevel()1446 public Integer wifiGetVerboseLoggingLevel() { 1447 return mWifi.isVerboseLoggingEnabled() ? 1 : 0; 1448 } 1449 1450 /** 1451 * Query whether or not the device supports concurrency of Station (STA) + multiple access 1452 * points (AP) (where the APs bridged together). 1453 * 1454 * @return true if this device supports concurrency of STA + multiple APs which are bridged 1455 * together, false otherwise. 1456 */ 1457 @Rpc(description = "true if this adapter supports STA + bridged Soft AP concurrency.") wifiIsStaBridgedApConcurrencySupported()1458 public Boolean wifiIsStaBridgedApConcurrencySupported() { 1459 return mWifi.isStaBridgedApConcurrencySupported(); 1460 } 1461 1462 /** 1463 * Query whether or not the device supports multiple Access point (AP) which are bridged 1464 * together. 1465 * 1466 * @return true if this device supports concurrency of multiple AP which bridged together, 1467 * false otherwise. 1468 */ 1469 @Rpc(description = "true if this adapter supports bridged Soft AP concurrency.") wifiIsBridgedApConcurrencySupported()1470 public Boolean wifiIsBridgedApConcurrencySupported() { 1471 return mWifi.isBridgedApConcurrencySupported(); 1472 } 1473 1474 @Rpc(description = "true if this adapter supports 5 GHz band.") wifiIs5GHzBandSupported()1475 public Boolean wifiIs5GHzBandSupported() { 1476 return mWifi.is5GHzBandSupported(); 1477 } 1478 1479 @Rpc(description = "true if this adapter supports multiple simultaneous connections for" 1480 + "local only use-case.") wifiIsStaConcurrencyForLocalOnlyConnectionsSupported()1481 public Boolean wifiIsStaConcurrencyForLocalOnlyConnectionsSupported() { 1482 return mWifi.isStaConcurrencyForLocalOnlyConnectionsSupported(); 1483 } 1484 1485 @Rpc(description = "true if this adapter supports multiple simultaneous connections for mbb " 1486 + "wifi to wifi switching.") wifiIsMakeBeforeBreakWifiSwitchingSupported()1487 public Boolean wifiIsMakeBeforeBreakWifiSwitchingSupported() { 1488 return mWifi.isMakeBeforeBreakWifiSwitchingSupported(); 1489 } 1490 1491 @Rpc(description = "true if this adapter supports multiple simultaneous connections for " 1492 + "restricted connection use-case.") wifiIsStaConcurrencyForRestrictedConnectionsSupported()1493 public Boolean wifiIsStaConcurrencyForRestrictedConnectionsSupported() { 1494 return mWifi.isStaConcurrencyForRestrictedConnectionsSupported(); 1495 } 1496 1497 @Rpc(description = "Return true if WiFi is enabled.") wifiGetisWifiEnabled()1498 public Boolean wifiGetisWifiEnabled() { 1499 return mWifi.isWifiEnabled(); 1500 } 1501 1502 @Rpc(description = "Return whether Wi-Fi AP is enabled or disabled.") wifiIsApEnabled()1503 public Boolean wifiIsApEnabled() { 1504 return mWifi.isWifiApEnabled(); 1505 } 1506 1507 @Rpc(description = "Check if Device-to-AP RTT is supported.") wifiIsDeviceToApRttSupported()1508 public Boolean wifiIsDeviceToApRttSupported() { 1509 return mWifi.isDeviceToApRttSupported(); 1510 } 1511 1512 @Rpc(description = "Check if Device-to-device RTT is supported.") wifiIsDeviceToDeviceRttSupported()1513 public Boolean wifiIsDeviceToDeviceRttSupported() { 1514 return mWifi.isDeviceToDeviceRttSupported(); 1515 } 1516 1517 /** 1518 * @return true if chipset supports 5GHz band and false otherwise. 1519 */ 1520 @Rpc(description = "Check if the chipset supports 5GHz frequency band.") is5GhzBandSupported()1521 public Boolean is5GhzBandSupported() { 1522 return mWifi.is5GHzBandSupported(); 1523 } 1524 1525 /** 1526 * @return true if chipset supports 6GHz band and false otherwise. 1527 */ 1528 @Rpc(description = "Check if the chipset supports 6GHz frequency band.") is6GhzBandSupported()1529 public Boolean is6GhzBandSupported() { 1530 return mWifi.is6GHzBandSupported(); 1531 } 1532 1533 @Rpc(description = "Check if this adapter supports advanced power/performance counters.") wifiIsEnhancedPowerReportingSupported()1534 public Boolean wifiIsEnhancedPowerReportingSupported() { 1535 return mWifi.isEnhancedPowerReportingSupported(); 1536 } 1537 1538 @Rpc(description = "Check if multicast is enabled.") wifiIsMulticastEnabled()1539 public Boolean wifiIsMulticastEnabled() { 1540 return mWifi.isMulticastEnabled(); 1541 } 1542 1543 @Rpc(description = "true if this adapter supports Wi-Fi Aware APIs.") wifiIsAwareSupported()1544 public Boolean wifiIsAwareSupported() { 1545 return mWifi.isWifiAwareSupported(); 1546 } 1547 1548 @Rpc(description = "true if this adapter supports Off Channel Tunnel Directed Link Setup.") wifiIsOffChannelTdlsSupported()1549 public Boolean wifiIsOffChannelTdlsSupported() { 1550 return mWifi.isOffChannelTdlsSupported(); 1551 } 1552 1553 @Rpc(description = "true if this adapter supports WifiP2pManager (Wi-Fi Direct).") wifiIsP2pSupported()1554 public Boolean wifiIsP2pSupported() { 1555 return mWifi.isP2pSupported(); 1556 } 1557 1558 @Rpc(description = "true if this adapter supports passpoint.") wifiIsPasspointSupported()1559 public Boolean wifiIsPasspointSupported() { 1560 return mWifi.isPasspointSupported(); 1561 } 1562 1563 @Rpc(description = "true if this adapter supports portable Wi-Fi hotspot.") wifiIsPortableHotspotSupported()1564 public Boolean wifiIsPortableHotspotSupported() { 1565 return mWifi.isPortableHotspotSupported(); 1566 } 1567 1568 @Rpc(description = "true if this adapter supports offloaded connectivity scan.") wifiIsPreferredNetworkOffloadSupported()1569 public Boolean wifiIsPreferredNetworkOffloadSupported() { 1570 return mWifi.isPreferredNetworkOffloadSupported(); 1571 } 1572 1573 @Rpc(description = "Check if wifi scanner is supported on this device.") wifiIsScannerSupported()1574 public Boolean wifiIsScannerSupported() { 1575 return mWifi.isWifiScannerSupported(); 1576 } 1577 1578 @Rpc(description = "Check if tdls is supported on this device.") wifiIsTdlsSupported()1579 public Boolean wifiIsTdlsSupported() { 1580 return mWifi.isTdlsSupported(); 1581 } 1582 1583 /** 1584 * @return true if this device supports WPA3-Personal SAE 1585 */ 1586 @Rpc(description = "Check if WPA3-Personal SAE is supported on this device.") wifiIsWpa3SaeSupported()1587 public Boolean wifiIsWpa3SaeSupported() { 1588 return mWifi.isWpa3SaeSupported(); 1589 } 1590 /** 1591 * @return true if this device supports WPA3-Enterprise Suite-B-192 1592 */ 1593 @Rpc(description = "Check if WPA3-Enterprise Suite-B-192 is supported on this device.") wifiIsWpa3SuiteBSupported()1594 public Boolean wifiIsWpa3SuiteBSupported() { 1595 return mWifi.isWpa3SuiteBSupported(); 1596 } 1597 /** 1598 * @return true if this device supports Wi-Fi Enhanced Open (OWE) 1599 */ 1600 @Rpc(description = "Check if Enhanced Open (OWE) is supported on this device.") wifiIsEnhancedOpenSupported()1601 public Boolean wifiIsEnhancedOpenSupported() { 1602 return mWifi.isEnhancedOpenSupported(); 1603 } 1604 1605 /** 1606 * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect) 1607 * Enrollee Responder mode 1608 */ 1609 @Rpc(description = "Check if Easy Connect (DPP) Enrollee responder mode is supported " 1610 + "on this device.") wifiIsEasyConnectEnrolleeResponderModeSupported()1611 public Boolean wifiIsEasyConnectEnrolleeResponderModeSupported() { 1612 return mWifi.isEasyConnectEnrolleeResponderModeSupported(); 1613 } 1614 1615 /** 1616 * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect) 1617 */ 1618 @Rpc(description = "Check if Easy Connect (DPP) is supported on this device.") wifiIsEasyConnectSupported()1619 public Boolean wifiIsEasyConnectSupported() { 1620 return mWifi.isEasyConnectSupported(); 1621 } 1622 1623 @Rpc(description = "Acquires a full Wifi lock.") wifiLockAcquireFull()1624 public void wifiLockAcquireFull() { 1625 makeLock(WifiManager.WIFI_MODE_FULL); 1626 } 1627 1628 @Rpc(description = "Acquires a scan only Wifi lock.") wifiLockAcquireScanOnly()1629 public void wifiLockAcquireScanOnly() { 1630 makeLock(WifiManager.WIFI_MODE_SCAN_ONLY); 1631 } 1632 1633 @Rpc(description = "Acquires a high performance Wifi lock.") wifiLockAcquireFullHighPerf()1634 public void wifiLockAcquireFullHighPerf() { 1635 makeLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF); 1636 } 1637 1638 @Rpc(description = "Acquires a low latency Wifi lock.") wifiLockAcquireFullLowLatency()1639 public void wifiLockAcquireFullLowLatency() { 1640 makeLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY); 1641 } 1642 1643 @Rpc(description = "Releases a previously acquired Wifi lock.") wifiLockRelease()1644 public void wifiLockRelease() { 1645 if (mLock != null) { 1646 mLock.release(); 1647 mLock = null; 1648 } 1649 } 1650 1651 @Rpc(description = "Reassociates with the currently active access point.", returns = "True if the operation succeeded.") wifiReassociate()1652 public Boolean wifiReassociate() { 1653 return mWifi.reassociate(); 1654 } 1655 1656 @Rpc(description = "Reconnects to the currently active access point.", returns = "True if the operation succeeded.") wifiReconnect()1657 public Boolean wifiReconnect() { 1658 return mWifi.reconnect(); 1659 } 1660 1661 @Rpc(description = "Remove a configured network.", returns = "True if the operation succeeded.") wifiRemoveNetwork(@pcParametername = "netId") Integer netId)1662 public Boolean wifiRemoveNetwork(@RpcParameter(name = "netId") Integer netId) { 1663 return mWifi.removeNetwork(netId); 1664 } 1665 getApBandFromChannelFrequency(int freq)1666 private int getApBandFromChannelFrequency(int freq) { 1667 if (ScanResult.is24GHz(freq)) { 1668 return SoftApConfiguration.BAND_2GHZ; 1669 } else if (ScanResult.is5GHz(freq)) { 1670 return SoftApConfiguration.BAND_5GHZ; 1671 } else if (ScanResult.is6GHz(freq)) { 1672 return SoftApConfiguration.BAND_6GHZ; 1673 } else if (ScanResult.is60GHz(freq)) { 1674 return SoftApConfiguration.BAND_60GHZ; 1675 } 1676 return -1; 1677 } 1678 convertJSONArrayToIntArray(JSONArray jArray)1679 private int[] convertJSONArrayToIntArray(JSONArray jArray) throws JSONException { 1680 if (jArray == null) { 1681 return null; 1682 } 1683 int[] iArray = new int[jArray.length()]; 1684 for (int i = 0; i < jArray.length(); i++) { 1685 iArray[i] = jArray.getInt(i); 1686 } 1687 return iArray; 1688 } 1689 createSoftApConfiguration(JSONObject configJson)1690 private SoftApConfiguration createSoftApConfiguration(JSONObject configJson) 1691 throws JSONException { 1692 if (configJson == null) { 1693 return null; 1694 } 1695 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 1696 if (configJson.has("SSID")) { 1697 configBuilder.setSsid(configJson.getString("SSID")); 1698 } 1699 if (configJson.has("password")) { 1700 String pwd = configJson.getString("password"); 1701 // Check if new security type SAE (WPA3) is present. Default to PSK 1702 if (configJson.has("security")) { 1703 String securityType = configJson.getString("security"); 1704 if (TextUtils.equals(securityType, "WPA2_PSK")) { 1705 configBuilder.setPassphrase(pwd, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 1706 } else if (TextUtils.equals(securityType, "WPA3_SAE_TRANSITION")) { 1707 configBuilder.setPassphrase(pwd, 1708 SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION); 1709 } else if (TextUtils.equals(securityType, "WPA3_SAE")) { 1710 configBuilder.setPassphrase(pwd, SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); 1711 } 1712 } else { 1713 configBuilder.setPassphrase(pwd, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 1714 } 1715 } 1716 if (configJson.has("BSSID")) { 1717 configBuilder.setBssid(MacAddress.fromString(configJson.getString("BSSID"))); 1718 } 1719 if (configJson.has("hiddenSSID")) { 1720 configBuilder.setHiddenSsid(configJson.getBoolean("hiddenSSID")); 1721 } 1722 if (configJson.has("apBand")) { 1723 configBuilder.setBand(configJson.getInt("apBand")); 1724 } 1725 if (configJson.has("apChannel") && configJson.has("apBand")) { 1726 configBuilder.setChannel(configJson.getInt("apChannel"), configJson.getInt("apBand")); 1727 } 1728 1729 if (configJson.has("MaxNumberOfClients")) { 1730 configBuilder.setMaxNumberOfClients(configJson.getInt("MaxNumberOfClients")); 1731 } 1732 1733 if (configJson.has("ShutdownTimeoutMillis")) { 1734 configBuilder.setShutdownTimeoutMillis(configJson.getLong("ShutdownTimeoutMillis")); 1735 } 1736 1737 if (configJson.has("AutoShutdownEnabled")) { 1738 configBuilder.setAutoShutdownEnabled(configJson.getBoolean("AutoShutdownEnabled")); 1739 } 1740 1741 if (configJson.has("ClientControlByUserEnabled")) { 1742 configBuilder.setClientControlByUserEnabled( 1743 configJson.getBoolean("ClientControlByUserEnabled")); 1744 } 1745 1746 List allowedClientList = new ArrayList<>(); 1747 if (configJson.has("AllowedClientList")) { 1748 JSONArray allowedList = configJson.getJSONArray("AllowedClientList"); 1749 for (int i = 0; i < allowedList.length(); i++) { 1750 allowedClientList.add(MacAddress.fromString(allowedList.getString(i))); 1751 } 1752 } 1753 1754 List blockedClientList = new ArrayList<>(); 1755 if (configJson.has("BlockedClientList")) { 1756 JSONArray blockedList = configJson.getJSONArray("BlockedClientList"); 1757 for (int j = 0; j < blockedList.length(); j++) { 1758 blockedClientList.add(MacAddress.fromString(blockedList.getString(j))); 1759 } 1760 } 1761 1762 configBuilder.setAllowedClientList(allowedClientList); 1763 configBuilder.setBlockedClientList(blockedClientList); 1764 1765 if (SdkLevel.isAtLeastS()) { 1766 if (configJson.has("apBands")) { 1767 JSONArray jBands = configJson.getJSONArray("apBands"); 1768 int[] bands = convertJSONArrayToIntArray(jBands); 1769 configBuilder.setBands(bands); 1770 } 1771 1772 if (configJson.has("apChannelFrequencies")) { 1773 JSONArray jChannelFrequencys = configJson.getJSONArray("apChannelFrequencies"); 1774 int[] channelFrequencies = convertJSONArrayToIntArray(jChannelFrequencys); 1775 SparseIntArray channels = new SparseIntArray(); 1776 for (int channelFrequency : channelFrequencies) { 1777 if (channelFrequency != 0) { 1778 channels.put(getApBandFromChannelFrequency(channelFrequency), 1779 ScanResult.convertFrequencyMhzToChannelIfSupported( 1780 channelFrequency)); 1781 } 1782 } 1783 if (channels.size() != 0) { 1784 configBuilder.setChannels(channels); 1785 } 1786 } 1787 1788 if (configJson.has("MacRandomizationSetting")) { 1789 configBuilder.setMacRandomizationSetting( 1790 configJson.getInt("MacRandomizationSetting")); 1791 } 1792 1793 if (configJson.has("BridgedModeOpportunisticShutdownEnabled")) { 1794 configBuilder.setBridgedModeOpportunisticShutdownEnabled( 1795 configJson.getBoolean("BridgedModeOpportunisticShutdownEnabled")); 1796 } 1797 1798 if (configJson.has("Ieee80211axEnabled")) { 1799 configBuilder.setIeee80211axEnabled(configJson.getBoolean("Ieee80211axEnabled")); 1800 } 1801 } 1802 return configBuilder.build(); 1803 } 1804 createSoftApWifiConfiguration(JSONObject configJson)1805 private WifiConfiguration createSoftApWifiConfiguration(JSONObject configJson) 1806 throws JSONException { 1807 WifiConfiguration config = genWifiConfig(configJson); 1808 // Need to strip of extra quotation marks for SSID and password. 1809 String ssid = config.SSID; 1810 if (ssid != null) { 1811 config.SSID = ssid.substring(1, ssid.length() - 1); 1812 } 1813 1814 config.allowedKeyManagement.clear(); 1815 String pwd = config.preSharedKey; 1816 if (pwd != null) { 1817 config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); 1818 config.preSharedKey = pwd.substring(1, pwd.length() - 1); 1819 } else { 1820 config.allowedKeyManagement.set(KeyMgmt.NONE); 1821 } 1822 return config; 1823 } 1824 1825 /** 1826 * Set SoftAp Configuration with SoftApConfiguration. 1827 */ 1828 @Rpc(description = "Set configuration for soft AP.") wifiSetWifiApConfiguration( @pcParametername = "configJson") JSONObject configJson)1829 public Boolean wifiSetWifiApConfiguration( 1830 @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException { 1831 return mWifi.setSoftApConfiguration(createSoftApConfiguration(configJson)); 1832 } 1833 1834 /** 1835 * Set SoftAp Configuration with WifiConfiguration. 1836 * 1837 * Used to test deprecated API to check backward compatible. 1838 */ 1839 @Rpc(description = "Set configuration for soft AP with WifiConfig.") wifiSetWifiApConfigurationWithWifiConfiguration( @pcParametername = "configJson") JSONObject configJson)1840 public Boolean wifiSetWifiApConfigurationWithWifiConfiguration( 1841 @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException { 1842 return mWifi.setWifiApConfiguration(createSoftApWifiConfiguration(configJson)); 1843 } 1844 1845 /** 1846 * Register softap callback. 1847 * 1848 * @return the id associated with the {@link SoftApCallbackImp} 1849 * used for registering callback. 1850 */ 1851 @Rpc(description = "Register softap callback function.", 1852 returns = "Id of the callback associated with registering.") registerSoftApCallback()1853 public Integer registerSoftApCallback() { 1854 SoftApCallbackImp softApCallback = new SoftApCallbackImp(mEventFacade); 1855 mSoftapCallbacks.put(softApCallback.mId, softApCallback); 1856 mWifi.registerSoftApCallback( 1857 new HandlerExecutor(new Handler(mCallbackHandlerThread.getLooper())), 1858 softApCallback); 1859 return softApCallback.mId; 1860 } 1861 1862 /** 1863 * Unregister softap callback role for the {@link SoftApCallbackImp} identified by the given 1864 * {@code callbackId}. 1865 * 1866 * @param callbackId the id associated with the {@link SoftApCallbackImp} 1867 * used for registering callback. 1868 * 1869 */ 1870 @Rpc(description = "Unregister softap callback function.") unregisterSoftApCallback(@pcParametername = "callbackId") Integer callbackId)1871 public void unregisterSoftApCallback(@RpcParameter(name = "callbackId") Integer callbackId) { 1872 mWifi.unregisterSoftApCallback(mSoftapCallbacks.get(callbackId)); 1873 mSoftapCallbacks.delete(callbackId); 1874 } 1875 1876 @Rpc(description = "Enable/disable tdls with a mac address.") wifiSetTdlsEnabledWithMacAddress( @pcParametername = "remoteMacAddress") String remoteMacAddress, @RpcParameter(name = "enable") Boolean enable)1877 public void wifiSetTdlsEnabledWithMacAddress( 1878 @RpcParameter(name = "remoteMacAddress") String remoteMacAddress, 1879 @RpcParameter(name = "enable") Boolean enable) { 1880 mWifi.setTdlsEnabledWithMacAddress(remoteMacAddress, enable); 1881 } 1882 1883 @Rpc(description = "Starts a scan for Wifi access points.", returns = "True if the scan was initiated successfully.") wifiStartScan()1884 public Boolean wifiStartScan() { 1885 mService.registerReceiver(mScanResultsAvailableReceiver, mScanFilter); 1886 return mWifi.startScan(); 1887 } 1888 1889 @Rpc(description = "Starts a scan for Wifi access points with scanResultCallback.", 1890 returns = "True if the scan was initiated successfully.") wifiStartScanWithListener()1891 public Boolean wifiStartScanWithListener() { 1892 mWifi.registerScanResultsCallback(mService.getMainExecutor(), mWifiScanResultsReceiver); 1893 return mWifi.startScan(); 1894 } 1895 1896 @Rpc(description = "Start Wi-fi Protected Setup.") wifiStartWps( @pcParametername = "config", description = "A json string with fields \\"setup\\", \\"BSSID\\", and \\"pin\\"") String config)1897 public void wifiStartWps( 1898 @RpcParameter(name = "config", description = "A json string with fields \"setup\", \"BSSID\", and \"pin\"") String config) 1899 throws JSONException { 1900 WpsInfo info = parseWpsInfo(config); 1901 WifiWpsCallback listener = new WifiWpsCallback(); 1902 Log.d("Starting wps with: " + info); 1903 mWifi.startWps(info, listener); 1904 } 1905 1906 @Rpc(description = "Start listening for wifi state change related broadcasts.") wifiStartTrackingStateChange()1907 public void wifiStartTrackingStateChange() { 1908 mService.registerReceiver(mStateChangeReceiver, mStateChangeFilter); 1909 mTrackingWifiStateChange = true; 1910 } 1911 1912 @Rpc(description = "Stop listening for wifi state change related broadcasts.") wifiStopTrackingStateChange()1913 public void wifiStopTrackingStateChange() { 1914 if (mTrackingWifiStateChange == true) { 1915 mService.unregisterReceiver(mStateChangeReceiver); 1916 mTrackingWifiStateChange = false; 1917 } 1918 } 1919 1920 @Rpc(description = "Start listening for tether state change related broadcasts.") wifiStartTrackingTetherStateChange()1921 public void wifiStartTrackingTetherStateChange() { 1922 mService.registerReceiver(mTetherStateReceiver, mTetherFilter); 1923 mTrackingTetherStateChange = true; 1924 } 1925 1926 @Rpc(description = "Stop listening for wifi state change related broadcasts.") wifiStopTrackingTetherStateChange()1927 public void wifiStopTrackingTetherStateChange() { 1928 if (mTrackingTetherStateChange == true) { 1929 mService.unregisterReceiver(mTetherStateReceiver); 1930 mTrackingTetherStateChange = false; 1931 } 1932 } 1933 1934 @Rpc(description = "Start listening for network suggestion change related broadcasts.") wifiStartTrackingNetworkSuggestionStateChange()1935 public void wifiStartTrackingNetworkSuggestionStateChange() { 1936 mService.registerReceiver( 1937 mNetworkSuggestionStateChangeReceiver, mNetworkSuggestionStateChangeFilter); 1938 mTrackingNetworkSuggestionStateChange = true; 1939 } 1940 1941 @Rpc(description = "Stop listening for network suggestion change related broadcasts.") wifiStopTrackingNetworkSuggestionStateChange()1942 public void wifiStopTrackingNetworkSuggestionStateChange() { 1943 if (mTrackingNetworkSuggestionStateChange) { 1944 mService.unregisterReceiver(mNetworkSuggestionStateChangeReceiver); 1945 mTrackingNetworkSuggestionStateChange = false; 1946 } 1947 } 1948 1949 @Rpc(description = "Toggle Wifi on and off.", returns = "True if Wifi is enabled.") wifiToggleState(@pcParametername = "enabled") @pcOptional Boolean enabled)1950 public Boolean wifiToggleState(@RpcParameter(name = "enabled") @RpcOptional Boolean enabled) { 1951 if (enabled == null) { 1952 enabled = !wifiCheckState(); 1953 } 1954 mWifi.setWifiEnabled(enabled); 1955 return enabled; 1956 } 1957 1958 @Rpc(description = "Restart the WiFi subsystem.") restartWifiSubsystem()1959 public void restartWifiSubsystem() { 1960 if (mSubsystemRestartTrackingCallback == null) { 1961 // one-time registration if needed 1962 mSubsystemRestartTrackingCallback = new SubsystemRestartTrackingCallbackFacade( 1963 mEventFacade); 1964 } 1965 mWifi.restartWifiSubsystem(); 1966 } 1967 1968 @Rpc(description = "Toggle Wifi scan always available on and off.", returns = "True if Wifi scan is always available.") wifiToggleScanAlwaysAvailable( @pcParametername = "enabled") @pcOptional Boolean enabled)1969 public Boolean wifiToggleScanAlwaysAvailable( 1970 @RpcParameter(name = "enabled") @RpcOptional Boolean enabled) 1971 throws SettingNotFoundException { 1972 boolean isSet = (enabled == null) ? !mWifi.isScanAlwaysAvailable() : enabled; 1973 mWifi.setScanAlwaysAvailable(isSet); 1974 return isSet; 1975 } 1976 1977 @Rpc(description = "Enable/disable WifiConnectivityManager.") wifiEnableWifiConnectivityManager( @pcParametername = "enable") Boolean enable)1978 public void wifiEnableWifiConnectivityManager( 1979 @RpcParameter(name = "enable") Boolean enable) { 1980 mWifi.allowAutojoinGlobal(enable); 1981 } 1982 1983 /** 1984 * Register network request match callback to simulate the UI flow. 1985 * 1986 * @throws JSONException 1987 * @throws GeneralSecurityException 1988 */ 1989 @Rpc(description = "Register network request match callback") wifiRegisterNetworkRequestMatchCallback()1990 public void wifiRegisterNetworkRequestMatchCallback() 1991 throws JSONException, GeneralSecurityException { 1992 // Listen for UI interaction callbacks 1993 mWifi.registerNetworkRequestMatchCallback( 1994 new HandlerExecutor(new Handler(mCallbackHandlerThread.getLooper())), 1995 mNetworkRequestMatchCallback); 1996 } 1997 1998 /** 1999 * Triggers connect to a specific wifi network. 2000 * 2001 * @param jsonConfig JSONObject Dictionary of wifi connection parameters 2002 * @throws JSONException 2003 * @throws GeneralSecurityException 2004 */ 2005 @Rpc(description = "Connects to the specified network for the ongoing network request") wifiSendUserSelectionForNetworkRequestMatch( @pcParametername = "jsonConfig") JSONObject jsonConfig)2006 public void wifiSendUserSelectionForNetworkRequestMatch( 2007 @RpcParameter(name = "jsonConfig") JSONObject jsonConfig) 2008 throws JSONException, GeneralSecurityException { 2009 synchronized (mCallbackLock) { 2010 if (mNetworkRequestUserSelectionCallback == null) { 2011 throw new IllegalStateException("user callback is null"); 2012 } 2013 // Copy the SSID for user selection. 2014 WifiConfiguration config = new WifiConfiguration(); 2015 if (jsonConfig.has("SSID")) { 2016 config.SSID = "\"" + jsonConfig.getString("SSID") + "\""; 2017 } 2018 mNetworkRequestUserSelectionCallback.select(config); 2019 } 2020 } 2021 2022 /** 2023 * Rejects network request. 2024 * 2025 * @throws JSONException 2026 * @throws GeneralSecurityException 2027 */ 2028 @Rpc(description = "Rejects ongoing network request") wifiSendUserRejectionForNetworkRequestMatch()2029 public void wifiSendUserRejectionForNetworkRequestMatch() 2030 throws JSONException, GeneralSecurityException { 2031 synchronized (mCallbackLock) { 2032 if (mNetworkRequestUserSelectionCallback == null) { 2033 throw new IllegalStateException("user callback is null"); 2034 } 2035 mNetworkRequestUserSelectionCallback.reject(); 2036 } 2037 } 2038 2039 /** 2040 * Add network suggestions. 2041 2042 * @param wifiNetworkSuggestions Array of JSONObject Dictionary of wifi network suggestion 2043 * parameters 2044 * @throws JSONException 2045 * @throws GeneralSecurityException 2046 */ 2047 @Rpc(description = "Add network suggestions to the platform") wifiAddNetworkSuggestions( @pcParametername = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions)2048 public boolean wifiAddNetworkSuggestions( 2049 @RpcParameter(name = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions) 2050 throws JSONException, GeneralSecurityException, IOException { 2051 return mWifi.addNetworkSuggestions(genWifiNetworkSuggestions(wifiNetworkSuggestions)) 2052 == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 2053 } 2054 2055 /** 2056 * Remove network suggestions. 2057 2058 * @param wifiNetworkSuggestions Array of JSONObject Dictionary of wifi network suggestion 2059 * parameters 2060 * @throws JSONException 2061 * @throws GeneralSecurityException 2062 */ 2063 @Rpc(description = "Remove network suggestions from the platform") wifiRemoveNetworkSuggestions( @pcParametername = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions)2064 public boolean wifiRemoveNetworkSuggestions( 2065 @RpcParameter(name = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions) 2066 throws JSONException, GeneralSecurityException, IOException { 2067 return mWifi.removeNetworkSuggestions(genWifiNetworkSuggestions(wifiNetworkSuggestions)) 2068 == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 2069 } 2070 2071 @Override shutdown()2072 public void shutdown() { 2073 wifiLockRelease(); 2074 if (mTrackingWifiStateChange == true) { 2075 wifiStopTrackingStateChange(); 2076 } 2077 if (mTrackingTetherStateChange == true) { 2078 wifiStopTrackingTetherStateChange(); 2079 } 2080 } 2081 2082 private class EasyConnectCallback extends EasyConnectStatusCallback { 2083 private static final String EASY_CONNECT_CALLBACK_TAG = "onDppCallback"; 2084 2085 @Override onEnrolleeSuccess(int newWifiConfigurationId)2086 public void onEnrolleeSuccess(int newWifiConfigurationId) { 2087 Bundle msg = new Bundle(); 2088 msg.putString("Type", "onEnrolleeSuccess"); 2089 msg.putInt("NetworkId", newWifiConfigurationId); 2090 Log.d("Posting event: onEnrolleeSuccess"); 2091 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2092 } 2093 2094 @Override onConfiguratorSuccess(int code)2095 public void onConfiguratorSuccess(int code) { 2096 Bundle msg = new Bundle(); 2097 msg.putString("Type", "onConfiguratorSuccess"); 2098 msg.putInt("Status", code); 2099 Log.d("Posting event: onConfiguratorSuccess"); 2100 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2101 } 2102 2103 @Override onFailure(int code, String ssid, SparseArray<int[]> channelList, int[] bandList)2104 public void onFailure(int code, String ssid, SparseArray<int[]> channelList, 2105 int[] bandList) { 2106 Bundle msg = new Bundle(); 2107 msg.putString("Type", "onFailure"); 2108 msg.putInt("Status", code); 2109 Log.d("Posting event: onFailure"); 2110 if (ssid != null) { 2111 Log.d("onFailure SSID: " + ssid); 2112 msg.putString("onFailureSsid", ssid); 2113 } else { 2114 msg.putString("onFailureSsid", ""); 2115 } 2116 if (channelList != null) { 2117 Log.d("onFailure list of tried channels: " + channelList); 2118 int key; 2119 int index = 0; 2120 JSONObject formattedChannelList = new JSONObject(); 2121 2122 // Build a JSON array of classes, with an array of channels for each class. 2123 do { 2124 try { 2125 key = channelList.keyAt(index); 2126 } catch (java.lang.ArrayIndexOutOfBoundsException e) { 2127 break; 2128 } 2129 try { 2130 JSONArray channelsInClassArray = new JSONArray(); 2131 2132 int[] output = channelList.get(key); 2133 for (int i = 0; i < output.length; i++) { 2134 channelsInClassArray.put(output[i]); 2135 } 2136 formattedChannelList.put(Integer.toString(key), 2137 build(channelsInClassArray)); 2138 } catch (org.json.JSONException e) { 2139 msg.putString("onFailureChannelList", ""); 2140 break; 2141 } 2142 index++; 2143 } while (true); 2144 2145 msg.putString("onFailureChannelList", formattedChannelList.toString()); 2146 } else { 2147 msg.putString("onFailureChannelList", ""); 2148 } 2149 2150 if (bandList != null) { 2151 // Build a JSON array of bands represented as operating classes 2152 Log.d("onFailure list of supported bands: " + bandList); 2153 JSONArray formattedBandList = new JSONArray(); 2154 for (int i = 0; i < bandList.length; i++) { 2155 formattedBandList.put(bandList[i]); 2156 } 2157 msg.putString("onFailureBandList", formattedBandList.toString()); 2158 } else { 2159 msg.putString("onFailureBandList", ""); 2160 } 2161 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2162 } 2163 2164 @Override onProgress(int code)2165 public void onProgress(int code) { 2166 Bundle msg = new Bundle(); 2167 msg.putString("Type", "onProgress"); 2168 msg.putInt("Status", code); 2169 Log.d("Posting event: onProgress"); 2170 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2171 } 2172 2173 @Override onBootstrapUriGenerated(@onNull Uri dppUri)2174 public void onBootstrapUriGenerated(@NonNull Uri dppUri) { 2175 Bundle msg = new Bundle(); 2176 msg.putString("Type", "onBootstrapUriGenerated"); 2177 Log.d("onBootstrapUriGenerated uri: " + dppUri.toString()); 2178 msg.putString("generatedUri", dppUri.toString()); 2179 Log.d("Posting event: onBootstrapUriGenerated"); 2180 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2181 } 2182 } 2183 2184 private static @WifiManager.EasyConnectCryptographyCurve getEasyConnectCryptographyCurve(String curve)2185 int getEasyConnectCryptographyCurve(String curve) { 2186 2187 switch (curve) { 2188 case "secp384r1": 2189 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1; 2190 case "secp521r1": 2191 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1; 2192 case "brainpoolP256r1": 2193 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1; 2194 case "brainpoolP384r1": 2195 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1; 2196 case "brainpoolP512r1": 2197 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1; 2198 case "prime256v1": 2199 default: 2200 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1; 2201 } 2202 } 2203 2204 /** 2205 * Start Easy Connect (DPP) in Initiator-Configurator role: Send Wi-Fi configuration to a peer 2206 * 2207 * @param enrolleeUri Peer URI 2208 * @param selectedNetworkId Wi-Fi configuration ID 2209 */ 2210 @Rpc(description = "Easy Connect Initiator-Configurator: Send Wi-Fi configuration to peer") startEasyConnectAsConfiguratorInitiator(@pcParametername = "enrolleeUri") String enrolleeUri, @RpcParameter(name = "selectedNetworkId") Integer selectedNetworkId, @RpcParameter(name = "netRole") String netRole)2211 public void startEasyConnectAsConfiguratorInitiator(@RpcParameter(name = "enrolleeUri") String 2212 enrolleeUri, @RpcParameter(name = "selectedNetworkId") Integer selectedNetworkId, 2213 @RpcParameter(name = "netRole") String netRole) 2214 throws JSONException { 2215 EasyConnectCallback dppStatusCallback = new EasyConnectCallback(); 2216 int netRoleInternal; 2217 2218 if (netRole.equals("ap")) { 2219 netRoleInternal = WifiManager.EASY_CONNECT_NETWORK_ROLE_AP; 2220 } else { 2221 netRoleInternal = WifiManager.EASY_CONNECT_NETWORK_ROLE_STA; 2222 } 2223 2224 // Start Easy Connect 2225 mWifi.startEasyConnectAsConfiguratorInitiator(enrolleeUri, selectedNetworkId, 2226 netRoleInternal, mService.getMainExecutor(), dppStatusCallback); 2227 } 2228 2229 /** 2230 * Start Easy Connect (DPP) in Initiator-Enrollee role: Receive Wi-Fi configuration from a peer 2231 * 2232 * @param configuratorUri 2233 */ 2234 @Rpc(description = "Easy Connect Initiator-Enrollee: Receive Wi-Fi configuration from peer") startEasyConnectAsEnrolleeInitiator(@pcParametername = "configuratorUri") String configuratorUri)2235 public void startEasyConnectAsEnrolleeInitiator(@RpcParameter(name = "configuratorUri") String 2236 configuratorUri) { 2237 EasyConnectCallback dppStatusCallback = new EasyConnectCallback(); 2238 2239 // Start Easy Connect 2240 mWifi.startEasyConnectAsEnrolleeInitiator(configuratorUri, mService.getMainExecutor(), 2241 dppStatusCallback); 2242 } 2243 2244 /** 2245 * Start Easy Connect (DPP) in Responder-Enrollee role: Receive Wi-Fi configuration from a peer 2246 * 2247 * @param deviceInfo The device specific info to attach in the generated URI 2248 * @param cryptographyCurve Elliptic curve cryptography used to generate DPP 2249 * public/private key pair 2250 */ 2251 @Rpc(description = "Easy Connect Responder-Enrollee: Receive Wi-Fi configuration from peer") startEasyConnectAsEnrolleeResponder(@pcParametername = "deviceInfo") String deviceInfo, @RpcParameter(name = "cryptographyCurve") String cryptographyCurve)2252 public void startEasyConnectAsEnrolleeResponder(@RpcParameter(name = "deviceInfo") String 2253 deviceInfo, @RpcParameter(name = "cryptographyCurve") String cryptographyCurve) { 2254 EasyConnectCallback dppStatusCallback = new EasyConnectCallback(); 2255 2256 // Start Easy Connect 2257 mWifi.startEasyConnectAsEnrolleeResponder(deviceInfo, 2258 getEasyConnectCryptographyCurve(cryptographyCurve), mService.getMainExecutor(), 2259 dppStatusCallback); 2260 } 2261 2262 /** 2263 * Stop Easy Connect (DPP) session 2264 * 2265 */ 2266 @Rpc(description = "Stop Easy Connect session") stopEasyConnectSession()2267 public void stopEasyConnectSession() { 2268 // Stop Easy Connect 2269 mWifi.stopEasyConnectSession(); 2270 } 2271 2272 /** 2273 * Enable/Disable auto join for target network 2274 */ 2275 @Rpc(description = "Set network auto join enable/disable") wifiEnableAutojoin(@pcParametername = "netId") Integer netId, @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin)2276 public void wifiEnableAutojoin(@RpcParameter(name = "netId") Integer netId, 2277 @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin) { 2278 mWifi.allowAutojoin(netId, enableAutojoin); 2279 } 2280 2281 /** 2282 * Enable/Disable auto join for target Passpoint network 2283 */ 2284 @Rpc(description = "Set passpoint network auto join enable/disable") wifiEnableAutojoinPasspoint(@pcParametername = "FQDN") String fqdn, @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin)2285 public void wifiEnableAutojoinPasspoint(@RpcParameter(name = "FQDN") String fqdn, 2286 @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin) { 2287 mWifi.allowAutojoinPasspoint(fqdn, enableAutojoin); 2288 } 2289 genCoexUnsafeChannel(JSONObject j)2290 private static CoexUnsafeChannel genCoexUnsafeChannel(JSONObject j) throws JSONException { 2291 if (j == null || !j.has("band") || !j.has("channel")) { 2292 return null; 2293 } 2294 2295 final int band; 2296 final String jsonBand = j.getString("band"); 2297 if (TextUtils.equals(jsonBand, "24_GHZ")) { 2298 band = WIFI_BAND_24_GHZ; 2299 } else if (TextUtils.equals(jsonBand, "5_GHZ")) { 2300 band = WIFI_BAND_5_GHZ; 2301 } else { 2302 return null; 2303 } 2304 if (j.has("powerCapDbm")) { 2305 return new CoexUnsafeChannel(band, j.getInt("channel"), j.getInt("powerCapDbm")); 2306 } 2307 return new CoexUnsafeChannel(band, j.getInt("channel")); 2308 } 2309 genCoexUnsafeChannels( JSONArray jsonCoexUnsafeChannelsArray)2310 private static List<CoexUnsafeChannel> genCoexUnsafeChannels( 2311 JSONArray jsonCoexUnsafeChannelsArray) throws JSONException { 2312 if (jsonCoexUnsafeChannelsArray == null) { 2313 return Collections.emptyList(); 2314 } 2315 List<CoexUnsafeChannel> unsafeChannels = new ArrayList<>(); 2316 for (int i = 0; i < jsonCoexUnsafeChannelsArray.length(); i++) { 2317 unsafeChannels.add( 2318 genCoexUnsafeChannel(jsonCoexUnsafeChannelsArray.getJSONObject(i))); 2319 } 2320 return unsafeChannels; 2321 } 2322 genCoexRestrictions(JSONArray jsonCoexRestrictionArray)2323 private static int genCoexRestrictions(JSONArray jsonCoexRestrictionArray) 2324 throws JSONException { 2325 if (jsonCoexRestrictionArray == null) { 2326 return 0; 2327 } 2328 int coexRestrictions = 0; 2329 for (int i = 0; i < jsonCoexRestrictionArray.length(); i++) { 2330 final String jsonRestriction = jsonCoexRestrictionArray.getString(i); 2331 if (TextUtils.equals(jsonRestriction, "WIFI_DIRECT")) { 2332 coexRestrictions |= WifiManager.COEX_RESTRICTION_WIFI_DIRECT; 2333 } 2334 if (TextUtils.equals(jsonRestriction, "SOFTAP")) { 2335 coexRestrictions |= WifiManager.COEX_RESTRICTION_SOFTAP; 2336 } 2337 if (TextUtils.equals(jsonRestriction, "WIFI_AWARE")) { 2338 coexRestrictions |= WifiManager.COEX_RESTRICTION_WIFI_AWARE; 2339 } 2340 } 2341 return coexRestrictions; 2342 } 2343 2344 /** 2345 * Converts a set of {@link CoexUnsafeChannel} to a {@link JSONArray} of {@link JSONObject} of 2346 * format: 2347 * { 2348 * "band": <"24_GHZ" or "5_GHZ"> 2349 * "channel" : <Channel Number> 2350 * (Optional) "powerCapDbm" : <Power Cap in Dbm> 2351 * } 2352 */ coexUnsafeChannelsToJson(List<CoexUnsafeChannel> unsafeChannels)2353 private static JSONArray coexUnsafeChannelsToJson(List<CoexUnsafeChannel> unsafeChannels) 2354 throws JSONException { 2355 final JSONArray jsonCoexUnsafeChannelArray = new JSONArray(); 2356 for (CoexUnsafeChannel unsafeChannel : unsafeChannels) { 2357 final String jsonBand; 2358 if (unsafeChannel.getBand() == WIFI_BAND_24_GHZ) { 2359 jsonBand = "24_GHZ"; 2360 } else if (unsafeChannel.getBand() == WIFI_BAND_5_GHZ) { 2361 jsonBand = "5_GHZ"; 2362 } else { 2363 continue; 2364 } 2365 final JSONObject jsonUnsafeChannel = new JSONObject(); 2366 jsonUnsafeChannel.put("band", jsonBand); 2367 jsonUnsafeChannel.put("channel", unsafeChannel.getChannel()); 2368 final int powerCapDbm = unsafeChannel.getPowerCapDbm(); 2369 if (powerCapDbm != CoexUnsafeChannel.POWER_CAP_NONE) { 2370 jsonUnsafeChannel.put("powerCapDbm", powerCapDbm); 2371 } 2372 jsonCoexUnsafeChannelArray.put(jsonUnsafeChannel); 2373 } 2374 return jsonCoexUnsafeChannelArray; 2375 } 2376 2377 /** 2378 * Converts a coex restriction bitmask {@link WifiManager#getCoexRestrictions()} to a JSON array 2379 * of possible values "WIFI_DIRECT", "SOFTAP", "WIFI_AWARE". 2380 */ coexRestrictionsToJson(int coexRestrictions)2381 private static JSONArray coexRestrictionsToJson(int coexRestrictions) { 2382 final JSONArray jsonCoexRestrictionArray = new JSONArray(); 2383 if ((coexRestrictions & WifiManager.COEX_RESTRICTION_WIFI_DIRECT) != 0) { 2384 jsonCoexRestrictionArray.put("WIFI_DIRECT"); 2385 } 2386 if ((coexRestrictions & WifiManager.COEX_RESTRICTION_SOFTAP) != 0) { 2387 jsonCoexRestrictionArray.put("SOFTAP"); 2388 } 2389 if ((coexRestrictions & WifiManager.COEX_RESTRICTION_WIFI_AWARE) != 0) { 2390 jsonCoexRestrictionArray.put("WIFI_AWARE"); 2391 } 2392 return jsonCoexRestrictionArray; 2393 } 2394 2395 /** 2396 * Returns whether the default coex algorithm is enabled or not 2397 * 2398 * @return {@code true} if the default coex algorithm is enabled, {@code false} otherwise. 2399 */ 2400 @Rpc(description = "Returns whether the default coex algorithm is enabled or not") wifiIsDefaultCoexAlgorithmEnabled()2401 public boolean wifiIsDefaultCoexAlgorithmEnabled() { 2402 if (!SdkLevel.isAtLeastS()) { 2403 return false; 2404 } 2405 return mWifi.isDefaultCoexAlgorithmEnabled(); 2406 } 2407 2408 /** 2409 * Sets the active list of unsafe channels to avoid for coex and the restricted Wifi interfaces. 2410 * 2411 * @param unsafeChannels JSONArray representation of {@link CoexUnsafeChannel}. 2412 * See {@link #coexUnsafeChannelsToJson(List)}. 2413 * @param restrictions JSONArray representation of coex restrictions. 2414 * See {@link #coexRestrictionsToJson(int)}. 2415 * @throws JSONException 2416 */ 2417 @Rpc(description = "Set the unsafe channels to avoid for coex") wifiSetCoexUnsafeChannels( @pcParametername = "unsafeChannels") JSONArray unsafeChannels, @RpcParameter(name = "restrictions") JSONArray restrictions)2418 public void wifiSetCoexUnsafeChannels( 2419 @RpcParameter(name = "unsafeChannels") JSONArray unsafeChannels, 2420 @RpcParameter(name = "restrictions") JSONArray restrictions) throws JSONException { 2421 if (!SdkLevel.isAtLeastS()) { 2422 return; 2423 } 2424 mWifi.setCoexUnsafeChannels( 2425 genCoexUnsafeChannels(unsafeChannels), genCoexRestrictions(restrictions)); 2426 } 2427 2428 /** 2429 * Registers a coex callback to start receiving coex update events. 2430 */ 2431 @Rpc(description = "Registers a coex callback to start receiving coex update events") wifiRegisterCoexCallback()2432 public void wifiRegisterCoexCallback() { 2433 if (!SdkLevel.isAtLeastS()) { 2434 return; 2435 } 2436 mWifi.registerCoexCallback( 2437 new HandlerExecutor(mCallbackHandlerThread.getThreadHandler()), mCoexCallback); 2438 } 2439 2440 /** 2441 * Unregisters the coex callback to stop receiving coex update events. 2442 */ 2443 @Rpc(description = "Unregisters the coex callback to stop receiving coex update events") wifiUnregisterCoexCallback()2444 public void wifiUnregisterCoexCallback() { 2445 if (!SdkLevel.isAtLeastS()) { 2446 return; 2447 } 2448 mWifi.unregisterCoexCallback(mCoexCallback); 2449 } 2450 } 2451