| /* |
| * 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 com.android.server.wifi; |
| |
| import android.annotation.Nullable; |
| import android.os.SystemClock; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.server.wifi.nano.WifiMetricsProto.WifiWakeStats; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Holds WifiWake metrics and converts them to a protobuf included in WifiLog. |
| */ |
| public class WifiWakeMetrics { |
| |
| /** Maximum number of sessions to store in WifiWakeStats proto. */ |
| @VisibleForTesting |
| static final int MAX_RECORDED_SESSIONS = 10; |
| |
| @GuardedBy("mLock") |
| private final List<Session> mSessions = new ArrayList<>(); |
| @GuardedBy("mLock") |
| private Session mCurrentSession; |
| |
| private boolean mIsInSession = false; |
| private int mTotalSessions = 0; |
| private int mTotalWakeups = 0; |
| private int mIgnoredStarts = 0; |
| |
| private final Object mLock = new Object(); |
| |
| /** |
| * Records the beginning of a Wifi Wake session. |
| * |
| * <p>Starts the session. |
| * |
| * @param numNetworks The total number of networks stored in the WakeupLock at start. |
| */ |
| public void recordStartEvent(int numNetworks) { |
| synchronized (mLock) { |
| mCurrentSession = new Session(numNetworks, SystemClock.elapsedRealtime()); |
| mIsInSession = true; |
| } |
| } |
| |
| /** |
| * Records the initialize event of the current Wifi Wake session. |
| * |
| * <p>Note: The start event must be recorded before this event, otherwise this call will be |
| * ignored. |
| * |
| * @param numScans The total number of elapsed scans since start. |
| * @param numNetworks The total number of networks in the lock. |
| */ |
| public void recordInitializeEvent(int numScans, int numNetworks) { |
| synchronized (mLock) { |
| if (!mIsInSession) { |
| return; |
| } |
| mCurrentSession.recordInitializeEvent(numScans, numNetworks, |
| SystemClock.elapsedRealtime()); |
| } |
| } |
| |
| /** |
| * Records the unlock event of the current Wifi Wake session. |
| * |
| * <p>The unlock event occurs when the WakeupLock has all of its networks removed. This event |
| * will not be recorded if the initialize event recorded 0 locked networks. |
| * |
| * <p>Note: The start event must be recorded before this event, otherwise this call will be |
| * ignored. |
| * |
| * @param numScans The total number of elapsed scans since start. |
| */ |
| public void recordUnlockEvent(int numScans) { |
| synchronized (mLock) { |
| if (!mIsInSession) { |
| return; |
| } |
| mCurrentSession.recordUnlockEvent(numScans, SystemClock.elapsedRealtime()); |
| } |
| } |
| |
| /** |
| * Records the wakeup event of the current Wifi Wake session. |
| * |
| * <p>The wakeup event occurs when Wifi is re-enabled by the WakeupController. |
| * |
| * <p>Note: The start event must be recorded before this event, otherwise this call will be |
| * ignored. |
| * |
| * @param numScans The total number of elapsed scans since start. |
| */ |
| public void recordWakeupEvent(int numScans) { |
| synchronized (mLock) { |
| if (!mIsInSession) { |
| return; |
| } |
| mCurrentSession.recordWakeupEvent(numScans, SystemClock.elapsedRealtime()); |
| } |
| } |
| |
| /** |
| * Records the reset event of the current Wifi Wake session. |
| * |
| * <p>The reset event occurs when Wifi enters client mode. Stores the first |
| * {@link #MAX_RECORDED_SESSIONS} in the session list. |
| * |
| * <p>Note: The start event must be recorded before this event, otherwise this call will be |
| * ignored. This event ends the current session. |
| * |
| * @param numScans The total number of elapsed scans since start. |
| */ |
| public void recordResetEvent(int numScans) { |
| synchronized (mLock) { |
| if (!mIsInSession) { |
| return; |
| } |
| mCurrentSession.recordResetEvent(numScans, SystemClock.elapsedRealtime()); |
| |
| // tally successful wakeups here since this is the actual point when wifi is turned on |
| if (mCurrentSession.hasWakeupTriggered()) { |
| mTotalWakeups++; |
| } |
| |
| mTotalSessions++; |
| if (mSessions.size() < MAX_RECORDED_SESSIONS) { |
| mSessions.add(mCurrentSession); |
| } |
| mIsInSession = false; |
| } |
| } |
| |
| /** |
| * Records instance of the start event being ignored due to the controller already being active. |
| */ |
| public void recordIgnoredStart() { |
| mIgnoredStarts++; |
| } |
| |
| /** |
| * Returns the consolidated WifiWakeStats proto for WifiMetrics. |
| */ |
| public WifiWakeStats buildProto() { |
| WifiWakeStats proto = new WifiWakeStats(); |
| |
| proto.numSessions = mTotalSessions; |
| proto.numWakeups = mTotalWakeups; |
| proto.numIgnoredStarts = mIgnoredStarts; |
| proto.sessions = new WifiWakeStats.Session[mSessions.size()]; |
| |
| for (int i = 0; i < mSessions.size(); i++) { |
| proto.sessions[i] = mSessions.get(i).buildProto(); |
| } |
| |
| return proto; |
| } |
| |
| /** |
| * Dump all WifiWake stats to console (pw) |
| * @param pw |
| */ |
| public void dump(PrintWriter pw) { |
| synchronized (mLock) { |
| pw.println("-------WifiWake metrics-------"); |
| pw.println("mTotalSessions: " + mTotalSessions); |
| pw.println("mTotalWakeups: " + mTotalWakeups); |
| pw.println("mIgnoredStarts: " + mIgnoredStarts); |
| pw.println("mIsInSession: " + mIsInSession); |
| pw.println("Stored Sessions: " + mSessions.size()); |
| for (Session session : mSessions) { |
| session.dump(pw); |
| } |
| if (mCurrentSession != null) { |
| pw.println("Current Session: "); |
| mCurrentSession.dump(pw); |
| } |
| pw.println("----end of WifiWake metrics----"); |
| } |
| } |
| |
| /** |
| * Clears WifiWakeMetrics. |
| * |
| * <p>Keeps the current WifiWake session. |
| */ |
| public void clear() { |
| synchronized (mLock) { |
| mSessions.clear(); |
| mTotalSessions = 0; |
| mTotalWakeups = 0; |
| mIgnoredStarts = 0; |
| } |
| } |
| |
| /** A single WifiWake session. */ |
| public static class Session { |
| |
| private final long mStartTimestamp; |
| private final int mStartNetworks; |
| private int mInitializeNetworks = 0; |
| |
| @VisibleForTesting |
| @Nullable |
| Event mUnlockEvent; |
| @VisibleForTesting |
| @Nullable |
| Event mInitEvent; |
| @VisibleForTesting |
| @Nullable |
| Event mWakeupEvent; |
| @VisibleForTesting |
| @Nullable |
| Event mResetEvent; |
| |
| /** Creates a new WifiWake session. */ |
| public Session(int numNetworks, long timestamp) { |
| mStartNetworks = numNetworks; |
| mStartTimestamp = timestamp; |
| } |
| |
| /** |
| * Records an initialize event. |
| * |
| * <p>Ignores subsequent calls. |
| * |
| * @param numScans Total number of scans at the time of this event. |
| * @param numNetworks Total number of networks in the lock. |
| * @param timestamp The timestamp of the event. |
| */ |
| public void recordInitializeEvent(int numScans, int numNetworks, long timestamp) { |
| if (mInitEvent == null) { |
| mInitializeNetworks = numNetworks; |
| mInitEvent = new Event(numScans, timestamp - mStartTimestamp); |
| } |
| } |
| |
| /** |
| * Records an unlock event. |
| * |
| * <p>Ignores subsequent calls. |
| * |
| * @param numScans Total number of scans at the time of this event. |
| * @param timestamp The timestamp of the event. |
| */ |
| public void recordUnlockEvent(int numScans, long timestamp) { |
| if (mUnlockEvent == null) { |
| mUnlockEvent = new Event(numScans, timestamp - mStartTimestamp); |
| } |
| } |
| |
| /** |
| * Records a wakeup event. |
| * |
| * <p>Ignores subsequent calls. |
| * |
| * @param numScans Total number of scans at the time of this event. |
| * @param timestamp The timestamp of the event. |
| */ |
| public void recordWakeupEvent(int numScans, long timestamp) { |
| if (mWakeupEvent == null) { |
| mWakeupEvent = new Event(numScans, timestamp - mStartTimestamp); |
| } |
| } |
| |
| /** |
| * Returns whether the current session has had its wakeup event triggered. |
| */ |
| public boolean hasWakeupTriggered() { |
| return mWakeupEvent != null; |
| } |
| |
| /** |
| * Records a reset event. |
| * |
| * <p>Ignores subsequent calls. |
| * |
| * @param numScans Total number of scans at the time of this event. |
| * @param timestamp The timestamp of the event. |
| */ |
| public void recordResetEvent(int numScans, long timestamp) { |
| if (mResetEvent == null) { |
| mResetEvent = new Event(numScans, timestamp - mStartTimestamp); |
| } |
| } |
| |
| /** Returns the proto representation of this session. */ |
| public WifiWakeStats.Session buildProto() { |
| WifiWakeStats.Session sessionProto = new WifiWakeStats.Session(); |
| sessionProto.startTimeMillis = mStartTimestamp; |
| sessionProto.lockedNetworksAtStart = mStartNetworks; |
| |
| if (mInitEvent != null) { |
| sessionProto.lockedNetworksAtInitialize = mInitializeNetworks; |
| sessionProto.initializeEvent = mInitEvent.buildProto(); |
| } |
| if (mUnlockEvent != null) { |
| sessionProto.unlockEvent = mUnlockEvent.buildProto(); |
| } |
| if (mWakeupEvent != null) { |
| sessionProto.wakeupEvent = mWakeupEvent.buildProto(); |
| } |
| if (mResetEvent != null) { |
| sessionProto.resetEvent = mResetEvent.buildProto(); |
| } |
| |
| return sessionProto; |
| } |
| |
| /** Dumps the current state of the session. */ |
| public void dump(PrintWriter pw) { |
| pw.println("WifiWakeMetrics.Session:"); |
| pw.println("mStartTimestamp: " + mStartTimestamp); |
| pw.println("mStartNetworks: " + mStartNetworks); |
| pw.println("mInitializeNetworks: " + mInitializeNetworks); |
| pw.println("mInitEvent: " + (mInitEvent == null ? "{}" : mInitEvent.toString())); |
| pw.println("mUnlockEvent: " + (mUnlockEvent == null ? "{}" : mUnlockEvent.toString())); |
| pw.println("mWakeupEvent: " + (mWakeupEvent == null ? "{}" : mWakeupEvent.toString())); |
| pw.println("mResetEvent: " + (mResetEvent == null ? "{}" : mResetEvent.toString())); |
| } |
| } |
| |
| /** An event in a WifiWake session. */ |
| public static class Event { |
| |
| /** Total number of scans that have elapsed prior to this event. */ |
| public final int mNumScans; |
| /** Total elapsed time in milliseconds at the instant of this event. */ |
| public final long mElapsedTime; |
| |
| public Event(int numScans, long elapsedTime) { |
| mNumScans = numScans; |
| mElapsedTime = elapsedTime; |
| } |
| |
| /** Returns the proto representation of this event. */ |
| public WifiWakeStats.Session.Event buildProto() { |
| WifiWakeStats.Session.Event eventProto = new WifiWakeStats.Session.Event(); |
| eventProto.elapsedScans = mNumScans; |
| eventProto.elapsedTimeMillis = mElapsedTime; |
| return eventProto; |
| } |
| |
| @Override |
| public String toString() { |
| return "{ mNumScans: " + mNumScans + ", elapsedTime: " + mElapsedTime + " }"; |
| } |
| } |
| } |