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