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