content: /*
   * Copyright (C) 2018 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 android.net.wifi;
  
  import static com.android.internal.util.Preconditions.checkNotNull;
  import static com.android.internal.util.Preconditions.checkState;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.net.MacAddress;
  import android.net.MatchAllNetworkSpecifier;
  import android.net.NetworkAgent;
  import android.net.NetworkRequest;
  import android.net.NetworkSpecifier;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.text.TextUtils;
  
  import java.util.Objects;
  
  /**
   * Network specifier object used by wifi's {@link android.net.NetworkAgent}.
   * @hide
   */
  public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements Parcelable {
      /**
       * Security credentials for the currently connected network.
       */
      private final WifiConfiguration mWifiConfiguration;
  
      /**
       * The UID of the app that requested a specific wifi network using {@link WifiNetworkSpecifier}.
       *
       * Will only be filled when the device connects to a wifi network as a result of a
       * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to -1 if the device
       * auto-connected to a wifi network.
       */
      private final int mOriginalRequestorUid;
  
      /**
       * The package name of the app that requested a specific wifi network using
       * {@link WifiNetworkSpecifier}.
       *
       * Will only be filled when the device connects to a wifi network as a result of a
       * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to null if the device
       * auto-connected to a wifi network.
       */
      private final String mOriginalRequestorPackageName;
  
      public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration,
                                       int originalRequestorUid,
                                       @Nullable String originalRequestorPackageName) {
          checkNotNull(wifiConfiguration);
  
          mWifiConfiguration = wifiConfiguration;
          mOriginalRequestorUid = originalRequestorUid;
          mOriginalRequestorPackageName = originalRequestorPackageName;
      }
  
      /**
       * @hide
       */
      public static final @android.annotation.NonNull Creator<WifiNetworkAgentSpecifier> CREATOR =
              new Creator<WifiNetworkAgentSpecifier>() {
                  @Override
                  public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) {
                      WifiConfiguration wifiConfiguration = in.readParcelable(null);
                      int originalRequestorUid = in.readInt();
                      String originalRequestorPackageName = in.readString();
                      return new WifiNetworkAgentSpecifier(
                              wifiConfiguration, originalRequestorUid, originalRequestorPackageName);
                  }
  
                  @Override
                  public WifiNetworkAgentSpecifier[] newArray(int size) {
                      return new WifiNetworkAgentSpecifier[size];
                  }
              };
  
      @Override
      public int describeContents() {
          return 0;
      }
  
      @Override
      public void writeToParcel(@NonNull Parcel dest, int flags) {
          dest.writeParcelable(mWifiConfiguration, flags);
          dest.writeInt(mOriginalRequestorUid);
          dest.writeString(mOriginalRequestorPackageName);
      }
  
      @Override
      public boolean satisfiedBy(@Nullable NetworkSpecifier other) {
          if (this == other) {
              return true;
          }
          // Any generic requests should be satisifed by a specific wifi network.
          if (other == null || other instanceof MatchAllNetworkSpecifier) {
              return true;
          }
          if (other instanceof WifiNetworkSpecifier) {
              return satisfiesNetworkSpecifier((WifiNetworkSpecifier) other);
          }
          return equals(other);
      }
  
      /**
       * Match {@link WifiNetworkSpecifier} in app's {@link NetworkRequest} with the
       * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link NetworkAgent}.
       */
      public boolean satisfiesNetworkSpecifier(@NonNull WifiNetworkSpecifier ns) {
          // None of these should be null by construction.
          // {@link WifiNetworkSpecifier.Builder} enforces non-null in {@link WifiNetworkSpecifier}.
          // {@link WifiNetworkFactory} ensures non-null in {@link WifiNetworkAgentSpecifier}.
          checkNotNull(ns);
          checkNotNull(ns.ssidPatternMatcher);
          checkNotNull(ns.bssidPatternMatcher);
          checkNotNull(ns.wifiConfiguration.allowedKeyManagement);
          checkNotNull(this.mWifiConfiguration.SSID);
          checkNotNull(this.mWifiConfiguration.BSSID);
          checkNotNull(this.mWifiConfiguration.allowedKeyManagement);
  
          final String ssidWithQuotes = this.mWifiConfiguration.SSID;
          checkState(ssidWithQuotes.startsWith("\"") && ssidWithQuotes.endsWith("\""));
          final String ssidWithoutQuotes = ssidWithQuotes.substring(1, ssidWithQuotes.length() - 1);
          if (!ns.ssidPatternMatcher.match(ssidWithoutQuotes)) {
              return false;
          }
          final MacAddress bssid = MacAddress.fromString(this.mWifiConfiguration.BSSID);
          final MacAddress matchBaseAddress = ns.bssidPatternMatcher.first;
          final MacAddress matchMask = ns.bssidPatternMatcher.second;
          if (!bssid.matches(matchBaseAddress, matchMask))  {
              return false;
          }
          if (!ns.wifiConfiguration.allowedKeyManagement.equals(
                  this.mWifiConfiguration.allowedKeyManagement)) {
              return false;
          }
          if (ns.requestorUid != this.mOriginalRequestorUid) {
              return false;
          }
          if (!TextUtils.equals(ns.requestorPackageName, this.mOriginalRequestorPackageName)) {
              return false;
          }
          return true;
      }
  
      @Override
      public int hashCode() {
          return Objects.hash(
                  mWifiConfiguration.SSID,
                  mWifiConfiguration.BSSID,
                  mWifiConfiguration.allowedKeyManagement,
                  mOriginalRequestorUid,
                  mOriginalRequestorPackageName);
      }
  
      @Override
      public boolean equals(Object obj) {
          if (this == obj) {
              return true;
          }
          if (!(obj instanceof WifiNetworkAgentSpecifier)) {
              return false;
          }
          WifiNetworkAgentSpecifier lhs = (WifiNetworkAgentSpecifier) obj;
          return Objects.equals(this.mWifiConfiguration.SSID, lhs.mWifiConfiguration.SSID)
                  && Objects.equals(this.mWifiConfiguration.BSSID, lhs.mWifiConfiguration.BSSID)
                  && Objects.equals(this.mWifiConfiguration.allowedKeyManagement,
                      lhs.mWifiConfiguration.allowedKeyManagement)
                  && mOriginalRequestorUid == lhs.mOriginalRequestorUid
                  && TextUtils.equals(mOriginalRequestorPackageName,
                  lhs.mOriginalRequestorPackageName);
      }
  
      @Override
      public String toString() {
          StringBuilder sb = new StringBuilder("WifiNetworkAgentSpecifier [");
          sb.append("WifiConfiguration=")
                  .append(", SSID=").append(mWifiConfiguration.SSID)
                  .append(", BSSID=").append(mWifiConfiguration.BSSID)
                  .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid)
                  .append(", mOriginalRequestorPackageName=").append(mOriginalRequestorPackageName)
                  .append("]");
          return sb.toString();
      }
  
      @Override
      public void assertValidFromUid(int requestorUid) {
          throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used "
                  + "for requests.");
      }
  
      @Override
      public NetworkSpecifier redact() {
          return null;
      }
  }
  