content: /*
   * Copyright (C) 2013 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 android.content.ContentResolver;
  import android.content.Context;
  import android.provider.Settings;
  
  import java.io.FileDescriptor;
  import java.io.PrintWriter;
  
  /* Tracks persisted settings for Wi-Fi and airplane mode interaction */
  public class WifiSettingsStore {
      /* Values tracked in Settings.Global.WIFI_ON */
      static final int WIFI_DISABLED                      = 0;
      static final int WIFI_ENABLED                       = 1;
  
      /* Wifi enabled while in airplane mode */
      private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE     = 2;
      /* Wifi disabled due to airplane mode on */
      private static final int WIFI_DISABLED_AIRPLANE_ON          = 3;
  
      /* Persisted state that tracks the wifi & airplane interaction from settings */
      private int mPersistWifiState = WIFI_DISABLED;
      /* Tracks current airplane mode state */
      private boolean mAirplaneModeOn = false;
  
      /* Tracks the setting of scan being available even when wi-fi is turned off
       */
      private boolean mScanAlwaysAvailable;
  
      private final Context mContext;
  
      /* Tracks if we have checked the saved wi-fi state after boot */
      private boolean mCheckSavedStateAtBoot = false;
  
      WifiSettingsStore(Context context) {
          mContext = context;
          mAirplaneModeOn = getPersistedAirplaneModeOn();
          mPersistWifiState = getPersistedWifiState();
          mScanAlwaysAvailable = getPersistedScanAlwaysAvailable();
      }
  
      public synchronized boolean isWifiToggleEnabled() {
          if (!mCheckSavedStateAtBoot) {
              mCheckSavedStateAtBoot = true;
              if (testAndClearWifiSavedState()) return true;
          }
  
          if (mAirplaneModeOn) {
              return mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE;
          } else {
              return mPersistWifiState != WIFI_DISABLED;
          }
      }
  
      /**
       * Returns true if airplane mode is currently on.
       * @return {@code true} if airplane mode is on.
       */
      public synchronized boolean isAirplaneModeOn() {
         return mAirplaneModeOn;
      }
  
      public synchronized boolean isScanAlwaysAvailable() {
          return !mAirplaneModeOn && mScanAlwaysAvailable;
      }
  
      public synchronized boolean handleWifiToggled(boolean wifiEnabled) {
          // Can Wi-Fi be toggled in airplane mode ?
          if (mAirplaneModeOn && !isAirplaneToggleable()) {
              return false;
          }
  
          if (wifiEnabled) {
              if (mAirplaneModeOn) {
                  persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
              } else {
                  persistWifiState(WIFI_ENABLED);
              }
          } else {
              // When wifi state is disabled, we do not care
              // if airplane mode is on or not. The scenario of
              // wifi being disabled due to airplane mode being turned on
              // is handled handleAirplaneModeToggled()
              persistWifiState(WIFI_DISABLED);
          }
          return true;
      }
  
      synchronized boolean handleAirplaneModeToggled() {
          // Is Wi-Fi sensitive to airplane mode changes ?
          if (!isAirplaneSensitive()) {
              return false;
          }
  
          mAirplaneModeOn = getPersistedAirplaneModeOn();
          if (mAirplaneModeOn) {
              // Wifi disabled due to airplane on
              if (mPersistWifiState == WIFI_ENABLED) {
                  persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
              }
          } else {
              /* On airplane mode disable, restore wifi state if necessary */
              if (testAndClearWifiSavedState() ||
                      mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE
                      || mPersistWifiState == WIFI_DISABLED_AIRPLANE_ON) {
                  persistWifiState(WIFI_ENABLED);
              }
          }
          return true;
      }
  
      synchronized void handleWifiScanAlwaysAvailableToggled() {
          mScanAlwaysAvailable = getPersistedScanAlwaysAvailable();
      }
  
      void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
          pw.println("mPersistWifiState " + mPersistWifiState);
          pw.println("mAirplaneModeOn " + mAirplaneModeOn);
      }
  
      private void persistWifiState(int state) {
          final ContentResolver cr = mContext.getContentResolver();
          mPersistWifiState = state;
          Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state);
      }
  
      /* Does Wi-Fi need to be disabled when airplane mode is on ? */
      private boolean isAirplaneSensitive() {
          String airplaneModeRadios = Settings.Global.getString(mContext.getContentResolver(),
                  Settings.Global.AIRPLANE_MODE_RADIOS);
          return airplaneModeRadios == null
                  || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI);
      }
  
      /* Is Wi-Fi allowed to be re-enabled while airplane mode is on ? */
      private boolean isAirplaneToggleable() {
          String toggleableRadios = Settings.Global.getString(mContext.getContentResolver(),
                  Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
          return toggleableRadios != null
                  && toggleableRadios.contains(Settings.Global.RADIO_WIFI);
      }
  
      /**
       * After a reboot, we restore wi-fi to be on if it was turned off temporarily for tethering.
       * The settings app tracks the saved state, but the framework has to check it at boot to
       * make sure the wi-fi is turned on in case it was turned off for the purpose of tethering.
       *
       * Note that this is not part of the regular WIFI_ON setting because this only needs to
       * be controlled through the settings app and not the Wi-Fi public API.
       */
      private boolean testAndClearWifiSavedState() {
          int wifiSavedState = getWifiSavedState();
          if (wifiSavedState == WIFI_ENABLED) {
              setWifiSavedState(WIFI_DISABLED);
          }
          return (wifiSavedState == WIFI_ENABLED);
      }
  
      /**
       * Allow callers to set the Settings.Global.WIFI_SAVED_STATE property.
       *
       * When changing states, we need to remember what the wifi state was before switching.  An
       * example of this is when WiFiController switches to APEnabledState.  Before swtiching to the
       * new state, WifiController sets the current WiFi enabled/disabled state.  When the AP is
       * turned off, the WIFI_SAVED_STATE setting is used to restore the previous wifi state.
       *
       * @param state WiFi state to store with the Settings.Global.WIFI_SAVED_STATE property.
       */
      public void setWifiSavedState(int state) {
          Settings.Global.putInt(mContext.getContentResolver(),
                  Settings.Global.WIFI_SAVED_STATE, state);
      }
  
      /**
       * Allow callers to get the Settings.Global.WIFI_SAVED_STATE property.
       *
       * When changing states we remember what the wifi state was before switching.  This function is
       * used to get the saved state.
       *
       * @return int Value for the previously saved state.
       */
      public int getWifiSavedState() {
          try {
              return Settings.Global.getInt(mContext.getContentResolver(),
                      Settings.Global.WIFI_SAVED_STATE);
          } catch (Settings.SettingNotFoundException e) {
              // If we have an error, return wifiSavedState off.
              return WIFI_DISABLED;
          }
      }
  
      private int getPersistedWifiState() {
          final ContentResolver cr = mContext.getContentResolver();
          try {
              return Settings.Global.getInt(cr, Settings.Global.WIFI_ON);
          } catch (Settings.SettingNotFoundException e) {
              Settings.Global.putInt(cr, Settings.Global.WIFI_ON, WIFI_DISABLED);
              return WIFI_DISABLED;
          }
      }
  
      private boolean getPersistedAirplaneModeOn() {
          return Settings.Global.getInt(mContext.getContentResolver(),
                  Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
      }
  
      private boolean getPersistedScanAlwaysAvailable() {
          return Settings.Global.getInt(mContext.getContentResolver(),
                  Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
                  0) == 1;
      }
  }
  