content: /*
   * Copyright (C) 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 android.net.metrics;
  
  import android.net.MacAddress;
  import android.os.Process;
  import android.os.SystemClock;
  import android.util.SparseIntArray;
  
  import java.util.StringJoiner;
  
  /**
   * An event logged per interface and that aggregates WakeupEvents for that interface.
   * {@hide}
   */
  public class WakeupStats {
  
      private static final int NO_UID = -1;
  
      public final long creationTimeMs = SystemClock.elapsedRealtime();
      public final String iface;
  
      public long totalWakeups = 0;
      public long rootWakeups = 0;
      public long systemWakeups = 0;
      public long nonApplicationWakeups = 0;
      public long applicationWakeups = 0;
      public long noUidWakeups = 0;
      public long durationSec = 0;
  
      public long l2UnicastCount = 0;
      public long l2MulticastCount = 0;
      public long l2BroadcastCount = 0;
  
      public final SparseIntArray ethertypes = new SparseIntArray();
      public final SparseIntArray ipNextHeaders = new SparseIntArray();
  
      public WakeupStats(String iface) {
          this.iface = iface;
      }
  
      /** Update durationSec with current time. */
      public void updateDuration() {
          durationSec = (SystemClock.elapsedRealtime() - creationTimeMs) / 1000;
      }
  
      /** Update wakeup counters for the given WakeupEvent. */
      public void countEvent(WakeupEvent ev) {
          totalWakeups++;
          switch (ev.uid) {
              case Process.ROOT_UID:
                  rootWakeups++;
                  break;
              case Process.SYSTEM_UID:
                  systemWakeups++;
                  break;
              case NO_UID:
                  noUidWakeups++;
                  break;
              default:
                  if (ev.uid >= Process.FIRST_APPLICATION_UID) {
                      applicationWakeups++;
                  } else {
                      nonApplicationWakeups++;
                  }
                  break;
          }
  
          switch (ev.dstHwAddr.getAddressType()) {
              case MacAddress.TYPE_UNICAST:
                  l2UnicastCount++;
                  break;
              case MacAddress.TYPE_MULTICAST:
                  l2MulticastCount++;
                  break;
              case MacAddress.TYPE_BROADCAST:
                  l2BroadcastCount++;
                  break;
              default:
                  break;
          }
  
          increment(ethertypes, ev.ethertype);
          if (ev.ipNextHeader >= 0) {
              increment(ipNextHeaders, ev.ipNextHeader);
          }
      }
  
      @Override
      public String toString() {
          updateDuration();
          StringJoiner j = new StringJoiner(", ", "WakeupStats(", ")");
          j.add(iface);
          j.add("" + durationSec + "s");
          j.add("total: " + totalWakeups);
          j.add("root: " + rootWakeups);
          j.add("system: " + systemWakeups);
          j.add("apps: " + applicationWakeups);
          j.add("non-apps: " + nonApplicationWakeups);
          j.add("no uid: " + noUidWakeups);
          j.add(String.format("l2 unicast/multicast/broadcast: %d/%d/%d",
                  l2UnicastCount, l2MulticastCount, l2BroadcastCount));
          for (int i = 0; i < ethertypes.size(); i++) {
              int eth = ethertypes.keyAt(i);
              int count = ethertypes.valueAt(i);
              j.add(String.format("ethertype 0x%x: %d", eth, count));
          }
          for (int i = 0; i < ipNextHeaders.size(); i++) {
              int proto = ipNextHeaders.keyAt(i);
              int count = ipNextHeaders.valueAt(i);
              j.add(String.format("ipNxtHdr %d: %d", proto, count));
          }
          return j.toString();
      }
  
      private static void increment(SparseIntArray counters, int key) {
          int newcount = counters.get(key, 0) + 1;
          counters.put(key, newcount);
      }
  }
  