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