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 package com.android.server.wifi; 17 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.hardware.wifi.hostapd.V1_0.HostapdStatus; 22 import android.hardware.wifi.hostapd.V1_0.HostapdStatusCode; 23 import android.hardware.wifi.hostapd.V1_0.IHostapd; 24 import android.hidl.manager.V1_0.IServiceManager; 25 import android.hidl.manager.V1_0.IServiceNotification; 26 import android.net.wifi.WifiConfiguration; 27 import android.os.Handler; 28 import android.os.HwRemoteBinder; 29 import android.os.Looper; 30 import android.os.RemoteException; 31 import android.util.Log; 32 33 import com.android.internal.R; 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.server.wifi.WifiNative.HostapdDeathEventHandler; 36 import com.android.server.wifi.util.ApConfigUtil; 37 import com.android.server.wifi.util.NativeUtil; 38 39 import java.util.ArrayList; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.NoSuchElementException; 43 import java.util.Random; 44 import java.util.concurrent.CountDownLatch; 45 import java.util.concurrent.TimeUnit; 46 47 import javax.annotation.concurrent.ThreadSafe; 48 49 /** 50 * To maintain thread-safety, the locking protocol is that every non-static method (regardless of 51 * access level) acquires mLock. 52 */ 53 @ThreadSafe 54 public class HostapdHal { 55 private static final String TAG = "HostapdHal"; 56 @VisibleForTesting 57 public static final String HAL_INSTANCE_NAME = "default"; 58 @VisibleForTesting 59 public static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L; 60 61 private final Object mLock = new Object(); 62 private boolean mVerboseLoggingEnabled = false; 63 private final Handler mEventHandler; 64 private final boolean mEnableAcs; 65 private final boolean mEnableIeee80211AC; 66 private final List<android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange> 67 mAcsChannelRanges; 68 private boolean mForceApChannel = false; 69 private int mForcedApChannel; 70 71 // Hostapd HAL interface objects 72 private IServiceManager mIServiceManager = null; 73 private IHostapd mIHostapd; 74 private HashMap<String, WifiNative.SoftApListener> mSoftApListeners = new HashMap<>(); 75 private HostapdDeathEventHandler mDeathEventHandler; 76 private ServiceManagerDeathRecipient mServiceManagerDeathRecipient; 77 private HostapdDeathRecipient mHostapdDeathRecipient; 78 // Death recipient cookie registered for current supplicant instance. 79 private long mDeathRecipientCookie = 0; 80 81 private final IServiceNotification mServiceNotificationCallback = 82 new IServiceNotification.Stub() { 83 public void onRegistration(String fqName, String name, boolean preexisting) { 84 synchronized (mLock) { 85 if (mVerboseLoggingEnabled) { 86 Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName 87 + ", " + name + " preexisting=" + preexisting); 88 } 89 if (!initHostapdService()) { 90 Log.e(TAG, "initalizing IHostapd failed."); 91 hostapdServiceDiedHandler(mDeathRecipientCookie); 92 } else { 93 Log.i(TAG, "Completed initialization of IHostapd."); 94 } 95 } 96 } 97 }; 98 private class ServiceManagerDeathRecipient implements HwRemoteBinder.DeathRecipient { 99 @Override serviceDied(long cookie)100 public void serviceDied(long cookie) { 101 mEventHandler.post(() -> { 102 synchronized (mLock) { 103 Log.w(TAG, "IServiceManager died: cookie=" + cookie); 104 hostapdServiceDiedHandler(mDeathRecipientCookie); 105 mIServiceManager = null; // Will need to register a new ServiceNotification 106 } 107 }); 108 } 109 } 110 private class HostapdDeathRecipient implements HwRemoteBinder.DeathRecipient { 111 @Override serviceDied(long cookie)112 public void serviceDied(long cookie) { 113 mEventHandler.post(() -> { 114 synchronized (mLock) { 115 Log.w(TAG, "IHostapd/IHostapd died: cookie=" + cookie); 116 hostapdServiceDiedHandler(cookie); 117 } 118 }); 119 } 120 } 121 HostapdHal(Context context, Looper looper)122 public HostapdHal(Context context, Looper looper) { 123 mEventHandler = new Handler(looper); 124 mEnableAcs = context.getResources().getBoolean(R.bool.config_wifi_softap_acs_supported); 125 mEnableIeee80211AC = 126 context.getResources().getBoolean(R.bool.config_wifi_softap_ieee80211ac_supported); 127 mAcsChannelRanges = toAcsChannelRanges(context.getResources().getString( 128 R.string.config_wifi_softap_acs_supported_channel_list)); 129 130 mServiceManagerDeathRecipient = new ServiceManagerDeathRecipient(); 131 mHostapdDeathRecipient = new HostapdDeathRecipient(); 132 } 133 134 /** 135 * Enable/Disable verbose logging. 136 * 137 * @param enable true to enable, false to disable. 138 */ enableVerboseLogging(boolean enable)139 void enableVerboseLogging(boolean enable) { 140 synchronized (mLock) { 141 mVerboseLoggingEnabled = enable; 142 } 143 } 144 145 /** 146 * Uses the IServiceManager to check if the device is running V1_1 of the HAL from the VINTF for 147 * the device. 148 * @return true if supported, false otherwise. 149 */ isV1_1()150 private boolean isV1_1() { 151 synchronized (mLock) { 152 if (mIServiceManager == null) { 153 Log.e(TAG, "isV1_1: called but mServiceManager is null!?"); 154 return false; 155 } 156 try { 157 return (mIServiceManager.getTransport( 158 android.hardware.wifi.hostapd.V1_1.IHostapd.kInterfaceName, 159 HAL_INSTANCE_NAME) 160 != IServiceManager.Transport.EMPTY); 161 } catch (RemoteException e) { 162 Log.e(TAG, "Exception while operating on IServiceManager: " + e); 163 handleRemoteException(e, "getTransport"); 164 return false; 165 } 166 } 167 } 168 169 /** 170 * Link to death for IServiceManager object. 171 * @return true on success, false otherwise. 172 */ linkToServiceManagerDeath()173 private boolean linkToServiceManagerDeath() { 174 synchronized (mLock) { 175 if (mIServiceManager == null) return false; 176 try { 177 if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) { 178 Log.wtf(TAG, "Error on linkToDeath on IServiceManager"); 179 hostapdServiceDiedHandler(mDeathRecipientCookie); 180 mIServiceManager = null; // Will need to register a new ServiceNotification 181 return false; 182 } 183 } catch (RemoteException e) { 184 Log.e(TAG, "IServiceManager.linkToDeath exception", e); 185 mIServiceManager = null; // Will need to register a new ServiceNotification 186 return false; 187 } 188 return true; 189 } 190 } 191 192 /** 193 * Registers a service notification for the IHostapd service, which triggers intialization of 194 * the IHostapd 195 * @return true if the service notification was successfully registered 196 */ initialize()197 public boolean initialize() { 198 synchronized (mLock) { 199 if (mVerboseLoggingEnabled) { 200 Log.i(TAG, "Registering IHostapd service ready callback."); 201 } 202 mIHostapd = null; 203 if (mIServiceManager != null) { 204 // Already have an IServiceManager and serviceNotification registered, don't 205 // don't register another. 206 return true; 207 } 208 try { 209 mIServiceManager = getServiceManagerMockable(); 210 if (mIServiceManager == null) { 211 Log.e(TAG, "Failed to get HIDL Service Manager"); 212 return false; 213 } 214 if (!linkToServiceManagerDeath()) { 215 return false; 216 } 217 /* TODO(b/33639391) : Use the new IHostapd.registerForNotifications() once it 218 exists */ 219 if (!mIServiceManager.registerForNotifications( 220 IHostapd.kInterfaceName, "", mServiceNotificationCallback)) { 221 Log.e(TAG, "Failed to register for notifications to " 222 + IHostapd.kInterfaceName); 223 mIServiceManager = null; // Will need to register a new ServiceNotification 224 return false; 225 } 226 } catch (RemoteException e) { 227 Log.e(TAG, "Exception while trying to register a listener for IHostapd service: " 228 + e); 229 hostapdServiceDiedHandler(mDeathRecipientCookie); 230 mIServiceManager = null; // Will need to register a new ServiceNotification 231 return false; 232 } 233 return true; 234 } 235 } 236 237 /** 238 * Link to death for IHostapd object. 239 * @return true on success, false otherwise. 240 */ linkToHostapdDeath(HwRemoteBinder.DeathRecipient deathRecipient, long cookie)241 private boolean linkToHostapdDeath(HwRemoteBinder.DeathRecipient deathRecipient, long cookie) { 242 synchronized (mLock) { 243 if (mIHostapd == null) return false; 244 try { 245 if (!mIHostapd.linkToDeath(deathRecipient, cookie)) { 246 Log.wtf(TAG, "Error on linkToDeath on IHostapd"); 247 hostapdServiceDiedHandler(mDeathRecipientCookie); 248 return false; 249 } 250 } catch (RemoteException e) { 251 Log.e(TAG, "IHostapd.linkToDeath exception", e); 252 return false; 253 } 254 return true; 255 } 256 } 257 registerCallback( android.hardware.wifi.hostapd.V1_1.IHostapdCallback callback)258 private boolean registerCallback( 259 android.hardware.wifi.hostapd.V1_1.IHostapdCallback callback) { 260 synchronized (mLock) { 261 String methodStr = "registerCallback_1_1"; 262 try { 263 android.hardware.wifi.hostapd.V1_1.IHostapd iHostapdV1_1 = getHostapdMockableV1_1(); 264 if (iHostapdV1_1 == null) return false; 265 HostapdStatus status = iHostapdV1_1.registerCallback(callback); 266 return checkStatusAndLogFailure(status, methodStr); 267 } catch (RemoteException e) { 268 handleRemoteException(e, methodStr); 269 return false; 270 } 271 } 272 } 273 274 /** 275 * Initialize the IHostapd object. 276 * @return true on success, false otherwise. 277 */ initHostapdService()278 private boolean initHostapdService() { 279 synchronized (mLock) { 280 try { 281 mIHostapd = getHostapdMockable(); 282 } catch (RemoteException e) { 283 Log.e(TAG, "IHostapd.getService exception: " + e); 284 return false; 285 } catch (NoSuchElementException e) { 286 Log.e(TAG, "IHostapd.getService exception: " + e); 287 return false; 288 } 289 if (mIHostapd == null) { 290 Log.e(TAG, "Got null IHostapd service. Stopping hostapd HIDL startup"); 291 return false; 292 } 293 if (!linkToHostapdDeath(mHostapdDeathRecipient, ++mDeathRecipientCookie)) { 294 mIHostapd = null; 295 return false; 296 } 297 // Register for callbacks for 1.1 hostapd. 298 if (isV1_1() && !registerCallback(new HostapdCallback())) { 299 mIHostapd = null; 300 return false; 301 } 302 } 303 return true; 304 } 305 306 /** 307 * Enable force-soft-AP-channel mode which takes effect when soft AP starts next time 308 * @param forcedApChannel The forced IEEE channel number 309 */ enableForceSoftApChannel(int forcedApChannel)310 void enableForceSoftApChannel(int forcedApChannel) { 311 mForceApChannel = true; 312 mForcedApChannel = forcedApChannel; 313 } 314 315 /** 316 * Disable force-soft-AP-channel mode which take effect when soft AP starts next time 317 */ disableForceSoftApChannel()318 void disableForceSoftApChannel() { 319 mForceApChannel = false; 320 } 321 322 /** 323 * Add and start a new access point. 324 * 325 * @param ifaceName Name of the interface. 326 * @param config Configuration to use for the AP. 327 * @param listener Callback for AP events. 328 * @return true on success, false otherwise. 329 */ addAccessPoint(@onNull String ifaceName, @NonNull WifiConfiguration config, @NonNull WifiNative.SoftApListener listener)330 public boolean addAccessPoint(@NonNull String ifaceName, @NonNull WifiConfiguration config, 331 @NonNull WifiNative.SoftApListener listener) { 332 synchronized (mLock) { 333 final String methodStr = "addAccessPoint"; 334 IHostapd.IfaceParams ifaceParams = new IHostapd.IfaceParams(); 335 ifaceParams.ifaceName = ifaceName; 336 ifaceParams.hwModeParams.enable80211N = true; 337 ifaceParams.hwModeParams.enable80211AC = mEnableIeee80211AC; 338 try { 339 ifaceParams.channelParams.band = getBand(config); 340 } catch (IllegalArgumentException e) { 341 Log.e(TAG, "Unrecognized apBand " + config.apBand); 342 return false; 343 } 344 if (mForceApChannel) { 345 ifaceParams.channelParams.enableAcs = false; 346 ifaceParams.channelParams.channel = mForcedApChannel; 347 if (mForcedApChannel <= ApConfigUtil.HIGHEST_2G_AP_CHANNEL) { 348 ifaceParams.channelParams.band = IHostapd.Band.BAND_2_4_GHZ; 349 } else { 350 ifaceParams.channelParams.band = IHostapd.Band.BAND_5_GHZ; 351 } 352 } else if (mEnableAcs) { 353 ifaceParams.channelParams.enableAcs = true; 354 ifaceParams.channelParams.acsShouldExcludeDfs = true; 355 } else { 356 // Downgrade IHostapd.Band.BAND_ANY to IHostapd.Band.BAND_2_4_GHZ if ACS 357 // is not supported. 358 // We should remove this workaround once channel selection is moved from 359 // ApConfigUtil to here. 360 if (ifaceParams.channelParams.band == IHostapd.Band.BAND_ANY) { 361 Log.d(TAG, "ACS is not supported on this device, using 2.4 GHz band."); 362 ifaceParams.channelParams.band = IHostapd.Band.BAND_2_4_GHZ; 363 } 364 ifaceParams.channelParams.enableAcs = false; 365 ifaceParams.channelParams.channel = config.apChannel; 366 } 367 368 IHostapd.NetworkParams nwParams = new IHostapd.NetworkParams(); 369 // TODO(b/67745880) Note that config.SSID is intended to be either a 370 // hex string or "double quoted". 371 // However, it seems that whatever is handing us these configurations does not obey 372 // this convention. 373 nwParams.ssid.addAll(NativeUtil.stringToByteArrayList(config.SSID)); 374 nwParams.isHidden = config.hiddenSSID; 375 nwParams.encryptionType = getEncryptionType(config); 376 nwParams.pskPassphrase = (config.preSharedKey != null) ? config.preSharedKey : ""; 377 if (!checkHostapdAndLogFailure(methodStr)) return false; 378 try { 379 HostapdStatus status; 380 if (isV1_1()) { 381 android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams ifaceParams1_1 = 382 new android.hardware.wifi.hostapd.V1_1.IHostapd.IfaceParams(); 383 ifaceParams1_1.V1_0 = ifaceParams; 384 if (mEnableAcs) { 385 ifaceParams1_1.channelParams.acsChannelRanges.addAll(mAcsChannelRanges); 386 } 387 android.hardware.wifi.hostapd.V1_1.IHostapd iHostapdV1_1 = 388 getHostapdMockableV1_1(); 389 if (iHostapdV1_1 == null) return false; 390 status = iHostapdV1_1.addAccessPoint_1_1(ifaceParams1_1, nwParams); 391 } else { 392 status = mIHostapd.addAccessPoint(ifaceParams, nwParams); 393 } 394 if (!checkStatusAndLogFailure(status, methodStr)) { 395 return false; 396 } 397 mSoftApListeners.put(ifaceName, listener); 398 return true; 399 } catch (RemoteException e) { 400 handleRemoteException(e, methodStr); 401 return false; 402 } 403 } 404 } 405 406 /** 407 * Remove a previously started access point. 408 * 409 * @param ifaceName Name of the interface. 410 * @return true on success, false otherwise. 411 */ removeAccessPoint(@onNull String ifaceName)412 public boolean removeAccessPoint(@NonNull String ifaceName) { 413 synchronized (mLock) { 414 final String methodStr = "removeAccessPoint"; 415 if (!checkHostapdAndLogFailure(methodStr)) return false; 416 try { 417 HostapdStatus status = mIHostapd.removeAccessPoint(ifaceName); 418 if (!checkStatusAndLogFailure(status, methodStr)) { 419 return false; 420 } 421 mSoftApListeners.remove(ifaceName); 422 return true; 423 } catch (RemoteException e) { 424 handleRemoteException(e, methodStr); 425 return false; 426 } 427 } 428 } 429 430 /** 431 * Registers a death notification for hostapd. 432 * @return Returns true on success. 433 */ registerDeathHandler(@onNull HostapdDeathEventHandler handler)434 public boolean registerDeathHandler(@NonNull HostapdDeathEventHandler handler) { 435 if (mDeathEventHandler != null) { 436 Log.e(TAG, "Death handler already present"); 437 } 438 mDeathEventHandler = handler; 439 return true; 440 } 441 442 /** 443 * Deregisters a death notification for hostapd. 444 * @return Returns true on success. 445 */ deregisterDeathHandler()446 public boolean deregisterDeathHandler() { 447 if (mDeathEventHandler == null) { 448 Log.e(TAG, "No Death handler present"); 449 } 450 mDeathEventHandler = null; 451 return true; 452 } 453 454 /** 455 * Clear internal state. 456 */ clearState()457 private void clearState() { 458 synchronized (mLock) { 459 mIHostapd = null; 460 } 461 } 462 463 /** 464 * Handle hostapd death. 465 */ hostapdServiceDiedHandler(long cookie)466 private void hostapdServiceDiedHandler(long cookie) { 467 synchronized (mLock) { 468 if (mDeathRecipientCookie != cookie) { 469 Log.i(TAG, "Ignoring stale death recipient notification"); 470 return; 471 } 472 clearState(); 473 if (mDeathEventHandler != null) { 474 mDeathEventHandler.onDeath(); 475 } 476 } 477 } 478 479 /** 480 * Signals whether Initialization completed successfully. 481 */ isInitializationStarted()482 public boolean isInitializationStarted() { 483 synchronized (mLock) { 484 return mIServiceManager != null; 485 } 486 } 487 488 /** 489 * Signals whether Initialization completed successfully. 490 */ isInitializationComplete()491 public boolean isInitializationComplete() { 492 synchronized (mLock) { 493 return mIHostapd != null; 494 } 495 } 496 497 /** 498 * Start the hostapd daemon. 499 * 500 * @return true on success, false otherwise. 501 */ startDaemon()502 public boolean startDaemon() { 503 synchronized (mLock) { 504 try { 505 // This should startup hostapd daemon using the lazy start HAL mechanism. 506 getHostapdMockable(); 507 } catch (RemoteException e) { 508 Log.e(TAG, "Exception while trying to start hostapd: " 509 + e); 510 hostapdServiceDiedHandler(mDeathRecipientCookie); 511 return false; 512 } catch (NoSuchElementException e) { 513 // We're starting the daemon, so expect |NoSuchElementException|. 514 Log.d(TAG, "Successfully triggered start of hostapd using HIDL"); 515 } 516 return true; 517 } 518 } 519 520 /** 521 * Terminate the hostapd daemon & wait for it's death. 522 */ terminate()523 public void terminate() { 524 synchronized (mLock) { 525 // Register for a new death listener to block until hostapd is dead. 526 final long waitForDeathCookie = new Random().nextLong(); 527 final CountDownLatch waitForDeathLatch = new CountDownLatch(1); 528 linkToHostapdDeath((cookie) -> { 529 Log.d(TAG, "IHostapd died: cookie=" + cookie); 530 if (cookie != waitForDeathCookie) return; 531 waitForDeathLatch.countDown(); 532 }, waitForDeathCookie); 533 534 final String methodStr = "terminate"; 535 if (!checkHostapdAndLogFailure(methodStr)) return; 536 try { 537 mIHostapd.terminate(); 538 } catch (RemoteException e) { 539 handleRemoteException(e, methodStr); 540 } 541 542 // Now wait for death listener callback to confirm that it's dead. 543 try { 544 if (!waitForDeathLatch.await(WAIT_FOR_DEATH_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 545 Log.w(TAG, "Timed out waiting for confirmation of hostapd death"); 546 } 547 } catch (InterruptedException e) { 548 Log.w(TAG, "Failed to wait for hostapd death"); 549 } 550 } 551 } 552 553 /** 554 * Wrapper functions to access static HAL methods, created to be mockable in unit tests 555 */ 556 @VisibleForTesting getServiceManagerMockable()557 protected IServiceManager getServiceManagerMockable() throws RemoteException { 558 synchronized (mLock) { 559 return IServiceManager.getService(); 560 } 561 } 562 563 @VisibleForTesting getHostapdMockable()564 protected IHostapd getHostapdMockable() throws RemoteException { 565 synchronized (mLock) { 566 return IHostapd.getService(); 567 } 568 } 569 570 @VisibleForTesting getHostapdMockableV1_1()571 protected android.hardware.wifi.hostapd.V1_1.IHostapd getHostapdMockableV1_1() 572 throws RemoteException { 573 synchronized (mLock) { 574 try { 575 return android.hardware.wifi.hostapd.V1_1.IHostapd.castFrom(mIHostapd); 576 } catch (NoSuchElementException e) { 577 Log.e(TAG, "Failed to get IHostapd", e); 578 return null; 579 } 580 } 581 } 582 getEncryptionType(WifiConfiguration localConfig)583 private static int getEncryptionType(WifiConfiguration localConfig) { 584 int encryptionType; 585 switch (localConfig.getAuthType()) { 586 case WifiConfiguration.KeyMgmt.NONE: 587 encryptionType = IHostapd.EncryptionType.NONE; 588 break; 589 case WifiConfiguration.KeyMgmt.WPA_PSK: 590 encryptionType = IHostapd.EncryptionType.WPA; 591 break; 592 case WifiConfiguration.KeyMgmt.WPA2_PSK: 593 encryptionType = IHostapd.EncryptionType.WPA2; 594 break; 595 default: 596 // We really shouldn't default to None, but this was how NetworkManagementService 597 // used to do this. 598 encryptionType = IHostapd.EncryptionType.NONE; 599 break; 600 } 601 return encryptionType; 602 } 603 getBand(WifiConfiguration localConfig)604 private static int getBand(WifiConfiguration localConfig) { 605 int bandType; 606 switch (localConfig.apBand) { 607 case WifiConfiguration.AP_BAND_2GHZ: 608 bandType = IHostapd.Band.BAND_2_4_GHZ; 609 break; 610 case WifiConfiguration.AP_BAND_5GHZ: 611 bandType = IHostapd.Band.BAND_5_GHZ; 612 break; 613 case WifiConfiguration.AP_BAND_ANY: 614 bandType = IHostapd.Band.BAND_ANY; 615 break; 616 default: 617 throw new IllegalArgumentException(); 618 } 619 return bandType; 620 } 621 622 /** 623 * Convert channel list string like '1-6,11' to list of AcsChannelRanges 624 */ 625 private List<android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange> toAcsChannelRanges(String channelListStr)626 toAcsChannelRanges(String channelListStr) { 627 ArrayList<android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange> acsChannelRanges = 628 new ArrayList<>(); 629 String[] channelRanges = channelListStr.split(","); 630 for (String channelRange : channelRanges) { 631 android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange acsChannelRange = 632 new android.hardware.wifi.hostapd.V1_1.IHostapd.AcsChannelRange(); 633 try { 634 if (channelRange.contains("-")) { 635 String[] channels = channelRange.split("-"); 636 if (channels.length != 2) { 637 Log.e(TAG, "Unrecognized channel range, length is " + channels.length); 638 continue; 639 } 640 int start = Integer.parseInt(channels[0]); 641 int end = Integer.parseInt(channels[1]); 642 if (start > end) { 643 Log.e(TAG, "Invalid channel range, from " + start + " to " + end); 644 continue; 645 } 646 acsChannelRange.start = start; 647 acsChannelRange.end = end; 648 } else { 649 acsChannelRange.start = Integer.parseInt(channelRange); 650 acsChannelRange.end = acsChannelRange.start; 651 } 652 } catch (NumberFormatException e) { 653 // Ignore malformed value 654 Log.e(TAG, "Malformed channel value detected: " + e); 655 continue; 656 } 657 acsChannelRanges.add(acsChannelRange); 658 } 659 return acsChannelRanges; 660 } 661 662 /** 663 * Returns false if Hostapd is null, and logs failure to call methodStr 664 */ checkHostapdAndLogFailure(String methodStr)665 private boolean checkHostapdAndLogFailure(String methodStr) { 666 synchronized (mLock) { 667 if (mIHostapd == null) { 668 Log.e(TAG, "Can't call " + methodStr + ", IHostapd is null"); 669 return false; 670 } 671 return true; 672 } 673 } 674 675 /** 676 * Returns true if provided status code is SUCCESS, logs debug message and returns false 677 * otherwise 678 */ checkStatusAndLogFailure(HostapdStatus status, String methodStr)679 private boolean checkStatusAndLogFailure(HostapdStatus status, 680 String methodStr) { 681 synchronized (mLock) { 682 if (status.code != HostapdStatusCode.SUCCESS) { 683 Log.e(TAG, "IHostapd." + methodStr + " failed: " + status.code 684 + ", " + status.debugMessage); 685 return false; 686 } else { 687 if (mVerboseLoggingEnabled) { 688 Log.d(TAG, "IHostapd." + methodStr + " succeeded"); 689 } 690 return true; 691 } 692 } 693 } 694 handleRemoteException(RemoteException e, String methodStr)695 private void handleRemoteException(RemoteException e, String methodStr) { 696 synchronized (mLock) { 697 hostapdServiceDiedHandler(mDeathRecipientCookie); 698 Log.e(TAG, "IHostapd." + methodStr + " failed with exception", e); 699 } 700 } 701 702 private class HostapdCallback extends 703 android.hardware.wifi.hostapd.V1_1.IHostapdCallback.Stub { 704 @Override onFailure(String ifaceName)705 public void onFailure(String ifaceName) { 706 Log.w(TAG, "Failure on iface " + ifaceName); 707 WifiNative.SoftApListener listener = mSoftApListeners.get(ifaceName); 708 if (listener != null) { 709 listener.onFailure(); 710 } 711 } 712 } 713 } 714