Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.server.wifi;
     17 
     18 import android.annotation.Nullable;
     19 import android.os.SystemClock;
     20 
     21 import com.android.internal.annotations.GuardedBy;
     22 import com.android.internal.annotations.VisibleForTesting;
     23 import com.android.server.wifi.nano.WifiMetricsProto.WifiWakeStats;
     24 
     25 import java.io.PrintWriter;
     26 import java.util.ArrayList;
     27 import java.util.List;
     28 
     29 /**
     30  * Holds WifiWake metrics and converts them to a protobuf included in WifiLog.
     31  */
     32 public class WifiWakeMetrics {
     33 
     34     /** Maximum number of sessions to store in WifiWakeStats proto. */
     35     @VisibleForTesting
     36     static final int MAX_RECORDED_SESSIONS = 10;
     37 
     38     @GuardedBy("mLock")
     39     private final List<Session> mSessions = new ArrayList<>();
     40     @GuardedBy("mLock")
     41     private Session mCurrentSession;
     42 
     43     private boolean mIsInSession = false;
     44     private int mTotalSessions = 0;
     45     private int mTotalWakeups = 0;
     46     private int mIgnoredStarts = 0;
     47 
     48     private final Object mLock = new Object();
     49 
     50     /**
     51      * Records the beginning of a Wifi Wake session.
     52      *
     53      * <p>Starts the session.
     54      *
     55      * @param numNetworks The total number of networks stored in the WakeupLock at start.
     56      */
     57     public void recordStartEvent(int numNetworks) {
     58         synchronized (mLock) {
     59             mCurrentSession = new Session(numNetworks, SystemClock.elapsedRealtime());
     60             mIsInSession = true;
     61         }
     62     }
     63 
     64     /**
     65      * Records the initialize event of the current Wifi Wake session.
     66      *
     67      * <p>Note: The start event must be recorded before this event, otherwise this call will be
     68      * ignored.
     69      *
     70      * @param numScans The total number of elapsed scans since start.
     71      * @param numNetworks The total number of networks in the lock.
     72      */
     73     public void recordInitializeEvent(int numScans, int numNetworks) {
     74         synchronized (mLock) {
     75             if (!mIsInSession) {
     76                 return;
     77             }
     78             mCurrentSession.recordInitializeEvent(numScans, numNetworks,
     79                     SystemClock.elapsedRealtime());
     80         }
     81     }
     82 
     83     /**
     84      * Records the unlock event of the current Wifi Wake session.
     85      *
     86      * <p>The unlock event occurs when the WakeupLock has all of its networks removed. This event
     87      * will not be recorded if the initialize event recorded 0 locked networks.
     88      *
     89      * <p>Note: The start event must be recorded before this event, otherwise this call will be
     90      * ignored.
     91      *
     92      * @param numScans The total number of elapsed scans since start.
     93      */
     94     public void recordUnlockEvent(int numScans) {
     95         synchronized (mLock) {
     96             if (!mIsInSession) {
     97                 return;
     98             }
     99             mCurrentSession.recordUnlockEvent(numScans, SystemClock.elapsedRealtime());
    100         }
    101     }
    102 
    103     /**
    104      * Records the wakeup event of the current Wifi Wake session.
    105      *
    106      * <p>The wakeup event occurs when Wifi is re-enabled by the WakeupController.
    107      *
    108      * <p>Note: The start event must be recorded before this event, otherwise this call will be
    109      * ignored.
    110      *
    111      * @param numScans The total number of elapsed scans since start.
    112      */
    113     public void recordWakeupEvent(int numScans) {
    114         synchronized (mLock) {
    115             if (!mIsInSession) {
    116                 return;
    117             }
    118             mCurrentSession.recordWakeupEvent(numScans, SystemClock.elapsedRealtime());
    119         }
    120     }
    121 
    122     /**
    123      * Records the reset event of the current Wifi Wake session.
    124      *
    125      * <p>The reset event occurs when Wifi enters client mode. Stores the first
    126      * {@link #MAX_RECORDED_SESSIONS} in the session list.
    127      *
    128      * <p>Note: The start event must be recorded before this event, otherwise this call will be
    129      * ignored. This event ends the current session.
    130      *
    131      * @param numScans The total number of elapsed scans since start.
    132      */
    133     public void recordResetEvent(int numScans) {
    134         synchronized (mLock) {
    135             if (!mIsInSession) {
    136                 return;
    137             }
    138             mCurrentSession.recordResetEvent(numScans, SystemClock.elapsedRealtime());
    139 
    140             // tally successful wakeups here since this is the actual point when wifi is turned on
    141             if (mCurrentSession.hasWakeupTriggered()) {
    142                 mTotalWakeups++;
    143             }
    144 
    145             mTotalSessions++;
    146             if (mSessions.size() < MAX_RECORDED_SESSIONS) {
    147                 mSessions.add(mCurrentSession);
    148             }
    149             mIsInSession = false;
    150         }
    151     }
    152 
    153     /**
    154      * Records instance of the start event being ignored due to the controller already being active.
    155      */
    156     public void recordIgnoredStart() {
    157         mIgnoredStarts++;
    158     }
    159 
    160     /**
    161      * Returns the consolidated WifiWakeStats proto for WifiMetrics.
    162      */
    163     public WifiWakeStats buildProto() {
    164         WifiWakeStats proto = new WifiWakeStats();
    165 
    166         proto.numSessions = mTotalSessions;
    167         proto.numWakeups = mTotalWakeups;
    168         proto.numIgnoredStarts = mIgnoredStarts;
    169         proto.sessions = new WifiWakeStats.Session[mSessions.size()];
    170 
    171         for (int i = 0; i < mSessions.size(); i++) {
    172             proto.sessions[i] = mSessions.get(i).buildProto();
    173         }
    174 
    175         return proto;
    176     }
    177 
    178     /**
    179      * Dump all WifiWake stats to console (pw)
    180      * @param pw
    181      */
    182     public void dump(PrintWriter pw) {
    183         synchronized (mLock) {
    184             pw.println("-------WifiWake metrics-------");
    185             pw.println("mTotalSessions: " + mTotalSessions);
    186             pw.println("mTotalWakeups: " + mTotalWakeups);
    187             pw.println("mIgnoredStarts: " + mIgnoredStarts);
    188             pw.println("mIsInSession: " + mIsInSession);
    189             pw.println("Stored Sessions: " + mSessions.size());
    190             for (Session session : mSessions) {
    191                 session.dump(pw);
    192             }
    193             if (mCurrentSession != null) {
    194                 pw.println("Current Session: ");
    195                 mCurrentSession.dump(pw);
    196             }
    197             pw.println("----end of WifiWake metrics----");
    198         }
    199     }
    200 
    201     /**
    202      * Clears WifiWakeMetrics.
    203      *
    204      * <p>Keeps the current WifiWake session.
    205      */
    206     public void clear() {
    207         synchronized (mLock) {
    208             mSessions.clear();
    209             mTotalSessions = 0;
    210             mTotalWakeups = 0;
    211             mIgnoredStarts = 0;
    212         }
    213     }
    214 
    215     /** A single WifiWake session. */
    216     public static class Session {
    217 
    218         private final long mStartTimestamp;
    219         private final int mStartNetworks;
    220         private int mInitializeNetworks = 0;
    221 
    222         @VisibleForTesting
    223         @Nullable
    224         Event mUnlockEvent;
    225         @VisibleForTesting
    226         @Nullable
    227         Event mInitEvent;
    228         @VisibleForTesting
    229         @Nullable
    230         Event mWakeupEvent;
    231         @VisibleForTesting
    232         @Nullable
    233         Event mResetEvent;
    234 
    235         /** Creates a new WifiWake session. */
    236         public Session(int numNetworks, long timestamp) {
    237             mStartNetworks = numNetworks;
    238             mStartTimestamp = timestamp;
    239         }
    240 
    241         /**
    242          * Records an initialize event.
    243          *
    244          * <p>Ignores subsequent calls.
    245          *
    246          * @param numScans Total number of scans at the time of this event.
    247          * @param numNetworks Total number of networks in the lock.
    248          * @param timestamp The timestamp of the event.
    249          */
    250         public void recordInitializeEvent(int numScans, int numNetworks, long timestamp) {
    251             if (mInitEvent == null) {
    252                 mInitializeNetworks = numNetworks;
    253                 mInitEvent = new Event(numScans, timestamp - mStartTimestamp);
    254             }
    255         }
    256 
    257         /**
    258          * Records an unlock event.
    259          *
    260          * <p>Ignores subsequent calls.
    261          *
    262          * @param numScans Total number of scans at the time of this event.
    263          * @param timestamp The timestamp of the event.
    264          */
    265         public void recordUnlockEvent(int numScans, long timestamp) {
    266             if (mUnlockEvent == null) {
    267                 mUnlockEvent = new Event(numScans, timestamp - mStartTimestamp);
    268             }
    269         }
    270 
    271         /**
    272          * Records a wakeup event.
    273          *
    274          * <p>Ignores subsequent calls.
    275          *
    276          * @param numScans Total number of scans at the time of this event.
    277          * @param timestamp The timestamp of the event.
    278          */
    279         public void recordWakeupEvent(int numScans, long timestamp) {
    280             if (mWakeupEvent == null) {
    281                 mWakeupEvent = new Event(numScans, timestamp - mStartTimestamp);
    282             }
    283         }
    284 
    285         /**
    286          * Returns whether the current session has had its wakeup event triggered.
    287          */
    288         public boolean hasWakeupTriggered() {
    289             return mWakeupEvent != null;
    290         }
    291 
    292         /**
    293          * Records a reset event.
    294          *
    295          * <p>Ignores subsequent calls.
    296          *
    297          * @param numScans Total number of scans at the time of this event.
    298          * @param timestamp The timestamp of the event.
    299          */
    300         public void recordResetEvent(int numScans, long timestamp) {
    301             if (mResetEvent == null) {
    302                 mResetEvent = new Event(numScans, timestamp - mStartTimestamp);
    303             }
    304         }
    305 
    306         /** Returns the proto representation of this session. */
    307         public WifiWakeStats.Session buildProto() {
    308             WifiWakeStats.Session sessionProto = new WifiWakeStats.Session();
    309             sessionProto.startTimeMillis = mStartTimestamp;
    310             sessionProto.lockedNetworksAtStart = mStartNetworks;
    311 
    312             if (mInitEvent != null) {
    313                 sessionProto.lockedNetworksAtInitialize = mInitializeNetworks;
    314                 sessionProto.initializeEvent = mInitEvent.buildProto();
    315             }
    316             if (mUnlockEvent != null) {
    317                 sessionProto.unlockEvent = mUnlockEvent.buildProto();
    318             }
    319             if (mWakeupEvent != null) {
    320                 sessionProto.wakeupEvent = mWakeupEvent.buildProto();
    321             }
    322             if (mResetEvent != null) {
    323                 sessionProto.resetEvent = mResetEvent.buildProto();
    324             }
    325 
    326             return sessionProto;
    327         }
    328 
    329         /** Dumps the current state of the session. */
    330         public void dump(PrintWriter pw) {
    331             pw.println("WifiWakeMetrics.Session:");
    332             pw.println("mStartTimestamp: " + mStartTimestamp);
    333             pw.println("mStartNetworks: " + mStartNetworks);
    334             pw.println("mInitializeNetworks: " + mInitializeNetworks);
    335             pw.println("mInitEvent: " + (mInitEvent == null ? "{}" : mInitEvent.toString()));
    336             pw.println("mUnlockEvent: " + (mUnlockEvent == null ? "{}" : mUnlockEvent.toString()));
    337             pw.println("mWakeupEvent: " + (mWakeupEvent == null ? "{}" : mWakeupEvent.toString()));
    338             pw.println("mResetEvent: " + (mResetEvent == null ? "{}" : mResetEvent.toString()));
    339         }
    340     }
    341 
    342     /** An event in a WifiWake session. */
    343     public static class Event {
    344 
    345         /** Total number of scans that have elapsed prior to this event. */
    346         public final int mNumScans;
    347         /** Total elapsed time in milliseconds at the instant of this event. */
    348         public final long mElapsedTime;
    349 
    350         public Event(int numScans, long elapsedTime) {
    351             mNumScans = numScans;
    352             mElapsedTime = elapsedTime;
    353         }
    354 
    355         /** Returns the proto representation of this event. */
    356         public WifiWakeStats.Session.Event buildProto() {
    357             WifiWakeStats.Session.Event eventProto = new WifiWakeStats.Session.Event();
    358             eventProto.elapsedScans = mNumScans;
    359             eventProto.elapsedTimeMillis = mElapsedTime;
    360             return eventProto;
    361         }
    362 
    363         @Override
    364         public String toString() {
    365             return "{ mNumScans: " + mNumScans + ", elapsedTime: " + mElapsedTime + " }";
    366         }
    367     }
    368 }
    369