1 /*
2  * Copyright (C) 2019 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 
17 package com.android.server.wifi.p2p;
18 
19 import static android.os.Process.SYSTEM_UID;
20 
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.net.wifi.WifiInfo;
24 import android.net.wifi.WifiManager;
25 import android.net.wifi.p2p.WifiP2pConfig;
26 import android.net.wifi.p2p.WifiP2pGroup;
27 import android.net.wifi.p2p.WifiP2pGroupList;
28 import android.util.Log;
29 
30 import com.android.server.wifi.Clock;
31 import com.android.server.wifi.proto.WifiStatsLog;
32 import com.android.server.wifi.proto.nano.WifiMetricsProto.GroupEvent;
33 import com.android.server.wifi.proto.nano.WifiMetricsProto.P2pConnectionEvent;
34 import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiP2pStats;
35 import com.android.server.wifi.util.StringUtil;
36 
37 import java.io.PrintWriter;
38 import java.util.ArrayList;
39 import java.util.Calendar;
40 import java.util.Collection;
41 import java.util.List;
42 
43 /**
44  * Provides storage for wireless connectivity P2p metrics, as they are generated.
45  * Metrics logged by this class include:
46  *   Aggregated connection stats (num of connections, num of failures, ...)
47  *   Discrete connection event stats (time, duration, failure codes, ...)
48  */
49 public class WifiP2pMetrics {
50     private static final String TAG = "WifiP2pMetrics";
51     private static final boolean DBG = false;
52 
53     private static final int MAX_CONNECTION_EVENTS = 256;
54     private static final int MAX_GROUP_EVENTS = 256;
55     private static final int MIN_2G_FREQUENCY_MHZ = 2412;
56 
57     private static final int MAX_CONNECTION_ATTEMPT_TIME_INTERVAL_MS = 30 * 1000;
58 
59     private Clock mClock;
60     private final Context mContext;
61     private final Object mLock = new Object();
62     private boolean mIsCountryCodeWorldMode = true;
63 
64     /**
65      * Metrics are stored within an instance of the WifiP2pStats proto during runtime,
66      * The P2pConnectionEvent and GroupEvent metrics are stored during runtime in member
67      * lists of this WifiP2pMetrics class, with the final WifiLog proto being pieced
68      * together at dump-time
69      */
70     private final WifiP2pStats mWifiP2pStatsProto =
71             new WifiP2pStats();
72 
73     /**
74      * Connection information that gets logged for every P2P connection attempt.
75      */
76     private final List<P2pConnectionEvent> mConnectionEventList =
77             new ArrayList<>();
78 
79     /**
80      * The latest started (but un-ended) connection attempt
81      */
82     private P2pConnectionEvent mCurrentConnectionEvent;
83 
84     /**
85      * The latest started (but un-ended) connection attempt start time
86      */
87     private long mCurrentConnectionEventStartTime;
88 
89     private long mLastConnectionEventStartTime;
90 
91     private int mLastConnectionEventUid;
92 
93     private int mLastConnectionTryCount;
94 
95     /**
96      * Group Session information that gets logged for every formed group.
97      */
98     private final List<GroupEvent> mGroupEventList =
99             new ArrayList<>();
100 
101     /**
102      * The latest started (but un-ended) group
103      */
104     private GroupEvent mCurrentGroupEvent;
105 
106     /**
107      * The latest started (but un-ended) group start time
108      */
109     private long mCurrentGroupEventStartTime;
110 
111     /**
112      * The latest started (but un-ended) group idle start time.
113      * The group is idle if there is no connected client.
114      */
115     private long mCurrentGroupEventIdleStartTime;
116 
117     /**
118      * The current number of persistent groups.
119      * This should be persisted after a dump.
120      */
121     private int mNumPersistentGroup;
122 
WifiP2pMetrics(Clock clock, Context context)123     public WifiP2pMetrics(Clock clock, Context context) {
124         mClock = clock;
125         mContext = context;
126         mNumPersistentGroup = 0;
127     }
128 
129     /**
130      * Clear all WifiP2pMetrics, except for currentConnectionEvent.
131      */
clear()132     public void clear() {
133         synchronized (mLock) {
134             mConnectionEventList.clear();
135             if (mCurrentConnectionEvent != null) {
136                 mConnectionEventList.add(mCurrentConnectionEvent);
137             }
138             mGroupEventList.clear();
139             if (mCurrentGroupEvent != null) {
140                 mGroupEventList.add(mCurrentGroupEvent);
141             }
142             mWifiP2pStatsProto.clear();
143         }
144     }
145 
146     /**
147      * Put all metrics that were being tracked separately into mWifiP2pStatsProto
148      */
consolidateProto()149     public WifiP2pStats consolidateProto() {
150         synchronized (mLock) {
151             mWifiP2pStatsProto.numPersistentGroup = mNumPersistentGroup;
152             int connectionEventCount = mConnectionEventList.size();
153             if (mCurrentConnectionEvent != null) {
154                 connectionEventCount--;
155             }
156             mWifiP2pStatsProto.connectionEvent =
157                     new P2pConnectionEvent[connectionEventCount];
158             for (int i = 0; i < connectionEventCount; i++) {
159                 mWifiP2pStatsProto.connectionEvent[i] = mConnectionEventList.get(i);
160             }
161 
162             int groupEventCount = mGroupEventList.size();
163             if (mCurrentGroupEvent != null) {
164                 groupEventCount--;
165             }
166             mWifiP2pStatsProto.groupEvent =
167                     new GroupEvent[groupEventCount];
168             for (int i = 0; i < groupEventCount; i++) {
169                 mWifiP2pStatsProto.groupEvent[i] = mGroupEventList.get(i);
170             }
171             return mWifiP2pStatsProto;
172         }
173     }
174 
175     /**
176      * Dump all WifiP2pMetrics. Collects some metrics at this time.
177      *
178      * @param pw PrintWriter for writing dump to
179      */
dump(PrintWriter pw)180     public void dump(PrintWriter pw) {
181         synchronized (mLock) {
182             pw.println("WifiP2pMetrics:");
183             pw.println("mConnectionEvents:");
184             for (P2pConnectionEvent event : mConnectionEventList) {
185                 StringBuilder sb = new StringBuilder();
186                 Calendar c = Calendar.getInstance();
187                 c.setTimeInMillis(event.startTimeMillis);
188                 sb.append("startTime=");
189                 if (event.startTimeMillis == 0) {
190                     sb.append("            <null>");
191                 } else {
192                     sb.append(StringUtil.calendarToString(c));
193                 }
194                 sb.append(", connectionType=").append(
195                         getconnectionTypeToString(event.connectionType));
196                 sb.append(", wpsMethod=");
197                 switch (event.wpsMethod) {
198                     case P2pConnectionEvent.WPS_NA:
199                         sb.append("NA");
200                         break;
201                     case P2pConnectionEvent.WPS_PBC:
202                         sb.append("PBC");
203                         break;
204                     case P2pConnectionEvent.WPS_DISPLAY:
205                         sb.append("DISPLAY");
206                         break;
207                     case P2pConnectionEvent.WPS_KEYPAD:
208                         sb.append("KEYPAD");
209                         break;
210                     case P2pConnectionEvent.WPS_LABEL:
211                         sb.append("LABLE");
212                         break;
213                     default:
214                         sb.append("UNKNOWN");
215                         break;
216                 }
217                 sb.append(", durationTakenToConnectMillis=");
218                 sb.append(event.durationTakenToConnectMillis);
219                 sb.append(", groupRole=").append(getGroupRoleToString(event.groupRole));
220                 sb.append(", tryCount=");
221                 sb.append(event.tryCount);
222                 sb.append(", inviteToNeg=");
223                 sb.append(event.fallbackToNegotiationOnInviteStatusInfoUnavailable);
224                 sb.append(", isCcWw=");
225                 sb.append(event.isCountryCodeWorldMode);
226                 sb.append(", band=");
227                 sb.append(event.band);
228                 sb.append(", freq=");
229                 sb.append(event.frequencyMhz);
230                 sb.append(", sta freq=");
231                 sb.append(event.staFrequencyMhz);
232                 sb.append(", uid=");
233                 sb.append(event.uid);
234                 sb.append(", attributionTag=");
235                 sb.append(event.attributionTag);
236                 sb.append(", connectivityLevelFailureCode=").append(
237                         getConnectivityLevelFailureCodeToString(
238                                 event.connectivityLevelFailureCode));
239                 if (event == mCurrentConnectionEvent) {
240                     sb.append(" CURRENTLY OPEN EVENT");
241                 }
242                 pw.println(sb.toString());
243             }
244             pw.println("mGroupEvents:");
245             for (GroupEvent event : mGroupEventList) {
246                 StringBuilder sb = new StringBuilder();
247                 Calendar c = Calendar.getInstance();
248                 c.setTimeInMillis(event.startTimeMillis);
249                 sb.append("netId=");
250                 sb.append(event.netId);
251                 sb.append(", startTime=");
252                 sb.append(event.startTimeMillis == 0 ? "            <null>" :
253                         String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
254                 sb.append(", channelFrequency=");
255                 sb.append(event.channelFrequency);
256                 sb.append(", groupRole=");
257                 switch (event.groupRole) {
258                     case GroupEvent.GROUP_CLIENT:
259                         sb.append("GroupClient");
260                         break;
261                     case GroupEvent.GROUP_OWNER:
262                     default:
263                         sb.append("GroupOwner");
264                         break;
265                 }
266                 sb.append(", numConnectedClients=");
267                 sb.append(event.numConnectedClients);
268                 sb.append(", numCumulativeClients=");
269                 sb.append(event.numCumulativeClients);
270                 sb.append(", sessionDurationMillis=");
271                 sb.append(event.sessionDurationMillis);
272                 sb.append(", idleDurationMillis=");
273                 sb.append(event.idleDurationMillis);
274 
275                 if (event == mCurrentGroupEvent) {
276                     sb.append(" CURRENTLY OPEN EVENT");
277                 }
278                 pw.println(sb.toString());
279             }
280             pw.println("mWifiP2pStatsProto.numPersistentGroup="
281                     + mNumPersistentGroup);
282             pw.println("mWifiP2pStatsProto.numTotalPeerScans="
283                     + mWifiP2pStatsProto.numTotalPeerScans);
284             pw.println("mWifiP2pStatsProto.numTotalServiceScans="
285                     + mWifiP2pStatsProto.numTotalServiceScans);
286         }
287     }
288 
getconnectionTypeToString(int connectionType)289     private String getconnectionTypeToString(int connectionType) {
290         switch (connectionType) {
291             case P2pConnectionEvent.CONNECTION_FRESH:
292                 return "FRESH";
293             case P2pConnectionEvent.CONNECTION_REINVOKE:
294                 return "REINVOKE";
295             case P2pConnectionEvent.CONNECTION_LOCAL:
296                 return "LOCAL";
297             case P2pConnectionEvent.CONNECTION_FAST:
298                 return "FAST";
299             default:
300                 return "UNKNOWN";
301         }
302     }
303 
getGroupRoleToString(int groupRole)304     private String getGroupRoleToString(int groupRole) {
305         switch (groupRole) {
306             case GroupEvent.GROUP_OWNER:
307                 return "OWNER";
308             case GroupEvent.GROUP_CLIENT:
309                 return "CLIENT";
310             default:
311                 return "UNKNOWN DURING CONNECT";
312         }
313     }
314 
getConnectivityLevelFailureCodeToString(int connectivityLevelFailureCode)315     private String getConnectivityLevelFailureCodeToString(int connectivityLevelFailureCode) {
316         switch (connectivityLevelFailureCode) {
317             case P2pConnectionEvent.CLF_NONE:
318                 return "NONE";
319             case P2pConnectionEvent.CLF_TIMEOUT:
320                 return "TIMEOUT";
321             case P2pConnectionEvent.CLF_CANCEL:
322                 return "CANCEL";
323             case P2pConnectionEvent.CLF_PROV_DISC_FAIL:
324                 return "PROV_DISC_FAIL";
325             case P2pConnectionEvent.CLF_INVITATION_FAIL:
326                 return "INVITATION_FAIL";
327             case P2pConnectionEvent.CLF_USER_REJECT:
328                 return "USER_REJECT";
329             case P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT:
330                 return "NEW_CONNECTION_ATTEMPT";
331             case P2pConnectionEvent.CLF_GROUP_REMOVED:
332                 return "GROUP_REMOVED";
333             case P2pConnectionEvent.CLF_CREATE_GROUP_FAILED:
334                 return "CREATE_GROUP_FAILED";
335             case P2pConnectionEvent.CLF_UNKNOWN:
336             default:
337                 return "UNKNOWN";
338         }
339     }
340 
341     /** Increment total number of peer scans */
incrementPeerScans()342     public void incrementPeerScans() {
343         synchronized (mLock) {
344             mWifiP2pStatsProto.numTotalPeerScans++;
345         }
346     }
347 
348     /** Increment total number of service scans */
incrementServiceScans()349     public void incrementServiceScans() {
350         synchronized (mLock) {
351             mWifiP2pStatsProto.numTotalServiceScans++;
352         }
353     }
354 
355     /** Set the number of saved persistent group */
updatePersistentGroup(WifiP2pGroupList groups)356     public void updatePersistentGroup(WifiP2pGroupList groups) {
357         synchronized (mLock) {
358             final Collection<WifiP2pGroup> list = groups.getGroupList();
359             mNumPersistentGroup = list.size();
360         }
361     }
362 
363     /** Returns if current connection event type is FAST connection */
isP2pFastConnectionType()364     public boolean isP2pFastConnectionType() {
365         if (mCurrentConnectionEvent == null) {
366             return false;
367         }
368         return P2pConnectionEvent.CONNECTION_FAST == mCurrentConnectionEvent.connectionType;
369     }
370 
371     /** Gets current connection event group role string */
getP2pGroupRoleString()372     public String getP2pGroupRoleString() {
373         if (mCurrentConnectionEvent == null) {
374             return "UNKNOWN";
375         }
376         return (GroupEvent.GROUP_OWNER == mCurrentConnectionEvent.groupRole) ? "GO" : "GC";
377     }
378 
379     /**
380      * Create a new connection event. Call when p2p attempts to make a new connection to
381      * another peer. If there is a current 'un-ended' connection event, it will be ended with
382      * P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT.
383      *
384      * @param connectionType indicate this connection is fresh or reinvoke.
385      * @param config configuration used for this connection.
386      * @param groupRole groupRole used for this connection.
387      * @param uid uid of caller app
388      * @param attributionTag attributionTag of caller app
389      */
startConnectionEvent(int connectionType, WifiP2pConfig config, int groupRole, int uid, @Nullable String attributionTag)390     public void startConnectionEvent(int connectionType, WifiP2pConfig config, int groupRole,
391             int uid, @Nullable String attributionTag) {
392         synchronized (mLock) {
393             if (attributionTag == null) {
394                 attributionTag = "";
395             }
396             StringBuilder stringBuilder = new StringBuilder("Start connection event");
397             if (mCurrentConnectionEvent == null) {
398                 stringBuilder.append(", mCurrentConnectionEvent:null");
399             } else {
400                 stringBuilder.append(", curConnectionType:")
401                         .append(getconnectionTypeToString(mCurrentConnectionEvent.connectionType))
402                         .append(", curGroupRole:")
403                         .append(getGroupRoleToString(mCurrentConnectionEvent.groupRole))
404                         .append(", curUid:").append(mCurrentConnectionEvent.uid)
405                         .append(", attributionTag:").append(mCurrentConnectionEvent.attributionTag)
406                         .append(", curConnectivityLevelFailureCode:")
407                         .append(getConnectivityLevelFailureCodeToString(
408                                 mCurrentConnectionEvent.connectivityLevelFailureCode));
409             }
410             stringBuilder.append(", startConnectionType:")
411                     .append(getconnectionTypeToString(connectionType))
412                     .append(", startGroupRole:").append(getGroupRoleToString(groupRole))
413                     .append(", startUid:").append(uid)
414                     .append(", startAttributionTag:").append(attributionTag);
415             Log.d(TAG, stringBuilder.toString());
416             // handle overlapping connection event first.
417             if (mCurrentConnectionEvent != null) {
418                 endConnectionEvent(P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT);
419             }
420 
421             while (mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) {
422                 mConnectionEventList.remove(0);
423             }
424             mCurrentConnectionEventStartTime = mClock.getElapsedSinceBootMillis();
425 
426             mCurrentConnectionEvent = new P2pConnectionEvent();
427             mCurrentConnectionEvent.startTimeMillis = mClock.getWallClockMillis();
428             mCurrentConnectionEvent.connectionType = connectionType;
429             mCurrentConnectionEvent.groupRole = groupRole;
430 
431             if (config != null) {
432                 mCurrentConnectionEvent.wpsMethod = config.wps.setup;
433                 mCurrentConnectionEvent.band = convertGroupOwnerBand(config.groupOwnerBand);
434                 mCurrentConnectionEvent.frequencyMhz =
435                         (config.groupOwnerBand < MIN_2G_FREQUENCY_MHZ) ? 0 : config.groupOwnerBand;
436             }
437             mCurrentConnectionEvent.staFrequencyMhz = getWifiStaFrequency();
438             mCurrentConnectionEvent.uid = uid;
439             mCurrentConnectionEvent.attributionTag = attributionTag;
440             if (mLastConnectionEventUid == uid && mCurrentConnectionEventStartTime < (
441                     mLastConnectionEventStartTime + MAX_CONNECTION_ATTEMPT_TIME_INTERVAL_MS)) {
442                 mLastConnectionTryCount += 1;
443             } else {
444                 mLastConnectionTryCount = 1;
445             }
446             mLastConnectionEventUid = uid;
447             mLastConnectionEventStartTime = mCurrentConnectionEventStartTime;
448             mCurrentConnectionEvent.tryCount = mLastConnectionTryCount;
449 
450             mConnectionEventList.add(mCurrentConnectionEvent);
451         }
452     }
453 
454     /** Returns if there is an ongoing connection */
hasOngoingConnection()455     public boolean hasOngoingConnection() {
456         return mCurrentConnectionEvent != null;
457     }
458 
459     /**
460      * End a Connection event record. Call when p2p connection attempt succeeds or fails.
461      * If a Connection event has not been started when .end is called,
462      * a new one is created with zero duration.
463      *
464      * @param failure indicate the failure with WifiMetricsProto.P2pConnectionEvent.CLF_X.
465      */
endConnectionEvent(int failure)466     public void endConnectionEvent(int failure) {
467         synchronized (mLock) {
468             StringBuilder stringBuilder = new StringBuilder("End connection event");
469             if (mCurrentConnectionEvent == null) {
470                 stringBuilder.append(", mCurrentConnectionEvent:null");
471             } else {
472                 stringBuilder.append(", curConnectionType:")
473                         .append(getconnectionTypeToString(mCurrentConnectionEvent.connectionType))
474                         .append(", curGroupRole:")
475                         .append(getGroupRoleToString(mCurrentConnectionEvent.groupRole))
476                         .append(", curUid:")
477                         .append(mCurrentConnectionEvent.uid)
478                         .append(", attributionTag:").append(mCurrentConnectionEvent.attributionTag)
479                         .append(", curConnectivityLevelFailureCode:")
480                         .append(getConnectivityLevelFailureCodeToString(
481                                 mCurrentConnectionEvent.connectivityLevelFailureCode));
482             }
483             stringBuilder.append(", endConnectivityLevelFailureCode:")
484                     .append(getConnectivityLevelFailureCodeToString(failure));
485             Log.d(TAG, stringBuilder.toString());
486             if (mCurrentConnectionEvent == null) {
487                 // Reinvoking a group with invitation will be handled in supplicant.
488                 // There won't be a connection starting event in framework.
489                 // The framework only gets the connection ending event in GroupStarted state.
490                 startConnectionEvent(P2pConnectionEvent.CONNECTION_REINVOKE, null,
491                         GroupEvent.GROUP_UNKNOWN, SYSTEM_UID, null);
492             }
493 
494             mCurrentConnectionEvent.durationTakenToConnectMillis = (int)
495                     (mClock.getElapsedSinceBootMillis()
496                     - mCurrentConnectionEventStartTime);
497             mCurrentConnectionEvent.connectivityLevelFailureCode = failure;
498             mCurrentConnectionEvent.isCountryCodeWorldMode = mIsCountryCodeWorldMode;
499 
500             WifiStatsLog.write(WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED,
501                     convertConnectionType(mCurrentConnectionEvent.connectionType),
502                     mCurrentConnectionEvent.durationTakenToConnectMillis,
503                     mCurrentConnectionEvent.durationTakenToConnectMillis / 200,
504                     convertFailureCode(failure),
505                     convertGroupRole(mCurrentConnectionEvent.groupRole),
506                     convertBandStatsLog(mCurrentConnectionEvent.band),
507                     mCurrentConnectionEvent.frequencyMhz,
508                     mCurrentConnectionEvent.staFrequencyMhz,
509                     mCurrentConnectionEvent.uid,
510                     mIsCountryCodeWorldMode,
511                     mCurrentConnectionEvent.fallbackToNegotiationOnInviteStatusInfoUnavailable,
512                     mCurrentConnectionEvent.tryCount,
513                     mCurrentConnectionEvent.attributionTag);
514             mCurrentConnectionEvent = null;
515             if (P2pConnectionEvent.CLF_NONE == failure) {
516                 mLastConnectionTryCount = 0;
517             }
518         }
519     }
520 
521     /**
522      * Fallback to GO negotiation if device receives invitation response status code -
523      * information is currently unavailable
524      */
setFallbackToNegotiationOnInviteStatusInfoUnavailable()525     public void setFallbackToNegotiationOnInviteStatusInfoUnavailable() {
526         if (mCurrentConnectionEvent == null) {
527             return;
528         }
529         mCurrentConnectionEvent.fallbackToNegotiationOnInviteStatusInfoUnavailable = true;
530     }
531 
532    /** Sets if the Country Code is in world mode */
setIsCountryCodeWorldMode(boolean isCountryCodeWorldMode)533     public void setIsCountryCodeWorldMode(boolean isCountryCodeWorldMode) {
534         mIsCountryCodeWorldMode = isCountryCodeWorldMode;
535     }
536 
convertConnectionType(int connectionType)537     private int convertConnectionType(int connectionType) {
538         switch (connectionType) {
539             case P2pConnectionEvent.CONNECTION_FRESH:
540                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__FRESH;
541             case P2pConnectionEvent.CONNECTION_REINVOKE:
542                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__REINVOKE;
543             case P2pConnectionEvent.CONNECTION_LOCAL:
544                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__LOCAL;
545             case P2pConnectionEvent.CONNECTION_FAST:
546                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__FAST;
547             default:
548                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__TYPE__UNSPECIFIED;
549         }
550     }
551 
convertFailureCode(int failureCode)552     private int convertFailureCode(int failureCode) {
553         switch (failureCode) {
554             case P2pConnectionEvent.CLF_NONE:
555                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__NONE;
556             case P2pConnectionEvent.CLF_TIMEOUT:
557                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__TIMEOUT;
558             case P2pConnectionEvent.CLF_CANCEL:
559                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__CANCEL;
560             case P2pConnectionEvent.CLF_PROV_DISC_FAIL:
561                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__PROV_DISC_FAIL;
562             case P2pConnectionEvent.CLF_INVITATION_FAIL:
563                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__INVITATION_FAIL;
564             case P2pConnectionEvent.CLF_USER_REJECT:
565                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__USER_REJECT;
566             case P2pConnectionEvent.CLF_NEW_CONNECTION_ATTEMPT:
567                 return WifiStatsLog
568                         .WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__NEW_CONNECTION_ATTEMPT;
569             case P2pConnectionEvent.CLF_GROUP_REMOVED:
570                 return WifiStatsLog
571                         .WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__GROUP_REMOVED;
572             case P2pConnectionEvent.CLF_CREATE_GROUP_FAILED:
573                 return WifiStatsLog
574                         .WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__CREATE_GROUP_FAILED;
575             case P2pConnectionEvent.CLF_UNKNOWN:
576             default:
577                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__FAILURE_CODE__UNKNOWN;
578         }
579     }
580 
convertGroupRole(int groupRole)581     private int convertGroupRole(int groupRole) {
582         switch (groupRole) {
583             case GroupEvent.GROUP_OWNER:
584                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__GROUP_ROLE__GROUP_OWNER;
585             case GroupEvent.GROUP_CLIENT:
586                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__GROUP_ROLE__GROUP_CLIENT;
587             default:
588                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__GROUP_ROLE__GROUP_UNKNOWN;
589         }
590     }
591 
convertGroupOwnerBand(int bandOrFrequency)592     private int convertGroupOwnerBand(int bandOrFrequency) {
593         if (bandOrFrequency >= MIN_2G_FREQUENCY_MHZ) {
594             return P2pConnectionEvent.BAND_FREQUENCY;
595         } else {
596             switch (bandOrFrequency) {
597                 case WifiP2pConfig.GROUP_OWNER_BAND_AUTO:
598                     return P2pConnectionEvent.BAND_AUTO;
599                 case WifiP2pConfig.GROUP_OWNER_BAND_2GHZ:
600                     return P2pConnectionEvent.BAND_2G;
601                 case WifiP2pConfig.GROUP_OWNER_BAND_5GHZ:
602                     return P2pConnectionEvent.BAND_5G;
603                 default:
604                     return P2pConnectionEvent.BAND_UNKNOWN;
605             }
606         }
607     }
608 
convertBandStatsLog(int band)609     private int convertBandStatsLog(int band) {
610         switch (band) {
611             case P2pConnectionEvent.BAND_AUTO:
612                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_AUTO;
613             case P2pConnectionEvent.BAND_2G:
614                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_2G;
615             case P2pConnectionEvent.BAND_5G:
616                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_5G;
617             case P2pConnectionEvent.BAND_6G:
618                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_6G;
619             case P2pConnectionEvent.BAND_FREQUENCY:
620                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_FREQUENCY;
621             default:
622                 return WifiStatsLog.WIFI_P2P_CONNECTION_REPORTED__BAND__BAND_UNKNOWN;
623         }
624     }
625 
getWifiStaFrequency()626     private int getWifiStaFrequency() {
627         WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
628         WifiInfo wifiInfo = wifiManager.getConnectionInfo();
629         if (wifiInfo.getFrequency() > 0) {
630             return wifiInfo.getFrequency();
631         } else {
632             return 0;
633         }
634     }
635 
636     /**
637      * Create a new group event.
638      *
639      * @param group the information of started group.
640      */
startGroupEvent(WifiP2pGroup group)641     public void startGroupEvent(WifiP2pGroup group) {
642         if (group == null) {
643             if (DBG) Log.d(TAG, "Cannot start group event due to null group");
644             return;
645         }
646         synchronized (mLock) {
647             // handle overlapping group event first.
648             if (mCurrentGroupEvent != null) {
649                 if (DBG) Log.d(TAG, "Overlapping group event!");
650                 endGroupEvent();
651             }
652 
653             while (mGroupEventList.size() >= MAX_GROUP_EVENTS) {
654                 mGroupEventList.remove(0);
655             }
656             mCurrentGroupEventStartTime = mClock.getElapsedSinceBootMillis();
657             if (group.getClientList().size() == 0) {
658                 mCurrentGroupEventIdleStartTime = mClock.getElapsedSinceBootMillis();
659             } else {
660                 mCurrentGroupEventIdleStartTime = 0;
661             }
662 
663             mCurrentGroupEvent = new GroupEvent();
664             mCurrentGroupEvent.netId = group.getNetworkId();
665             mCurrentGroupEvent.startTimeMillis = mClock.getWallClockMillis();
666             mCurrentGroupEvent.numConnectedClients = group.getClientList().size();
667             mCurrentGroupEvent.channelFrequency = group.getFrequency();
668             mCurrentGroupEvent.groupRole = group.isGroupOwner()
669                     ? GroupEvent.GROUP_OWNER
670                     : GroupEvent.GROUP_CLIENT;
671             mGroupEventList.add(mCurrentGroupEvent);
672         }
673     }
674 
675     /**
676      * Update the information of started group.
677      */
updateGroupEvent(WifiP2pGroup group)678     public void updateGroupEvent(WifiP2pGroup group) {
679         if (group == null) {
680             if (DBG) Log.d(TAG, "Cannot update group event due to null group.");
681             return;
682         }
683         synchronized (mLock) {
684             if (mCurrentGroupEvent == null) {
685                 Log.w(TAG, "Cannot update group event due to no current group.");
686                 return;
687             }
688 
689             if (mCurrentGroupEvent.netId != group.getNetworkId()) {
690                 Log.w(TAG, "Updating group id " + group.getNetworkId()
691                         + " is different from current group id " + mCurrentGroupEvent.netId
692                         + ".");
693                 return;
694             }
695 
696             int delta = group.getClientList().size() - mCurrentGroupEvent.numConnectedClients;
697             mCurrentGroupEvent.numConnectedClients = group.getClientList().size();
698             if (delta > 0) {
699                 mCurrentGroupEvent.numCumulativeClients += delta;
700             }
701 
702             // if new client comes during idle period, cumulate idle duration and reset idle timer.
703             // if the last client disconnected during non-idle period, start idle timer.
704             if (mCurrentGroupEventIdleStartTime > 0) {
705                 if (group.getClientList().size() > 0) {
706                     mCurrentGroupEvent.idleDurationMillis +=
707                             (mClock.getElapsedSinceBootMillis()
708                             - mCurrentGroupEventIdleStartTime);
709                     mCurrentGroupEventIdleStartTime = 0;
710                 }
711             } else {
712                 if (group.getClientList().size() == 0) {
713                     mCurrentGroupEventIdleStartTime = mClock.getElapsedSinceBootMillis();
714                 }
715             }
716         }
717     }
718 
719     /**
720      * End a group event.
721      */
endGroupEvent()722     public void endGroupEvent() {
723         synchronized (mLock) {
724             if (mCurrentGroupEvent != null) {
725                 mCurrentGroupEvent.sessionDurationMillis = (int)
726                         (mClock.getElapsedSinceBootMillis()
727                         - mCurrentGroupEventStartTime);
728                 if (mCurrentGroupEventIdleStartTime > 0) {
729                     mCurrentGroupEvent.idleDurationMillis +=
730                             (mClock.getElapsedSinceBootMillis()
731                             - mCurrentGroupEventIdleStartTime);
732                     mCurrentGroupEventIdleStartTime = 0;
733                 }
734             } else {
735                 Log.e(TAG, "No current group!");
736             }
737             mCurrentGroupEvent = null;
738         }
739     }
740 
741     /* Log Metrics */
742 }
743