• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 
20 import static android.telephony.TelephonyManager.EmergencyCallDiagnosticParams;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNotEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.any;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.content.ComponentName;
35 import android.net.Uri;
36 import android.os.BugreportManager;
37 import android.os.DropBoxManager;
38 import android.telecom.DisconnectCause;
39 import android.telecom.PhoneAccount;
40 import android.telecom.PhoneAccountHandle;
41 import android.telephony.TelephonyManager;
42 
43 import com.android.server.telecom.Call;
44 import com.android.server.telecom.CallState;
45 import com.android.server.telecom.CallerInfoLookupHelper;
46 import com.android.server.telecom.CallsManager;
47 import com.android.server.telecom.ClockProxy;
48 import com.android.server.telecom.EmergencyCallDiagnosticLogger;
49 import com.android.server.telecom.PhoneAccountRegistrar;
50 import com.android.server.telecom.PhoneNumberUtilsAdapter;
51 import com.android.server.telecom.TelecomSystem;
52 import com.android.server.telecom.Timeouts;
53 import com.android.server.telecom.ui.ToastFactory;
54 
55 import org.junit.After;
56 import org.junit.Before;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 import org.junit.runners.JUnit4;
60 import org.mockito.ArgumentCaptor;
61 import org.mockito.Mock;
62 
63 import java.util.HashSet;
64 import java.util.List;
65 import java.util.Set;
66 
67 @RunWith(JUnit4.class)
68 public class EmergencyCallDiagnosticLoggerTest extends TelecomTestCase {
69 
70     private static final ComponentName COMPONENT_NAME_1 = ComponentName
71             .unflattenFromString("com.foo/.Blah");
72     private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
73             COMPONENT_NAME_1, "Sim1");
74     private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.
75             Builder(SIM_1_HANDLE, "Sim1")
76             .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
77                     | PhoneAccount.CAPABILITY_CALL_PROVIDER)
78             .setIsEnabled(true)
79             .build();
80     private static final String DROP_BOX_TAG = "ecall_diagnostic_data";
81 
82     private static final long EMERGENCY_CALL_ACTIVE_TIME_THRESHOLD_MILLIS = 100L;
83 
84     private static final long EMERGENCY_CALL_TIME_BEFORE_USER_DISCONNECT_THRESHOLD_MILLIS = 120L;
85 
86     private static final int DAYS_BACK_TO_SEARCH_EMERGENCY_DIAGNOSTIC_ENTRIES = 1;
87     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {
88     };
89     EmergencyCallDiagnosticLogger mEmergencyCallDiagnosticLogger;
90     @Mock
91     private Timeouts.Adapter mTimeouts;
92     @Mock
93     private CallsManager mMockCallsManager;
94     @Mock
95     private CallerInfoLookupHelper mMockCallerInfoLookupHelper;
96     @Mock
97     private PhoneAccountRegistrar mMockPhoneAccountRegistrar;
98     @Mock
99     private ClockProxy mMockClockProxy;
100     @Mock
101     private ToastFactory mMockToastProxy;
102     @Mock
103     private PhoneNumberUtilsAdapter mMockPhoneNumberUtilsAdapter;
104 
105     @Mock
106     private TelephonyManager mTm;
107     @Mock
108     private BugreportManager mBrm;
109     @Mock
110     private DropBoxManager mDbm;
111 
112     @Mock
113     private ClockProxy mClockProxy;
114 
115     @Override
116     @Before
setUp()117     public void setUp() throws Exception {
118         super.setUp();
119 
120         doReturn(mMockCallerInfoLookupHelper).when(mMockCallsManager).getCallerInfoLookupHelper();
121         doReturn(mMockPhoneAccountRegistrar).when(mMockCallsManager).getPhoneAccountRegistrar();
122         doReturn(SIM_1_ACCOUNT).when(mMockPhoneAccountRegistrar).getPhoneAccountUnchecked(
123                 eq(SIM_1_HANDLE));
124         when(mTimeouts.getEmergencyCallActiveTimeThresholdMillis()).
125                 thenReturn(EMERGENCY_CALL_ACTIVE_TIME_THRESHOLD_MILLIS);
126         when(mTimeouts.getEmergencyCallTimeBeforeUserDisconnectThresholdMillis()).
127                 thenReturn(EMERGENCY_CALL_TIME_BEFORE_USER_DISCONNECT_THRESHOLD_MILLIS);
128         when(mTimeouts.getDaysBackToSearchEmergencyDiagnosticEntries()).
129                 thenReturn(DAYS_BACK_TO_SEARCH_EMERGENCY_DIAGNOSTIC_ENTRIES);
130         when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
131 
132         mEmergencyCallDiagnosticLogger = new EmergencyCallDiagnosticLogger(mTm, mBrm,
133                 mTimeouts, mDbm, Runnable::run, mClockProxy);
134     }
135 
136     @Override
137     @After
tearDown()138     public void tearDown() throws Exception {
139         super.tearDown();
140         //reset(mTm);
141     }
142 
143     /**
144      * Helper function that creates the call being tested.
145      * Also invokes onStartCreateConnection
146      */
createCall(boolean isEmergencyCall, int direction)147     private Call createCall(boolean isEmergencyCall, int direction) {
148         Call call = getCall();
149         call.setCallDirection(direction);
150         call.setIsEmergencyCall(isEmergencyCall);
151         mEmergencyCallDiagnosticLogger.onStartCreateConnection(call);
152         return call;
153     }
154 
155     /**
156      * @return an instance of {@link Call} for testing purposes.
157      */
getCall()158     private Call getCall() {
159         return new Call(
160                 "1", /* callId */
161                 mContext,
162                 mMockCallsManager,
163                 mLock,
164                 null /* ConnectionServiceRepository */,
165                 mMockPhoneNumberUtilsAdapter,
166                 Uri.parse("tel:6505551212"),
167                 null /* GatewayInfo */,
168                 null /* connectionManagerPhoneAccountHandle */,
169                 SIM_1_HANDLE,
170                 Call.CALL_DIRECTION_OUTGOING,
171                 false /* shouldAttachToExistingConnection*/,
172                 false /* isConference */,
173                 mMockClockProxy,
174                 mMockToastProxy);
175     }
176 
177     /**
178      * Test that only outgoing emergency calls are tracked
179      */
180     @Test
testNonEmergencyCallNotTracked()181     public void testNonEmergencyCallNotTracked() {
182         //should not be tracked
183         createCall(false, Call.CALL_DIRECTION_OUTGOING);
184         assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
185 
186         //should not be tracked (not in scope)
187         createCall(false, Call.CALL_DIRECTION_INCOMING);
188         assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
189     }
190 
191     /**
192      * Test that incoming emergency calls are not tracked (not in scope right now)
193      */
194     @Test
testIncomingEmergencyCallsNotTracked()195     public void testIncomingEmergencyCallsNotTracked() {
196         //should not be tracked
197         createCall(true, Call.CALL_DIRECTION_INCOMING);
198         assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
199     }
200 
201 
202     /**
203      * Test getDataCollectionTypes(reason)
204      */
205     @Test
testCollectionTypeForReasonDoesNotReturnUnreasonableValues()206     public void testCollectionTypeForReasonDoesNotReturnUnreasonableValues() {
207         int reason = EmergencyCallDiagnosticLogger.REPORT_REASON_RANGE_START + 1;
208         while (reason < EmergencyCallDiagnosticLogger.REPORT_REASON_RANGE_END) {
209             List<Integer> ctypes = EmergencyCallDiagnosticLogger.getDataCollectionTypes(reason);
210             assertNotNull(ctypes);
211             Set<Integer> ctypesSet = new HashSet<>(ctypes);
212 
213             //assert that list is not empty
214             assertNotEquals(0, ctypes.size());
215 
216             //assert no repeated values
217             assertEquals(ctypes.size(), ctypesSet.size());
218 
219             //if bugreport type is present, that should be the only collection type
220             if (ctypesSet.contains(EmergencyCallDiagnosticLogger.COLLECTION_TYPE_BUGREPORT)) {
221                 assertEquals(1, ctypes.size());
222             }
223             reason++;
224         }
225     }
226 
227 
228     /**
229      * Test emergency call reported stuck
230      */
231     @Test
testStuckEmergencyCall()232     public void testStuckEmergencyCall() {
233         Call call = createCall(true, Call.CALL_DIRECTION_OUTGOING);
234         mEmergencyCallDiagnosticLogger.onCallAdded(call);
235         mEmergencyCallDiagnosticLogger.reportStuckCall(call);
236 
237         //for stuck calls, we should always be persisting some data
238         ArgumentCaptor<EmergencyCallDiagnosticParams> captor =
239                 ArgumentCaptor.forClass(EmergencyCallDiagnosticParams.class);
240         verify(mTm, times(1)).persistEmergencyCallDiagnosticData(eq(DROP_BOX_TAG),
241                 captor.capture());
242         EmergencyCallDiagnosticParams dp = captor.getValue();
243 
244         assertNotNull(dp);
245         assertTrue(
246                 dp.isLogcatCollectionEnabled() || dp.isTelecomDumpSysCollectionEnabled()
247                         || dp.isTelephonyDumpSysCollectionEnabled());
248 
249         //tracking should end
250         assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
251     }
252 
253     @Test
testEmergencyCallNeverWentActiveWithNonLocalDisconnectCause()254     public void testEmergencyCallNeverWentActiveWithNonLocalDisconnectCause() {
255         Call call = createCall(true, Call.CALL_DIRECTION_OUTGOING);
256         mEmergencyCallDiagnosticLogger.onCallAdded(call);
257 
258         //call is tracked
259         assertEquals(1, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
260 
261         call.setDisconnectCause(new DisconnectCause(DisconnectCause.REJECTED));
262         mEmergencyCallDiagnosticLogger.onCallRemoved(call);
263 
264         //for non-local disconnect of non-active call,  we should always be persisting some data
265         ArgumentCaptor<TelephonyManager.EmergencyCallDiagnosticParams> captor =
266                 ArgumentCaptor.forClass(
267                         TelephonyManager.EmergencyCallDiagnosticParams.class);
268         verify(mTm, times(1)).persistEmergencyCallDiagnosticData(eq(DROP_BOX_TAG),
269                 captor.capture());
270         TelephonyManager.EmergencyCallDiagnosticParams dp = captor.getValue();
271 
272         assertNotNull(dp);
273         assertTrue(
274                 dp.isLogcatCollectionEnabled() || dp.isTelecomDumpSysCollectionEnabled()
275                         || dp.isTelephonyDumpSysCollectionEnabled());
276 
277         //tracking should end
278         assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
279     }
280 
281     @Test
testEmergencyCallWentActiveForLongDuration_shouldNotCollectDiagnostics()282     public void testEmergencyCallWentActiveForLongDuration_shouldNotCollectDiagnostics()
283             throws Exception {
284         Call call = createCall(true, Call.CALL_DIRECTION_OUTGOING);
285         mEmergencyCallDiagnosticLogger.onCallAdded(call);
286 
287         //call went active
288         mEmergencyCallDiagnosticLogger.onCallStateChanged(call, CallState.DIALING,
289                 CallState.ACTIVE);
290 
291         //return large value for time when call is disconnected
292         when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis() + 10000L);
293 
294         call.setDisconnectCause(new DisconnectCause(DisconnectCause.ERROR));
295         mEmergencyCallDiagnosticLogger.onCallRemoved(call);
296 
297         //no diagnostic data should be persisted
298         verify(mTm, never()).persistEmergencyCallDiagnosticData(eq(DROP_BOX_TAG),
299                 any());
300 
301         //tracking should end
302         assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
303     }
304 
305 }
306