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.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 20 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 21 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO; 22 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT; 23 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST; 24 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST; 25 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertNull; 30 import static org.junit.Assert.assertTrue; 31 import static org.mockito.Mockito.anyInt; 32 import static org.mockito.Mockito.anyString; 33 import static org.mockito.Mockito.doReturn; 34 import static org.mockito.Mockito.eq; 35 import static org.mockito.Mockito.inOrder; 36 import static org.mockito.Mockito.times; 37 38 import android.annotation.Nullable; 39 import android.content.Context; 40 import android.telephony.DisconnectCause; 41 import android.telephony.TelephonyManager; 42 import android.telephony.ims.ImsReasonInfo; 43 import android.test.suitebuilder.annotation.SmallTest; 44 45 import com.android.internal.telephony.TelephonyTest; 46 import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms; 47 import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage; 48 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; 49 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec; 50 import com.android.internal.telephony.protobuf.nano.MessageNano; 51 52 import org.junit.After; 53 import org.junit.Before; 54 import org.junit.Rule; 55 import org.junit.Test; 56 import org.junit.rules.TemporaryFolder; 57 import org.mockito.ArgumentCaptor; 58 import org.mockito.InOrder; 59 import org.mockito.Mock; 60 61 import java.io.File; 62 import java.io.FileOutputStream; 63 import java.nio.charset.StandardCharsets; 64 import java.util.Arrays; 65 import java.util.Comparator; 66 67 public class PersistAtomsStorageTest extends TelephonyTest { 68 private static final String TEST_FILE = "PersistAtomsStorageTest.pb"; 69 private static final int MAX_NUM_CALL_SESSIONS = 50; 70 private static final long START_TIME_MILLIS = 2000L; 71 private static final int CARRIER1_ID = 1; 72 private static final int CARRIER2_ID = 1187; 73 private static final int CARRIER3_ID = 1435; 74 75 @Mock private FileOutputStream mTestFileOutputStream; 76 77 @Rule public TemporaryFolder mFolder = new TemporaryFolder(); 78 79 private File mTestFile; 80 81 // call with SRVCC 82 private VoiceCallSession mCall1Proto; 83 84 // call held after another incoming call, ended before the other call 85 private VoiceCallSession mCall2Proto; 86 private VoiceCallSession mCall3Proto; 87 88 // failed call 89 private VoiceCallSession mCall4Proto; 90 91 private RawVoiceCallRatUsage mCarrier1LteUsageProto; 92 private RawVoiceCallRatUsage mCarrier1UmtsUsageProto; 93 private RawVoiceCallRatUsage mCarrier2LteUsageProto; 94 private RawVoiceCallRatUsage mCarrier3LteUsageProto; 95 private RawVoiceCallRatUsage mCarrier3GsmUsageProto; 96 97 private VoiceCallSession[] mVoiceCallSessions; 98 private RawVoiceCallRatUsage[] mVoiceCallRatUsages; 99 makeTestData()100 private void makeTestData() { 101 // MO call with SRVCC (LTE to UMTS) 102 mCall1Proto = new VoiceCallSession(); 103 mCall1Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 104 mCall1Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 105 mCall1Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO; 106 mCall1Proto.setupDuration = 107 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST; 108 mCall1Proto.setupFailed = false; 109 mCall1Proto.disconnectReasonCode = DisconnectCause.LOCAL; 110 mCall1Proto.disconnectExtraCode = 0; 111 mCall1Proto.disconnectExtraMessage = ""; 112 mCall1Proto.ratAtStart = TelephonyManager.NETWORK_TYPE_LTE; 113 mCall1Proto.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS; 114 mCall1Proto.ratSwitchCount = 1L; 115 mCall1Proto.codecBitmask = 116 (1 << AudioCodec.AUDIO_CODEC_EVS_SWB) | (1 << AudioCodec.AUDIO_CODEC_AMR); 117 mCall1Proto.concurrentCallCountAtStart = 0; 118 mCall1Proto.concurrentCallCountAtEnd = 0; 119 mCall1Proto.simSlotIndex = 0; 120 mCall1Proto.isMultiSim = false; 121 mCall1Proto.isEsim = false; 122 mCall1Proto.carrierId = CARRIER1_ID; 123 mCall1Proto.srvccCompleted = true; 124 mCall1Proto.srvccFailureCount = 0L; 125 mCall1Proto.srvccCancellationCount = 0L; 126 mCall1Proto.rttEnabled = false; 127 mCall1Proto.isEmergency = false; 128 mCall1Proto.isRoaming = false; 129 130 // VoLTE MT call on DSDS/eSIM, hanged up by remote 131 // concurrent with mCall3Proto, started first and ended first 132 mCall2Proto = new VoiceCallSession(); 133 mCall2Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 134 mCall2Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 135 mCall2Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT; 136 mCall2Proto.setupDuration = 137 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST; 138 mCall2Proto.setupFailed = false; 139 mCall2Proto.disconnectReasonCode = ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE; 140 mCall2Proto.disconnectExtraCode = 0; 141 mCall2Proto.disconnectExtraMessage = "normal call clearing"; 142 mCall2Proto.ratAtStart = TelephonyManager.NETWORK_TYPE_LTE; 143 mCall2Proto.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE; 144 mCall2Proto.ratSwitchCount = 0L; 145 mCall2Proto.codecBitmask = (1 << AudioCodec.AUDIO_CODEC_EVS_SWB); 146 mCall2Proto.concurrentCallCountAtStart = 0; 147 mCall2Proto.concurrentCallCountAtEnd = 1; 148 mCall2Proto.simSlotIndex = 1; 149 mCall2Proto.isMultiSim = true; 150 mCall2Proto.isEsim = true; 151 mCall2Proto.carrierId = CARRIER2_ID; 152 mCall2Proto.srvccCompleted = false; 153 mCall2Proto.srvccFailureCount = 0L; 154 mCall2Proto.srvccCancellationCount = 0L; 155 mCall2Proto.rttEnabled = false; 156 mCall2Proto.isEmergency = false; 157 mCall2Proto.isRoaming = false; 158 159 // VoLTE MT call on DSDS/eSIM, hanged up by local, with RTT 160 // concurrent with mCall2Proto, started last and ended last 161 mCall3Proto = new VoiceCallSession(); 162 mCall3Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 163 mCall3Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 164 mCall3Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT; 165 mCall3Proto.setupDuration = 166 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST; 167 mCall3Proto.setupFailed = false; 168 mCall3Proto.disconnectReasonCode = ImsReasonInfo.CODE_USER_TERMINATED; 169 mCall3Proto.disconnectExtraCode = 0; 170 mCall3Proto.disconnectExtraMessage = "normal call clearing"; 171 mCall3Proto.ratAtStart = TelephonyManager.NETWORK_TYPE_LTE; 172 mCall3Proto.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE; 173 mCall3Proto.ratSwitchCount = 0L; 174 mCall3Proto.codecBitmask = (1 << AudioCodec.AUDIO_CODEC_EVS_SWB); 175 mCall3Proto.concurrentCallCountAtStart = 1; 176 mCall3Proto.concurrentCallCountAtEnd = 0; 177 mCall3Proto.simSlotIndex = 1; 178 mCall3Proto.isMultiSim = true; 179 mCall3Proto.isEsim = true; 180 mCall3Proto.carrierId = CARRIER2_ID; 181 mCall3Proto.srvccCompleted = false; 182 mCall3Proto.srvccFailureCount = 0L; 183 mCall3Proto.srvccCancellationCount = 0L; 184 mCall3Proto.rttEnabled = true; 185 mCall3Proto.isEmergency = false; 186 mCall3Proto.isRoaming = false; 187 188 // CS MO call while camped on LTE 189 mCall4Proto = new VoiceCallSession(); 190 mCall4Proto.bearerAtStart = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 191 mCall4Proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 192 mCall4Proto.direction = VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO; 193 mCall4Proto.setupDuration = 194 VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW; 195 mCall4Proto.setupFailed = true; 196 mCall4Proto.disconnectReasonCode = DisconnectCause.NORMAL; 197 mCall4Proto.disconnectExtraCode = 0; 198 mCall4Proto.disconnectExtraMessage = ""; 199 mCall4Proto.ratAtStart = TelephonyManager.NETWORK_TYPE_LTE; 200 mCall4Proto.ratAtEnd = TelephonyManager.NETWORK_TYPE_GSM; 201 mCall4Proto.ratSwitchCount = 1L; 202 mCall4Proto.codecBitmask = (1 << AudioCodec.AUDIO_CODEC_AMR); 203 mCall4Proto.concurrentCallCountAtStart = 0; 204 mCall4Proto.concurrentCallCountAtEnd = 0; 205 mCall4Proto.simSlotIndex = 0; 206 mCall4Proto.isMultiSim = true; 207 mCall4Proto.isEsim = false; 208 mCall4Proto.carrierId = CARRIER3_ID; 209 mCall4Proto.srvccCompleted = false; 210 mCall4Proto.srvccFailureCount = 0L; 211 mCall4Proto.srvccCancellationCount = 0L; 212 mCall4Proto.rttEnabled = false; 213 mCall4Proto.isEmergency = false; 214 mCall4Proto.isRoaming = true; 215 216 mCarrier1LteUsageProto = new RawVoiceCallRatUsage(); 217 mCarrier1LteUsageProto.carrierId = CARRIER1_ID; 218 mCarrier1LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE; 219 mCarrier1LteUsageProto.callCount = 1L; 220 mCarrier1LteUsageProto.totalDurationMillis = 8000L; 221 222 mCarrier1UmtsUsageProto = new RawVoiceCallRatUsage(); 223 mCarrier1UmtsUsageProto.carrierId = CARRIER1_ID; 224 mCarrier1UmtsUsageProto.rat = TelephonyManager.NETWORK_TYPE_UMTS; 225 mCarrier1UmtsUsageProto.callCount = 1L; 226 mCarrier1UmtsUsageProto.totalDurationMillis = 6000L; 227 228 mCarrier2LteUsageProto = new RawVoiceCallRatUsage(); 229 mCarrier2LteUsageProto.carrierId = CARRIER2_ID; 230 mCarrier2LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE; 231 mCarrier2LteUsageProto.callCount = 2L; 232 mCarrier2LteUsageProto.totalDurationMillis = 20000L; 233 234 mCarrier3LteUsageProto = new RawVoiceCallRatUsage(); 235 mCarrier3LteUsageProto.carrierId = CARRIER3_ID; 236 mCarrier3LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE; 237 mCarrier3LteUsageProto.callCount = 1L; 238 mCarrier3LteUsageProto.totalDurationMillis = 1000L; 239 240 mCarrier3GsmUsageProto = new RawVoiceCallRatUsage(); 241 mCarrier3GsmUsageProto.carrierId = CARRIER3_ID; 242 mCarrier3GsmUsageProto.rat = TelephonyManager.NETWORK_TYPE_GSM; 243 mCarrier3GsmUsageProto.callCount = 1L; 244 mCarrier3GsmUsageProto.totalDurationMillis = 100000L; 245 246 mVoiceCallRatUsages = 247 new RawVoiceCallRatUsage[] { 248 mCarrier1UmtsUsageProto, 249 mCarrier1LteUsageProto, 250 mCarrier2LteUsageProto, 251 mCarrier3LteUsageProto, 252 mCarrier3GsmUsageProto 253 }; 254 mVoiceCallSessions = 255 new VoiceCallSession[] {mCall1Proto, mCall2Proto, mCall3Proto, mCall4Proto}; 256 } 257 258 private static class TestablePersistAtomsStorage extends PersistAtomsStorage { 259 private long mTimeMillis = START_TIME_MILLIS; 260 TestablePersistAtomsStorage(Context context)261 TestablePersistAtomsStorage(Context context) { 262 super(context); 263 } 264 265 @Override getWallTimeMillis()266 protected long getWallTimeMillis() { 267 // NOTE: super class constructor will be executed before private field is set, which 268 // gives the wrong start time (mTimeMillis will have its default value of 0L) 269 return mTimeMillis == 0L ? START_TIME_MILLIS : mTimeMillis; 270 } 271 setTimeMillis(long timeMillis)272 private void setTimeMillis(long timeMillis) { 273 mTimeMillis = timeMillis; 274 } 275 incTimeMillis(long timeMillis)276 private void incTimeMillis(long timeMillis) { 277 mTimeMillis += timeMillis; 278 } 279 getAtomsProto()280 private PersistAtoms getAtomsProto() { 281 // NOTE: not guarded by mLock as usual, should be fine since the test is single-threaded 282 return mAtoms; 283 } 284 } 285 286 private TestablePersistAtomsStorage mPersistAtomsStorage; 287 288 private static final Comparator<MessageNano> sProtoComparator = 289 new Comparator<>() { 290 @Override 291 public int compare(MessageNano o1, MessageNano o2) { 292 if (o1 == o2) { 293 return 0; 294 } 295 if (o1 == null) { 296 return -1; 297 } 298 if (o2 == null) { 299 return 1; 300 } 301 assertEquals(o1.getClass(), o2.getClass()); 302 return o1.toString().compareTo(o2.toString()); 303 } 304 }; 305 306 @Before setUp()307 public void setUp() throws Exception { 308 super.setUp(getClass().getSimpleName()); 309 makeTestData(); 310 311 // by default, test loading with real file IO and saving with mocks 312 mTestFile = mFolder.newFile(TEST_FILE); 313 doReturn(mTestFileOutputStream).when(mContext).openFileOutput(anyString(), anyInt()); 314 doReturn(mTestFile).when(mContext).getFileStreamPath(anyString()); 315 } 316 317 @After tearDown()318 public void tearDown() throws Exception { 319 mTestFile.delete(); 320 super.tearDown(); 321 } 322 323 @Test 324 @SmallTest loadAtoms_fileNotExist()325 public void loadAtoms_fileNotExist() throws Exception { 326 mTestFile.delete(); 327 328 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 329 mPersistAtomsStorage.incTimeMillis(100L); 330 331 // no exception should be thrown, storage should be empty, pull time should be start time 332 assertEquals( 333 START_TIME_MILLIS, 334 mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis); 335 assertEquals( 336 START_TIME_MILLIS, 337 mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis); 338 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 339 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 340 assertNotNull(voiceCallRatUsage); 341 assertEquals(0, voiceCallRatUsage.length); 342 assertNotNull(voiceCallSession); 343 assertEquals(0, voiceCallSession.length); 344 } 345 346 @Test 347 @SmallTest loadAtoms_unreadable()348 public void loadAtoms_unreadable() throws Exception { 349 createEmptyTestFile(); 350 mTestFile.setReadable(false); 351 352 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 353 mPersistAtomsStorage.incTimeMillis(100L); 354 355 // no exception should be thrown, storage should be empty, pull time should be start time 356 assertEquals( 357 START_TIME_MILLIS, 358 mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis); 359 assertEquals( 360 START_TIME_MILLIS, 361 mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis); 362 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 363 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 364 assertNotNull(voiceCallRatUsage); 365 assertEquals(0, voiceCallRatUsage.length); 366 assertNotNull(voiceCallSession); 367 assertEquals(0, voiceCallSession.length); 368 } 369 370 @Test 371 @SmallTest loadAtoms_emptyProto()372 public void loadAtoms_emptyProto() throws Exception { 373 createEmptyTestFile(); 374 375 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 376 mPersistAtomsStorage.incTimeMillis(100L); 377 378 // no exception should be thrown, storage should be empty, pull time should be start time 379 assertEquals( 380 START_TIME_MILLIS, 381 mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis); 382 assertEquals( 383 START_TIME_MILLIS, 384 mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis); 385 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 386 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 387 assertNotNull(voiceCallRatUsage); 388 assertEquals(0, voiceCallRatUsage.length); 389 assertNotNull(voiceCallSession); 390 assertEquals(0, voiceCallSession.length); 391 } 392 393 @Test 394 @SmallTest loadAtoms_malformedFile()395 public void loadAtoms_malformedFile() throws Exception { 396 FileOutputStream stream = new FileOutputStream(mTestFile); 397 stream.write("This is not a proto file.".getBytes(StandardCharsets.UTF_8)); 398 stream.close(); 399 400 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 401 mPersistAtomsStorage.incTimeMillis(100L); 402 403 // no exception should be thrown, storage should be empty, pull time should be start time 404 assertEquals( 405 START_TIME_MILLIS, 406 mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis); 407 assertEquals( 408 START_TIME_MILLIS, 409 mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis); 410 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 411 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 412 assertNotNull(voiceCallRatUsage); 413 assertEquals(0, voiceCallRatUsage.length); 414 assertNotNull(voiceCallSession); 415 assertEquals(0, voiceCallSession.length); 416 } 417 418 @Test 419 @SmallTest loadAtoms_pullTimeMissing()420 public void loadAtoms_pullTimeMissing() throws Exception { 421 createTestFile(0L); 422 423 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 424 mPersistAtomsStorage.incTimeMillis(100L); 425 426 // no exception should be thrown, storage should be match, pull time should be start time 427 assertEquals( 428 START_TIME_MILLIS, 429 mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis); 430 assertEquals( 431 START_TIME_MILLIS, 432 mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis); 433 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 434 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 435 assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage); 436 assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession); 437 } 438 439 @Test 440 @SmallTest loadAtoms_validContents()441 public void loadAtoms_validContents() throws Exception { 442 createTestFile(100L); 443 444 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 445 446 // no exception should be thrown, storage and pull time should match 447 assertEquals( 448 100L, mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis); 449 assertEquals( 450 100L, mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis); 451 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 452 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 453 assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage); 454 assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession); 455 } 456 457 @Test 458 @SmallTest addVoiceCallSession_emptyProto()459 public void addVoiceCallSession_emptyProto() throws Exception { 460 createEmptyTestFile(); 461 462 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 463 mPersistAtomsStorage.addVoiceCallSession(mCall1Proto); 464 mPersistAtomsStorage.incTimeMillis(100L); 465 466 // call should be added successfully, there should be no RAT usage, changes should be saved 467 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 468 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 469 assertNotNull(voiceCallRatUsage); 470 assertEquals(0, voiceCallRatUsage.length); 471 assertProtoArrayEquals(new VoiceCallSession[] {mCall1Proto}, voiceCallSession); 472 InOrder inOrder = inOrder(mTestFileOutputStream); 473 inOrder.verify(mTestFileOutputStream, times(1)) 474 .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto()))); 475 inOrder.verify(mTestFileOutputStream, times(1)).close(); 476 inOrder.verifyNoMoreInteractions(); 477 } 478 479 @Test 480 @SmallTest addVoiceCallSession_withExistingCalls()481 public void addVoiceCallSession_withExistingCalls() throws Exception { 482 createTestFile(100L); 483 484 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 485 mPersistAtomsStorage.addVoiceCallSession(mCall1Proto); 486 mPersistAtomsStorage.incTimeMillis(100L); 487 488 // call should be added successfully, RAT usages should not change, changes should be saved 489 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 490 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 491 assertNotNull(voiceCallRatUsage); 492 assertEquals(mVoiceCallRatUsages.length, voiceCallRatUsage.length); 493 assertNotNull(voiceCallSession); 494 // call lists are randomized, but sorted version should be identical 495 VoiceCallSession[] expectedVoiceCallSessions = 496 new VoiceCallSession[] { 497 mCall1Proto, mCall1Proto, mCall2Proto, mCall3Proto, mCall4Proto 498 }; 499 Arrays.sort(expectedVoiceCallSessions, sProtoComparator); 500 Arrays.sort(voiceCallSession, sProtoComparator); 501 assertProtoArrayEquals(expectedVoiceCallSessions, voiceCallSession); 502 InOrder inOrder = inOrder(mTestFileOutputStream); 503 inOrder.verify(mTestFileOutputStream, times(1)) 504 .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto()))); 505 inOrder.verify(mTestFileOutputStream, times(1)).close(); 506 inOrder.verifyNoMoreInteractions(); 507 } 508 509 @Test 510 @SmallTest addVoiceCallSession_tooManyCalls()511 public void addVoiceCallSession_tooManyCalls() throws Exception { 512 createEmptyTestFile(); 513 514 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 515 addRepeatedCalls(mPersistAtomsStorage, mCall1Proto, 50); 516 mPersistAtomsStorage.addVoiceCallSession(mCall2Proto); 517 mPersistAtomsStorage.incTimeMillis(100L); 518 519 // one previous call should be evicted, the new call should be added 520 VoiceCallSession[] calls = mPersistAtomsStorage.getVoiceCallSessions(0L); 521 assertHasCall(calls, mCall1Proto, 49); 522 assertHasCall(calls, mCall2Proto, 1); 523 } 524 525 @Test 526 @SmallTest addVoiceCallRatUsage_emptyProto()527 public void addVoiceCallRatUsage_emptyProto() throws Exception { 528 createEmptyTestFile(); 529 VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(mVoiceCallRatUsages); 530 531 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 532 mPersistAtomsStorage.addVoiceCallRatUsage(ratTracker); 533 mPersistAtomsStorage.incTimeMillis(100L); 534 535 // RAT should be added successfully, calls should not change, changes should be saved 536 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 537 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 538 RawVoiceCallRatUsage[] expectedVoiceCallRatUsage = mVoiceCallRatUsages.clone(); 539 Arrays.sort(expectedVoiceCallRatUsage, sProtoComparator); 540 Arrays.sort(voiceCallRatUsage, sProtoComparator); 541 assertProtoArrayEquals(expectedVoiceCallRatUsage, voiceCallRatUsage); 542 assertNotNull(voiceCallSession); 543 assertEquals(0, voiceCallSession.length); 544 InOrder inOrder = inOrder(mTestFileOutputStream); 545 inOrder.verify(mTestFileOutputStream, times(1)) 546 .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto()))); 547 inOrder.verify(mTestFileOutputStream, times(1)).close(); 548 inOrder.verifyNoMoreInteractions(); 549 } 550 551 @Test 552 @SmallTest addVoiceCallRatUsage_withExistingUsages()553 public void addVoiceCallRatUsage_withExistingUsages() throws Exception { 554 createTestFile(100L); 555 VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(mVoiceCallRatUsages); 556 557 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 558 mPersistAtomsStorage.addVoiceCallRatUsage(ratTracker); 559 mPersistAtomsStorage.incTimeMillis(100L); 560 561 // RAT should be added successfully, calls should not change, changes should be saved 562 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 563 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 564 // call count and duration should become doubled since mVoiceCallRatUsages applied through 565 // both file and addVoiceCallRatUsage() 566 RawVoiceCallRatUsage[] expectedVoiceCallRatUsage = 567 multiplyVoiceCallRatUsage(mVoiceCallRatUsages, 2); 568 Arrays.sort(expectedVoiceCallRatUsage, sProtoComparator); 569 Arrays.sort(voiceCallRatUsage, sProtoComparator); 570 assertProtoArrayEquals(expectedVoiceCallRatUsage, voiceCallRatUsage); 571 assertNotNull(voiceCallSession); 572 assertEquals(mVoiceCallSessions.length, voiceCallSession.length); 573 InOrder inOrder = inOrder(mTestFileOutputStream); 574 inOrder.verify(mTestFileOutputStream, times(1)) 575 .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto()))); 576 inOrder.verify(mTestFileOutputStream, times(1)).close(); 577 inOrder.verifyNoMoreInteractions(); 578 } 579 580 @Test 581 @SmallTest addVoiceCallRatUsage_empty()582 public void addVoiceCallRatUsage_empty() throws Exception { 583 createEmptyTestFile(); 584 VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(new RawVoiceCallRatUsage[0]); 585 586 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 587 mPersistAtomsStorage.addVoiceCallRatUsage(ratTracker); 588 mPersistAtomsStorage.incTimeMillis(100L); 589 590 // RAT should be added successfully, calls should not change 591 // in this case it does not necessarily need to save 592 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L); 593 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L); 594 assertNotNull(voiceCallRatUsage); 595 assertEquals(0, voiceCallRatUsage.length); 596 assertNotNull(voiceCallSession); 597 assertEquals(0, voiceCallSession.length); 598 } 599 600 @Test 601 @SmallTest getVoiceCallRatUsages_tooFrequent()602 public void getVoiceCallRatUsages_tooFrequent() throws Exception { 603 createTestFile(START_TIME_MILLIS); 604 605 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 606 mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum 607 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(100L); 608 609 // should be denied 610 assertNull(voiceCallRatUsage); 611 } 612 613 @Test 614 @SmallTest getVoiceCallRatUsages_withSavedAtoms()615 public void getVoiceCallRatUsages_withSavedAtoms() throws Exception { 616 createTestFile(START_TIME_MILLIS); 617 618 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 619 mPersistAtomsStorage.incTimeMillis(100L); 620 RawVoiceCallRatUsage[] voiceCallRatUsage1 = mPersistAtomsStorage.getVoiceCallRatUsages(50L); 621 mPersistAtomsStorage.incTimeMillis(100L); 622 RawVoiceCallRatUsage[] voiceCallRatUsage2 = mPersistAtomsStorage.getVoiceCallRatUsages(50L); 623 long voiceCallSessionPullTimestampMillis = 624 mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis; 625 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(50L); 626 627 // first set of results should equal to file contents, second should be empty, corresponding 628 // pull timestamp should be updated and saved, other fields should be unaffected 629 assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage1); 630 assertProtoArrayEquals(new RawVoiceCallRatUsage[0], voiceCallRatUsage2); 631 assertEquals( 632 START_TIME_MILLIS + 200L, 633 mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis); 634 assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession); 635 assertEquals(START_TIME_MILLIS, voiceCallSessionPullTimestampMillis); 636 InOrder inOrder = inOrder(mTestFileOutputStream); 637 assertEquals( 638 START_TIME_MILLIS + 100L, 639 getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis); 640 assertEquals( 641 START_TIME_MILLIS + 200L, 642 getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis); 643 assertEquals( 644 START_TIME_MILLIS + 200L, 645 getAtomsWritten(inOrder).voiceCallSessionPullTimestampMillis); 646 inOrder.verifyNoMoreInteractions(); 647 } 648 649 @Test 650 @SmallTest getVoiceCallSessions_tooFrequent()651 public void getVoiceCallSessions_tooFrequent() throws Exception { 652 createTestFile(START_TIME_MILLIS); 653 654 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 655 mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum 656 VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(100L); 657 658 // should be denied 659 assertNull(voiceCallSession); 660 } 661 662 @Test 663 @SmallTest getVoiceCallSessions_withSavedAtoms()664 public void getVoiceCallSessions_withSavedAtoms() throws Exception { 665 createTestFile(START_TIME_MILLIS); 666 667 mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); 668 mPersistAtomsStorage.incTimeMillis(100L); 669 VoiceCallSession[] voiceCallSession1 = mPersistAtomsStorage.getVoiceCallSessions(50L); 670 mPersistAtomsStorage.incTimeMillis(100L); 671 VoiceCallSession[] voiceCallSession2 = mPersistAtomsStorage.getVoiceCallSessions(50L); 672 long voiceCallRatUsagePullTimestampMillis = 673 mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis; 674 RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(50L); 675 676 // first set of results should equal to file contents, second should be empty, corresponding 677 // pull timestamp should be updated and saved, other fields should be unaffected 678 assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession1); 679 assertProtoArrayEquals(new VoiceCallSession[0], voiceCallSession2); 680 assertEquals( 681 START_TIME_MILLIS + 200L, 682 mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis); 683 assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage); 684 assertEquals(START_TIME_MILLIS, voiceCallRatUsagePullTimestampMillis); 685 InOrder inOrder = inOrder(mTestFileOutputStream); 686 assertEquals( 687 START_TIME_MILLIS + 100L, 688 getAtomsWritten(inOrder).voiceCallSessionPullTimestampMillis); 689 assertEquals( 690 START_TIME_MILLIS + 200L, 691 getAtomsWritten(inOrder).voiceCallSessionPullTimestampMillis); 692 assertEquals( 693 START_TIME_MILLIS + 200L, 694 getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis); 695 inOrder.verifyNoMoreInteractions(); 696 } 697 createEmptyTestFile()698 private void createEmptyTestFile() throws Exception { 699 PersistAtoms atoms = new PersistAtoms(); 700 FileOutputStream stream = new FileOutputStream(mTestFile); 701 stream.write(PersistAtoms.toByteArray(atoms)); 702 stream.close(); 703 } 704 createTestFile(long lastPullTimeMillis)705 private void createTestFile(long lastPullTimeMillis) throws Exception { 706 PersistAtoms atoms = new PersistAtoms(); 707 atoms.rawVoiceCallRatUsagePullTimestampMillis = lastPullTimeMillis; 708 atoms.voiceCallSessionPullTimestampMillis = lastPullTimeMillis; 709 atoms.rawVoiceCallRatUsage = mVoiceCallRatUsages; 710 atoms.voiceCallSession = mVoiceCallSessions; 711 FileOutputStream stream = new FileOutputStream(mTestFile); 712 stream.write(PersistAtoms.toByteArray(atoms)); 713 stream.close(); 714 } 715 getAtomsWritten(@ullable InOrder inOrder)716 private PersistAtoms getAtomsWritten(@Nullable InOrder inOrder) throws Exception { 717 if (inOrder == null) { 718 inOrder = inOrder(mTestFileOutputStream); 719 } 720 ArgumentCaptor bytesCaptor = ArgumentCaptor.forClass(Object.class); 721 inOrder.verify(mTestFileOutputStream, times(1)).write((byte[]) bytesCaptor.capture()); 722 PersistAtoms savedAtoms = PersistAtoms.parseFrom((byte[]) bytesCaptor.getValue()); 723 inOrder.verify(mTestFileOutputStream, times(1)).close(); 724 return savedAtoms; 725 } 726 addRepeatedCalls( PersistAtomsStorage storage, VoiceCallSession call, int count)727 private static void addRepeatedCalls( 728 PersistAtomsStorage storage, VoiceCallSession call, int count) { 729 for (int i = 0; i < count; i++) { 730 storage.addVoiceCallSession(call); 731 } 732 } 733 multiplyVoiceCallRatUsage( RawVoiceCallRatUsage[] usages, int times)734 private static RawVoiceCallRatUsage[] multiplyVoiceCallRatUsage( 735 RawVoiceCallRatUsage[] usages, int times) { 736 RawVoiceCallRatUsage[] multipliedUsages = new RawVoiceCallRatUsage[usages.length]; 737 for (int i = 0; i < usages.length; i++) { 738 multipliedUsages[i] = new RawVoiceCallRatUsage(); 739 multipliedUsages[i].carrierId = usages[i].carrierId; 740 multipliedUsages[i].rat = usages[i].rat; 741 multipliedUsages[i].callCount = usages[i].callCount * 2; 742 multipliedUsages[i].totalDurationMillis = usages[i].totalDurationMillis * 2; 743 } 744 return multipliedUsages; 745 } 746 assertProtoArrayEquals(MessageNano[] expected, MessageNano[] actual)747 private static void assertProtoArrayEquals(MessageNano[] expected, MessageNano[] actual) { 748 assertNotNull(expected); 749 assertNotNull(actual); 750 assertEquals(expected.length, actual.length); 751 for (int i = 0; i < expected.length; i++) { 752 assertTrue( 753 String.format( 754 "Message %d of %d differs:\n=== expected ===\n%s=== got ===\n%s", 755 i + 1, expected.length, expected[i].toString(), actual[i].toString()), 756 MessageNano.messageNanoEquals(expected[i], actual[i])); 757 } 758 } 759 assertHasCall( VoiceCallSession[] calls, @Nullable VoiceCallSession expectedCall, int expectedCount)760 private static void assertHasCall( 761 VoiceCallSession[] calls, @Nullable VoiceCallSession expectedCall, int expectedCount) { 762 assertNotNull(calls); 763 int actualCount = 0; 764 for (VoiceCallSession call : calls) { 765 if (call != null && expectedCall != null) { 766 if (MessageNano.messageNanoEquals(call, expectedCall)) { 767 actualCount++; 768 } 769 } 770 } 771 assertEquals(expectedCount, actualCount); 772 } 773 } 774