content: /*
   * Copyright 2017 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.annotation.Nullable;
  import android.util.ArraySet;
  import android.util.Log;
  
  import com.android.server.wifi.WifiConfigStore.StoreData;
  import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil;
  import com.android.server.wifi.util.XmlUtil;
  
  import org.xmlpull.v1.XmlPullParser;
  import org.xmlpull.v1.XmlPullParserException;
  import org.xmlpull.v1.XmlSerializer;
  
  import java.io.IOException;
  import java.util.Collections;
  import java.util.Set;
  
  /**
   * Config store data for Wifi Wake.
   */
  public class WakeupConfigStoreData implements StoreData {
      private static final String TAG = "WakeupConfigStoreData";
  
      private static final String XML_TAG_FEATURE_STATE_SECTION = "FeatureState";
      private static final String XML_TAG_IS_ACTIVE = "IsActive";
      private static final String XML_TAG_IS_ONBOARDED = "IsOnboarded";
      private static final String XML_TAG_NOTIFICATIONS_SHOWN = "NotificationsShown";
      private static final String XML_TAG_NETWORK_SECTION = "Network";
      private static final String XML_TAG_SSID = "SSID";
      private static final String XML_TAG_SECURITY = "Security";
  
      private final DataSource<Boolean> mIsActiveDataSource;
      private final DataSource<Boolean> mIsOnboardedDataSource;
      private final DataSource<Integer> mNotificationsDataSource;
      private final DataSource<Set<ScanResultMatchInfo>> mNetworkDataSource;
      private boolean mHasBeenRead = false;
  
      /**
       * Interface defining a data source for the store data.
       *
       * @param <T> Type of data source
       */
      public interface DataSource<T> {
          /**
           * Returns the data from the data source.
           */
          T getData();
  
          /**
           * Updates the data in the data source.
           *
           * @param data Data retrieved from the store
           */
          void setData(T data);
      }
  
      /**
       * Creates the config store data with its data sources.
       *
       * @param isActiveDataSource Data source for isActive
       * @param networkDataSource Data source for the locked network list
       */
      public WakeupConfigStoreData(
              DataSource<Boolean> isActiveDataSource,
              DataSource<Boolean> isOnboardedDataSource,
              DataSource<Integer> notificationsDataSource,
              DataSource<Set<ScanResultMatchInfo>> networkDataSource) {
          mIsActiveDataSource = isActiveDataSource;
          mIsOnboardedDataSource = isOnboardedDataSource;
          mNotificationsDataSource = notificationsDataSource;
          mNetworkDataSource = networkDataSource;
      }
  
      /**
       * Returns whether the user store has been read.
       */
      public boolean hasBeenRead() {
          return mHasBeenRead;
      }
  
      @Override
      public void serializeData(XmlSerializer out,
              @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
              throws XmlPullParserException, IOException {
          writeFeatureState(out);
  
          for (ScanResultMatchInfo scanResultMatchInfo : mNetworkDataSource.getData()) {
              writeNetwork(out, scanResultMatchInfo);
          }
      }
  
      /**
       * Writes the current state of Wifi Wake to an XML output stream.
       *
       * @param out XML output stream
       * @throws XmlPullParserException
       * @throws IOException
       */
      private void writeFeatureState(XmlSerializer out)
              throws IOException, XmlPullParserException {
          XmlUtil.writeNextSectionStart(out, XML_TAG_FEATURE_STATE_SECTION);
  
          XmlUtil.writeNextValue(out, XML_TAG_IS_ACTIVE, mIsActiveDataSource.getData());
          XmlUtil.writeNextValue(out, XML_TAG_IS_ONBOARDED, mIsOnboardedDataSource.getData());
          XmlUtil.writeNextValue(out, XML_TAG_NOTIFICATIONS_SHOWN,
                  mNotificationsDataSource.getData());
  
          XmlUtil.writeNextSectionEnd(out, XML_TAG_FEATURE_STATE_SECTION);
      }
  
      /**
       * Writes a {@link ScanResultMatchInfo} to an XML output stream.
       *
       * @param out XML output stream
       * @param scanResultMatchInfo The ScanResultMatchInfo to serialize
       * @throws XmlPullParserException
       * @throws IOException
       */
      private void writeNetwork(XmlSerializer out, ScanResultMatchInfo scanResultMatchInfo)
              throws XmlPullParserException, IOException {
          XmlUtil.writeNextSectionStart(out, XML_TAG_NETWORK_SECTION);
  
          XmlUtil.writeNextValue(out, XML_TAG_SSID, scanResultMatchInfo.networkSsid);
          XmlUtil.writeNextValue(out, XML_TAG_SECURITY, scanResultMatchInfo.networkType);
  
          XmlUtil.writeNextSectionEnd(out, XML_TAG_NETWORK_SECTION);
      }
  
      @Override
      public void deserializeData(XmlPullParser in, int outerTagDepth,
              @WifiConfigStore.Version int version,
              @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)
              throws XmlPullParserException, IOException {
          if (!mHasBeenRead) {
              Log.d(TAG, "WifiWake user data has been read");
              mHasBeenRead = true;
          }
          // Ignore empty reads.
          if (in == null) {
              return;
          }
  
          Set<ScanResultMatchInfo> networks = new ArraySet<>();
  
          String[] headerName = new String[1];
          while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
              switch (headerName[0]) {
                  case XML_TAG_FEATURE_STATE_SECTION:
                      parseFeatureState(in, outerTagDepth + 1);
                      break;
                  case XML_TAG_NETWORK_SECTION:
                      networks.add(parseNetwork(in, outerTagDepth + 1));
                      break;
              }
          }
  
          mNetworkDataSource.setData(networks);
      }
  
      /**
       * Parses the state of Wifi Wake from an XML input stream and sets the respective data sources.
       *
       * @param in XML input stream
       * @param outerTagDepth XML tag depth of the containing section
       * @throws IOException
       * @throws XmlPullParserException
       */
      private void parseFeatureState(XmlPullParser in, int outerTagDepth)
              throws IOException, XmlPullParserException {
          boolean isActive = false;
          boolean isOnboarded = false;
          int notificationsShown = 0;
  
          while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
              String[] valueName = new String[1];
              Object value = XmlUtil.readCurrentValue(in, valueName);
              if (valueName[0] == null) {
                  throw new XmlPullParserException("Missing value name");
              }
              switch (valueName[0]) {
                  case XML_TAG_IS_ACTIVE:
                      isActive = (Boolean) value;
                      break;
                  case XML_TAG_IS_ONBOARDED:
                      isOnboarded = (Boolean) value;
                      break;
                  case XML_TAG_NOTIFICATIONS_SHOWN:
                      notificationsShown = (Integer) value;
                      break;
                  default:
                      throw new XmlPullParserException("Unknown value found: " + valueName[0]);
              }
          }
  
          mIsActiveDataSource.setData(isActive);
          mIsOnboardedDataSource.setData(isOnboarded);
          mNotificationsDataSource.setData(notificationsShown);
      }
  
      /**
       * Parses a {@link ScanResultMatchInfo} from an XML input stream.
       *
       * @param in XML input stream
       * @param outerTagDepth XML tag depth of the containing section
       * @return The {@link ScanResultMatchInfo}
       * @throws IOException
       * @throws XmlPullParserException
       */
      private ScanResultMatchInfo parseNetwork(XmlPullParser in, int outerTagDepth)
              throws IOException, XmlPullParserException {
          ScanResultMatchInfo scanResultMatchInfo = new ScanResultMatchInfo();
          while (!XmlUtil.isNextSectionEnd(in, outerTagDepth)) {
              String[] valueName = new String[1];
              Object value = XmlUtil.readCurrentValue(in, valueName);
              if (valueName[0] == null) {
                  throw new XmlPullParserException("Missing value name");
              }
              switch (valueName[0]) {
                  case XML_TAG_SSID:
                      scanResultMatchInfo.networkSsid = (String) value;
                      break;
                  case XML_TAG_SECURITY:
                      scanResultMatchInfo.networkType = (int) value;
                      break;
                  default:
                      throw new XmlPullParserException("Unknown tag under " + TAG + ": "
                              + valueName[0]);
              }
          }
  
          return scanResultMatchInfo;
      }
  
      @Override
      public void resetData() {
          mNetworkDataSource.setData(Collections.emptySet());
          mIsActiveDataSource.setData(false);
          mIsOnboardedDataSource.setData(false);
          mNotificationsDataSource.setData(0);
      }
  
      @Override
      public boolean hasNewDataToSerialize() {
          // always persist.
          return true;
      }
  
      @Override
      public String getName() {
          return TAG;
      }
  
      @Override
      public @WifiConfigStore.StoreFileId int getStoreFileId() {
          // Shared general store.
          return WifiConfigStore.STORE_FILE_USER_GENERAL;
      }
  }
  