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 = "Check if the chipset supports a certain Wi-Fi standard.", returns = "true if standard is supported") wifiIsWifiStandardSupported(@pcParametername = "standard") Integer standard)1498 public Boolean wifiIsWifiStandardSupported(@RpcParameter(name = "standard") Integer standard) { 1499 return mWifi.isWifiStandardSupported(standard); 1500 } 1501 1502 @Rpc(description = "Return true if WiFi is enabled.") wifiGetisWifiEnabled()1503 public Boolean wifiGetisWifiEnabled() { 1504 return mWifi.isWifiEnabled(); 1505 } 1506 1507 @Rpc(description = "Return whether Wi-Fi AP is enabled or disabled.") wifiIsApEnabled()1508 public Boolean wifiIsApEnabled() { 1509 return mWifi.isWifiApEnabled(); 1510 } 1511 1512 @Rpc(description = "Check if Device-to-AP RTT is supported.") wifiIsDeviceToApRttSupported()1513 public Boolean wifiIsDeviceToApRttSupported() { 1514 return mWifi.isDeviceToApRttSupported(); 1515 } 1516 1517 @Rpc(description = "Check if Device-to-device RTT is supported.") wifiIsDeviceToDeviceRttSupported()1518 public Boolean wifiIsDeviceToDeviceRttSupported() { 1519 return mWifi.isDeviceToDeviceRttSupported(); 1520 } 1521 1522 /** 1523 * @return true if chipset supports 5GHz band and false otherwise. 1524 */ 1525 @Rpc(description = "Check if the chipset supports 5GHz frequency band.") is5GhzBandSupported()1526 public Boolean is5GhzBandSupported() { 1527 return mWifi.is5GHzBandSupported(); 1528 } 1529 1530 /** 1531 * @return true if chipset supports 6GHz band and false otherwise. 1532 */ 1533 @Rpc(description = "Check if the chipset supports 6GHz frequency band.") is6GhzBandSupported()1534 public Boolean is6GhzBandSupported() { 1535 return mWifi.is6GHzBandSupported(); 1536 } 1537 1538 @Rpc(description = "Check if this adapter supports advanced power/performance counters.") wifiIsEnhancedPowerReportingSupported()1539 public Boolean wifiIsEnhancedPowerReportingSupported() { 1540 return mWifi.isEnhancedPowerReportingSupported(); 1541 } 1542 1543 @Rpc(description = "Check if multicast is enabled.") wifiIsMulticastEnabled()1544 public Boolean wifiIsMulticastEnabled() { 1545 return mWifi.isMulticastEnabled(); 1546 } 1547 1548 @Rpc(description = "true if this adapter supports Wi-Fi Aware APIs.") wifiIsAwareSupported()1549 public Boolean wifiIsAwareSupported() { 1550 return mWifi.isWifiAwareSupported(); 1551 } 1552 1553 @Rpc(description = "true if this adapter supports Off Channel Tunnel Directed Link Setup.") wifiIsOffChannelTdlsSupported()1554 public Boolean wifiIsOffChannelTdlsSupported() { 1555 return mWifi.isOffChannelTdlsSupported(); 1556 } 1557 1558 @Rpc(description = "true if this adapter supports WifiP2pManager (Wi-Fi Direct).") wifiIsP2pSupported()1559 public Boolean wifiIsP2pSupported() { 1560 return mWifi.isP2pSupported(); 1561 } 1562 1563 @Rpc(description = "true if this adapter supports passpoint.") wifiIsPasspointSupported()1564 public Boolean wifiIsPasspointSupported() { 1565 return mWifi.isPasspointSupported(); 1566 } 1567 1568 @Rpc(description = "true if this adapter supports portable Wi-Fi hotspot.") wifiIsPortableHotspotSupported()1569 public Boolean wifiIsPortableHotspotSupported() { 1570 return mWifi.isPortableHotspotSupported(); 1571 } 1572 1573 @Rpc(description = "true if this adapter supports offloaded connectivity scan.") wifiIsPreferredNetworkOffloadSupported()1574 public Boolean wifiIsPreferredNetworkOffloadSupported() { 1575 return mWifi.isPreferredNetworkOffloadSupported(); 1576 } 1577 1578 @Rpc(description = "Check if wifi scanner is supported on this device.") wifiIsScannerSupported()1579 public Boolean wifiIsScannerSupported() { 1580 return mWifi.isWifiScannerSupported(); 1581 } 1582 1583 @Rpc(description = "Check if tdls is supported on this device.") wifiIsTdlsSupported()1584 public Boolean wifiIsTdlsSupported() { 1585 return mWifi.isTdlsSupported(); 1586 } 1587 1588 /** 1589 * @return true if this device supports WPA3-Personal SAE 1590 */ 1591 @Rpc(description = "Check if WPA3-Personal SAE is supported on this device.") wifiIsWpa3SaeSupported()1592 public Boolean wifiIsWpa3SaeSupported() { 1593 return mWifi.isWpa3SaeSupported(); 1594 } 1595 /** 1596 * @return true if this device supports WPA3-Enterprise Suite-B-192 1597 */ 1598 @Rpc(description = "Check if WPA3-Enterprise Suite-B-192 is supported on this device.") wifiIsWpa3SuiteBSupported()1599 public Boolean wifiIsWpa3SuiteBSupported() { 1600 return mWifi.isWpa3SuiteBSupported(); 1601 } 1602 /** 1603 * @return true if this device supports Wi-Fi Enhanced Open (OWE) 1604 */ 1605 @Rpc(description = "Check if Enhanced Open (OWE) is supported on this device.") wifiIsEnhancedOpenSupported()1606 public Boolean wifiIsEnhancedOpenSupported() { 1607 return mWifi.isEnhancedOpenSupported(); 1608 } 1609 1610 /** 1611 * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect) 1612 * Enrollee Responder mode 1613 */ 1614 @Rpc(description = "Check if Easy Connect (DPP) Enrollee responder mode is supported " 1615 + "on this device.") wifiIsEasyConnectEnrolleeResponderModeSupported()1616 public Boolean wifiIsEasyConnectEnrolleeResponderModeSupported() { 1617 return mWifi.isEasyConnectEnrolleeResponderModeSupported(); 1618 } 1619 1620 /** 1621 * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect) 1622 */ 1623 @Rpc(description = "Check if Easy Connect (DPP) is supported on this device.") wifiIsEasyConnectSupported()1624 public Boolean wifiIsEasyConnectSupported() { 1625 return mWifi.isEasyConnectSupported(); 1626 } 1627 1628 @Rpc(description = "Acquires a full Wifi lock.") wifiLockAcquireFull()1629 public void wifiLockAcquireFull() { 1630 makeLock(WifiManager.WIFI_MODE_FULL); 1631 } 1632 1633 @Rpc(description = "Acquires a scan only Wifi lock.") wifiLockAcquireScanOnly()1634 public void wifiLockAcquireScanOnly() { 1635 makeLock(WifiManager.WIFI_MODE_SCAN_ONLY); 1636 } 1637 1638 @Rpc(description = "Acquires a high performance Wifi lock.") wifiLockAcquireFullHighPerf()1639 public void wifiLockAcquireFullHighPerf() { 1640 makeLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF); 1641 } 1642 1643 @Rpc(description = "Acquires a low latency Wifi lock.") wifiLockAcquireFullLowLatency()1644 public void wifiLockAcquireFullLowLatency() { 1645 makeLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY); 1646 } 1647 1648 @Rpc(description = "Releases a previously acquired Wifi lock.") wifiLockRelease()1649 public void wifiLockRelease() { 1650 if (mLock != null) { 1651 mLock.release(); 1652 mLock = null; 1653 } 1654 } 1655 1656 @Rpc(description = "Reassociates with the currently active access point.", returns = "True if the operation succeeded.") wifiReassociate()1657 public Boolean wifiReassociate() { 1658 return mWifi.reassociate(); 1659 } 1660 1661 @Rpc(description = "Reconnects to the currently active access point.", returns = "True if the operation succeeded.") wifiReconnect()1662 public Boolean wifiReconnect() { 1663 return mWifi.reconnect(); 1664 } 1665 1666 @Rpc(description = "Remove a configured network.", returns = "True if the operation succeeded.") wifiRemoveNetwork(@pcParametername = "netId") Integer netId)1667 public Boolean wifiRemoveNetwork(@RpcParameter(name = "netId") Integer netId) { 1668 return mWifi.removeNetwork(netId); 1669 } 1670 getApBandFromChannelFrequency(int freq)1671 private int getApBandFromChannelFrequency(int freq) { 1672 if (ScanResult.is24GHz(freq)) { 1673 return SoftApConfiguration.BAND_2GHZ; 1674 } else if (ScanResult.is5GHz(freq)) { 1675 return SoftApConfiguration.BAND_5GHZ; 1676 } else if (ScanResult.is6GHz(freq)) { 1677 return SoftApConfiguration.BAND_6GHZ; 1678 } else if (ScanResult.is60GHz(freq)) { 1679 return SoftApConfiguration.BAND_60GHZ; 1680 } 1681 return -1; 1682 } 1683 convertJSONArrayToIntArray(JSONArray jArray)1684 private int[] convertJSONArrayToIntArray(JSONArray jArray) throws JSONException { 1685 if (jArray == null) { 1686 return null; 1687 } 1688 int[] iArray = new int[jArray.length()]; 1689 for (int i = 0; i < jArray.length(); i++) { 1690 iArray[i] = jArray.getInt(i); 1691 } 1692 return iArray; 1693 } 1694 createSoftApConfiguration(JSONObject configJson)1695 private SoftApConfiguration createSoftApConfiguration(JSONObject configJson) 1696 throws JSONException { 1697 if (configJson == null) { 1698 return null; 1699 } 1700 SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(); 1701 if (configJson.has("SSID")) { 1702 configBuilder.setSsid(configJson.getString("SSID")); 1703 } 1704 if (configJson.has("password")) { 1705 String pwd = configJson.getString("password"); 1706 // Check if new security type SAE (WPA3) is present. Default to PSK 1707 if (configJson.has("security")) { 1708 String securityType = configJson.getString("security"); 1709 if (TextUtils.equals(securityType, "WPA2_PSK")) { 1710 configBuilder.setPassphrase(pwd, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 1711 } else if (TextUtils.equals(securityType, "WPA3_SAE_TRANSITION")) { 1712 configBuilder.setPassphrase(pwd, 1713 SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION); 1714 } else if (TextUtils.equals(securityType, "WPA3_SAE")) { 1715 configBuilder.setPassphrase(pwd, SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); 1716 } 1717 } else { 1718 configBuilder.setPassphrase(pwd, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK); 1719 } 1720 } 1721 if (configJson.has("BSSID")) { 1722 configBuilder.setBssid(MacAddress.fromString(configJson.getString("BSSID"))); 1723 } 1724 if (configJson.has("hiddenSSID")) { 1725 configBuilder.setHiddenSsid(configJson.getBoolean("hiddenSSID")); 1726 } 1727 if (configJson.has("apBand")) { 1728 configBuilder.setBand(configJson.getInt("apBand")); 1729 } 1730 if (configJson.has("apChannel") && configJson.has("apBand")) { 1731 configBuilder.setChannel(configJson.getInt("apChannel"), configJson.getInt("apBand")); 1732 } 1733 1734 if (configJson.has("MaxNumberOfClients")) { 1735 configBuilder.setMaxNumberOfClients(configJson.getInt("MaxNumberOfClients")); 1736 } 1737 1738 if (configJson.has("ShutdownTimeoutMillis")) { 1739 configBuilder.setShutdownTimeoutMillis(configJson.getLong("ShutdownTimeoutMillis")); 1740 } 1741 1742 if (configJson.has("AutoShutdownEnabled")) { 1743 configBuilder.setAutoShutdownEnabled(configJson.getBoolean("AutoShutdownEnabled")); 1744 } 1745 1746 if (configJson.has("ClientControlByUserEnabled")) { 1747 configBuilder.setClientControlByUserEnabled( 1748 configJson.getBoolean("ClientControlByUserEnabled")); 1749 } 1750 1751 List allowedClientList = new ArrayList<>(); 1752 if (configJson.has("AllowedClientList")) { 1753 JSONArray allowedList = configJson.getJSONArray("AllowedClientList"); 1754 for (int i = 0; i < allowedList.length(); i++) { 1755 allowedClientList.add(MacAddress.fromString(allowedList.getString(i))); 1756 } 1757 } 1758 1759 List blockedClientList = new ArrayList<>(); 1760 if (configJson.has("BlockedClientList")) { 1761 JSONArray blockedList = configJson.getJSONArray("BlockedClientList"); 1762 for (int j = 0; j < blockedList.length(); j++) { 1763 blockedClientList.add(MacAddress.fromString(blockedList.getString(j))); 1764 } 1765 } 1766 1767 configBuilder.setAllowedClientList(allowedClientList); 1768 configBuilder.setBlockedClientList(blockedClientList); 1769 1770 if (SdkLevel.isAtLeastS()) { 1771 if (configJson.has("apBands")) { 1772 JSONArray jBands = configJson.getJSONArray("apBands"); 1773 int[] bands = convertJSONArrayToIntArray(jBands); 1774 configBuilder.setBands(bands); 1775 } 1776 1777 if (configJson.has("apChannelFrequencies")) { 1778 JSONArray jChannelFrequencys = configJson.getJSONArray("apChannelFrequencies"); 1779 int[] channelFrequencies = convertJSONArrayToIntArray(jChannelFrequencys); 1780 SparseIntArray channels = new SparseIntArray(); 1781 for (int channelFrequency : channelFrequencies) { 1782 if (channelFrequency != 0) { 1783 channels.put(getApBandFromChannelFrequency(channelFrequency), 1784 ScanResult.convertFrequencyMhzToChannelIfSupported( 1785 channelFrequency)); 1786 } 1787 } 1788 if (channels.size() != 0) { 1789 configBuilder.setChannels(channels); 1790 } 1791 } 1792 1793 if (configJson.has("MacRandomizationSetting")) { 1794 configBuilder.setMacRandomizationSetting( 1795 configJson.getInt("MacRandomizationSetting")); 1796 } 1797 1798 if (configJson.has("BridgedModeOpportunisticShutdownEnabled")) { 1799 configBuilder.setBridgedModeOpportunisticShutdownEnabled( 1800 configJson.getBoolean("BridgedModeOpportunisticShutdownEnabled")); 1801 } 1802 1803 if (configJson.has("Ieee80211axEnabled")) { 1804 configBuilder.setIeee80211axEnabled(configJson.getBoolean("Ieee80211axEnabled")); 1805 } 1806 } 1807 return configBuilder.build(); 1808 } 1809 createSoftApWifiConfiguration(JSONObject configJson)1810 private WifiConfiguration createSoftApWifiConfiguration(JSONObject configJson) 1811 throws JSONException { 1812 WifiConfiguration config = genWifiConfig(configJson); 1813 // Need to strip of extra quotation marks for SSID and password. 1814 String ssid = config.SSID; 1815 if (ssid != null) { 1816 config.SSID = ssid.substring(1, ssid.length() - 1); 1817 } 1818 1819 config.allowedKeyManagement.clear(); 1820 String pwd = config.preSharedKey; 1821 if (pwd != null) { 1822 config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); 1823 config.preSharedKey = pwd.substring(1, pwd.length() - 1); 1824 } else { 1825 config.allowedKeyManagement.set(KeyMgmt.NONE); 1826 } 1827 return config; 1828 } 1829 1830 /** 1831 * Set SoftAp Configuration with SoftApConfiguration. 1832 */ 1833 @Rpc(description = "Set configuration for soft AP.") wifiSetWifiApConfiguration( @pcParametername = "configJson") JSONObject configJson)1834 public Boolean wifiSetWifiApConfiguration( 1835 @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException { 1836 return mWifi.setSoftApConfiguration(createSoftApConfiguration(configJson)); 1837 } 1838 1839 /** 1840 * Set SoftAp Configuration with WifiConfiguration. 1841 * 1842 * Used to test deprecated API to check backward compatible. 1843 */ 1844 @Rpc(description = "Set configuration for soft AP with WifiConfig.") wifiSetWifiApConfigurationWithWifiConfiguration( @pcParametername = "configJson") JSONObject configJson)1845 public Boolean wifiSetWifiApConfigurationWithWifiConfiguration( 1846 @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException { 1847 return mWifi.setWifiApConfiguration(createSoftApWifiConfiguration(configJson)); 1848 } 1849 1850 /** 1851 * Register softap callback. 1852 * 1853 * @return the id associated with the {@link SoftApCallbackImp} 1854 * used for registering callback. 1855 */ 1856 @Rpc(description = "Register softap callback function.", 1857 returns = "Id of the callback associated with registering.") registerSoftApCallback()1858 public Integer registerSoftApCallback() { 1859 SoftApCallbackImp softApCallback = new SoftApCallbackImp(mEventFacade); 1860 mSoftapCallbacks.put(softApCallback.mId, softApCallback); 1861 mWifi.registerSoftApCallback( 1862 new HandlerExecutor(new Handler(mCallbackHandlerThread.getLooper())), 1863 softApCallback); 1864 return softApCallback.mId; 1865 } 1866 1867 /** 1868 * Unregister softap callback role for the {@link SoftApCallbackImp} identified by the given 1869 * {@code callbackId}. 1870 * 1871 * @param callbackId the id associated with the {@link SoftApCallbackImp} 1872 * used for registering callback. 1873 * 1874 */ 1875 @Rpc(description = "Unregister softap callback function.") unregisterSoftApCallback(@pcParametername = "callbackId") Integer callbackId)1876 public void unregisterSoftApCallback(@RpcParameter(name = "callbackId") Integer callbackId) { 1877 mWifi.unregisterSoftApCallback(mSoftapCallbacks.get(callbackId)); 1878 mSoftapCallbacks.delete(callbackId); 1879 } 1880 1881 @Rpc(description = "Enable/disable tdls with a mac address.") wifiSetTdlsEnabledWithMacAddress( @pcParametername = "remoteMacAddress") String remoteMacAddress, @RpcParameter(name = "enable") Boolean enable)1882 public void wifiSetTdlsEnabledWithMacAddress( 1883 @RpcParameter(name = "remoteMacAddress") String remoteMacAddress, 1884 @RpcParameter(name = "enable") Boolean enable) { 1885 mWifi.setTdlsEnabledWithMacAddress(remoteMacAddress, enable); 1886 } 1887 1888 @Rpc(description = "Starts a scan for Wifi access points.", returns = "True if the scan was initiated successfully.") wifiStartScan()1889 public Boolean wifiStartScan() { 1890 mService.registerReceiver(mScanResultsAvailableReceiver, mScanFilter); 1891 return mWifi.startScan(); 1892 } 1893 1894 @Rpc(description = "Starts a scan for Wifi access points with scanResultCallback.", 1895 returns = "True if the scan was initiated successfully.") wifiStartScanWithListener()1896 public Boolean wifiStartScanWithListener() { 1897 mWifi.registerScanResultsCallback(mService.getMainExecutor(), mWifiScanResultsReceiver); 1898 return mWifi.startScan(); 1899 } 1900 1901 @Rpc(description = "Start Wi-fi Protected Setup.") wifiStartWps( @pcParametername = "config", description = "A json string with fields \\"setup\\", \\"BSSID\\", and \\"pin\\"") String config)1902 public void wifiStartWps( 1903 @RpcParameter(name = "config", description = "A json string with fields \"setup\", \"BSSID\", and \"pin\"") String config) 1904 throws JSONException { 1905 WpsInfo info = parseWpsInfo(config); 1906 WifiWpsCallback listener = new WifiWpsCallback(); 1907 Log.d("Starting wps with: " + info); 1908 mWifi.startWps(info, listener); 1909 } 1910 1911 @Rpc(description = "Start listening for wifi state change related broadcasts.") wifiStartTrackingStateChange()1912 public void wifiStartTrackingStateChange() { 1913 mService.registerReceiver(mStateChangeReceiver, mStateChangeFilter); 1914 mTrackingWifiStateChange = true; 1915 } 1916 1917 @Rpc(description = "Stop listening for wifi state change related broadcasts.") wifiStopTrackingStateChange()1918 public void wifiStopTrackingStateChange() { 1919 if (mTrackingWifiStateChange == true) { 1920 mService.unregisterReceiver(mStateChangeReceiver); 1921 mTrackingWifiStateChange = false; 1922 } 1923 } 1924 1925 @Rpc(description = "Start listening for tether state change related broadcasts.") wifiStartTrackingTetherStateChange()1926 public void wifiStartTrackingTetherStateChange() { 1927 mService.registerReceiver(mTetherStateReceiver, mTetherFilter); 1928 mTrackingTetherStateChange = true; 1929 } 1930 1931 @Rpc(description = "Stop listening for wifi state change related broadcasts.") wifiStopTrackingTetherStateChange()1932 public void wifiStopTrackingTetherStateChange() { 1933 if (mTrackingTetherStateChange == true) { 1934 mService.unregisterReceiver(mTetherStateReceiver); 1935 mTrackingTetherStateChange = false; 1936 } 1937 } 1938 1939 @Rpc(description = "Start listening for network suggestion change related broadcasts.") wifiStartTrackingNetworkSuggestionStateChange()1940 public void wifiStartTrackingNetworkSuggestionStateChange() { 1941 mService.registerReceiver( 1942 mNetworkSuggestionStateChangeReceiver, mNetworkSuggestionStateChangeFilter); 1943 mTrackingNetworkSuggestionStateChange = true; 1944 } 1945 1946 @Rpc(description = "Stop listening for network suggestion change related broadcasts.") wifiStopTrackingNetworkSuggestionStateChange()1947 public void wifiStopTrackingNetworkSuggestionStateChange() { 1948 if (mTrackingNetworkSuggestionStateChange) { 1949 mService.unregisterReceiver(mNetworkSuggestionStateChangeReceiver); 1950 mTrackingNetworkSuggestionStateChange = false; 1951 } 1952 } 1953 1954 @Rpc(description = "Toggle Wifi on and off.", returns = "True if Wifi is enabled.") wifiToggleState(@pcParametername = "enabled") @pcOptional Boolean enabled)1955 public Boolean wifiToggleState(@RpcParameter(name = "enabled") @RpcOptional Boolean enabled) { 1956 if (enabled == null) { 1957 enabled = !wifiCheckState(); 1958 } 1959 mWifi.setWifiEnabled(enabled); 1960 return enabled; 1961 } 1962 1963 @Rpc(description = "Restart the WiFi subsystem.") restartWifiSubsystem()1964 public void restartWifiSubsystem() { 1965 if (mSubsystemRestartTrackingCallback == null) { 1966 // one-time registration if needed 1967 mSubsystemRestartTrackingCallback = new SubsystemRestartTrackingCallbackFacade( 1968 mEventFacade); 1969 } 1970 mWifi.restartWifiSubsystem(); 1971 } 1972 1973 @Rpc(description = "Toggle Wifi scan always available on and off.", returns = "True if Wifi scan is always available.") wifiToggleScanAlwaysAvailable( @pcParametername = "enabled") @pcOptional Boolean enabled)1974 public Boolean wifiToggleScanAlwaysAvailable( 1975 @RpcParameter(name = "enabled") @RpcOptional Boolean enabled) 1976 throws SettingNotFoundException { 1977 boolean isSet = (enabled == null) ? !mWifi.isScanAlwaysAvailable() : enabled; 1978 mWifi.setScanAlwaysAvailable(isSet); 1979 return isSet; 1980 } 1981 1982 @Rpc(description = "Enable/disable WifiConnectivityManager.") wifiEnableWifiConnectivityManager( @pcParametername = "enable") Boolean enable)1983 public void wifiEnableWifiConnectivityManager( 1984 @RpcParameter(name = "enable") Boolean enable) { 1985 mWifi.allowAutojoinGlobal(enable); 1986 } 1987 1988 /** 1989 * Register network request match callback to simulate the UI flow. 1990 * 1991 * @throws JSONException 1992 * @throws GeneralSecurityException 1993 */ 1994 @Rpc(description = "Register network request match callback") wifiRegisterNetworkRequestMatchCallback()1995 public void wifiRegisterNetworkRequestMatchCallback() 1996 throws JSONException, GeneralSecurityException { 1997 // Listen for UI interaction callbacks 1998 mWifi.registerNetworkRequestMatchCallback( 1999 new HandlerExecutor(new Handler(mCallbackHandlerThread.getLooper())), 2000 mNetworkRequestMatchCallback); 2001 } 2002 2003 /** 2004 * Triggers connect to a specific wifi network. 2005 * 2006 * @param jsonConfig JSONObject Dictionary of wifi connection parameters 2007 * @throws JSONException 2008 * @throws GeneralSecurityException 2009 */ 2010 @Rpc(description = "Connects to the specified network for the ongoing network request") wifiSendUserSelectionForNetworkRequestMatch( @pcParametername = "jsonConfig") JSONObject jsonConfig)2011 public void wifiSendUserSelectionForNetworkRequestMatch( 2012 @RpcParameter(name = "jsonConfig") JSONObject jsonConfig) 2013 throws JSONException, GeneralSecurityException { 2014 synchronized (mCallbackLock) { 2015 if (mNetworkRequestUserSelectionCallback == null) { 2016 throw new IllegalStateException("user callback is null"); 2017 } 2018 // Copy the SSID for user selection. 2019 WifiConfiguration config = new WifiConfiguration(); 2020 if (jsonConfig.has("SSID")) { 2021 config.SSID = "\"" + jsonConfig.getString("SSID") + "\""; 2022 } 2023 mNetworkRequestUserSelectionCallback.select(config); 2024 } 2025 } 2026 2027 /** 2028 * Rejects network request. 2029 * 2030 * @throws JSONException 2031 * @throws GeneralSecurityException 2032 */ 2033 @Rpc(description = "Rejects ongoing network request") wifiSendUserRejectionForNetworkRequestMatch()2034 public void wifiSendUserRejectionForNetworkRequestMatch() 2035 throws JSONException, GeneralSecurityException { 2036 synchronized (mCallbackLock) { 2037 if (mNetworkRequestUserSelectionCallback == null) { 2038 throw new IllegalStateException("user callback is null"); 2039 } 2040 mNetworkRequestUserSelectionCallback.reject(); 2041 } 2042 } 2043 2044 /** 2045 * Add network suggestions. 2046 2047 * @param wifiNetworkSuggestions Array of JSONObject Dictionary of wifi network suggestion 2048 * parameters 2049 * @throws JSONException 2050 * @throws GeneralSecurityException 2051 */ 2052 @Rpc(description = "Add network suggestions to the platform") wifiAddNetworkSuggestions( @pcParametername = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions)2053 public boolean wifiAddNetworkSuggestions( 2054 @RpcParameter(name = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions) 2055 throws JSONException, GeneralSecurityException, IOException { 2056 return mWifi.addNetworkSuggestions(genWifiNetworkSuggestions(wifiNetworkSuggestions)) 2057 == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 2058 } 2059 2060 /** 2061 * Remove network suggestions. 2062 2063 * @param wifiNetworkSuggestions Array of JSONObject Dictionary of wifi network suggestion 2064 * parameters 2065 * @throws JSONException 2066 * @throws GeneralSecurityException 2067 */ 2068 @Rpc(description = "Remove network suggestions from the platform") wifiRemoveNetworkSuggestions( @pcParametername = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions)2069 public boolean wifiRemoveNetworkSuggestions( 2070 @RpcParameter(name = "wifiNetworkSuggestions") JSONArray wifiNetworkSuggestions) 2071 throws JSONException, GeneralSecurityException, IOException { 2072 return mWifi.removeNetworkSuggestions(genWifiNetworkSuggestions(wifiNetworkSuggestions)) 2073 == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS; 2074 } 2075 2076 @Override shutdown()2077 public void shutdown() { 2078 wifiLockRelease(); 2079 if (mTrackingWifiStateChange == true) { 2080 wifiStopTrackingStateChange(); 2081 } 2082 if (mTrackingTetherStateChange == true) { 2083 wifiStopTrackingTetherStateChange(); 2084 } 2085 } 2086 2087 private class EasyConnectCallback extends EasyConnectStatusCallback { 2088 private static final String EASY_CONNECT_CALLBACK_TAG = "onDppCallback"; 2089 2090 @Override onEnrolleeSuccess(int newWifiConfigurationId)2091 public void onEnrolleeSuccess(int newWifiConfigurationId) { 2092 Bundle msg = new Bundle(); 2093 msg.putString("Type", "onEnrolleeSuccess"); 2094 msg.putInt("NetworkId", newWifiConfigurationId); 2095 Log.d("Posting event: onEnrolleeSuccess"); 2096 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2097 } 2098 2099 @Override onConfiguratorSuccess(int code)2100 public void onConfiguratorSuccess(int code) { 2101 Bundle msg = new Bundle(); 2102 msg.putString("Type", "onConfiguratorSuccess"); 2103 msg.putInt("Status", code); 2104 Log.d("Posting event: onConfiguratorSuccess"); 2105 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2106 } 2107 2108 @Override onFailure(int code, String ssid, SparseArray<int[]> channelList, int[] bandList)2109 public void onFailure(int code, String ssid, SparseArray<int[]> channelList, 2110 int[] bandList) { 2111 Bundle msg = new Bundle(); 2112 msg.putString("Type", "onFailure"); 2113 msg.putInt("Status", code); 2114 Log.d("Posting event: onFailure"); 2115 if (ssid != null) { 2116 Log.d("onFailure SSID: " + ssid); 2117 msg.putString("onFailureSsid", ssid); 2118 } else { 2119 msg.putString("onFailureSsid", ""); 2120 } 2121 if (channelList != null) { 2122 Log.d("onFailure list of tried channels: " + channelList); 2123 int key; 2124 int index = 0; 2125 JSONObject formattedChannelList = new JSONObject(); 2126 2127 // Build a JSON array of classes, with an array of channels for each class. 2128 do { 2129 try { 2130 key = channelList.keyAt(index); 2131 } catch (java.lang.ArrayIndexOutOfBoundsException e) { 2132 break; 2133 } 2134 try { 2135 JSONArray channelsInClassArray = new JSONArray(); 2136 2137 int[] output = channelList.get(key); 2138 for (int i = 0; i < output.length; i++) { 2139 channelsInClassArray.put(output[i]); 2140 } 2141 formattedChannelList.put(Integer.toString(key), 2142 build(channelsInClassArray)); 2143 } catch (org.json.JSONException e) { 2144 msg.putString("onFailureChannelList", ""); 2145 break; 2146 } 2147 index++; 2148 } while (true); 2149 2150 msg.putString("onFailureChannelList", formattedChannelList.toString()); 2151 } else { 2152 msg.putString("onFailureChannelList", ""); 2153 } 2154 2155 if (bandList != null) { 2156 // Build a JSON array of bands represented as operating classes 2157 Log.d("onFailure list of supported bands: " + Arrays.toString(bandList)); 2158 JSONArray formattedBandList = new JSONArray(); 2159 for (int i = 0; i < bandList.length; i++) { 2160 formattedBandList.put(bandList[i]); 2161 } 2162 msg.putString("onFailureBandList", formattedBandList.toString()); 2163 } else { 2164 msg.putString("onFailureBandList", ""); 2165 } 2166 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2167 } 2168 2169 @Override onProgress(int code)2170 public void onProgress(int code) { 2171 Bundle msg = new Bundle(); 2172 msg.putString("Type", "onProgress"); 2173 msg.putInt("Status", code); 2174 Log.d("Posting event: onProgress"); 2175 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2176 } 2177 2178 @Override onBootstrapUriGenerated(@onNull Uri dppUri)2179 public void onBootstrapUriGenerated(@NonNull Uri dppUri) { 2180 Bundle msg = new Bundle(); 2181 msg.putString("Type", "onBootstrapUriGenerated"); 2182 Log.d("onBootstrapUriGenerated uri: " + dppUri.toString()); 2183 msg.putString("generatedUri", dppUri.toString()); 2184 Log.d("Posting event: onBootstrapUriGenerated"); 2185 mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg); 2186 } 2187 } 2188 2189 private static @WifiManager.EasyConnectCryptographyCurve getEasyConnectCryptographyCurve(String curve)2190 int getEasyConnectCryptographyCurve(String curve) { 2191 2192 switch (curve) { 2193 case "secp384r1": 2194 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1; 2195 case "secp521r1": 2196 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1; 2197 case "brainpoolP256r1": 2198 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1; 2199 case "brainpoolP384r1": 2200 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1; 2201 case "brainpoolP512r1": 2202 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1; 2203 case "prime256v1": 2204 default: 2205 return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1; 2206 } 2207 } 2208 2209 /** 2210 * Start Easy Connect (DPP) in Initiator-Configurator role: Send Wi-Fi configuration to a peer 2211 * 2212 * @param enrolleeUri Peer URI 2213 * @param selectedNetworkId Wi-Fi configuration ID 2214 */ 2215 @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)2216 public void startEasyConnectAsConfiguratorInitiator(@RpcParameter(name = "enrolleeUri") String 2217 enrolleeUri, @RpcParameter(name = "selectedNetworkId") Integer selectedNetworkId, 2218 @RpcParameter(name = "netRole") String netRole) 2219 throws JSONException { 2220 EasyConnectCallback dppStatusCallback = new EasyConnectCallback(); 2221 int netRoleInternal; 2222 2223 if (netRole.equals("ap")) { 2224 netRoleInternal = WifiManager.EASY_CONNECT_NETWORK_ROLE_AP; 2225 } else { 2226 netRoleInternal = WifiManager.EASY_CONNECT_NETWORK_ROLE_STA; 2227 } 2228 2229 // Start Easy Connect 2230 mWifi.startEasyConnectAsConfiguratorInitiator(enrolleeUri, selectedNetworkId, 2231 netRoleInternal, mService.getMainExecutor(), dppStatusCallback); 2232 } 2233 2234 /** 2235 * Start Easy Connect (DPP) in Initiator-Enrollee role: Receive Wi-Fi configuration from a peer 2236 * 2237 * @param configuratorUri 2238 */ 2239 @Rpc(description = "Easy Connect Initiator-Enrollee: Receive Wi-Fi configuration from peer") startEasyConnectAsEnrolleeInitiator(@pcParametername = "configuratorUri") String configuratorUri)2240 public void startEasyConnectAsEnrolleeInitiator(@RpcParameter(name = "configuratorUri") String 2241 configuratorUri) { 2242 EasyConnectCallback dppStatusCallback = new EasyConnectCallback(); 2243 2244 // Start Easy Connect 2245 mWifi.startEasyConnectAsEnrolleeInitiator(configuratorUri, mService.getMainExecutor(), 2246 dppStatusCallback); 2247 } 2248 2249 /** 2250 * Start Easy Connect (DPP) in Responder-Enrollee role: Receive Wi-Fi configuration from a peer 2251 * 2252 * @param deviceInfo The device specific info to attach in the generated URI 2253 * @param cryptographyCurve Elliptic curve cryptography used to generate DPP 2254 * public/private key pair 2255 */ 2256 @Rpc(description = "Easy Connect Responder-Enrollee: Receive Wi-Fi configuration from peer") startEasyConnectAsEnrolleeResponder(@pcParametername = "deviceInfo") String deviceInfo, @RpcParameter(name = "cryptographyCurve") String cryptographyCurve)2257 public void startEasyConnectAsEnrolleeResponder(@RpcParameter(name = "deviceInfo") String 2258 deviceInfo, @RpcParameter(name = "cryptographyCurve") String cryptographyCurve) { 2259 EasyConnectCallback dppStatusCallback = new EasyConnectCallback(); 2260 2261 // Start Easy Connect 2262 mWifi.startEasyConnectAsEnrolleeResponder(deviceInfo, 2263 getEasyConnectCryptographyCurve(cryptographyCurve), mService.getMainExecutor(), 2264 dppStatusCallback); 2265 } 2266 2267 /** 2268 * Stop Easy Connect (DPP) session 2269 * 2270 */ 2271 @Rpc(description = "Stop Easy Connect session") stopEasyConnectSession()2272 public void stopEasyConnectSession() { 2273 // Stop Easy Connect 2274 mWifi.stopEasyConnectSession(); 2275 } 2276 2277 /** 2278 * Enable/Disable auto join for target network 2279 */ 2280 @Rpc(description = "Set network auto join enable/disable") wifiEnableAutojoin(@pcParametername = "netId") Integer netId, @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin)2281 public void wifiEnableAutojoin(@RpcParameter(name = "netId") Integer netId, 2282 @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin) { 2283 mWifi.allowAutojoin(netId, enableAutojoin); 2284 } 2285 2286 /** 2287 * Enable/Disable auto join for target Passpoint network 2288 */ 2289 @Rpc(description = "Set passpoint network auto join enable/disable") wifiEnableAutojoinPasspoint(@pcParametername = "FQDN") String fqdn, @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin)2290 public void wifiEnableAutojoinPasspoint(@RpcParameter(name = "FQDN") String fqdn, 2291 @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin) { 2292 mWifi.allowAutojoinPasspoint(fqdn, enableAutojoin); 2293 } 2294 genCoexUnsafeChannel(JSONObject j)2295 private static CoexUnsafeChannel genCoexUnsafeChannel(JSONObject j) throws JSONException { 2296 if (j == null || !j.has("band") || !j.has("channel")) { 2297 return null; 2298 } 2299 2300 final int band; 2301 final String jsonBand = j.getString("band"); 2302 if (TextUtils.equals(jsonBand, "24_GHZ")) { 2303 band = WIFI_BAND_24_GHZ; 2304 } else if (TextUtils.equals(jsonBand, "5_GHZ")) { 2305 band = WIFI_BAND_5_GHZ; 2306 } else { 2307 return null; 2308 } 2309 if (j.has("powerCapDbm")) { 2310 return new CoexUnsafeChannel(band, j.getInt("channel"), j.getInt("powerCapDbm")); 2311 } 2312 return new CoexUnsafeChannel(band, j.getInt("channel")); 2313 } 2314 genCoexUnsafeChannels( JSONArray jsonCoexUnsafeChannelsArray)2315 private static List<CoexUnsafeChannel> genCoexUnsafeChannels( 2316 JSONArray jsonCoexUnsafeChannelsArray) throws JSONException { 2317 if (jsonCoexUnsafeChannelsArray == null) { 2318 return Collections.emptyList(); 2319 } 2320 List<CoexUnsafeChannel> unsafeChannels = new ArrayList<>(); 2321 for (int i = 0; i < jsonCoexUnsafeChannelsArray.length(); i++) { 2322 unsafeChannels.add( 2323 genCoexUnsafeChannel(jsonCoexUnsafeChannelsArray.getJSONObject(i))); 2324 } 2325 return unsafeChannels; 2326 } 2327 genCoexRestrictions(JSONArray jsonCoexRestrictionArray)2328 private static int genCoexRestrictions(JSONArray jsonCoexRestrictionArray) 2329 throws JSONException { 2330 if (jsonCoexRestrictionArray == null) { 2331 return 0; 2332 } 2333 int coexRestrictions = 0; 2334 for (int i = 0; i < jsonCoexRestrictionArray.length(); i++) { 2335 final String jsonRestriction = jsonCoexRestrictionArray.getString(i); 2336 if (TextUtils.equals(jsonRestriction, "WIFI_DIRECT")) { 2337 coexRestrictions |= WifiManager.COEX_RESTRICTION_WIFI_DIRECT; 2338 } 2339 if (TextUtils.equals(jsonRestriction, "SOFTAP")) { 2340 coexRestrictions |= WifiManager.COEX_RESTRICTION_SOFTAP; 2341 } 2342 if (TextUtils.equals(jsonRestriction, "WIFI_AWARE")) { 2343 coexRestrictions |= WifiManager.COEX_RESTRICTION_WIFI_AWARE; 2344 } 2345 } 2346 return coexRestrictions; 2347 } 2348 2349 /** 2350 * Converts a set of {@link CoexUnsafeChannel} to a {@link JSONArray} of {@link JSONObject} of 2351 * format: 2352 * { 2353 * "band": <"24_GHZ" or "5_GHZ"> 2354 * "channel" : <Channel Number> 2355 * (Optional) "powerCapDbm" : <Power Cap in Dbm> 2356 * } 2357 */ coexUnsafeChannelsToJson(List<CoexUnsafeChannel> unsafeChannels)2358 private static JSONArray coexUnsafeChannelsToJson(List<CoexUnsafeChannel> unsafeChannels) 2359 throws JSONException { 2360 final JSONArray jsonCoexUnsafeChannelArray = new JSONArray(); 2361 for (CoexUnsafeChannel unsafeChannel : unsafeChannels) { 2362 final String jsonBand; 2363 if (unsafeChannel.getBand() == WIFI_BAND_24_GHZ) { 2364 jsonBand = "24_GHZ"; 2365 } else if (unsafeChannel.getBand() == WIFI_BAND_5_GHZ) { 2366 jsonBand = "5_GHZ"; 2367 } else { 2368 continue; 2369 } 2370 final JSONObject jsonUnsafeChannel = new JSONObject(); 2371 jsonUnsafeChannel.put("band", jsonBand); 2372 jsonUnsafeChannel.put("channel", unsafeChannel.getChannel()); 2373 final int powerCapDbm = unsafeChannel.getPowerCapDbm(); 2374 if (powerCapDbm != CoexUnsafeChannel.POWER_CAP_NONE) { 2375 jsonUnsafeChannel.put("powerCapDbm", powerCapDbm); 2376 } 2377 jsonCoexUnsafeChannelArray.put(jsonUnsafeChannel); 2378 } 2379 return jsonCoexUnsafeChannelArray; 2380 } 2381 2382 /** 2383 * Converts a coex restriction bitmask {@link WifiManager#getCoexRestrictions()} to a JSON array 2384 * of possible values "WIFI_DIRECT", "SOFTAP", "WIFI_AWARE". 2385 */ coexRestrictionsToJson(int coexRestrictions)2386 private static JSONArray coexRestrictionsToJson(int coexRestrictions) { 2387 final JSONArray jsonCoexRestrictionArray = new JSONArray(); 2388 if ((coexRestrictions & WifiManager.COEX_RESTRICTION_WIFI_DIRECT) != 0) { 2389 jsonCoexRestrictionArray.put("WIFI_DIRECT"); 2390 } 2391 if ((coexRestrictions & WifiManager.COEX_RESTRICTION_SOFTAP) != 0) { 2392 jsonCoexRestrictionArray.put("SOFTAP"); 2393 } 2394 if ((coexRestrictions & WifiManager.COEX_RESTRICTION_WIFI_AWARE) != 0) { 2395 jsonCoexRestrictionArray.put("WIFI_AWARE"); 2396 } 2397 return jsonCoexRestrictionArray; 2398 } 2399 2400 /** 2401 * Returns whether the default coex algorithm is enabled or not 2402 * 2403 * @return {@code true} if the default coex algorithm is enabled, {@code false} otherwise. 2404 */ 2405 @Rpc(description = "Returns whether the default coex algorithm is enabled or not") wifiIsDefaultCoexAlgorithmEnabled()2406 public boolean wifiIsDefaultCoexAlgorithmEnabled() { 2407 if (!SdkLevel.isAtLeastS()) { 2408 return false; 2409 } 2410 return mWifi.isDefaultCoexAlgorithmEnabled(); 2411 } 2412 2413 /** 2414 * Sets the active list of unsafe channels to avoid for coex and the restricted Wifi interfaces. 2415 * 2416 * @param unsafeChannels JSONArray representation of {@link CoexUnsafeChannel}. 2417 * See {@link #coexUnsafeChannelsToJson(List)}. 2418 * @param restrictions JSONArray representation of coex restrictions. 2419 * See {@link #coexRestrictionsToJson(int)}. 2420 * @throws JSONException 2421 */ 2422 @Rpc(description = "Set the unsafe channels to avoid for coex") wifiSetCoexUnsafeChannels( @pcParametername = "unsafeChannels") JSONArray unsafeChannels, @RpcParameter(name = "restrictions") JSONArray restrictions)2423 public void wifiSetCoexUnsafeChannels( 2424 @RpcParameter(name = "unsafeChannels") JSONArray unsafeChannels, 2425 @RpcParameter(name = "restrictions") JSONArray restrictions) throws JSONException { 2426 if (!SdkLevel.isAtLeastS()) { 2427 return; 2428 } 2429 mWifi.setCoexUnsafeChannels( 2430 genCoexUnsafeChannels(unsafeChannels), genCoexRestrictions(restrictions)); 2431 } 2432 2433 /** 2434 * Registers a coex callback to start receiving coex update events. 2435 */ 2436 @Rpc(description = "Registers a coex callback to start receiving coex update events") wifiRegisterCoexCallback()2437 public void wifiRegisterCoexCallback() { 2438 if (!SdkLevel.isAtLeastS()) { 2439 return; 2440 } 2441 mWifi.registerCoexCallback( 2442 new HandlerExecutor(mCallbackHandlerThread.getThreadHandler()), mCoexCallback); 2443 } 2444 2445 /** 2446 * Unregisters the coex callback to stop receiving coex update events. 2447 */ 2448 @Rpc(description = "Unregisters the coex callback to stop receiving coex update events") wifiUnregisterCoexCallback()2449 public void wifiUnregisterCoexCallback() { 2450 if (!SdkLevel.isAtLeastS()) { 2451 return; 2452 } 2453 mWifi.unregisterCoexCallback(mCoexCallback); 2454 } 2455 } 2456