• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.internal.telephony.metrics;
18 
19 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
20 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
21 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SHORT_CODE_SMS;
22 import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
23 import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
24 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
25 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION;
26 
27 import static com.google.common.truth.Truth.assertThat;
28 
29 import static org.mockito.Mockito.anyLong;
30 import static org.mockito.Mockito.doReturn;
31 import static org.mockito.Mockito.eq;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.times;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.verifyNoMoreInteractions;
36 
37 import android.app.StatsManager;
38 import android.telephony.TelephonyManager;
39 import android.test.suitebuilder.annotation.SmallTest;
40 import android.util.StatsEvent;
41 
42 import com.android.internal.telephony.Phone;
43 import com.android.internal.telephony.PhoneFactory;
44 import com.android.internal.telephony.TelephonyTest;
45 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
46 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
47 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms;
48 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
49 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
50 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
51 import com.android.internal.telephony.uicc.UiccCard;
52 import com.android.internal.telephony.uicc.UiccController;
53 import com.android.internal.telephony.uicc.UiccPort;
54 import com.android.internal.telephony.uicc.UiccSlot;
55 
56 import org.junit.After;
57 import org.junit.Before;
58 import org.junit.Test;
59 
60 import java.util.ArrayList;
61 import java.util.List;
62 
63 public class MetricsCollectorTest extends TelephonyTest {
64     private static final StatsManager.PullAtomMetadata POLICY_PULL_DAILY =
65             new StatsManager.PullAtomMetadata.Builder()
66                     .setCoolDownMillis(24L * 3600L * 1000L)
67                     .build();
68     private static final long MIN_COOLDOWN_MILLIS = 23L * 3600L * 1000L;
69     private static final long MIN_CALLS_PER_BUCKET = 5L;
70 
71     // NOTE: these fields are currently 32-bit internally and padded to 64-bit by TelephonyManager
72     private static final int SUPPORTED_RAF_1 =
73             (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM
74                     | (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE
75                     | (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR;
76     private static final int SUPPORTED_RAF_2 =
77             (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM
78                     | (int) TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT
79                     | (int) TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
80     private static final int SUPPORTED_RAF_BOTH = SUPPORTED_RAF_1 | SUPPORTED_RAF_2;
81 
82     // TODO: if we want to check puller registration by mocking StatsManager, we will have to enable
83     // inline mocking since the StatsManager class is final
84 
85     // b/153195691: we cannot verify the contents of StatsEvent as its getters are marked with @hide
86 
87     // Mocked classes
88     private Phone mSecondPhone;
89     private UiccSlot mPhysicalSlot;
90     private UiccSlot mEsimSlot;
91     private UiccCard mActiveCard;
92     private UiccPort mActivePort;
93     private ServiceStateStats mServiceStateStats;
94 
95     private MetricsCollector mMetricsCollector;
96 
97     @Before
setUp()98     public void setUp() throws Exception {
99         super.setUp(getClass().getSimpleName());
100         mSecondPhone = mock(Phone.class);
101         mPhysicalSlot = mock(UiccSlot.class);
102         mEsimSlot = mock(UiccSlot.class);
103         mActiveCard = mock(UiccCard.class);
104         mActivePort = mock(UiccPort.class);
105         mServiceStateStats = mock(ServiceStateStats.class);
106         mMetricsCollector =
107                 new MetricsCollector(mContext, mPersistAtomsStorage, mDeviceStateHelper);
108         doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
109         doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
110     }
111 
112     @After
tearDown()113     public void tearDown() throws Exception {
114         mMetricsCollector = null;
115         super.tearDown();
116     }
117 
118     @Test
119     @SmallTest
onPullAtom_simSlotState_bothSimPresent()120     public void onPullAtom_simSlotState_bothSimPresent() {
121         // these have been tested extensively in SimSlotStateTest, here we verify atom generation
122         doReturn(true).when(mPhysicalSlot).isActive();
123         doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot).getCardState();
124         doReturn(false).when(mPhysicalSlot).isEuicc();
125         doReturn(true).when(mEsimSlot).isActive();
126         doReturn(CardState.CARDSTATE_PRESENT).when(mEsimSlot).getCardState();
127         doReturn(true).when(mEsimSlot).isEuicc();
128         doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
129         doReturn(4).when(mActivePort).getNumApplications();
130         doReturn(new UiccPort[] {mActivePort}).when(mActiveCard).getUiccPortList();
131         doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
132         doReturn(mPhysicalSlot).when(mUiccController).getUiccSlot(eq(0));
133         doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
134         StatsEvent expectedAtom =
135                 StatsEvent.newBuilder()
136                         .setAtomId(SIM_SLOT_STATE)
137                         .writeInt(2)
138                         .writeInt(2)
139                         .writeInt(1)
140                         .build();
141         List<StatsEvent> actualAtoms = new ArrayList<>();
142 
143         int result = mMetricsCollector.onPullAtom(SIM_SLOT_STATE, actualAtoms);
144 
145         assertThat(actualAtoms).hasSize(1);
146         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
147         // TODO(b/153196254): verify atom contents
148     }
149 
150     @Test
151     @SmallTest
onPullAtom_simSlotState_beforeUiccControllerReady()152     public void onPullAtom_simSlotState_beforeUiccControllerReady() throws Exception {
153         // there is a slight chance that MetricsCollector gets pulled after registration while
154         // PhoneFactory havne't made UiccController yet, RuntimeException will be thrown
155         replaceInstance(UiccController.class, "mInstance", mUiccController, null);
156         List<StatsEvent> actualAtoms = new ArrayList<>();
157 
158         int result = mMetricsCollector.onPullAtom(SIM_SLOT_STATE, actualAtoms);
159 
160         assertThat(actualAtoms).hasSize(0);
161         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
162     }
163 
164     @Test
165     @SmallTest
onPullAtom_supportedRadioAccessFamily_singlePhone()166     public void onPullAtom_supportedRadioAccessFamily_singlePhone() {
167         doReturn(SUPPORTED_RAF_1).when(mPhone).getRadioAccessFamily();
168         StatsEvent expectedAtom =
169                 StatsEvent.newBuilder()
170                         .setAtomId(SUPPORTED_RADIO_ACCESS_FAMILY)
171                         .writeLong(SUPPORTED_RAF_1)
172                         .build();
173         List<StatsEvent> actualAtoms = new ArrayList<>();
174 
175         int result = mMetricsCollector.onPullAtom(SUPPORTED_RADIO_ACCESS_FAMILY, actualAtoms);
176 
177         assertThat(actualAtoms).hasSize(1);
178         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
179         // TODO(b/153196254): verify atom contents
180     }
181 
182     @Test
183     @SmallTest
onPullAtom_supportedRadioAccessFamily_dualPhones()184     public void onPullAtom_supportedRadioAccessFamily_dualPhones() {
185         doReturn(SUPPORTED_RAF_1).when(mPhone).getRadioAccessFamily();
186         doReturn(SUPPORTED_RAF_2).when(mSecondPhone).getRadioAccessFamily();
187         mPhones = new Phone[] {mPhone, mSecondPhone};
188         StatsEvent expectedAtom =
189                 StatsEvent.newBuilder()
190                         .setAtomId(SUPPORTED_RADIO_ACCESS_FAMILY)
191                         .writeLong(SUPPORTED_RAF_BOTH)
192                         .build();
193         List<StatsEvent> actualAtoms = new ArrayList<>();
194 
195         int result = mMetricsCollector.onPullAtom(SUPPORTED_RADIO_ACCESS_FAMILY, actualAtoms);
196 
197         assertThat(actualAtoms).hasSize(1);
198         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
199         // TODO(b/153196254): verify atom contents
200     }
201 
202     @Test
203     @SmallTest
onPullAtom_supportedRadioAccessFamily_dualPhonesWithUnknownRaf()204     public void onPullAtom_supportedRadioAccessFamily_dualPhonesWithUnknownRaf() {
205         doReturn(SUPPORTED_RAF_1).when(mPhone).getRadioAccessFamily();
206         doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN)
207                 .when(mSecondPhone)
208                 .getRadioAccessFamily();
209         mPhones = new Phone[] {mPhone, mSecondPhone};
210         StatsEvent expectedAtom =
211                 StatsEvent.newBuilder()
212                         .setAtomId(SUPPORTED_RADIO_ACCESS_FAMILY)
213                         .writeLong(SUPPORTED_RAF_1)
214                         .build();
215         List<StatsEvent> actualAtoms = new ArrayList<>();
216 
217         int result = mMetricsCollector.onPullAtom(SUPPORTED_RADIO_ACCESS_FAMILY, actualAtoms);
218 
219         assertThat(actualAtoms).hasSize(1);
220         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
221         // TODO(b/153196254): verify atom contents
222     }
223 
224     @Test
225     @SmallTest
onPullAtom_supportedRadioAccessFamily_beforePhoneReady()226     public void onPullAtom_supportedRadioAccessFamily_beforePhoneReady() throws Exception {
227         replaceInstance(PhoneFactory.class, "sMadeDefaults", true, false);
228         List<StatsEvent> actualAtoms = new ArrayList<>();
229 
230         int result = mMetricsCollector.onPullAtom(SUPPORTED_RADIO_ACCESS_FAMILY, actualAtoms);
231 
232         assertThat(actualAtoms).hasSize(0);
233         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
234     }
235 
236     @Test
237     @SmallTest
onPullAtom_voiceCallRatUsage_empty()238     public void onPullAtom_voiceCallRatUsage_empty() throws Exception {
239         doReturn(new VoiceCallRatUsage[0])
240                 .when(mPersistAtomsStorage)
241                 .getVoiceCallRatUsages(anyLong());
242         List<StatsEvent> actualAtoms = new ArrayList<>();
243 
244         int result = mMetricsCollector.onPullAtom(VOICE_CALL_RAT_USAGE, actualAtoms);
245 
246         assertThat(actualAtoms).hasSize(0);
247         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
248     }
249 
250     @Test
251     @SmallTest
onPullAtom_voiceCallRatUsage_tooFrequent()252     public void onPullAtom_voiceCallRatUsage_tooFrequent() throws Exception {
253         doReturn(null).when(mPersistAtomsStorage).getVoiceCallRatUsages(anyLong());
254         List<StatsEvent> actualAtoms = new ArrayList<>();
255 
256         int result = mMetricsCollector.onPullAtom(VOICE_CALL_RAT_USAGE, actualAtoms);
257 
258         assertThat(actualAtoms).hasSize(0);
259         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
260         verify(mPersistAtomsStorage, times(1)).getVoiceCallRatUsages(eq(MIN_COOLDOWN_MILLIS));
261         verifyNoMoreInteractions(mPersistAtomsStorage);
262     }
263 
264     @Test
265     @SmallTest
onPullAtom_voiceCallRatUsage_bucketWithTooFewCalls()266     public void onPullAtom_voiceCallRatUsage_bucketWithTooFewCalls() throws Exception {
267         VoiceCallRatUsage usage1 = new VoiceCallRatUsage();
268         usage1.callCount = MIN_CALLS_PER_BUCKET;
269         VoiceCallRatUsage usage2 = new VoiceCallRatUsage();
270         usage2.callCount = MIN_CALLS_PER_BUCKET - 1L;
271         doReturn(new VoiceCallRatUsage[] {usage1, usage1, usage1, usage2})
272                 .when(mPersistAtomsStorage)
273                 .getVoiceCallRatUsages(anyLong());
274         List<StatsEvent> actualAtoms = new ArrayList<>();
275 
276         int result = mMetricsCollector.onPullAtom(VOICE_CALL_RAT_USAGE, actualAtoms);
277 
278         assertThat(actualAtoms).hasSize(3); // usage 2 should be dropped
279         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
280         // TODO(b/153196254): verify atom contents
281     }
282 
283     @Test
284     @SmallTest
onPullAtom_voiceCallSession_empty()285     public void onPullAtom_voiceCallSession_empty() throws Exception {
286         doReturn(new VoiceCallSession[0])
287                 .when(mPersistAtomsStorage)
288                 .getVoiceCallSessions(anyLong());
289         List<StatsEvent> actualAtoms = new ArrayList<>();
290 
291         int result = mMetricsCollector.onPullAtom(VOICE_CALL_SESSION, actualAtoms);
292 
293         assertThat(actualAtoms).hasSize(0);
294         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
295     }
296 
297     @Test
298     @SmallTest
onPullAtom_voiceCallSession_tooFrequent()299     public void onPullAtom_voiceCallSession_tooFrequent() throws Exception {
300         doReturn(null).when(mPersistAtomsStorage).getVoiceCallSessions(anyLong());
301         List<StatsEvent> actualAtoms = new ArrayList<>();
302 
303         int result = mMetricsCollector.onPullAtom(VOICE_CALL_SESSION, actualAtoms);
304 
305         assertThat(actualAtoms).hasSize(0);
306         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
307         verify(mPersistAtomsStorage, times(1)).getVoiceCallSessions(eq(MIN_COOLDOWN_MILLIS));
308         verifyNoMoreInteractions(mPersistAtomsStorage);
309     }
310 
311     @Test
312     @SmallTest
onPullAtom_voiceCallSession_multipleCalls()313     public void onPullAtom_voiceCallSession_multipleCalls() throws Exception {
314         VoiceCallSession call = new VoiceCallSession();
315         doReturn(new VoiceCallSession[] {call, call, call, call})
316                 .when(mPersistAtomsStorage)
317                 .getVoiceCallSessions(anyLong());
318         List<StatsEvent> actualAtoms = new ArrayList<>();
319 
320         int result = mMetricsCollector.onPullAtom(VOICE_CALL_SESSION, actualAtoms);
321 
322         assertThat(actualAtoms).hasSize(4);
323         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
324         // TODO(b/153196254): verify atom contents
325     }
326 
327     @Test
328     @SmallTest
onPullAtom_cellularDataServiceSwitch_empty()329     public void onPullAtom_cellularDataServiceSwitch_empty() throws Exception {
330         doReturn(new CellularDataServiceSwitch[0])
331                 .when(mPersistAtomsStorage)
332                 .getCellularDataServiceSwitches(anyLong());
333         List<StatsEvent> actualAtoms = new ArrayList<>();
334 
335         int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
336 
337         assertThat(actualAtoms).hasSize(0);
338         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
339         // TODO(b/153196254): verify atom contents
340     }
341 
342     @Test
343     @SmallTest
onPullAtom_cellularDataServiceSwitch_tooFrequent()344     public void onPullAtom_cellularDataServiceSwitch_tooFrequent() throws Exception {
345         doReturn(null).when(mPersistAtomsStorage).getCellularDataServiceSwitches(anyLong());
346         List<StatsEvent> actualAtoms = new ArrayList<>();
347 
348         int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
349 
350         assertThat(actualAtoms).hasSize(0);
351         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
352         verify(mPersistAtomsStorage, times(1))
353                 .getCellularDataServiceSwitches(eq(MIN_COOLDOWN_MILLIS));
354         verifyNoMoreInteractions(mPersistAtomsStorage);
355     }
356 
357     @Test
358     @SmallTest
onPullAtom_cellularDataServiceSwitch_multipleSwitches()359     public void onPullAtom_cellularDataServiceSwitch_multipleSwitches() throws Exception {
360         CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
361         doReturn(new CellularDataServiceSwitch[] {serviceSwitch, serviceSwitch, serviceSwitch})
362                 .when(mPersistAtomsStorage)
363                 .getCellularDataServiceSwitches(anyLong());
364         List<StatsEvent> actualAtoms = new ArrayList<>();
365 
366         int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
367 
368         assertThat(actualAtoms).hasSize(3);
369         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
370         // TODO(b/153196254): verify atom contents
371     }
372 
373     @Test
374     @SmallTest
onPullAtom_cellularServiceState_empty()375     public void onPullAtom_cellularServiceState_empty() throws Exception {
376         doReturn(new CellularServiceState[0])
377                 .when(mPersistAtomsStorage)
378                 .getCellularServiceStates(anyLong());
379         List<StatsEvent> actualAtoms = new ArrayList<>();
380 
381         int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
382 
383         assertThat(actualAtoms).hasSize(0);
384         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
385         // TODO(b/153196254): verify atom contents
386     }
387 
388     @Test
389     @SmallTest
onPullAtom_cellularServiceState_tooFrequent()390     public void onPullAtom_cellularServiceState_tooFrequent() throws Exception {
391         doReturn(null).when(mPersistAtomsStorage).getCellularServiceStates(anyLong());
392         List<StatsEvent> actualAtoms = new ArrayList<>();
393 
394         int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
395 
396         assertThat(actualAtoms).hasSize(0);
397         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
398         verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(eq(MIN_COOLDOWN_MILLIS));
399         verifyNoMoreInteractions(mPersistAtomsStorage);
400     }
401 
402     @Test
403     @SmallTest
onPullAtom_cellularServiceState_multipleStates()404     public void onPullAtom_cellularServiceState_multipleStates() throws Exception {
405         CellularServiceState state = new CellularServiceState();
406         doReturn(new CellularServiceState[] {state, state, state})
407                 .when(mPersistAtomsStorage)
408                 .getCellularServiceStates(anyLong());
409         List<StatsEvent> actualAtoms = new ArrayList<>();
410 
411         int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
412 
413         assertThat(actualAtoms).hasSize(3);
414         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
415         // TODO(b/153196254): verify atom contents
416     }
417 
418     @Test
onPullAtom_outgoingShortCodeSms_empty()419     public void onPullAtom_outgoingShortCodeSms_empty() {
420         doReturn(new OutgoingShortCodeSms[0]).when(mPersistAtomsStorage)
421                 .getOutgoingShortCodeSms(anyLong());
422         List<StatsEvent> actualAtoms = new ArrayList<>();
423 
424         int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
425 
426         assertThat(actualAtoms).hasSize(0);
427         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
428     }
429 
430     @Test
onPullAtom_outgoingShortCodeSms_tooFrequent()431     public void onPullAtom_outgoingShortCodeSms_tooFrequent() {
432         doReturn(null).when(mPersistAtomsStorage).getOutgoingShortCodeSms(anyLong());
433         List<StatsEvent> actualAtoms = new ArrayList<>();
434 
435         int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
436 
437         assertThat(actualAtoms).hasSize(0);
438         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
439         verify(mPersistAtomsStorage, times(1))
440                 .getOutgoingShortCodeSms(eq(MIN_COOLDOWN_MILLIS));
441         verifyNoMoreInteractions(mPersistAtomsStorage);
442     }
443 
444     @Test
onPullAtom_outgoingShortCodeSms_multipleSms()445     public void onPullAtom_outgoingShortCodeSms_multipleSms() {
446         OutgoingShortCodeSms outgoingShortCodeSms = new OutgoingShortCodeSms();
447         doReturn(new OutgoingShortCodeSms[] {outgoingShortCodeSms, outgoingShortCodeSms,
448                 outgoingShortCodeSms, outgoingShortCodeSms})
449                 .when(mPersistAtomsStorage)
450                 .getOutgoingShortCodeSms(anyLong());
451         List<StatsEvent> actualAtoms = new ArrayList<>();
452 
453         int result = mMetricsCollector.onPullAtom(OUTGOING_SHORT_CODE_SMS, actualAtoms);
454 
455         assertThat(actualAtoms).hasSize(4);
456         assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
457     }
458 }
459