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