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 android.net.metrics;
  
  import android.annotation.IntDef;
  import android.annotation.NonNull;
  import android.annotation.SystemApi;
  import android.annotation.TestApi;
  import android.annotation.UnsupportedAppUsage;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.text.TextUtils;
  import android.util.SparseArray;
  
  import com.android.internal.util.MessageUtils;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.ArrayList;
  import java.util.BitSet;
  import java.util.List;
  
  /**
   * An event logged when there is a change or event that requires updating the
   * the APF program in place with a new APF program.
   * {@hide}
   */
  @TestApi
  @SystemApi
  public final class ApfProgramEvent implements IpConnectivityLog.Event {
  
      // Bitflag constants describing what an Apf program filters.
      // Bits are indexeds from LSB to MSB, starting at index 0.
      /** @hide */
      public static final int FLAG_MULTICAST_FILTER_ON = 0;
      /** @hide */
      public static final int FLAG_HAS_IPV4_ADDRESS    = 1;
  
      /** {@hide} */
      @IntDef(flag = true, value = {FLAG_MULTICAST_FILTER_ON, FLAG_HAS_IPV4_ADDRESS})
      @Retention(RetentionPolicy.SOURCE)
      public @interface Flags {}
  
      /** @hide */
      @UnsupportedAppUsage
      public final long lifetime;       // Maximum computed lifetime of the program in seconds
      /** @hide */
      @UnsupportedAppUsage
      public final long actualLifetime; // Effective program lifetime in seconds
      /** @hide */
      @UnsupportedAppUsage
      public final int filteredRas;     // Number of RAs filtered by the APF program
      /** @hide */
      @UnsupportedAppUsage
      public final int currentRas;      // Total number of current RAs at generation time
      /** @hide */
      @UnsupportedAppUsage
      public final int programLength;   // Length of the APF program in bytes
      /** @hide */
      @UnsupportedAppUsage
      public final int flags;           // Bitfield compound of FLAG_* constants
  
      private ApfProgramEvent(long lifetime, long actualLifetime, int filteredRas, int currentRas,
              int programLength, int flags) {
          this.lifetime = lifetime;
          this.actualLifetime = actualLifetime;
          this.filteredRas = filteredRas;
          this.currentRas = currentRas;
          this.programLength = programLength;
          this.flags = flags;
      }
  
      private ApfProgramEvent(Parcel in) {
          this.lifetime = in.readLong();
          this.actualLifetime = in.readLong();
          this.filteredRas = in.readInt();
          this.currentRas = in.readInt();
          this.programLength = in.readInt();
          this.flags = in.readInt();
      }
  
      /**
       * Utility to create an instance of {@link ApfProgramEvent}.
       */
      public static final class Builder {
          private long mLifetime;
          private long mActualLifetime;
          private int mFilteredRas;
          private int mCurrentRas;
          private int mProgramLength;
          private int mFlags;
  
          /**
           * Set the maximum computed lifetime of the program in seconds.
           */
          @NonNull
          public Builder setLifetime(long lifetime) {
              mLifetime = lifetime;
              return this;
          }
  
          /**
           * Set the effective program lifetime in seconds.
           */
          @NonNull
          public Builder setActualLifetime(long lifetime) {
              mActualLifetime = lifetime;
              return this;
          }
  
          /**
           * Set the number of RAs filtered by the APF program.
           */
          @NonNull
          public Builder setFilteredRas(int filteredRas) {
              mFilteredRas = filteredRas;
              return this;
          }
  
          /**
           * Set the total number of current RAs at generation time.
           */
          @NonNull
          public Builder setCurrentRas(int currentRas) {
              mCurrentRas = currentRas;
              return this;
          }
  
          /**
           * Set the length of the APF program in bytes.
           */
          @NonNull
          public Builder setProgramLength(int programLength) {
              mProgramLength = programLength;
              return this;
          }
  
          /**
           * Set the flags describing what an Apf program filters.
           */
          @NonNull
          public Builder setFlags(boolean hasIPv4, boolean multicastFilterOn) {
              mFlags = flagsFor(hasIPv4, multicastFilterOn);
              return this;
          }
  
          /**
           * Build a new {@link ApfProgramEvent}.
           */
          @NonNull
          public ApfProgramEvent build() {
              return new ApfProgramEvent(mLifetime, mActualLifetime, mFilteredRas, mCurrentRas,
                      mProgramLength, mFlags);
          }
      }
  
      /** @hide */
      @Override
      public void writeToParcel(Parcel out, int flags) {
          out.writeLong(lifetime);
          out.writeLong(actualLifetime);
          out.writeInt(filteredRas);
          out.writeInt(currentRas);
          out.writeInt(programLength);
          out.writeInt(this.flags);
      }
  
      /** @hide */
      @Override
      public int describeContents() {
          return 0;
      }
  
      @Override
      public String toString() {
          String lifetimeString = (lifetime < Long.MAX_VALUE) ? lifetime + "s" : "forever";
          return String.format("ApfProgramEvent(%d/%d RAs %dB %ds/%s %s)", filteredRas, currentRas,
                  programLength, actualLifetime, lifetimeString, namesOf(flags));
      }
  
      @Override
      public boolean equals(Object obj) {
          if (obj == null || !(obj.getClass().equals(ApfProgramEvent.class))) return false;
          final ApfProgramEvent other = (ApfProgramEvent) obj;
          return lifetime == other.lifetime
                  && actualLifetime == other.actualLifetime
                  && filteredRas == other.filteredRas
                  && currentRas == other.currentRas
                  && programLength == other.programLength
                  && flags == other.flags;
      }
  
      /** @hide */
      public static final @android.annotation.NonNull Parcelable.Creator<ApfProgramEvent> CREATOR
              = new Parcelable.Creator<ApfProgramEvent>() {
          public ApfProgramEvent createFromParcel(Parcel in) {
              return new ApfProgramEvent(in);
          }
  
          public ApfProgramEvent[] newArray(int size) {
              return new ApfProgramEvent[size];
          }
      };
  
      /** @hide */
      @UnsupportedAppUsage
      public static @Flags int flagsFor(boolean hasIPv4, boolean multicastFilterOn) {
          int bitfield = 0;
          if (hasIPv4) {
              bitfield |= (1 << FLAG_HAS_IPV4_ADDRESS);
          }
          if (multicastFilterOn) {
              bitfield |= (1 << FLAG_MULTICAST_FILTER_ON);
          }
          return bitfield;
      }
  
      private static String namesOf(@Flags int bitfield) {
          List<String> names = new ArrayList<>(Integer.bitCount(bitfield));
          BitSet set = BitSet.valueOf(new long[]{bitfield & Integer.MAX_VALUE});
          // Only iterate over flag bits which are set.
          for (int bit = set.nextSetBit(0); bit >= 0; bit = set.nextSetBit(bit+1)) {
              names.add(Decoder.constants.get(bit));
          }
          return TextUtils.join("|", names);
      }
  
      final static class Decoder {
          static final SparseArray<String> constants =
                  MessageUtils.findMessageNames(
                         new Class[]{ApfProgramEvent.class}, new String[]{"FLAG_"});
      }
  }
  