summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxshu <xshu@google.com>2020-04-06 19:55:37 -0700
committerxshu <xshu@google.com>2020-04-14 15:33:54 -0700
commit8b8fafe34020b5fe5b43fa99685fb38dcb506c4a (patch)
tree8a2b1176dfb106f75fcc606c6d19778f44d3f2cd
parent20641dfcb5c341b8335c7c44d272c58de7a5adbf (diff)
Wifi user action metrics
Adds logging support for user action metrics. Logs a user action metrics when user forgets network. Bug: 153925826 Test: atest com.android.service.wifi Test: manual sanity test Change-Id: Ief1d4d391bc55cc41bdeaf7ca31705d05e57929f
-rw-r--r--service/java/com/android/server/wifi/WifiMetrics.java111
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java13
-rw-r--r--service/proto/src/metrics.proto52
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java60
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java8
5 files changed, 240 insertions, 4 deletions
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index a9385ed91..5ceee3f37 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -77,6 +77,7 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.PnoScanMetrics;
import com.android.server.wifi.proto.nano.WifiMetricsProto.SoftApConnectedClientsEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent.ConfigInfo;
+import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiLinkLayerUsageStats;
import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiLockStats;
@@ -221,7 +222,9 @@ public class WifiMetrics {
private long mScoreBreachLowTimeMillis = -1;
public static final int MAX_STA_EVENTS = 768;
+ @VisibleForTesting static final int MAX_USER_ACTION_EVENTS = 200;
private LinkedList<StaEventWithTime> mStaEventList = new LinkedList<>();
+ private LinkedList<UserActionEventWithTime> mUserActionEventList = new LinkedList<>();
private int mLastPollRssi = -127;
private int mLastPollLinkSpeed = -1;
private int mLastPollRxLinkSpeed = -1;
@@ -725,6 +728,83 @@ public class WifiMetrics {
}
}
+ class UserActionEventWithTime {
+ private UserActionEvent mUserActionEvent;
+ private long mWallClockTimeMs = 0; // wall clock time for debugging only
+
+ UserActionEventWithTime(int eventType, int targetNetId) {
+ mUserActionEvent = new UserActionEvent();
+ mUserActionEvent.eventType = eventType;
+ mUserActionEvent.startTimeMillis = mClock.getElapsedSinceBootMillis();
+ mWallClockTimeMs = mClock.getWallClockMillis();
+ if (targetNetId >= 0) {
+ WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(targetNetId);
+ if (config != null) {
+ WifiMetricsProto.TargetNetworkInfo networkInfo =
+ new WifiMetricsProto.TargetNetworkInfo();
+ networkInfo.isEphemeral = config.isEphemeral();
+ networkInfo.isPasspoint = config.isPasspoint();
+ mUserActionEvent.targetNetworkInfo = networkInfo;
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(mWallClockTimeMs);
+ sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
+ String eventType = "UNKNOWN";
+ switch (mUserActionEvent.eventType) {
+ case UserActionEvent.EVENT_FORGET_WIFI:
+ eventType = "EVENT_FORGET_WIFI";
+ break;
+ case UserActionEvent.EVENT_DISCONNECT_WIFI:
+ eventType = "EVENT_DISCONNECT_WIFI";
+ break;
+ case UserActionEvent.EVENT_CONFIGURE_METERED_STATUS_METERED:
+ eventType = "EVENT_CONFIGURE_METERED_STATUS_METERED";
+ break;
+ case UserActionEvent.EVENT_CONFIGURE_METERED_STATUS_UNMETERED:
+ eventType = "EVENT_CONFIGURE_METERED_STATUS_UNMETERED";
+ break;
+ case UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_ON:
+ eventType = "EVENT_CONFIGURE_MAC_RANDOMIZATION_ON";
+ break;
+ case UserActionEvent.EVENT_CONFIGURE_MAC_RANDOMIZATION_OFF:
+ eventType = "EVENT_CONFIGURE_MAC_RANDOMIZATION_OFF";
+ break;
+ case UserActionEvent.EVENT_CONFIGURE_AUTO_CONNECT_ON:
+ eventType = "EVENT_CONFIGURE_AUTO_CONNECT_ON";
+ break;
+ case UserActionEvent.EVENT_CONFIGURE_AUTO_CONNECT_OFF:
+ eventType = "EVENT_CONFIGURE_AUTO_CONNECT_OFF";
+ break;
+ case UserActionEvent.EVENT_TOGGLE_WIFI_ON:
+ eventType = "EVENT_TOGGLE_WIFI_ON";
+ break;
+ case UserActionEvent.EVENT_TOGGLE_WIFI_OFF:
+ eventType = "EVENT_TOGGLE_WIFI_OFF";
+ break;
+ case UserActionEvent.EVENT_MANUAL_CONNECT:
+ eventType = "EVENT_MANUAL_CONNECT";
+ break;
+ }
+ sb.append(" eventType=").append(eventType);
+ sb.append(" startTimeMillis=").append(mUserActionEvent.startTimeMillis);
+ WifiMetricsProto.TargetNetworkInfo networkInfo = mUserActionEvent.targetNetworkInfo;
+ if (networkInfo != null) {
+ sb.append(" isEphemeral=").append(networkInfo.isEphemeral);
+ sb.append(" isPasspoint=").append(networkInfo.isPasspoint);
+ }
+ return sb.toString();
+ }
+
+ public UserActionEvent toProto() {
+ return mUserActionEvent;
+ }
+ }
+
/**
* Log event, tracking the start time, end time and result of a wireless connection attempt.
*/
@@ -3254,6 +3334,10 @@ public class WifiMetrics {
for (StaEventWithTime event : mStaEventList) {
pw.println(event);
}
+ pw.println("UserActionEvents:");
+ for (UserActionEventWithTime event : mUserActionEventList) {
+ pw.println(event);
+ }
pw.println("mWifiLogProto.numPasspointProviders="
+ mWifiLogProto.numPasspointProviders);
@@ -3937,6 +4021,10 @@ public class WifiMetrics {
for (int i = 0; i < mStaEventList.size(); i++) {
mWifiLogProto.staEventList[i] = mStaEventList.get(i).staEvent;
}
+ mWifiLogProto.userActionEvents = new UserActionEvent[mUserActionEventList.size()];
+ for (int i = 0; i < mUserActionEventList.size(); i++) {
+ mWifiLogProto.userActionEvents[i] = mUserActionEventList.get(i).toProto();
+ }
mWifiLogProto.totalSsidsInScanHistogram =
makeNumConnectableNetworksBucketArray(mTotalSsidsInScanHistogram);
mWifiLogProto.totalBssidsInScanHistogram =
@@ -4339,6 +4427,7 @@ public class WifiMetrics {
mScanResultRssiTimestampMillis = -1;
mSoftApManagerReturnCodeCounts.clear();
mStaEventList.clear();
+ mUserActionEventList.clear();
mWifiAwareMetrics.clear();
mRttMetrics.clear();
mTotalSsidsInScanHistogram.clear();
@@ -5007,6 +5096,28 @@ public class WifiMetrics {
}
/**
+ * Logs a UserActionEvent without a target network.
+ * @param eventType the type of user action (one of WifiMetricsProto.UserActionEvent.EventType)
+ */
+ public void logUserActionEvent(int eventType) {
+ logUserActionEvent(eventType, -1);
+ }
+
+ /**
+ * Logs a UserActionEvent which has a target network.
+ * @param eventType the type of user action (one of WifiMetricsProto.UserActionEvent.EventType)
+ * @param networkId networkId of the target network.
+ */
+ public void logUserActionEvent(int eventType, int networkId) {
+ synchronized (mLock) {
+ mUserActionEventList.add(new UserActionEventWithTime(eventType, networkId));
+ if (mUserActionEventList.size() > MAX_USER_ACTION_EVENTS) {
+ mUserActionEventList.remove();
+ }
+ }
+ }
+
+ /**
* Update the difference between the last two WifiLinkLayerStats for WifiIsUnusableEvent
*/
public void updateWifiIsUnusableLinkLayerStats(long txSuccessDelta, long txRetriesDelta,
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index a373c3fae..4a9902484 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -112,6 +112,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
import com.android.server.wifi.hotspot2.PasspointManager;
import com.android.server.wifi.hotspot2.PasspointProvider;
+import com.android.server.wifi.proto.nano.WifiMetricsProto;
import com.android.server.wifi.util.ApConfigUtil;
import com.android.server.wifi.util.ExternalCallbackTracker;
import com.android.server.wifi.util.RssiUtil;
@@ -4090,12 +4091,18 @@ public class WifiServiceImpl extends BaseWifiService {
@Override
public void forget(int netId, IBinder binder, @Nullable IActionListener callback,
int callbackIdentifier) {
- if (!isPrivileged(Binder.getCallingPid(), Binder.getCallingUid())) {
+ int uid = Binder.getCallingUid();
+ if (!isPrivileged(Binder.getCallingPid(), uid)) {
throw new SecurityException(TAG + ": Permission denied");
}
mLog.info("forget uid=%").c(Binder.getCallingUid()).flush();
- mClientModeImpl.forget(
- netId, binder, callback, callbackIdentifier, Binder.getCallingUid());
+ if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
+ // It's important to log this metric before the actual forget executes because
+ // the netId becomes invalid after the forget operation.
+ mWifiMetrics.logUserActionEvent(WifiMetricsProto.UserActionEvent.EVENT_FORGET_WIFI,
+ netId);
+ }
+ mClientModeImpl.forget(netId, binder, callback, callbackIdentifier, uid);
}
/**
diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto
index 1f6848482..c60508607 100644
--- a/service/proto/src/metrics.proto
+++ b/service/proto/src/metrics.proto
@@ -684,6 +684,9 @@ message WifiLog {
// Total number of Passpoint providers with subscription expiration date in their profile.
optional int32 num_passpoint_provider_with_subscription_expiration = 191;
+
+ // List of user initiated actions
+ repeated UserActionEvent user_action_events = 192;
}
// Information that gets logged for every WiFi connection.
@@ -3049,6 +3052,55 @@ message PasspointProvisionStats {
}
}
+// An event capturing user action on wifi
+message UserActionEvent {
+ enum EventType {
+ // The default and unused event
+ EVENT_UNKNOWN = 0;
+ // User forgets the network
+ EVENT_FORGET_WIFI = 1;
+ // User manually triggers disconnect
+ EVENT_DISCONNECT_WIFI = 2;
+ // User changes the metered setting to "metered"
+ EVENT_CONFIGURE_METERED_STATUS_METERED = 3;
+ // User changes the metered setting to "unmetered"
+ EVENT_CONFIGURE_METERED_STATUS_UNMETERED = 4;
+ // User changes the mac randomization setting to use random MAC
+ EVENT_CONFIGURE_MAC_RANDOMIZATION_ON = 5;
+ // User changes the mac randomization setting to use factory MAC
+ EVENT_CONFIGURE_MAC_RANDOMIZATION_OFF = 6;
+ // User sets auto-connect to on
+ EVENT_CONFIGURE_AUTO_CONNECT_ON = 7;
+ // User sets auto-connect to off
+ EVENT_CONFIGURE_AUTO_CONNECT_OFF = 8;
+ // User toggles wifi on
+ EVENT_TOGGLE_WIFI_ON = 9;
+ // User toggles wifi off
+ EVENT_TOGGLE_WIFI_OFF = 10;
+ // User manually connects to a network
+ EVENT_MANUAL_CONNECT = 11;
+ }
+
+ // The type of user action
+ optional EventType event_type = 1;
+
+ // The start time of the event in milliseconds since boot.
+ optional int64 start_time_millis = 2;
+
+ // Additional information on the target network for the action. This is not applicable and will
+ // be null for some actions such as EVENT_TOGGLE_WIFI_ON.
+ optional TargetNetworkInfo target_network_info = 3;
+}
+
+// Additional information on a network
+message TargetNetworkInfo {
+ // Whether the network is an ephemeral network.
+ optional bool is_ephemeral = 1;
+
+ // Whether the target is a passpoint network.
+ optional bool is_passpoint = 2;
+}
+
// Number of networks with a large change of connection/disconnection
// failure rate or high failure rate at high RSSI
message HealthMonitorFailureStats {
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 3964d2d37..8b67c046a 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -2409,6 +2409,66 @@ public class WifiMetricsTest extends WifiBaseTest {
}
/**
+ * Test the logging of UserActionEvent with a valid network ID
+ */
+ @Test
+ public void testLogUserActionEventValidNetworkId() throws Exception {
+ int testEventType = WifiMetricsProto.UserActionEvent.EVENT_FORGET_WIFI;
+ int testNetworkId = 0;
+ long testStartTimeMillis = 123123L;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartTimeMillis);
+ WifiConfiguration config = mock(WifiConfiguration.class);
+ when(config.isEphemeral()).thenReturn(true);
+ when(config.isPasspoint()).thenReturn(true);
+ when(mWcm.getConfiguredNetwork(testNetworkId)).thenReturn(config);
+
+ mWifiMetrics.logUserActionEvent(testEventType, testNetworkId);
+ dumpProtoAndDeserialize();
+
+ WifiMetricsProto.UserActionEvent[] userActionEvents = mDecodedProto.userActionEvents;
+ assertEquals(1, userActionEvents.length);
+ assertEquals(WifiMetricsProto.UserActionEvent.EVENT_FORGET_WIFI,
+ userActionEvents[0].eventType);
+ assertEquals(testStartTimeMillis, userActionEvents[0].startTimeMillis);
+ assertEquals(true, userActionEvents[0].targetNetworkInfo.isEphemeral);
+ assertEquals(true, userActionEvents[0].targetNetworkInfo.isPasspoint);
+ }
+
+ /**
+ * Test the logging of UserActionEvent with invalid network ID
+ */
+ @Test
+ public void testLogUserActionEventInvalidNetworkId() throws Exception {
+ int testEventType = WifiMetricsProto.UserActionEvent.EVENT_FORGET_WIFI;
+ int testNetworkId = 0;
+ long testStartTimeMillis = 123123L;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(testStartTimeMillis);
+ when(mWcm.getConfiguredNetwork(testNetworkId)).thenReturn(null);
+
+ mWifiMetrics.logUserActionEvent(testEventType, testNetworkId);
+ dumpProtoAndDeserialize();
+
+ WifiMetricsProto.UserActionEvent[] userActionEvents = mDecodedProto.userActionEvents;
+ assertEquals(1, userActionEvents.length);
+ assertEquals(WifiMetricsProto.UserActionEvent.EVENT_FORGET_WIFI,
+ userActionEvents[0].eventType);
+ assertEquals(testStartTimeMillis, userActionEvents[0].startTimeMillis);
+ assertNull(userActionEvents[0].targetNetworkInfo);
+ }
+
+ /**
+ * Verify that the max length of the UserActionEvent list is limited to MAX_USER_ACTION_EVENTS.
+ */
+ @Test
+ public void testLogUserActionEventCapped() throws Exception {
+ for (int i = 0; i < WifiMetrics.MAX_USER_ACTION_EVENTS + 1; i++) {
+ mWifiMetrics.logUserActionEvent(WifiMetricsProto.UserActionEvent.EVENT_FORGET_WIFI, 0);
+ }
+ dumpProtoAndDeserialize();
+ assertEquals(WifiMetrics.MAX_USER_ACTION_EVENTS, mDecodedProto.userActionEvents.length);
+ }
+
+ /**
* Ensure WifiMetrics doesn't cause a null pointer exception when called with null args
*/
@Test
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
index b5c5e7b42..fcc290560 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -152,6 +152,7 @@ import com.android.internal.util.AsyncChannel;
import com.android.server.wifi.WifiServiceImpl.LocalOnlyRequestorCallback;
import com.android.server.wifi.hotspot2.PasspointManager;
import com.android.server.wifi.hotspot2.PasspointProvisioningTestUtil;
+import com.android.server.wifi.proto.nano.WifiMetricsProto;
import com.android.server.wifi.util.ApConfigUtil;
import com.android.server.wifi.util.WifiAsyncChannel;
import com.android.server.wifi.util.WifiPermissionsUtil;
@@ -3566,9 +3567,14 @@ public class WifiServiceImplTest extends WifiBaseTest {
public void testForgetNetworkWithPrivilegedPermission() throws Exception {
when(mContext.checkPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
mWifiServiceImpl.forget(TEST_NETWORK_ID, mock(Binder.class), mock(IActionListener.class),
0);
- verify(mClientModeImpl).forget(anyInt(), any(Binder.class),
+
+ InOrder inOrder = inOrder(mClientModeImpl, mWifiMetrics);
+ inOrder.verify(mWifiMetrics).logUserActionEvent(
+ WifiMetricsProto.UserActionEvent.EVENT_FORGET_WIFI, TEST_NETWORK_ID);
+ inOrder.verify(mClientModeImpl).forget(anyInt(), any(Binder.class),
any(IActionListener.class), anyInt(), anyInt());
}