AnalyticsTests.java revision 5cfc86b2dc2f912077ba6d047630ac8f4b251c06
1/* 2 * Copyright (C) 2016 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 17package com.android.server.telecom.tests; 18 19import android.content.Context; 20import android.os.Build; 21import android.os.Handler; 22import android.os.Looper; 23import android.telecom.CallAudioState; 24import android.telecom.Connection; 25import android.telecom.DisconnectCause; 26import android.telecom.InCallService; 27import android.telecom.Log; 28import android.telecom.Logging.EventManager; 29import android.telecom.ParcelableCallAnalytics; 30import android.telecom.TelecomAnalytics; 31import android.telecom.TelecomManager; 32import android.telecom.VideoCallImpl; 33import android.telecom.VideoProfile; 34import android.support.test.filters.FlakyTest; 35import android.test.suitebuilder.annotation.MediumTest; 36import android.test.suitebuilder.annotation.SmallTest; 37import android.util.Base64; 38 39import com.android.internal.util.IndentingPrintWriter; 40import com.android.server.telecom.Analytics; 41import com.android.server.telecom.CallAudioRouteStateMachine; 42import com.android.server.telecom.LogUtils; 43import com.android.server.telecom.nano.TelecomLogClass; 44 45import java.io.PrintWriter; 46import java.io.StringWriter; 47import java.util.Arrays; 48import java.util.HashSet; 49import java.util.LinkedList; 50import java.util.List; 51import java.util.Map; 52import java.util.Set; 53import java.util.concurrent.CountDownLatch; 54import java.util.concurrent.TimeUnit; 55 56import static org.mockito.Matchers.any; 57import static org.mockito.Matchers.anyInt; 58import static org.mockito.Mockito.doAnswer; 59import static org.mockito.Mockito.mock; 60 61public class AnalyticsTests extends TelecomSystemTest { 62 @MediumTest 63 public void testAnalyticsSingleCall() throws Exception { 64 IdPair testCall = startAndMakeActiveIncomingCall( 65 "650-555-1212", 66 mPhoneAccountA0.getAccountHandle(), 67 mConnectionServiceFixtureA); 68 69 Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData(); 70 71 assertTrue(analyticsMap.containsKey(testCall.mCallId)); 72 73 Analytics.CallInfoImpl callAnalytics = analyticsMap.get(testCall.mCallId); 74 assertTrue(callAnalytics.startTime > 0); 75 assertEquals(0, callAnalytics.endTime); 76 assertEquals(Analytics.INCOMING_DIRECTION, callAnalytics.callDirection); 77 assertFalse(callAnalytics.isInterrupted); 78 assertNull(callAnalytics.callTerminationReason); 79 assertEquals(mConnectionServiceComponentNameA.flattenToShortString(), 80 callAnalytics.connectionService); 81 82 mConnectionServiceFixtureA. 83 sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR); 84 85 analyticsMap = Analytics.cloneData(); 86 callAnalytics = analyticsMap.get(testCall.mCallId); 87 assertTrue(callAnalytics.endTime > 0); 88 assertNotNull(callAnalytics.callTerminationReason); 89 assertEquals(DisconnectCause.ERROR, callAnalytics.callTerminationReason.getCode()); 90 91 StringWriter sr = new StringWriter(); 92 IndentingPrintWriter ip = new IndentingPrintWriter(sr, " "); 93 Analytics.dump(ip); 94 String dumpResult = sr.toString(); 95 String[] expectedFields = {"startTime", "endTime", "direction", "isAdditionalCall", 96 "isInterrupted", "callTechnologies", "callTerminationReason", "connectionService"}; 97 for (String field : expectedFields) { 98 assertTrue(dumpResult.contains(field)); 99 } 100 } 101 102 @FlakyTest 103 @MediumTest 104 public void testAnalyticsDumping() throws Exception { 105 Analytics.reset(); 106 IdPair testCall = startAndMakeActiveIncomingCall( 107 "650-555-1212", 108 mPhoneAccountA0.getAccountHandle(), 109 mConnectionServiceFixtureA); 110 111 mConnectionServiceFixtureA. 112 sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR); 113 Analytics.CallInfoImpl expectedAnalytics = Analytics.cloneData().get(testCall.mCallId); 114 115 TelecomManager tm = (TelecomManager) mSpyContext.getSystemService(Context.TELECOM_SERVICE); 116 List<ParcelableCallAnalytics> analyticsList = tm.dumpAnalytics().getCallAnalytics(); 117 118 assertEquals(1, analyticsList.size()); 119 ParcelableCallAnalytics pCA = analyticsList.get(0); 120 121 assertTrue(Math.abs(expectedAnalytics.startTime - pCA.getStartTimeMillis()) < 122 ParcelableCallAnalytics.MILLIS_IN_5_MINUTES); 123 assertEquals(0, pCA.getStartTimeMillis() % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES); 124 assertTrue(Math.abs((expectedAnalytics.endTime - expectedAnalytics.startTime) - 125 pCA.getCallDurationMillis()) < ParcelableCallAnalytics.MILLIS_IN_1_SECOND); 126 assertEquals(0, pCA.getCallDurationMillis() % ParcelableCallAnalytics.MILLIS_IN_1_SECOND); 127 128 assertEquals(expectedAnalytics.callDirection, pCA.getCallType()); 129 assertEquals(expectedAnalytics.isAdditionalCall, pCA.isAdditionalCall()); 130 assertEquals(expectedAnalytics.isInterrupted, pCA.isInterrupted()); 131 assertEquals(expectedAnalytics.callTechnologies, pCA.getCallTechnologies()); 132 assertEquals(expectedAnalytics.callTerminationReason.getCode(), 133 pCA.getCallTerminationCode()); 134 assertEquals(expectedAnalytics.connectionService, pCA.getConnectionService()); 135 List<ParcelableCallAnalytics.AnalyticsEvent> analyticsEvents = pCA.analyticsEvents(); 136 Set<Integer> capturedEvents = new HashSet<>(); 137 for (ParcelableCallAnalytics.AnalyticsEvent e : analyticsEvents) { 138 capturedEvents.add(e.getEventName()); 139 assertIsRoundedToOneSigFig(e.getTimeSinceLastEvent()); 140 } 141 assertTrue(capturedEvents.contains(ParcelableCallAnalytics.AnalyticsEvent.SET_ACTIVE)); 142 assertTrue(capturedEvents.contains( 143 ParcelableCallAnalytics.AnalyticsEvent.FILTERING_INITIATED)); 144 } 145 146 @MediumTest 147 public void testAnalyticsTwoCalls() throws Exception { 148 IdPair testCall1 = startAndMakeActiveIncomingCall( 149 "650-555-1212", 150 mPhoneAccountA0.getAccountHandle(), 151 mConnectionServiceFixtureA); 152 IdPair testCall2 = startAndMakeActiveOutgoingCall( 153 "650-555-1213", 154 mPhoneAccountA0.getAccountHandle(), 155 mConnectionServiceFixtureA); 156 157 Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData(); 158 assertTrue(analyticsMap.containsKey(testCall1.mCallId)); 159 assertTrue(analyticsMap.containsKey(testCall2.mCallId)); 160 161 Analytics.CallInfoImpl callAnalytics1 = analyticsMap.get(testCall1.mCallId); 162 Analytics.CallInfoImpl callAnalytics2 = analyticsMap.get(testCall2.mCallId); 163 assertTrue(callAnalytics1.startTime > 0); 164 assertTrue(callAnalytics2.startTime > 0); 165 assertEquals(0, callAnalytics1.endTime); 166 assertEquals(0, callAnalytics2.endTime); 167 168 assertEquals(Analytics.INCOMING_DIRECTION, callAnalytics1.callDirection); 169 assertEquals(Analytics.OUTGOING_DIRECTION, callAnalytics2.callDirection); 170 171 assertTrue(callAnalytics1.isInterrupted); 172 assertTrue(callAnalytics2.isAdditionalCall); 173 174 assertNull(callAnalytics1.callTerminationReason); 175 assertNull(callAnalytics2.callTerminationReason); 176 177 assertEquals(mConnectionServiceComponentNameA.flattenToShortString(), 178 callAnalytics1.connectionService); 179 assertEquals(mConnectionServiceComponentNameA.flattenToShortString(), 180 callAnalytics1.connectionService); 181 182 mConnectionServiceFixtureA. 183 sendSetDisconnected(testCall2.mConnectionId, DisconnectCause.REMOTE); 184 mConnectionServiceFixtureA. 185 sendSetDisconnected(testCall1.mConnectionId, DisconnectCause.ERROR); 186 187 analyticsMap = Analytics.cloneData(); 188 callAnalytics1 = analyticsMap.get(testCall1.mCallId); 189 callAnalytics2 = analyticsMap.get(testCall2.mCallId); 190 assertTrue(callAnalytics1.endTime > 0); 191 assertTrue(callAnalytics2.endTime > 0); 192 assertNotNull(callAnalytics1.callTerminationReason); 193 assertNotNull(callAnalytics2.callTerminationReason); 194 assertEquals(DisconnectCause.ERROR, callAnalytics1.callTerminationReason.getCode()); 195 assertEquals(DisconnectCause.REMOTE, callAnalytics2.callTerminationReason.getCode()); 196 } 197 198 @MediumTest 199 public void testAnalyticsVideo() throws Exception { 200 Analytics.reset(); 201 IdPair callIds = startAndMakeActiveOutgoingCall( 202 "650-555-1212", 203 mPhoneAccountA0.getAccountHandle(), 204 mConnectionServiceFixtureA); 205 206 CountDownLatch counter = new CountDownLatch(1); 207 InCallService.VideoCall.Callback callback = mock(InCallService.VideoCall.Callback.class); 208 209 doAnswer(invocation -> { 210 counter.countDown(); 211 return null; 212 }).when(callback) 213 .onSessionModifyResponseReceived(anyInt(), any(VideoProfile.class), 214 any(VideoProfile.class)); 215 216 mConnectionServiceFixtureA.sendSetVideoProvider( 217 mConnectionServiceFixtureA.mLatestConnectionId); 218 InCallService.VideoCall videoCall = 219 mInCallServiceFixtureX.getCall(callIds.mCallId).getVideoCallImpl( 220 mInCallServiceComponentNameX.getPackageName(), Build.VERSION.SDK_INT); 221 videoCall.registerCallback(callback); 222 ((VideoCallImpl) videoCall).setVideoState(VideoProfile.STATE_BIDIRECTIONAL); 223 224 videoCall.sendSessionModifyRequest(new VideoProfile(VideoProfile.STATE_RX_ENABLED)); 225 counter.await(10000, TimeUnit.MILLISECONDS); 226 227 StringWriter sw = new StringWriter(); 228 PrintWriter pw = new PrintWriter(sw); 229 Analytics.dumpToEncodedProto(pw, new String[]{}); 230 TelecomLogClass.TelecomLog analyticsProto = 231 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT)); 232 233 assertEquals(1, analyticsProto.callLogs.length); 234 TelecomLogClass.VideoEvent[] videoEvents = analyticsProto.callLogs[0].videoEvents; 235 assertEquals(2, videoEvents.length); 236 237 assertEquals(Analytics.SEND_LOCAL_SESSION_MODIFY_REQUEST, videoEvents[0].getEventName()); 238 assertEquals(VideoProfile.STATE_RX_ENABLED, videoEvents[0].getVideoState()); 239 assertEquals(-1, videoEvents[0].getTimeSinceLastEventMillis()); 240 241 assertEquals(Analytics.RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE, 242 videoEvents[1].getEventName()); 243 assertEquals(VideoProfile.STATE_RX_ENABLED, videoEvents[1].getVideoState()); 244 assertIsRoundedToOneSigFig(videoEvents[1].getTimeSinceLastEventMillis()); 245 } 246 247 @SmallTest 248 public void testAnalyticsRounding() { 249 long[] testVals = {0, -1, -10, -100, -57836, 1, 10, 100, 1000, 458457}; 250 long[] expected = {0, -1, -10, -100, -60000, 1, 10, 100, 1000, 500000}; 251 for (int i = 0; i < testVals.length; i++) { 252 assertEquals(expected[i], Analytics.roundToOneSigFig(testVals[i])); 253 } 254 } 255 256 @SmallTest 257 public void testAnalyticsLogSessionTiming() throws Exception { 258 long minTime = 50; 259 Log.startSession(LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL); 260 Thread.sleep(minTime); 261 Log.endSession(); 262 TelecomManager tm = (TelecomManager) mSpyContext.getSystemService(Context.TELECOM_SERVICE); 263 List<TelecomAnalytics.SessionTiming> sessions = tm.dumpAnalytics().getSessionTimings(); 264 sessions.stream() 265 .filter(s -> LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL.equals( 266 Analytics.sSessionIdToLogSession.get(s.getKey()))) 267 .forEach(s -> assertTrue(s.getTime() >= minTime)); 268 } 269 270 @MediumTest 271 public void testAnalyticsDumpToProto() throws Exception { 272 Analytics.reset(); 273 IdPair testCall = startAndMakeActiveIncomingCall( 274 "650-555-1212", 275 mPhoneAccountA0.getAccountHandle(), 276 mConnectionServiceFixtureA); 277 278 mConnectionServiceFixtureA. 279 sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR); 280 Analytics.CallInfoImpl expectedAnalytics = Analytics.cloneData().get(testCall.mCallId); 281 282 StringWriter sw = new StringWriter(); 283 PrintWriter pw = new PrintWriter(sw); 284 Analytics.dumpToEncodedProto(pw, new String[]{}); 285 TelecomLogClass.TelecomLog analyticsProto = 286 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT)); 287 288 assertEquals(1, analyticsProto.callLogs.length); 289 TelecomLogClass.CallLog callLog = analyticsProto.callLogs[0]; 290 291 assertTrue(Math.abs(expectedAnalytics.startTime - callLog.getStartTime5Min()) < 292 ParcelableCallAnalytics.MILLIS_IN_5_MINUTES); 293 assertEquals(0, callLog.getStartTime5Min() % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES); 294 assertTrue(Math.abs((expectedAnalytics.endTime - expectedAnalytics.startTime) - 295 callLog.getCallDurationMillis()) < ParcelableCallAnalytics.MILLIS_IN_1_SECOND); 296 assertEquals(0, 297 callLog.getCallDurationMillis() % ParcelableCallAnalytics.MILLIS_IN_1_SECOND); 298 299 assertEquals(expectedAnalytics.callDirection, callLog.getType()); 300 assertEquals(expectedAnalytics.isAdditionalCall, callLog.getIsAdditionalCall()); 301 assertEquals(expectedAnalytics.isInterrupted, callLog.getIsInterrupted()); 302 assertEquals(expectedAnalytics.callTechnologies, callLog.getCallTechnologies()); 303 assertEquals(expectedAnalytics.callTerminationReason.getCode(), 304 callLog.getCallTerminationCode()); 305 assertEquals(expectedAnalytics.connectionService, callLog.connectionService[0]); 306 TelecomLogClass.Event[] analyticsEvents = callLog.callEvents; 307 Set<Integer> capturedEvents = new HashSet<>(); 308 for (TelecomLogClass.Event e : analyticsEvents) { 309 capturedEvents.add(e.getEventName()); 310 assertIsRoundedToOneSigFig(e.getTimeSinceLastEventMillis()); 311 } 312 assertTrue(capturedEvents.contains(ParcelableCallAnalytics.AnalyticsEvent.SET_ACTIVE)); 313 assertTrue(capturedEvents.contains( 314 ParcelableCallAnalytics.AnalyticsEvent.FILTERING_INITIATED)); 315 } 316 317 @MediumTest 318 public void testAnalyticsAudioRoutes() throws Exception { 319 Analytics.reset(); 320 IdPair testCall = startAndMakeActiveIncomingCall( 321 "650-555-1212", 322 mPhoneAccountA0.getAccountHandle(), 323 mConnectionServiceFixtureA); 324 List<Integer> audioRoutes = new LinkedList<>(); 325 326 waitForHandlerAction( 327 mTelecomSystem.getCallsManager().getCallAudioManager() 328 .getCallAudioRouteStateMachine().getHandler(), 329 TEST_TIMEOUT); 330 audioRoutes.add(mInCallServiceFixtureX.mCallAudioState.getRoute()); 331 mInCallServiceFixtureX.getInCallAdapter().setAudioRoute(CallAudioState.ROUTE_SPEAKER); 332 waitForHandlerAction( 333 mTelecomSystem.getCallsManager().getCallAudioManager() 334 .getCallAudioRouteStateMachine().getHandler(), 335 TEST_TIMEOUT); 336 audioRoutes.add(CallAudioState.ROUTE_SPEAKER); 337 338 Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData(); 339 assertTrue(analyticsMap.containsKey(testCall.mCallId)); 340 341 Analytics.CallInfoImpl callAnalytics = analyticsMap.get(testCall.mCallId); 342 List<EventManager.Event> events = callAnalytics.callEvents.getEvents(); 343 for (int route : audioRoutes) { 344 String logEvent = CallAudioRouteStateMachine.AUDIO_ROUTE_TO_LOG_EVENT.get(route); 345 assertTrue(events.stream().anyMatch(event -> event.eventId.equals(logEvent))); 346 } 347 } 348 349 @MediumTest 350 public void testAnalyticsConnectionProperties() throws Exception { 351 Analytics.reset(); 352 IdPair testCall = startAndMakeActiveIncomingCall( 353 "650-555-1212", 354 mPhoneAccountA0.getAccountHandle(), 355 mConnectionServiceFixtureA); 356 357 int properties1 = Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE 358 | Connection.PROPERTY_WIFI 359 | Connection.PROPERTY_EMERGENCY_CALLBACK_MODE; 360 int properties2 = Connection.PROPERTY_HIGH_DEF_AUDIO 361 | Connection.PROPERTY_WIFI; 362 int expectedProperties = properties1 | properties2; 363 364 mConnectionServiceFixtureA.mConnectionById.get(testCall.mConnectionId).properties = 365 properties1; 366 mConnectionServiceFixtureA.sendSetConnectionProperties(testCall.mConnectionId); 367 mConnectionServiceFixtureA.mConnectionById.get(testCall.mConnectionId).properties = 368 properties2; 369 mConnectionServiceFixtureA.sendSetConnectionProperties(testCall.mConnectionId); 370 371 mConnectionServiceFixtureA. 372 sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR); 373 374 StringWriter sw = new StringWriter(); 375 PrintWriter pw = new PrintWriter(sw); 376 Analytics.dumpToEncodedProto(pw, new String[]{}); 377 TelecomLogClass.TelecomLog analyticsProto = 378 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT)); 379 380 assertEquals(expectedProperties, 381 analyticsProto.callLogs[0].getConnectionProperties() & expectedProperties); 382 } 383 384 @SmallTest 385 public void testAnalyticsMaxSize() throws Exception { 386 Analytics.reset(); 387 for (int i = 0; i < Analytics.MAX_NUM_CALLS_TO_STORE * 2; i++) { 388 Analytics.initiateCallAnalytics(String.valueOf(i), Analytics.INCOMING_DIRECTION) 389 .addCallTechnology(i); 390 } 391 392 StringWriter sw = new StringWriter(); 393 PrintWriter pw = new PrintWriter(sw); 394 Analytics.dumpToEncodedProto(pw, new String[]{}); 395 TelecomLogClass.TelecomLog analyticsProto = 396 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT)); 397 398 assertEquals(Analytics.MAX_NUM_CALLS_TO_STORE, analyticsProto.callLogs.length); 399 assertEquals(Arrays.stream(analyticsProto.callLogs) 400 .filter(x -> x.getCallTechnologies() < 100) 401 .count(), 0); 402 } 403 404 private void assertIsRoundedToOneSigFig(long x) { 405 assertEquals(x, Analytics.roundToOneSigFig(x)); 406 } 407} 408