content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.wifi;
  
  import static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC;
  import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL;
  import static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
  
  import android.annotation.NonNull;
  import android.content.Context;
  import android.content.Intent;
  import android.database.ContentObserver;
  import android.net.wifi.ScanResult;
  import android.net.wifi.WifiConfiguration;
  import android.net.wifi.WifiManager;
  import android.os.Handler;
  import android.os.Looper;
  import android.os.Message;
  import android.os.SystemClock;
  import android.os.UserHandle;
  import android.provider.Settings;
  import android.text.TextUtils;
  import android.util.Log;
  
  import com.android.internal.R;
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.internal.util.IState;
  import com.android.internal.util.State;
  import com.android.internal.util.StateMachine;
  import com.android.internal.util.WakeupMessage;
  import com.android.server.wifi.WifiNative.InterfaceCallback;
  import com.android.server.wifi.WifiNative.SoftApListener;
  import com.android.server.wifi.util.ApConfigUtil;
  
  import java.io.FileDescriptor;
  import java.io.PrintWriter;
  import java.util.Locale;
  
  /**
   * Manage WiFi in AP mode.
   * The internal state machine runs under the ClientModeImpl handler thread context.
   */
  public class SoftApManager implements ActiveModeManager {
      private static final String TAG = "SoftApManager";
  
      // Minimum limit to use for timeout delay if the value from overlay setting is too small.
      private static final int MIN_SOFT_AP_TIMEOUT_DELAY_MS = 600_000;  // 10 minutes
  
      @VisibleForTesting
      public static final String SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG = TAG
              + " Soft AP Send Message Timeout";
  
      private final Context mContext;
      private final FrameworkFacade mFrameworkFacade;
      private final WifiNative mWifiNative;
  
      private final String mCountryCode;
  
      private final SoftApStateMachine mStateMachine;
  
      private final WifiManager.SoftApCallback mCallback;
  
      private String mApInterfaceName;
      private boolean mIfaceIsUp;
      private boolean mIfaceIsDestroyed;
  
      private final WifiApConfigStore mWifiApConfigStore;
  
      private final WifiMetrics mWifiMetrics;
  
      private final int mMode;
      private WifiConfiguration mApConfig;
  
      private int mReportedFrequency = -1;
      private int mReportedBandwidth = -1;
  
      private int mNumAssociatedStations = 0;
      private boolean mTimeoutEnabled = false;
  
      private final SarManager mSarManager;
  
      private long mStartTimestamp = -1;
  
      private BaseWifiDiagnostics mWifiDiagnostics;
  
      /**
       * Listener for soft AP events.
       */
      private final SoftApListener mSoftApListener = new SoftApListener() {
  
          @Override
          public void onFailure() {
              mStateMachine.sendMessage(SoftApStateMachine.CMD_FAILURE);
          }
  
          @Override
          public void onNumAssociatedStationsChanged(int numStations) {
              mStateMachine.sendMessage(
                      SoftApStateMachine.CMD_NUM_ASSOCIATED_STATIONS_CHANGED, numStations);
          }
  
          @Override
          public void onSoftApChannelSwitched(int frequency, int bandwidth) {
              mStateMachine.sendMessage(
                      SoftApStateMachine.CMD_SOFT_AP_CHANNEL_SWITCHED, frequency, bandwidth);
          }
      };
  
      public SoftApManager(@NonNull Context context,
                           @NonNull Looper looper,
                           @NonNull FrameworkFacade framework,
                           @NonNull WifiNative wifiNative,
                           String countryCode,
                           @NonNull WifiManager.SoftApCallback callback,
                           @NonNull WifiApConfigStore wifiApConfigStore,
                           @NonNull SoftApModeConfiguration apConfig,
                           @NonNull WifiMetrics wifiMetrics,
                           @NonNull SarManager sarManager,
                           @NonNull BaseWifiDiagnostics wifiDiagnostics) {
          mContext = context;
          mFrameworkFacade = framework;
          mWifiNative = wifiNative;
          mCountryCode = countryCode;
          mCallback = callback;
          mWifiApConfigStore = wifiApConfigStore;
          mMode = apConfig.getTargetMode();
          WifiConfiguration config = apConfig.getWifiConfiguration();
          if (config == null) {
              mApConfig = mWifiApConfigStore.getApConfiguration();
          } else {
              mApConfig = config;
          }
          mWifiMetrics = wifiMetrics;
          mSarManager = sarManager;
          mWifiDiagnostics = wifiDiagnostics;
          mStateMachine = new SoftApStateMachine(looper);
      }
  
      /**
       * Start soft AP with the supplied config.
       */
      public void start() {
          mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig);
      }
  
      /**
       * Stop soft AP.
       */
      public void stop() {
          Log.d(TAG, " currentstate: " + getCurrentStateName());
          if (mApInterfaceName != null) {
              if (mIfaceIsUp) {
                  updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                          WifiManager.WIFI_AP_STATE_ENABLED, 0);
              } else {
                  updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                          WifiManager.WIFI_AP_STATE_ENABLING, 0);
              }
          }
          mStateMachine.quitNow();
      }
  
      public @ScanMode int getScanMode() {
          return SCAN_NONE;
      }
  
      public int getIpMode() {
          return mMode;
      }
  
      /**
       * Dump info about this softap manager.
       */
      public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
          pw.println("--Dump of SoftApManager--");
  
          pw.println("current StateMachine mode: " + getCurrentStateName());
          pw.println("mApInterfaceName: " + mApInterfaceName);
          pw.println("mIfaceIsUp: " + mIfaceIsUp);
          pw.println("mMode: " + mMode);
          pw.println("mCountryCode: " + mCountryCode);
          if (mApConfig != null) {
              pw.println("mApConfig.SSID: " + mApConfig.SSID);
              pw.println("mApConfig.apBand: " + mApConfig.apBand);
              pw.println("mApConfig.hiddenSSID: " + mApConfig.hiddenSSID);
          } else {
              pw.println("mApConfig: null");
          }
          pw.println("mNumAssociatedStations: " + mNumAssociatedStations);
          pw.println("mTimeoutEnabled: " + mTimeoutEnabled);
          pw.println("mReportedFrequency: " + mReportedFrequency);
          pw.println("mReportedBandwidth: " + mReportedBandwidth);
          pw.println("mStartTimestamp: " + mStartTimestamp);
      }
  
      private String getCurrentStateName() {
          IState currentState = mStateMachine.getCurrentState();
  
          if (currentState != null) {
              return currentState.getName();
          }
  
          return "StateMachine not active";
      }
  
      /**
       * Update AP state.
       * @param newState new AP state
       * @param currentState current AP state
       * @param reason Failure reason if the new AP state is in failure state
       */
      private void updateApState(int newState, int currentState, int reason) {
          mCallback.onStateChanged(newState, reason);
  
          //send the AP state change broadcast
          final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
          intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
          intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, newState);
          intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);
          if (newState == WifiManager.WIFI_AP_STATE_FAILED) {
              //only set reason number when softAP start failed
              intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
          }
  
          intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
          intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mMode);
          mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
      }
  
      /**
       * Start a soft AP instance with the given configuration.
       * @param config AP configuration
       * @return integer result code
       */
      private int startSoftAp(WifiConfiguration config) {
          if (config == null || config.SSID == null) {
              Log.e(TAG, "Unable to start soft AP without valid configuration");
              return ERROR_GENERIC;
          }
          // Setup country code
          if (TextUtils.isEmpty(mCountryCode)) {
              if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
                  // Country code is mandatory for 5GHz band.
                  Log.e(TAG, "Invalid country code, required for setting up "
                          + "soft ap in 5GHz");
                  return ERROR_GENERIC;
              }
              // Absence of country code is not fatal for 2Ghz & Any band options.
          } else if (!mWifiNative.setCountryCodeHal(
                  mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
              if (config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
                  // Return an error if failed to set country code when AP is configured for
                  // 5GHz band.
                  Log.e(TAG, "Failed to set country code, required for setting up "
                          + "soft ap in 5GHz");
                  return ERROR_GENERIC;
              }
              // Failure to set country code is not fatal for 2Ghz & Any band options.
          }
  
          // Make a copy of configuration for updating AP band and channel.
          WifiConfiguration localConfig = new WifiConfiguration(config);
  
          int result = ApConfigUtil.updateApChannelConfig(
                  mWifiNative, mCountryCode,
                  mWifiApConfigStore.getAllowed2GChannel(), localConfig);
  
          if (result != SUCCESS) {
              Log.e(TAG, "Failed to update AP band and channel");
              return result;
          }
  
          if (localConfig.hiddenSSID) {
              Log.d(TAG, "SoftAP is a hidden network");
          }
          if (!mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener)) {
              Log.e(TAG, "Soft AP start failed");
              return ERROR_GENERIC;
          }
          mWifiDiagnostics.startLogging(mApInterfaceName);
          mStartTimestamp = SystemClock.elapsedRealtime();
          Log.d(TAG, "Soft AP is started");
  
          return SUCCESS;
      }
  
      /**
       * Teardown soft AP and teardown the interface.
       */
      private void stopSoftAp() {
          mWifiDiagnostics.stopLogging(mApInterfaceName);
          mWifiNative.teardownInterface(mApInterfaceName);
          Log.d(TAG, "Soft AP is stopped");
      }
  
      private class SoftApStateMachine extends StateMachine {
          // Commands for the state machine.
          public static final int CMD_START = 0;
          public static final int CMD_FAILURE = 2;
          public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
          public static final int CMD_NUM_ASSOCIATED_STATIONS_CHANGED = 4;
          public static final int CMD_NO_ASSOCIATED_STATIONS_TIMEOUT = 5;
          public static final int CMD_TIMEOUT_TOGGLE_CHANGED = 6;
          public static final int CMD_INTERFACE_DESTROYED = 7;
          public static final int CMD_INTERFACE_DOWN = 8;
          public static final int CMD_SOFT_AP_CHANNEL_SWITCHED = 9;
  
          private final State mIdleState = new IdleState();
          private final State mStartedState = new StartedState();
  
          private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() {
              @Override
              public void onDestroyed(String ifaceName) {
                  if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
                      sendMessage(CMD_INTERFACE_DESTROYED);
                  }
              }
  
              @Override
              public void onUp(String ifaceName) {
                  if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
                      sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1);
                  }
              }
  
              @Override
              public void onDown(String ifaceName) {
                  if (mApInterfaceName != null && mApInterfaceName.equals(ifaceName)) {
                      sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0);
                  }
              }
          };
  
          SoftApStateMachine(Looper looper) {
              super(TAG, looper);
  
              addState(mIdleState);
              addState(mStartedState);
  
              setInitialState(mIdleState);
              start();
          }
  
          private class IdleState extends State {
              @Override
              public void enter() {
                  mApInterfaceName = null;
                  mIfaceIsUp = false;
                  mIfaceIsDestroyed = false;
              }
  
              @Override
              public boolean processMessage(Message message) {
                  switch (message.what) {
                      case CMD_START:
                          mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
                                  mWifiNativeInterfaceCallback);
                          if (TextUtils.isEmpty(mApInterfaceName)) {
                              Log.e(TAG, "setup failure when creating ap interface.");
                              updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                      WifiManager.WIFI_AP_STATE_DISABLED,
                                      WifiManager.SAP_START_FAILURE_GENERAL);
                              mWifiMetrics.incrementSoftApStartResult(
                                      false, WifiManager.SAP_START_FAILURE_GENERAL);
                              break;
                          }
                          updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
                                  WifiManager.WIFI_AP_STATE_DISABLED, 0);
                          int result = startSoftAp((WifiConfiguration) message.obj);
                          if (result != SUCCESS) {
                              int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
                              if (result == ERROR_NO_CHANNEL) {
                                  failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
                              }
                              updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                            WifiManager.WIFI_AP_STATE_ENABLING,
                                            failureReason);
                              stopSoftAp();
                              mWifiMetrics.incrementSoftApStartResult(false, failureReason);
                              break;
                          }
                          transitionTo(mStartedState);
                          break;
                      default:
                          // Ignore all other commands.
                          break;
                  }
  
                  return HANDLED;
              }
          }
  
          private class StartedState extends State {
              private int mTimeoutDelay;
              private WakeupMessage mSoftApTimeoutMessage;
              private SoftApTimeoutEnabledSettingObserver mSettingObserver;
  
              /**
              * Observer for timeout settings changes.
              */
              private class SoftApTimeoutEnabledSettingObserver extends ContentObserver {
                  SoftApTimeoutEnabledSettingObserver(Handler handler) {
                      super(handler);
                  }
  
                  public void register() {
                      mFrameworkFacade.registerContentObserver(mContext,
                              Settings.Global.getUriFor(Settings.Global.SOFT_AP_TIMEOUT_ENABLED),
                              true, this);
                      mTimeoutEnabled = getValue();
                  }
  
                  public void unregister() {
                      mFrameworkFacade.unregisterContentObserver(mContext, this);
                  }
  
                  @Override
                  public void onChange(boolean selfChange) {
                      super.onChange(selfChange);
                      mStateMachine.sendMessage(SoftApStateMachine.CMD_TIMEOUT_TOGGLE_CHANGED,
                              getValue() ? 1 : 0);
                  }
  
                  private boolean getValue() {
                      boolean enabled = mFrameworkFacade.getIntegerSetting(mContext,
                              Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1) == 1;
                      return enabled;
                  }
              }
  
              private int getConfigSoftApTimeoutDelay() {
                  int delay = mContext.getResources().getInteger(
                          R.integer.config_wifi_framework_soft_ap_timeout_delay);
                  if (delay < MIN_SOFT_AP_TIMEOUT_DELAY_MS) {
                      delay = MIN_SOFT_AP_TIMEOUT_DELAY_MS;
                      Log.w(TAG, "Overriding timeout delay with minimum limit value");
                  }
                  Log.d(TAG, "Timeout delay: " + delay);
                  return delay;
              }
  
              private void scheduleTimeoutMessage() {
                  if (!mTimeoutEnabled) {
                      return;
                  }
                  mSoftApTimeoutMessage.schedule(SystemClock.elapsedRealtime() + mTimeoutDelay);
                  Log.d(TAG, "Timeout message scheduled");
              }
  
              private void cancelTimeoutMessage() {
                  mSoftApTimeoutMessage.cancel();
                  Log.d(TAG, "Timeout message canceled");
              }
  
              /**
               * Set number of stations associated with this soft AP
               * @param numStations Number of connected stations
               */
              private void setNumAssociatedStations(int numStations) {
                  if (mNumAssociatedStations == numStations) {
                      return;
                  }
                  mNumAssociatedStations = numStations;
                  Log.d(TAG, "Number of associated stations changed: " + mNumAssociatedStations);
  
                  if (mCallback != null) {
                      mCallback.onNumClientsChanged(mNumAssociatedStations);
                  } else {
                      Log.e(TAG, "SoftApCallback is null. Dropping NumClientsChanged event.");
                  }
                  mWifiMetrics.addSoftApNumAssociatedStationsChangedEvent(mNumAssociatedStations,
                          mMode);
  
                  if (mNumAssociatedStations == 0) {
                      scheduleTimeoutMessage();
                  } else {
                      cancelTimeoutMessage();
                  }
              }
  
              private void onUpChanged(boolean isUp) {
                  if (isUp == mIfaceIsUp) {
                      return;  // no change
                  }
                  mIfaceIsUp = isUp;
                  if (isUp) {
                      Log.d(TAG, "SoftAp is ready for use");
                      updateApState(WifiManager.WIFI_AP_STATE_ENABLED,
                              WifiManager.WIFI_AP_STATE_ENABLING, 0);
                      mWifiMetrics.incrementSoftApStartResult(true, 0);
                      if (mCallback != null) {
                          mCallback.onNumClientsChanged(mNumAssociatedStations);
                      }
                  } else {
                      // the interface was up, but goes down
                      sendMessage(CMD_INTERFACE_DOWN);
                  }
                  mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode);
              }
  
              @Override
              public void enter() {
                  mIfaceIsUp = false;
                  mIfaceIsDestroyed = false;
                  onUpChanged(mWifiNative.isInterfaceUp(mApInterfaceName));
  
                  mTimeoutDelay = getConfigSoftApTimeoutDelay();
                  Handler handler = mStateMachine.getHandler();
                  mSoftApTimeoutMessage = new WakeupMessage(mContext, handler,
                          SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG,
                          SoftApStateMachine.CMD_NO_ASSOCIATED_STATIONS_TIMEOUT);
                  mSettingObserver = new SoftApTimeoutEnabledSettingObserver(handler);
  
                  if (mSettingObserver != null) {
                      mSettingObserver.register();
                  }
  
                  mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_ENABLED);
  
                  Log.d(TAG, "Resetting num stations on start");
                  mNumAssociatedStations = 0;
                  scheduleTimeoutMessage();
              }
  
              @Override
              public void exit() {
                  if (!mIfaceIsDestroyed) {
                      stopSoftAp();
                  }
  
                  if (mSettingObserver != null) {
                      mSettingObserver.unregister();
                  }
                  Log.d(TAG, "Resetting num stations on stop");
                  setNumAssociatedStations(0);
                  cancelTimeoutMessage();
                  // Need this here since we are exiting |Started| state and won't handle any
                  // future CMD_INTERFACE_STATUS_CHANGED events after this point
                  mWifiMetrics.addSoftApUpChangedEvent(false, mMode);
                  updateApState(WifiManager.WIFI_AP_STATE_DISABLED,
                          WifiManager.WIFI_AP_STATE_DISABLING, 0);
  
                  mSarManager.setSapWifiState(WifiManager.WIFI_AP_STATE_DISABLED);
                  mApInterfaceName = null;
                  mIfaceIsUp = false;
                  mIfaceIsDestroyed = false;
                  mStateMachine.quitNow();
              }
  
              private void updateUserBandPreferenceViolationMetricsIfNeeded() {
                  boolean bandPreferenceViolated = false;
                  if (mApConfig.apBand == WifiConfiguration.AP_BAND_2GHZ
                          && ScanResult.is5GHz(mReportedFrequency)) {
                      bandPreferenceViolated = true;
                  } else if (mApConfig.apBand == WifiConfiguration.AP_BAND_5GHZ
                          && ScanResult.is24GHz(mReportedFrequency)) {
                      bandPreferenceViolated = true;
                  }
                  if (bandPreferenceViolated) {
                      Log.e(TAG, "Channel does not satisfy user band preference: "
                              + mReportedFrequency);
                      mWifiMetrics.incrementNumSoftApUserBandPreferenceUnsatisfied();
                  }
              }
  
              @Override
              public boolean processMessage(Message message) {
                  switch (message.what) {
                      case CMD_NUM_ASSOCIATED_STATIONS_CHANGED:
                          if (message.arg1 < 0) {
                              Log.e(TAG, "Invalid number of associated stations: " + message.arg1);
                              break;
                          }
                          Log.d(TAG, "Setting num stations on CMD_NUM_ASSOCIATED_STATIONS_CHANGED");
                          setNumAssociatedStations(message.arg1);
                          break;
                      case CMD_SOFT_AP_CHANNEL_SWITCHED:
                          mReportedFrequency = message.arg1;
                          mReportedBandwidth = message.arg2;
                          Log.d(TAG, "Channel switched. Frequency: " + mReportedFrequency
                                  + " Bandwidth: " + mReportedBandwidth);
                          mWifiMetrics.addSoftApChannelSwitchedEvent(mReportedFrequency,
                                  mReportedBandwidth, mMode);
                          updateUserBandPreferenceViolationMetricsIfNeeded();
                          break;
                      case CMD_TIMEOUT_TOGGLE_CHANGED:
                          boolean isEnabled = (message.arg1 == 1);
                          if (mTimeoutEnabled == isEnabled) {
                              break;
                          }
                          mTimeoutEnabled = isEnabled;
                          if (!mTimeoutEnabled) {
                              cancelTimeoutMessage();
                          }
                          if (mTimeoutEnabled && mNumAssociatedStations == 0) {
                              scheduleTimeoutMessage();
                          }
                          break;
                      case CMD_INTERFACE_STATUS_CHANGED:
                          boolean isUp = message.arg1 == 1;
                          onUpChanged(isUp);
                          break;
                      case CMD_START:
                          // Already started, ignore this command.
                          break;
                      case CMD_NO_ASSOCIATED_STATIONS_TIMEOUT:
                          if (!mTimeoutEnabled) {
                              Log.wtf(TAG, "Timeout message received while timeout is disabled."
                                      + " Dropping.");
                              break;
                          }
                          if (mNumAssociatedStations != 0) {
                              Log.wtf(TAG, "Timeout message received but has clients. Dropping.");
                              break;
                          }
                          Log.i(TAG, "Timeout message received. Stopping soft AP.");
                          updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                  WifiManager.WIFI_AP_STATE_ENABLED, 0);
                          transitionTo(mIdleState);
                          break;
                      case CMD_INTERFACE_DESTROYED:
                          Log.d(TAG, "Interface was cleanly destroyed.");
                          updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                  WifiManager.WIFI_AP_STATE_ENABLED, 0);
                          mIfaceIsDestroyed = true;
                          transitionTo(mIdleState);
                          break;
                      case CMD_FAILURE:
                          Log.w(TAG, "hostapd failure, stop and report failure");
                          /* fall through */
                      case CMD_INTERFACE_DOWN:
                          Log.w(TAG, "interface error, stop and report failure");
                          updateApState(WifiManager.WIFI_AP_STATE_FAILED,
                                  WifiManager.WIFI_AP_STATE_ENABLED,
                                  WifiManager.SAP_START_FAILURE_GENERAL);
                          updateApState(WifiManager.WIFI_AP_STATE_DISABLING,
                                  WifiManager.WIFI_AP_STATE_FAILED, 0);
                          transitionTo(mIdleState);
                          break;
                      default:
                          return NOT_HANDLED;
                  }
                  return HANDLED;
              }
          }
      }
  }
  