/* * Copyright (C) 2019 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.p2p; import static android.os.Process.SYSTEM_UID; import android.annotation.Nullable; import android.content.Context; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.util.Log; import com.android.server.wifi.Clock; import com.android.server.wifi.proto.WifiStatsLog; import com.android.server.wifi.proto.nano.WifiMetricsProto.GroupEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.P2pConnectionEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiP2pStats; import com.android.server.wifi.util.StringUtil; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.List; /** * Provides storage for wireless connectivity P2p metrics, as they are generated. * Metrics logged by this class include: * Aggregated connection stats (num of connections, num of failures, ...) * Discrete connection event stats (time, duration, failure codes, ...) */ public class WifiP2pMetrics { private static final String TAG = "WifiP2pMetrics"; private static final boolean DBG = false; private static final int MAX_CONNECTION_EVENTS = 256; private static final int MAX_GROUP_EVENTS = 256; private static final int MIN_2G_FREQUENCY_MHZ = 2412; private static final int MAX_CONNECTION_ATTEMPT_TIME_INTERVAL_MS = 30 * 1000; private Clock mClock; private final Context mContext; private final Object mLock = new Object(); private boolean mIsCountryCodeWorldMode = true; /** * Metrics are stored within an instance of the WifiP2pStats proto during runtime, * The P2pConnectionEvent and GroupEvent metrics are stored during runtime in member * lists of this WifiP2pMetrics class, with the final WifiLog proto being pieced * together at dump-time */ private final WifiP2pStats mWifiP2pStatsProto = new WifiP2pStats(); /** * Connection information that gets logged for every P2P connection attempt. */ private final List mConnectionEventList = new ArrayList<>(); /** * The latest started (but un-ended) connection attempt */ private P2pConnectionEvent mCurrentConnectionEvent; /** * The latest started (but un-ended) connection attempt start time */ private long mCurrentConnectionEventStartTime; private long mLastConnectionEventStartTime; private int mLastConnectionEventUid; private int mLastConnectionTryCount; /** * Group Session information that gets logged for every formed group. */ private final List mGroupEventList = new ArrayList<>(); /** * The latest started (but un-ended) group */ private GroupEvent mCurrentGroupEvent; /** * The latest started (but un-ended) group start time */ private long mCurrentGroupEventStartTime; /** * The latest started (but un-ended) group idle start time. * The group is idle if there is no connected client. */ private long mCurrentGroupEventIdleStartTime; /** * The current number of persistent groups. * This should be persisted after a dump. */ private int mNumPersistentGroup; public WifiP2pMetrics(Clock clock, Context context) { mClock = clock; mContext = context; mNumPersistentGroup = 0; } /** * Clear all WifiP2pMetrics, except for currentConnectionEvent. */ public void clear() { synchronized (mLock) { mConnectionEventList.clear(); if (mCurrentConnectionEvent != null) { mConnectionEventList.add(mCurrentConnectionEvent); } mGroupEventList.clear(); if (mCurrentGroupEvent != null) { mGroupEventList.add(mCurrentGroupEvent); } mWifiP2pStatsProto.clear(); } } /** * Put all metrics that were being tracked separately into mWifiP2pStatsProto */ public WifiP2pStats consolidateProto() { synchronized (mLock) { mWifiP2pStatsProto.numPersistentGroup = mNumPersistentGroup; int connectionEventCount = mConnectionEventList.size(); if (mCurrentConnectionEvent != null) { connectionEventCount--; } mWifiP2pStatsProto.connectionEvent = new P2pConnectionEvent[connectionEventCount]; for (int i = 0; i < connectionEventCount; i++) { mWifiP2pStatsProto.connectionEvent[i] = mConnectionEventList.get(i); } int groupEventCount = mGroupEventList.size(); if (mCurrentGroupEvent != null) { groupEventCount--; } mWifiP2pStatsProto.groupEvent = new GroupEvent[groupEventCount]; for (int i = 0; i < groupEventCount; i++) { mWifiP2pStatsProto.groupEvent[i] = mGroupEventList.get(i); } return mWifiP2pStatsProto; } } /** * Dump all WifiP2pMetrics. Collects some metrics at this time. * * @param pw PrintWriter for writing dump to */ public void dump(PrintWriter pw) { synchronized (mLock) { pw.println("WifiP2pMetrics:"); pw.println("mConnectionEvents:"); for (P2pConnectionEvent event : mConnectionEventList) { StringBuilder sb = new StringBuilder(); Calendar c = Calendar.getInstance(); c.setTimeInMillis(event.startTimeMillis); sb.append("startTime="); if (event.startTimeMillis == 0) { sb.append(" "); } else { sb.append(StringUtil.calendarToString(c)); } sb.append(", connectionType=").append( getconnectionTypeToString(event.connectionType)); sb.append(", wpsMethod="); switch (event.wpsMethod) { case P2pConnectionEvent.WPS_NA: sb.append("NA"); break; case P2pConnectionEvent.WPS_PBC: sb.append("PBC"); break; case P2pConnectionEvent.WPS_DISPLAY: sb.append("DISPLAY"); break; case P2pConnectionEvent.WPS_KEYPAD: sb.append("KEYPAD"); break; case P2pConnectionEvent.WPS_LABEL: sb.append("LABLE"); break; default: sb.append("UNKNOWN"); break; } sb.append(", durationTakenToConnectMillis="); sb.append(event.durationTakenToConnectMillis); sb.append(", groupRole=").append(getGroupRoleToString(event.groupRole)); sb.append(", tryCount="); sb.append(event.tryCount); sb.append(", inviteToNeg="); sb.append(event.fallbackToNegotiationOnInviteStatusInfoUnavailable); sb.append(", isCcWw="); sb.append(event.isCountryCodeWorldMode); sb.append(", band="); sb.append(event.band); sb.append(", freq="); sb.append(event.frequencyMhz); sb.append(", sta freq="); sb.append(event.staFrequencyMhz); sb.append(", uid="); sb.append(event.uid); sb.append(", attributionTag="); sb.append(event.attributionTag); sb.append(", connectivityLevelFailureCode=").append( getConnectivityLevelFailureCodeToString( event.connectivityLevelFailureCode)); if (event == mCurrentConnectionEvent) { sb.append(" CURRENTLY OPEN EVENT"); } pw.println(sb.toString()); } pw.println("mGroupEvents:"); for (GroupEvent event : mGroupEventList) { StringBuilder sb = new StringBuilder(); Calendar c = Calendar.getInstance(); c.setTimeInMillis(event.startTimeMillis); sb.append("netId="); sb.append(event.netId); sb.append(", startTime="); sb.append(event.startTimeMillis == 0 ? " " : String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c)); sb.append(", channelFrequency="); sb.append(event.channelFrequency); sb.append(", groupRole="); switch (event.groupRole) { case GroupEvent.GROUP_CLIENT: sb.append("GroupClient"); break; case GroupEvent.GROUP_OWNER: default: sb.append("GroupOwner"); break; } sb.append(", numConnectedClients="); sb.append(event.numConnectedClients); sb.append(", numCumulativeClients="); sb.append(event.numCumulativeClients); sb.append(", sessionDurationMillis="); sb.append(event.sessionDurationMillis); sb.append(", idleDurationMillis="); sb.append(event.idleDurationMillis); if (event == mCurrentGroupEvent) { sb.append(" CURRENTLY OPEN EVENT"); } pw.println(sb.toString()); } pw.println("mWifiP2pStatsProto.numPersistentGroup=" + mNumPersistentGroup); pw.println("mWifiP2pStatsProto.numTotalPeerScans=" + mWifiP2pStatsProto.numTotalPeerScans); pw.println("mWifiP2pStatsProto.numTotalServiceScans=" + mWifiP2pStatsProto.numTotalServiceScans); } } private String getconnectionTypeToString(int connectionType) { switch (connectionType) { case P2pConnectionEvent.CONNECTION_FRESH: return "FRESH"; case P2pConnectionEvent.CONNECTION_REINVOKE: return "REINVOKE"; case P2pConnectionEvent.CONNECTION_LOCAL: return "LOCAL"; case P2pConnectionEvent.CONNECTION_FAST: return "FAST"; default: return "UNKNOWN"; } } private String getGroupRoleToString(int groupRole) { switch (groupRole) { case GroupEvent.GROUP_OWNER: return "OWNER"; case GroupEvent.GROUP_CLIENT: return "CLIENT"; default: return "UNKNOWN DURING CONNECT"; } } private String getConnectivityLevelFailureCodeToString(int connectivityLevelFailureCode) { switch (connectivityLevelFailureCode) { case P2pConnectionEvent.CLF_NONE: return "NONE"; case P2pConnectionEvent.CLF_TIMEOUT: return "TIMEOUT"; case P2pConnectionEvent.CLF_CANCEL: return "CANCEL"; case P2pConnectionEvent.CLF_PROV_DISC_FAIL: return "PROV_DISC_FAIL"; case P2pConnectionEvent.CLF_INVITATION_FAIL: return "INVITATION_FAIL"; case P2pConnectionEvent.CLF_USER_REJECT: return "USER_REJECT"; case P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT: return "NEW_CONNECTION_ATTEMPT"; case P2pConnectionEvent.CLF_GROUP_REMOVED: return "GROUP_REMOVED"; case P2pConnectionEvent.CLF_CREATE_GROUP_FAILED: return "CREATE_GROUP_FAILED"; case P2pConnectionEvent.CLF_UNKNOWN: default: return "UNKNOWN"; } } /** Increment total number of peer scans */ public void incrementPeerScans() { synchronized (mLock) { mWifiP2pStatsProto.numTotalPeerScans++; } } /** Increment total number of service scans */ public void incrementServiceScans() { synchronized (mLock) { mWifiP2pStatsProto.numTotalServiceScans++; } } /** Set the number of saved persistent group */ public void updatePersistentGroup(WifiP2pGroupList groups) { synchronized (mLock) { final Collection list = groups.getGroupList(); mNumPersistentGroup = list.size(); } } /** Returns if current connection event type is FAST connection */ public boolean isP2pFastConnectionType() { if (mCurrentConnectionEvent == null) { return false; } return P2pConnectionEvent.CONNECTION_FAST == mCurrentConnectionEvent.connectionType; } /** Gets current connection event group role string */ public String getP2pGroupRoleString() { if (mCurrentConnectionEvent == null) { return "UNKNOWN"; } return (GroupEvent.GROUP_OWNER == mCurrentConnectionEvent.groupRole) ? "GO" : "GC"; } /** * Create a new connection event. Call when p2p attempts to make a new connection to * another peer. If there is a current 'un-ended' connection event, it will be ended with * P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT. * * @param connectionType indicate this connection is fresh or reinvoke. * @param config configuration used for this connection. * @param groupRole groupRole used for this connection. * @param uid uid of caller app * @param attributionTag attributionTag of caller app */ public void startConnectionEvent(int connectionType, WifiP2pConfig config, int groupRole, int uid, @Nullable String attributionTag) { synchronized (mLock) { if (attributionTag == null) { attributionTag = ""; } StringBuilder stringBuilder = new StringBuilder("Start connection event"); if (mCurrentConnectionEvent == null) { stringBuilder.append(", mCurrentConnectionEvent:null"); } else { stringBuilder.append(", curConnectionType:") .append(getconnectionTypeToString(mCurrentConnectionEvent.connectionType)) .append(", curGroupRole:") .append(getGroupRoleToString(mCurrentConnectionEvent.groupRole)) .append(", curUid:").append(mCurrentConnectionEvent.uid) .append(", attributionTag:").append(mCurrentConnectionEvent.attributionTag) .append(", curConnectivityLevelFailureCode:") .append(getConnectivityLevelFailureCodeToString( mCurrentConnectionEvent.connectivityLevelFailureCode)); } stringBuilder.append(", startConnectionType:") .append(getconnectionTypeToString(connectionType)) .append(", startGroupRole:").append(getGroupRoleToString(groupRole)) .append(", startUid:").append(uid) .append(", startAttributionTag:").append(attributionTag); Log.d(TAG, stringBuilder.toString()); // handle overlapping connection event first. if (mCurrentConnectionEvent != null) { endConnectionEvent(P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT); } while (mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) { mConnectionEventList.remove(0); } mCurrentConnectionEventStartTime = mClock.getElapsedSinceBootMillis(); mCurrentConnectionEvent = new P2pConnectionEvent(); mCurrentConnectionEvent.startTimeMillis = mClock.getWallClockMillis(); mCurrentConnectionEvent.connectionType = connectionType; mCurrentConnectionEvent.groupRole = groupRole; if (config != null) { mCurrentConnectionEvent.wpsMethod = config.wps.setup; mCurrentConnectionEvent.band = convertGroupOwnerBand(config.groupOwnerBand); mCurrentConnectionEvent.frequencyMhz = (config.groupOwnerBand < MIN_2G_FREQUENCY_MHZ) ? 0 : config.groupOwnerBand; } mCurrentConnectionEvent.staFrequencyMhz = getWifiStaFrequency(); mCurrentConnectionEvent.uid = uid; mCurrentConnectionEvent.attributionTag = attributionTag; if (mLastConnectionEventUid == uid && mCurrentConnectionEventStartTime < ( mLastConnectionEventStartTime + MAX_CONNECTION_ATTEMPT_TIME_INTERVAL_MS)) { mLastConnectionTryCount += 1; } else { mLastConnectionTryCount = 1; } mLastConnectionEventUid = uid; mLastConnectionEventStartTime = mCurrentConnectionEventStartTime; mCurrentConnectionEvent.tryCount = mLastConnectionTryCount; mConnectionEventList.add(mCurrentConnectionEvent); } } /** Returns if there is an ongoing connection */ public boolean hasOngoingConnection() { return mCurrentConnectionEvent != null; } /** * End a Connection event record. Call when p2p connection attempt succeeds or fails. * If a Connection event has not been started when .end is called, * a new one is created with zero duration. * * @param failure indicate the failure with WifiMetricsProto.P2pConnectionEvent.CLF_X. */ public void endConnectionEvent(int failure) { synchronized (mLock) { StringBuilder stringBuilder = new StringBuilder("End connection event"); if (mCurrentConnectionEvent == null) { stringBuilder.append(", mCurrentConnectionEvent:null"); } else { stringBuilder.append(", curConnectionType:") .append(getconnectionTypeToString(mCurrentConnectionEvent.connectionType)) .append(", curGroupRole:") .append(getGroupRoleToString(mCurrentConnectionEvent.groupRole)) .append(", curUid:") .append(mCurrentConnectionEvent.uid) .append(", attributionTag:").append(mCurrentConnectionEvent.attributionTag) .append(", curConnectivityLevelFailureCode:") .append(getConnectivityLevelFailureCodeToString( mCurrentConnectionEvent.connectivityLevelFailureCode)); } stringBuilder.append(", endConnectivityLevelFailureCode:") .append(getConnectivityLevelFailureCodeToString(failure)); Log.d(TAG, stringBuilder.toString()); if (mCurrentConnectionEvent == null) { // Reinvoking a group with invitation will be handled in supplicant. // There won't be a connection starting event in framework. // The framework only gets the connection ending event in GroupStarted state. startConnectionEvent(P2pConnectionEvent.CONNECTION_REINVOKE, null, GroupEvent.GROUP_UNKNOWN, SYSTEM_UID, null); } mCurrentConnectionEvent.durationTakenToConnectMillis = (int) (mClock.getElapsedSinceBootMillis() - mCurrentConnectionEventStartTime); mCurrentConnectionEvent.connectivityLevelFailureCode = failure; mCurrentConnectionEvent.isCountryCodeWorldMode = mIsCountryCodeWorldMode; WifiStatsLog.write(WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED, convertConnectionType(mCurrentConnectionEvent.connectionType), mCurrentConnectionEvent.durationTakenToConnectMillis, mCurrentConnectionEvent.durationTakenToConnectMillis / 200, convertFailureCode(failure), convertGroupRole(mCurrentConnectionEvent.groupRole), convertBandStatsLog(mCurrentConnectionEvent.band), mCurrentConnectionEvent.frequencyMhz, mCurrentConnectionEvent.staFrequencyMhz, mCurrentConnectionEvent.uid, mIsCountryCodeWorldMode, mCurrentConnectionEvent.fallbackToNegotiationOnInviteStatusInfoUnavailable, mCurrentConnectionEvent.tryCount, mCurrentConnectionEvent.attributionTag); mCurrentConnectionEvent = null; if (P2pConnectionEvent.CLF_NONE == failure) { mLastConnectionTryCount = 0; } } } /** * Fallback to GO negotiation if device receives invitation response status code - * information is currently unavailable */ public void setFallbackToNegotiationOnInviteStatusInfoUnavailable() { if (mCurrentConnectionEvent == null) { return; } mCurrentConnectionEvent.fallbackToNegotiationOnInviteStatusInfoUnavailable = true; } /** Sets if the Country Code is in world mode */ public void setIsCountryCodeWorldMode(boolean isCountryCodeWorldMode) { mIsCountryCodeWorldMode = isCountryCodeWorldMode; } private int convertConnectionType(int connectionType) { switch (connectionType) { case P2pConnectionEvent.CONNECTION_FRESH: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__FRESH; case P2pConnectionEvent.CONNECTION_REINVOKE: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__REINVOKE; case P2pConnectionEvent.CONNECTION_LOCAL: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__LOCAL; case P2pConnectionEvent.CONNECTION_FAST: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__FAST; default: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__UNSPECIFIED; } } private int convertFailureCode(int failureCode) { switch (failureCode) { case P2pConnectionEvent.CLF_NONE: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__NONE; case P2pConnectionEvent.CLF_TIMEOUT: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__TIMEOUT; case P2pConnectionEvent.CLF_CANCEL: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__CANCEL; case P2pConnectionEvent.CLF_PROV_DISC_FAIL: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__PROV_DISC_FAIL; case P2pConnectionEvent.CLF_INVITATION_FAIL: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__INVITATION_FAIL; case P2pConnectionEvent.CLF_USER_REJECT: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__USER_REJECT; case P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT: return WifiStatsLog .WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__NEW_CONNECTION_ATTEMPT; case P2pConnectionEvent.CLF_GROUP_REMOVED: return WifiStatsLog .WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__GROUP_REMOVED; case P2pConnectionEvent.CLF_CREATE_GROUP_FAILED: return WifiStatsLog .WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__CREATE_GROUP_FAILED; case P2pConnectionEvent.CLF_UNKNOWN: default: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__UNKNOWN; } } private int convertGroupRole(int groupRole) { switch (groupRole) { case GroupEvent.GROUP_OWNER: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__GROUP_ROLE__GROUP_OWNER; case GroupEvent.GROUP_CLIENT: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__GROUP_ROLE__GROUP_CLIENT; default: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__GROUP_ROLE__GROUP_UNKNOWN; } } private int convertGroupOwnerBand(int bandOrFrequency) { if (bandOrFrequency >= MIN_2G_FREQUENCY_MHZ) { return P2pConnectionEvent.BAND_FREQUENCY; } else { switch (bandOrFrequency) { case WifiP2pConfig.GROUP_OWNER_BAND_AUTO: return P2pConnectionEvent.BAND_AUTO; case WifiP2pConfig.GROUP_OWNER_BAND_2GHZ: return P2pConnectionEvent.BAND_2G; case WifiP2pConfig.GROUP_OWNER_BAND_5GHZ: return P2pConnectionEvent.BAND_5G; default: return P2pConnectionEvent.BAND_UNKNOWN; } } } private int convertBandStatsLog(int band) { switch (band) { case P2pConnectionEvent.BAND_AUTO: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_AUTO; case P2pConnectionEvent.BAND_2G: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_2G; case P2pConnectionEvent.BAND_5G: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_5G; case P2pConnectionEvent.BAND_6G: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_6G; case P2pConnectionEvent.BAND_FREQUENCY: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_FREQUENCY; default: return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_UNKNOWN; } } private int getWifiStaFrequency() { WifiManager wifiManager = mContext.getSystemService(WifiManager.class); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if (wifiInfo.getFrequency() > 0) { return wifiInfo.getFrequency(); } else { return 0; } } /** * Create a new group event. * * @param group the information of started group. */ public void startGroupEvent(WifiP2pGroup group) { if (group == null) { if (DBG) Log.d(TAG, "Cannot start group event due to null group"); return; } synchronized (mLock) { // handle overlapping group event first. if (mCurrentGroupEvent != null) { if (DBG) Log.d(TAG, "Overlapping group event!"); endGroupEvent(); } while (mGroupEventList.size() >= MAX_GROUP_EVENTS) { mGroupEventList.remove(0); } mCurrentGroupEventStartTime = mClock.getElapsedSinceBootMillis(); if (group.getClientList().size() == 0) { mCurrentGroupEventIdleStartTime = mClock.getElapsedSinceBootMillis(); } else { mCurrentGroupEventIdleStartTime = 0; } mCurrentGroupEvent = new GroupEvent(); mCurrentGroupEvent.netId = group.getNetworkId(); mCurrentGroupEvent.startTimeMillis = mClock.getWallClockMillis(); mCurrentGroupEvent.numConnectedClients = group.getClientList().size(); mCurrentGroupEvent.channelFrequency = group.getFrequency(); mCurrentGroupEvent.groupRole = group.isGroupOwner() ? GroupEvent.GROUP_OWNER : GroupEvent.GROUP_CLIENT; mGroupEventList.add(mCurrentGroupEvent); } } /** * Update the information of started group. */ public void updateGroupEvent(WifiP2pGroup group) { if (group == null) { if (DBG) Log.d(TAG, "Cannot update group event due to null group."); return; } synchronized (mLock) { if (mCurrentGroupEvent == null) { Log.w(TAG, "Cannot update group event due to no current group."); return; } if (mCurrentGroupEvent.netId != group.getNetworkId()) { Log.w(TAG, "Updating group id " + group.getNetworkId() + " is different from current group id " + mCurrentGroupEvent.netId + "."); return; } int delta = group.getClientList().size() - mCurrentGroupEvent.numConnectedClients; mCurrentGroupEvent.numConnectedClients = group.getClientList().size(); if (delta > 0) { mCurrentGroupEvent.numCumulativeClients += delta; } // if new client comes during idle period, cumulate idle duration and reset idle timer. // if the last client disconnected during non-idle period, start idle timer. if (mCurrentGroupEventIdleStartTime > 0) { if (group.getClientList().size() > 0) { mCurrentGroupEvent.idleDurationMillis += (mClock.getElapsedSinceBootMillis() - mCurrentGroupEventIdleStartTime); mCurrentGroupEventIdleStartTime = 0; } } else { if (group.getClientList().size() == 0) { mCurrentGroupEventIdleStartTime = mClock.getElapsedSinceBootMillis(); } } } } /** * End a group event. */ public void endGroupEvent() { synchronized (mLock) { if (mCurrentGroupEvent != null) { mCurrentGroupEvent.sessionDurationMillis = (int) (mClock.getElapsedSinceBootMillis() - mCurrentGroupEventStartTime); if (mCurrentGroupEventIdleStartTime > 0) { mCurrentGroupEvent.idleDurationMillis += (mClock.getElapsedSinceBootMillis() - mCurrentGroupEventIdleStartTime); mCurrentGroupEventIdleStartTime = 0; } } else { Log.e(TAG, "No current group!"); } mCurrentGroupEvent = null; } } /* Log Metrics */ }