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