aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:29:10 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:29:10 +0000
commit5a4c8800b87d448ecb2f703730a2aa05b1f4bb84 (patch)
treee58c4a3fcfb0a8019291e323a7ad1f6599753545
parent2737234323cace5e607386b113962930957e3365 (diff)
parente4b5daf81a97ddb11e15057b6ec753f8d9c13767 (diff)
downloadtelephony-android12-mainline-conscrypt-release.tar.gz
Snap for 7550844 from e4b5daf81a97ddb11e15057b6ec753f8d9c13767 to mainline-conscrypt-releaseandroid-mainline-12.0.0_r8android-mainline-12.0.0_r25android12-mainline-conscrypt-release
Change-Id: Id6a9b17283b07f359bdd38eac87001a19f15c90a
-rw-r--r--Android.bp32
-rw-r--r--TEST_MAPPING3
-rw-r--r--jarjar-rules-shared.txt3
-rw-r--r--proto/Android.bp4
-rw-r--r--proto/src/persist_atoms.proto207
-rw-r--r--proto/src/pin_storage.proto62
-rw-r--r--proto/src/telephony.proto74
-rw-r--r--src/java/com/android/internal/telephony/AppSmsManager.java16
-rw-r--r--src/java/com/android/internal/telephony/BaseCommands.java60
-rw-r--r--src/java/com/android/internal/telephony/BiMap.java78
-rw-r--r--src/java/com/android/internal/telephony/BtSmsInterfaceManager.java16
-rw-r--r--src/java/com/android/internal/telephony/Call.java13
-rw-r--r--src/java/com/android/internal/telephony/CallManager.java73
-rw-r--r--src/java/com/android/internal/telephony/CallTracker.java16
-rw-r--r--src/java/com/android/internal/telephony/CarrierInfoManager.java80
-rw-r--r--src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java249
-rw-r--r--src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java132
-rw-r--r--src/java/com/android/internal/telephony/CarrierResolver.java126
-rw-r--r--src/java/com/android/internal/telephony/CarrierServiceBindHelper.java6
-rw-r--r--src/java/com/android/internal/telephony/CarrierServiceStateTracker.java95
-rw-r--r--src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java74
-rw-r--r--src/java/com/android/internal/telephony/CarrierSignalAgent.java114
-rw-r--r--src/java/com/android/internal/telephony/CarrierSmsUtils.java40
-rw-r--r--src/java/com/android/internal/telephony/CellBroadcastServiceManager.java41
-rw-r--r--src/java/com/android/internal/telephony/CellularNetworkService.java125
-rw-r--r--src/java/com/android/internal/telephony/CellularNetworkValidator.java2
-rw-r--r--src/java/com/android/internal/telephony/CommandException.java18
-rw-r--r--src/java/com/android/internal/telephony/CommandsInterface.java235
-rw-r--r--src/java/com/android/internal/telephony/Connection.java125
-rw-r--r--src/java/com/android/internal/telephony/DefaultPhoneNotifier.java110
-rw-r--r--src/java/com/android/internal/telephony/DeviceStateMonitor.java106
-rw-r--r--src/java/com/android/internal/telephony/DisplayInfoController.java8
-rw-r--r--src/java/com/android/internal/telephony/DriverCall.java1
-rw-r--r--src/java/com/android/internal/telephony/GbaManager.java527
-rw-r--r--src/java/com/android/internal/telephony/GsmCdmaCall.java11
-rwxr-xr-xsrc/java/com/android/internal/telephony/GsmCdmaCallTracker.java125
-rw-r--r--src/java/com/android/internal/telephony/GsmCdmaConnection.java62
-rw-r--r--src/java/com/android/internal/telephony/GsmCdmaPhone.java914
-rw-r--r--src/java/com/android/internal/telephony/IIccPhoneBook.aidl57
-rw-r--r--src/java/com/android/internal/telephony/IccCard.java5
-rw-r--r--src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java219
-rw-r--r--src/java/com/android/internal/telephony/IccProvider.java171
-rw-r--r--src/java/com/android/internal/telephony/IccSmsInterfaceManager.java321
-rw-r--r--src/java/com/android/internal/telephony/ImsSmsDispatcher.java297
-rw-r--r--src/java/com/android/internal/telephony/InboundSmsHandler.java692
-rw-r--r--src/java/com/android/internal/telephony/InboundSmsTracker.java41
-rw-r--r--src/java/com/android/internal/telephony/IntRangeManager.java3
-rw-r--r--src/java/com/android/internal/telephony/LinkCapacityEstimate.java73
-rwxr-xr-xsrc/java/com/android/internal/telephony/LocaleTracker.java21
-rw-r--r--src/java/com/android/internal/telephony/MccTable.java4
-rw-r--r--src/java/com/android/internal/telephony/MultiSimSettingController.java185
-rw-r--r--src/java/com/android/internal/telephony/NetworkRegistrationManager.java5
-rw-r--r--src/java/com/android/internal/telephony/NetworkScanRequestTracker.java12
-rw-r--r--src/java/com/android/internal/telephony/NetworkTypeController.java349
-rw-r--r--src/java/com/android/internal/telephony/NitzData.java47
-rw-r--r--src/java/com/android/internal/telephony/OemHookIndication.java53
-rw-r--r--src/java/com/android/internal/telephony/OemHookResponse.java59
-rw-r--r--src/java/com/android/internal/telephony/Phone.java882
-rw-r--r--src/java/com/android/internal/telephony/PhoneConfigurationManager.java67
-rw-r--r--src/java/com/android/internal/telephony/PhoneFactory.java62
-rw-r--r--src/java/com/android/internal/telephony/PhoneInternalInterface.java149
-rw-r--r--src/java/com/android/internal/telephony/PhoneNotifier.java39
-rw-r--r--src/java/com/android/internal/telephony/PhoneSubInfoController.java69
-rw-r--r--src/java/com/android/internal/telephony/PhoneSwitcher.java391
-rw-r--r--src/java/com/android/internal/telephony/ProxyController.java21
-rw-r--r--src/java/com/android/internal/telephony/RIL.java1577
-rw-r--r--src/java/com/android/internal/telephony/RILRequest.java16
-rw-r--r--src/java/com/android/internal/telephony/RadioCapability.java3
-rw-r--r--src/java/com/android/internal/telephony/RadioConfig.java165
-rw-r--r--src/java/com/android/internal/telephony/RadioConfigResponse.java107
-rw-r--r--src/java/com/android/internal/telephony/RadioIndication.java262
-rw-r--r--src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java185
-rw-r--r--src/java/com/android/internal/telephony/RadioResponse.java832
-rw-r--r--src/java/com/android/internal/telephony/RatRatcheter.java66
-rw-r--r--src/java/com/android/internal/telephony/RetryManager.java169
-rw-r--r--src/java/com/android/internal/telephony/SMSDispatcher.java935
-rwxr-xr-xsrc/java/com/android/internal/telephony/ServiceStateTracker.java1293
-rw-r--r--src/java/com/android/internal/telephony/SlidingWindowEventCounter.java87
-rw-r--r--src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java72
-rw-r--r--src/java/com/android/internal/telephony/SmsController.java38
-rw-r--r--src/java/com/android/internal/telephony/SmsDispatchersController.java432
-rw-r--r--src/java/com/android/internal/telephony/SmsPermissions.java17
-rw-r--r--src/java/com/android/internal/telephony/SmsResponse.java18
-rwxr-xr-xsrc/java/com/android/internal/telephony/SmsStorageMonitor.java3
-rw-r--r--src/java/com/android/internal/telephony/SmsUsageMonitor.java35
-rw-r--r--src/java/com/android/internal/telephony/SubscriptionController.java744
-rw-r--r--src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java256
-rw-r--r--src/java/com/android/internal/telephony/TelephonyCapabilities.java3
-rw-r--r--src/java/com/android/internal/telephony/TelephonyComponentFactory.java31
-rw-r--r--src/java/com/android/internal/telephony/TelephonyFacade.java48
-rw-r--r--src/java/com/android/internal/telephony/TelephonyTester.java64
-rw-r--r--src/java/com/android/internal/telephony/UUSInfo.java7
-rw-r--r--src/java/com/android/internal/telephony/UiccPhoneBookController.java81
-rw-r--r--src/java/com/android/internal/telephony/WakeLockStateMachine.java9
-rwxr-xr-xsrc/java/com/android/internal/telephony/WapPushOverSms.java235
-rwxr-xr-xsrc/java/com/android/internal/telephony/WspTypeDecoder.java9
-rwxr-xr-xsrc/java/com/android/internal/telephony/cat/AppInterface.java3
-rw-r--r--src/java/com/android/internal/telephony/cat/CatCmdMessage.java21
-rw-r--r--src/java/com/android/internal/telephony/cat/CatLog.java5
-rw-r--r--src/java/com/android/internal/telephony/cat/CatService.java17
-rw-r--r--src/java/com/android/internal/telephony/cat/CommandDetails.java13
-rwxr-xr-xsrc/java/com/android/internal/telephony/cat/CommandParams.java17
-rw-r--r--src/java/com/android/internal/telephony/cat/CommandParamsFactory.java9
-rw-r--r--src/java/com/android/internal/telephony/cat/ComprehensionTlv.java9
-rw-r--r--src/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java3
-rw-r--r--src/java/com/android/internal/telephony/cat/Duration.java7
-rw-r--r--src/java/com/android/internal/telephony/cat/IconLoader.java3
-rw-r--r--src/java/com/android/internal/telephony/cat/Menu.java3
-rw-r--r--src/java/com/android/internal/telephony/cat/ResponseData.java5
-rw-r--r--src/java/com/android/internal/telephony/cat/ResultCode.java3
-rw-r--r--src/java/com/android/internal/telephony/cat/ResultException.java3
-rwxr-xr-xsrc/java/com/android/internal/telephony/cat/RilMessageDecoder.java13
-rw-r--r--src/java/com/android/internal/telephony/cat/TextMessage.java7
-rw-r--r--src/java/com/android/internal/telephony/cat/ValueParser.java9
-rw-r--r--src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java35
-rw-r--r--src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java62
-rw-r--r--src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java38
-rw-r--r--src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java3
-rw-r--r--src/java/com/android/internal/telephony/cdma/EriInfo.java12
-rw-r--r--src/java/com/android/internal/telephony/cdnr/BrandOverrideEfData.java7
-rw-r--r--src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java2
-rw-r--r--src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java168
-rw-r--r--src/java/com/android/internal/telephony/cdnr/EfData.java2
-rw-r--r--src/java/com/android/internal/telephony/cdnr/RuimEfData.java2
-rw-r--r--src/java/com/android/internal/telephony/cdnr/UsimEfData.java13
-rw-r--r--src/java/com/android/internal/telephony/d2d/Communicator.java347
-rw-r--r--src/java/com/android/internal/telephony/d2d/DtmfAdapter.java33
-rw-r--r--src/java/com/android/internal/telephony/d2d/DtmfTransport.java624
-rw-r--r--src/java/com/android/internal/telephony/d2d/MessageTypeAndValueHelper.java97
-rw-r--r--src/java/com/android/internal/telephony/d2d/RtpAdapter.java49
-rw-r--r--src/java/com/android/internal/telephony/d2d/RtpTransport.java644
-rw-r--r--src/java/com/android/internal/telephony/d2d/Timeouts.java125
-rw-r--r--src/java/com/android/internal/telephony/d2d/TransportProtocol.java83
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java223
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java1
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/ApnContext.java78
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/CellularDataService.java88
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataConnection.java1577
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java49
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java4
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java89
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java203
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataThrottler.java366
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DcController.java529
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java8
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java221
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DcTracker.java1836
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/LinkBandwidthEstimator.java1169
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java288
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java124
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/TransportManager.java133
-rw-r--r--src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java151
-rw-r--r--src/java/com/android/internal/telephony/euicc/EuiccConnector.java13
-rw-r--r--src/java/com/android/internal/telephony/euicc/EuiccController.java16
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java29
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmMmiCode.java115
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java46
-rw-r--r--src/java/com/android/internal/telephony/gsm/SimTlv.java3
-rw-r--r--src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java30
-rwxr-xr-xsrc/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java21
-rw-r--r--src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java2
-rw-r--r--src/java/com/android/internal/telephony/ims/ImsResolver.java472
-rw-r--r--src/java/com/android/internal/telephony/ims/ImsServiceController.java488
-rw-r--r--src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java60
-rw-r--r--src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java8
-rw-r--r--src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java7
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java3
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java5
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhone.java504
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java79
-rwxr-xr-xsrc/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java701
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java35
-rwxr-xr-xsrc/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java182
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java254
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java5
-rw-r--r--src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java119
-rw-r--r--src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java65
-rw-r--r--src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java241
-rw-r--r--src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java70
-rw-r--r--src/java/com/android/internal/telephony/metrics/ImsStats.java456
-rw-r--r--src/java/com/android/internal/telephony/metrics/MetricsCollector.java441
-rw-r--r--src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java2
-rw-r--r--src/java/com/android/internal/telephony/metrics/ModemRestartStats.java79
-rw-r--r--src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java67
-rw-r--r--src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java730
-rw-r--r--src/java/com/android/internal/telephony/metrics/ServiceStateStats.java244
-rw-r--r--src/java/com/android/internal/telephony/metrics/SimSlotState.java20
-rw-r--r--src/java/com/android/internal/telephony/metrics/SmsStats.java358
-rw-r--r--src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java7
-rw-r--r--src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java385
-rw-r--r--src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java18
-rw-r--r--src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java414
-rw-r--r--src/java/com/android/internal/telephony/nitz/TimeZoneLookupHelper.java6
-rw-r--r--src/java/com/android/internal/telephony/sip/SipCommandInterface.java35
-rwxr-xr-xsrc/java/com/android/internal/telephony/sip/SipPhone.java15
-rw-r--r--src/java/com/android/internal/telephony/uicc/AdnCapacity.aidl19
-rw-r--r--src/java/com/android/internal/telephony/uicc/AdnCapacity.java196
-rw-r--r--src/java/com/android/internal/telephony/uicc/AdnRecord.java171
-rw-r--r--src/java/com/android/internal/telephony/uicc/AdnRecordCache.java23
-rw-r--r--src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java11
-rw-r--r--src/java/com/android/internal/telephony/uicc/CsimFileHandler.java4
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java7
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccCardStatus.java13
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccConstants.java5
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccFileHandler.java35
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccRecords.java153
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java7
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccServiceTable.java5
-rw-r--r--src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java2
-rw-r--r--src/java/com/android/internal/telephony/uicc/IsimRecords.java7
-rw-r--r--src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java44
-rw-r--r--src/java/com/android/internal/telephony/uicc/PinStorage.java1221
-rw-r--r--src/java/com/android/internal/telephony/uicc/ReceivedPhonebookRecords.java69
-rw-r--r--src/java/com/android/internal/telephony/uicc/RuimFileHandler.java4
-rwxr-xr-x[-rw-r--r--]src/java/com/android/internal/telephony/uicc/RuimRecords.java162
-rw-r--r--src/java/com/android/internal/telephony/uicc/SIMRecords.java162
-rw-r--r--src/java/com/android/internal/telephony/uicc/SimPhonebookRecord.java199
-rw-r--r--src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java629
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccCard.java21
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccCardApplication.java54
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java34
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccController.java71
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccPkcs15.java171
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccProfile.java191
-rw-r--r--src/java/com/android/internal/telephony/uicc/UsimServiceTable.java3
-rw-r--r--src/java/com/android/internal/telephony/uicc/VoiceMailConstants.java3
-rw-r--r--src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java6
-rw-r--r--src/java/com/android/internal/telephony/uicc/euicc/Tags.java1
-rw-r--r--src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java175
-rw-r--r--src/java/com/android/internal/telephony/vendor/VendorMultiSimSettingController.java112
-rw-r--r--src/java/com/android/internal/telephony/vendor/VendorPhoneSwitcher.java656
-rw-r--r--src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java94
-rw-r--r--src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java439
-rw-r--r--src/java/com/android/internal/telephony/vendor/VendorSubscriptionInfoUpdater.java138
-rw-r--r--src/java/com/android/internal/telephony/vendor/dataconnection/VendorDataResetEventTracker.java209
-rw-r--r--src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java366
-rw-r--r--testing/Android.bp27
-rw-r--r--testing/AndroidManifest.xml26
-rw-r--r--testing/src/com/android/internal/telephony/testing/CursorSubject.java161
-rw-r--r--testing/src/com/android/internal/telephony/testing/TelephonyAssertions.java51
-rw-r--r--tests/telephonytests/Android.bp13
-rw-r--r--tests/telephonytests/AndroidManifest.xml21
-rw-r--r--tests/telephonytests/AndroidTest.xml2
-rw-r--r--tests/telephonytests/assets/eccdatabin51 -> 51 bytes
-rw-r--r--tests/telephonytests/assets/eccdata_input.txt15
-rw-r--r--tests/telephonytests/assets/eccdata_otabin0 -> 49 bytes
-rw-r--r--tests/telephonytests/assets/eccdata_ota_input.txt11
-rw-r--r--tests/telephonytests/jarjar-rules-tests.txt8
-rw-r--r--tests/telephonytests/src/android/telephony/BinderCacheManagerTest.java123
-rw-r--r--tests/telephonytests/src/android/telephony/SmsMessageTest.java1
-rw-r--r--tests/telephonytests/src/android/telephony/ims/ImsCallSessionListenerTests.java56
-rw-r--r--tests/telephonytests/src/android/telephony/ims/ImsConfigImplTest.java166
-rw-r--r--tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java2
-rw-r--r--tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java34
-rw-r--r--tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java66
-rw-r--r--tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java34
-rw-r--r--tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java20
-rw-r--r--tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java303
-rw-r--r--tests/telephonytests/src/android/telephony/ims/TestImsService.java7
-rw-r--r--tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java11
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java24
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/BarringInfoTest.java4
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java95
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java93
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java138
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java4
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java32
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java211
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java14
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java132
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java277
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java4
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java67
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java36
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java12
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java8
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java259
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTest.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java20
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java55
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java305
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java145
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java52
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java4
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java9
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java262
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java583
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java43
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java84
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java8
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java178
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java233
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java143
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/RILTest.java359
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java96
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/RadioInterfaceCapabilityControllerTest.java117
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java7
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java523
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java12
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SignalStrengthUpdateRequestTest.java125
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java381
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java72
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java156
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java76
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SlidingWindowEventCounterTest.java79
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java24
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java364
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java134
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java129
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java533
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java128
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TestExecutorService.java274
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java5
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java13
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java4
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java174
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportConversionTest.java189
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportTest.java400
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java193
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java120
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java10
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java11
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java64
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java28
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java513
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java21
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java31
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java235
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java13
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java1747
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/LinkBandwidthEstimatorTest.java660
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java490
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java141
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java94
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java12
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java186
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java27
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java488
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java116
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java258
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java220
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectorTest.java155
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java66
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java899
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java309
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java487
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java59
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/ImsUtTest.java153
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java296
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java117
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java50
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java58
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java272
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java103
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java122
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java149
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java725
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java109
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java979
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java826
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java73
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java89
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java316
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneLookupHelperTest.java15
-rwxr-xr-x[-rw-r--r--]tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java119
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java103
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/IccUtilsTest.java73
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java414
-rwxr-xr-xtests/telephonytests/src/com/android/internal/telephony/uicc/RuimRecordsTest.java86
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java113
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/SimPhonebookRecordCacheTest.java150
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java4
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java290
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java72
377 files changed, 46809 insertions, 14245 deletions
diff --git a/Android.bp b/Android.bp
index 3f87578b0f..9aa92f9c5a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13,6 +13,35 @@
// limitations under the License.
+package {
+ default_applicable_licenses: ["frameworks_opt_telephony_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "frameworks_opt_telephony_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ "legacy_not_a_contribution",
+ ],
+ // large-scale-change unable to identify any license_text files
+}
+
filegroup {
name: "opt-telephony-srcs",
srcs: [
@@ -75,6 +104,7 @@ java_library {
"android.hardware.radio-V1.3-java",
"android.hardware.radio-V1.4-java",
"android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
"voip-common",
"ims-common",
"unsupportedappusage",
@@ -83,12 +113,14 @@ java_library {
"android.hardware.radio.config-V1.0-java-shallow",
"android.hardware.radio.config-V1.1-java-shallow",
"android.hardware.radio.config-V1.2-java-shallow",
+ "android.hardware.radio.config-V1.3-java-shallow",
"android.hardware.radio.deprecated-V1.0-java-shallow",
"ecc-protos-lite",
"libphonenumber-nogeocoder",
"PlatformProperties",
"net-utils-framework-common",
"telephony-protos",
+ "modules-utils-build_system",
],
product_variables: {
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e75dcb02bf..75b9d49ced 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -7,6 +7,9 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CarrierAppIntegrationTestCases"
}
]
}
diff --git a/jarjar-rules-shared.txt b/jarjar-rules-shared.txt
index 2bac95f847..4f3f6171d1 100644
--- a/jarjar-rules-shared.txt
+++ b/jarjar-rules-shared.txt
@@ -1,9 +1,10 @@
rule android.net.NetworkFactory* com.android.internal.telephony.NetworkFactory@1
-rule android.os.BasicShellCommandHandler* com.android.internal.telephony.BasicShellCommandHandler@1
+rule com.android.modules.utils.** com.android.internal.telephony.@1
rule android.os.RegistrantList* com.android.internal.telephony.RegistrantList@1
rule android.os.Registrant* com.android.internal.telephony.Registrant@1
rule android.hidl.** android.internal.hidl.@1
rule android.sysprop.** android.internal.telephony.sysprop.@1
+rule android.util.IndentingPrintWriter* com.android.internal.telephony.AndroidUtilIndentingPrintWriter@1
rule android.util.LocalLog* com.android.internal.telephony.LocalLog@1
rule android.util.TimeUtils* com.android.internal.telephony.TimeUtils@1
rule com.android.internal.os.SomeArgs* com.android.internal.telephony.SomeArgs@1
diff --git a/proto/Android.bp b/proto/Android.bp
index d7e379f6fc..15c0aea3eb 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
java_library_static {
name: "telephony-protos",
proto: {
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 6600ff1ccd..17013188e0 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -23,25 +23,82 @@ option java_outer_classname = "PersistAtomsProto";
// Holds atoms to store on persist storage in case of power cycle or process crash.
// NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation.
-// Next id: 5
+// Next id: 22
message PersistAtoms {
/* Aggregated RAT usage during the call. */
- repeated RawVoiceCallRatUsage raw_voice_call_rat_usage = 1;
+ repeated VoiceCallRatUsage voice_call_rat_usage = 1;
/* Timestamp of last voice_call_rat_usages pull. */
- optional int64 raw_voice_call_rat_usage_pull_timestamp_millis = 2;
+ optional int64 voice_call_rat_usage_pull_timestamp_millis = 2;
/* Per call statistics and information. */
repeated VoiceCallSession voice_call_session = 3;
/* Timestamp of last voice_call_sessions pull. */
optional int64 voice_call_session_pull_timestamp_millis = 4;
+
+ /* Incoming SMS statistics and information. */
+ repeated IncomingSms incoming_sms = 5;
+
+ /* Timestamp of last incoming_sms pull. */
+ optional int64 incoming_sms_pull_timestamp_millis = 6;
+
+ /* Outgoing SMS statistics and information. */
+ repeated OutgoingSms outgoing_sms = 7;
+
+ /* Timestamp of last incoming_sms pull. */
+ optional int64 outgoing_sms_pull_timestamp_millis = 8;
+
+ /* List of carrier ID mismatch events already sent. */
+ repeated CarrierIdMismatch carrier_id_mismatch = 9;
+
+ /* Last version of carrier ID table sent. */
+ optional int32 carrier_id_table_version = 10;
+
+ /* Data Call session statistics and information. */
+ repeated DataCallSession data_call_session = 11;
+
+ /* Timestamp of last data_call_session pull. */
+ optional int64 data_call_session_pull_timestamp_millis = 12;
+
+ /* Duration spent in each possible service state. */
+ repeated CellularServiceState cellular_service_state = 13;
+
+ /* Timestamp of last cellular_service_state pull. */
+ optional int64 cellular_service_state_pull_timestamp_millis = 14;
+
+ /* Switch count between data RATs. */
+ repeated CellularDataServiceSwitch cellular_data_service_switch = 15;
+
+ /* Timestamp of last cellular_data_service_switch pull. */
+ optional int64 cellular_data_service_switch_pull_timestamp_millis = 16;
+
+ /* List of IMS registration terminations. */
+ repeated ImsRegistrationTermination ims_registration_termination = 17;
+
+ /* Timestamp of last ims_registration_termination pull. */
+ optional int64 ims_registration_termination_pull_timestamp_millis = 18;
+
+ /* Durations of IMS registrations and capabilities. */
+ repeated ImsRegistrationStats ims_registration_stats = 19;
+
+ /* Timestamp of last ims_registration_stats pull. */
+ optional int64 ims_registration_stats_pull_timestamp_millis = 20;
+
+ /* Last Android build fingerprint. This usually changes after system OTA. */
+ optional string build_fingerprint = 21;
+
+ /* Summary of received network requests. */
+ repeated NetworkRequests network_requests = 22;
+
+ /* Timestamp of last network_requests pull. */
+ optional int64 network_requests_pull_timestamp_millis = 23;
}
// The canonical versions of the following enums live in:
-// frameworks/base/core/proto/android/telephony/enums.proto
+// frameworks/proto_logging/stats/enums/telephony/enums.proto
// The canonical versions of the following atoms live in:
-// frameworks/base/cmds/statsd/src/atoms.proto
+// frameworks/proto_logging/stats/atoms.proto
// We cannot link against framework's and statsd's protolite libraries as it is "for test only".
// NOTE: StatsLog functions use int in place of enum
@@ -70,15 +127,149 @@ message VoiceCallSession {
optional bool rtt_enabled = 22;
optional bool is_emergency = 23;
optional bool is_roaming = 24;
+ optional int32 signal_strength_at_end = 25;
+ optional int32 band_at_end = 26;
+ optional int32 setup_duration_millis = 27;
+ optional int32 main_codec_quality = 28;
+ optional bool video_enabled = 29;
+ optional int32 rat_at_connected = 30;
+ optional bool is_multiparty = 31;
// Internal use only
optional int64 setup_begin_millis = 10001;
}
-// Internal use only
-message RawVoiceCallRatUsage {
+message VoiceCallRatUsage {
optional int32 carrier_id = 1;
optional int32 rat = 2;
- optional int64 total_duration_millis = 3;
+ optional int64 total_duration_millis = 3; // Duration needs to be rounded when pulled
optional int64 call_count = 4;
}
+
+message IncomingSms {
+ optional int32 sms_format = 1;
+ optional int32 sms_tech = 2;
+ optional int32 rat = 3;
+ optional int32 sms_type = 4;
+ optional int32 total_parts = 5;
+ optional int32 received_parts = 6;
+ optional bool blocked = 7;
+ optional int32 error = 8;
+ optional bool is_roaming = 9;
+ optional int32 sim_slot_index = 10;
+ optional bool is_multi_sim = 11;
+ optional bool is_esim = 12;
+ optional int32 carrier_id = 13;
+ optional int64 message_id = 14;
+}
+
+message OutgoingSms {
+ optional int32 sms_format = 1;
+ optional int32 sms_tech = 2;
+ optional int32 rat = 3;
+ optional int32 send_result = 4;
+ optional int32 error_code = 5;
+ optional bool is_roaming = 6;
+ optional bool is_from_default_app = 7;
+ optional int32 sim_slot_index = 8;
+ optional bool is_multi_sim = 9;
+ optional bool is_esim = 10;
+ optional int32 carrier_id = 11;
+ optional int64 message_id = 12;
+ optional int32 retry_id = 13;
+}
+
+message CarrierIdMismatch {
+ optional string mcc_mnc = 1;
+ optional string gid1 = 2;
+ optional string spn = 3;
+ optional string pnn = 4;
+}
+
+message DataCallSession {
+ reserved 4;
+ optional int32 dimension = 1;
+ optional bool is_multi_sim = 2;
+ optional bool is_esim = 3;
+ optional int32 apn_type_bitmask = 5;
+ optional int32 carrier_id = 6;
+ optional bool is_roaming = 7;
+ optional int32 rat_at_end = 8;
+ optional bool oos_at_end = 9;
+ optional int64 rat_switch_count = 10;
+ optional bool is_opportunistic = 11;
+ optional int32 ip_type = 12;
+ optional bool setup_failed = 13;
+ optional int32 failure_cause = 14;
+ optional int32 suggested_retry_millis = 15;
+ optional int32 deactivate_reason = 16;
+ optional int64 duration_minutes = 17;
+ optional bool ongoing = 18;
+ optional int32 band_at_end = 19;
+}
+
+message CellularServiceState {
+ optional int32 voice_rat = 1;
+ optional int32 data_rat = 2;
+ optional int32 voice_roaming_type = 3;
+ optional int32 data_roaming_type = 4;
+ optional bool is_endc = 5;
+ optional int32 sim_slot_index = 6;
+ optional bool is_multi_sim = 7;
+ optional int32 carrier_id = 8;
+ optional int64 total_time_millis = 9; // Duration needs to be rounded when pulled
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
+
+message CellularDataServiceSwitch {
+ optional int32 rat_from = 1;
+ optional int32 rat_to = 2;
+ optional int32 sim_slot_index = 3;
+ optional bool is_multi_sim = 4;
+ optional int32 carrier_id = 5;
+ optional int32 switch_count = 6;
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
+
+message ImsRegistrationTermination {
+ optional int32 carrier_id = 1;
+ optional bool is_multi_sim = 2;
+ optional int32 rat_at_end = 3;
+ optional bool setup_failed = 4;
+ optional int32 reason_code = 5;
+ optional int32 extra_code = 6;
+ optional string extra_message = 7;
+ optional int32 count = 8;
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
+
+message ImsRegistrationStats {
+ optional int32 carrier_id = 1;
+ optional int32 sim_slot_index = 2;
+ optional int32 rat = 3;
+ // Durations need to be rounded when pulled
+ optional int64 registered_millis = 4;
+ optional int64 voice_capable_millis = 5;
+ optional int64 voice_available_millis = 6;
+ optional int64 sms_capable_millis = 7;
+ optional int64 sms_available_millis = 8;
+ optional int64 video_capable_millis = 9;
+ optional int64 video_available_millis = 10;
+ optional int64 ut_capable_millis = 11;
+ optional int64 ut_available_millis = 12;
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
+
+message NetworkRequests {
+ optional int32 carrier_id = 1;
+ optional int32 enterprise_request_count = 2;
+ optional int32 enterprise_release_count = 3;
+}
diff --git a/proto/src/pin_storage.proto b/proto/src/pin_storage.proto
new file mode 100644
index 0000000000..f572a212f4
--- /dev/null
+++ b/proto/src/pin_storage.proto
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package telephonyPinStorage;
+
+option java_package = "com.android.internal.telephony";
+option java_outer_classname = "StoredPinProto";
+
+// Stores information about PIN of a SIM card.
+message StoredPin {
+ // Status of the PIN.
+ enum PinStatus {
+ // The PIN code is stored, but cannot be used for automatic verification.
+ AVAILABLE = 1;
+
+ // The PIN code is stored and will be usable for automatic verification after the
+ // unattended reboot is completed.
+ REBOOT_READY = 2;
+
+ // The PIN code is stored and can be used for automatic verification.
+ VERIFICATION_READY = 3;
+ }
+
+ // ICCID of the SIM card
+ optional string iccid = 1;
+
+ // PIN code
+ optional string pin = 2;
+
+ // Slot number
+ optional int32 slot_id = 3;
+
+ // Status of the PIN code
+ optional PinStatus status = 4;
+
+ // Boot count when the proto was generated.
+ optional int32 boot_count = 5;
+}
+
+// Stores the encrypted version of StoredPin.
+message EncryptedPin {
+ // Encrypted StoredPin
+ optional bytes encrypted_stored_pin = 1;
+
+ // Initialization vector
+ optional bytes iv = 2;
+} \ No newline at end of file
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index 6e81ec027e..0d9ee80655 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -53,6 +53,9 @@ message TelephonyLog {
// The last active subscription info for each slot.
repeated ActiveSubscriptionInfo last_active_subscription_info = 10;
+
+ // Bandwidth estimator stats
+ optional BandwidthEstimatorStats bandwidth_estimator_stats = 11;
}
// The time information
@@ -418,6 +421,20 @@ enum RadioAccessTechnology {
RAT_NR = 20;
}
+// NR (5G) operation mode
+enum NrMode {
+ // The device is not in a NR network.
+ NR_NONE = 1;
+ // The device is in a NR non-standalone network at non-MMWAVE frequencies.
+ NR_NSA = 2;
+ // The device is in a NR non-standalone network at MMWAVE frequencies.
+ NR_NSA_MMWAVE = 3;
+ // The device is in a NR standalone network at non-MMWAVE frequencies.
+ NR_SA = 4;
+ // The device is in a NR standalone network at MMWAVE frequencies.
+ NR_SA_MMWAVE = 5;
+}
+
// The information about IMS errors
// https://cs.corp.google.com/#android/frameworks/base/telephony/java/com/android/ims/ImsReasonInfo.java
message ImsReasonInfo {
@@ -706,7 +723,7 @@ message RilDataCall {
optional PdpType type = 2;
// The network interface name e.g. wlan0, rmnet_data0.
- optional string iframe = 3;
+ optional string ifname = 3;
// State of the Data Call connection
optional State state = 4;
@@ -836,6 +853,9 @@ message TelephonyEvent {
// Signal strength
SIGNAL_STRENGTH = 23;
+
+ // Radio state change event
+ RADIO_STATE_CHANGED = 24;
}
enum ApnType {
@@ -884,6 +904,20 @@ message TelephonyEvent {
NETWORK_VALIDATION_STATE_PASSED = 3;
}
+ enum RadioState {
+ /** Radio state is unknown or invalid. */
+ RADIO_STATE_UNKNOWN = 0;
+
+ /** Radio is explicitly off (e.g. airplane mode). */
+ RADIO_STATE_OFF = 1;
+
+ /** Radio is on. */
+ RADIO_STATE_ON = 2;
+
+ /** Radio power unavailable (eg, modem resetting or not booted). */
+ RADIO_STATE_UNAVAILABLE = 3;
+ }
+
message DataSwitch {
enum Reason {
/** Data switch caused by unknown reason. */
@@ -1743,7 +1777,7 @@ message TelephonyEvent {
optional bool is_network_unmetered = 1;
}
- // Time when event happened on device, in milliseconds since epoch
+ // Time when event happened on device, in milliseconds since boot
optional int64 timestamp_millis = 1;
// In Multi-SIM devices this indicates SIM slot
@@ -1828,6 +1862,9 @@ message TelephonyEvent {
// (datapol.semantic_type) = ST_SOFTWARE_ID,
// (datapol.qualifier) = { is_public: true }
//]
+
+ // Radio state for the given phone_id
+ optional RadioState radio_state = 29;
}
message ActiveSubscriptionInfo {
@@ -2457,7 +2494,7 @@ message SmsSession {
// Formats used to encode SMS messages
enum Format {
- // State is unknown.
+ // Format is unknown.
SMS_FORMAT_UNKNOWN = 0;
// GSM, WCDMA
@@ -2680,3 +2717,34 @@ message ModemPowerStats {
// Actual monitored rail energy consumed by modem (mAh)
optional double monitored_rail_energy_consumed_mah = 15;
}
+
+// Bandwidth estimator stats
+message BandwidthEstimatorStats {
+ // Bandwidth stats of each level
+ message PerLevel {
+ optional uint32 signal_level = 1;
+ // Accumulated bandwidth sample count
+ optional uint32 count = 2;
+ // Average end-to-end bandwidth in kbps
+ optional uint32 avg_bw_kbps = 3;
+ // Normalized error of static BW values in percent
+ optional uint32 static_bw_error_percent = 4;
+ // Normalized error of end-to-end BW estimation in percent
+ optional uint32 bw_est_error_percent = 5;
+ }
+
+ // Bandwidth stats of each RAT
+ message PerRat {
+ // radio access technology
+ optional RadioAccessTechnology rat = 1;
+ // NR (5g) operation mode
+ optional NrMode nr_mode = 2;
+ // bandwidth stats of signal levels
+ repeated PerLevel per_level = 3;
+ }
+
+ // Tx Stats of visited RATs
+ repeated PerRat per_rat_tx = 1;
+ // Rx Stats of visited RATs
+ repeated PerRat per_rat_rx = 2;
+}
diff --git a/src/java/com/android/internal/telephony/AppSmsManager.java b/src/java/com/android/internal/telephony/AppSmsManager.java
index f473c9aa65..64a5bdc9e2 100644
--- a/src/java/com/android/internal/telephony/AppSmsManager.java
+++ b/src/java/com/android/internal/telephony/AppSmsManager.java
@@ -36,8 +36,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import java.security.SecureRandom;
+import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -144,7 +144,7 @@ public class AppSmsManager {
* Handle an incoming SMS_DELIVER_ACTION intent if it is an app-only SMS.
*/
public boolean handleSmsReceivedIntent(Intent intent) {
- // Sanity check the action.
+ // Correctness check the action.
if (intent.getAction() != Intents.SMS_DELIVER_ACTION) {
Log.wtf(LOG_TAG, "Got intent with incorrect action: " + intent.getAction());
return false;
@@ -187,9 +187,10 @@ public class AppSmsManager {
private void removeExpiredTokenLocked() {
final long currentTimeMillis = System.currentTimeMillis();
- final Set<String> keySet = mTokenMap.keySet();
- for (String token : keySet) {
- AppRequestInfo request = mTokenMap.get(token);
+ Iterator<Map.Entry<String, AppRequestInfo>> iterator = mTokenMap.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, AppRequestInfo> entry = iterator.next();
+ AppRequestInfo request = entry.getValue();
if (request.packageBasedToken
&& (currentTimeMillis - TIMEOUT_MILLIS > request.timestamp)) {
// Send the provided intent with SMS retriever status
@@ -202,8 +203,9 @@ public class AppSmsManager {
} catch (PendingIntent.CanceledException e) {
// do nothing
}
-
- removeRequestLocked(request);
+ // Remove from mTokenMap and mPackageMap
+ iterator.remove();
+ mPackageMap.remove(entry.getValue().packageName);
}
}
}
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 24d27f8079..02530daa77 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -20,6 +20,7 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -28,6 +29,8 @@ import android.telephony.Annotation.RadioPowerState;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
+
import java.util.ArrayList;
import java.util.List;
@@ -51,6 +54,7 @@ public abstract class BaseCommands implements CommandsInterface {
protected RegistrantList mCallStateRegistrants = new RegistrantList();
protected RegistrantList mNetworkStateRegistrants = new RegistrantList();
protected RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
+ protected RegistrantList mApnUnthrottledRegistrants = new RegistrantList();
@UnsupportedAppUsage
protected RegistrantList mVoiceRadioTechChangedRegistrants = new RegistrantList();
@UnsupportedAppUsage
@@ -107,6 +111,8 @@ public abstract class BaseCommands implements CommandsInterface {
protected RegistrantList mEmergencyNumberListRegistrants = new RegistrantList();
protected RegistrantList mUiccApplicationsEnablementRegistrants = new RegistrantList();
protected RegistrantList mBarringInfoChangedRegistrants = new RegistrantList();
+ protected RegistrantList mSimPhonebookChangedRegistrants = new RegistrantList();
+ protected RegistrantList mSimPhonebookRecordsReceivedRegistrants = new RegistrantList();
@UnsupportedAppUsage
protected Registrant mGsmSmsRegistrant;
@@ -156,8 +162,8 @@ public abstract class BaseCommands implements CommandsInterface {
// Preferred network type received from PhoneFactory.
// This is used when establishing a connection to the
// vendor ril so it starts up in the correct mode.
- @UnsupportedAppUsage
- protected int mPreferredNetworkType;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ protected int mAllowedNetworkTypesBitmask;
// CDMA subscription received from PhoneFactory
protected int mCdmaSubscription;
// Type of Phone, GSM or CDMA. Set by GsmCdmaPhone.
@@ -303,6 +309,16 @@ public abstract class BaseCommands implements CommandsInterface {
}
@Override
+ public void registerForApnUnthrottled(Handler h, int what, Object obj) {
+ mApnUnthrottledRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForApnUnthrottled(Handler h) {
+ mApnUnthrottledRegistrants.remove(h);
+ }
+
+ @Override
public void registerForVoiceRadioTechChanged(Handler h, int what, Object obj) {
mVoiceRadioTechChangedRegistrants.addUnique(h, what, obj);
}
@@ -895,14 +911,6 @@ public abstract class BaseCommands implements CommandsInterface {
* {@inheritDoc}
*/
@Override
- public int getLteOnCdmaMode() {
- return TelephonyManager.getLteOnCdmaModeStatic();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
public void registerForCellInfoList(Handler h, int what, Object obj) {
mRilCellInfoListRegistrants.addUnique(h, what, obj);
}
@@ -1081,4 +1089,36 @@ public abstract class BaseCommands implements CommandsInterface {
public void unregisterForBarringInfoChanged(Handler h) {
mBarringInfoChangedRegistrants.remove(h);
}
+
+ @Override
+ public void registerForSimPhonebookChanged(Handler h, int what, Object obj) {
+ mSimPhonebookChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSimPhonebookChanged(Handler h) {
+ mSimPhonebookChangedRegistrants.remove(h);
+ }
+
+ @Override
+ public void registerForSimPhonebookRecordsReceived(Handler h, int what, Object obj) {
+ mSimPhonebookRecordsReceivedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSimPhonebookRecordsReceived(Handler h) {
+ mSimPhonebookRecordsReceivedRegistrants.remove(h);
+ }
+
+ @Override
+ public void getSimPhonebookRecords(Message result) {
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result) {
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/BiMap.java b/src/java/com/android/internal/telephony/BiMap.java
new file mode 100644
index 0000000000..9ca5460272
--- /dev/null
+++ b/src/java/com/android/internal/telephony/BiMap.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.util.ArrayMap;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A very basic bidirectional map.
+ */
+public class BiMap<K, V> {
+ private Map<K, V> mPrimaryMap = new ArrayMap<>();
+ private Map<V, K> mSecondaryMap = new ArrayMap<>();
+
+ public boolean put(K key, V value) {
+ if (key == null || value == null || mPrimaryMap.containsKey(key) ||
+ mSecondaryMap.containsKey(value)) {
+ return false;
+ }
+
+ mPrimaryMap.put(key, value);
+ mSecondaryMap.put(value, key);
+ return true;
+ }
+
+ public boolean remove(K key) {
+ if (key == null) {
+ return false;
+ }
+ if (mPrimaryMap.containsKey(key)) {
+ V value = getValue(key);
+ mPrimaryMap.remove(key);
+ mSecondaryMap.remove(value);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean removeValue(V value) {
+ if (value == null) {
+ return false;
+ }
+ return remove(getKey(value));
+ }
+
+ public V getValue(K key) {
+ return mPrimaryMap.get(key);
+ }
+
+ public K getKey(V value) {
+ return mSecondaryMap.get(value);
+ }
+
+ public Collection<V> getValues() {
+ return mPrimaryMap.values();
+ }
+
+ public void clear() {
+ mPrimaryMap.clear();
+ mSecondaryMap.clear();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java b/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
index 7271bf3655..40e9a1cf52 100644
--- a/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
@@ -18,7 +18,9 @@
package com.android.internal.telephony;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.net.Uri;
@@ -40,10 +42,6 @@ public class BtSmsInterfaceManager {
*/
public void sendText(Context context, String destAddr, String text, PendingIntent sentIntent,
PendingIntent deliveryIntent, SubscriptionInfo info) {
- /*
- This is to remove the usage of hidden constant MAP_CLIENT and hidden API
- BluetoothMapClient.sendMessage(). This code is currently not functional anyway; it will be
- re-enabled in a later release.
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter == null) {
// No bluetooth service on this platform?
@@ -56,10 +54,11 @@ public class BtSmsInterfaceManager {
sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS);
return;
}
- btAdapter.getProfileProxy(context.getApplicationContext(),
+ if (btAdapter.getProfileProxy(context.getApplicationContext(),
new MapMessageSender(destAddr, text, device, sentIntent, deliveryIntent),
- BluetoothProfile.MAP_CLIENT);
- */
+ BluetoothProfile.MAP_CLIENT)) {
+ return;
+ }
throw new RuntimeException("Can't send message through BluetoothMapClient");
}
@@ -100,7 +99,6 @@ public class BtSmsInterfaceManager {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
Log.d(LOG_TAG, "Service connected");
- /*
if (profile != BluetoothProfile.MAP_CLIENT) {
return;
}
@@ -112,8 +110,6 @@ public class BtSmsInterfaceManager {
}
BluetoothAdapter.getDefaultAdapter()
.closeProfileProxy(BluetoothProfile.MAP_CLIENT, mapProfile);
- */
- throw new RuntimeException("Can't send message through BluetoothMapClient");
}
@Override
diff --git a/src/java/com/android/internal/telephony/Call.java b/src/java/com/android/internal/telephony/Call.java
index 50f65d2aee..d04dd43cc1 100644
--- a/src/java/com/android/internal/telephony/Call.java
+++ b/src/java/com/android/internal/telephony/Call.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.ims.internal.ConferenceParticipant;
import com.android.telephony.Rlog;
@@ -81,10 +82,10 @@ public abstract class Call {
/* Instance Variables */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public State mState = State.IDLE;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ArrayList<Connection> mConnections = new ArrayList<>();
private Object mLock = new Object();
@@ -120,9 +121,9 @@ public abstract class Call {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract Phone getPhone();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract boolean isMultiparty();
@UnsupportedAppUsage
public abstract void hangup() throws CallStateException;
@@ -208,7 +209,7 @@ public abstract class Call {
* FIXME rename
* @return true if the call contains only disconnected connections (if any)
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isIdle() {
return !getState().isAlive();
}
@@ -305,7 +306,7 @@ public abstract class Call {
* Returns the Connection associated with this Call that was created
* last, or null if there are no Connections in this Call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Connection
getLatestConnection() {
List<Connection> l = getConnections();
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index 151a4bf56a..d2b1bdd820 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -19,6 +19,7 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -27,6 +28,7 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.internal.telephony.sip.SipPhone;
import com.android.telephony.Rlog;
@@ -87,23 +89,23 @@ public class CallManager {
private static final CallManager INSTANCE = new CallManager();
// list of registered phones, which are Phone objs
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<Phone> mPhones;
// list of supported ringing calls
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<Call> mRingingCalls;
// list of supported background calls
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<Call> mBackgroundCalls;
// list of supported foreground calls
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<Call> mForegroundCalls;
// empty connection list
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<Connection> mEmptyConnections = new ArrayList<Connection>();
// mapping of phones to registered handler instances used for callbacks from RIL
@@ -350,7 +352,7 @@ public class CallManager {
* @param phone to be registered
* @return true if register successfully
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean registerPhone(Phone phone) {
if (phone != null && !mPhones.contains(phone)) {
@@ -376,7 +378,7 @@ public class CallManager {
* unregister phone from CallManager
* @param phone to be unregistered
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void unregisterPhone(Phone phone) {
if (phone != null && mPhones.contains(phone)) {
@@ -408,7 +410,7 @@ public class CallManager {
/**
* return the default phone or null if no phone available
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Phone getDefaultPhone() {
return mDefaultPhone;
}
@@ -416,7 +418,7 @@ public class CallManager {
/**
* @return the phone associated with the foreground call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Phone getFgPhone() {
return getActiveFgCall().getPhone();
}
@@ -433,7 +435,7 @@ public class CallManager {
/**
* @return the phone associated with the background call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Phone getBgPhone() {
return getFirstActiveBgCall().getPhone();
}
@@ -441,7 +443,7 @@ public class CallManager {
/**
* @return the phone associated with the ringing call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Phone getRingingPhone() {
return getFirstActiveRingingCall().getPhone();
}
@@ -529,7 +531,7 @@ public class CallManager {
}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Context getContext() {
Phone defaultPhone = getDefaultPhone();
return ((defaultPhone == null) ? null : defaultPhone.getContext());
@@ -726,7 +728,7 @@ public class CallManager {
* @exception CallStateException if canConference() would return false.
* In these cases, this operation may not be performed.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void conference(Call heldCall) throws CallStateException {
int subId = heldCall.getPhone().getSubId();
@@ -813,9 +815,6 @@ public class CallManager {
}
}
- // FIXME Taken from klp-sprout-dev but setAudioMode was removed in L.
- //mIsEccDialing = PhoneNumberUtils.isEmergencyNumber(dialString);
-
result = phone.dial(dialString, new PhoneInternalInterface.DialArgs.Builder<>()
.setVideoState(videoState).build());
@@ -875,7 +874,7 @@ public class CallManager {
* @param phone
* @return true if the phone can make a new call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean canDial(Phone phone) {
int serviceState = phone.getServiceState().getState();
int subId = phone.getSubId();
@@ -1166,7 +1165,7 @@ public class CallManager {
* <li>AsyncResult.result = a Connection object that is
* no longer connected.</li></ul>
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void registerForDisconnect(Handler h, int what, Object obj) {
mDisconnectRegistrants.addUnique(h, what, obj);
}
@@ -1175,7 +1174,7 @@ public class CallManager {
* Unregisters for voice disconnection notification.
* Extraneous calls are tolerated silently
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void unregisterForDisconnect(Handler h){
mDisconnectRegistrants.remove(h);
}
@@ -1189,7 +1188,7 @@ public class CallManager {
* AsyncResult.userData will be set to the obj argument here.
* The <em>h</em> parameter is held only by a weak reference.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
mPreciseCallStateRegistrants.addUnique(h, what, obj);
}
@@ -1198,7 +1197,7 @@ public class CallManager {
* Unregisters for voice call state change notifications.
* Extraneous calls are tolerated silently.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void unregisterForPreciseCallStateChanged(Handler h){
mPreciseCallStateRegistrants.remove(h);
}
@@ -1231,7 +1230,7 @@ public class CallManager {
* If Connection.isRinging() is true, then
* Connection.getCall() == Phone.getRingingCall()
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void registerForNewRingingConnection(Handler h, int what, Object obj){
mNewRingingConnectionRegistrants.addUnique(h, what, obj);
}
@@ -1241,7 +1240,7 @@ public class CallManager {
* Extraneous calls are tolerated silently
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void unregisterForNewRingingConnection(Handler h){
mNewRingingConnectionRegistrants.remove(h);
}
@@ -1644,7 +1643,7 @@ public class CallManager {
/**
* @return list of all ringing calls
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<Call> getRingingCalls() {
return Collections.unmodifiableList(mRingingCalls);
}
@@ -1659,7 +1658,7 @@ public class CallManager {
/**
* @return list of all background calls
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<Call> getBackgroundCalls() {
return Collections.unmodifiableList(mBackgroundCalls);
}
@@ -1667,7 +1666,7 @@ public class CallManager {
/**
* Return true if there is at least one active foreground call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean hasActiveFgCall() {
return (getFirstActiveCall(mForegroundCalls) != null);
}
@@ -1684,7 +1683,7 @@ public class CallManager {
/**
* Return true if there is at least one active background call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean hasActiveBgCall() {
// TODO since hasActiveBgCall may get called often
// better to cache it to improve performance
@@ -1795,7 +1794,7 @@ public class CallManager {
*
* Complete background calls list can be get by getBackgroundCalls()
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Call getFirstActiveBgCall() {
Call call = getFirstNonIdleCall(mBackgroundCalls);
if (call == null) {
@@ -1901,7 +1900,7 @@ public class CallManager {
* @return the connections of active foreground call
* return empty list if there is no active foreground call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<Connection> getFgCallConnections() {
Call fgCall = getActiveFgCall();
if ( fgCall != null) {
@@ -1926,7 +1925,7 @@ public class CallManager {
* @return the connections of active background call
* return empty list if there is no active background call
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<Connection> getBgCallConnections() {
Call bgCall = getFirstActiveBgCall();
if ( bgCall != null) {
@@ -2018,7 +2017,7 @@ public class CallManager {
return null;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean hasMoreThanOneRingingCall() {
int count = 0;
for (Call call : mRingingCalls) {
@@ -2036,7 +2035,7 @@ public class CallManager {
* subId and also active calls on SIP Phone.
*
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean hasMoreThanOneRingingCall(int subId) {
int count = 0;
for (Call call : mRingingCalls) {
@@ -2076,8 +2075,6 @@ public class CallManager {
case EVENT_DISCONNECT:
if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_DISCONNECT)");
mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
- // FIXME Taken from klp-sprout-dev but setAudioMode was removed in L.
- //mIsEccDialing = false;
break;
case EVENT_PRECISE_CALL_STATE_CHANGED:
if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)");
@@ -2087,7 +2084,13 @@ public class CallManager {
if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_NEW_RINGING_CONNECTION)");
Connection c = (Connection) ((AsyncResult) msg.obj).result;
int subId = c.getCall().getPhone().getSubId();
- if (getActiveFgCallState(subId).isDialing() || hasMoreThanOneRingingCall()) {
+ boolean incomingRejected = false;
+ if ((c.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS)
+ && ((ImsPhoneConnection) c).isIncomingCallAutoRejected()) {
+ incomingRejected = true;
+ }
+ if ((getActiveFgCallState(subId).isDialing() || hasMoreThanOneRingingCall())
+ && (!incomingRejected)) {
try {
Rlog.d(LOG_TAG, "silently drop incoming call: " + c.getCall());
c.getCall().hangup();
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index 5975fde7ea..cfa6767630 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -19,6 +19,7 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
@@ -41,17 +42,17 @@ public abstract class CallTracker extends Handler {
static final int POLL_DELAY_MSEC = 250;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected int mPendingOperations;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected boolean mNeedsPoll;
protected Message mLastRelevantPoll;
protected ArrayList<Connection> mHandoverConnections = new ArrayList<Connection>();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public CommandsInterface mCi;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected boolean mNumberConverted = false;
private final int VALID_COMPARE_LENGTH = 3;
@@ -133,7 +134,8 @@ public abstract class CallTracker extends Handler {
// Individual connections will be removed from the list in handlePollCalls()
mHandoverConnections.clear();
}
- log("notifySrvccState: mHandoverConnections= " + mHandoverConnections.toString());
+ log("notifySrvccState: state=" + state.name() + ", mHandoverConnections= "
+ + mHandoverConnections.toString());
}
protected void handleRadioAvailable() {
@@ -270,9 +272,9 @@ public abstract class CallTracker extends Handler {
@UnsupportedAppUsage
public abstract void registerForVoiceCallEnded(Handler h, int what, Object obj);
public abstract void unregisterForVoiceCallEnded(Handler h);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract PhoneConstants.State getState();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected abstract void log(String msg);
/**
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index ea4d75e09b..00f8c39fcc 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -22,16 +22,20 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Telephony;
+import android.telephony.CarrierConfigManager;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import java.security.PublicKey;
import java.util.Date;
/**
@@ -47,6 +51,10 @@ public class CarrierInfoManager {
*/
private static final int RESET_CARRIER_KEY_RATE_LIMIT = 12 * 60 * 60 * 1000;
+ // Key ID used with the backup key from carrier config
+ private static final String EPDG_BACKUP_KEY_ID = "backup_key_from_carrier_config_epdg";
+ private static final String WLAN_BACKUP_KEY_ID = "backup_key_from_carrier_config_wlan";
+
// Last time the resetCarrierKeysForImsiEncryption API was called successfully.
private long mLastAccessResetCarrierKey = 0;
@@ -54,18 +62,21 @@ public class CarrierInfoManager {
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
* @param context
+ * @param fallback whether to fallback to the IMSI key info stored in carrier config
* @return ImsiEncryptionInfo which contains the information, including the public key, to be
* used for encryption.
*/
public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType,
Context context,
- String operatorNumeric) {
+ String operatorNumeric,
+ boolean fallback,
+ int subId) {
String mcc = "";
String mnc = "";
if (!TextUtils.isEmpty(operatorNumeric)) {
mcc = operatorNumeric.substring(0, 3);
mnc = operatorNumeric.substring(3);
- Log.i(LOG_TAG, "using values for mnc, mcc: " + mnc + "," + mcc);
+ Log.i(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc);
} else {
Log.e(LOG_TAG, "Invalid networkOperator: " + operatorNumeric);
return null;
@@ -83,7 +94,54 @@ public class CarrierInfoManager {
new String[]{mcc, mnc, String.valueOf(keyType)}, null);
if (findCursor == null || !findCursor.moveToFirst()) {
Log.d(LOG_TAG, "No rows found for keyType: " + keyType);
- return null;
+ if (!fallback) {
+ Log.d(LOG_TAG, "Skipping fallback logic");
+ return null;
+ }
+ // return carrier config key as fallback
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (carrierConfigManager == null) {
+ Log.d(LOG_TAG, "Could not get CarrierConfigManager for backup key");
+ return null;
+ }
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.d(LOG_TAG, "Could not get carrier config with invalid subId");
+ return null;
+ }
+ PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
+ if (b == null) {
+ Log.d(LOG_TAG, "Could not get carrier config bundle for backup key");
+ return null;
+ }
+ int keyAvailabilityBitmask = b.getInt(
+ CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
+ if (!CarrierKeyDownloadManager.isKeyEnabled(keyType, keyAvailabilityBitmask)) {
+ Log.d(LOG_TAG, "Backup key does not have matching keyType. keyType=" + keyType
+ + " keyAvailability=" + keyAvailabilityBitmask);
+ return null;
+ }
+ String keyString = null;
+ String keyId = null;
+ if (keyType == TelephonyManager.KEY_TYPE_EPDG) {
+ keyString = b.getString(
+ CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING);
+ keyId = EPDG_BACKUP_KEY_ID;
+ } else if (keyType == TelephonyManager.KEY_TYPE_WLAN) {
+ keyString = b.getString(
+ CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING);
+ keyId = WLAN_BACKUP_KEY_ID;
+ }
+ if (TextUtils.isEmpty(keyString)) {
+ Log.d(LOG_TAG,
+ "Could not get carrier config key string for backup key. keyType="
+ + keyType);
+ return null;
+ }
+ Pair<PublicKey, Long> keyInfo =
+ CarrierKeyDownloadManager.getKeyInformation(keyString.getBytes());
+ return new ImsiEncryptionInfo(mcc, mnc, keyType, keyId,
+ keyInfo.first, new Date(keyInfo.second));
}
if (findCursor.getCount() > 1) {
Log.e(LOG_TAG, "More than 1 row found for the keyType: " + keyType);
@@ -196,7 +254,13 @@ public class CarrierInfoManager {
return;
}
mLastAccessResetCarrierKey = now;
- deleteCarrierInfoForImsiEncryption(context);
+ int[] subIds = context.getSystemService(SubscriptionManager.class)
+ .getSubscriptionIds(mPhoneId);
+ if (subIds == null || subIds.length < 1) {
+ Log.e(LOG_TAG, "Could not reset carrier keys, subscription for mPhoneId=" + mPhoneId);
+ return;
+ }
+ deleteCarrierInfoForImsiEncryption(context, subIds[0]);
Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId);
context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
@@ -206,12 +270,12 @@ public class CarrierInfoManager {
* Deletes all the keys for a given Carrier from the device keystore.
* @param context Context
*/
- public static void deleteCarrierInfoForImsiEncryption(Context context) {
- Log.i(LOG_TAG, "deleting carrier key from db");
+ public static void deleteCarrierInfoForImsiEncryption(Context context, int subId) {
+ Log.i(LOG_TAG, "deleting carrier key from db for subId=" + subId);
String mcc = "";
String mnc = "";
- final TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(subId);
String simOperator = telephonyManager.getSimOperator();
if (!TextUtils.isEmpty(simOperator)) {
mcc = simOperator.substring(0, 3);
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 3e94f4f1db..757530ea60 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony;
-import static android.preference.PreferenceManager.getDefaultSharedPreferences;
import static android.telephony.CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -28,7 +27,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
@@ -60,6 +58,7 @@ import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Random;
import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipException;
/**
* This class contains logic to get Certificates and keep them current.
@@ -68,8 +67,6 @@ import java.util.zip.GZIPInputStream;
public class CarrierKeyDownloadManager extends Handler {
private static final String LOG_TAG = "CarrierKeyDownloadManager";
- private static final String MCC_MNC_PREF_TAG = "CARRIER_KEY_DM_MCC_MNC";
-
private static final String CERT_BEGIN_STRING = "-----BEGIN CERTIFICATE-----";
private static final String CERT_END_STRING = "-----END CERTIFICATE-----";
@@ -84,8 +81,6 @@ public class CarrierKeyDownloadManager extends Handler {
// This will define the end date of the window.
private static final int END_RENEWAL_WINDOW_DAYS = 7;
-
-
/* Intent for downloading the public key */
private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX =
"com.android.internal.telephony.carrier_key_download_alarm";
@@ -93,10 +88,6 @@ public class CarrierKeyDownloadManager extends Handler {
@VisibleForTesting
public int mKeyAvailability = 0;
- public static final String MNC = "MNC";
- public static final String MCC = "MCC";
- private static final String SEPARATOR = ":";
-
private static final String JSON_CERTIFICATE = "certificate";
private static final String JSON_CERTIFICATE_ALTERNATE = "public-key";
private static final String JSON_TYPE = "key-type";
@@ -118,42 +109,58 @@ public class CarrierKeyDownloadManager extends Handler {
private String mURL;
private boolean mAllowedOverMeteredNetwork = false;
+ @VisibleForTesting
+ public String mMccMncForDownload;
+ @VisibleForTesting
+ public long mDownloadId;
+
public CarrierKeyDownloadManager(Phone phone) {
mPhone = phone;
mContext = phone.getContext();
IntentFilter filter = new IntentFilter();
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
- filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId());
+ filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX);
filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
mContext.registerReceiver(mBroadcastReceiver, filter, null, phone);
mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
}
+ private final BroadcastReceiver mDownloadReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
+ Log.d(LOG_TAG, "Download Complete");
+ sendMessage(obtainMessage(EVENT_DOWNLOAD_COMPLETE,
+ intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)));
+ }
+ }
+ };
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- int slotId = mPhone.getPhoneId();
- if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) {
- Log.d(LOG_TAG, "Handling key renewal alarm: " + action);
- sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+ int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
+ int phoneId = mPhone.getPhoneId();
+ if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX)) {
+ int slotIndexExtra = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, -1);
+ if (slotIndexExtra == slotIndex) {
+ Log.d(LOG_TAG, "Handling key renewal alarm: " + action);
+ sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
+ }
} else if (action.equals(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD)) {
- if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
+ if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
Log.d(LOG_TAG, "Handling reset intent: " + action);
sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
}
} else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
- if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
+ if (phoneId == intent.getIntExtra(PhoneConstants.PHONE_KEY,
SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
Log.d(LOG_TAG, "Carrier Config changed: " + action);
sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE);
}
- } else if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
- Log.d(LOG_TAG, "Download Complete");
- sendMessage(obtainMessage(EVENT_DOWNLOAD_COMPLETE,
- intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)));
}
}
};
@@ -166,9 +173,9 @@ public class CarrierKeyDownloadManager extends Handler {
break;
case EVENT_DOWNLOAD_COMPLETE:
long carrierKeyDownloadIdentifier = (long) msg.obj;
- String mccMnc = getMccMncSetFromPref();
- if (isValidDownload(mccMnc)) {
- onDownloadComplete(carrierKeyDownloadIdentifier, mccMnc);
+ String currentMccMnc = getSimOperator();
+ if (isValidDownload(currentMccMnc, carrierKeyDownloadIdentifier)) {
+ onDownloadComplete(carrierKeyDownloadIdentifier, currentMccMnc);
onPostDownloadProcessing(carrierKeyDownloadIdentifier);
}
break;
@@ -177,14 +184,17 @@ public class CarrierKeyDownloadManager extends Handler {
private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) {
resetRenewalAlarm();
- cleanupDownloadPreferences(carrierKeyDownloadIdentifier);
+ cleanupDownloadInfo();
+
+ // unregister from DOWNLOAD_COMPLETE
+ mContext.unregisterReceiver(mDownloadReceiver);
}
private void handleAlarmOrConfigChange() {
if (carrierUsesKeys()) {
if (areCarrierKeysAbsentOrExpiring()) {
boolean downloadStartedSuccessfully = downloadKey();
- // if the download was attemped, but not started successfully, and if carriers uses
+ // if the download was attempted, but not started successfully, and if carriers uses
// keys, we'll still want to renew the alarms, and try downloading the key a day
// later.
if (!downloadStartedSuccessfully) {
@@ -196,20 +206,22 @@ public class CarrierKeyDownloadManager extends Handler {
} else {
// delete any existing alarms.
cleanupRenewalAlarms();
+ mPhone.deleteCarrierInfoForImsiEncryption();
}
}
- private void cleanupDownloadPreferences(long carrierKeyDownloadIdentifier) {
- Log.d(LOG_TAG, "Cleaning up download preferences: " + carrierKeyDownloadIdentifier);
- SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
- editor.remove(String.valueOf(carrierKeyDownloadIdentifier));
- editor.commit();
+ private void cleanupDownloadInfo() {
+ Log.d(LOG_TAG, "Cleaning up download info");
+ mDownloadId = -1;
+ mMccMncForDownload = null;
+
}
private void cleanupRenewalAlarms() {
Log.d(LOG_TAG, "Cleaning up existing renewal alarms");
- int slotId = mPhone.getPhoneId();
- Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId);
+ int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
+ Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX);
+ intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex);
PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
AlarmManager alarmManager =
@@ -229,7 +241,7 @@ public class CarrierKeyDownloadManager extends Handler {
continue;
}
ImsiEncryptionInfo imsiEncryptionInfo =
- mPhone.getCarrierInfoForImsiEncryption(key_type);
+ mPhone.getCarrierInfoForImsiEncryption(key_type, false);
if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) {
if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) {
minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime();
@@ -264,27 +276,20 @@ public class CarrierKeyDownloadManager extends Handler {
@VisibleForTesting
public void resetRenewalAlarm() {
cleanupRenewalAlarms();
- int slotId = mPhone.getPhoneId();
+ int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
long minExpirationDate = getExpirationDate();
Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate));
final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
- Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId);
+ Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX);
+ intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex);
PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
alarmManager.set(AlarmManager.RTC_WAKEUP, minExpirationDate, carrierKeyDownloadIntent);
- Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time="
+ Log.d(LOG_TAG, "setRenewalAlarm: action=" + intent.getAction() + " time="
+ new Date(minExpirationDate));
}
- private String getMccMncSetFromPref() {
- // check if this is a download that we had created. We do this by checking if the
- // downloadId is stored in the shared prefs.
- int slotId = mPhone.getPhoneId();
- SharedPreferences preferences = getDefaultSharedPreferences(mContext);
- return preferences.getString(MCC_MNC_PREF_TAG + slotId, null);
- }
-
/**
* Returns the sim operator.
**/
@@ -301,31 +306,21 @@ public class CarrierKeyDownloadManager extends Handler {
* instance of the phone.
**/
@VisibleForTesting
- public boolean isValidDownload(String mccMnc) {
- String mccCurrent = "";
- String mncCurrent = "";
- String mccSource = "";
- String mncSource = "";
-
- String simOperator = getSimOperator();
- if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) {
- Log.e(LOG_TAG, "simOperator or mcc/mnc is empty");
+ public boolean isValidDownload(String currentMccMnc, long currentDownloadId) {
+ if (currentDownloadId != mDownloadId) {
+ Log.e(LOG_TAG, "download ID=" + currentDownloadId
+ + " for completed download does not match stored id=" + mDownloadId);
return false;
}
- String[] splitValue = mccMnc.split(SEPARATOR);
- mccSource = splitValue[0];
- mncSource = splitValue[1];
- Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource);
-
- mccCurrent = simOperator.substring(0, 3);
- mncCurrent = simOperator.substring(3);
- Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent);
-
- if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) {
- return true;
+ if (TextUtils.isEmpty(currentMccMnc) || TextUtils.isEmpty(mMccMncForDownload)
+ || !TextUtils.equals(currentMccMnc, mMccMncForDownload)) {
+ Log.e(LOG_TAG, "currentMccMnc=" + currentMccMnc + " stored=" + mMccMncForDownload);
+ return false;
}
- return false;
+
+ Log.d(LOG_TAG, "Matched MccMnc, downloadId: " + currentMccMnc + "," + currentDownloadId);
+ return true;
}
/**
@@ -337,7 +332,6 @@ public class CarrierKeyDownloadManager extends Handler {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(carrierKeyDownloadIdentifier);
Cursor cursor = mDownloadManager.query(query);
- InputStream source = null;
if (cursor == null) {
return;
@@ -346,21 +340,18 @@ public class CarrierKeyDownloadManager extends Handler {
int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
try {
- source = new FileInputStream(
- mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier)
- .getFileDescriptor());
- jsonStr = convertToString(source);
+ jsonStr = convertToString(mDownloadManager, carrierKeyDownloadIdentifier);
+ if (TextUtils.isEmpty(jsonStr)) {
+ Log.d(LOG_TAG, "fallback to no gzip");
+ jsonStr = convertToStringNoGZip(mDownloadManager,
+ carrierKeyDownloadIdentifier);
+ }
parseJsonAndPersistKey(jsonStr, mccMnc);
} catch (Exception e) {
Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier
+ ". " + e);
} finally {
mDownloadManager.remove(carrierKeyDownloadIdentifier);
- try {
- source.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
}
}
Log.d(LOG_TAG, "Completed downloading keys");
@@ -389,8 +380,10 @@ public class CarrierKeyDownloadManager extends Handler {
mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING);
mAllowedOverMeteredNetwork = b.getBoolean(
KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
- if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) {
- Log.d(LOG_TAG, "Carrier not enabled or invalid values");
+ if (mKeyAvailability == 0 || TextUtils.isEmpty(mURL)) {
+ Log.d(LOG_TAG,
+ "Carrier not enabled or invalid values. mKeyAvailability=" + mKeyAvailability
+ + " mURL=" + mURL);
return false;
}
for (int key_type : CARRIER_KEY_TYPES) {
@@ -401,13 +394,31 @@ public class CarrierKeyDownloadManager extends Handler {
return false;
}
- private static String convertToString(InputStream is) {
- try {
- // The current implementation at certain Carriers has the data gzipped, which requires
- // us to unzip the contents. Longer term, we want to add a flag in carrier config which
- // determines if the data needs to be zipped or not.
- GZIPInputStream gunzip = new GZIPInputStream(is);
- BufferedReader reader = new BufferedReader(new InputStreamReader(gunzip, UTF_8));
+ private static String convertToStringNoGZip(DownloadManager downloadManager, long downloadId) {
+ StringBuilder sb = new StringBuilder();
+ try (InputStream source = new FileInputStream(
+ downloadManager.openDownloadedFile(downloadId).getFileDescriptor())) {
+ // If the carrier does not have the data gzipped, fallback to assuming it is not zipped.
+ // parseJsonAndPersistKey may still fail if the data is malformed, so we won't be
+ // persisting random bogus strings thinking it's the cert
+ BufferedReader reader = new BufferedReader(new InputStreamReader(source, UTF_8));
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append('\n');
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ return sb.toString();
+ }
+
+ private static String convertToString(DownloadManager downloadManager, long downloadId) {
+ try (InputStream source = new FileInputStream(
+ downloadManager.openDownloadedFile(downloadId).getFileDescriptor());
+ InputStream gzipIs = new GZIPInputStream(source)) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(gzipIs, UTF_8));
StringBuilder sb = new StringBuilder();
String line;
@@ -415,10 +426,14 @@ public class CarrierKeyDownloadManager extends Handler {
sb.append(line).append('\n');
}
return sb.toString();
+ } catch (ZipException e) {
+ // GZIPInputStream constructor will throw exception if stream is not GZIP
+ Log.d(LOG_TAG, "Stream is not gzipped e=" + e);
+ return null;
} catch (IOException e) {
- e.printStackTrace();
+ Log.e(LOG_TAG, "Unexpected exception in convertToString e=" + e);
+ return null;
}
- return null;
}
/**
@@ -439,11 +454,8 @@ public class CarrierKeyDownloadManager extends Handler {
return;
}
try {
- String mcc = "";
- String mnc = "";
- String[] splitValue = mccMnc.split(SEPARATOR);
- mcc = splitValue[0];
- mnc = splitValue[1];
+ String mcc = mccMnc.substring(0, 3);
+ String mnc = mccMnc.substring(3);
JSONObject jsonObj = new JSONObject(jsonStr);
JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS);
for (int i = 0; i < keys.length(); i++) {
@@ -484,8 +496,17 @@ public class CarrierKeyDownloadManager extends Handler {
*/
@VisibleForTesting
public boolean isKeyEnabled(int keyType) {
- //since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
- int returnValue = (mKeyAvailability >> (keyType - 1)) & 1;
+ // since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
+ return isKeyEnabled(keyType, mKeyAvailability);
+ }
+
+ /**
+ * introspects the mKeyAvailability bitmask
+ * @return true if the digit at position k is 1, else false.
+ */
+ public static boolean isKeyEnabled(int keyType, int keyAvailability) {
+ // since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
+ int returnValue = (keyAvailability >> (keyType - 1)) & 1;
return (returnValue == 1) ? true : false;
}
@@ -500,8 +521,10 @@ public class CarrierKeyDownloadManager extends Handler {
if (!isKeyEnabled(key_type)) {
continue;
}
+ // get encryption info with fallback=false so that we attempt a download even if there's
+ // backup info stored in carrier config
ImsiEncryptionInfo imsiEncryptionInfo =
- mPhone.getCarrierInfoForImsiEncryption(key_type);
+ mPhone.getCarrierInfoForImsiEncryption(key_type, false);
if (imsiEncryptionInfo == null) {
Log.d(LOG_TAG, "Key not found for: " + key_type);
return true;
@@ -515,38 +538,34 @@ public class CarrierKeyDownloadManager extends Handler {
private boolean downloadKey() {
Log.d(LOG_TAG, "starting download from: " + mURL);
- String mcc = "";
- String mnc = "";
- String simOperator = getSimOperator();
-
- if (!TextUtils.isEmpty(simOperator)) {
- mcc = simOperator.substring(0, 3);
- mnc = simOperator.substring(3);
- Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc);
+ String mccMnc = getSimOperator();
+
+ if (!TextUtils.isEmpty(mccMnc)) {
+ Log.d(LOG_TAG, "downloading key for mccmnc: " + mccMnc);
} else {
- Log.e(LOG_TAG, "mcc, mnc: is empty");
+ Log.e(LOG_TAG, "mccmnc: is empty");
return false;
}
try {
+ // register the broadcast receiver to listen for download complete
+ IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(mDownloadReceiver, filter, null, mPhone);
+
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL));
// TODO(b/128550341): Implement the logic to minimize using metered network such as
// LTE for downloading a certificate.
request.setAllowedOverMetered(mAllowedOverMeteredNetwork);
- request.setVisibleInDownloadsUi(false);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.addRequestHeader("Accept-Encoding", "gzip");
Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
- SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
-
- String mccMnc = mcc + SEPARATOR + mnc;
- int slotId = mPhone.getPhoneId();
- Log.d(LOG_TAG, "storing values in sharedpref mcc, mnc, days: " + mcc + "," + mnc
- + "," + carrierKeyDownloadRequestId);
- editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc);
- editor.commit();
+
+ Log.d(LOG_TAG, "saving values mccmnc, downloadId: " + mccMnc
+ + ", " + carrierKeyDownloadRequestId);
+ mMccMncForDownload = mccMnc;
+ mDownloadId = carrierKeyDownloadRequestId;
} catch (Exception e) {
- Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL);
+ Log.e(LOG_TAG, "exception trying to download key from url: " + mURL);
return false;
}
return true;
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index 276471a29d..8709047a51 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -58,10 +58,13 @@ import android.util.IntArray;
import android.util.LocalLog;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccProfile;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -134,11 +137,11 @@ public class CarrierPrivilegesTracker extends Handler {
private final RegistrantList mRegistrantList;
private final LocalLog mLocalLog;
- // Stores certificate hashes for Carrier Config-loaded certs. Certs must be UPPERCASE.
- private final Set<String> mCarrierConfigCerts;
+ // Stores rules for Carrier Config-loaded rules
+ private final List<UiccAccessRule> mCarrierConfigRules;
- // TODO(b/151981841): Use Set<UiccAccessRule> to also check for package names loaded from SIM
- private final Set<String> mUiccCerts;
+ // Stores rules for SIM-loaded rules.
+ private final List<UiccAccessRule> mUiccRules;
// Map of PackageName -> Certificate hashes for that Package
private final Map<String, Set<String>> mInstalledPackageCerts;
@@ -234,8 +237,8 @@ public class CarrierPrivilegesTracker extends Handler {
mContext.registerReceiver(mIntentReceiver, packageFilter);
mRegistrantList = new RegistrantList();
- mCarrierConfigCerts = new ArraySet<>();
- mUiccCerts = new ArraySet<>();
+ mCarrierConfigRules = new ArrayList<>();
+ mUiccRules = new ArrayList<>();
mInstalledPackageCerts = new ArrayMap<>();
mCachedUids = new ArrayMap<>();
mPrivilegedUids = new int[0];
@@ -297,71 +300,72 @@ public class CarrierPrivilegesTracker extends Handler {
private void handleCarrierConfigUpdated(int subId, int slotIndex) {
if (slotIndex != mPhone.getPhoneId()) return;
- Set<String> updatedCarrierConfigCerts = Collections.EMPTY_SET;
+ List<UiccAccessRule> updatedCarrierConfigRules = Collections.EMPTY_LIST;
// Carrier Config broadcasts with INVALID_SUBSCRIPTION_ID when the SIM is removed. This is
// an expected event. When this happens, clear the certificates from the previous configs.
- // The certs will be cleared in maybeUpdateCertsAndNotifyRegistrants() below.
+ // The rules will be cleared in maybeUpdateRulesAndNotifyRegistrants() below.
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- updatedCarrierConfigCerts = getCarrierConfigCerts(subId);
+ updatedCarrierConfigRules = getCarrierConfigRules(subId);
}
mLocalLog.log("CarrierConfigUpdated:"
+ " subId=" + subId
+ " slotIndex=" + slotIndex
- + " updated CarrierConfig certs=" + updatedCarrierConfigCerts);
- maybeUpdateCertsAndNotifyRegistrants(mCarrierConfigCerts, updatedCarrierConfigCerts);
+ + " updated CarrierConfig rules=" + updatedCarrierConfigRules);
+ maybeUpdateRulesAndNotifyRegistrants(mCarrierConfigRules, updatedCarrierConfigRules);
}
- private Set<String> getCarrierConfigCerts(int subId) {
+ private List<UiccAccessRule> getCarrierConfigRules(int subId) {
PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
if (!mCarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) {
- return Collections.EMPTY_SET;
+ return Collections.EMPTY_LIST;
}
- Set<String> updatedCarrierConfigCerts = new ArraySet<>();
- String[] carrierConfigCerts =
+ String[] carrierConfigRules =
carrierConfigs.getStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
-
- if (carrierConfigCerts != null) {
- for (String cert : carrierConfigCerts) {
- updatedCarrierConfigCerts.add(cert.toUpperCase());
- }
+ if (carrierConfigRules == null) {
+ return Collections.EMPTY_LIST;
}
- return updatedCarrierConfigCerts;
+ return Arrays.asList(UiccAccessRule.decodeRulesFromCarrierConfig(carrierConfigRules));
}
private void handleSimStateChanged(int slotId, int simState) {
if (slotId != mPhone.getPhoneId()) return;
- Set<String> updatedUiccCerts = Collections.EMPTY_SET;
+ List<UiccAccessRule> updatedUiccRules = Collections.EMPTY_LIST;
- // Only include the UICC certs if the SIM is fully loaded
+ // Only include the UICC rules if the SIM is fully loaded
if (simState == SIM_STATE_LOADED) {
- updatedUiccCerts = getSimCerts();
+ updatedUiccRules = getSimRules();
}
mLocalLog.log("SIM State Changed:"
+ " slotId=" + slotId
+ " simState=" + simState
- + " updated SIM-loaded certs=" + updatedUiccCerts);
- maybeUpdateCertsAndNotifyRegistrants(mUiccCerts, updatedUiccCerts);
+ + " updated SIM-loaded rules=" + updatedUiccRules);
+ maybeUpdateRulesAndNotifyRegistrants(mUiccRules, updatedUiccRules);
}
- private Set<String> getSimCerts() {
- Set<String> updatedUiccCerts = Collections.EMPTY_SET;
- TelephonyManager telMan = mTelephonyManager.createForSubscriptionId(mPhone.getSubId());
-
- if (telMan.hasIccCard(mPhone.getPhoneId())) {
- updatedUiccCerts = new ArraySet<>();
- List<String> uiccCerts = telMan.getCertsFromCarrierPrivilegeAccessRules();
- if (uiccCerts != null) {
- for (String cert : uiccCerts) {
- updatedUiccCerts.add(cert.toUpperCase());
- }
- }
+ private List<UiccAccessRule> getSimRules() {
+ if (!mTelephonyManager.hasIccCard(mPhone.getPhoneId())) {
+ return Collections.EMPTY_LIST;
+ }
+ UiccCard uiccCard = mPhone.getUiccCard();
+ if (uiccCard == null) {
+ Rlog.w(
+ TAG,
+ "Null UiccCard, but hasIccCard was true for phoneId " + mPhone.getPhoneId());
+ return Collections.EMPTY_LIST;
}
- return updatedUiccCerts;
+ UiccProfile uiccProfile = uiccCard.getUiccProfile();
+ if (uiccProfile == null) {
+ Rlog.w(
+ TAG,
+ "Null UiccProfile, but hasIccCard was true for phoneId " + mPhone.getPhoneId());
+ return Collections.EMPTY_LIST;
+ }
+ return uiccProfile.getCarrierPrivilegeAccessRules();
}
private void handlePackageAddedOrReplaced(String pkgName) {
@@ -409,11 +413,11 @@ public class CarrierPrivilegesTracker extends Handler {
}
private void handleInitializeTracker() {
- // Cache CarrierConfig Certs
- mCarrierConfigCerts.addAll(getCarrierConfigCerts(mPhone.getSubId()));
+ // Cache CarrierConfig rules
+ mCarrierConfigRules.addAll(getCarrierConfigRules(mPhone.getSubId()));
- // Cache SIM certs
- mUiccCerts.addAll(getSimCerts());
+ // Cache SIM rules
+ mUiccRules.addAll(getSimRules());
// Cache all installed packages and their certs
int flags =
@@ -431,8 +435,8 @@ public class CarrierPrivilegesTracker extends Handler {
maybeUpdatePrivilegedUidsAndNotifyRegistrants();
String msg = "Initializing state:"
- + " CarrierConfig certs=" + mCarrierConfigCerts
- + " SIM-loaded certs=" + mUiccCerts;
+ + " CarrierConfig rules=" + mCarrierConfigRules
+ + " SIM-loaded rules=" + mUiccRules;
if (VDBG) {
msg += " installed pkgs=" + getObfuscatedPackages();
}
@@ -447,12 +451,12 @@ public class CarrierPrivilegesTracker extends Handler {
return obfuscatedPkgs.toString();
}
- private void maybeUpdateCertsAndNotifyRegistrants(
- Set<String> currentCerts, Set<String> updatedCerts) {
- if (currentCerts.equals(updatedCerts)) return;
+ private void maybeUpdateRulesAndNotifyRegistrants(
+ List<UiccAccessRule> currentRules, List<UiccAccessRule> updatedRules) {
+ if (currentRules.equals(updatedRules)) return;
- currentCerts.clear();
- currentCerts.addAll(updatedCerts);
+ currentRules.clear();
+ currentRules.addAll(updatedRules);
maybeUpdatePrivilegedUidsAndNotifyRegistrants();
}
@@ -473,7 +477,7 @@ public class CarrierPrivilegesTracker extends Handler {
private int[] getCurrentPrivilegedUidsForAllUsers() {
Set<Integer> privilegedUids = new ArraySet<>();
for (Map.Entry<String, Set<String>> e : mInstalledPackageCerts.entrySet()) {
- if (isPackagePrivileged(e.getValue())) {
+ if (isPackagePrivileged(e.getKey(), e.getValue())) {
privilegedUids.addAll(getUidsForPackage(e.getKey()));
}
}
@@ -487,11 +491,25 @@ public class CarrierPrivilegesTracker extends Handler {
/**
* Returns true iff there is an overlap between the provided certificate hashes and the
- * certificate hashes stored in mCarrierConfigCerts and mUiccCerts.
+ * certificate hashes stored in mCarrierConfigRules and mUiccRules.
*/
- private boolean isPackagePrivileged(Set<String> certs) {
- return !Collections.disjoint(mCarrierConfigCerts, certs)
- || !Collections.disjoint(mUiccCerts, certs);
+ private boolean isPackagePrivileged(String pkgName, Set<String> certs) {
+ // Double-nested for loops, but each collection should contain at most 2 elements in nearly
+ // every case.
+ // TODO(b/184382310) find a way to speed this up
+ for (String cert : certs) {
+ for (UiccAccessRule rule : mCarrierConfigRules) {
+ if (rule.matches(cert, pkgName)) {
+ return true;
+ }
+ }
+ for (UiccAccessRule rule : mUiccRules) {
+ if (rule.matches(cert, pkgName)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
private Set<Integer> getUidsForPackage(String pkgName) {
@@ -524,8 +542,8 @@ public class CarrierPrivilegesTracker extends Handler {
pw.println("CarrierPrivilegesTracker - Log End ----");
pw.println("CarrierPrivilegesTracker - Privileged UIDs: "
+ Arrays.toString(mPrivilegedUids));
- pw.println("CarrierPrivilegesTracker - SIM-loaded Certs: " + mUiccCerts);
- pw.println("CarrierPrivilegesTracker - CarrierPrivileged Certs: " + mCarrierConfigCerts);
+ pw.println("CarrierPrivilegesTracker - SIM-loaded rules: " + mUiccRules);
+ pw.println("CarrierPrivilegesTracker - Carrier config rules: " + mCarrierConfigRules);
if (VDBG) {
pw.println("CarrierPrivilegesTracker - Obfuscated Pkgs + Certs: "
+ getObfuscatedPackages());
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
index d5b630a9b6..794bb41527 100644
--- a/src/java/com/android/internal/telephony/CarrierResolver.java
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -16,13 +16,14 @@
package com.android.internal.telephony;
import static android.provider.Telephony.CarrierId;
-import static android.provider.Telephony.Carriers.CONTENT_URI;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
@@ -30,6 +31,7 @@ import android.os.Handler;
import android.os.Message;
import android.provider.Telephony;
import android.service.carrier.CarrierIdentifier;
+import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -38,9 +40,11 @@ import android.util.LocalLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.CarrierIdMatchStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -70,6 +74,12 @@ public class CarrierResolver extends Handler {
private static final Uri CONTENT_URL_PREFER_APN = Uri.withAppendedPath(
Telephony.Carriers.CONTENT_URI, "preferapn");
+ // Test purpose only.
+ private static final String TEST_ACTION = "com.android.internal.telephony"
+ + ".ACTION_TEST_OVERRIDE_CARRIER_ID";
+
+ // cached version of the carrier list, so that we don't need to re-query it every time.
+ private Integer mCarrierListVersion;
// cached matching rules based mccmnc to speed up resolution
private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>();
// cached carrier Id
@@ -112,6 +122,53 @@ public class CarrierResolver extends Handler {
}
};
+ /**
+ * A broadcast receiver used for overriding carrier id for testing. There are six parameters,
+ * only override_carrier_id is required, the others are options.
+ *
+ * To override carrier id by adb command, e.g.:
+ * adb shell am broadcast -a com.android.internal.telephony.ACTION_TEST_OVERRIDE_CARRIER_ID \
+ * --ei override_carrier_id 1
+ * --ei override_specific_carrier_id 1
+ * --ei override_mno_carrier_id 1
+ * --es override_carrier_name test
+ * --es override_specific_carrier_name test
+ * --ei sub_id 1
+ */
+ private final BroadcastReceiver mCarrierIdTestReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int phoneId = mPhone.getPhoneId();
+ int carrierId = intent.getIntExtra("override_carrier_id",
+ TelephonyManager.UNKNOWN_CARRIER_ID);
+ int specificCarrierId = intent.getIntExtra("override_specific_carrier_id", carrierId);
+ int mnoCarrierId = intent.getIntExtra("override_mno_carrier_id", carrierId);
+ String carrierName = intent.getStringExtra("override_carrier_name");
+ String specificCarrierName = intent.getStringExtra("override_specific_carrier_name");
+ int subId = intent.getIntExtra("sub_id",
+ SubscriptionManager.getDefaultSubscriptionId());
+
+ if (carrierId <= 0) {
+ logd("Override carrier id must be greater than 0.", phoneId);
+ return;
+ } else if (subId != mPhone.getSubId()) {
+ logd("Override carrier id failed. The sub id doesn't same as phone's sub id.",
+ phoneId);
+ return;
+ } else {
+ logd("Override carrier id to: " + carrierId, phoneId);
+ logd("Override specific carrier id to: " + specificCarrierId, phoneId);
+ logd("Override mno carrier id to: " + mnoCarrierId, phoneId);
+ logd("Override carrier name to: " + carrierName, phoneId);
+ logd("Override specific carrier name to: " + specificCarrierName, phoneId);
+ updateCarrierIdAndName(
+ carrierId, carrierName != null ? carrierName : "",
+ specificCarrierId, specificCarrierName != null ? carrierName : "",
+ mnoCarrierId);
+ }
+ }
+ };
+
public CarrierResolver(Phone phone) {
logd("Creating CarrierResolver[" + phone.getPhoneId() + "]");
mContext = phone.getContext();
@@ -124,6 +181,12 @@ public class CarrierResolver extends Handler {
mContext.getContentResolver().registerContentObserver(
CarrierId.All.CONTENT_URI, false, mContentObserver);
UiccController.getInstance().registerForIccChanged(this, ICC_CHANGED_EVENT, null);
+
+ if (TelephonyUtils.IS_DEBUGGABLE) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TEST_ACTION);
+ mContext.registerReceiver(mCarrierIdTestReceiver, filter);
+ }
}
/**
@@ -169,7 +232,7 @@ public class CarrierResolver extends Handler {
loge("mIccRecords is null on SIM_LOAD_EVENT, could not get SPN");
}
mPreferApn = getPreferApn();
- loadCarrierMatchingRulesOnMccMnc();
+ loadCarrierMatchingRulesOnMccMnc(false /* update carrier config */);
}
private void handleSimAbsent() {
@@ -214,14 +277,16 @@ public class CarrierResolver extends Handler {
handleSimLoaded();
break;
case CARRIER_ID_DB_UPDATE_EVENT:
- loadCarrierMatchingRulesOnMccMnc();
+ // clean the cached carrier list version, so that a new one will be queried.
+ mCarrierListVersion = null;
+ loadCarrierMatchingRulesOnMccMnc(true /* update carrier config*/);
break;
case PREFER_APN_UPDATE_EVENT:
String preferApn = getPreferApn();
if (!equals(mPreferApn, preferApn, true)) {
logd("[updatePreferApn] from:" + mPreferApn + " to:" + preferApn);
mPreferApn = preferApn;
- matchSubscriptionCarrier();
+ matchSubscriptionCarrier(true /* update carrier config*/);
}
break;
case ICC_CHANGED_EVENT:
@@ -247,7 +312,7 @@ public class CarrierResolver extends Handler {
}
}
- private void loadCarrierMatchingRulesOnMccMnc() {
+ private void loadCarrierMatchingRulesOnMccMnc(boolean updateCarrierConfig) {
try {
String mccmnc = mTelephonyMgr.getSimOperatorNumericForPhone(mPhone.getPhoneId());
Cursor cursor = mContext.getContentResolver().query(
@@ -265,7 +330,10 @@ public class CarrierResolver extends Handler {
while (cursor.moveToNext()) {
mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
}
- matchSubscriptionCarrier();
+ matchSubscriptionCarrier(updateCarrierConfig);
+
+ // Generate metrics related to carrier ID table version.
+ CarrierIdMatchStats.sendCarrierIdTableVersion(getCarrierListVersion());
}
} finally {
if (cursor != null) {
@@ -744,10 +812,22 @@ public class CarrierResolver extends Handler {
TelephonyManager.UNKNOWN_CARRIER_ID);
}
+ private void updateCarrierConfig() {
+ IccCard iccCard = mPhone.getIccCard();
+ IccCardConstants.State simState = IccCardConstants.State.UNKNOWN;
+ if (iccCard != null) {
+ simState = iccCard.getState();
+ }
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ configManager.updateConfigForPhoneId(mPhone.getPhoneId(),
+ UiccController.getIccStateIntentString(simState));
+ }
+
/**
* find the best matching carrier from candidates with matched subscription MCCMNC.
*/
- private void matchSubscriptionCarrier() {
+ private void matchSubscriptionCarrier(boolean updateCarrierConfig) {
if (!SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) {
logd("[matchSubscriptionCarrier]" + "skip before sim records loaded");
return;
@@ -805,6 +885,11 @@ public class CarrierResolver extends Handler {
updateCarrierIdAndName(maxRuleParent.mCid, maxRuleParent.mName,
maxRule.mCid, maxRule.mName,
(mnoRule == null) ? maxRule.mCid : mnoRule.mCid);
+
+ if (updateCarrierConfig) {
+ logd("[matchSubscriptionCarrier] - Calling updateCarrierConfig()");
+ updateCarrierConfig();
+ }
}
/*
@@ -853,14 +938,26 @@ public class CarrierResolver extends Handler {
TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
unknownMccmncToLog, unknownGid1ToLog, simInfo);
+
+ // Generate statsd metrics only when MCC/MNC is unknown or there is no match for GID1.
+ if (unknownMccmncToLog != null || unknownGid1ToLog != null) {
+ // Pass the PNN value to metrics only if the SPN is empty
+ String pnn = TextUtils.isEmpty(subscriptionRule.spn) ? subscriptionRule.plmn : "";
+ CarrierIdMatchStats.onCarrierIdMismatch(
+ mCarrierId, unknownMccmncToLog, unknownGid1ToLog, subscriptionRule.spn, pnn);
+ }
}
public int getCarrierListVersion() {
- final Cursor cursor = mContext.getContentResolver().query(
- Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
- "get_version"), null, null, null);
- cursor.moveToFirst();
- return cursor.getInt(0);
+ // Use the cached value if it exists, otherwise retrieve it.
+ if (mCarrierListVersion == null) {
+ final Cursor cursor = mContext.getContentResolver().query(
+ Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
+ "get_version"), null, null, null);
+ cursor.moveToFirst();
+ mCarrierListVersion = cursor.getInt(0);
+ }
+ return mCarrierListVersion;
}
public int getCarrierId() {
@@ -1068,6 +1165,11 @@ public class CarrierResolver extends Handler {
private static void loge(String str) {
Rlog.e(LOG_TAG, str);
}
+
+ private static void logd(String str, int phoneId) {
+ Rlog.d(LOG_TAG + "[" + phoneId + "]", str);
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println("mCarrierResolverLocalLogs:");
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index b57a9a69b0..69d59afb17 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -26,6 +26,7 @@ import android.content.ServiceConnection;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -34,7 +35,6 @@ import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.service.carrier.CarrierService;
-import android.telephony.PackageChangeReceiver;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -61,7 +61,7 @@ public class CarrierServiceBindHelper {
*/
private static final int UNBIND_DELAY_MILLIS = 30 * 1000; // 30 seconds
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Context mContext;
@VisibleForTesting
public SparseArray<AppBinding> mBindings = new SparseArray();
@@ -94,7 +94,7 @@ public class CarrierServiceBindHelper {
@VisibleForTesting
public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 2;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public Handler mHandler = new Handler() {
@Override
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index bfa89e0d3d..64dc7ecee9 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -24,19 +24,22 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Message;
import android.os.PersistableBundle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
+import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.NetworkTypeBitMask;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.telephony.Rlog;
@@ -71,9 +74,34 @@ public class CarrierServiceStateTracker extends Handler {
@VisibleForTesting
public static final String PREF_NETWORK_NOTIFICATION_TAG = "PrefNetworkNotification";
+ private long mAllowedNetworkType = -1;
+ private AllowedNetworkTypesListener mAllowedNetworkTypesListener;
+ private TelephonyManager mTelephonyManager;
+
+ /**
+ * The listener for allowed network types changed
+ */
+ @VisibleForTesting
+ public class AllowedNetworkTypesListener extends TelephonyCallback
+ implements TelephonyCallback.AllowedNetworkTypesListener {
+ @Override
+ public void onAllowedNetworkTypesChanged(int reason, long newAllowedNetworkType) {
+ if (reason != TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER) {
+ return;
+ }
+
+ if (mAllowedNetworkType != newAllowedNetworkType) {
+ mAllowedNetworkType = newAllowedNetworkType;
+ handleAllowedNetworkTypeChanged();
+ }
+ }
+ }
+
public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst) {
this.mPhone = phone;
this.mSST = sst;
+ mTelephonyManager = mPhone.getContext().getSystemService(
+ TelephonyManager.class).createForSubscriptionId(mPhone.getSubId());
phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
// Listen for subscriber changes
@@ -84,44 +112,42 @@ public class CarrierServiceStateTracker extends Handler {
int subId = mPhone.getSubId();
if (mPreviousSubId != subId) {
mPreviousSubId = subId;
- registerPrefNetworkModeObserver();
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(
+ mPhone.getSubId());
+ registerAllowedNetworkTypesListener();
}
}
});
registerNotificationTypes();
- registerPrefNetworkModeObserver();
+ mAllowedNetworkType = RadioAccessFamily.getNetworkTypeFromRaf(
+ (int) mPhone.getAllowedNetworkTypes(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
+ mAllowedNetworkTypesListener = new AllowedNetworkTypesListener();
+ registerAllowedNetworkTypesListener();
}
- private ContentObserver mPrefNetworkModeObserver = new ContentObserver(this) {
- @Override
- public void onChange(boolean selfChange) {
- handlePrefNetworkModeChanged();
- }
- };
-
/**
- * Return preferred network mode observer
+ * Return preferred network mode listener
*/
@VisibleForTesting
- public ContentObserver getContentObserver() {
- return mPrefNetworkModeObserver;
+ public AllowedNetworkTypesListener getAllowedNetworkTypesChangedListener() {
+ return mAllowedNetworkTypesListener;
}
- private void registerPrefNetworkModeObserver() {
+ private void registerAllowedNetworkTypesListener() {
int subId = mPhone.getSubId();
- unregisterPrefNetworkModeObserver();
+ unregisterAllowedNetworkTypesListener();
if (SubscriptionManager.isValidSubscriptionId(subId)) {
- mPhone.getContext().getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.PREFERRED_NETWORK_MODE + subId),
- true,
- mPrefNetworkModeObserver);
+ if (mTelephonyManager != null) {
+ mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this),
+ mAllowedNetworkTypesListener);
+ }
}
}
- private void unregisterPrefNetworkModeObserver() {
- mPhone.getContext().getContentResolver().unregisterContentObserver(
- mPrefNetworkModeObserver);
+ private void unregisterAllowedNetworkTypesListener() {
+ mTelephonyManager.unregisterTelephonyCallback(mAllowedNetworkTypesListener);
}
/**
@@ -196,13 +222,10 @@ public class CarrierServiceStateTracker extends Handler {
* Returns true if the preferred network is set to 'Global'.
*/
private boolean isGlobalMode() {
- Context context = mPhone.getContext();
int preferredNetworkSetting = -1;
try {
- preferredNetworkSetting =
- android.provider.Settings.Global.getInt(context.getContentResolver(),
- android.provider.Settings.Global.PREFERRED_NETWORK_MODE
- + mPhone.getSubId(), Phone.PREFERRED_NT_MODE);
+ preferredNetworkSetting = PhoneFactory.calculatePreferredNetworkType(
+ mPhone.getPhoneId());
} catch (Exception e) {
Rlog.e(LOG_TAG, "Unable to get PREFERRED_NETWORK_MODE.");
return true;
@@ -210,9 +233,11 @@ public class CarrierServiceStateTracker extends Handler {
if (isNrSupported()) {
return (preferredNetworkSetting
- == RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA);
+ == RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA));
} else {
- return (preferredNetworkSetting == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ return (preferredNetworkSetting == RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
}
}
@@ -225,7 +250,9 @@ public class CarrierServiceStateTracker extends Handler {
boolean isRadioAccessFamilySupported = checkSupportedBitmask(
tm.getSupportedRadioAccessFamily(), TelephonyManager.NETWORK_TYPE_BITMASK_NR);
boolean isNrNetworkTypeAllowed = checkSupportedBitmask(
- tm.getAllowedNetworkTypes(), TelephonyManager.NETWORK_TYPE_BITMASK_NR);
+ tm.getAllowedNetworkTypesForReason(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER),
+ TelephonyManager.NETWORK_TYPE_BITMASK_NR);
Rlog.i(LOG_TAG, "isNrSupported: " + " carrierConfigEnabled: " + isCarrierConfigEnabled
+ ", AccessFamilySupported: " + isRadioAccessFamilySupported
@@ -246,7 +273,9 @@ public class CarrierServiceStateTracker extends Handler {
Rlog.e(LOG_TAG, "isCarrierConfigEnableNr: Cannot get config " + mPhone.getSubId());
return false;
}
- return config.getBoolean(CarrierConfigManager.KEY_NR_ENABLED_BOOL);
+ int[] nrAvailabilities = config.getIntArray(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+ return !ArrayUtils.isEmpty(nrAvailabilities);
}
private boolean checkSupportedBitmask(@NetworkTypeBitMask long supportedBitmask,
@@ -261,7 +290,7 @@ public class CarrierServiceStateTracker extends Handler {
}
}
- private void handlePrefNetworkModeChanged() {
+ private void handleAllowedNetworkTypeChanged() {
NotificationType notificationType = mNotificationTypeMap.get(NOTIFICATION_PREF_NETWORK);
if (notificationType != null) {
evaluateSendingMessageOrCancelNotification(notificationType);
@@ -369,7 +398,7 @@ public class CarrierServiceStateTracker extends Handler {
* Dispose the CarrierServiceStateTracker.
*/
public void dispose() {
- unregisterPrefNetworkModeObserver();
+ unregisterAllowedNetworkTypesListener();
}
/**
diff --git a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
index c8404b442c..6bc2450f4b 100644
--- a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
+++ b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
@@ -25,8 +25,9 @@ import android.os.Handler;
import android.os.Message;
import android.service.carrier.CarrierMessagingService;
import android.service.carrier.CarrierMessagingServiceWrapper;
-import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper;
+import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback;
import android.service.carrier.MessagePdu;
+import android.telephony.AnomalyReporter;
import android.util.LocalLog;
import com.android.internal.annotations.VisibleForTesting;
@@ -40,10 +41,18 @@ import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
/**
* Filters incoming SMS with carrier services.
- * <p> A new instance must be created for filtering each message.
+ *
+ * <p>A new instance must be created for filtering each message.
+ *
+ * <p>Note that if a carrier services app is unavailable at the time a message is received because
+ * credential-encrypted storage is unavailable and it is not direct-boot aware, and the message ends
+ * up being handled by a filter further down the chain, that message will not be redelivered to the
+ * carrier app once the user unlocks the storage.
*/
public class CarrierServicesSmsFilter {
protected static final boolean DBG = true;
@@ -53,6 +62,10 @@ public class CarrierServicesSmsFilter {
/** onFilterComplete timeout. */
public static final int FILTER_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000; //10 minutes
+ /** SMS anomaly uuid -- CarrierMessagingService did not respond */
+ private static final UUID sAnomalyNoResponseFromCarrierMessagingService =
+ UUID.fromString("94095e8e-b516-4065-a8be-e05b84071002");
+
private final Context mContext;
private final Phone mPhone;
private final byte[][] mPdus;
@@ -89,7 +102,8 @@ public class CarrierServicesSmsFilter {
}
/**
- * @return {@code true} if the SMS was handled by carrier services.
+ * @return {@code true} if the SMS was handled by a carrier application or an ImsService
+ * implementing RCS features.
*/
@VisibleForTesting
public boolean filter() {
@@ -98,10 +112,10 @@ public class CarrierServicesSmsFilter {
if (carrierAppForFiltering.isPresent()) {
smsFilterPackages.add(carrierAppForFiltering.get());
}
- String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
+ String imsRcsPackage = CarrierSmsUtils.getImsRcsPackageForIntent(mContext, mPhone,
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
- if (carrierImsPackage != null) {
- smsFilterPackages.add(carrierImsPackage);
+ if (imsRcsPackage != null) {
+ smsFilterPackages.add(imsRcsPackage);
}
if (mFilterAggregator != null) {
@@ -116,7 +130,7 @@ public class CarrierServicesSmsFilter {
mFilterAggregator = new FilterAggregator(numPackages);
//start the timer
mCallbackTimeoutHandler.sendMessageDelayed(mCallbackTimeoutHandler
- .obtainMessage(EVENT_ON_FILTER_COMPLETE_NOT_CALLED),
+ .obtainMessage(EVENT_ON_FILTER_COMPLETE_NOT_CALLED, mFilterAggregator),
FILTER_COMPLETE_TIMEOUT_MS);
for (String smsFilterPackage : smsFilterPackages) {
filterWithPackage(smsFilterPackage, mFilterAggregator);
@@ -161,7 +175,8 @@ public class CarrierServicesSmsFilter {
CarrierSmsFilter smsFilter = new CarrierSmsFilter(mPdus, mDestPort, mPduFormat,
packageName);
CarrierSmsFilterCallback smsFilterCallback =
- new CarrierSmsFilterCallback(filterAggregator, smsFilter, packageName);
+ new CarrierSmsFilterCallback(filterAggregator,
+ smsFilter.mCarrierMessagingServiceWrapper, packageName);
filterAggregator.addToCallbacks(smsFilterCallback);
smsFilter.filterSms(smsFilterCallback);
@@ -213,13 +228,15 @@ public class CarrierServicesSmsFilter {
* instructed to do so by the carrier messaging service. A new instance must be used for every
* message.
*/
- private final class CarrierSmsFilter extends CarrierMessagingServiceWrapper {
+ private final class CarrierSmsFilter {
private final byte[][] mPdus;
private final int mDestPort;
private final String mSmsFormat;
// Instantiated in filterSms.
private volatile CarrierSmsFilterCallback mSmsFilterCallback;
private final String mPackageName;
+ protected final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
+ new CarrierMessagingServiceWrapper();
CarrierSmsFilter(byte[][] pdus, int destPort, String smsFormat, String packageName) {
mPdus = pdus;
@@ -234,11 +251,13 @@ public class CarrierServicesSmsFilter {
*/
void filterSms(CarrierSmsFilterCallback smsFilterCallback) {
mSmsFilterCallback = smsFilterCallback;
- if (!bindToCarrierMessagingService(mContext, mPackageName)) {
- loge("CarrierSmsFilter::filterSms: bindService() for failed for " + mPackageName);
- smsFilterCallback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
+ if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
+ mContext, mPackageName, runnable -> runnable.run(), ()-> onServiceReady())) {
+ loge("CarrierSmsFilter::filterSms: bindService() failed for " + mPackageName);
+ smsFilterCallback.onReceiveSmsComplete(
+ CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
} else {
- logv("CarrierSmsFilter::filterSms: bindService() for succeeded for "
+ logv("CarrierSmsFilter::filterSms: bindService() succeeded for "
+ mPackageName);
}
}
@@ -247,15 +266,15 @@ public class CarrierServicesSmsFilter {
* Invokes the {@code carrierMessagingService} to filter messages. The filtering result is
* delivered to {@code smsFilterCallback}.
*/
- @Override
- public void onServiceReady() {
+ private void onServiceReady() {
try {
log("onServiceReady: calling filterSms on " + mPackageName);
- filterSms(new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
- mPhone.getSubId(), mSmsFilterCallback);
+ mCarrierMessagingServiceWrapper.receiveSms(
+ new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
+ mPhone.getSubId(), runnable -> runnable.run(), mSmsFilterCallback);
} catch (RuntimeException e) {
loge("Exception filtering the SMS with " + mPackageName + ": " + e);
- mSmsFilterCallback.onFilterComplete(
+ mSmsFilterCallback.onReceiveSmsComplete(
CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
}
}
@@ -265,7 +284,7 @@ public class CarrierServicesSmsFilter {
* A callback used to notify the platform of the carrier messaging app filtering result. Once
* the result is ready, the carrier messaging service connection is disposed.
*/
- private final class CarrierSmsFilterCallback extends CarrierMessagingCallbackWrapper {
+ private final class CarrierSmsFilterCallback implements CarrierMessagingCallback {
private final FilterAggregator mFilterAggregator;
private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper;
private boolean mIsOnFilterCompleteCalled;
@@ -283,15 +302,15 @@ public class CarrierServicesSmsFilter {
* This method should be called only once.
*/
@Override
- public void onFilterComplete(int result) {
+ public void onReceiveSmsComplete(int result) {
log("CarrierSmsFilterCallback::onFilterComplete: Called from " + mPackageName
+ " with result: " + result);
// in the case that timeout has already passed and triggered, but the initial callback
// is run afterwards, we should not follow through
if (!mIsOnFilterCompleteCalled) {
mIsOnFilterCompleteCalled = true;
- mCarrierMessagingServiceWrapper.disposeConnection(mContext);
- mFilterAggregator.onFilterComplete(result);
+ mCarrierMessagingServiceWrapper.disconnect();
+ mFilterAggregator.onFilterComplete(result, this);
}
}
@@ -332,9 +351,10 @@ public class CarrierServicesSmsFilter {
mFilterResult = CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT;
}
- void onFilterComplete(int result) {
+ void onFilterComplete(int result, CarrierSmsFilterCallback callback) {
synchronized (mFilterLock) {
mNumPendingFilters--;
+ mCallbacks.remove(callback);
combine(result);
if (mNumPendingFilters == 0) {
// Calling identity was the CarrierMessagingService in this callback, change it
@@ -382,6 +402,12 @@ public class CarrierServicesSmsFilter {
case EVENT_ON_FILTER_COMPLETE_NOT_CALLED:
mLocalLog.log("CarrierServicesSmsFilter: onFilterComplete timeout: not"
+ " called before " + FILTER_COMPLETE_TIMEOUT_MS + " milliseconds.");
+ FilterAggregator filterAggregator = (FilterAggregator) msg.obj;
+ String packages = filterAggregator.mCallbacks.stream()
+ .map(callback -> callback.mPackageName)
+ .collect(Collectors.joining(", "));
+ AnomalyReporter.reportAnomaly(sAnomalyNoResponseFromCarrierMessagingService,
+ "No response from " + packages);
handleFilterCallbacksTimeout();
break;
}
@@ -390,7 +416,7 @@ public class CarrierServicesSmsFilter {
private void handleFilterCallbacksTimeout() {
for (CarrierSmsFilterCallback callback : mFilterAggregator.mCallbacks) {
log("handleFilterCallbacksTimeout: calling onFilterComplete");
- callback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
+ callback.onReceiveSmsComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
}
}
}
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
index ed7eb47358..1f6385d9f7 100644
--- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY;
+import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -28,6 +29,7 @@ import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
@@ -35,6 +37,7 @@ import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -50,6 +53,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* This class act as an CarrierSignalling Agent.
@@ -98,13 +102,30 @@ public class CarrierSignalAgent extends Handler {
/**
* This is a list of supported signals from CarrierSignalAgent
*/
- private final Set<String> mCarrierSignalList = new HashSet<>(Arrays.asList(
+ private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = new HashSet<>(Arrays.asList(
TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE));
+ private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP =
+ new HashMap<String, String>() {{
+ put(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE);
+ put(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
+ put(TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
+ put(TelephonyManager.ACTION_CARRIER_SIGNAL_RESET,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET);
+ put(TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
+ }};
+
+ private static final Map<String, String> COMPAT_ACTION_TO_NEW_MAP = NEW_ACTION_TO_COMPAT_MAP
+ .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
+
private final LocalLog mErrorLocalLog = new LocalLog(20);
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -140,7 +161,7 @@ public class CarrierSignalAgent extends Handler {
Rlog.e(LOG_TAG, "Register default network exception: " + ar.exception);
return;
}
- final ConnectivityManager connectivityMgr = mPhone.getContext()
+ final ConnectivityManager connectivityMgr = mPhone.getContext()
.getSystemService(ConnectivityManager.class);
if ((boolean) ar.result) {
mNetworkCallback = new ConnectivityManager.NetworkCallback() {
@@ -251,9 +272,15 @@ public class CarrierSignalAgent extends Handler {
}
String[] signals = splitStr[1].split(CARRIER_SIGNAL_DELIMITER);
for (String s : signals) {
- if (!mCarrierSignalList.contains(s)) {
- loge("Invalid signal name: " + s);
- continue;
+ if (!VALID_CARRIER_SIGNAL_ACTIONS.contains(s)) {
+ // It could be a legacy action in the com.android.internal.telephony
+ // namespace. If that's the case, translate it to the new actions.
+ if (COMPAT_ACTION_TO_NEW_MAP.containsKey(s)) {
+ s = COMPAT_ACTION_TO_NEW_MAP.get(s);
+ } else {
+ loge("Invalid signal name: " + s);
+ continue;
+ }
}
Set<ComponentName> componentList = newCachedWakeSignalConfigs.get(s);
if (componentList == null) {
@@ -285,7 +312,7 @@ public class CarrierSignalAgent extends Handler {
/**
* Broadcast the intents explicitly.
- * Some sanity check will be applied before broadcasting.
+ * Some correctness checks will be applied before broadcasting.
* - for non-wakeup(runtime) receivers, make sure the intent is not declared in their manifests
* and apply FLAG_EXCLUDE_STOPPED_PACKAGES to avoid wake-up
* - for wakeup(manifest) receivers, make sure there are matched receivers with registered
@@ -301,7 +328,13 @@ public class CarrierSignalAgent extends Handler {
final PackageManager packageManager = mPhone.getContext().getPackageManager();
for (ComponentName name : receivers) {
Intent signal = new Intent(intent);
- signal.setComponent(name);
+ if (wakeup) {
+ signal.setComponent(name);
+ } else {
+ // Explicit intents won't reach dynamically registered receivers -- set the package
+ // instead.
+ signal.setPackage(name.getPackageName());
+ }
if (wakeup && packageManager.queryBroadcastReceivers(signal,
PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
@@ -320,11 +353,22 @@ public class CarrierSignalAgent extends Handler {
signal.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
if (!wakeup) signal.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
+ Intent compatIntent = null;
+ try {
+ if (mPhone.getContext().getPackageManager()
+ .getApplicationInfo(name.getPackageName(), 0).targetSdkVersion
+ <= Build.VERSION_CODES.R) {
+ compatIntent = createCompatIntent(signal);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // ignore, don't do anything special for compatibility
+ }
try {
- mPhone.getContext().sendBroadcastAsUser(signal, UserHandle.ALL);
+ Intent intentToSend = compatIntent == null ? signal : compatIntent;
+ mPhone.getContext().sendBroadcastAsUser(intentToSend, UserHandle.ALL);
if (DBG) {
- log("Sending signal " + signal.getAction() + ((signal.getComponent() != null)
- ? " to the carrier signal receiver: " + signal.getComponent() : ""));
+ log("Sending signal " + intentToSend.getAction()
+ + " to the carrier signal receiver: " + intentToSend.getComponent());
}
} catch (ActivityNotFoundException e) {
loge("Send broadcast failed: " + e);
@@ -356,6 +400,56 @@ public class CarrierSignalAgent extends Handler {
}
}
+ private static @Nullable Intent createCompatIntent(Intent original) {
+ String compatAction = NEW_ACTION_TO_COMPAT_MAP.get(original.getAction());
+ if (compatAction == null) {
+ Rlog.i(LOG_TAG, "intent action " + original.getAction() + " does not have a"
+ + " compat alternative for component " + original.getComponent());
+ return null;
+ }
+ Intent compatIntent = new Intent(original);
+ compatIntent.setAction(compatAction);
+ for (String extraKey : original.getExtras().keySet()) {
+ switch (extraKey) {
+ case TelephonyManager.EXTRA_REDIRECTION_URL:
+ compatIntent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL,
+ original.getStringExtra(TelephonyManager.EXTRA_REDIRECTION_URL));
+ break;
+ case TelephonyManager.EXTRA_DATA_FAIL_CAUSE:
+ compatIntent.putExtra(TelephonyIntents.EXTRA_ERROR_CODE,
+ original.getIntExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE, -1));
+ break;
+ case TelephonyManager.EXTRA_PCO_ID:
+ compatIntent.putExtra(TelephonyIntents.EXTRA_PCO_ID,
+ original.getIntExtra(TelephonyManager.EXTRA_PCO_ID, -1));
+ break;
+ case TelephonyManager.EXTRA_PCO_VALUE:
+ compatIntent.putExtra(TelephonyIntents.EXTRA_PCO_VALUE,
+ original.getByteArrayExtra(TelephonyManager.EXTRA_PCO_VALUE));
+ break;
+ case TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE:
+ compatIntent.putExtra(TelephonyIntents.EXTRA_DEFAULT_NETWORK_AVAILABLE,
+ original.getBooleanExtra(
+ TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, false));
+ break;
+ case TelephonyManager.EXTRA_APN_TYPE:
+ int apnType = original.getIntExtra(TelephonyManager.EXTRA_APN_TYPE,
+ ApnSetting.TYPE_DEFAULT);
+ compatIntent.putExtra(TelephonyIntents.EXTRA_APN_TYPE_INT, apnType);
+ compatIntent.putExtra(TelephonyIntents.EXTRA_APN_TYPE,
+ ApnSetting.getApnTypesStringFromBitmask(apnType));
+ break;
+ case TelephonyManager.EXTRA_APN_PROTOCOL:
+ int apnProtocol = original.getIntExtra(TelephonyManager.EXTRA_APN_PROTOCOL, -1);
+ compatIntent.putExtra(TelephonyIntents.EXTRA_APN_PROTOCOL_INT, apnProtocol);
+ compatIntent.putExtra(TelephonyIntents.EXTRA_APN_PROTOCOL,
+ ApnSetting.getProtocolStringFromInt(apnProtocol));
+ break;
+ }
+ }
+ return compatIntent;
+ }
+
private void log(String s) {
Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
}
diff --git a/src/java/com/android/internal/telephony/CarrierSmsUtils.java b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
index f78d147623..76a0c23646 100644
--- a/src/java/com/android/internal/telephony/CarrierSmsUtils.java
+++ b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
@@ -22,9 +22,9 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
+import android.telephony.ims.feature.ImsFeature;
+import com.android.internal.telephony.ims.ImsResolver;
import com.android.telephony.Rlog;
import java.util.List;
@@ -36,23 +36,20 @@ public class CarrierSmsUtils {
protected static final boolean VDBG = false;
protected static final String TAG = CarrierSmsUtils.class.getSimpleName();
- private static final String CARRIER_IMS_PACKAGE_KEY =
- CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING;
-
- /** Return a Carrier-overridden IMS package, if it exists and is a CarrierSmsFilter
- *
+ /**
+ * Return the package name of the ImsService that is implementing RCS features for the device.
* @param context calling context
* @param phone object from telephony
* @param intent that should match a CarrierSmsFilter
- * @return the name of the IMS CarrierService package
+ * @return the name of the ImsService implementing RCS features on the device.
*/
@Nullable
- public static String getCarrierImsPackageForIntent(
+ public static String getImsRcsPackageForIntent(
Context context, Phone phone, Intent intent) {
- String carrierImsPackage = getCarrierImsPackage(context, phone);
+ String carrierImsPackage = getImsRcsPackage(phone);
if (carrierImsPackage == null) {
- if (VDBG) Rlog.v(TAG, "No CarrierImsPackage override found");
+ if (VDBG) Rlog.v(TAG, "No ImsService found implementing RCS.");
return null;
}
@@ -71,23 +68,22 @@ public class CarrierSmsUtils {
return null;
}
+ /**
+ * @return the package name of the ImsService that is configured to implement RCS, or null if
+ * there is none configured/available.
+ */
@Nullable
- private static String getCarrierImsPackage(Context context, Phone phone) {
- CarrierConfigManager cm = (CarrierConfigManager) context.getSystemService(
- Context.CARRIER_CONFIG_SERVICE);
- if (cm == null) {
- Rlog.e(TAG, "Failed to retrieve CarrierConfigManager");
+ private static String getImsRcsPackage(Phone phone) {
+ ImsResolver resolver = ImsResolver.getInstance();
+ if (resolver == null) {
+ Rlog.i(TAG, "getImsRcsPackage: Device does not support IMS - skipping");
return null;
}
final long identity = Binder.clearCallingIdentity();
try {
- PersistableBundle config = cm.getConfigForSubId(phone.getSubId());
- if (config == null) {
- if (VDBG) Rlog.v(TAG, "No CarrierConfig for subId:" + phone.getSubId());
- return null;
- }
- return config.getString(CARRIER_IMS_PACKAGE_KEY, null);
+ return resolver.getConfiguredImsServicePackageName(phone.getPhoneId(),
+ ImsFeature.FEATURE_RCS);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java b/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
index a991153e27..30d6129987 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
@@ -74,11 +74,27 @@ public class CellBroadcastServiceManager {
mPhone = phone;
}
+ private boolean cbMessagesDisabledByOem() {
+ if (mContext != null && mContext.getResources() != null) {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_disable_all_cb_messages);
+ } else {
+ return false;
+ }
+ }
+
/**
* Send a GSM CB message to the CellBroadcastServiceManager's handler.
* @param m the message
*/
public void sendGsmMessageToHandler(Message m) {
+ if (cbMessagesDisabledByOem()) {
+ Log.d(TAG, "GSM CB message ignored - CB messages disabled by OEM.");
+ CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_FILTERED,
+ CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__TYPE__GSM,
+ CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__FILTER__DISABLED_BY_OEM);
+ return;
+ }
m.what = EVENT_NEW_GSM_SMS_CB;
mModuleCellBroadcastHandler.sendMessage(m);
}
@@ -88,6 +104,13 @@ public class CellBroadcastServiceManager {
* @param sms the SmsMessage to forward
*/
public void sendCdmaMessageToHandler(SmsMessage sms) {
+ if (cbMessagesDisabledByOem()) {
+ Log.d(TAG, "CDMA CB message ignored - CB messages disabled by OEM.");
+ CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_FILTERED,
+ CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__TYPE__CDMA,
+ CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__FILTER__DISABLED_BY_OEM);
+ return;
+ }
Message m = Message.obtain();
m.what = EVENT_NEW_CDMA_SMS_CB;
m.obj = sms;
@@ -99,6 +122,13 @@ public class CellBroadcastServiceManager {
* @param sms the SCP message
*/
public void sendCdmaScpMessageToHandler(SmsMessage sms, RemoteCallback callback) {
+ if (cbMessagesDisabledByOem()) {
+ Log.d(TAG, "CDMA SCP CB message ignored - CB messages disabled by OEM.");
+ CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_FILTERED,
+ CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__TYPE__CDMA_SPC,
+ CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__FILTER__DISABLED_BY_OEM);
+ return;
+ }
Message m = Message.obtain();
m.what = EVENT_NEW_CDMA_SCP_MESSAGE;
m.obj = Pair.create(sms, callback);
@@ -295,6 +325,17 @@ public class CellBroadcastServiceManager {
pw.println("CellBroadcastServiceManager:");
pw.println(" mEnabled=" + mEnabled);
pw.println(" mCellBroadcastServicePackage=" + mCellBroadcastServicePackage);
+ if (mEnabled) {
+ try {
+ if (sServiceConnection != null && sServiceConnection.mService != null) {
+ sServiceConnection.mService.dump(fd, args);
+ } else {
+ pw.println(" sServiceConnection is null");
+ }
+ } catch (RemoteException e) {
+ pw.println(" mService.dump() threw RemoteException e: " + e.toString());
+ }
+ }
mLocalLog.dump(fd, pw, args);
pw.flush();
}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkService.java b/src/java/com/android/internal/telephony/CellularNetworkService.java
index 3bb38147be..886438adee 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkService.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkService.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.radio.V1_0.RegState;
import android.hardware.radio.V1_4.DataRegStateResult.VopsInfo.hidl_discriminator;
+import android.hardware.radio.V1_6.RegStateResult.AccessTechnologySpecificInfo;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@@ -32,9 +33,11 @@ import android.telephony.LteVopsSupportInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
import android.telephony.NetworkServiceCallback;
+import android.telephony.NrVopsSupportInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.VopsSupportInfo;
import android.text.TextUtils;
import com.android.telephony.Rlog;
@@ -217,8 +220,14 @@ public class CellularNetworkService extends NetworkService {
final int transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
final int domain = NetworkRegistrationInfo.DOMAIN_CS;
+ if (result instanceof android.hardware.radio.V1_6.RegStateResult) {
+ return getNetworkRegistrationInfo1_6(
+ domain,
+ transportType,
+ (android.hardware.radio.V1_6.RegStateResult) result);
+ }
// 1.5 at the top so that we can do an "early exit" from the method
- if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
+ else if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
return getNetworkRegistrationInfo(
domain,
transportType,
@@ -228,9 +237,6 @@ public class CellularNetworkService extends NetworkService {
(android.hardware.radio.V1_0.VoiceRegStateResult) result;
int regState = getRegStateFromHalRegState(voiceRegState.regState);
int networkType = ServiceState.rilRadioTechnologyToNetworkType(voiceRegState.rat);
- if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
- networkType = TelephonyManager.NETWORK_TYPE_LTE;
- }
int reasonForDenial = voiceRegState.reasonForDenial;
boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
boolean cssSupported = voiceRegState.cssSupported;
@@ -251,9 +257,6 @@ public class CellularNetworkService extends NetworkService {
(android.hardware.radio.V1_2.VoiceRegStateResult) result;
int regState = getRegStateFromHalRegState(voiceRegState.regState);
int networkType = ServiceState.rilRadioTechnologyToNetworkType(voiceRegState.rat);
- if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
- networkType = TelephonyManager.NETWORK_TYPE_LTE;
- }
int reasonForDenial = voiceRegState.reasonForDenial;
boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
boolean cssSupported = voiceRegState.cssSupported;
@@ -281,7 +284,6 @@ public class CellularNetworkService extends NetworkService {
int regState = NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
int reasonForDenial = 0;
- boolean isUsingCarrierAggregation = false;
boolean emergencyOnly = false;
int maxDataCalls = 0;
CellIdentity cellIdentity;
@@ -293,8 +295,14 @@ public class CellularNetworkService extends NetworkService {
new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+ if (result instanceof android.hardware.radio.V1_6.RegStateResult) {
+ return getNetworkRegistrationInfo1_6(
+ domain,
+ transportType,
+ (android.hardware.radio.V1_6.RegStateResult) result);
+ }
// 1.5 at the top so that we can do an "early exit" from the method
- if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
+ else if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
return getNetworkRegistrationInfo(
domain,
transportType,
@@ -355,15 +363,10 @@ public class CellularNetworkService extends NetworkService {
List<Integer> availableServices = getAvailableServices(
regState, domain, emergencyOnly);
- if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
- isUsingCarrierAggregation = true;
- networkType = TelephonyManager.NETWORK_TYPE_LTE;
- }
-
return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
reasonForDenial, emergencyOnly, availableServices, cellIdentity, rplmn,
maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable,
- lteVopsSupportInfo, isUsingCarrierAggregation);
+ lteVopsSupportInfo);
}
private @NonNull NetworkRegistrationInfo getNetworkRegistrationInfo(
@@ -380,16 +383,10 @@ public class CellularNetworkService extends NetworkService {
final String rplmn = regResult.registeredPlmn;
final int reasonForDenial = regResult.reasonForDenial;
- // Network Type fixup for carrier aggregation
int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat);
- // In earlier versions of the HAL, LTE_CA was allowed to indicate that the device
- // is on CA; however, that has been superseded by the PHYSICAL_CHANNEL_CONFIG signal.
- // Because some vendors provide both NETWORK_TYPE_LTE_CA *and* PHYSICAL_CHANNEL_CONFIG,
- // this tweak is left for compatibility; however, the network type is no longer allowed
- // to be used to declare that carrier aggregation is in effect, because the other
- // signal provides a much richer information set, and we want to mitigate confusion in
- // how CA information is being provided.
if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
+ // In Radio HAL v1.5, NETWORK_TYPE_LTE_CA is ignored. Callers should use
+ // PhysicalChannelConfig.
networkType = TelephonyManager.NETWORK_TYPE_LTE;
}
@@ -448,7 +445,87 @@ public class CellularNetworkService extends NetworkService {
return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
reasonForDenial, isEmergencyOnly, availableServices, cellIdentity,
rplmn, MAX_DATA_CALLS, isDcNrRestricted, isNrAvailable, isEndcAvailable,
- vopsInfo, false /* isUsingCarrierAggregation */);
+ vopsInfo);
+ }
+ }
+
+ private @NonNull NetworkRegistrationInfo getNetworkRegistrationInfo1_6(
+ int domain, int transportType,
+ android.hardware.radio.V1_6.RegStateResult regResult) {
+
+ // Perform common conversions that aren't domain specific
+ final int regState = getRegStateFromHalRegState(regResult.regState);
+ final boolean isEmergencyOnly = isEmergencyOnly(regResult.regState);
+ final List<Integer> availableServices = getAvailableServices(
+ regState, domain, isEmergencyOnly);
+ final int rejectCause = regResult.reasonForDenial;
+ final CellIdentity cellIdentity = CellIdentity.create(regResult.cellIdentity);
+ final String rplmn = regResult.registeredPlmn;
+ final int reasonForDenial = regResult.reasonForDenial;
+
+ int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat);
+ if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
+ // In Radio HAL v1.5, NETWORK_TYPE_LTE_CA is ignored. Callers should use
+ // PhysicalChannelConfig.
+ networkType = TelephonyManager.NETWORK_TYPE_LTE;
+ }
+
+ // Conditional parameters for specific RANs
+ boolean cssSupported = false;
+ int roamingIndicator = 0;
+ int systemIsInPrl = 0;
+ int defaultRoamingIndicator = 0;
+ boolean isEndcAvailable = false;
+ boolean isNrAvailable = false;
+ boolean isDcNrRestricted = false;
+ VopsSupportInfo vopsInfo = null;
+
+ android.hardware.radio.V1_6.RegStateResult.AccessTechnologySpecificInfo info =
+ regResult.accessTechnologySpecificInfo;
+
+ switch (info.getDiscriminator()) {
+ case AccessTechnologySpecificInfo.hidl_discriminator.cdmaInfo:
+ cssSupported = info.cdmaInfo().cssSupported;
+ roamingIndicator = info.cdmaInfo().roamingIndicator;
+ systemIsInPrl = info.cdmaInfo().systemIsInPrl;
+ defaultRoamingIndicator = info.cdmaInfo().defaultRoamingIndicator;
+ break;
+ case AccessTechnologySpecificInfo.hidl_discriminator.eutranInfo:
+ isDcNrRestricted = info.eutranInfo().nrIndicators.isDcNrRestricted;
+ isNrAvailable = info.eutranInfo().nrIndicators.isNrAvailable;
+ isEndcAvailable = info.eutranInfo().nrIndicators.isEndcAvailable;
+ vopsInfo = convertHalLteVopsSupportInfo(
+ info.eutranInfo().lteVopsInfo.isVopsSupported,
+ info.eutranInfo().lteVopsInfo.isEmcBearerSupported);
+ break;
+ case AccessTechnologySpecificInfo.hidl_discriminator.ngranNrVopsInfo:
+ vopsInfo = new NrVopsSupportInfo(info.ngranNrVopsInfo().vopsSupported,
+ info.ngranNrVopsInfo().emcSupported,
+ info.ngranNrVopsInfo().emfSupported);
+ break;
+ case AccessTechnologySpecificInfo.hidl_discriminator.geranDtmSupported:
+ cssSupported = info.geranDtmSupported();
+ break;
+ default:
+ log("No access tech specific info passes for RegStateResult");
+ break;
+ }
+
+ // build the result based on the domain for the request
+ switch(domain) {
+ case NetworkRegistrationInfo.DOMAIN_CS:
+ return new NetworkRegistrationInfo(domain, transportType, regState,
+ networkType, reasonForDenial, isEmergencyOnly, availableServices,
+ cellIdentity, rplmn, cssSupported, roamingIndicator, systemIsInPrl,
+ defaultRoamingIndicator);
+ default:
+ loge("Unknown domain passed to CellularNetworkService= " + domain);
+ // fall through
+ case NetworkRegistrationInfo.DOMAIN_PS:
+ return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
+ reasonForDenial, isEmergencyOnly, availableServices, cellIdentity,
+ rplmn, MAX_DATA_CALLS, isDcNrRestricted, isNrAvailable, isEndcAvailable,
+ vopsInfo);
}
}
diff --git a/src/java/com/android/internal/telephony/CellularNetworkValidator.java b/src/java/com/android/internal/telephony/CellularNetworkValidator.java
index cee9cea69c..7124703c54 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkValidator.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkValidator.java
@@ -235,7 +235,7 @@ public class CellularNetworkValidator {
*/
public boolean isValidationFeatureSupported() {
return PhoneConfigurationManager.getInstance().getCurrentPhoneCapability()
- .validationBeforeSwitchSupported;
+ .isNetworkValidationBeforeSwitchSupported();
}
@VisibleForTesting
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
index ccd9251a23..72bb6a36f5 100644
--- a/src/java/com/android/internal/telephony/CommandException.java
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.telephony.Rlog;
@@ -24,7 +25,7 @@ import com.android.telephony.Rlog;
* {@hide}
*/
public class CommandException extends RuntimeException {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Error mError;
public enum Error {
@@ -125,6 +126,11 @@ public class CommandException extends RuntimeException {
OEM_ERROR_24,
OEM_ERROR_25,
REQUEST_CANCELLED,
+ SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED,
+ ACCESS_BARRED,
+ BLOCKED_DUE_TO_CALL,
+ RF_HARDWARE_ISSUE,
+ NO_RF_CALIBRATION_INFO,
}
@UnsupportedAppUsage
@@ -325,6 +331,16 @@ public class CommandException extends RuntimeException {
return new CommandException(Error.OEM_ERROR_25);
case RILConstants.REQUEST_CANCELLED:
return new CommandException(Error.REQUEST_CANCELLED);
+ case RILConstants.SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED:
+ return new CommandException(Error.SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED);
+ case RILConstants.ACCESS_BARRED:
+ return new CommandException(Error.ACCESS_BARRED);
+ case RILConstants.BLOCKED_DUE_TO_CALL:
+ return new CommandException(Error.BLOCKED_DUE_TO_CALL);
+ case RILConstants.RF_HARDWARE_ISSUE:
+ return new CommandException(Error.RF_HARDWARE_ISSUE);
+ case RILConstants.NO_RF_CALIBRATION_INFO:
+ return new CommandException(Error.NO_RF_CALIBRATION_INFO);
default:
Rlog.e("GSM", "Unrecognized RIL errno " + ril_errno);
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 40ffd58915..a6ff3fa99a 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.KeepalivePacketData;
import android.net.LinkProperties;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.WorkSource;
@@ -30,13 +31,18 @@ import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
import android.telephony.RadioAccessSpecifier;
import android.telephony.SignalThresholdInfo;
+import android.telephony.TelephonyManager;
+import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
-import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import java.util.List;
@@ -52,6 +58,8 @@ public interface CommandsInterface {
static final int CLIR_INVOCATION = 1; // (restrict CLI presentation)
static final int CLIR_SUPPRESSION = 2; // (allow CLI presentation)
+ // Used as return value for CDMA SS query
+ static final int SS_STATUS_UNKNOWN = 0xff;
// Used as parameters for call forward methods below
static final int CF_ACTION_DISABLE = 0;
@@ -147,9 +155,9 @@ public interface CommandsInterface {
void unregisterForRadioStateChanged(Handler h);
void registerForVoiceRadioTechChanged(Handler h, int what, Object obj);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void unregisterForVoiceRadioTechChanged(Handler h);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void registerForImsNetworkStateChanged(Handler h, int what, Object obj);
void unregisterForImsNetworkStateChanged(Handler h);
@@ -216,6 +224,10 @@ public interface CommandsInterface {
void registerForDataCallListChanged(Handler h, int what, Object obj);
/** Unregister from data call list changed event */
void unregisterForDataCallListChanged(Handler h);
+ /** Register for the apn unthrottled event */
+ void registerForApnUnthrottled(Handler h, int what, Object obj);
+ /** Unregister for apn unthrottled event */
+ void unregisterForApnUnthrottled(Handler h);
/** InCall voice privacy notifications */
void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj);
@@ -668,9 +680,9 @@ public interface CommandsInterface {
* @param what User-defined message code.
* @param obj User object.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void registerForRilConnected(Handler h, int what, Object obj);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void unregisterForRilConnected(Handler h);
/**
@@ -1111,7 +1123,7 @@ public interface CommandsInterface {
* Please note that registration state 4 ("unknown") is treated
* as "out of service" above
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void getVoiceRegistrationState (Message response);
/**
@@ -1321,7 +1333,7 @@ public interface CommandsInterface {
* response.obj will be an AsyncResult
* response.obj.userObj will be a IccIoResult on success
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
String data, String pin2, String aid, Message response);
@@ -1552,13 +1564,54 @@ public interface CommandsInterface {
void getPreferredNetworkType(Message response);
/**
+ * Requests to set the allowed network types for searching and registering.
+ *
+ * @param networkTypeBitmask {@link TelephonyManager.NetworkTypeBitMask}
+ * @param response is callback message
+ */
+ void setAllowedNetworkTypesBitmap(
+ @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message response);
+
+ /**
+ * Query the allowed network types setting.
+ *
+ * @param response is callback message to report allowed network types bitmask
+ */
+ void getAllowedNetworkTypesBitmap(Message response);
+
+ /**
+ * Enable/Disable E-UTRA-NR Dual Connectivity
+ * @param nrDualConnectivityState expected NR dual connectivity state
+ * This can be passed following states
+ * <ol>
+ * <li>Enable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_ENABLE}
+ * <li>Disable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE}
+ * <li>Disable NR dual connectivity and force secondary cell to be released
+ * {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
+ * </ol>
+ */
+ default void setNrDualConnectivityState(int nrDualConnectivityState,
+ Message message, WorkSource workSource) {}
+
+ /**
+ * Is E-UTRA-NR Dual Connectivity enabled
+ */
+ default void isNrDualConnectivityEnabled(Message message, WorkSource workSource) {}
+
+ /**
* Request to enable/disable network state change notifications when
* location information (lac and/or cid) has changed.
*
* @param enable true to enable, false to disable
+ * @param workSource calling WorkSource
* @param response callback message
*/
- void setLocationUpdates(boolean enable, Message response);
+ default void setLocationUpdates(boolean enable, WorkSource workSource, Message response) {}
+
+ /**
+ * To be deleted
+ */
+ default void setLocationUpdates(boolean enable, Message response) {}
/**
* Gets the default SMSC address.
@@ -1797,12 +1850,33 @@ public interface CommandsInterface {
* @param linkProperties
* If the reason is for handover, this indicates the link properties of the existing
* data connection
+ * @param pduSessionId the pdu session id to be used for this data call.
+ * The standard range of values are 1-15 while 0 means no pdu session id was attached
+ * to this call. Reference: 3GPP TS 24.007 section 11.2.3.1b.
+ * @param sliceInfo used within the data connection when a handover occurs from EPDG to 5G.
+ * The value is null unless the access network is
+ * {@link android.telephony.AccessNetworkConstants.AccessNetworkType#NGRAN} and a
+ * handover is occurring from EPDG to 5G. If the slice passed is rejected, then
+ * {@link DataCallResponse#getCause()} is
+ * {@link android.telephony.DataFailCause#SLICE_REJECTED}.
+ * @param trafficDescriptor TrafficDescriptor for which data connection needs to be established.
+ * It is used for URSP traffic matching as described in 3GPP TS 24.526 Section 4.2.2.
+ * It includes an optional DNN which, if present, must be used for traffic matching;
+ * it does not specify the end point to be used for the data call.
+ * @param matchAllRuleAllowed indicates if using default match-all URSP rule for this request is
+ * allowed. If false, this request must not use the match-all URSP rule and if a
+ * non-match-all rule is not found (or if URSP rules are not available) then
+ * {@link DataCallResponse#getCause()} is
+ * {@link android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed
+ * as some requests need to have a hard failure if the intention cannot be met,
+ * for example, a zero-rating slice.
* @param result
* Callback message
*/
void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- Message result);
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, Message result);
/**
* Deactivate packet data connection
@@ -1879,16 +1953,6 @@ public interface CommandsInterface {
void setLogicalToPhysicalSlotMapping(int[] physicalSlots, Message result);
/**
- * Return if the current radio is LTE on CDMA. This
- * is a tri-state return value as for a period of time
- * the mode may be unknown.
- *
- * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
- * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
- */
- public int getLteOnCdmaMode();
-
- /**
* Request the SIM application on the UICC to perform authentication
* challenge/response algorithm. The data string and challenge response are
* Base64 encoded Strings.
@@ -1901,7 +1965,7 @@ public interface CommandsInterface {
* 102.221 8.1 and 101.220 4
* @param response a callback message with the String response in the obj field
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void requestIccSimAuthentication(int authContext, String data, String aid, Message response);
/**
@@ -1945,7 +2009,7 @@ public interface CommandsInterface {
/**
* Fires when RIL_UNSOL_CELL_INFO_LIST is received from the RIL.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void registerForCellInfoList(Handler h, int what, Object obj);
void unregisterForCellInfoList(Handler h);
@@ -2104,6 +2168,13 @@ public interface CommandsInterface {
*/
int getRilVersion();
+ /**
+ * @return the radio hal version
+ */
+ default HalVersion getHalVersion() {
+ return HalVersion.UNKNOWN;
+ }
+
/**
* Sets user selected subscription at Modem.
*
@@ -2132,7 +2203,7 @@ public interface CommandsInterface {
* Callback message contains the information of SUCCESS/FAILURE.
*/
// FIXME We may need to pass AID and slotid also
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setDataAllowed(boolean allowed, Message result);
/**
@@ -2140,7 +2211,7 @@ public interface CommandsInterface {
*
* @param result Callback message contains the information of SUCCESS/FAILURE
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void requestShutdown(Message result);
/**
@@ -2483,6 +2554,13 @@ public interface CommandsInterface {
Message onComplete) {}
/**
+ * Get which bands the modem's background scan is acting on.
+ *
+ * @param onComplete a message to send when complete.
+ */
+ default void getSystemSelectionChannels(Message onComplete) {}
+
+ /**
* Whether uicc applications are enabled or not.
*
* @param onCompleteMessage a Message to return to the requester
@@ -2524,4 +2602,113 @@ public interface CommandsInterface {
* @param result Message will be sent back to handler and result.obj will be the AsycResult.
*/
default void getBarringInfo(Message result) {};
+
+ /**
+ * Allocates a pdu session id
+ *
+ * AsyncResult.result is the allocated pdu session id
+ *
+ * @param result Message will be sent back to handler and result.obj will be the AsycResult.
+ *
+ */
+ default void allocatePduSessionId(Message result) {};
+
+ /**
+ * Release the pdu session id
+ *
+ * @param result Message that will be sent back to handler.
+ * @param pduSessionId The id that was allocated and should now be released.
+ *
+ */
+ default void releasePduSessionId(Message result, int pduSessionId) {};
+
+ /**
+ * Indicates that a handover has started
+ *
+ * @param result Message that will be sent back to handler.
+ * @param callId Identifier associated with the data call
+ */
+ default void startHandover(Message result, int callId) {};
+
+ /**
+ * Indicates that a handover has been cancelled
+ *
+ * @param result Message that will be sent back to handler.
+ * @param callId Identifier associated with the data call
+ */
+ default void cancelHandover(Message result, int callId) {};
+
+ /**
+ * Control the data throttling at modem.
+ *
+ * @param result Message that will be sent back to the requester
+ * @param workSource calling Worksource
+ * @param dataThrottlingAction the DataThrottlingAction that is being requested.
+ * Defined in android.hardware.radio@1.6.types.
+ * @param completionWindowMillis milliseconds in which data throttling action has to be
+ * achieved.
+ */
+ default void setDataThrottling(Message result, WorkSource workSource,
+ int dataThrottlingAction, long completionWindowMillis) {};
+
+ /**
+ * Request to get the current slicing configuration including URSP rules and
+ * NSSAIs (configured, allowed and rejected).
+ *
+ * @param result Message that will be sent back to handler.
+ */
+ default void getSlicingConfig(Message result) {};
+
+ /**
+ * Request the SIM phonebook records of all activated UICC applications
+ *
+ * @param result Callback message containing the count of ADN valid record.
+ */
+ public void getSimPhonebookRecords(Message result);
+
+ /**
+ * Request the SIM phonebook Capacity of all activated UICC applications
+ *
+ */
+ public void getSimPhonebookCapacity(Message result);
+
+ /**
+ * Request to insert/delete/update the SIM phonebook record
+ *
+ * @param phonebookRecordInfo adn record information to be updated
+ * @param result Callback message containing the SIM phonebook record index.
+ */
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecordInfo, Message result);
+
+ /**
+ * Registers the handler when the SIM phonebook is changed.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object .
+ */
+ public void registerForSimPhonebookChanged(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications when SIM phonebook has already init done.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSimPhonebookChanged(Handler h);
+
+ /**
+ * Registers the handler when a group of SIM phonebook records received.
+ *
+ * @param h Handler for notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSimPhonebookRecordsReceived(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications when a group of SIM phonebook records received.
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSimPhonebookRecordsReceived(Handler h);
}
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index f1baa1ba0b..59a5195e63 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -16,14 +16,17 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.telephony.DisconnectCause;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.RtpHeaderExtension;
import android.util.Log;
import com.android.ims.internal.ConferenceParticipant;
@@ -102,6 +105,7 @@ public abstract class Connection {
public void onVideoProviderChanged(
android.telecom.Connection.VideoProvider videoProvider);
public void onAudioQualityChanged(int audioQuality);
+ public void onMediaAttributesChanged();
public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants);
public void onCallSubstateChanged(int callSubstate);
public void onMultipartyStateChanged(boolean isMultiParty);
@@ -118,6 +122,18 @@ public abstract class Connection {
public void onRttTerminated();
public void onOriginalConnectionReplaced(Connection newConnection);
public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall);
+
+ /**
+ * Indicates a DTMF digit has been received from the network.
+ * @param digit The DTMF digit.
+ */
+ public void onReceivedDtmfDigit(char digit);
+
+ /**
+ * Indicates data from an RTP header extension has been received from the network.
+ * @param extensionData The extension data.
+ */
+ public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData);
}
/**
@@ -136,6 +152,8 @@ public abstract class Connection {
@Override
public void onAudioQualityChanged(int audioQuality) {}
@Override
+ public void onMediaAttributesChanged() {}
+ @Override
public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {}
@Override
public void onCallSubstateChanged(int callSubstate) {}
@@ -167,10 +185,16 @@ public abstract class Connection {
public void onOriginalConnectionReplaced(Connection newConnection) {}
@Override
public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) {}
+ @Override
+ public void onReceivedDtmfDigit(char digit) {}
+ @Override
+ public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {}
}
public static final int AUDIO_QUALITY_STANDARD = 1;
public static final int AUDIO_QUALITY_HIGH_DEFINITION = 2;
+ // the threshold used to compare mAudioCodecBitrateKbps and mAudioCodecBandwidth.
+ public static final float THRESHOLD = 0.01f;
/**
* The telecom internal call ID associated with this connection. Only to be used for debugging
@@ -179,23 +203,23 @@ public abstract class Connection {
private String mTelecomCallId;
//Caller Name Display
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected String mCnapName;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected int mCnapNamePresentation = PhoneConstants.PRESENTATION_ALLOWED;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected String mAddress; // MAY BE NULL!!!
// The VERSTAT number verification status; defaults to not verified.
protected @android.telecom.Connection.VerificationStatus int mNumberVerificationStatus =
android.telecom.Connection.VERIFICATION_STATUS_NOT_VERIFIED;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected String mDialString; // outgoing calls only
protected String[] mParticipantsToDial;// outgoing calls only
protected boolean mIsAdhocConference;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected int mNumberPresentation = PhoneConstants.PRESENTATION_ALLOWED;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected boolean mIsIncoming;
/*
* These time/timespan values are based on System.currentTimeMillis(),
@@ -209,7 +233,7 @@ public abstract class Connection {
* calculating deltas.
*/
protected long mConnectTimeReal;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected long mDuration;
protected long mHoldingStartTime; // The time when the Connection last transitioned
// into HOLDING
@@ -220,6 +244,8 @@ public abstract class Connection {
protected boolean mNumberConverted = false;
protected String mConvertedNumber;
+ protected ArrayList<String> mForwardedNumber = null; //May be null. Incoming calls only.
+
protected String mPostDialString; // outgoing calls only
protected int mNextPostDialChar; // index into postDialString
@@ -228,6 +254,10 @@ public abstract class Connection {
// Store the current audio code
protected int mAudioCodec;
+ // audio codec bitrate in kbps
+ protected float mAudioCodecBitrateKbps;
+ // audio codec bandwidth in kHz
+ protected float mAudioCodecBandwidthKhz;
@UnsupportedAppUsage
private static String LOG_TAG = "Connection";
@@ -285,7 +315,7 @@ public abstract class Connection {
*/
private int mPulledDialogId;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Connection(int phoneType) {
mPhoneType = phoneType;
}
@@ -336,6 +366,15 @@ public abstract class Connection {
}
/**
+ * Gets redirecting address (e.g. phone number) associated with connection.
+ *
+ * @return ArrayList of the forwarded number or null if unavailable
+ */
+ public ArrayList<String> getForwardedNumber() {
+ return mForwardedNumber;
+ }
+
+ /**
* Gets CNAP name associated with connection.
* @return cnap name or null if unavailable
*/
@@ -430,7 +469,7 @@ public abstract class Connection {
* The time when this Connection makes a transition into ENDED or FAIL.
* Returns 0 before then.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract long getDisconnectTime();
/**
@@ -439,7 +478,7 @@ public abstract class Connection {
* If the call is still connected, then returns the elapsed
* time since connect.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public long getDurationMillis() {
if (mConnectTimeReal == 0) {
return 0;
@@ -472,7 +511,7 @@ public abstract class Connection {
* {@link android.telephony.DisconnectCause}. If the call is not yet
* disconnected, NOT_DISCONNECTED is returned.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getDisconnectCause() {
return mCause;
}
@@ -569,6 +608,20 @@ public abstract class Connection {
}
/**
+ * Set the non-detectable emergency number information.
+ */
+ public void setNonDetectableEmergencyCallInfo(int eccCategory) {
+ if (!mIsEmergencyCall) {
+ mIsEmergencyCall = true;
+ mEmergencyNumberInfo = new EmergencyNumber(mAddress, ""/*countryIso*/,
+ ""/*mnc*/, eccCategory,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ }
+ }
+
+ /**
* Set if we have known the user's intent for the call is emergency.
*
* This is only used to specify when the dialed number is ambiguous, identified as both
@@ -632,7 +685,7 @@ public abstract class Connection {
* @return true if the connection isn't disconnected
* (could be active, holding, ringing, dialing, etc)
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean
isAlive() {
return getState().isAlive();
@@ -682,7 +735,7 @@ public abstract class Connection {
/**
* Hangup individual Connection
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract void hangup() throws CallStateException;
/**
@@ -989,7 +1042,7 @@ public abstract class Connection {
*
* @return The video state.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setVideoState(int videoState) {
mVideoState = videoState;
for (Listener l : mListeners) {
@@ -1051,6 +1104,15 @@ public abstract class Connection {
}
/**
+ * Notifies interested parties of changes to the media attributes of the call.
+ */
+ public void notifyMediaAttributesChanged() {
+ for (Listener l: mListeners) {
+ l.onMediaAttributesChanged();
+ }
+ }
+
+ /**
* Notifies listeners that connection extras has changed.
* @param extras New connection extras. This Bundle will be cloned to ensure that any concurrent
* modifications to the extras Bundle do not affect Bundle operations in the onExtrasChanged
@@ -1386,6 +1448,7 @@ public abstract class Connection {
StringBuilder str = new StringBuilder(128);
str.append(" callId: " + getTelecomCallId());
+ str.append(" objId: " + System.identityHashCode(this));
str.append(" isExternal: " + (((mConnectionCapabilities & Capability.IS_EXTERNAL_CONNECTION)
== Capability.IS_EXTERNAL_CONNECTION) ? "Y" : "N"));
if (Rlog.isLoggable(LOG_TAG, Log.DEBUG)) {
@@ -1411,6 +1474,20 @@ public abstract class Connection {
}
/**
+ * @return the audio codec bitrate in kbps.
+ */
+ public float getAudioCodecBitrateKbps() {
+ return mAudioCodecBitrateKbps;
+ }
+
+ /**
+ * @return the audio codec bandwidth in kHz.
+ */
+ public float getAudioCodecBandwidthKhz() {
+ return mAudioCodecBandwidthKhz;
+ }
+
+ /**
* @return The number verification status; only applicable for IMS calls.
*/
public @android.telecom.Connection.VerificationStatus int getNumberVerificationStatus() {
@@ -1425,4 +1502,24 @@ public abstract class Connection {
@android.telecom.Connection.VerificationStatus int verificationStatus) {
mNumberVerificationStatus = verificationStatus;
}
+
+ /**
+ * Called to report a DTMF digit received from the network.
+ * @param digit the received digit.
+ */
+ public void receivedDtmfDigit(char digit) {
+ for (Listener l : mListeners) {
+ l.onReceivedDtmfDigit(digit);
+ }
+ }
+
+ /**
+ * Called to report RTP header extensions received from the network.
+ * @param extensionData the received extension data.
+ */
+ public void receivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {
+ for (Listener l : mListeners) {
+ l.onReceivedRtpHeaderExtensions(extensionData);
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 004853587f..333e994cba 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -18,21 +18,22 @@ package com.android.internal.telephony;
import android.annotation.NonNull;
import android.content.Context;
-import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
import android.telephony.BarringInfo;
import android.telephony.CallQuality;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.PhoneCapability;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.DataEnabledReason;
import android.telephony.TelephonyRegistryManager;
-import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
@@ -66,23 +67,26 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
if (ringingCall != null && ringingCall.getEarliestConnection() != null) {
incomingNumber = ringingCall.getEarliestConnection().getAddress();
}
- mTelephonyRegistryMgr.notifyCallStateChanged(subId, phoneId,
- PhoneConstantConversions.convertCallState(sender.getState()), incomingNumber);
+ mTelephonyRegistryMgr.notifyCallStateChanged(phoneId, subId,
+ PhoneConstantConversions.convertCallState(sender.getState()), incomingNumber);
}
@Override
public void notifyServiceState(Phone sender) {
- ServiceState ss = sender.getServiceState();
+ notifyServiceStateForSubId(sender, sender.getServiceState(), sender.getSubId());
+ }
+
+ @Override
+ public void notifyServiceStateForSubId(Phone sender, ServiceState ss, int subId) {
int phoneId = sender.getPhoneId();
- int subId = sender.getSubId();
- Rlog.d(LOG_TAG, "notifyServiceState: mRegistryMgr=" + mTelephonyRegistryMgr + " ss="
+ Rlog.d(LOG_TAG, "notifyServiceStateForSubId: mRegistryMgr=" + mTelephonyRegistryMgr + " ss="
+ ss + " sender=" + sender + " phondId=" + phoneId + " subId=" + subId);
if (ss == null) {
ss = new ServiceState();
ss.setStateOutOfService();
}
- mTelephonyRegistryMgr.notifyServiceStateChanged(subId, phoneId, ss);
+ mTelephonyRegistryMgr.notifyServiceStateChanged(phoneId, subId, ss);
}
@Override
@@ -94,16 +98,16 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
Rlog.d(LOG_TAG, "notifySignalStrength: mRegistryMgr=" + mTelephonyRegistryMgr
+ " ss=" + sender.getSignalStrength() + " sender=" + sender);
}
- mTelephonyRegistryMgr.notifySignalStrengthChanged(subId, phoneId,
- sender.getSignalStrength());
+ mTelephonyRegistryMgr.notifySignalStrengthChanged(phoneId, subId,
+ sender.getSignalStrength());
}
@Override
public void notifyMessageWaitingChanged(Phone sender) {
int phoneId = sender.getPhoneId();
int subId = sender.getSubId();
- mTelephonyRegistryMgr.notifyMessageWaitingChanged(subId, phoneId,
- sender.getMessageWaitingIndicator());
+ mTelephonyRegistryMgr.notifyMessageWaitingChanged(phoneId, subId,
+ sender.getMessageWaitingIndicator());
}
@Override
@@ -124,15 +128,9 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
}
@Override
- public void notifyDataConnection(
- Phone sender, String apnType, PreciseDataConnectionState preciseState) {
-
- int subId = sender.getSubId();
- int phoneId = sender.getPhoneId();
- int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
-
- mTelephonyRegistryMgr.notifyDataConnectionForSubscriber(
- phoneId, subId, apnTypeBitmask, preciseState);
+ public void notifyDataConnection(Phone sender, PreciseDataConnectionState preciseState) {
+ mTelephonyRegistryMgr.notifyDataConnectionForSubscriber(sender.getPhoneId(),
+ sender.getSubId(), preciseState);
}
@Override
@@ -152,10 +150,10 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
Call foregroundCall = sender.getForegroundCall();
Call backgroundCall = sender.getBackgroundCall();
if (ringingCall != null && foregroundCall != null && backgroundCall != null) {
- mTelephonyRegistryMgr.notifyPreciseCallState(sender.getSubId(), sender.getPhoneId(),
- convertPreciseCallState(ringingCall.getState()),
- convertPreciseCallState(foregroundCall.getState()),
- convertPreciseCallState(backgroundCall.getState()));
+ mTelephonyRegistryMgr.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(),
+ convertPreciseCallState(ringingCall.getState()),
+ convertPreciseCallState(foregroundCall.getState()),
+ convertPreciseCallState(backgroundCall.getState()));
}
}
@@ -170,29 +168,20 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
}
@Override
- /** Notify the TelephonyRegistry that a data connection has failed with a specified cause */
- public void notifyDataConnectionFailed(Phone sender, String apnType,
- String apn, @DataFailureCause int failCause) {
- mTelephonyRegistryMgr.notifyPreciseDataConnectionFailed(
- sender.getSubId(), sender.getPhoneId(),
- ApnSetting.getApnTypesBitmaskFromString(apnType), apn, failCause);
- }
-
- @Override
public void notifySrvccStateChanged(Phone sender, @SrvccState int state) {
mTelephonyRegistryMgr.notifySrvccStateChanged(sender.getSubId(), state);
}
@Override
public void notifyDataActivationStateChanged(Phone sender, int activationState) {
- mTelephonyRegistryMgr.notifyDataActivationStateChanged(sender.getSubId(),
- sender.getPhoneId(), activationState);
+ mTelephonyRegistryMgr.notifyDataActivationStateChanged(sender.getPhoneId(),
+ sender.getSubId(), activationState);
}
@Override
public void notifyVoiceActivationStateChanged(Phone sender, int activationState) {
- mTelephonyRegistryMgr.notifyVoiceActivationStateChanged(sender.getSubId(),
- sender.getPhoneId(), activationState);
+ mTelephonyRegistryMgr.notifyVoiceActivationStateChanged(sender.getPhoneId(),
+ sender.getSubId(), activationState);
}
@Override
@@ -214,19 +203,13 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
@Override
public void notifyRadioPowerStateChanged(Phone sender, @RadioPowerState int state) {
- mTelephonyRegistryMgr.notifyRadioPowerStateChanged(sender.getSubId(), sender.getPhoneId(),
- state);
+ mTelephonyRegistryMgr.notifyRadioPowerStateChanged(sender.getPhoneId(), sender.getSubId(),
+ state);
}
@Override
public void notifyEmergencyNumberList(Phone sender) {
- mTelephonyRegistryMgr.notifyEmergencyNumberList(sender.getSubId(), sender.getPhoneId());
- }
-
- @Override
- public void notifyOutgoingEmergencyCall(Phone sender, EmergencyNumber emergencyNumber) {
- mTelephonyRegistryMgr.notifyOutgoingEmergencyCall(
- sender.getPhoneId(), sender.getSubId(), emergencyNumber);
+ mTelephonyRegistryMgr.notifyEmergencyNumberList(sender.getPhoneId(), sender.getSubId());
}
@Override
@@ -237,9 +220,9 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
@Override
public void notifyCallQualityChanged(Phone sender, CallQuality callQuality,
- int callNetworkType) {
- mTelephonyRegistryMgr.notifyCallQualityChanged(sender.getSubId(), sender.getPhoneId(),
- callQuality, callNetworkType);
+ int callNetworkType) {
+ mTelephonyRegistryMgr.notifyCallQualityChanged(sender.getPhoneId(), sender.getSubId(),
+ callQuality, callNetworkType);
}
@Override
@@ -255,6 +238,33 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
barringInfo);
}
+ @Override
+ public void notifyPhysicalChannelConfig(Phone sender,
+ List<PhysicalChannelConfig> configs) {
+ mTelephonyRegistryMgr.notifyPhysicalChannelConfigForSubscriber(
+ sender.getPhoneId(), sender.getSubId(), configs);
+ }
+
+ @Override
+ public void notifyDataEnabled(Phone sender, boolean enabled, @DataEnabledReason int reason) {
+ mTelephonyRegistryMgr.notifyDataEnabled(sender.getPhoneId(), sender.getSubId(),
+ enabled, reason);
+ }
+
+ @Override
+ public void notifyAllowedNetworkTypesChanged(Phone sender, int reason,
+ long allowedNetworkType) {
+ mTelephonyRegistryMgr.notifyAllowedNetworkTypesChanged(sender.getPhoneId(),
+ sender.getSubId(), reason, allowedNetworkType);
+ }
+
+ @Override
+ public void notifyLinkCapacityEstimateChanged(Phone sender,
+ List<LinkCapacityEstimate> linkCapacityEstimateList) {
+ mTelephonyRegistryMgr.notifyLinkCapacityEstimateChanged(sender.getPhoneId(),
+ sender.getSubId(), linkCapacityEstimateList);
+ }
+
/**
* Convert the {@link DataActivityState} enum into the TelephonyManager.DATA_* constants for the
* public API.
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index e009aadee8..1650b6f99a 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
@@ -25,7 +26,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.hardware.radio.V1_5.IndicationFilter;
import android.net.ConnectivityManager;
@@ -71,7 +71,7 @@ public class DeviceStateMonitor extends Handler {
protected static final String TAG = DeviceStateMonitor.class.getSimpleName();
static final int EVENT_RIL_CONNECTED = 0;
- static final int EVENT_CAR_MODE_CHANGED = 1;
+ static final int EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED = 1;
@VisibleForTesting
static final int EVENT_SCREEN_STATE_CHANGED = 2;
static final int EVENT_POWER_SAVE_MODE_CHANGED = 3;
@@ -173,11 +173,11 @@ public class DeviceStateMonitor extends Handler {
private boolean mIsWifiConnected;
/**
- * Car mode is on. True means the device is currently connected to Android Auto. This should be
- * handled by mIsScreenOn, but the Android Auto display is private and not accessible by
- * DeviceStateMonitor from DisplayMonitor.
+ * Automotive projection is active. True means the device is currently connected to Android
+ * Auto. This should be handled by mIsScreenOn, but the Android Auto display is private and not
+ * accessible by DeviceStateMonitor from DisplayMonitor.
*/
- private boolean mIsCarModeOn;
+ private boolean mIsAutomotiveProjectionActive;
/**
* True indicates we should always enable the signal strength reporting from radio.
@@ -248,14 +248,6 @@ public class DeviceStateMonitor extends Handler {
msg = obtainMessage(EVENT_TETHERING_STATE_CHANGED);
msg.arg1 = isTetheringOn ? 1 : 0;
break;
- case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED:
- msg = obtainMessage(EVENT_CAR_MODE_CHANGED);
- msg.arg1 = 1; // car mode on
- break;
- case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED:
- msg = obtainMessage(EVENT_CAR_MODE_CHANGED);
- msg.arg1 = 0; // car mode off
- break;
default:
log("Unexpected broadcast intent: " + intent, false);
return;
@@ -279,7 +271,7 @@ public class DeviceStateMonitor extends Handler {
mIsPowerSaveOn = isPowerSaveModeOn();
mIsCharging = isDeviceCharging();
mIsScreenOn = isScreenOn();
- mIsCarModeOn = isCarModeOn();
+ mIsAutomotiveProjectionActive = isAutomotiveProjectionActive();
// Assuming tethering is always off after boot up.
mIsTetheringOn = false;
mIsLowDataExpected = false;
@@ -289,7 +281,7 @@ public class DeviceStateMonitor extends Handler {
+ ", mIsCharging=" + mIsCharging
+ ", mIsPowerSaveOn=" + mIsPowerSaveOn
+ ", mIsLowDataExpected=" + mIsLowDataExpected
- + ", mIsCarModeOn=" + mIsCarModeOn
+ + ", mIsAutomotiveProjectionActive=" + mIsAutomotiveProjectionActive
+ ", mIsWifiConnected=" + mIsWifiConnected
+ ", mIsAlwaysSignalStrengthReportingEnabled="
+ mIsAlwaysSignalStrengthReportingEnabled, false);
@@ -298,9 +290,7 @@ public class DeviceStateMonitor extends Handler {
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(BatteryManager.ACTION_CHARGING);
filter.addAction(BatteryManager.ACTION_DISCHARGING);
- filter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
- filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
- filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
+ filter.addAction(TetheringManager.ACTION_TETHER_STATE_CHANGED);
mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
@@ -309,6 +299,16 @@ public class DeviceStateMonitor extends Handler {
ConnectivityManager cm = (ConnectivityManager) phone.getContext().getSystemService(
Context.CONNECTIVITY_SERVICE);
cm.registerNetworkCallback(mWifiNetworkRequest, mNetworkCallback);
+
+ UiModeManager umm = (UiModeManager) phone.getContext().getSystemService(
+ Context.UI_MODE_SERVICE);
+ umm.addOnProjectionStateChangedListener(PROJECTION_TYPE_AUTOMOTIVE,
+ phone.getContext().getMainExecutor(),
+ (t, pkgs) -> {
+ Message msg = obtainMessage(EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED);
+ msg.arg1 = Math.min(pkgs.size(), 1);
+ sendMessage(msg);
+ });
}
/**
@@ -398,13 +398,13 @@ public class DeviceStateMonitor extends Handler {
*
* @return True if the response update should be enabled.
*/
- private boolean shouldEnableHighPowerConsumptionIndications() {
+ public boolean shouldEnableHighPowerConsumptionIndications() {
// We should enable indications reports if one of the following condition is true.
// 1. The device is charging.
// 2. When the screen is on.
// 3. When the tethering is on.
- // 4. When car mode (Android Auto) is on.
- return mIsCharging || mIsScreenOn || mIsTetheringOn || mIsCarModeOn;
+ // 4. When automotive projection (Android Auto) is on.
+ return mIsCharging || mIsScreenOn || mIsTetheringOn || mIsAutomotiveProjectionActive;
}
/**
@@ -437,7 +437,9 @@ public class DeviceStateMonitor extends Handler {
* @param isEnable
*/
public void setAlwaysReportSignalStrength(boolean isEnable) {
- sendMessage(obtainMessage(EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH, isEnable ? 1 : 0));
+ Message msg = obtainMessage(EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH);
+ msg.arg1 = isEnable ? 1 : 0;
+ sendMessage(msg);
}
/**
@@ -458,7 +460,7 @@ public class DeviceStateMonitor extends Handler {
case EVENT_CHARGING_STATE_CHANGED:
case EVENT_TETHERING_STATE_CHANGED:
case EVENT_UPDATE_ALWAYS_REPORT_SIGNAL_STRENGTH:
- case EVENT_CAR_MODE_CHANGED:
+ case EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED:
onUpdateDeviceState(msg.what, msg.arg1 != 0);
break;
case EVENT_WIFI_CONNECTION_CHANGED:
@@ -477,6 +479,7 @@ public class DeviceStateMonitor extends Handler {
*/
private void onUpdateDeviceState(int eventType, boolean state) {
final boolean shouldEnableBarringInfoReportsOld = shouldEnableBarringInfoReports();
+ final boolean wasHighPowerEnabled = shouldEnableHighPowerConsumptionIndications();
switch (eventType) {
case EVENT_SCREEN_STATE_CHANGED:
if (mIsScreenOn == state) return;
@@ -504,14 +507,19 @@ public class DeviceStateMonitor extends Handler {
if (mIsAlwaysSignalStrengthReportingEnabled == state) return;
mIsAlwaysSignalStrengthReportingEnabled = state;
break;
- case EVENT_CAR_MODE_CHANGED:
- if (mIsCarModeOn == state) return;
- mIsCarModeOn = state;
+ case EVENT_AUTOMOTIVE_PROJECTION_STATE_CHANGED:
+ if (mIsAutomotiveProjectionActive == state) return;
+ mIsAutomotiveProjectionActive = state;
break;
default:
return;
}
+ final boolean isHighPowerEnabled = shouldEnableHighPowerConsumptionIndications();
+ if (wasHighPowerEnabled != isHighPowerEnabled) {
+ mPhone.notifyDeviceIdleStateChanged(!isHighPowerEnabled /*isIdle*/);
+ }
+
final int newCellInfoMinInterval = computeCellInfoMinInterval();
if (mCellInfoMinInterval != newCellInfoMinInterval) {
mCellInfoMinInterval = newCellInfoMinInterval;
@@ -628,26 +636,31 @@ public class DeviceStateMonitor extends Handler {
}
private void setSignalStrengthReportingCriteria() {
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
AccessNetworkThresholds.GERAN, AccessNetworkType.GERAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSCP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
AccessNetworkThresholds.UTRAN, AccessNetworkType.UTRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
AccessNetworkThresholds.EUTRAN_RSRP, AccessNetworkType.EUTRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
AccessNetworkThresholds.CDMA2000, AccessNetworkType.CDMA2000, true);
if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
AccessNetworkThresholds.EUTRAN_RSRQ, AccessNetworkType.EUTRAN, false);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSNR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
AccessNetworkThresholds.EUTRAN_RSSNR, AccessNetworkType.EUTRAN, true);
// Defaultly we only need SSRSRP for NGRAN signal criteria reporting
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRP,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
AccessNetworkThresholds.NGRAN_RSRSRP, AccessNetworkType.NGRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
AccessNetworkThresholds.NGRAN_RSRSRQ, AccessNetworkType.NGRAN, false);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSSINR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
AccessNetworkThresholds.NGRAN_SSSINR, AccessNetworkType.NGRAN, false);
}
}
@@ -727,15 +740,16 @@ public class DeviceStateMonitor extends Handler {
}
/**
- * @return True if car mode (Android Auto) is on.
+ * @return True if automotive projection (Android Auto) is active.
*/
- private boolean isCarModeOn() {
+ private boolean isAutomotiveProjectionActive() {
final UiModeManager umm = (UiModeManager) mPhone.getContext().getSystemService(
Context.UI_MODE_SERVICE);
if (umm == null) return false;
- boolean retval = umm.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR;
- log("isCarModeOn=" + retval, true);
- return retval;
+ boolean isAutomotiveProjectionActive = (umm.getActiveProjectionTypes()
+ & PROJECTION_TYPE_AUTOMOTIVE) != 0;
+ log("isAutomotiveProjectionActive=" + isAutomotiveProjectionActive, true);
+ return isAutomotiveProjectionActive;
}
/**
@@ -786,7 +800,7 @@ public class DeviceStateMonitor extends Handler {
ipw.println("mIsCharging=" + mIsCharging);
ipw.println("mIsPowerSaveOn=" + mIsPowerSaveOn);
ipw.println("mIsLowDataExpected=" + mIsLowDataExpected);
- ipw.println("mIsCarModeOn=" + mIsCarModeOn);
+ ipw.println("mIsAutomotiveProjectionActive=" + mIsAutomotiveProjectionActive);
ipw.println("mUnsolicitedResponseFilter=" + mUnsolicitedResponseFilter);
ipw.println("mIsWifiConnected=" + mIsWifiConnected);
ipw.println("mIsAlwaysSignalStrengthReportingEnabled="
@@ -891,10 +905,10 @@ public class DeviceStateMonitor extends Handler {
* List of dB thresholds for NGRAN {@link AccessNetworkType} RSRSRP
*/
public static final int[] NGRAN_RSRSRQ = new int[] {
- -16, /* SIGNAL_STRENGTH_POOR */
- -12, /* SIGNAL_STRENGTH_MODERATE */
- -9, /* SIGNAL_STRENGTH_GOOD */
- -6 /* SIGNAL_STRENGTH_GREAT */
+ -31, /* SIGNAL_STRENGTH_POOR */
+ -19, /* SIGNAL_STRENGTH_MODERATE */
+ -7, /* SIGNAL_STRENGTH_GOOD */
+ 6 /* SIGNAL_STRENGTH_GREAT */
};
/**
diff --git a/src/java/com/android/internal/telephony/DisplayInfoController.java b/src/java/com/android/internal/telephony/DisplayInfoController.java
index 6870355a78..eb18693afe 100644
--- a/src/java/com/android/internal/telephony/DisplayInfoController.java
+++ b/src/java/com/android/internal/telephony/DisplayInfoController.java
@@ -70,6 +70,14 @@ public class DisplayInfoController extends Handler {
}
/**
+ * @return True if either the primary or secondary 5G hysteresis timer is active,
+ * and false if neither are.
+ */
+ public boolean is5GHysteresisActive() {
+ return mNetworkTypeController.is5GHysteresisActive();
+ }
+
+ /**
* Register for TelephonyDisplayInfo changed.
* @param h Handler to notify
* @param what msg.what when the message is delivered
diff --git a/src/java/com/android/internal/telephony/DriverCall.java b/src/java/com/android/internal/telephony/DriverCall.java
index a67de7a69d..8feb7956bc 100644
--- a/src/java/com/android/internal/telephony/DriverCall.java
+++ b/src/java/com/android/internal/telephony/DriverCall.java
@@ -79,6 +79,7 @@ public class DriverCall implements Comparable<DriverCall> {
public boolean isMpty;
@UnsupportedAppUsage
public String number;
+ public String forwardedNumber; // May be null. Incoming calls only.
public int TOA;
@UnsupportedAppUsage
public boolean isVoice;
diff --git a/src/java/com/android/internal/telephony/GbaManager.java b/src/java/com/android/internal/telephony/GbaManager.java
new file mode 100644
index 0000000000..d6c59eae7d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/GbaManager.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.IBootstrapAuthenticationCallback;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.GbaAuthRequest;
+import android.telephony.gba.GbaService;
+import android.telephony.gba.IGbaService;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.telephony.Rlog;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * Class that serves as the layer between GbaService and ServiceStateTracker. It helps binding,
+ * sending request, receiving callback, and registering for state change to GbaService.
+ */
+public class GbaManager {
+ private static final boolean DBG = Build.IS_DEBUGGABLE;
+ private static final int EVENT_BIND_SERVICE = 1;
+ private static final int EVENT_UNBIND_SERVICE = 2;
+ private static final int EVENT_BIND_FAIL = 3;
+ private static final int EVENT_BIND_SUCCESS = 4;
+ private static final int EVENT_CONFIG_CHANGED = 5;
+ private static final int EVENT_REQUESTS_RECEIVED = 6;
+
+ @VisibleForTesting
+ public static final int RETRY_TIME_MS = 3000;
+ @VisibleForTesting
+ public static final int MAX_RETRY = 5;
+ @VisibleForTesting
+ public static final int REQUEST_TIMEOUT_MS = 5000;
+
+ private final String mLogTag;
+ private final Context mContext;
+ private final int mSubId;
+
+ private IGbaService mIGbaService;
+ private GbaDeathRecipient mDeathRecipient;
+ private String mTargetBindingPackageName;
+ private GbaServiceConnection mServiceConnection;
+ private Handler mHandler;
+
+ private String mServicePackageName;
+ private String mServicePackageNameOverride;
+ private int mReleaseTime;
+ private int mRetryTimes = 0;
+
+ //the requests to be sent to the GBA service
+ private final ConcurrentLinkedQueue<GbaAuthRequest> mRequestQueue =
+ new ConcurrentLinkedQueue<>();
+ //the callbacks of the pending requests which have been sent to the GBA service
+ private final SparseArray<IBootstrapAuthenticationCallback> mCallbacks = new SparseArray<>();
+
+ private static final SparseArray<GbaManager> sGbaManagers = new SparseArray<>();
+
+ private final class GbaManagerHandler extends Handler {
+ GbaManagerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ logv("handle msg:" + msg.what);
+ switch (msg.what) {
+ case EVENT_BIND_SERVICE:
+ if (mRetryTimes++ < MAX_RETRY) {
+ rebindService(false);
+ } else {
+ loge("Too many retries, stop now!");
+ sendEmptyMessage(EVENT_BIND_FAIL);
+ }
+ break;
+ case EVENT_UNBIND_SERVICE:
+ //do nothing if new requests are coming
+ if (mRequestQueue.isEmpty()) {
+ clearCallbacksAndNotifyFailure();
+ unbindService();
+ }
+ break;
+ case EVENT_BIND_FAIL:
+ case EVENT_BIND_SUCCESS:
+ mRetryTimes = 0;
+ processRequests();
+ break;
+ case EVENT_REQUESTS_RECEIVED:
+ if (isServiceConnected()) {
+ processRequests();
+ } else {
+ if (!mHandler.hasMessages(EVENT_BIND_SERVICE)) {
+ mHandler.sendEmptyMessage(EVENT_BIND_SERVICE);
+ }
+ }
+ break;
+ case EVENT_CONFIG_CHANGED:
+ mRetryTimes = 0;
+ if (isServiceConnetable() || isServiceConnected()) {
+ //force to rebind when config is changed
+ rebindService(true);
+ }
+ break;
+ default:
+ loge("Unhandled event " + msg.what);
+ }
+ }
+ }
+
+ private final class GbaDeathRecipient implements IBinder.DeathRecipient {
+
+ private final ComponentName mComponentName;
+ private IBinder mBinder;
+
+ GbaDeathRecipient(ComponentName name) {
+ mComponentName = name;
+ }
+
+ public void linkToDeath(IBinder service) throws RemoteException {
+ if (service != null) {
+ mBinder = service;
+ mBinder.linkToDeath(this, 0);
+ }
+ }
+
+ public synchronized void unlinkToDeath() {
+ if (mBinder != null) {
+ mBinder.unlinkToDeath(this, 0);
+ mBinder = null;
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ logd("GbaService(" + mComponentName + ") has died.");
+ unlinkToDeath();
+ //retry if died
+ retryBind();
+ }
+ }
+
+ private final class GbaServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ logd("service " + name + " for Gba is connected.");
+ mIGbaService = IGbaService.Stub.asInterface(service);
+ mDeathRecipient = new GbaDeathRecipient(name);
+ try {
+ mDeathRecipient.linkToDeath(service);
+ } catch (RemoteException exception) {
+ // Remote exception means that the binder already died.
+ mDeathRecipient.binderDied();
+ logd("RemoteException " + exception);
+ }
+ mHandler.sendEmptyMessage(EVENT_BIND_SUCCESS);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ logd("service " + name + " is now disconnected.");
+ mTargetBindingPackageName = null;
+ }
+ }
+
+ @VisibleForTesting
+ public GbaManager(Context context, int subId, String servicePackageName, int releaseTime) {
+ mContext = context;
+ mSubId = subId;
+ mLogTag = "GbaManager[" + subId + "]";
+
+ mServicePackageName = servicePackageName;
+ mReleaseTime = releaseTime;
+
+ HandlerThread headlerThread = new HandlerThread(mLogTag);
+ headlerThread.start();
+ mHandler = new GbaManagerHandler(headlerThread.getLooper());
+
+ if (mReleaseTime < 0) {
+ mHandler.sendEmptyMessage(EVENT_BIND_SERVICE);
+ }
+ }
+
+ /**
+ * create a GbaManager instance for a sub
+ */
+ public static GbaManager make(Context context, int subId,
+ String servicePackageName, int releaseTime) {
+ GbaManager gm = new GbaManager(context, subId, servicePackageName, releaseTime);
+ synchronized (sGbaManagers) {
+ sGbaManagers.put(subId, gm);
+ }
+ return gm;
+ }
+
+ /**
+ * get singleton instance of GbaManager
+ * @return GbaManager
+ */
+ public static GbaManager getInstance(int subId) {
+ synchronized (sGbaManagers) {
+ return sGbaManagers.get(subId);
+ }
+ }
+
+ /**
+ * handle the bootstrap authentication request
+ * @hide
+ */
+ public void bootstrapAuthenticationRequest(GbaAuthRequest req) {
+ logv("bootstrapAuthenticationRequest: " + req);
+ //No GBA service configured
+ if (TextUtils.isEmpty(getServicePackage())) {
+ logd("do not support!");
+ try {
+ req.getCallback().onAuthenticationFailure(req.getToken(),
+ TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED);
+ } catch (RemoteException exception) {
+ loge("exception to call service: " + exception);
+ }
+ return;
+ }
+
+ mRequestQueue.offer(req);
+ if (!mHandler.hasMessages(EVENT_REQUESTS_RECEIVED)) {
+ mHandler.sendEmptyMessage(EVENT_REQUESTS_RECEIVED);
+ }
+ }
+
+ private final IBootstrapAuthenticationCallback mServiceCallback =
+ new IBootstrapAuthenticationCallback.Stub() {
+ @Override
+ public void onKeysAvailable(int token, byte[] gbaKey, String btId) {
+ logv("onKeysAvailable: " + Integer.toHexString(token) + ", id: " + btId);
+
+ IBootstrapAuthenticationCallback cb = null;
+ synchronized (mCallbacks) {
+ cb = mCallbacks.get(token);
+ }
+ if (cb != null) {
+ try {
+ cb.onKeysAvailable(token, gbaKey, btId);
+ } catch (RemoteException exception) {
+ logd("RemoteException " + exception);
+ }
+ synchronized (mCallbacks) {
+ mCallbacks.remove(token);
+ if (mCallbacks.size() == 0) {
+ releaseServiceAsNeeded(0);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailure(int token, int reason) {
+ logd("onAuthenticationFailure: "
+ + Integer.toHexString(token) + " for: " + reason);
+
+ IBootstrapAuthenticationCallback cb = null;
+ synchronized (mCallbacks) {
+ cb = mCallbacks.get(token);
+ }
+ if (cb != null) {
+ try {
+ cb.onAuthenticationFailure(token, reason);
+ } catch (RemoteException exception) {
+ logd("RemoteException " + exception);
+ }
+ synchronized (mCallbacks) {
+ mCallbacks.remove(token);
+ if (mCallbacks.size() == 0) {
+ releaseServiceAsNeeded(0);
+ }
+ }
+ }
+ }
+ };
+
+ private void processRequests() {
+ if (isServiceConnected()) {
+ try {
+ while (!mRequestQueue.isEmpty()) {
+ GbaAuthRequest request = new GbaAuthRequest(mRequestQueue.peek());
+ synchronized (mCallbacks) {
+ mCallbacks.put(request.getToken(), request.getCallback());
+ }
+ request.setCallback(mServiceCallback);
+ mIGbaService.authenticationRequest(request);
+ mRequestQueue.poll();
+ }
+ } catch (RemoteException exception) {
+ // Remote exception means that the binder already died.
+ mDeathRecipient.binderDied();
+ logd("RemoteException " + exception);
+ }
+ } else {
+ while (!mRequestQueue.isEmpty()) {
+ GbaAuthRequest req = mRequestQueue.poll();
+ try {
+ req.getCallback().onAuthenticationFailure(req.getToken(),
+ TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED);
+ } catch (RemoteException exception) {
+ logd("RemoteException " + exception);
+ }
+ }
+ }
+
+ releaseServiceAsNeeded(REQUEST_TIMEOUT_MS);
+ }
+
+ private void releaseServiceAsNeeded(int timeout) {
+ int configReleaseTime = getReleaseTime();
+ //always on
+ if (configReleaseTime < 0) {
+ return;
+ }
+ //schedule to release service
+ int delayTime = configReleaseTime > timeout ? configReleaseTime : timeout;
+ if (mHandler.hasMessages(EVENT_UNBIND_SERVICE)) {
+ mHandler.removeMessages(EVENT_UNBIND_SERVICE);
+ }
+ mHandler.sendEmptyMessageDelayed(EVENT_UNBIND_SERVICE, delayTime);
+ }
+
+ private void clearCallbacksAndNotifyFailure() {
+ synchronized (mCallbacks) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ IBootstrapAuthenticationCallback cb = mCallbacks.valueAt(i);
+ try {
+ cb.onAuthenticationFailure(mCallbacks.keyAt(i),
+ TelephonyManager.GBA_FAILURE_REASON_UNKNOWN);
+ } catch (RemoteException exception) {
+ logd("RemoteException " + exception);
+ }
+ }
+ mCallbacks.clear();
+ }
+ }
+
+ /** return if GBA service has been connected */
+ @VisibleForTesting
+ public boolean isServiceConnected() {
+ //current bound service should be the updated service package.
+ synchronized (this) {
+ return (mIGbaService != null) && (mIGbaService.asBinder().isBinderAlive())
+ && TextUtils.equals(mServicePackageName, mTargetBindingPackageName);
+ }
+ }
+
+ private boolean isServiceConnetable() {
+ synchronized (this) {
+ return mTargetBindingPackageName != null || (
+ mReleaseTime < 0 && !TextUtils.isEmpty(mServicePackageName));
+ }
+ }
+
+ private void unbindService() {
+ if (mDeathRecipient != null) {
+ mDeathRecipient.unlinkToDeath();
+ }
+ if (mServiceConnection != null) {
+ logv("unbind service.");
+ mContext.unbindService(mServiceConnection);
+ }
+ mDeathRecipient = null;
+ mIGbaService = null;
+ mServiceConnection = null;
+ mTargetBindingPackageName = null;
+ }
+
+ private void bindService() {
+ if (mContext == null || !SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ loge("Can't bind service with invalid sub Id.");
+ return;
+ }
+
+ String servicePackage = getServicePackage();
+ if (TextUtils.isEmpty(servicePackage)) {
+ loge("Can't find the binding package");
+ return;
+ }
+
+ Intent intent = new Intent(GbaService.SERVICE_INTERFACE);
+ intent.setPackage(servicePackage);
+
+ try {
+ logv("Trying to bind " + servicePackage);
+ mServiceConnection = new GbaServiceConnection();
+ if (!mContext.bindService(intent, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE)) {
+ logd("Cannot bind to the service.");
+ retryBind();
+ return;
+ }
+ mTargetBindingPackageName = servicePackage;
+ } catch (SecurityException exception) {
+ loge("bindService failed " + exception);
+ }
+ }
+
+ private void retryBind() {
+ //do nothing if binding service has been scheduled
+ if (mHandler.hasMessages(EVENT_BIND_SERVICE)) {
+ logv("wait for pending retry.");
+ return;
+ }
+
+ logv("starting retry:" + mRetryTimes);
+
+ mHandler.sendEmptyMessageDelayed(EVENT_BIND_SERVICE, RETRY_TIME_MS);
+ }
+
+ private void rebindService(boolean isForce) {
+ // Do nothing if no need to rebind.
+ if (!isForce && isServiceConnected()) {
+ logv("Service " + getServicePackage() + " already bound or being bound.");
+ return;
+ }
+
+ unbindService();
+ bindService();
+ }
+
+ /** override GBA service package name to be connected */
+ public boolean overrideServicePackage(String packageName) {
+ synchronized (this) {
+ if (!TextUtils.equals(mServicePackageName, packageName)) {
+ logv("Service package name is changed from " + mServicePackageName
+ + " to " + packageName);
+ mServicePackageName = packageName;
+ if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) {
+ mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** return GBA service package name */
+ public String getServicePackage() {
+ synchronized (this) {
+ return mServicePackageName;
+ }
+ }
+
+ /** override the release time to unbind GBA service after the request is handled */
+ public boolean overrideReleaseTime(int interval) {
+ synchronized (this) {
+ if (mReleaseTime != interval) {
+ logv("Service release time is changed from " + mReleaseTime
+ + " to " + interval);
+ mReleaseTime = interval;
+ if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) {
+ mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** return the release time to unbind GBA service after the request is handled */
+ public int getReleaseTime() {
+ synchronized (this) {
+ return mReleaseTime;
+ }
+ }
+
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /** only for testing */
+ @VisibleForTesting
+ public void destroy() {
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.getLooper().quit();
+ mRequestQueue.clear();
+ mCallbacks.clear();
+ unbindService();
+ sGbaManagers.remove(mSubId);
+ }
+
+ private void logv(String msg) {
+ if (DBG) {
+ Rlog.d(mLogTag, msg);
+ }
+ }
+
+ private void logd(String msg) {
+ Rlog.d(mLogTag, msg);
+ }
+
+ private void loge(String msg) {
+ Rlog.e(mLogTag, msg);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCall.java b/src/java/com/android/internal/telephony/GsmCdmaCall.java
index 93f2e6b760..58513a9dc9 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaCall.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCall.java
@@ -17,6 +17,9 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+
+import com.android.internal.annotations.VisibleForTesting;
/**
* {@hide}
@@ -76,7 +79,7 @@ public class GsmCdmaCall extends Call {
mState = stateFromDCState (dc.state);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void attachFake(Connection conn, State state) {
addConnection(conn);
@@ -146,7 +149,11 @@ public class GsmCdmaCall extends Call {
* Note that at this point, the hangup request has been dispatched to the radio
* but no response has yet been received so update() has not yet been called
*/
- void onHangupLocal() {
+ @VisibleForTesting
+ public void onHangupLocal() {
+ if (!mState.isAlive()) {
+ return;
+ }
for (Connection conn : getConnections()) {
((GsmCdmaConnection) conn).onHangupLocal();
}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 4659ea4d9b..4d9d8d742c 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -42,6 +43,7 @@ import android.text.TextUtils;
import android.util.EventLog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.telephony.Rlog;
@@ -80,24 +82,24 @@ public class GsmCdmaCallTracker extends CallTracker {
private ArrayList<GsmCdmaConnection> mDroppedDuringPoll =
new ArrayList<GsmCdmaConnection>(MAX_CONNECTIONS_GSM);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public GsmCdmaCall mRingingCall = new GsmCdmaCall(this);
// A call that is ringing or (call) waiting
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private GsmCdmaConnection mPendingMO;
private boolean mHangupPendingMO;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private GsmCdmaPhone mPhone;
private boolean mDesiredMute = false; // false = mute off
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public PhoneConstants.State mState = PhoneConstants.State.IDLE;
private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
@@ -258,7 +260,7 @@ public class GsmCdmaCallTracker extends CallTracker {
mCallWaitingRegistrants.remove(h);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void fakeHoldForegroundBeforeDial() {
// We need to make a copy here, since fakeHoldBeforeDial()
// modifies the lists, and we don't want to reverse the order
@@ -274,15 +276,22 @@ public class GsmCdmaCallTracker extends CallTracker {
/**
* clirMode is one of the CLIR_ constants
*/
- public synchronized Connection dialGsm(String dialString, int clirMode, UUSInfo uusInfo,
- Bundle intentExtras)
+ public synchronized Connection dialGsm(String dialString, DialArgs dialArgs)
throws CallStateException {
+ int clirMode = dialArgs.clirMode;
+ UUSInfo uusInfo = dialArgs.uusInfo;
+ Bundle intentExtras = dialArgs.intentExtras;
+ boolean isEmergencyCall = dialArgs.isEmergency;
+ if (isEmergencyCall) {
+ clirMode = CommandsInterface.CLIR_SUPPRESSION;
+ if (Phone.DEBUG_PHONE) log("dial gsm emergency call, set clirModIe=" + clirMode);
+
+ }
+
// note that this triggers call state changed notif
clearDisconnected();
// Check for issues which would preclude dialing and throw a CallStateException.
- boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(),
- dialString);
checkForDialIssues(isEmergencyCall);
String origNumber = dialString;
@@ -297,6 +306,7 @@ public class GsmCdmaCallTracker extends CallTracker {
// and we need to make sure the foreground call is clear
// for the newly dialed connection
switchWaitingOrHoldingAndActive();
+
// This is a hack to delay DIAL so that it is sent out to RIL only after
// EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
// multi-way conference calls due to DIAL being sent out before SWITCH is processed
@@ -320,7 +330,8 @@ public class GsmCdmaCallTracker extends CallTracker {
}
mPendingMO = new GsmCdmaConnection(mPhone, dialString, this, mForegroundCall,
- isEmergencyCall);
+ dialArgs);
+
if (intentExtras != null) {
Rlog.d(LOG_TAG, "dialGsm - emergency dialer: " + intentExtras.getBoolean(
TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL));
@@ -328,6 +339,7 @@ public class GsmCdmaCallTracker extends CallTracker {
TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL));
}
mHangupPendingMO = false;
+
mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo);
mPhone.getVoiceCallSessionStats().onRilDial(mPendingMO);
@@ -340,6 +352,7 @@ public class GsmCdmaCallTracker extends CallTracker {
// and will mark it as dropped.
pollCallsWhenSafe();
} else {
+
// Always unmute when initiating a new call
setMute(false);
@@ -363,7 +376,7 @@ public class GsmCdmaCallTracker extends CallTracker {
/**
* Handle Ecm timer to be canceled or re-started
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void handleEcmTimer(int action) {
mPhone.handleTimerInEmergencyCallbackMode(action);
}
@@ -372,9 +385,11 @@ public class GsmCdmaCallTracker extends CallTracker {
/**
* Disable data call when emergency call is connected
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void disableDataCallInEmergencyCall(String dialString) {
- if (PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString)) {
+ TelephonyManager tm =
+ (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm.isEmergencyNumber(dialString)) {
if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
setIsInEmergencyCall();
}
@@ -392,14 +407,20 @@ public class GsmCdmaCallTracker extends CallTracker {
/**
* clirMode is one of the CLIR_ constants
*/
- private Connection dialCdma(String dialString, int clirMode, Bundle intentExtras)
+ private Connection dialCdma(String dialString, DialArgs dialArgs)
throws CallStateException {
+ int clirMode = dialArgs.clirMode;
+ Bundle intentExtras = dialArgs.intentExtras;
+ boolean isEmergencyCall = dialArgs.isEmergency;
+
+ if (isEmergencyCall) {
+ clirMode = CommandsInterface.CLIR_SUPPRESSION;
+ if (Phone.DEBUG_PHONE) log("dial cdma emergency call, set clirModIe=" + clirMode);
+ }
+
// note that this triggers call state changed notif
clearDisconnected();
- boolean isEmergencyCall =
- PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
-
// Check for issues which would preclude dialing and throw a CallStateException.
checkForDialIssues(isEmergencyCall);
@@ -433,11 +454,12 @@ public class GsmCdmaCallTracker extends CallTracker {
// That call must be idle, so place anything that's
// there on hold
if (mForegroundCall.getState() == GsmCdmaCall.State.ACTIVE) {
- return dialThreeWay(dialString, intentExtras);
+ return dialThreeWay(dialString, dialArgs);
}
mPendingMO = new GsmCdmaConnection(mPhone, dialString, this, mForegroundCall,
- isEmergencyCall);
+ dialArgs);
+
if (intentExtras != null) {
Rlog.d(LOG_TAG, "dialGsm - emergency dialer: " + intentExtras.getBoolean(
TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL));
@@ -487,14 +509,16 @@ public class GsmCdmaCallTracker extends CallTracker {
}
//CDMA
- private Connection dialThreeWay(String dialString, Bundle intentExtras) {
+ private Connection dialThreeWay(String dialString, DialArgs dialArgs) {
+ Bundle intentExtras = dialArgs.intentExtras;
+
if (!mForegroundCall.isIdle()) {
// Check data call and possibly set mIsInEmergencyCall
disableDataCallInEmergencyCall(dialString);
// Attach the new connection to foregroundCall
mPendingMO = new GsmCdmaConnection(mPhone, dialString, this, mForegroundCall,
- mIsInEmergencyCall);
+ dialArgs);
if (intentExtras != null) {
Rlog.d(LOG_TAG, "dialThreeWay - emergency dialer " + intentExtras.getBoolean(
TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL));
@@ -523,24 +547,41 @@ public class GsmCdmaCallTracker extends CallTracker {
return null;
}
- public Connection dial(String dialString, Bundle intentExtras) throws CallStateException {
+ public Connection dial(String dialString, DialArgs dialArgs) throws CallStateException {
if (isPhoneTypeGsm()) {
- return dialGsm(dialString, CommandsInterface.CLIR_DEFAULT, intentExtras);
+ return dialGsm(dialString, dialArgs);
} else {
- return dialCdma(dialString, CommandsInterface.CLIR_DEFAULT, intentExtras);
+ return dialCdma(dialString, dialArgs);
}
}
//GSM
public Connection dialGsm(String dialString, UUSInfo uusInfo, Bundle intentExtras)
throws CallStateException {
- return dialGsm(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras);
+ return dialGsm(dialString, new DialArgs.Builder<>()
+ .setUusInfo(uusInfo)
+ .setClirMode(CommandsInterface.CLIR_DEFAULT)
+ .setIntentExtras(intentExtras)
+ .build());
}
//GSM
private Connection dialGsm(String dialString, int clirMode, Bundle intentExtras)
throws CallStateException {
- return dialGsm(dialString, clirMode, null, intentExtras);
+ return dialGsm(dialString, new DialArgs.Builder<>()
+ .setClirMode(clirMode)
+ .setIntentExtras(intentExtras)
+ .build());
+ }
+
+ //GSM
+ public Connection dialGsm(String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)
+ throws CallStateException {
+ return dialGsm(dialString, new DialArgs.Builder<>()
+ .setClirMode(clirMode)
+ .setUusInfo(uusInfo)
+ .setIntentExtras(intentExtras)
+ .build());
}
public void acceptCall() throws CallStateException {
@@ -591,7 +632,7 @@ public class GsmCdmaCallTracker extends CallTracker {
mPhone.notifyPreciseCallStateChanged();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void switchWaitingOrHoldingAndActive() throws CallStateException {
// Should we bother with this check?
if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) {
@@ -627,7 +668,7 @@ public class GsmCdmaCallTracker extends CallTracker {
mCi.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT));
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void clearDisconnected() {
internalClearDisconnected();
@@ -711,7 +752,7 @@ public class GsmCdmaCallTracker extends CallTracker {
* Obtain a message to use for signalling "invoke getCurrentCalls() when
* this operation and all other pending operations are complete
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Message obtainCompleteMessage() {
return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
}
@@ -720,7 +761,7 @@ public class GsmCdmaCallTracker extends CallTracker {
* Obtain a message to use for signalling "invoke getCurrentCalls() when
* this operation and all other pending operations are complete
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Message obtainCompleteMessage(int what) {
mPendingOperations++;
mLastRelevantPoll = null;
@@ -748,7 +789,7 @@ public class GsmCdmaCallTracker extends CallTracker {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void updatePhoneState() {
PhoneConstants.State oldState = mState;
if (mRingingCall.isRinging()) {
@@ -790,7 +831,7 @@ public class GsmCdmaCallTracker extends CallTracker {
if (ar.exception == null) {
polledCalls = (List)ar.result;
} else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
- // just a dummy empty ArrayList to cause the loop
+ // just a placeholder empty ArrayList to cause the loop
// to hang up all the calls
polledCalls = new ArrayList();
} else {
@@ -877,9 +918,12 @@ public class GsmCdmaCallTracker extends CallTracker {
}
mConnections[i] = new GsmCdmaConnection(mPhone, dc, this, i);
+ log("New connection is not mPendingMO. Creating new GsmCdmaConnection,"
+ + " objId=" + System.identityHashCode(mConnections[i]));
Connection hoConnection = getHoConnection(dc);
if (hoConnection != null) {
+ log("Handover connection found.");
// Single Radio Voice Call Continuity (SRVCC) completed
mConnections[i].migrateFrom(hoConnection);
// Updating connect time for silent redial cases (ex: Calls are transferred
@@ -910,6 +954,7 @@ public class GsmCdmaCallTracker extends CallTracker {
mPhone.notifyHandoverStateChanged(mConnections[i]);
} else {
// find if the MT call is a new ring or unknown connection
+ log("New connection is not mPendingMO nor a pending handover.");
newRinging = checkMtFindNewRinging(dc,i);
if (newRinging == null) {
unknownConnectionAppeared = true;
@@ -1266,7 +1311,7 @@ public class GsmCdmaCallTracker extends CallTracker {
//***** Called from GsmCdmaPhone
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setMute(boolean mute) {
mDesiredMute = mute;
mCi.setMute(mDesiredMute, null);
@@ -1542,7 +1587,7 @@ public class GsmCdmaCallTracker extends CallTracker {
causeCode == CallFailCause.BEARER_NOT_AVAIL ||
causeCode == CallFailCause.ERROR_UNSPECIFIED) {
- CellLocation loc = mPhone.getCellIdentity().asCellLocation();
+ CellLocation loc = mPhone.getCurrentCellIdentity().asCellLocation();
int cid = -1;
if (loc != null) {
if (loc instanceof GsmCellLocation) {
@@ -1555,7 +1600,7 @@ public class GsmCdmaCallTracker extends CallTracker {
TelephonyManager.getDefault().getNetworkType());
}
- if (isEmcRetryCause(causeCode)) {
+ if (isEmcRetryCause(causeCode) && mPhone.useImsForEmergency()) {
String dialString = "";
for(Connection conn : mForegroundCall.mConnections) {
GsmCdmaConnection gsmCdmaConnection = (GsmCdmaConnection)conn;
@@ -1767,12 +1812,12 @@ public class GsmCdmaCallTracker extends CallTracker {
.count() > 0);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isPhoneTypeGsm() {
return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public GsmCdmaPhone getPhone() {
return mPhone;
@@ -1786,7 +1831,7 @@ public class GsmCdmaCallTracker extends CallTracker {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void log(String msg) {
Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index 282d974127..e9ecb79874 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -31,6 +32,7 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.text.TextUtils;
+import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
@@ -39,6 +41,9 @@ import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.telephony.Rlog;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
* {@hide}
*/
@@ -51,13 +56,13 @@ public class GsmCdmaConnection extends Connection {
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
GsmCdmaCallTracker mOwner;
GsmCdmaCall mParent;
boolean mDisconnected;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mIndex; // index in GsmCdmaCallTracker.connections[], -1 if unassigned
// The GsmCdma index is 1 + this
@@ -136,6 +141,7 @@ public class GsmCdmaConnection extends Connection {
mAddress = dc.number;
setEmergencyCallInfo(mOwner);
+ mForwardedNumber = new ArrayList<String>(Arrays.asList(dc.forwardedNumber));
mIsIncoming = dc.isMT;
mCreateTime = System.currentTimeMillis();
mCnapName = dc.name;
@@ -157,7 +163,7 @@ public class GsmCdmaConnection extends Connection {
/** This is an MO call, created when dialing */
public GsmCdmaConnection (GsmCdmaPhone phone, String dialString, GsmCdmaCallTracker ct,
- GsmCdmaCall parent, boolean isEmergencyCall) {
+ GsmCdmaCall parent, DialArgs dialArgs) {
super(phone.getPhoneType());
createWakeLock(phone.getContext());
acquireWakeLock();
@@ -176,8 +182,15 @@ public class GsmCdmaConnection extends Connection {
}
mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
- if (isEmergencyCall) {
+ if (dialArgs.isEmergency) {
setEmergencyCallInfo(mOwner);
+
+ // There was no emergency number info found for this call, however it is
+ // still marked as an emergency number. This may happen if it was a redialed
+ // non-detectable emergency call from IMS.
+ if (getEmergencyNumberInfo() == null) {
+ setNonDetectableEmergencyCallInfo(dialArgs.eccCategory);
+ }
}
mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
@@ -265,7 +278,7 @@ public class GsmCdmaConnection extends Connection {
* If consecutive PAUSE/WAIT sequence in the middle of the string,
* and if there is any WAIT in PAUSE/WAIT sequence, treat them like WAIT.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static String formatDialString(String phoneNumber) {
/**
* TODO(cleanup): This function should move to PhoneNumberUtils, and
@@ -355,7 +368,7 @@ public class GsmCdmaConnection extends Connection {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public GsmCdmaCall.State getState() {
if (mDisconnected) {
@@ -461,7 +474,7 @@ public class GsmCdmaConnection extends Connection {
* @param causeCode RIL disconnect code
* @return the corresponding value from {@link DisconnectCause}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int disconnectCauseFromCode(int causeCode) {
/**
* See 22.001 Annex F.4 for mapping of cause codes
@@ -546,6 +559,9 @@ public class GsmCdmaConnection extends Connection {
case CallFailCause.USER_ALERTING_NO_ANSWER:
return DisconnectCause.TIMED_OUT;
+ case CallFailCause.RADIO_OFF:
+ return DisconnectCause.POWER_OFF;
+
case CallFailCause.ACCESS_CLASS_BLOCKED:
case CallFailCause.ERROR_UNSPECIFIED:
case CallFailCause.NORMAL_CLEARING:
@@ -694,6 +710,14 @@ public class GsmCdmaConnection extends Connection {
mOwner.getPhone().getVoiceCallSessionStats().onAudioCodecChanged(this, dc.audioQuality);
}
+ ArrayList<String> forwardedNumber =
+ new ArrayList<String>(Arrays.asList(dc.forwardedNumber));
+ if (!equalsHandlesNulls(mForwardedNumber, forwardedNumber)) {
+ if (Phone.DEBUG_PHONE) log("update: mForwardedNumber, # changed!");
+ mForwardedNumber = forwardedNumber;
+ changed = true;
+ }
+
// A null cnapName should be the same as ""
if (TextUtils.isEmpty(dc.name)) {
if (!TextUtils.isEmpty(mCnapName)) {
@@ -775,7 +799,7 @@ public class GsmCdmaConnection extends Connection {
/**
* An incoming or outgoing call has connected
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void
onConnectedInOrOut() {
mConnectTime = System.currentTimeMillis();
@@ -878,7 +902,7 @@ public class GsmCdmaConnection extends Connection {
}
//CDMA
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void updateParent(GsmCdmaCall oldParent, GsmCdmaCall newParent){
if (newParent != oldParent) {
if (oldParent != null) {
@@ -1034,13 +1058,13 @@ public class GsmCdmaConnection extends Connection {
notifyPostDialListeners();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void createWakeLock(Context context) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void acquireWakeLock() {
if (mPartialWakeLock != null) {
synchronized (mPartialWakeLock) {
@@ -1071,12 +1095,12 @@ public class GsmCdmaConnection extends Connection {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static boolean isPause(char c) {
return c == PhoneNumberUtils.PAUSE;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static boolean isWait(char c) {
return c == PhoneNumberUtils.WAIT;
}
@@ -1089,7 +1113,7 @@ public class GsmCdmaConnection extends Connection {
// This function is to find the next PAUSE character index if
// multiple pauses in a row. Otherwise it finds the next non PAUSE or
// non WAIT character index.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static int findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
boolean wMatched = isWait(phoneNumber.charAt(currIndex));
int index = currIndex + 1;
@@ -1121,7 +1145,7 @@ public class GsmCdmaConnection extends Connection {
// This function returns either PAUSE or WAIT character to append.
// It is based on the next non PAUSE/WAIT character in the phoneNumber and the
// index for the current PAUSE/WAIT character
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static char findPOrWCharToAppend(String phoneNumber, int currPwIndex,
int nextNonPwCharIndex) {
char c = phoneNumber.charAt(currPwIndex);
@@ -1142,7 +1166,7 @@ public class GsmCdmaConnection extends Connection {
return ret;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String maskDialString(String dialString) {
if (VDBG) {
return dialString;
@@ -1151,7 +1175,7 @@ public class GsmCdmaConnection extends Connection {
return "<MASKED>";
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void fetchDtmfToneDelay(GsmCdmaPhone phone) {
CarrierConfigManager configMgr = (CarrierConfigManager)
phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -1161,12 +1185,12 @@ public class GsmCdmaConnection extends Connection {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isPhoneTypeGsm() {
return mOwner.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String msg) {
Rlog.d(LOG_TAG, "[GsmCdmaConn] " + msg);
}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 771e542d42..78901a265a 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -15,7 +15,6 @@
*/
package com.android.internal.telephony;
-
import static com.android.internal.telephony.CommandException.Error.GENERIC_FAILURE;
import static com.android.internal.telephony.CommandException.Error.SIM_BUSY;
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
@@ -42,6 +41,7 @@ import android.content.SharedPreferences;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -57,23 +57,27 @@ import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Telephony;
import android.sysprop.TelephonyProperties;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.RadioPowerState;
import android.telephony.BarringInfo;
import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentity;
-import android.telephony.DataFailCause;
import android.telephony.ImsiEncryptionInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkScanRequest;
import android.telephony.PhoneNumberUtils;
-import android.telephony.PreciseDataConnectionState;
+import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.SignalThresholdInfo;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
import android.telephony.UssdResponse;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
@@ -86,11 +90,15 @@ import com.android.internal.telephony.cdma.CdmaMmiCode;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.gsm.GsmMmiCode;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -114,6 +122,9 @@ import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -151,6 +162,10 @@ public class GsmCdmaPhone extends Phone {
public static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
public static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
private static final String PREFIX_WPS = "*272";
+ // WPS prefix when CLIR is being deactivated for the call.
+ private static final String PREFIX_WPS_CLIR_DEACTIVATE = "#31#*272";
+ // WPS prefix when CLIS is being activated for the call.
+ private static final String PREFIX_WPS_CLIR_ACTIVATE = "*31#*272";
private CdmaSubscriptionSourceManager mCdmaSSM;
public int mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
private PowerManager.WakeLock mWakeLock;
@@ -186,18 +201,18 @@ public class GsmCdmaPhone extends Phone {
private SIMRecords mSimRecords;
// For non-persisted manual network selection
- private String mManualNetworkSelectionPlmn = "";
+ private String mManualNetworkSelectionPlmn;
//Common
// Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IsimUiccRecords mIsimUiccRecords;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public GsmCdmaCallTracker mCT;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ServiceStateTracker mSST;
public EmergencyNumberTracker mEmergencyNumberTracker;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ArrayList <MmiCode> mPendingMMIs = new ArrayList<MmiCode>();
private IccPhoneBookInterfaceManager mIccPhoneBookIntManager;
@@ -220,14 +235,25 @@ public class GsmCdmaPhone extends Phone {
final String mSetCfNumber;
final Message mOnComplete;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Cfu(String cfNumber, Message onComplete) {
mSetCfNumber = cfNumber;
mOnComplete = onComplete;
}
}
- @UnsupportedAppUsage
+ /**
+ * Used to create ImsManager instances, which may be injected during testing.
+ */
+ @VisibleForTesting
+ public interface ImsManagerFactory {
+ /**
+ * Create a new instance of ImsManager for the specified phoneId.
+ */
+ ImsManager create(Context context, int phoneId);
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IccSmsInterfaceManager mIccSmsInterfaceManager;
private boolean mResetModemOnRadioTechnologyChange = false;
@@ -239,6 +265,8 @@ public class GsmCdmaPhone extends Phone {
private final SettingsObserver mSettingsObserver;
+ private final ImsManagerFactory mImsManagerFactory;
+
// Constructors
public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId,
@@ -249,12 +277,23 @@ public class GsmCdmaPhone extends Phone {
public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
boolean unitTestMode, int phoneId, int precisePhoneType,
TelephonyComponentFactory telephonyComponentFactory) {
+ this(context, ci, notifier,
+ unitTestMode, phoneId, precisePhoneType,
+ telephonyComponentFactory,
+ ImsManager::getInstance);
+ }
+
+ public GsmCdmaPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+ boolean unitTestMode, int phoneId, int precisePhoneType,
+ TelephonyComponentFactory telephonyComponentFactory,
+ ImsManagerFactory imsManagerFactory) {
super(precisePhoneType == PhoneConstants.PHONE_TYPE_GSM ? "GSM" : "CDMA",
notifier, context, ci, unitTestMode, phoneId, telephonyComponentFactory);
// phone type needs to be set before other initialization as other objects rely on it
mPrecisePhoneType = precisePhoneType;
mVoiceCallSessionStats = new VoiceCallSessionStats(mPhoneId, this);
+ mImsManagerFactory = imsManagerFactory;
initOnce(ci);
initRatSpecific(precisePhoneType);
// CarrierSignalAgent uses CarrierActionAgent in construction so it needs to be created
@@ -269,7 +308,7 @@ public class GsmCdmaPhone extends Phone {
.makeServiceStateTracker(this, this.mCi);
mEmergencyNumberTracker = mTelephonyComponentFactory
.inject(EmergencyNumberTracker.class.getName()).makeEmergencyNumberTracker(
- this, this.mCi);
+ this, this.mCi);
mDataEnabledSettings = mTelephonyComponentFactory
.inject(DataEnabledSettings.class.getName()).makeDataEnabledSettings(this);
mDeviceStateMonitor = mTelephonyComponentFactory.inject(DeviceStateMonitor.class.getName())
@@ -283,8 +322,10 @@ public class GsmCdmaPhone extends Phone {
// DcTracker uses ServiceStateTracker and DisplayInfoController so needs to be created
// after they are instantiated
for (int transport : mTransportManager.getAvailableTransports()) {
- mDcTrackers.put(transport, mTelephonyComponentFactory.inject(DcTracker.class.getName())
- .makeDcTracker(this, transport));
+ DcTracker dcTracker = mTelephonyComponentFactory.inject(DcTracker.class.getName())
+ .makeDcTracker(this, transport);
+ mDcTrackers.put(transport, dcTracker);
+ mTransportManager.registerDataThrottler(dcTracker.getDataThrottler());
}
mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
@@ -308,7 +349,13 @@ public class GsmCdmaPhone extends Phone {
SubscriptionController.getInstance().registerForUiccAppsEnabled(this,
EVENT_UICC_APPS_ENABLEMENT_SETTING_CHANGED, null, false);
+ mLinkBandwidthEstimator = mTelephonyComponentFactory
+ .inject(LinkBandwidthEstimator.class.getName())
+ .makeLinkBandwidthEstimator(this);
+
loadTtyMode();
+
+ CallManager.getInstance().registerPhone(this);
logd("GsmCdmaPhone: constructor: sub = " + mPhoneId);
}
@@ -383,6 +430,9 @@ public class GsmCdmaPhone extends Phone {
mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
mCi.registerForVoiceRadioTechChanged(this, EVENT_VOICE_RADIO_TECH_CHANGED, null);
+ mCi.registerForLceInfo(this, EVENT_LINK_CAPACITY_CHANGED, null);
+ mCi.registerForCarrierInfoForImsiEncryption(this,
+ EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION, null);
IntentFilter filter = new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
@@ -396,8 +446,6 @@ public class GsmCdmaPhone extends Phone {
private void initRatSpecific(int precisePhoneType) {
mPendingMMIs.clear();
mIccPhoneBookIntManager.updateIccRecords(null);
- mEsn = null;
- mMeid = null;
mPrecisePhoneType = precisePhoneType;
logd("Precise phone type " + mPrecisePhoneType);
@@ -440,8 +488,19 @@ public class GsmCdmaPhone extends Phone {
tm.setSimOperatorNumericForPhone(mPhoneId, operatorNumeric);
SubscriptionController.getInstance().setMccMnc(operatorNumeric, getSubId());
+
// Sets iso country property by retrieving from build-time system property
- setIsoCountryProperty(operatorNumeric);
+ String iso = "";
+ try {
+ iso = MccTable.countryCodeForMcc(operatorNumeric.substring(0, 3));
+ } catch (StringIndexOutOfBoundsException ex) {
+ Rlog.e(LOG_TAG, "init: countryCodeForMcc error", ex);
+ }
+
+ logd("init: set 'gsm.sim.operator.iso-country' to iso=" + iso);
+ tm.setSimCountryIsoForPhone(mPhoneId, iso);
+ SubscriptionController.getInstance().setCountryIso(iso, getSubId());
+
// Updates MCC MNC device configuration information
logd("update mccmnc=" + operatorNumeric);
MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
@@ -452,32 +511,7 @@ public class GsmCdmaPhone extends Phone {
}
}
- //CDMA
- /**
- * Sets PROPERTY_ICC_OPERATOR_ISO_COUNTRY property
- *
- */
- private void setIsoCountryProperty(String operatorNumeric) {
- TelephonyManager tm = TelephonyManager.from(mContext);
- if (TextUtils.isEmpty(operatorNumeric)) {
- logd("setIsoCountryProperty: clear 'gsm.sim.operator.iso-country'");
- tm.setSimCountryIsoForPhone(mPhoneId, "");
- SubscriptionController.getInstance().setCountryIso("", getSubId());
- } else {
- String iso = "";
- try {
- iso = MccTable.countryCodeForMcc(operatorNumeric.substring(0, 3));
- } catch (StringIndexOutOfBoundsException ex) {
- Rlog.e(LOG_TAG, "setIsoCountryProperty: countryCodeForMcc error", ex);
- }
-
- logd("setIsoCountryProperty: set 'gsm.sim.operator.iso-country' to iso=" + iso);
- tm.setSimCountryIsoForPhone(mPhoneId, iso);
- SubscriptionController.getInstance().setCountryIso(iso, getSubId());
- }
- }
-
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isPhoneTypeGsm() {
return mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM;
}
@@ -521,6 +555,14 @@ public class GsmCdmaPhone extends Phone {
}
}
+ private void updateLinkCapacityEstimate(List<LinkCapacityEstimate> linkCapacityEstimateList) {
+ if (DBG) logd("updateLinkCapacityEstimate: lce list=" + linkCapacityEstimateList);
+ if (linkCapacityEstimateList == null) {
+ return;
+ }
+ notifyLinkCapacityEstimateChanged(linkCapacityEstimateList);
+ }
+
@Override
protected void finalize() {
if(DBG) logd("GsmCdmaPhone finalized");
@@ -530,7 +572,7 @@ public class GsmCdmaPhone extends Phone {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
@NonNull
public ServiceState getServiceState() {
@@ -554,7 +596,7 @@ public class GsmCdmaPhone extends Phone {
mSST.requestCellIdentity(workSource, rspMsg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public PhoneConstants.State getState() {
if (mImsPhone != null) {
@@ -567,7 +609,7 @@ public class GsmCdmaPhone extends Phone {
return mCT.mState;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public int getPhoneType() {
if (mPrecisePhoneType == PhoneConstants.PHONE_TYPE_GSM) {
@@ -587,7 +629,7 @@ public class GsmCdmaPhone extends Phone {
return mEmergencyNumberTracker;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public CallTracker getCallTracker() {
return mCT;
@@ -641,37 +683,7 @@ public class GsmCdmaPhone extends Phone {
}
@Override
- public PreciseDataConnectionState getPreciseDataConnectionState(String apnType) {
- // If we are OOS, then all data connections are null.
- // FIXME: we need to figure out how to report the EIMS PDN connectivity here, which
- // should imply emergency attach - today emergency attach is unknown at the AP,
- // so, we take a guess.
- boolean isEmergencyData = isPhoneTypeGsm()
- && apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY);
-
- if (mSST == null
- || ((mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE)
- && !isEmergencyData)) {
- return new PreciseDataConnectionState(TelephonyManager.DATA_DISCONNECTED,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- ApnSetting.getApnTypesBitmaskFromString(apnType),
- apnType, null, DataFailCause.NONE, null);
- }
-
- // must never be null
- final DcTracker dctForApn = getActiveDcTrackerForApn(apnType);
-
- int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- // Always non-null
- ServiceState ss = getServiceState();
- if (ss != null) {
- networkType = ss.getDataNetworkType();
- }
-
- return dctForApn.getPreciseDataConnectionState(apnType, isDataSuspended(), networkType);
- }
-
- boolean isDataSuspended() {
+ public boolean isDataSuspended() {
return mCT.mState != PhoneConstants.State.IDLE && !mSST.isConcurrentVoiceAndDataAllowed();
}
@@ -686,7 +698,7 @@ public class GsmCdmaPhone extends Phone {
ret = PhoneConstants.DataState.DISCONNECTED;
} else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE
&& (isPhoneTypeCdma() || isPhoneTypeCdmaLte() ||
- (isPhoneTypeGsm() && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)))) {
+ (isPhoneTypeGsm() && !apnType.equals(ApnSetting.TYPE_EMERGENCY_STRING)))) {
// If we're out of service, open TCP sockets may still work
// but no data will flow
@@ -765,7 +777,7 @@ public class GsmCdmaPhone extends Phone {
* {@link com.android.internal.telephony.Call.State}. Use this when changes
* in the precise call state are needed, else use notifyPhoneStateChanged.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
super.notifyPreciseCallStateChangedP();
@@ -843,11 +855,15 @@ public class GsmCdmaPhone extends Phone {
mSuppServiceFailedRegistrants.notifyResult(code);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void notifyServiceStateChanged(ServiceState ss) {
super.notifyServiceStateChangedP(ss);
}
+ void notifyServiceStateChangedForSubId(ServiceState ss, int subId) {
+ super.notifyServiceStateChangedPForSubId(ss, subId);
+ }
+
/**
* Notify that the cell location has changed.
*
@@ -1204,7 +1220,7 @@ public class GsmCdmaPhone extends Phone {
return true;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean handleInCallMmiCommands(String dialString) throws CallStateException {
if (!isPhoneTypeGsm()) {
@@ -1254,7 +1270,7 @@ public class GsmCdmaPhone extends Phone {
return result;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isInCall() {
GsmCdmaCall.State foregroundCallState = getForegroundCall().getState();
GsmCdmaCall.State backgroundCallState = getBackgroundCall().getState();
@@ -1268,11 +1284,22 @@ public class GsmCdmaPhone extends Phone {
private boolean useImsForCall(DialArgs dialArgs) {
return isImsUseEnabled()
&& mImsPhone != null
- && (mImsPhone.isVolteEnabled() || mImsPhone.isWifiCallingEnabled() ||
- (mImsPhone.isVideoEnabled() && VideoProfile.isVideo(dialArgs.videoState)))
+ && (mImsPhone.isVoiceOverCellularImsEnabled() || mImsPhone.isWifiCallingEnabled()
+ || (mImsPhone.isVideoEnabled() && VideoProfile.isVideo(dialArgs.videoState)))
&& (mImsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
}
+ public boolean useImsForEmergency() {
+ CarrierConfigManager configManager =
+ (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);
+ return mImsPhone != null
+ && alwaysTryImsForEmergencyCarrierConfig
+ && ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled()
+ && mImsPhone.isImsAvailable();
+ }
+
@Override
public Connection startConference(String[] participantsToDial, DialArgs dialArgs)
throws CallStateException {
@@ -1311,25 +1338,28 @@ public class GsmCdmaPhone extends Phone {
+ possibleEmergencyNumber);
dialString = possibleEmergencyNumber;
}
- boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString);
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ boolean isEmergency = tm.isEmergencyNumber(dialString);
+ /** Check if the call is Wireless Priority Service call */
+ boolean isWpsCall = dialString != null ? (dialString.startsWith(PREFIX_WPS)
+ || dialString.startsWith(PREFIX_WPS_CLIR_ACTIVATE)
+ || dialString.startsWith(PREFIX_WPS_CLIR_DEACTIVATE)) : false;
+
+ ImsPhone.ImsDialArgs.Builder imsDialArgsBuilder;
+ imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder.from(dialArgs)
+ .setIsEmergency(isEmergency)
+ .setIsWpsCall(isWpsCall);
+ mDialArgs = dialArgs = imsDialArgsBuilder.build();
+
Phone imsPhone = mImsPhone;
- mDialArgs = dialArgs;
CarrierConfigManager configManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
- .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);
- /** Check if the call is Wireless Priority Service call */
- boolean isWpsCall = dialString != null ? dialString.startsWith(PREFIX_WPS) : false;
boolean allowWpsOverIms = configManager.getConfigForSubId(getSubId())
.getBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL);
- boolean useImsForEmergency = imsPhone != null
- && isEmergency
- && alwaysTryImsForEmergencyCarrierConfig
- && ImsManager.getInstance(mContext, mPhoneId).isNonTtyOrTtyOnVolteEnabled()
- && imsPhone.isImsAvailable();
+ boolean useImsForEmergency = isEmergency && useImsForEmergency();
String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
stripSeparators(dialString));
@@ -1352,8 +1382,8 @@ public class GsmCdmaPhone extends Phone {
+ ", isWpsCall=" + isWpsCall
+ ", allowWpsOverIms=" + allowWpsOverIms
+ ", imsPhone=" + imsPhone
- + ", imsPhone.isVolteEnabled()="
- + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
+ + ", imsPhone.isVoiceOverCellularImsEnabled()="
+ + ((imsPhone != null) ? imsPhone.isVoiceOverCellularImsEnabled() : "N/A")
+ ", imsPhone.isVowifiEnabled()="
+ ((imsPhone != null) ? imsPhone.isWifiCallingEnabled() : "N/A")
+ ", imsPhone.isVideoEnabled()="
@@ -1362,7 +1392,17 @@ public class GsmCdmaPhone extends Phone {
+ ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
}
- Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext);
+ // Bypass WiFi Only WFC check if this is an emergency call - we should still try to
+ // place over cellular if possible.
+ if (!isEmergency) {
+ Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mPhoneId, mContext);
+ }
+ if (imsPhone != null && !allowWpsOverIms && !useImsForCall && isWpsCall
+ && imsPhone.getCallTracker() instanceof ImsPhoneCallTracker) {
+ logi("WPS call placed over CS; disconnecting all IMS calls..");
+ ImsPhoneCallTracker tracker = (ImsPhoneCallTracker) imsPhone.getCallTracker();
+ tracker.hangupAllConnections();
+ }
if ((useImsForCall && (!isMmiCode || isPotentialUssdCode))
|| (isMmiCode && useImsForUt)
@@ -1424,13 +1464,8 @@ public class GsmCdmaPhone extends Phone {
mIsTestingEmergencyCallbackMode = true;
mCi.testingEmergencyCall();
}
- if (isPhoneTypeGsm()) {
- return dialInternal(dialString, new DialArgs.Builder<>()
- .setIntentExtras(dialArgs.intentExtras)
- .build());
- } else {
- return dialInternal(dialString, dialArgs);
- }
+
+ return dialInternal(dialString, dialArgs);
}
/**
@@ -1454,13 +1489,14 @@ public class GsmCdmaPhone extends Phone {
}
Phone imsPhone = mImsPhone;
- boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString);
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ boolean isEmergency = tm.isEmergencyNumber(dialString);
boolean shouldConfirmCall =
// Using IMS
isImsUseEnabled()
&& imsPhone != null
// VoLTE not available
- && !imsPhone.isVolteEnabled()
+ && !imsPhone.isVoiceOverCellularImsEnabled()
// WFC is available
&& imsPhone.isWifiCallingEnabled()
&& !isEmergency
@@ -1495,7 +1531,7 @@ public class GsmCdmaPhone extends Phone {
if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
if (mmi == null) {
- return mCT.dialGsm(newDialString, dialArgs.uusInfo, dialArgs.intentExtras);
+ return mCT.dialGsm(newDialString, dialArgs);
} else if (mmi.isTemporaryModeCLIR()) {
return mCT.dialGsm(mmi.mDialingNumber, mmi.getCLIRMode(), dialArgs.uusInfo,
dialArgs.intentExtras);
@@ -1506,7 +1542,7 @@ public class GsmCdmaPhone extends Phone {
return null;
}
} else {
- return mCT.dial(newDialString, dialArgs.intentExtras);
+ return mCT.dial(newDialString, dialArgs);
}
}
@@ -1637,9 +1673,27 @@ public class GsmCdmaPhone extends Phone {
}
@Override
+ public void setRadioPowerOnForTestEmergencyCall(boolean isSelectedPhoneForEmergencyCall) {
+ mSST.clearAllRadioOffReasons();
+
+ // We don't want to have forEmergency call be true to prevent radio emergencyDial command
+ // from being called for a test emergency number because the network may not be able to
+ // find emergency routing for it and dial it do the default emergency services line.
+ setRadioPower(true, false, isSelectedPhoneForEmergencyCall, false);
+ }
+
+ @Override
public void setRadioPower(boolean power, boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
- mSST.setRadioPower(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply);
+ setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
+ Phone.RADIO_POWER_REASON_USER);
+ }
+
+ @Override
+ public void setRadioPowerForReason(boolean power, boolean forEmergencyCall,
+ boolean isSelectedPhoneForEmergencyCall, boolean forceApply, int reason) {
+ mSST.setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall,
+ forceApply, reason);
}
private void storeVoiceMailNumber(String number) {
@@ -1647,7 +1701,7 @@ public class GsmCdmaPhone extends Phone {
SharedPreferences.Editor editor = sp.edit();
setVmSimImsi(getSubscriberId());
logd("storeVoiceMailNumber: mPrecisePhoneType=" + mPrecisePhoneType + " vmNumber="
- + number);
+ + Rlog.pii(LOG_TAG, number));
if (isPhoneTypeGsm()) {
editor.putString(VM_NUMBER + getPhoneId(), number);
editor.apply();
@@ -1668,9 +1722,10 @@ public class GsmCdmaPhone extends Phone {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
String spName = isPhoneTypeGsm() ? VM_NUMBER : VM_NUMBER_CDMA;
number = sp.getString(spName + getPhoneId(), null);
- logd("getVoiceMailNumber: from " + spName + " number=" + number);
+ logd("getVoiceMailNumber: from " + spName + " number="
+ + Rlog.pii(LOG_TAG, number));
} else {
- logd("getVoiceMailNumber: from IccRecords number=" + number);
+ logd("getVoiceMailNumber: from IccRecords number=" + Rlog.pii(LOG_TAG, number));
}
}
if (!isPhoneTypeGsm() && TextUtils.isEmpty(number)) {
@@ -1736,8 +1791,8 @@ public class GsmCdmaPhone extends Phone {
public String getVoiceMailAlphaTag() {
String ret = "";
- if (isPhoneTypeGsm()) {
- IccRecords r = mIccRecords.get();
+ if (isPhoneTypeGsm() || mSimRecords != null) {
+ IccRecords r = isPhoneTypeGsm() ? mIccRecords.get() : mSimRecords;
ret = (r != null) ? r.getVoiceMailAlphaTag() : "";
}
@@ -1790,7 +1845,7 @@ public class GsmCdmaPhone extends Phone {
return mImei;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getEsn() {
if (isPhoneTypeGsm()) {
@@ -1833,51 +1888,65 @@ public class GsmCdmaPhone extends Phone {
}
@Override
- public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+ public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback) {
String operatorNumeric = TelephonyManager.from(mContext)
.getSimOperatorNumericForPhone(mPhoneId);
return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType,
- mContext, operatorNumeric);
+ mContext, operatorNumeric, fallback, getSubId());
}
@Override
public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, mContext, mPhoneId);
+ mCi.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, null);
+ }
+
+ @Override
+ public void deleteCarrierInfoForImsiEncryption() {
+ CarrierInfoManager.deleteCarrierInfoForImsiEncryption(mContext, getSubId());
}
@Override
public int getCarrierId() {
- return mCarrierResolver.getCarrierId();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getCarrierId() : super.getCarrierId();
}
@Override
public String getCarrierName() {
- return mCarrierResolver.getCarrierName();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getCarrierName() : super.getCarrierName();
}
@Override
public int getMNOCarrierId() {
- return mCarrierResolver.getMnoCarrierId();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getMnoCarrierId() : super.getMNOCarrierId();
}
@Override
public int getSpecificCarrierId() {
- return mCarrierResolver.getSpecificCarrierId();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getSpecificCarrierId() : super.getSpecificCarrierId();
}
@Override
public String getSpecificCarrierName() {
- return mCarrierResolver.getSpecificCarrierName();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getSpecificCarrierName() : super.getSpecificCarrierName();
}
@Override
public void resolveSubscriptionCarrierId(String simState) {
- mCarrierResolver.resolveSubscriptionCarrierId(simState);
+ if (mCarrierResolver != null) {
+ mCarrierResolver.resolveSubscriptionCarrierId(simState);
+ }
}
@Override
public int getCarrierIdListVersion() {
- return mCarrierResolver.getCarrierListVersion();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getCarrierListVersion() : super.getCarrierIdListVersion();
}
@Override
@@ -1894,7 +1963,23 @@ public class GsmCdmaPhone extends Phone {
public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
String gid2, String pnn, String spn, String carrierPrivilegeRules, String apn) {
mCarrierResolver.setTestOverrideApn(apn);
- mCarrierResolver.setTestOverrideCarrierPriviledgeRule(carrierPrivilegeRules);
+ UiccProfile uiccProfile = mUiccController.getUiccProfileForPhone(getPhoneId());
+ if (uiccProfile != null) {
+ List<UiccAccessRule> testRules;
+ if (carrierPrivilegeRules == null) {
+ testRules = null;
+ } else if (carrierPrivilegeRules.isEmpty()) {
+ testRules = Collections.emptyList();
+ } else {
+ UiccAccessRule accessRule = new UiccAccessRule(
+ IccUtils.hexStringToBytes(carrierPrivilegeRules), null, 0);
+ testRules = Collections.singletonList(accessRule);
+ }
+ uiccProfile.setTestOverrideCarrierPrivilegeRules(testRules);
+ } else {
+ // TODO: Fix "privilege" typo throughout telephony.
+ mCarrierResolver.setTestOverrideCarrierPriviledgeRule(carrierPrivilegeRules); // NOTYPO
+ }
IccRecords r = null;
if (isPhoneTypeGsm()) {
r = mIccRecords.get();
@@ -1934,7 +2019,7 @@ public class GsmCdmaPhone extends Phone {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getLine1Number() {
if (isPhoneTypeGsm()) {
@@ -1977,7 +2062,7 @@ public class GsmCdmaPhone extends Phone {
mManualNetworkSelectionPlmn = nsm.operatorNumeric;
} else {
//on Phone0 in emergency mode (no SIM), or in some races then clear the cache
- mManualNetworkSelectionPlmn = "";
+ mManualNetworkSelectionPlmn = null;
Rlog.e(LOG_TAG, "Cannot update network selection due to invalid subId "
+ subId);
}
@@ -2060,7 +2145,7 @@ public class GsmCdmaPhone extends Phone {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
switch (commandInterfaceCFReason) {
case CF_REASON_UNCONDITIONAL:
@@ -2075,7 +2160,7 @@ public class GsmCdmaPhone extends Phone {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getSystemProperty(String property, String defValue) {
if (getUnitTestMode()) {
@@ -2084,7 +2169,7 @@ public class GsmCdmaPhone extends Phone {
return TelephonyManager.getTelephonyProperty(mPhoneId, property, defValue);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
switch (commandInterfaceCFAction) {
case CF_ACTION_DISABLE:
@@ -2097,7 +2182,7 @@ public class GsmCdmaPhone extends Phone {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isCfEnable(int action) {
return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
}
@@ -2108,6 +2193,26 @@ public class GsmCdmaPhone extends Phone {
&& mImsPhone.isUtEnabled();
}
+ private boolean isCsRetry(Message onComplete) {
+ if (onComplete != null) {
+ return onComplete.getData().getBoolean(CS_FALLBACK_SS, false);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean useSsOverIms(Message onComplete) {
+ boolean isUtEnabled = isUtEnabled();
+
+ Rlog.d(LOG_TAG, "useSsOverIms: isUtEnabled()= " + isUtEnabled +
+ " isCsRetry(onComplete))= " + isCsRetry(onComplete));
+
+ if (isUtEnabled && !isCsRetry(onComplete)) {
+ return true;
+ }
+ return false;
+ }
+
@Override
public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) {
getCallForwardingOption(commandInterfaceCFReason,
@@ -2117,16 +2222,13 @@ public class GsmCdmaPhone extends Phone {
@Override
public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass,
Message onComplete) {
- if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null)
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
- || imsPhone.isUtEnabled())) {
- imsPhone.getCallForwardingOption(commandInterfaceCFReason, serviceClass,
- onComplete);
- return;
- }
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.getCallForwardingOption(commandInterfaceCFReason, serviceClass, onComplete);
+ return;
+ }
+ if (isPhoneTypeGsm()) {
if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
if (DBG) logd("requesting call forwarding query.");
Message resp;
@@ -2138,9 +2240,8 @@ public class GsmCdmaPhone extends Phone {
mCi.queryCallForwardStatus(commandInterfaceCFReason, serviceClass, null, resp);
}
} else {
- loge("getCallForwardingOption: not possible in CDMA without IMS");
- AsyncResult.forMessage(onComplete, null,
- CommandException.fromRilErrno(RILConstants.REQUEST_NOT_SUPPORTED));
+ loge("getCallForwardingOption: not possible in CDMA, just return empty result");
+ AsyncResult.forMessage(onComplete, makeEmptyCallForward(), null);
onComplete.sendToTarget();
}
}
@@ -2162,17 +2263,14 @@ public class GsmCdmaPhone extends Phone {
int serviceClass,
int timerSeconds,
Message onComplete) {
- if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null)
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
- || imsPhone.isUtEnabled())) {
- imsPhone.setCallForwardingOption(commandInterfaceCFAction,
- commandInterfaceCFReason, dialingNumber, serviceClass,
- timerSeconds, onComplete);
- return;
- }
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason,
+ dialingNumber, serviceClass, timerSeconds, onComplete);
+ return;
+ }
+ if (isPhoneTypeGsm()) {
if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
(isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
@@ -2192,9 +2290,21 @@ public class GsmCdmaPhone extends Phone {
resp);
}
} else {
- loge("setCallForwardingOption: not possible in CDMA without IMS");
- AsyncResult.forMessage(onComplete, null,
- CommandException.fromRilErrno(RILConstants.REQUEST_NOT_SUPPORTED));
+ String formatNumber = GsmCdmaConnection.formatDialString(dialingNumber);
+ String cfNumber = CdmaMmiCode.getCallForwardingPrefixAndNumber(
+ commandInterfaceCFAction, commandInterfaceCFReason, formatNumber);
+ loge("setCallForwardingOption: dial for set call forwarding"
+ + " prefixWithNumber= " + cfNumber + " number= " + dialingNumber);
+
+ PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(getSubId());
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+
+ final TelecomManager telecomManager = TelecomManager.from(mContext);
+ telecomManager.placeCall(
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, cfNumber, null), extras);
+
+ AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null);
onComplete.sendToTarget();
}
}
@@ -2202,12 +2312,13 @@ public class GsmCdmaPhone extends Phone {
@Override
public void getCallBarring(String facility, String password, Message onComplete,
int serviceClass) {
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
+ return;
+ }
+
if (isPhoneTypeGsm()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null) && imsPhone.isUtEnabled()) {
- imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
- return;
- }
mCi.queryFacilityLock(facility, password, serviceClass, onComplete);
} else {
loge("getCallBarringOption: not possible in CDMA");
@@ -2217,12 +2328,13 @@ public class GsmCdmaPhone extends Phone {
@Override
public void setCallBarring(String facility, boolean lockState, String password,
Message onComplete, int serviceClass) {
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
+ return;
+ }
+
if (isPhoneTypeGsm()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null) && imsPhone.isUtEnabled()) {
- imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
- return;
- }
mCi.setFacilityLock(facility, lockState, password, serviceClass, onComplete);
} else {
loge("setCallBarringOption: not possible in CDMA");
@@ -2248,30 +2360,31 @@ public class GsmCdmaPhone extends Phone {
@Override
public void getOutgoingCallerIdDisplay(Message onComplete) {
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.getOutgoingCallerIdDisplay(onComplete);
+ return;
+ }
+
if (isPhoneTypeGsm()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null)
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
- || imsPhone.isUtEnabled())) {
- imsPhone.getOutgoingCallerIdDisplay(onComplete);
- return;
- }
mCi.getCLIR(onComplete);
} else {
loge("getOutgoingCallerIdDisplay: not possible in CDMA");
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED));
+ onComplete.sendToTarget();
}
}
@Override
public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) {
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
+ return;
+ }
+
if (isPhoneTypeGsm()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null)
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
- || imsPhone.isUtEnabled())) {
- imsPhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, onComplete);
- return;
- }
// Packing CLIR value in the message. This will be required for
// SharedPreference caching, if the message comes back as part of
// a success response.
@@ -2279,51 +2392,85 @@ public class GsmCdmaPhone extends Phone {
obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete));
} else {
loge("setOutgoingCallerIdDisplay: not possible in CDMA");
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED));
+ onComplete.sendToTarget();
+ }
+ }
+
+ @Override
+ public void queryCLIP(Message onComplete) {
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.queryCLIP(onComplete);
+ return;
+ }
+
+ if (isPhoneTypeGsm()) {
+ mCi.queryCLIP(onComplete);
+ } else {
+ loge("queryCLIP: not possible in CDMA");
+ AsyncResult.forMessage(onComplete, null,
+ new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED));
+ onComplete.sendToTarget();
}
}
@Override
public void getCallWaiting(Message onComplete) {
- if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null)
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
- || imsPhone.isUtEnabled())) {
- imsPhone.getCallWaiting(onComplete);
- return;
- }
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.getCallWaiting(onComplete);
+ return;
+ }
+ if (isPhoneTypeGsm()) {
//As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
//class parameter in call waiting interrogation to network
mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete);
} else {
- mCi.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
+ int arr[] = {CommandsInterface.SS_STATUS_UNKNOWN, CommandsInterface.SERVICE_CLASS_NONE};
+ AsyncResult.forMessage(onComplete, arr, null);
+ onComplete.sendToTarget();
}
}
@Override
public void setCallWaiting(boolean enable, Message onComplete) {
- if (isPhoneTypeGsm() || isImsUtEnabledOverCdma()) {
- Phone imsPhone = mImsPhone;
- if ((imsPhone != null)
- && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
- || imsPhone.isUtEnabled())) {
- imsPhone.setCallWaiting(enable, onComplete);
- return;
- }
- int serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
- CarrierConfigManager configManager = (CarrierConfigManager)
- getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configManager.getConfigForSubId(getSubId());
- if (b != null) {
- serviceClass = b.getInt(CarrierConfigManager.KEY_CALL_WAITING_SERVICE_CLASS_INT,
- CommandsInterface.SERVICE_CLASS_VOICE);
- }
+ int serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle b = configManager.getConfigForSubId(getSubId());
+ if (b != null) {
+ serviceClass = b.getInt(CarrierConfigManager.KEY_CALL_WAITING_SERVICE_CLASS_INT,
+ CommandsInterface.SERVICE_CLASS_VOICE);
+ }
+ setCallWaiting(enable, serviceClass, onComplete);
+ }
+
+ @Override
+ public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
+ Phone imsPhone = mImsPhone;
+ if (useSsOverIms(onComplete)) {
+ imsPhone.setCallWaiting(enable, onComplete);
+ return;
+ }
+
+ if (isPhoneTypeGsm()) {
mCi.setCallWaiting(enable, serviceClass, onComplete);
} else {
- loge("method setCallWaiting is NOT supported in CDMA without IMS!");
- AsyncResult.forMessage(onComplete, null,
- CommandException.fromRilErrno(RILConstants.REQUEST_NOT_SUPPORTED));
+ String cwPrefix = CdmaMmiCode.getCallWaitingPrefix(enable);
+ Rlog.i(LOG_TAG, "setCallWaiting in CDMA : dial for set call waiting" + " prefix= " + cwPrefix);
+
+ PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(getSubId());
+ Bundle extras = new Bundle();
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+
+ final TelecomManager telecomManager = TelecomManager.from(mContext);
+ telecomManager.placeCall(
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, cwPrefix, null), extras);
+
+ AsyncResult.forMessage(onComplete, CommandsInterface.SS_STATUS_UNKNOWN, null);
onComplete.sendToTarget();
}
}
@@ -2375,8 +2522,8 @@ public class GsmCdmaPhone extends Phone {
}
@Override
- public void updateServiceLocation() {
- mSST.enableSingleLocationUpdate();
+ public void updateServiceLocation(WorkSource workSource) {
+ mSST.enableSingleLocationUpdate(workSource);
}
@Override
@@ -2425,7 +2572,7 @@ public class GsmCdmaPhone extends Phone {
mSST.unregisterForSubscriptionInfoReady(h);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
mEcmExitRespRegistrant = new Registrant(h, what, obj);
@@ -2466,7 +2613,6 @@ public class GsmCdmaPhone extends Phone {
* @param mmi MMI that is done
*/
public void onMMIDone(MmiCode mmi) {
-
/* Only notify complete if it's on the pending list.
* Otherwise, it's already been handled (eg, previously canceled).
* The exception is cancellation of an incoming USSD-REQUEST, which is
@@ -2474,7 +2620,6 @@ public class GsmCdmaPhone extends Phone {
*/
if (mPendingMMIs.remove(mmi) || (isPhoneTypeGsm() && (mmi.isUssdRequest() ||
((GsmMmiCode)mmi).isSsInfo()))) {
-
ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
if (receiverCallback != null) {
Rlog.i(LOG_TAG, "onMMIDone: invoking callback: " + mmi);
@@ -2515,6 +2660,7 @@ public class GsmCdmaPhone extends Phone {
if (!isPhoneTypeGsm()) {
loge("onIncomingUSSD: not expected on GSM");
}
+
boolean isUssdError;
boolean isUssdRequest;
boolean isUssdRelease;
@@ -2542,7 +2688,6 @@ public class GsmCdmaPhone extends Phone {
if (found != null) {
// Complete pending USSD
-
if (isUssdRelease) {
found.onUssdRelease();
} else if (isUssdError) {
@@ -2562,13 +2707,20 @@ public class GsmCdmaPhone extends Phone {
GsmCdmaPhone.this,
mUiccApplication.get());
onNetworkInitiatedUssd(mmi);
+ } else if (isUssdError && !isUssdRelease) {
+ GsmMmiCode mmi;
+ mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage,
+ true,
+ GsmCdmaPhone.this,
+ mUiccApplication.get());
+ mmi.onUssdFinishedError();
}
}
/**
* Make sure the network knows our preferred setting.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void syncClirSetting() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
migrateClirSettingIfNeeded(sp);
@@ -2577,6 +2729,10 @@ public class GsmCdmaPhone extends Phone {
Rlog.i(LOG_TAG, "syncClirSetting: " + CLIR_KEY + getSubId() + "=" + clirSetting);
if (clirSetting >= 0) {
mCi.setCLIR(clirSetting, null);
+ } else {
+ // if there is no preference set, ensure the CLIR is updated to the default value in
+ // order to ensure that CLIR values in the RIL are not carried over during SIM swap.
+ mCi.setCLIR(CommandsInterface.CLIR_DEFAULT, null);
}
}
@@ -2638,8 +2794,10 @@ public class GsmCdmaPhone extends Phone {
}
private void handleRadioPowerStateChange() {
- Rlog.d(LOG_TAG, "handleRadioPowerStateChange, state= " + mCi.getRadioState());
- mNotifier.notifyRadioPowerStateChanged(this, mCi.getRadioState());
+ @RadioPowerState int newState = mCi.getRadioState();
+ Rlog.d(LOG_TAG, "handleRadioPowerStateChange, state= " + newState);
+ mNotifier.notifyRadioPowerStateChanged(this, newState);
+ TelephonyMetrics.getInstance().writeRadioState(mPhoneId, newState);
}
@Override
@@ -2664,6 +2822,11 @@ public class GsmCdmaPhone extends Phone {
mImeiSv = respId[1];
mEsn = respId[2];
mMeid = respId[3];
+ // some modems return all 0's instead of null/empty string when MEID is unavailable
+ if (!TextUtils.isEmpty(mMeid) && mMeid.matches("^0*$")) {
+ logd("EVENT_GET_DEVICE_IDENTITY_DONE: set mMeid to null");
+ mMeid = null;
+ }
}
break;
@@ -2730,6 +2893,15 @@ public class GsmCdmaPhone extends Phone {
}
break;
+ case EVENT_LINK_CAPACITY_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null && ar.result != null) {
+ updateLinkCapacityEstimate((List<LinkCapacityEstimate>) ar.result);
+ } else {
+ logd("Unexpected exception on EVENT_LINK_CAPACITY_CHANGED");
+ }
+ break;
+
case EVENT_UPDATE_PHONE_OBJECT:
phoneObjectUpdater(msg.arg1);
break;
@@ -2742,67 +2914,19 @@ public class GsmCdmaPhone extends Phone {
.config_switch_phone_on_voice_reg_state_change)) {
mCi.getVoiceRadioTechnology(obtainMessage(EVENT_REQUEST_VOICE_RADIO_TECH_DONE));
}
- // Force update IMS service if it is available, if it isn't the config will be
- // updated when ImsPhoneCallTracker opens a connection.
- ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
- if (imsManager.isServiceAvailable()) {
- imsManager.updateImsServiceConfig(true);
- } else {
- logd("ImsManager is not available to update CarrierConfig.");
- }
- // Update broadcastEmergencyCallStateChanges
CarrierConfigManager configMgr = (CarrierConfigManager)
getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
PersistableBundle b = configMgr.getConfigForSubId(getSubId());
- if (b != null) {
- boolean broadcastEmergencyCallStateChanges = b.getBoolean(
- CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL);
- logd("broadcastEmergencyCallStateChanges = " +
- broadcastEmergencyCallStateChanges);
- setBroadcastEmergencyCallStateChanges(broadcastEmergencyCallStateChanges);
- } else {
- loge("didn't get broadcastEmergencyCallStateChanges from carrier config");
- }
- // Changing the cdma roaming settings based carrier config.
- if (b != null) {
- int config_cdma_roaming_mode = b.getInt(
- CarrierConfigManager.KEY_CDMA_ROAMING_MODE_INT);
- int current_cdma_roaming_mode =
- Settings.Global.getInt(getContext().getContentResolver(),
- Settings.Global.CDMA_ROAMING_MODE,
- TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
- switch (config_cdma_roaming_mode) {
- // Carrier's cdma_roaming_mode will overwrite the user's previous settings
- // Keep the user's previous setting in global variable which will be used
- // when carrier's setting is turn off.
- case TelephonyManager.CDMA_ROAMING_MODE_HOME:
- case TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED:
- case TelephonyManager.CDMA_ROAMING_MODE_ANY:
- logd("cdma_roaming_mode is going to changed to "
- + config_cdma_roaming_mode);
- setCdmaRoamingPreference(config_cdma_roaming_mode,
- obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE));
- break;
-
- // When carrier's setting is turn off, change the cdma_roaming_mode to the
- // previous user's setting
- case TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT:
- if (current_cdma_roaming_mode != config_cdma_roaming_mode) {
- logd("cdma_roaming_mode is going to changed to "
- + current_cdma_roaming_mode);
- setCdmaRoamingPreference(current_cdma_roaming_mode,
- obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE));
- }
-
- default:
- loge("Invalid cdma_roaming_mode settings: "
- + config_cdma_roaming_mode);
- }
- } else {
- loge("didn't get the cdma_roaming_mode changes from the carrier config.");
- }
+ updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(b);
+
+ updateCdmaRoamingSettingsAfterCarrierConfigChanged(b);
+
+ updateNrSettingsAfterCarrierConfigChanged(b);
+ loadAllowedNetworksFromSubscriptionDatabase();
+ // Obtain new radio capabilities from the modem, since some are SIM-dependent
+ mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
break;
case EVENT_SET_ROAMING_PREFERENCE_DONE:
@@ -2847,8 +2971,21 @@ public class GsmCdmaPhone extends Phone {
}
if (DBG) logd("Baseband version: " + ar.result);
- TelephonyManager.from(mContext).setBasebandVersionForPhone(getPhoneId(),
- (String)ar.result);
+ /* Android property value is limited to 91 characters, but low layer
+ could pass a larger version string. To avoid runtime exception,
+ truncate the string baseband version string to 45 characters at most
+ for this per sub property. Since the latter part of the version string
+ is meaningful, truncated the version string from the beginning and
+ keep the end of the version.
+ */
+ String version = (String)ar.result;
+ if (version != null) {
+ int length = version.length();
+ final int MAX_VERSION_LEN = SystemProperties.PROP_VALUE_MAX/2;
+ TelephonyManager.from(mContext).setBasebandVersionForPhone(getPhoneId(),
+ length <= MAX_VERSION_LEN ? version
+ : version.substring(length - MAX_VERSION_LEN, length));
+ }
break;
case EVENT_GET_IMEI_DONE:
@@ -3010,7 +3147,7 @@ public class GsmCdmaPhone extends Phone {
Rlog.d(LOG_TAG, "get phone radio capability fail, no need to change " +
"mRadioCapability");
} else {
- radioCapabilityUpdated(rc);
+ radioCapabilityUpdated(rc, false);
}
Rlog.d(LOG_TAG, "EVENT_GET_RADIO_CAPABILITY: phone rc: " + rc);
break;
@@ -3023,7 +3160,8 @@ public class GsmCdmaPhone extends Phone {
case EVENT_SET_CARRIER_DATA_ENABLED:
ar = (AsyncResult) msg.obj;
boolean enabled = (boolean) ar.result;
- mDataEnabledSettings.setCarrierDataEnabled(enabled);
+ mDataEnabledSettings.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_CARRIER,
+ enabled);
break;
case EVENT_DEVICE_PROVISIONED_CHANGE:
mDataEnabledSettings.updateProvisionedChanged();
@@ -3093,6 +3231,10 @@ public class GsmCdmaPhone extends Phone {
}
break;
}
+ case EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION: {
+ resetCarrierKeysForImsiEncryption();
+ break;
+ }
default:
super.handleMessage(msg);
}
@@ -3349,7 +3491,8 @@ public class GsmCdmaPhone extends Phone {
*/
public boolean shouldForceAutoNetworkSelect() {
- int nwMode = Phone.PREFERRED_NT_MODE;
+ int networkTypeBitmask = RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.PREFERRED_NETWORK_MODE);
int subId = getSubId();
// If it's invalid subId, we shouldn't force to auto network select mode.
@@ -3357,21 +3500,23 @@ public class GsmCdmaPhone extends Phone {
return false;
}
- nwMode = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId, nwMode);
+ networkTypeBitmask = (int) getAllowedNetworkTypes(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER);
- logd("shouldForceAutoNetworkSelect in mode = " + nwMode);
+ logd("shouldForceAutoNetworkSelect in mode = " + networkTypeBitmask);
/*
* For multimode targets in global mode manual network
* selection is disallowed. So we should force auto select mode.
*/
if (isManualSelProhibitedInGlobalMode()
- && ((nwMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
- || (nwMode == TelephonyManager.NETWORK_MODE_GLOBAL)) ){
- logd("Should force auto network select mode = " + nwMode);
+ && ((networkTypeBitmask == RadioAccessFamily.getRafFromNetworkType(
+ TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA))
+ || (networkTypeBitmask == RadioAccessFamily.getRafFromNetworkType(
+ TelephonyManager.NETWORK_MODE_GLOBAL)))) {
+ logd("Should force auto network select mode = " + networkTypeBitmask);
return true;
} else {
- logd("Should not force auto network select mode = " + nwMode);
+ logd("Should not force auto network select mode = " + networkTypeBitmask);
}
/*
@@ -3384,7 +3529,7 @@ public class GsmCdmaPhone extends Phone {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isManualSelProhibitedInGlobalMode() {
boolean isProhibited = false;
final String configString = getContext().getResources().getString(com.android.internal
@@ -3434,7 +3579,7 @@ public class GsmCdmaPhone extends Phone {
r.unregisterForRecordsLoaded(this);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void exitEmergencyCallbackMode() {
if (DBG) {
@@ -3630,9 +3775,9 @@ public class GsmCdmaPhone extends Phone {
// Get how many number of system selection code ranges
int selRc = Integer.parseInt(sch[1]);
for (int i = 0; i < selRc; i++) {
- if (!TextUtils.isEmpty(sch[i+2]) && !TextUtils.isEmpty(sch[i+3])) {
- int selMin = Integer.parseInt(sch[i+2]);
- int selMax = Integer.parseInt(sch[i+3]);
+ if (!TextUtils.isEmpty(sch[i*2+2]) && !TextUtils.isEmpty(sch[i*2+3])) {
+ int selMin = Integer.parseInt(sch[i*2+2]);
+ int selMax = Integer.parseInt(sch[i*2+3]);
// Check if the selection code extracted from the dial string falls
// within any of the range pairs specified in the schema.
if ((sysSelCodeInt >= selMin) && (sysSelCodeInt <= selMax)) {
@@ -3773,7 +3918,7 @@ public class GsmCdmaPhone extends Phone {
/**
* Returns the CDMA ERI text,
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getCdmaEriText() {
if (isPhoneTypeGsm()) {
@@ -3922,10 +4067,28 @@ public class GsmCdmaPhone extends Phone {
}
@Override
- public void setSignalStrengthReportingCriteria(
- int signalStrengthMeasure, int[] thresholds, int ran, boolean isEnabled) {
- mCi.setSignalStrengthReportingCriteria(new SignalThresholdInfo(signalStrengthMeasure,
- REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_DB, thresholds, isEnabled),
+ public void setSignalStrengthReportingCriteria(int signalStrengthMeasure,
+ int[] systemThresholds, int ran, boolean isEnabledForSystem) {
+ int[] consolidatedThresholds = mSST.getConsolidatedSignalThresholds(
+ ran,
+ signalStrengthMeasure,
+ isEnabledForSystem && mSST.shouldHonorSystemThresholds() ? systemThresholds
+ : new int[]{},
+ REPORTING_HYSTERESIS_DB);
+ boolean isEnabledForAppRequest = mSST.shouldEnableSignalThresholdForAppRequest(
+ ran,
+ signalStrengthMeasure,
+ getSubId(),
+ isDeviceIdle());
+ mCi.setSignalStrengthReportingCriteria(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(ran)
+ .setSignalMeasurementType(signalStrengthMeasure)
+ .setHysteresisMs(REPORTING_HYSTERESIS_MILLIS)
+ .setHysteresisDb(REPORTING_HYSTERESIS_DB)
+ .setThresholds(consolidatedThresholds, true /*isSystem*/)
+ .setIsEnabled(isEnabledForSystem || isEnabledForAppRequest)
+ .build(),
ran, null);
}
@@ -3960,7 +4123,7 @@ public class GsmCdmaPhone extends Phone {
@Override
public IccCard getIccCard() {
// This function doesn't return null for backwards compatability purposes.
- // To differentiate between cases where SIM is absent vs. unknown we return a dummy
+ // To differentiate between cases where SIM is absent vs. unknown we return a placeholder
// IccCard with the sim state set.
IccCard card = getUiccProfile();
if (card != null) {
@@ -4252,7 +4415,38 @@ public class GsmCdmaPhone extends Phone {
}
}
- @UnsupportedAppUsage
+ private CallForwardInfo[] makeEmptyCallForward() {
+ CallForwardInfo infos[] = new CallForwardInfo[1];
+
+ infos[0] = new CallForwardInfo();
+ infos[0].status = CommandsInterface.SS_STATUS_UNKNOWN;
+ infos[0].reason = 0;
+ infos[0].serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
+ infos[0].toa = PhoneNumberUtils.TOA_Unknown;
+ infos[0].number = "";
+ infos[0].timeSeconds = 0;
+
+ return infos;
+ }
+
+ private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
+ final TelecomManager telecomManager = TelecomManager.from(mContext);
+ final TelephonyManager telephonyManager = TelephonyManager.from(mContext);
+ final Iterator<PhoneAccountHandle> phoneAccounts =
+ telecomManager.getCallCapablePhoneAccounts(true).listIterator();
+
+ while (phoneAccounts.hasNext()) {
+ final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
+ final PhoneAccount phoneAccount = telecomManager.getPhoneAccount(phoneAccountHandle);
+ if (subId == telephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
+ return phoneAccountHandle;
+ }
+ }
+
+ return null;
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void logd(String s) {
Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s);
}
@@ -4261,7 +4455,7 @@ public class GsmCdmaPhone extends Phone {
Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loge(String s) {
Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s);
}
@@ -4292,9 +4486,9 @@ public class GsmCdmaPhone extends Phone {
return mWakeLock;
}
- @Override
public int getLteOnCdmaMode() {
- int currentConfig = super.getLteOnCdmaMode();
+ int currentConfig = TelephonyProperties.lte_on_cdma_device()
+ .orElse(PhoneConstants.LTE_ON_CDMA_FALSE);
int lteOnCdmaModeDynamicValue = currentConfig;
UiccCardApplication cdmaApplication =
@@ -4406,4 +4600,122 @@ public class GsmCdmaPhone extends Phone {
public boolean canDisablePhysicalSubscription() {
return mCi.canToggleUiccApplicationsEnablement();
}
+
+ @Override
+ public @NonNull List<String> getEquivalentHomePlmns() {
+ if (isPhoneTypeGsm()) {
+ IccRecords r = mIccRecords.get();
+ if (r != null && r.getEhplmns() != null) {
+ return Arrays.asList(r.getEhplmns());
+ }
+ } else if (isPhoneTypeCdma()) {
+ loge("EHPLMN is not available in CDMA");
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * @return Currently bound data service package names.
+ */
+ public @NonNull List<String> getDataServicePackages() {
+ List<String> packages = new ArrayList<>();
+ int[] transports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN};
+
+ for (int transport : transports) {
+ DcTracker dct = getDcTracker(transport);
+ if (dct != null) {
+ String pkg = dct.getDataServiceManager().getDataServicePackageName();
+ if (!TextUtils.isEmpty(pkg)) {
+ packages.add(pkg);
+ }
+ }
+ }
+
+ return packages;
+ }
+
+ private void updateBroadcastEmergencyCallStateChangesAfterCarrierConfigChanged(
+ PersistableBundle config) {
+ if (config == null) {
+ loge("didn't get broadcastEmergencyCallStateChanges from carrier config");
+ return;
+ }
+
+ // get broadcastEmergencyCallStateChanges
+ boolean broadcastEmergencyCallStateChanges = config.getBoolean(
+ CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL);
+ logd("broadcastEmergencyCallStateChanges = " + broadcastEmergencyCallStateChanges);
+ setBroadcastEmergencyCallStateChanges(broadcastEmergencyCallStateChanges);
+ }
+
+ private void updateNrSettingsAfterCarrierConfigChanged(PersistableBundle config) {
+ if (config == null) {
+ loge("didn't get the carrier_nr_availability_int from the carrier config.");
+ return;
+ }
+ int[] nrAvailabilities = config.getIntArray(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+ mIsCarrierNrSupported = !ArrayUtils.isEmpty(nrAvailabilities);
+ }
+
+ private void updateCdmaRoamingSettingsAfterCarrierConfigChanged(PersistableBundle config) {
+ if (config == null) {
+ loge("didn't get the cdma_roaming_mode changes from the carrier config.");
+ return;
+ }
+
+ // Changing the cdma roaming settings based carrier config.
+ int config_cdma_roaming_mode = config.getInt(
+ CarrierConfigManager.KEY_CDMA_ROAMING_MODE_INT);
+ int current_cdma_roaming_mode =
+ Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.CDMA_ROAMING_MODE,
+ TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
+ switch (config_cdma_roaming_mode) {
+ // Carrier's cdma_roaming_mode will overwrite the user's previous settings
+ // Keep the user's previous setting in global variable which will be used
+ // when carrier's setting is turn off.
+ case TelephonyManager.CDMA_ROAMING_MODE_HOME:
+ case TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED:
+ case TelephonyManager.CDMA_ROAMING_MODE_ANY:
+ logd("cdma_roaming_mode is going to changed to "
+ + config_cdma_roaming_mode);
+ setCdmaRoamingPreference(config_cdma_roaming_mode,
+ obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE));
+ break;
+
+ // When carrier's setting is turn off, change the cdma_roaming_mode to the
+ // previous user's setting
+ case TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT:
+ if (current_cdma_roaming_mode != config_cdma_roaming_mode) {
+ logd("cdma_roaming_mode is going to changed to "
+ + current_cdma_roaming_mode);
+ setCdmaRoamingPreference(current_cdma_roaming_mode,
+ obtainMessage(EVENT_SET_ROAMING_PREFERENCE_DONE));
+ }
+ break;
+ default:
+ loge("Invalid cdma_roaming_mode settings: " + config_cdma_roaming_mode);
+ }
+ }
+
+ /**
+ * Determines if IMS is enabled for call.
+ *
+ * @return {@code true} if IMS calling is enabled.
+ */
+ public boolean isImsUseEnabled() {
+ ImsManager imsManager = mImsManagerFactory.create(mContext, mPhoneId);
+ boolean imsUseEnabled = ((imsManager.isVolteEnabledByPlatform()
+ && imsManager.isEnhanced4gLteModeSettingEnabledByUser())
+ || (imsManager.isWfcEnabledByPlatform() && imsManager.isWfcEnabledByUser())
+ && imsManager.isNonTtyOrTtyOnVolteEnabled());
+ return imsUseEnabled;
+ }
+
+ @Override
+ public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) {
+ return mIccSmsInterfaceManager.getInboundSmsHandler(is3gpp2);
+ }
}
diff --git a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
index dc990de650..f6e81073b0 100644
--- a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
+++ b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
@@ -16,6 +16,9 @@
package com.android.internal.telephony;
+import android.content.ContentValues;
+
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.AdnRecord;
/**
@@ -68,49 +71,20 @@ interface IIccPhoneBook {
String newTag, String newPhoneNumber,
String pin2);
-
-
/**
* Replace oldAdn with newAdn in ADN-like record in EF
*
* getAdnRecordsInEf must be called at least once before this function,
* otherwise an error will be returned
*
- * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param oldTag adn tag to be replaced
- * @param oldPhoneNumber adn number to be replaced
- * Set both oldTag and oldPhoneNubmer to "" means to replace an
- * empty record, aka, insert new record
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number ot be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
- * @param pin2 required to update EF_FDN, otherwise must be null
* @param subId user preferred subId
- * @return true for success
- */
- boolean updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid,
- String oldTag, String oldPhoneNumber,
- String newTag, String newPhoneNumber,
- String pin2);
- /**
- * Update an ADN-like EF record by record index
- *
- * This is useful for iteration the whole ADN file, such as write the whole
- * phone book or erase/format the whole phonebook
- *
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number to be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
- * @param index is 1-based adn record index to be updated
+ * @param values including ADN,EMAIL,ANR to be updated
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
- boolean updateAdnRecordsInEfByIndex(int efid, String newTag,
- String newPhoneNumber, int index,
- String pin2);
+ boolean updateAdnRecordsInEfBySearchForSubscriber(int subId,
+ int efid, in ContentValues values, String pin2);
/**
* Update an ADN-like EF record by record index
@@ -118,19 +92,15 @@ interface IIccPhoneBook {
* This is useful for iteration the whole ADN file, such as write the whole
* phone book or erase/format the whole phonebook
*
+ * @param subId user preferred subId
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number to be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
+ * @param values including ADN,EMAIL,ANR to be updated
* @param index is 1-based adn record index to be updated
* @param pin2 required to update EF_FDN, otherwise must be null
- * @param subId user preferred subId
* @return true for success
*/
- boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, String newTag,
- String newPhoneNumber, int index,
- String pin2);
+ boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, in ContentValues values,
+ int index, String pin2);
/**
* Get the max munber of records in efid
@@ -157,4 +127,11 @@ interface IIccPhoneBook {
@UnsupportedAppUsage
int[] getAdnRecordsSizeForSubscriber(int subId, int efid);
+ /**
+ * Get the capacity of ADN records
+ *
+ * @param subId user preferred subId
+ * @return AdnCapacity
+ */
+ AdnCapacity getAdnRecordsCapacityForSubscriber(int subId);
}
diff --git a/src/java/com/android/internal/telephony/IccCard.java b/src/java/com/android/internal/telephony/IccCard.java
index 39ce269c0c..949a7b7ab7 100644
--- a/src/java/com/android/internal/telephony/IccCard.java
+++ b/src/java/com/android/internal/telephony/IccCard.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -58,7 +59,7 @@ public class IccCard {
/**
* @return combined Card and current App state
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public State getState() {
return mIccCardState;
}
@@ -133,7 +134,7 @@ public class IccCard {
/**
* Supply Network depersonalization code to the RIL
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void supplyNetworkDepersonalization(String pin, Message onComplete) {
sendMessageWithCardAbsentException(onComplete);
}
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index e23000170e..c6c3d6a384 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -17,36 +17,44 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.text.TextUtils;
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.AdnRecordCache;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.SimPhonebookRecordCache;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import com.android.telephony.Rlog;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * SimPhoneBookInterfaceManager to provide an inter-process communication to
+ * IccPhoneBookInterfaceManager to provide an inter-process communication to
* access ADN-like SIM records.
*/
public class IccPhoneBookInterfaceManager {
static final String LOG_TAG = "IccPhoneBookIM";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected static final boolean DBG = true;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Phone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected AdnRecordCache mAdnCache;
+ protected SimPhonebookRecordCache mSimPbRecordCache;
protected static final int EVENT_GET_SIZE_DONE = 1;
protected static final int EVENT_LOAD_DONE = 2;
@@ -117,9 +125,13 @@ public class IccPhoneBookInterfaceManager {
if (r != null) {
mAdnCache = r.getAdnCache();
}
+
+ mSimPbRecordCache = new SimPhonebookRecordCache(
+ phone.getContext(), phone.getPhoneId(), phone.mCi);
}
public void dispose() {
+ mSimPbRecordCache.dispose();
}
public void updateIccRecords(IccRecords iccRecords) {
@@ -140,60 +152,87 @@ public class IccPhoneBookInterfaceManager {
Rlog.e(LOG_TAG, "[IccPbInterfaceManager] " + msg);
}
+ private AdnRecord generateAdnRecordWithOldTagByContentValues(ContentValues values) {
+ if (values == null) {
+ return null;
+ }
+ final String oldTag = values.getAsString(IccProvider.STR_TAG);
+ final String oldPhoneNumber = values.getAsString(IccProvider.STR_NUMBER);
+ final String oldEmail = values.getAsString(IccProvider.STR_EMAILS);
+ final String oldAnr = values.getAsString(IccProvider.STR_ANRS);;
+ String[] oldEmailArray = TextUtils.isEmpty(oldEmail)
+ ? null : getEmailStringArray(oldEmail);
+ String[] oldAnrArray = TextUtils.isEmpty(oldAnr) ? null : getAnrStringArray(oldAnr);
+ return new AdnRecord(oldTag, oldPhoneNumber, oldEmailArray, oldAnrArray);
+ }
+
+ private AdnRecord generateAdnRecordWithNewTagByContentValues(ContentValues values) {
+ return generateAdnRecordWithNewTagByContentValues(0, 0, values);
+ }
+
+ private AdnRecord generateAdnRecordWithNewTagByContentValues(
+ int efId, int recordNumber, ContentValues values) {
+ if (values == null) {
+ return null;
+ }
+ final String newTag = values.getAsString(IccProvider.STR_NEW_TAG);
+ final String newPhoneNumber = values.getAsString(IccProvider.STR_NEW_NUMBER);
+ final String newEmail = values.getAsString(IccProvider.STR_NEW_EMAILS);
+ final String newAnr = values.getAsString(IccProvider.STR_NEW_ANRS);
+ String[] newEmailArray = TextUtils.isEmpty(newEmail)
+ ? null : getEmailStringArray(newEmail);
+ String[] newAnrArray = TextUtils.isEmpty(newAnr) ? null : getAnrStringArray(newAnr);
+ return new AdnRecord(
+ efId, recordNumber, newTag, newPhoneNumber, newEmailArray, newAnrArray);
+ }
+
/**
* Replace oldAdn with newAdn in ADN-like record in EF
*
* getAdnRecordsInEf must be called at least once before this function,
- * otherwise an error will be returned. Currently the email field
- * if set in the ADN record is ignored.
+ * otherwise an error will be returned.
* throws SecurityException if no WRITE_CONTACTS permission
*
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
- * @param oldTag adn tag to be replaced
- * @param oldPhoneNumber adn number to be replaced
- * Set both oldTag and oldPhoneNubmer to "" means to replace an
- * empty record, aka, insert new record
- * @param newTag adn tag to be stored
- * @param newPhoneNumber adn number ot be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
- * record with empty one, aka, delete old record
+ * @param values old adn tag, phone number, email and anr to be replaced
+ * new adn tag, phone number, email and anr to be stored
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
- public boolean
- updateAdnRecordsInEfBySearch (int efid,
- String oldTag, String oldPhoneNumber,
- String newTag, String newPhoneNumber, String pin2) {
-
+ public boolean updateAdnRecordsInEfBySearchForSubscriber(int efid, ContentValues values,
+ String pin2) {
if (mPhone.getContext().checkCallingOrSelfPermission(
- android.Manifest.permission.WRITE_CONTACTS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Requires android.permission.WRITE_CONTACTS permission");
+ android.Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires android.permission.WRITE_CONTACTS permission");
}
-
- if (DBG) logd("updateAdnRecordsInEfBySearch: efid=0x" +
- Integer.toHexString(efid).toUpperCase() + " ("+ Rlog.pii(LOG_TAG, oldTag) + "," +
- Rlog.pii(LOG_TAG, oldPhoneNumber) + ")" + "==>" + " ("+ Rlog.pii(LOG_TAG, newTag) +
- "," + Rlog.pii(LOG_TAG, newPhoneNumber) + ")"+ " pin2=" + Rlog.pii(LOG_TAG, pin2));
-
efid = updateEfForIccType(efid);
+ if (DBG) {
+ logd("updateAdnRecordsWithContentValuesInEfBySearch: efid=" + efid + ", values = " +
+ values + ", pin2=" + pin2);
+ }
+
checkThread();
Request updateRequest = new Request();
synchronized (updateRequest) {
Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, updateRequest);
- AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber);
- AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
- if (mAdnCache != null) {
- mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
+ AdnRecord oldAdn = generateAdnRecordWithOldTagByContentValues(values);
+ AdnRecord newAdn = generateAdnRecordWithNewTagByContentValues(values);
+ if (usesPbCache(efid)) {
+ mSimPbRecordCache.updateSimPbAdnBySearch(oldAdn, newAdn, response);
waitForResult(updateRequest);
return (boolean) updateRequest.mResult;
} else {
- loge("Failure while trying to update by search due to uninitialised adncache");
- return false;
+ if (mAdnCache != null) {
+ mAdnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response);
+ waitForResult(updateRequest);
+ return (boolean) updateRequest.mResult;
+ } else {
+ loge("Failure while trying to update by search due to uninitialised adncache");
+ return false;
+ }
}
}
}
@@ -209,15 +248,14 @@ public class IccPhoneBookInterfaceManager {
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
* @param newTag adn tag to be stored
* @param newPhoneNumber adn number to be stored
- * Set both newTag and newPhoneNubmer to "" means to replace the old
+ * Set both newTag and newPhoneNumber to "" means to replace the old
* record with empty one, aka, delete old record
* @param index is 1-based adn record index to be updated
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
public boolean
- updateAdnRecordsInEfByIndex(int efid, String newTag,
- String newPhoneNumber, int index, String pin2) {
+ updateAdnRecordsInEfByIndex(int efid, ContentValues values, int index, String pin2) {
if (mPhone.getContext().checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_CONTACTS)
@@ -225,25 +263,29 @@ public class IccPhoneBookInterfaceManager {
throw new SecurityException(
"Requires android.permission.WRITE_CONTACTS permission");
}
-
- if (DBG) logd("updateAdnRecordsInEfByIndex: efid=0x" +
- Integer.toHexString(efid).toUpperCase() + " Index=" + index + " ==> " + "(" +
- Rlog.pii(LOG_TAG, newTag) + "," + Rlog.pii(LOG_TAG, newPhoneNumber) + ")" +
- " pin2=" + Rlog.pii(LOG_TAG, pin2));
-
+ if (DBG) {
+ logd("updateAdnRecordsInEfByIndex: efid=" + efid + ", values = " +
+ values + " index=" + index + ", pin2=" + pin2);
+ }
checkThread();
Request updateRequest = new Request();
synchronized (updateRequest) {
Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE, updateRequest);
- AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber);
- if (mAdnCache != null) {
- mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
+ AdnRecord newAdn = generateAdnRecordWithNewTagByContentValues(efid, index, values);
+ if (usesPbCache(efid)) {
+ mSimPbRecordCache.updateSimPbAdnByRecordId(index, newAdn, response);
waitForResult(updateRequest);
return (boolean) updateRequest.mResult;
} else {
- loge("Failure while trying to update by index due to uninitialised adncache");
- return false;
+ if (mAdnCache != null) {
+ mAdnCache.updateAdnByIndex(efid, newAdn, index, pin2, response);
+ waitForResult(updateRequest);
+ return (boolean) updateRequest.mResult;
+ } else {
+ loge("Failure while trying to update by index due to uninitialised adncache");
+ return false;
+ }
}
}
}
@@ -300,13 +342,20 @@ public class IccPhoneBookInterfaceManager {
Request loadRequest = new Request();
synchronized (loadRequest) {
Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE, loadRequest);
- if (mAdnCache != null) {
- mAdnCache.requestLoadAllAdnLike(efid, mAdnCache.extensionEfForEf(efid), response);
+ if (usesPbCache(efid)) {
+ mSimPbRecordCache.requestLoadAllPbRecords(response);
waitForResult(loadRequest);
return (List<AdnRecord>) loadRequest.mResult;
} else {
- loge("Failure while trying to load from SIM due to uninitialised adncache");
- return null;
+ if (mAdnCache != null) {
+ mAdnCache.requestLoadAllAdnLike(efid,
+ mAdnCache.extensionEfForEf(efid), response);
+ waitForResult(loadRequest);
+ return (List<AdnRecord>) loadRequest.mResult;
+ } else {
+ loge("Failure while trying to load from SIM due to uninitialised adncache");
+ return null;
+ }
}
}
}
@@ -343,5 +392,67 @@ public class IccPhoneBookInterfaceManager {
}
return efid;
}
-}
+ private String[] getEmailStringArray(String str) {
+ return str != null ? str.split(",") : null;
+ }
+
+ private String[] getAnrStringArray(String str) {
+ return str != null ? str.split(":") : null;
+ }
+
+ /**
+ * Get the capacity of ADN records
+ *
+ * @return AdnCapacity
+ */
+ public AdnCapacity getAdnRecordsCapacity() {
+ if (DBG) logd("getAdnRecordsCapacity" );
+ if (mPhone.getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires android.permission.READ_CONTACTS permission");
+ }
+ int phoneId = mPhone.getPhoneId();
+
+ UiccProfile profile = UiccController.getInstance().getUiccProfileForPhone(phoneId);
+
+ if (profile != null) {
+ IccCardConstants.State cardstate = profile.getState();
+ if (cardstate == IccCardConstants.State.READY
+ || cardstate == IccCardConstants.State.LOADED) {
+ checkThread();
+ AdnCapacity capacity = mSimPbRecordCache.isEnabled()
+ ? mSimPbRecordCache.getAdnCapacity() : null;
+ if (capacity == null) {
+ loge("Adn capacity is null");
+ return null;
+ }
+
+ if (DBG) logd("getAdnRecordsCapacity on slot " + phoneId
+ + ": max adn=" + capacity.getMaxAdnCount()
+ + ", used adn=" + capacity.getUsedAdnCount()
+ + ", max email=" + capacity.getMaxEmailCount()
+ + ", used email=" + capacity.getUsedEmailCount()
+ + ", max anr=" + capacity.getMaxAnrCount()
+ + ", used anr=" + capacity.getUsedAnrCount()
+ + ", max name length="+ capacity.getMaxNameLength()
+ + ", max number length =" + capacity.getMaxNumberLength()
+ + ", max email length =" + capacity.getMaxEmailLength()
+ + ", max anr length =" + capacity.getMaxAnrLength());
+ return capacity;
+ } else {
+ logd("No UICC when getAdnRecordsCapacity.");
+ }
+ } else {
+ logd("sim state is not ready when getAdnRecordsCapacity.");
+ }
+ return null;
+ }
+
+ private boolean usesPbCache(int efid) {
+ return mSimPbRecordCache.isEnabled() &&
+ (efid == IccConstants.EF_PBR || efid == IccConstants.EF_ADN);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
index 1b68ff6808..7a128c034d 100644
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -24,12 +24,14 @@ import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
+import android.os.Build;
import android.os.RemoteException;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.telephony.Rlog;
@@ -41,7 +43,7 @@ import java.util.List;
*/
public class IccProvider extends ContentProvider {
private static final String TAG = "IccProvider";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static final boolean DBG = true;
@@ -50,6 +52,7 @@ public class IccProvider extends ContentProvider {
"name",
"number",
"emails",
+ "anrs",
"_id"
};
@@ -61,10 +64,24 @@ public class IccProvider extends ContentProvider {
protected static final int SDN_SUB = 6;
protected static final int ADN_ALL = 7;
- protected static final String STR_TAG = "tag";
- protected static final String STR_NUMBER = "number";
- protected static final String STR_EMAILS = "emails";
- protected static final String STR_PIN2 = "pin2";
+ @VisibleForTesting
+ public static final String STR_TAG = "tag";
+ @VisibleForTesting
+ public static final String STR_NUMBER = "number";
+ @VisibleForTesting
+ public static final String STR_EMAILS = "emails";
+ @VisibleForTesting
+ public static final String STR_ANRS = "anrs";
+ @VisibleForTesting
+ public static final String STR_NEW_TAG = "newTag";
+ @VisibleForTesting
+ public static final String STR_NEW_NUMBER = "newNumber";
+ @VisibleForTesting
+ public static final String STR_NEW_EMAILS = "newEmails";
+ @VisibleForTesting
+ public static final String STR_NEW_ANRS = "newAnrs";
+ @VisibleForTesting
+ public static final String STR_PIN2 = "pin2";
private static final UriMatcher URL_MATCHER =
new UriMatcher(UriMatcher.NO_MATCH);
@@ -202,10 +219,19 @@ public class IccProvider extends ContentProvider {
"Cannot insert into URL: " + url);
}
- String tag = initialValues.getAsString("tag");
- String number = initialValues.getAsString("number");
- // TODO(): Read email instead of sending null.
- boolean success = addIccRecordToEf(efType, tag, number, null, pin2, subId);
+ // We're not using the incoming initialValues
+ // so we can check/gate the arguments.
+ String tag = initialValues.getAsString(STR_TAG);
+ String number = initialValues.getAsString(STR_NUMBER);
+ String emails = initialValues.getAsString(STR_EMAILS);
+ String anrs = initialValues.getAsString(STR_ANRS);
+
+ ContentValues values = new ContentValues();
+ values.put(STR_NEW_TAG, tag);
+ values.put(STR_NEW_NUMBER, number);
+ values.put(STR_NEW_EMAILS, emails);
+ values.put(STR_NEW_ANRS, anrs);
+ boolean success = updateIccRecordInEf(efType, values, pin2, subId);
if (!success) {
return null;
@@ -298,7 +324,8 @@ public class IccProvider extends ContentProvider {
// parse where clause
String tag = null;
String number = null;
- String[] emails = null;
+ String emails = null;
+ String anrs = null;
String pin2 = null;
String[] tokens = where.split(" AND ");
@@ -322,18 +349,24 @@ public class IccProvider extends ContentProvider {
} else if (STR_NUMBER.equals(key)) {
number = normalizeValue(val);
} else if (STR_EMAILS.equals(key)) {
- //TODO(): Email is null.
- emails = null;
+ emails = normalizeValue(val);
+ } else if (STR_ANRS.equals(key)) {
+ anrs = normalizeValue(val);
} else if (STR_PIN2.equals(key)) {
pin2 = normalizeValue(val);
}
}
- if (efType == FDN && TextUtils.isEmpty(pin2)) {
+ ContentValues values = new ContentValues();
+ values.put(STR_TAG, tag);
+ values.put(STR_NUMBER, number);
+ values.put(STR_EMAILS, emails);
+ values.put(STR_ANRS, anrs);
+ if ((efType == FDN) && TextUtils.isEmpty(pin2)) {
return 0;
}
-
- boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2, subId);
+ if (DBG) log("delete mvalues= " + values);
+ boolean success = updateIccRecordInEf(efType, values, pin2, subId);
if (!success) {
return 0;
}
@@ -379,15 +412,7 @@ public class IccProvider extends ContentProvider {
"Cannot insert into URL: " + url);
}
- String tag = values.getAsString("tag");
- String number = values.getAsString("number");
- String[] emails = null;
- String newTag = values.getAsString("newTag");
- String newNumber = values.getAsString("newNumber");
- String[] newEmails = null;
- // TODO(): Update for email.
- boolean success = updateIccRecordInEf(efType, tag, number,
- newTag, newNumber, pin2, subId);
+ boolean success = updateIccRecordInEf(efType, values, pin2, subId);
if (!success) {
return 0;
@@ -434,48 +459,10 @@ public class IccProvider extends ContentProvider {
}
private boolean
- addIccRecordToEf(int efType, String name, String number, String[] emails,
- String pin2, int subId) {
- if (DBG) log("addIccRecordToEf: efType=0x" + Integer.toHexString(efType).toUpperCase() +
- ", name=" + Rlog.pii(TAG, name) + ", number=" + Rlog.pii(TAG, number) +
- ", emails=" + Rlog.pii(TAG, emails) + ", subscription=" + subId);
-
- boolean success = false;
-
- // TODO: do we need to call getAdnRecordsInEf() before calling
- // updateAdnRecordsInEfBySearch()? In any case, we will leave
- // the UI level logic to fill that prereq if necessary. But
- // hopefully, we can remove this requirement.
-
- try {
- IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getIccPhoneBookServiceRegisterer()
- .get());
- if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType,
- "", "", name, number, pin2);
- }
- } catch (RemoteException ex) {
- // ignore it
- } catch (SecurityException ex) {
- if (DBG) log(ex.toString());
- }
- if (DBG) log("addIccRecordToEf: " + success);
- return success;
- }
-
- private boolean
- updateIccRecordInEf(int efType, String oldName, String oldNumber,
- String newName, String newNumber, String pin2, int subId) {
- if (DBG) log("updateIccRecordInEf: efType=0x" + Integer.toHexString(efType).toUpperCase() +
- ", oldname=" + Rlog.pii(TAG, oldName) + ", oldnumber=" + Rlog.pii(TAG, oldNumber) +
- ", newname=" + Rlog.pii(TAG, newName) + ", newnumber=" + Rlog.pii(TAG, newName) +
- ", subscription=" + subId);
-
+ updateIccRecordInEf(int efType, ContentValues values, String pin2, int subId) {
boolean success = false;
-
+ if (DBG) log("updateIccRecordInEf: efType=" + efType +
+ ", values: [ "+ values + " ], subId:" + subId);
try {
IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
TelephonyFrameworkInitializer
@@ -483,8 +470,9 @@ public class IccProvider extends ContentProvider {
.getIccPhoneBookServiceRegisterer()
.get());
if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType, oldName,
- oldNumber, newName, newNumber, pin2);
+ success = iccIpb
+ .updateAdnRecordsInEfBySearchForSubscriber(
+ subId, efType, values, pin2);
}
} catch (RemoteException ex) {
// ignore it
@@ -495,45 +483,16 @@ public class IccProvider extends ContentProvider {
return success;
}
-
- private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails,
- String pin2, int subId) {
- if (DBG) log("deleteIccRecordFromEf: efType=0x" +
- Integer.toHexString(efType).toUpperCase() + ", name=" + Rlog.pii(TAG, name) +
- ", number=" + Rlog.pii(TAG, number) + ", emails=" + Rlog.pii(TAG, emails) +
- ", pin2=" + Rlog.pii(TAG, pin2) + ", subscription=" + subId);
-
- boolean success = false;
-
- try {
- IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getIccPhoneBookServiceRegisterer()
- .get());
- if (iccIpb != null) {
- success = iccIpb.updateAdnRecordsInEfBySearchForSubscriber(subId, efType,
- name, number, "", "", pin2);
- }
- } catch (RemoteException ex) {
- // ignore it
- } catch (SecurityException ex) {
- if (DBG) log(ex.toString());
- }
- if (DBG) log("deleteIccRecordFromEf: " + success);
- return success;
- }
-
/**
* Loads an AdnRecord into a MatrixCursor. Must be called with mLock held.
*
* @param record the ADN record to load from
* @param cursor the cursor to receive the results
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loadRecord(AdnRecord record, MatrixCursor cursor, int id) {
if (!record.isEmpty()) {
- Object[] contact = new Object[4];
+ Object[] contact = new Object[5];
String alphaTag = record.getAlphaTag();
String number = record.getNumber();
@@ -551,12 +510,24 @@ public class IccProvider extends ContentProvider {
}
contact[2] = emailString.toString();
}
- contact[3] = id;
+
+ String[] anrs = record.getAdditionalNumbers();
+ if (anrs != null) {
+ StringBuilder anrString = new StringBuilder();
+ for (String anr : anrs) {
+ if (DBG) log("Adding anr:" + anr);
+ anrString.append(anr);
+ anrString.append(":");
+ }
+ contact[3] = anrString.toString();
+ }
+
+ contact[4] = id;
cursor.addRow(contact);
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String msg) {
Rlog.d(TAG, "[IccProvider] " + msg);
}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 2c402defc9..982c2a290f 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -21,6 +21,7 @@ import static android.telephony.SmsManager.STATUS_ON_ICC_READ;
import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
@@ -32,10 +33,10 @@ import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.UserManager;
import android.provider.Telephony;
import android.telephony.SmsCbMessage;
import android.telephony.SmsManager;
@@ -60,6 +61,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* IccSmsInterfaceManager to provide an inter-process communication to
@@ -69,16 +71,7 @@ public class IccSmsInterfaceManager {
static final String LOG_TAG = "IccSmsInterfaceManager";
static final boolean DBG = true;
- @UnsupportedAppUsage
- protected final Object mLock = new Object();
- @UnsupportedAppUsage
- protected boolean mSuccess;
- @UnsupportedAppUsage
- private List<SmsRawData> mSms;
-
- private String mSmsc;
-
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CellBroadcastRangeManager mCellBroadcastRangeManager =
new CellBroadcastRangeManager();
private CdmaBroadcastRangeManager mCdmaBroadcastRangeManager =
@@ -95,11 +88,11 @@ public class IccSmsInterfaceManager {
public static final int SMS_MESSAGE_PRIORITY_NOT_SPECIFIED = -1;
public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Phone mPhone;
@UnsupportedAppUsage
final protected Context mContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final protected AppOpsManager mAppOps;
@VisibleForTesting
public SmsDispatchersController mDispatchersController;
@@ -107,84 +100,80 @@ public class IccSmsInterfaceManager {
private final LocalLog mCellBroadcastLocalLog = new LocalLog(100);
- @UnsupportedAppUsage
+ private static final class Request {
+ AtomicBoolean mStatus = new AtomicBoolean(false);
+ Object mResult = null;
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- AsyncResult ar;
+ AsyncResult ar = (AsyncResult) msg.obj;
+ Request request = (Request) ar.userObj;
switch (msg.what) {
case EVENT_UPDATE_DONE:
- ar = (AsyncResult) msg.obj;
- synchronized (mLock) {
- mSuccess = (ar.exception == null);
- mLock.notifyAll();
- }
- break;
- case EVENT_LOAD_DONE:
- ar = (AsyncResult)msg.obj;
- synchronized (mLock) {
- if (ar.exception == null) {
- mSms = buildValidRawData((ArrayList<byte[]>) ar.result);
- //Mark SMS as read after importing it from card.
- markMessagesAsRead((ArrayList<byte[]>) ar.result);
- } else {
- if (Rlog.isLoggable("SMS", Log.DEBUG)) {
- loge("Cannot load Sms records");
- }
- mSms = null;
- }
- mLock.notifyAll();
- }
- break;
case EVENT_SET_BROADCAST_ACTIVATION_DONE:
case EVENT_SET_BROADCAST_CONFIG_DONE:
- ar = (AsyncResult) msg.obj;
- synchronized (mLock) {
- mSuccess = (ar.exception == null);
- mLock.notifyAll();
- }
+ case EVENT_SET_SMSC_DONE:
+ notifyPending(request, ar.exception == null);
break;
- case EVENT_GET_SMSC_DONE:
- ar = (AsyncResult) msg.obj;
- synchronized (mLock) {
- if (ar.exception == null) {
- mSmsc = (String) ar.result;
- } else {
- loge("Cannot read SMSC");
- mSmsc = null;
+ case EVENT_LOAD_DONE:
+ List<SmsRawData> smsRawDataList = null;
+ if (ar.exception == null) {
+ smsRawDataList = buildValidRawData((ArrayList<byte[]>) ar.result);
+ //Mark SMS as read after importing it from card.
+ markMessagesAsRead((ArrayList<byte[]>) ar.result);
+ } else {
+ if (Rlog.isLoggable("SMS", Log.DEBUG)) {
+ loge("Cannot load Sms records");
}
- mLock.notifyAll();
}
+ notifyPending(request, smsRawDataList);
break;
- case EVENT_SET_SMSC_DONE:
- ar = (AsyncResult) msg.obj;
- synchronized (mLock) {
- mSuccess = (ar.exception == null);
- mLock.notifyAll();
+ case EVENT_GET_SMSC_DONE:
+ String smsc = null;
+ if (ar.exception == null) {
+ smsc = (String) ar.result;
+ } else {
+ loge("Cannot read SMSC");
}
+ notifyPending(request, smsc);
break;
}
}
+
+ private void notifyPending(Request request, Object result) {
+ if (request != null) {
+ synchronized (request) {
+ request.mResult = result;
+ request.mStatus.set(true);
+ request.notifyAll();
+ }
+ }
+ }
};
protected IccSmsInterfaceManager(Phone phone) {
this(phone, phone.getContext(),
(AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE),
- (UserManager) phone.getContext().getSystemService(Context.USER_SERVICE),
new SmsDispatchersController(
- phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor));
+ phone, phone.mSmsStorageMonitor, phone.mSmsUsageMonitor),
+ new SmsPermissions(phone, phone.getContext(),
+ (AppOpsManager) phone.getContext().getSystemService(
+ Context.APP_OPS_SERVICE)));
}
@VisibleForTesting
public IccSmsInterfaceManager(
- Phone phone, Context context, AppOpsManager appOps, UserManager userManager,
- SmsDispatchersController dispatchersController) {
+ Phone phone, Context context, AppOpsManager appOps,
+ SmsDispatchersController dispatchersController, SmsPermissions smsPermissions) {
mPhone = phone;
mContext = context;
mAppOps = appOps;
mDispatchersController = dispatchersController;
- mSmsPermissions = new SmsPermissions(phone, context, appOps);
+ mSmsPermissions = smsPermissions;
}
private void enforceNotOnHandlerThread(String methodName) {
@@ -227,7 +216,7 @@ public class IccSmsInterfaceManager {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void enforceReceiveAndSend(String message) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.RECEIVE_SMS, message);
@@ -255,7 +244,7 @@ public class IccSmsInterfaceManager {
*
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean
updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu) {
if (DBG) log("updateMessageOnIccEf: index=" + index +
@@ -268,9 +257,9 @@ public class IccSmsInterfaceManager {
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return false;
}
- synchronized(mLock) {
- mSuccess = false;
- Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
+ Request updateRequest = new Request();
+ synchronized (updateRequest) {
+ Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE, updateRequest);
if ((status & 0x01) == STATUS_ON_ICC_FREE) {
// RIL_REQUEST_DELETE_SMS_ON_SIM vs RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM
@@ -287,20 +276,16 @@ public class IccSmsInterfaceManager {
IccFileHandler fh = mPhone.getIccFileHandler();
if (fh == null) {
response.recycle();
- return mSuccess; /* is false */
+ return false; /* is false */
}
byte[] record = makeSmsRecordData(status, pdu);
fh.updateEFLinearFixed(
IccConstants.EF_SMS,
index, record, null, response);
}
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to update by index");
- }
+ waitForResult(updateRequest);
}
- return mSuccess;
+ return (boolean) updateRequest.mResult;
}
/**
@@ -316,7 +301,7 @@ public class IccSmsInterfaceManager {
* @param smsc the SMSC for this message. Null means use default.
* @return true for success. Otherwise false.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc) {
//NOTE smsc not used in RUIM
if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
@@ -328,9 +313,9 @@ public class IccSmsInterfaceManager {
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return false;
}
- synchronized(mLock) {
- mSuccess = false;
- Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE);
+ Request copyRequest = new Request();
+ synchronized (copyRequest) {
+ Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE, copyRequest);
//RIL_REQUEST_WRITE_SMS_TO_SIM vs RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM
if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
@@ -340,13 +325,9 @@ public class IccSmsInterfaceManager {
mPhone.mCi.writeSmsToRuim(status, pdu, response);
}
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to update by index");
- }
+ waitForResult(copyRequest);
}
- return mSuccess;
+ return (boolean) copyRequest.mResult;
}
/**
@@ -355,7 +336,7 @@ public class IccSmsInterfaceManager {
* @return list of SmsRawData of all sms on Icc
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage) {
if (DBG) log("getAllMessagesFromEF");
@@ -368,25 +349,21 @@ public class IccSmsInterfaceManager {
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return new ArrayList<SmsRawData>();
}
- synchronized(mLock) {
+ Request getRequest = new Request();
+ synchronized (getRequest) {
IccFileHandler fh = mPhone.getIccFileHandler();
if (fh == null) {
loge("Cannot load Sms records. No icc card?");
- mSms = null;
- return mSms;
+ return null;
}
- Message response = mHandler.obtainMessage(EVENT_LOAD_DONE);
+ Message response = mHandler.obtainMessage(EVENT_LOAD_DONE, getRequest);
fh.loadEFLinearFixedAll(IccConstants.EF_SMS, response);
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to load from the Icc");
- }
+ waitForResult(getRequest);
}
- return mSms;
+ return (List<SmsRawData>) getRequest.mResult;
}
/**
@@ -410,7 +387,7 @@ public class IccSmsInterfaceManager {
* PendingIntent)} instead.
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendData(callingPackage, null, destAddr, scAddr, destPort, data,
@@ -561,7 +538,7 @@ public class IccSmsInterfaceManager {
+ " text='" + text + "' sentIntent=" + sentIntent + " deliveryIntent="
+ deliveryIntent + " priority=" + priority + " expectMore=" + expectMore
+ " validityPeriod=" + validityPeriod + " isForVVM=" + isForVvm
- + " id= " + messageId);
+ + " " + SmsController.formatCrossStackMessageId(messageId));
}
notifyIfOutgoingEmergencySms(destAddr);
destAddr = filterDestAddress(destAddr);
@@ -640,7 +617,7 @@ public class IccSmsInterfaceManager {
* android application framework. This intent is broadcasted at
* the same time an SMS received from radio is acknowledged back.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -652,7 +629,7 @@ public class IccSmsInterfaceManager {
"\n format=" + format +
"\n receivedIntent=" + receivedIntent);
}
- mDispatchersController.injectSmsPdu(pdu, format,
+ mDispatchersController.injectSmsPdu(pdu, format, false /* isOverIms */,
result -> {
if (receivedIntent != null) {
try {
@@ -767,7 +744,7 @@ public class IccSmsInterfaceManager {
for (String part : parts) {
log("sendMultipartTextWithOptions: destAddr=" + destAddr + ", srAddr=" + scAddr
+ ", part[" + (i++) + "]=" + part
- + " id: " + messageId);
+ + " " + SmsController.formatCrossStackMessageId(messageId));
}
}
notifyIfOutgoingEmergencySms(destAddr);
@@ -813,13 +790,13 @@ public class IccSmsInterfaceManager {
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getPremiumSmsPermission(String packageName) {
return mDispatchersController.getPremiumSmsPermission(packageName);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setPremiumSmsPermission(String packageName, int permission) {
mDispatchersController.setPremiumSmsPermission(packageName, permission);
}
@@ -888,17 +865,13 @@ public class IccSmsInterfaceManager {
return null;
}
enforceNotOnHandlerThread("getSmscAddressFromIccEf");
- synchronized (mLock) {
- mSmsc = null;
- Message response = mHandler.obtainMessage(EVENT_GET_SMSC_DONE);
+ Request getRequest = new Request();
+ synchronized (getRequest) {
+ Message response = mHandler.obtainMessage(EVENT_GET_SMSC_DONE, getRequest);
mPhone.mCi.getSmscAddress(response);
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to read SMSC");
- }
+ waitForResult(getRequest);
}
- return mSmsc;
+ return (String) getRequest.mResult;
}
/**
@@ -912,17 +885,14 @@ public class IccSmsInterfaceManager {
callingPackage, "setSmscAddressOnIccEf")) {
return false;
}
- synchronized (mLock) {
- mSuccess = false;
- Message response = mHandler.obtainMessage(EVENT_SET_SMSC_DONE);
+ enforceNotOnHandlerThread("setSmscAddressOnIccEf");
+ Request setRequest = new Request();
+ synchronized (setRequest) {
+ Message response = mHandler.obtainMessage(EVENT_SET_SMSC_DONE, setRequest);
mPhone.mCi.setSmscAddress(smsc, response);
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to write SMSC");
- }
+ waitForResult(setRequest);
}
- return mSuccess;
+ return (boolean) setRequest.mResult;
}
public boolean enableCellBroadcast(int messageIdentifier, int ranType) {
@@ -959,7 +929,7 @@ public class IccSmsInterfaceManager {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
synchronized public boolean enableGsmBroadcastRange(int startMessageId, int endMessageId) {
mContext.enforceCallingPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
@@ -989,7 +959,7 @@ public class IccSmsInterfaceManager {
return true;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
synchronized public boolean disableGsmBroadcastRange(int startMessageId, int endMessageId) {
mContext.enforceCallingPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
@@ -1019,7 +989,7 @@ public class IccSmsInterfaceManager {
return true;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
synchronized public boolean enableCdmaBroadcastRange(int startMessageId, int endMessageId) {
mContext.enforceCallingPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
@@ -1048,7 +1018,7 @@ public class IccSmsInterfaceManager {
return true;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
synchronized public boolean disableCdmaBroadcastRange(int startMessageId, int endMessageId) {
mContext.enforceCallingPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
@@ -1080,8 +1050,9 @@ public class IccSmsInterfaceManager {
/**
* Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this.
*/
+ @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
public void resetAllCellBroadcastRanges() {
- mContext.enforceCallingPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
+ mContext.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
"resetAllCellBroadcastRanges");
mCdmaBroadcastRangeManager.clearRanges();
mCellBroadcastRangeManager.clearRanges();
@@ -1168,26 +1139,22 @@ public class IccSmsInterfaceManager {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
if (DBG) {
log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
}
enforceNotOnHandlerThread("setCellBroadcastConfig");
- synchronized (mLock) {
- Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
+ Request setRequest = new Request();
+ synchronized (setRequest) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE, setRequest);
- mSuccess = false;
mPhone.mCi.setGsmBroadcastConfig(configs, response);
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to set cell broadcast config");
- }
+ waitForResult(setRequest);
}
- return mSuccess;
+ return (boolean) setRequest.mResult;
}
private boolean setCellBroadcastActivation(boolean activate) {
@@ -1196,43 +1163,35 @@ public class IccSmsInterfaceManager {
}
enforceNotOnHandlerThread("setCellBroadcastConfig");
- synchronized (mLock) {
- Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
+ Request setRequest = new Request();
+ synchronized (setRequest) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE,
+ setRequest);
- mSuccess = false;
mPhone.mCi.setGsmBroadcastActivation(activate, response);
-
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to set cell broadcast activation");
- }
+ waitForResult(setRequest);
}
- return mSuccess;
+ return (boolean) setRequest.mResult;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs) {
if (DBG) {
log("Calling setCdmaBroadcastConfig with " + configs.length + " configurations");
}
enforceNotOnHandlerThread("setCdmaBroadcastConfig");
- synchronized (mLock) {
- Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
+ Request setRequest = new Request();
+ synchronized (setRequest) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE, setRequest);
- mSuccess = false;
mPhone.mCi.setCdmaBroadcastConfig(configs, response);
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to set cdma broadcast config");
- }
+ waitForResult(setRequest);
}
- return mSuccess;
+ return (boolean) setRequest.mResult;
}
private boolean setCdmaBroadcastActivation(boolean activate) {
@@ -1241,20 +1200,17 @@ public class IccSmsInterfaceManager {
}
enforceNotOnHandlerThread("setCdmaBroadcastActivation");
- synchronized (mLock) {
- Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
+ Request setRequest = new Request();
+ synchronized (setRequest) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE,
+ setRequest);
- mSuccess = false;
mPhone.mCi.setCdmaBroadcastActivation(activate, response);
- try {
- mLock.wait();
- } catch (InterruptedException e) {
- loge("interrupted while trying to set cdma broadcast activation");
- }
+ waitForResult(setRequest);
}
- return mSuccess;
+ return (boolean) setRequest.mResult;
}
@UnsupportedAppUsage
@@ -1270,12 +1226,12 @@ public class IccSmsInterfaceManager {
Rlog.e(LOG_TAG, msg, e);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isImsSmsSupported() {
return mDispatchersController.isIms();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getImsSmsFormat() {
return mDispatchersController.getImsSmsFormat();
}
@@ -1285,7 +1241,7 @@ public class IccSmsInterfaceManager {
* PendingIntent)} instead
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void sendStoredText(String callingPkg, Uri messageUri, String scAddress,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendStoredText(callingPkg, null, messageUri, scAddress, sentIntent, deliveryIntent);
@@ -1329,7 +1285,7 @@ public class IccSmsInterfaceManager {
* instead
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
sendStoredMultipartText(callingPkg, null, messageUri, scAddress, sentIntents,
@@ -1412,10 +1368,12 @@ public class IccSmsInterfaceManager {
0L /* messageId */);
}
- public int getSmsCapacityOnIcc() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
- "getSmsCapacityOnIcc");
+ public int getSmsCapacityOnIcc(String callingPackage, String callingFeatureId) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mContext, mPhone.getSubId(), callingPackage, callingFeatureId,
+ "getSmsCapacityOnIcc")) {
+ return 0;
+ }
int numberOnIcc = 0;
if (mPhone.getIccRecordsLoaded()) {
@@ -1521,12 +1479,31 @@ public class IccSmsInterfaceManager {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String filterDestAddress(String destAddr) {
String result = SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(), destAddr);
return result != null ? result : destAddr;
}
+ private void waitForResult(Request request) {
+ synchronized (request) {
+ while (!request.mStatus.get()) {
+ try {
+ request.wait();
+ } catch (InterruptedException e) {
+ log("Interrupted while waiting for result");
+ }
+ }
+ }
+ }
+
+ /**
+ * Get InboundSmsHandler for the phone.
+ */
+ public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) {
+ return mDispatchersController.getInboundSmsHandler(is3gpp2);
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Enabled GSM channels: " + mCellBroadcastRangeManager);
pw.println("Enabled CDMA channels: " + mCdmaBroadcastRangeManager);
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index c99b282d79..eb96af224f 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -22,8 +22,8 @@ import android.os.PersistableBundle;
import android.os.RemoteException;
import android.provider.Telephony.Sms.Intents;
import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
+import android.telephony.SmsManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsSmsListener;
@@ -31,7 +31,6 @@ import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
-import android.util.Pair;
import com.android.ims.FeatureConnector;
import com.android.ims.ImsException;
@@ -46,6 +45,7 @@ import com.android.telephony.Rlog;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -56,6 +56,20 @@ import java.util.concurrent.atomic.AtomicInteger;
public class ImsSmsDispatcher extends SMSDispatcher {
private static final String TAG = "ImsSmsDispatcher";
+ private static final int CONNECT_DELAY_MS = 5000; // 5 seconds;
+
+ /**
+ * Creates FeatureConnector instances for ImsManager, used during testing to inject mock
+ * connector instances.
+ */
+ @VisibleForTesting
+ public interface FeatureConnectorFactory {
+ /**
+ * Create a new FeatureConnector for ImsManager.
+ */
+ FeatureConnector<ImsManager> create(Context context, int phoneId, String logPrefix,
+ FeatureConnector.Listener<ImsManager> listener, Executor executor);
+ }
@VisibleForTesting
public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>();
@@ -68,6 +82,15 @@ public class ImsSmsDispatcher extends SMSDispatcher {
private final FeatureConnector<ImsManager> mImsManagerConnector;
/** Telephony metrics instance for logging metrics event */
private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
+ private ImsManager mImsManager;
+ private FeatureConnectorFactory mConnectorFactory;
+
+ private Runnable mConnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mImsManagerConnector.connect();
+ }
+ };
/**
* Listen to the IMS service state change
@@ -117,156 +140,162 @@ public class ImsSmsDispatcher extends SMSDispatcher {
private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
@Override
public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
- int reason, int networkReasonCode) {
- logd("onSendSmsResult token=" + token + " messageRef=" + messageRef
- + " status=" + status + " reason=" + reason + " networkReasonCode="
- + networkReasonCode);
- // TODO integrate networkReasonCode into IMS SMS metrics.
- SmsTracker tracker = mTrackers.get(token);
- mMetrics.writeOnImsServiceSmsSolicitedResponse(mPhone.getPhoneId(), status, reason,
- (tracker != null ? tracker.mMessageId : 0L));
- if (tracker == null) {
- throw new IllegalArgumentException("Invalid token.");
- }
- tracker.mMessageRef = messageRef;
- switch(status) {
- case ImsSmsImplBase.SEND_STATUS_OK:
- if (tracker.mDeliveryIntent == null) {
- // Remove the tracker here if a status report is not requested.
+ @SmsManager.Result int reason, int networkReasonCode) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ logd("onSendSmsResult token=" + token + " messageRef=" + messageRef
+ + " status=" + status + " reason=" + reason + " networkReasonCode="
+ + networkReasonCode);
+ // TODO integrate networkReasonCode into IMS SMS metrics.
+ SmsTracker tracker = mTrackers.get(token);
+ mMetrics.writeOnImsServiceSmsSolicitedResponse(mPhone.getPhoneId(), status, reason,
+ (tracker != null ? tracker.mMessageId : 0L));
+ if (tracker == null) {
+ throw new IllegalArgumentException("Invalid token.");
+ }
+ tracker.mMessageRef = messageRef;
+ switch(status) {
+ case ImsSmsImplBase.SEND_STATUS_OK:
+ if (tracker.mDeliveryIntent != null) {
+ // Expecting a status report. Put this tracker to the map.
+ mSmsDispatchersController.putDeliveryPendingTracker(tracker);
+ }
+ tracker.onSent(mContext);
mTrackers.remove(token);
- }
- tracker.onSent(mContext);
- mPhone.notifySmsSent(tracker.mDestAddress);
- break;
- case ImsSmsImplBase.SEND_STATUS_ERROR:
- tracker.onFailed(mContext, reason, networkReasonCode);
- mTrackers.remove(token);
- break;
- case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
- tracker.mRetryCount += 1;
- sendSms(tracker);
- break;
- case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK:
- tracker.mRetryCount += 1;
- fallbackToPstn(token, tracker);
- break;
- default:
+ mPhone.notifySmsSent(tracker.mDestAddress);
+ break;
+ case ImsSmsImplBase.SEND_STATUS_ERROR:
+ tracker.onFailed(mContext, reason, networkReasonCode);
+ mTrackers.remove(token);
+ break;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
+ tracker.mRetryCount += 1;
+ sendSms(tracker);
+ break;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK:
+ tracker.mRetryCount += 1;
+ mTrackers.remove(token);
+ fallbackToPstn(tracker);
+ break;
+ default:
+ }
+ mPhone.getSmsStats().onOutgoingSms(
+ true /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK,
+ reason,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onSmsStatusReportReceived(int token, String format, byte[] pdu)
throws RemoteException {
- logd("Status report received.");
- android.telephony.SmsMessage message =
- android.telephony.SmsMessage.createFromPdu(pdu, format);
- if (message == null || message.mWrappedSmsMessage == null) {
- throw new RemoteException(
- "Status report received with a PDU that could not be parsed.");
- }
- int messageRef = message.mWrappedSmsMessage.mMessageRef;
- SmsTracker tracker = null;
- int key = 0;
- for (Map.Entry<Integer, SmsTracker> entry : mTrackers.entrySet()) {
- if (messageRef == ((SmsTracker) entry.getValue()).mMessageRef) {
- tracker = entry.getValue();
- key = entry.getKey();
- break;
- }
- }
-
- if (tracker == null) {
- throw new RemoteException("No tracker for messageRef " + messageRef);
- }
- Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
- tracker, format, pdu);
- logd("Status report handle result, success: " + result.first
- + " complete: " + result.second);
+ final long identity = Binder.clearCallingIdentity();
try {
- getImsManager().acknowledgeSmsReport(
- token,
- messageRef,
- result.first ? ImsSmsImplBase.STATUS_REPORT_STATUS_OK
- : ImsSmsImplBase.STATUS_REPORT_STATUS_ERROR);
- } catch (ImsException e) {
- loge("Failed to acknowledgeSmsReport(). Error: "
- + e.getMessage());
- }
- if (result.second) {
- mTrackers.remove(key);
+ logd("Status report received.");
+ android.telephony.SmsMessage message =
+ android.telephony.SmsMessage.createFromPdu(pdu, format);
+ if (message == null || message.mWrappedSmsMessage == null) {
+ throw new RemoteException(
+ "Status report received with a PDU that could not be parsed.");
+ }
+ mSmsDispatchersController.handleSmsStatusReport(format, pdu);
+ try {
+ getImsManager().acknowledgeSmsReport(
+ token,
+ message.mWrappedSmsMessage.mMessageRef,
+ ImsSmsImplBase.STATUS_REPORT_STATUS_OK);
+ } catch (ImsException e) {
+ loge("Failed to acknowledgeSmsReport(). Error: " + e.getMessage());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onSmsReceived(int token, String format, byte[] pdu) {
- logd("SMS received.");
- android.telephony.SmsMessage message =
- android.telephony.SmsMessage.createFromPdu(pdu, format);
- mSmsDispatchersController.injectSmsPdu(message, format, result -> {
- logd("SMS handled result: " + result);
- int mappedResult;
- switch (result) {
- case Intents.RESULT_SMS_HANDLED:
- mappedResult = ImsSmsImplBase.DELIVER_STATUS_OK;
- break;
- case Intents.RESULT_SMS_OUT_OF_MEMORY:
- mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_NO_MEMORY;
- break;
- case Intents.RESULT_SMS_UNSUPPORTED:
- mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED;
- break;
- default:
- mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC;
- break;
- }
- try {
- if (message != null && message.mWrappedSmsMessage != null) {
- getImsManager().acknowledgeSms(token,
- message.mWrappedSmsMessage.mMessageRef, mappedResult);
- } else {
- logw("SMS Received with a PDU that could not be parsed.");
- getImsManager().acknowledgeSms(token, 0, mappedResult);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ logd("SMS received.");
+ android.telephony.SmsMessage message =
+ android.telephony.SmsMessage.createFromPdu(pdu, format);
+ mSmsDispatchersController.injectSmsPdu(message, format, result -> {
+ logd("SMS handled result: " + result);
+ int mappedResult;
+ switch (result) {
+ case Intents.RESULT_SMS_HANDLED:
+ mappedResult = ImsSmsImplBase.DELIVER_STATUS_OK;
+ break;
+ case Intents.RESULT_SMS_OUT_OF_MEMORY:
+ mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_NO_MEMORY;
+ break;
+ case Intents.RESULT_SMS_UNSUPPORTED:
+ mappedResult =
+ ImsSmsImplBase.DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED;
+ break;
+ default:
+ mappedResult = ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC;
+ break;
}
- } catch (ImsException e) {
- loge("Failed to acknowledgeSms(). Error: " + e.getMessage());
- }
- }, true);
+ try {
+ if (message != null && message.mWrappedSmsMessage != null) {
+ getImsManager().acknowledgeSms(token,
+ message.mWrappedSmsMessage.mMessageRef, mappedResult);
+ } else {
+ logw("SMS Received with a PDU that could not be parsed.");
+ getImsManager().acknowledgeSms(token, 0, mappedResult);
+ }
+ } catch (ImsException e) {
+ loge("Failed to acknowledgeSms(). Error: " + e.getMessage());
+ }
+ }, true /* ignoreClass */, true /* isOverIms */);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
};
- public ImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
+ public ImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController,
+ FeatureConnectorFactory factory) {
super(phone, smsDispatchersController);
+ mConnectorFactory = factory;
- mImsManagerConnector = new FeatureConnector<>(mContext, mPhone.getPhoneId(),
+ mImsManagerConnector = mConnectorFactory.create(mContext, mPhone.getPhoneId(), TAG,
new FeatureConnector.Listener<ImsManager>() {
- @Override
- public ImsManager getFeatureManager() {
- return ImsManager.getInstance(mContext, phone.getPhoneId());
- }
-
- @Override
public void connectionReady(ImsManager manager) throws ImsException {
logd("ImsManager: connection ready.");
synchronized (mLock) {
+ mImsManager = manager;
setListeners();
mIsImsServiceUp = true;
}
}
@Override
- public void connectionUnavailable() {
- logd("ImsManager: connection unavailable.");
+ public void connectionUnavailable(int reason) {
+ logd("ImsManager: connection unavailable, reason=" + reason);
+ if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) {
+ loge("connectionUnavailable: unexpected, received server error");
+ removeCallbacks(mConnectRunnable);
+ postDelayed(mConnectRunnable, CONNECT_DELAY_MS);
+ }
synchronized (mLock) {
+ mImsManager = null;
mIsImsServiceUp = false;
}
}
- }, "ImsSmsDispatcher");
- mImsManagerConnector.connect();
+ }, this::post);
+ post(mConnectRunnable);
}
private void setListeners() throws ImsException {
- getImsManager().addRegistrationCallback(mRegistrationCallback);
- getImsManager().addCapabilitiesCallback(mCapabilityCallback);
+ getImsManager().addRegistrationCallback(mRegistrationCallback, this::post);
+ getImsManager().addCapabilitiesCallback(mCapabilityCallback, this::post);
getImsManager().setSmsListener(getSmsListener());
getImsManager().onSmsReady();
}
@@ -289,14 +318,14 @@ public class ImsSmsDispatcher extends SMSDispatcher {
public boolean isEmergencySmsSupport(String destAddr) {
PersistableBundle b;
boolean eSmsCarrierSupport = false;
- if (!PhoneNumberUtils.isLocalEmergencyNumber(mContext, mPhone.getSubId(), destAddr)) {
- loge("Emergency Sms is not supported for: " + Rlog.pii(TAG, destAddr));
+ if (!mTelephonyManager.isEmergencyNumber(destAddr)) {
+ logi(Rlog.pii(TAG, destAddr) + " is not emergency number");
return false;
}
final long identity = Binder.clearCallingIdentity();
try {
- CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+ CarrierConfigManager configManager = (CarrierConfigManager) mContext
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager == null) {
loge("configManager is null");
@@ -331,6 +360,9 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
protected String getFormat() {
+ // This is called in the constructor before ImsSmsDispatcher has a chance to initialize
+ // mLock. ImsManager will not be up anyway at this point, so report UNKNOWN.
+ if (mLock == null) return SmsConstants.FORMAT_UNKNOWN;
try {
return getImsManager().getSmsFormat();
} catch (ImsException e) {
@@ -405,23 +437,36 @@ public class ImsSmsDispatcher extends SMSDispatcher {
isRetry,
pdu);
mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format,
- ImsSmsImplBase.SEND_STATUS_OK);
+ ImsSmsImplBase.SEND_STATUS_OK, tracker.mMessageId);
} catch (ImsException e) {
loge("sendSms failed. Falling back to PSTN. Error: " + e.getMessage());
- fallbackToPstn(token, tracker);
+ mTrackers.remove(token);
+ fallbackToPstn(tracker);
mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format,
- ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK);
+ ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, tracker.mMessageId);
+ mPhone.getSmsStats().onOutgoingSms(
+ true /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(format),
+ true /* fallbackToCs */,
+ SmsManager.RESULT_SYSTEM_ERROR,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
}
}
- private ImsManager getImsManager() {
- return ImsManager.getInstance(mContext, mPhone.getPhoneId());
+ private ImsManager getImsManager() throws ImsException {
+ synchronized (mLock) {
+ if (mImsManager == null) {
+ throw new ImsException("ImsManager not up",
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+ return mImsManager;
+ }
}
@VisibleForTesting
- public void fallbackToPstn(int token, SmsTracker tracker) {
+ public void fallbackToPstn(SmsTracker tracker) {
mSmsDispatchersController.sendRetrySms(tracker);
- mTrackers.remove(token);
}
@Override
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 63c5c1c642..c937ee1090 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -16,6 +16,9 @@
package com.android.internal.telephony;
+import static android.os.PowerWhitelistManager.REASON_EVENT_MMS;
+import static android.os.PowerWhitelistManager.REASON_EVENT_SMS;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.provider.Telephony.Sms.Intents.RESULT_SMS_DATABASE_ERROR;
import static android.provider.Telephony.Sms.Intents.RESULT_SMS_DISPATCH_FAILURE;
import static android.provider.Telephony.Sms.Intents.RESULT_SMS_INVALID_URI;
@@ -24,6 +27,8 @@ import static android.provider.Telephony.Sms.Intents.RESULT_SMS_NULL_PDU;
import static android.service.carrier.CarrierMessagingService.RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE;
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
@@ -45,7 +50,7 @@ import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
-import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.PowerManager;
@@ -61,7 +66,6 @@ import android.service.carrier.CarrierMessagingService;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Pair;
@@ -80,31 +84,32 @@ import com.android.telephony.Rlog;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
/**
- * This class broadcasts incoming SMS messages to interested apps after storing them in
- * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
- * broadcast, its parts are removed from the raw table. If the device crashes after ACKing
- * but before the broadcast completes, the pending messages will be rebroadcast on the next boot.
+ * This class broadcasts incoming SMS messages to interested apps after storing them in the
+ * SmsProvider "raw" table and ACKing them to the SMSC. After each message has been broadcast, its
+ * parts are removed from the raw table. If the device crashes after ACKing but before the broadcast
+ * completes, the pending messages will be rebroadcast on the next boot.
*
- * <p>The state machine starts in {@link IdleState} state. When the {@link SMSDispatcher} receives a
- * new SMS from the radio, it calls {@link #dispatchNormalMessage},
- * which sends a message to the state machine, causing the wakelock to be acquired in
- * {@link #haltedProcessMessage}, which transitions to {@link DeliveringState} state, where the message
- * is saved to the raw table, then acknowledged via the {@link SMSDispatcher} which called us.
+ * <p>The state machine starts in {@link IdleState} state. When we receive a new SMS from the radio,
+ * the wakelock is acquired, then transition to {@link DeliveringState} state, where the message is
+ * saved to the raw table, then acknowledged to the modem which in turn acknowledges it to the SMSC.
*
- * <p>After saving the SMS, if the message is complete (either single-part or the final segment
- * of a multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to
+ * <p>After saving the SMS, if the message is complete (either single-part or the final segment of a
+ * multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to
* {@link WaitingState} state to wait for the broadcast to complete. When the local
* {@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE}
- * to the state machine, causing us to either broadcast the next pending message (if one has
- * arrived while waiting for the broadcast to complete), or to transition back to the halted state
- * after all messages are processed. Then the wakelock is released and we wait for the next SMS.
+ * to the state machine, causing us to either broadcast the next pending message (if one has arrived
+ * while waiting for the broadcast to complete), or to transition back to the halted state after all
+ * messages are processed. Then the wakelock is released and we wait for the next SMS.
*/
public abstract class InboundSmsHandler extends StateMachine {
protected static final boolean DBG = true;
@@ -164,7 +169,7 @@ public abstract class InboundSmsHandler extends StateMachine {
public static final int EVENT_BROADCAST_SMS = 2;
/** Message from resultReceiver notifying {@link WaitingState} of a completed broadcast. */
- private static final int EVENT_BROADCAST_COMPLETE = 3;
+ public static final int EVENT_BROADCAST_COMPLETE = 3;
/** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
private static final int EVENT_RETURN_TO_IDLE = 4;
@@ -181,30 +186,53 @@ public abstract class InboundSmsHandler extends StateMachine {
/** Update the sms tracker */
public static final int EVENT_UPDATE_TRACKER = 8;
+ /** BroadcastReceiver timed out waiting for an intent */
+ public static final int EVENT_RECEIVER_TIMEOUT = 9;
+
/** Wakelock release delay when returning to idle state. */
private static final int WAKELOCK_TIMEOUT = 3000;
+ /** Received SMS was not injected. */
+ public static final int SOURCE_NOT_INJECTED = 0;
+
+ /** Received SMS was received over IMS and injected. */
+ public static final int SOURCE_INJECTED_FROM_IMS = 1;
+
+ /** Received SMS was injected from source different than IMS. */
+ public static final int SOURCE_INJECTED_FROM_UNKNOWN = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SOURCE_"},
+ value = {
+ SOURCE_NOT_INJECTED,
+ SOURCE_INJECTED_FROM_IMS,
+ SOURCE_INJECTED_FROM_UNKNOWN
+ })
+ public @interface SmsSource {}
+
// The notitfication tag used when showing a notification. The combination of notification tag
// and notification id should be unique within the phone app.
- private static final String NOTIFICATION_TAG = "InboundSmsHandler";
- private static final int NOTIFICATION_ID_NEW_MESSAGE = 1;
+ @VisibleForTesting
+ public static final String NOTIFICATION_TAG = "InboundSmsHandler";
+ @VisibleForTesting
+ public static final int NOTIFICATION_ID_NEW_MESSAGE = 1;
/** URI for raw table of SMS provider. */
protected static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw");
protected static final Uri sRawUriPermanentDelete =
Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw/permanentDelete");
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final Context mContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ContentResolver mResolver;
/** Special handler for WAP push messages. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final WapPushOverSms mWapPush;
/** Wake lock to ensure device stays awake while dispatching the SMS intents. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final PowerManager.WakeLock mWakeLock;
/** DefaultState throws an exception or logs an error for unhandled message types. */
@@ -214,15 +242,15 @@ public abstract class InboundSmsHandler extends StateMachine {
private final StartupState mStartupState = new StartupState();
/** Idle state. Waiting for messages to process. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final IdleState mIdleState = new IdleState();
/** Delivering state. Saves the PDU in the raw table and acknowledges to SMSC. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final DeliveringState mDeliveringState = new DeliveringState();
/** Broadcasting state. Waits for current broadcast to complete before delivering next. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final WaitingState mWaitingState = new WaitingState();
/** Helper class to check whether storage is available for incoming messages. */
@@ -230,10 +258,10 @@ public abstract class InboundSmsHandler extends StateMachine {
private final boolean mSmsReceiveDisabled;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Phone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private UserManager mUserManager;
protected TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
@@ -256,9 +284,7 @@ public abstract class InboundSmsHandler extends StateMachine {
/** Timeout for releasing wakelock */
private int mWakeLockTimeout;
- /** Indicates if last SMS was injected. This is used to recognize SMS received over IMS from
- others in order to update metrics. */
- private boolean mLastSmsWasInjected = false;
+ private List<SmsFilter> mSmsFilters;
/**
* Create a new SMS broadcast helper.
@@ -289,6 +315,8 @@ public abstract class InboundSmsHandler extends StateMachine {
(PowerWhitelistManager) mContext.getSystemService(Context.POWER_WHITELIST_MANAGER);
mCellBroadcastServiceManager = new CellBroadcastServiceManager(context, phone);
+ mSmsFilters = createDefaultSmsFilters();
+
addState(mDefaultState);
addState(mStartupState, mDefaultState);
addState(mIdleState, mDefaultState);
@@ -320,7 +348,7 @@ public abstract class InboundSmsHandler extends StateMachine {
}
// CAF_MSIM Is this used anywhere ? if not remove it
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Phone getPhone() {
return mPhone;
}
@@ -353,6 +381,9 @@ public abstract class InboundSmsHandler extends StateMachine {
case EVENT_UPDATE_TRACKER:
whatString = "EVENT_UPDATE_TRACKER";
break;
+ case EVENT_RECEIVER_TIMEOUT:
+ whatString = "EVENT_RECEIVER_TIMEOUT";
+ break;
default:
whatString = "UNKNOWN EVENT " + what;
}
@@ -513,7 +544,7 @@ public abstract class InboundSmsHandler extends StateMachine {
case EVENT_INJECT_SMS:
// handle new injected SMS
- handleInjectSms((AsyncResult) msg.obj);
+ handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */);
sendMessage(EVENT_RETURN_TO_IDLE);
return HANDLED;
@@ -606,6 +637,15 @@ public abstract class InboundSmsHandler extends StateMachine {
deferMessage(msg);
return HANDLED;
+ case EVENT_RECEIVER_TIMEOUT:
+ logeWithLocalLog("WaitingState.processMessage: received "
+ + "EVENT_RECEIVER_TIMEOUT");
+ if (mLastDeliveredSmsTracker != null) {
+ mLastDeliveredSmsTracker.getSmsBroadcastReceiver(InboundSmsHandler.this)
+ .fakeNextAction();
+ }
+ return HANDLED;
+
case EVENT_BROADCAST_COMPLETE:
mLastDeliveredSmsTracker = null;
// return to idle after handling all deferred messages
@@ -628,7 +668,7 @@ public abstract class InboundSmsHandler extends StateMachine {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void handleNewSms(AsyncResult ar) {
if (ar.exception != null) {
loge("Exception processing incoming SMS: " + ar.exception);
@@ -638,8 +678,7 @@ public abstract class InboundSmsHandler extends StateMachine {
int result;
try {
SmsMessage sms = (SmsMessage) ar.result;
- mLastSmsWasInjected = false;
- result = dispatchMessage(sms.mWrappedSmsMessage);
+ result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED);
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
result = RESULT_SMS_DISPATCH_FAILURE;
@@ -657,8 +696,8 @@ public abstract class InboundSmsHandler extends StateMachine {
* This method is called when a new SMS PDU is injected into application framework.
* @param ar is the AsyncResult that has the SMS PDU to be injected.
*/
- @UnsupportedAppUsage
- private void handleInjectSms(AsyncResult ar) {
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ private void handleInjectSms(AsyncResult ar, boolean isOverIms) {
int result;
SmsDispatchersController.SmsInjectionCallback callback = null;
try {
@@ -668,8 +707,9 @@ public abstract class InboundSmsHandler extends StateMachine {
loge("Null injected sms");
result = RESULT_SMS_NULL_PDU;
} else {
- mLastSmsWasInjected = true;
- result = dispatchMessage(sms.mWrappedSmsMessage);
+ @SmsSource int smsSource =
+ isOverIms ? SOURCE_INJECTED_FROM_IMS : SOURCE_INJECTED_FROM_UNKNOWN;
+ result = dispatchMessage(sms.mWrappedSmsMessage, smsSource);
}
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
@@ -686,10 +726,11 @@ public abstract class InboundSmsHandler extends StateMachine {
* 3GPP2-specific message types.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return a result code from {@link android.provider.Telephony.Sms.Intents},
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
- private int dispatchMessage(SmsMessageBase smsb) {
+ private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource) {
// If sms is null, there was a parsing error.
if (smsb == null) {
loge("dispatchSmsMessage: message is null");
@@ -716,12 +757,13 @@ public abstract class InboundSmsHandler extends StateMachine {
return Intents.RESULT_SMS_RECEIVED_WHILE_ENCRYPTED;
}
- int result = dispatchMessageRadioSpecific(smsb);
+ int result = dispatchMessageRadioSpecific(smsb, smsSource);
// In case of error, add to metrics. This is not required in case of success, as the
// data will be tracked when the message is processed (processMessagePart).
- if (result != Intents.RESULT_SMS_HANDLED) {
- mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), mLastSmsWasInjected, result);
+ if (result != Intents.RESULT_SMS_HANDLED && result != Activity.RESULT_OK) {
+ mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), is3gpp2(), smsSource, result);
+ mPhone.getSmsStats().onIncomingSmsError(is3gpp2(), smsSource, result);
}
return result;
}
@@ -732,10 +774,12 @@ public abstract class InboundSmsHandler extends StateMachine {
* {@link #dispatchNormalMessage} from this class.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return a result code from {@link android.provider.Telephony.Sms.Intents},
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
- protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb);
+ protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb,
+ @SmsSource int smsSource);
/**
* Send an acknowledge message to the SMSC.
@@ -743,7 +787,7 @@ public abstract class InboundSmsHandler extends StateMachine {
* @param result result code indicating any error
* @param response callback message sent when operation completes.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected abstract void acknowledgeLastIncomingSms(boolean success,
int result, Message response);
@@ -779,10 +823,11 @@ public abstract class InboundSmsHandler extends StateMachine {
* {@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value.
*
* @param sms the message to dispatch
+ * @param smsSource the source of the SMS message
* @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status
*/
- @UnsupportedAppUsage
- protected int dispatchNormalMessage(SmsMessageBase sms) {
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ protected int dispatchNormalMessage(SmsMessageBase sms, @SmsSource int smsSource) {
SmsHeader smsHeader = sms.getUserDataHeader();
InboundSmsTracker tracker;
@@ -797,10 +842,10 @@ public abstract class InboundSmsHandler extends StateMachine {
tracker = TelephonyComponentFactory.getInstance()
.inject(InboundSmsTracker.class.getName())
.makeInboundSmsTracker(mContext, sms.getPdu(),
- sms.getTimestampMillis(), destPort, is3gpp2(), false,
- sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
- sms.getMessageBody(), sms.getMessageClass() == MessageClass.CLASS_0,
- mPhone.getSubId());
+ sms.getTimestampMillis(), destPort, is3gpp2(), false,
+ sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
+ sms.getMessageBody(), sms.getMessageClass() == MessageClass.CLASS_0,
+ mPhone.getSubId(), smsSource);
} else {
// Create a tracker for this message segment.
SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
@@ -809,10 +854,11 @@ public abstract class InboundSmsHandler extends StateMachine {
tracker = TelephonyComponentFactory.getInstance()
.inject(InboundSmsTracker.class.getName())
.makeInboundSmsTracker(mContext, sms.getPdu(),
- sms.getTimestampMillis(), destPort, is3gpp2(), sms.getOriginatingAddress(),
- sms.getDisplayOriginatingAddress(), concatRef.refNumber, concatRef.seqNumber,
- concatRef.msgCount, false, sms.getMessageBody(),
- sms.getMessageClass() == MessageClass.CLASS_0, mPhone.getSubId());
+ sms.getTimestampMillis(), destPort, is3gpp2(),
+ sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
+ concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount, false,
+ sms.getMessageBody(), sms.getMessageClass() == MessageClass.CLASS_0,
+ mPhone.getSubId(), smsSource);
}
if (VDBG) log("created tracker: " + tracker);
@@ -859,7 +905,7 @@ public abstract class InboundSmsHandler extends StateMachine {
* @param tracker the tracker containing the message segment to process
* @return true if an ordered broadcast was sent; false if waiting for more message segments
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean processMessagePart(InboundSmsTracker tracker) {
int messageCount = tracker.getMessageCount();
byte[][] pdus;
@@ -961,8 +1007,8 @@ public abstract class InboundSmsHandler extends StateMachine {
log("processMessagePart: all " + messageCount + " segments "
+ " received. refNumber: " + refNumber, tracker.getMessageId());
} catch (SQLException e) {
- loge("processMessagePart: Can't access multipart SMS database, id: "
- + tracker.getMessageId(), e);
+ loge("processMessagePart: Can't access multipart SMS database, "
+ + SmsController.formatCrossStackMessageId(tracker.getMessageId()), e);
return false;
} finally {
if (cursor != null) {
@@ -972,14 +1018,7 @@ public abstract class InboundSmsHandler extends StateMachine {
}
final boolean isWapPush = (destPort == SmsHeader.PORT_WAP_PUSH);
-
- // At this point, all parts of the SMS are received. Update metrics for incoming SMS.
- // WAP-PUSH messages are handled below to also keep track of the result of the processing.
String format = tracker.getFormat();
- if (!isWapPush) {
- mMetrics.writeIncomingSmsSession(mPhone.getPhoneId(), mLastSmsWasInjected,
- format, timestamps, block, tracker.getMessageId());
- }
// Do not process null pdu(s). Check for that and return false in that case.
List<byte[]> pduList = Arrays.asList(pdus);
@@ -987,6 +1026,8 @@ public abstract class InboundSmsHandler extends StateMachine {
String errorMsg = "processMessagePart: returning false due to "
+ (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)");
logeWithLocalLog(errorMsg, tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsError(
+ is3gpp2(), tracker.getSource(), RESULT_SMS_NULL_PDU);
return false;
}
@@ -1001,9 +1042,11 @@ public abstract class InboundSmsHandler extends StateMachine {
} else {
loge("processMessagePart: SmsMessage.createFromPdu returned null",
tracker.getMessageId());
- mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), mLastSmsWasInjected,
+ mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), tracker.getSource(),
SmsConstants.FORMAT_3GPP, timestamps, false,
tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(),
+ messageCount, RESULT_SMS_NULL_MESSAGE, tracker.getMessageId());
return false;
}
}
@@ -1011,7 +1054,7 @@ public abstract class InboundSmsHandler extends StateMachine {
}
}
- SmsBroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker);
+ SmsBroadcastReceiver resultReceiver = tracker.getSmsBroadcastReceiver(this);
if (!mUserManager.isUserUnlocked()) {
log("processMessagePart: !isUserUnlocked; calling processMessagePartWithUserLocked. "
@@ -1020,7 +1063,8 @@ public abstract class InboundSmsHandler extends StateMachine {
tracker,
(isWapPush ? new byte[][] {output.toByteArray()} : pdus),
destPort,
- resultReceiver);
+ resultReceiver,
+ block);
}
if (isWapPush) {
@@ -1032,13 +1076,12 @@ public abstract class InboundSmsHandler extends StateMachine {
}
// Add result of WAP-PUSH into metrics. RESULT_SMS_HANDLED indicates that the WAP-PUSH
// needs to be ignored, so treating it as a success case.
- if (result == Activity.RESULT_OK || result == Intents.RESULT_SMS_HANDLED) {
- mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), mLastSmsWasInjected,
- format, timestamps, true, tracker.getMessageId());
- } else {
- mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), mLastSmsWasInjected,
- format, timestamps, false, tracker.getMessageId());
- }
+ boolean wapPushResult =
+ result == Activity.RESULT_OK || result == Intents.RESULT_SMS_HANDLED;
+ mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), tracker.getSource(),
+ format, timestamps, wapPushResult, tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(), messageCount,
+ result, tracker.getMessageId());
// result is Activity.RESULT_OK if an ordered broadcast was sent
if (result == Activity.RESULT_OK) {
return true;
@@ -1051,18 +1094,32 @@ public abstract class InboundSmsHandler extends StateMachine {
}
}
- if (block) {
- deleteFromRawTable(tracker.getDeleteWhere(), tracker.getDeleteWhereArgs(),
- DELETE_PERMANENTLY);
- log("processMessagePart: returning false as the phone number is blocked",
- tracker.getMessageId());
- return false;
- }
+ // All parts of SMS are received. Update metrics for incoming SMS.
+ // The metrics are generated before SMS filters are invoked.
+ // For messages composed by multiple parts, the metrics are generated considering the
+ // characteristics of the last one.
+ mMetrics.writeIncomingSmsSession(mPhone.getPhoneId(), tracker.getSource(),
+ format, timestamps, block, tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsSuccess(is3gpp2(), tracker.getSource(),
+ messageCount, block, tracker.getMessageId());
+ // Always invoke SMS filters, even if the number ends up being blocked, to prevent
+ // surprising bugs due to blocking numbers that happen to be used for visual voicemail SMS
+ // or other carrier system messages.
boolean filterInvoked = filterSms(
- pdus, destPort, tracker, resultReceiver, true /* userUnlocked */);
+ pdus, destPort, tracker, resultReceiver, true /* userUnlocked */, block);
if (!filterInvoked) {
+ // Block now if the filter wasn't invoked. Otherwise, it will be the responsibility of
+ // the filter to delete the SMS once processing completes.
+ if (block) {
+ deleteFromRawTable(tracker.getDeleteWhere(), tracker.getDeleteWhereArgs(),
+ DELETE_PERMANENTLY);
+ log("processMessagePart: returning false as the phone number is blocked",
+ tracker.getMessageId());
+ return false;
+ }
+
dispatchSmsDeliveryIntent(pdus, format, destPort, resultReceiver,
tracker.isClass0(), tracker.getSubId(), tracker.getMessageId());
}
@@ -1080,7 +1137,7 @@ public abstract class InboundSmsHandler extends StateMachine {
* @return true if an ordered broadcast was sent to the carrier app; false otherwise.
*/
private boolean processMessagePartWithUserLocked(InboundSmsTracker tracker,
- byte[][] pdus, int destPort, SmsBroadcastReceiver resultReceiver) {
+ byte[][] pdus, int destPort, SmsBroadcastReceiver resultReceiver, boolean block) {
if (destPort == SmsHeader.PORT_WAP_PUSH && mWapPush.isWapPushForMms(pdus[0], this)) {
showNewMessageNotification();
return false;
@@ -1088,20 +1145,22 @@ public abstract class InboundSmsHandler extends StateMachine {
if (destPort == -1) {
// This is a regular SMS - hand it to the carrier or system app for filtering.
boolean filterInvoked = filterSms(
- pdus, destPort, tracker, resultReceiver, false /* userUnlocked */);
+ pdus, destPort, tracker, resultReceiver, false /* userUnlocked */,
+ block);
if (filterInvoked) {
// filter invoked, wait for it to return the result.
return true;
- } else {
- // filter not invoked, show the notification and do nothing further.
+ } else if (!block) {
+ // filter not invoked and message not blocked, show the notification and do nothing
+ // further. Even if the message is blocked, we keep it in the database so it can be
+ // reprocessed by filters once credential-encrypted storage is available.
showNewMessageNotification();
- return false;
}
}
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void showNewMessageNotification() {
// Do not show the notification on non-FBE devices.
if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
@@ -1136,53 +1195,100 @@ public abstract class InboundSmsHandler extends StateMachine {
}
/**
- * Filters the SMS.
- *
- * <p>currently 3 filters exists: the carrier package, the system package, and the
- * VisualVoicemailSmsFilter.
+ * Creates the default filters used to filter SMS messages.
*
- * <p>The filtering process is:
+ * <p>Currently 3 filters exist: the carrier package, the VisualVoicemailSmsFilter, and the
+ * missed incoming call SMS filter.
*
- * <p>If the carrier package exists, the SMS will be filtered with it first. If the carrier
- * package did not drop the SMS, then the VisualVoicemailSmsFilter will filter it in the
- * callback.
+ * <p>Since the carrier filter is asynchronous, if a message passes through the carrier filter,
+ * the remaining filters will be applied in the callback.
+ */
+ private List<SmsFilter> createDefaultSmsFilters() {
+ List<SmsFilter> smsFilters = new ArrayList<>(3);
+ smsFilters.add(
+ (pdus, destPort, tracker, resultReceiver, userUnlocked, block, remainingFilters)
+ -> {
+ CarrierServicesSmsFilterCallback filterCallback =
+ new CarrierServicesSmsFilterCallback(
+ pdus, destPort, tracker, tracker.getFormat(), resultReceiver,
+ userUnlocked,
+ tracker.isClass0(), tracker.getSubId(), tracker.getMessageId(),
+ block, remainingFilters);
+ CarrierServicesSmsFilter carrierServicesFilter = new CarrierServicesSmsFilter(
+ mContext, mPhone, pdus, destPort, tracker.getFormat(),
+ filterCallback, getName() + "::CarrierServicesSmsFilter",
+ mCarrierServiceLocalLog, tracker.getMessageId());
+ if (carrierServicesFilter.filter()) {
+ log("SMS is being handled by carrier service", tracker.getMessageId());
+ return true;
+ } else {
+ return false;
+ }
+ });
+ smsFilters.add(
+ (pdus, destPort, tracker, resultReceiver, userUnlocked, block, remainingFilters)
+ -> {
+ if (VisualVoicemailSmsFilter.filter(
+ mContext, pdus, tracker.getFormat(), destPort, tracker.getSubId())) {
+ logWithLocalLog("Visual voicemail SMS dropped", tracker.getMessageId());
+ dropFilteredSms(tracker, resultReceiver, block);
+ return true;
+ }
+ return false;
+ });
+ smsFilters.add(
+ (pdus, destPort, tracker, resultReceiver, userUnlocked, block, remainingFilters)
+ -> {
+ MissedIncomingCallSmsFilter missedIncomingCallSmsFilter =
+ new MissedIncomingCallSmsFilter(mPhone);
+ if (missedIncomingCallSmsFilter.filter(pdus, tracker.getFormat())) {
+ logWithLocalLog("Missed incoming call SMS received",
+ tracker.getMessageId());
+ dropFilteredSms(tracker, resultReceiver, block);
+ return true;
+ }
+ return false;
+ });
+ return smsFilters;
+ }
+
+ private void dropFilteredSms(
+ InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean block) {
+ if (block) {
+ deleteFromRawTable(
+ tracker.getDeleteWhere(), tracker.getDeleteWhereArgs(),
+ DELETE_PERMANENTLY);
+ sendMessage(EVENT_BROADCAST_COMPLETE);
+ } else {
+ dropSms(resultReceiver);
+ }
+ }
+
+ /**
+ * Filters the SMS.
*
- * <p>If the carrier package does not exists, we will let the VisualVoicemailSmsFilter filter
- * it. If the SMS passed the filter, then we will try to find the system package to do the
- * filtering.
+ * <p>Each filter in {@link #mSmsFilters} is invoked sequentially. If any filter returns true,
+ * this method returns true and subsequent filters are ignored.
*
* @return true if a filter is invoked and the SMS processing flow is diverted, false otherwise.
*/
private boolean filterSms(byte[][] pdus, int destPort,
- InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked) {
- CarrierServicesSmsFilterCallback filterCallback =
- new CarrierServicesSmsFilterCallback(
- pdus, destPort, tracker.getFormat(), resultReceiver, userUnlocked,
- tracker.isClass0(), tracker.getSubId(), tracker.getMessageId());
- CarrierServicesSmsFilter carrierServicesFilter = new CarrierServicesSmsFilter(
- mContext, mPhone, pdus, destPort, tracker.getFormat(),
- filterCallback, getName() + "::CarrierServicesSmsFilter", mCarrierServiceLocalLog,
- tracker.getMessageId());
- if (carrierServicesFilter.filter()) {
- log("filterSms: SMS is being handled by carrier service", tracker.getMessageId());
- return true;
- }
-
- if (VisualVoicemailSmsFilter.filter(
- mContext, pdus, tracker.getFormat(), destPort, tracker.getSubId())) {
- logWithLocalLog("filterSms: Visual voicemail SMS dropped", tracker.getMessageId());
- dropSms(resultReceiver);
- return true;
- }
+ InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked,
+ boolean block) {
+ return filterSms(pdus, destPort, tracker, resultReceiver, userUnlocked, block, mSmsFilters);
+ }
- MissedIncomingCallSmsFilter missedIncomingCallSmsFilter =
- new MissedIncomingCallSmsFilter(mPhone);
- if (missedIncomingCallSmsFilter.filter(pdus, tracker.getFormat())) {
- logWithLocalLog("filterSms: Missed incoming call SMS received", tracker.getMessageId());
- dropSms(resultReceiver);
- return true;
+ private static boolean filterSms(byte[][] pdus, int destPort,
+ InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked,
+ boolean block, List<SmsFilter> filters) {
+ ListIterator<SmsFilter> iterator = filters.listIterator();
+ while (iterator.hasNext()) {
+ SmsFilter smsFilter = iterator.next();
+ if (smsFilter.filterSms(pdus, destPort, tracker, resultReceiver, userUnlocked, block,
+ filters.subList(iterator.nextIndex(), filters.size()))) {
+ return true;
+ }
}
-
return false;
}
@@ -1195,11 +1301,23 @@ public abstract class InboundSmsHandler extends StateMachine {
* @param appOp app op that is being performed when dispatching to a receiver
* @param user user to deliver the intent to
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void dispatchIntent(Intent intent, String permission, String appOp,
- Bundle opts, BroadcastReceiver resultReceiver, UserHandle user, int subId) {
+ Bundle opts, SmsBroadcastReceiver resultReceiver, UserHandle user, int subId) {
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
final String action = intent.getAction();
+ if (Intents.SMS_DELIVER_ACTION.equals(action)
+ || Intents.SMS_RECEIVED_ACTION.equals(action)
+ || Intents.WAP_PUSH_DELIVER_ACTION.equals(action)
+ || Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
+ // Some intents need to be delivered with high priority:
+ // SMS_DELIVER, SMS_RECEIVED, WAP_PUSH_DELIVER, WAP_PUSH_RECEIVED
+ // In some situations, like after boot up or system under load, normal
+ // intent delivery could take a long time.
+ // This flag should only be set for intents for visible, timely operations
+ // which is true for the intents above.
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ }
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
// override the subId value in the intent with the values from tracker as they can be
@@ -1216,6 +1334,11 @@ public abstract class InboundSmsHandler extends StateMachine {
for (UserHandle handle : userHandles) {
if (mUserManager.isUserRunning(handle)) {
runningUserHandles.add(handle);
+ } else {
+ if (handle.equals(UserHandle.SYSTEM)) {
+ logeWithLocalLog("dispatchIntent: SYSTEM user is not running",
+ resultReceiver.mInboundSmsTracker.getMessageId());
+ }
}
}
if (runningUserHandles.isEmpty()) {
@@ -1242,6 +1365,9 @@ public abstract class InboundSmsHandler extends StateMachine {
}
// Only pass in the resultReceiver when the user SYSTEM is processed.
try {
+ if (users[i] == UserHandle.SYSTEM.getIdentifier()) {
+ resultReceiver.setWaitingForIntent(intent);
+ }
mContext.createPackageContextAsUser(mContext.getPackageName(), 0, targetUser)
.sendOrderedBroadcast(intent, Activity.RESULT_OK, permission, appOp,
users[i] == UserHandle.SYSTEM.getIdentifier()
@@ -1252,6 +1378,7 @@ public abstract class InboundSmsHandler extends StateMachine {
}
} else {
try {
+ resultReceiver.setWaitingForIntent(intent);
mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user)
.sendOrderedBroadcast(intent, Activity.RESULT_OK, permission, appOp,
resultReceiver, getHandler(), null /* initialData */,
@@ -1270,7 +1397,7 @@ public abstract class InboundSmsHandler extends StateMachine {
/**
* Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void deleteFromRawTable(String deleteWhere, String[] deleteWhereArgs,
int deleteType) {
Uri uri = deleteType == DELETE_PERMANENTLY ? sRawUriPermanentDelete : sRawUri;
@@ -1282,7 +1409,7 @@ public abstract class InboundSmsHandler extends StateMachine {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Bundle handleSmsWhitelisting(ComponentName target, boolean bgActivityStartAllowed) {
String pkgName;
String reason;
@@ -1301,9 +1428,12 @@ public abstract class InboundSmsHandler extends StateMachine {
bundle = bopts.toBundle();
}
long duration = mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
- pkgName, PowerWhitelistManager.EVENT_SMS, reason);
+ pkgName, PowerWhitelistManager.EVENT_SMS, REASON_EVENT_SMS, reason);
if (bopts == null) bopts = BroadcastOptions.makeBasic();
- bopts.setTemporaryAppWhitelistDuration(duration);
+ bopts.setTemporaryAppAllowlist(duration,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_EVENT_SMS,
+ "");
bundle = bopts.toBundle();
return bundle;
@@ -1390,7 +1520,7 @@ public abstract class InboundSmsHandler extends StateMachine {
if (cursor.getInt(
PDU_DELETED_FLAG_PROJECTION_INDEX_MAPPING.get(DELETED_FLAG_COLUMN)) == 1) {
logWithLocalLog("checkAndHandleDuplicate: Discarding duplicate "
- + "message/segment: " + tracker, tracker.getMessageId());
+ + "message/segment: " + tracker);
logDupPduMismatch(cursor, tracker);
return true; // reject message
} else {
@@ -1402,7 +1532,7 @@ public abstract class InboundSmsHandler extends StateMachine {
deleteFromRawTable(exactMatchQuery.first, exactMatchQuery.second,
DELETE_PERMANENTLY);
logWithLocalLog("checkAndHandleDuplicate: Replacing duplicate message: "
- + tracker, tracker.getMessageId());
+ + tracker);
logDupPduMismatch(cursor, tracker);
}
}
@@ -1476,8 +1606,8 @@ public abstract class InboundSmsHandler extends StateMachine {
return Intents.RESULT_SMS_DUPLICATED; // reject message
}
} catch (SQLException e) {
- loge("addTrackerToRawTable: Can't access SMS database, id: "
- + tracker.getMessageId(), e);
+ loge("addTrackerToRawTable: Can't access SMS database, "
+ + SmsController.formatCrossStackMessageId(tracker.getMessageId()), e);
return RESULT_SMS_DATABASE_ERROR; // reject message
}
} else {
@@ -1508,8 +1638,8 @@ public abstract class InboundSmsHandler extends StateMachine {
}
return Intents.RESULT_SMS_HANDLED;
} catch (Exception e) {
- loge("addTrackerToRawTable: error parsing URI for new row: " + newUri + " id: "
- + tracker.getMessageId(), e);
+ loge("addTrackerToRawTable: error parsing URI for new row: " + newUri
+ + " " + SmsController.formatCrossStackMessageId(tracker.getMessageId()), e);
return RESULT_SMS_INVALID_URI;
}
}
@@ -1523,26 +1653,80 @@ public abstract class InboundSmsHandler extends StateMachine {
return (PHONE_TYPE_CDMA == activePhone);
}
+ @VisibleForTesting
+ public static int sTimeoutDurationMillis = 10 * 60 * 1000; // 10 minutes
+
/**
* Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and
* logs the broadcast duration (as an error if the other receivers were especially slow).
*/
- private final class SmsBroadcastReceiver extends BroadcastReceiver {
- @UnsupportedAppUsage
+ public final class SmsBroadcastReceiver extends BroadcastReceiver {
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final String mDeleteWhere;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final String[] mDeleteWhereArgs;
- private long mBroadcastTimeNano;
-
- SmsBroadcastReceiver(InboundSmsTracker tracker) {
+ private long mBroadcastTimeMillis;
+ public Intent mWaitingForIntent;
+ private final InboundSmsTracker mInboundSmsTracker;
+
+ /**
+ * This method must be called anytime an ordered broadcast is sent that is expected to be
+ * received by this receiver.
+ */
+ public synchronized void setWaitingForIntent(Intent intent) {
+ mWaitingForIntent = intent;
+ mBroadcastTimeMillis = System.currentTimeMillis();
+ removeMessages(EVENT_RECEIVER_TIMEOUT);
+ sendMessageDelayed(EVENT_RECEIVER_TIMEOUT, sTimeoutDurationMillis);
+ }
+
+ public SmsBroadcastReceiver(InboundSmsTracker tracker) {
mDeleteWhere = tracker.getDeleteWhere();
mDeleteWhereArgs = tracker.getDeleteWhereArgs();
- mBroadcastTimeNano = System.nanoTime();
+ mInboundSmsTracker = tracker;
+ }
+
+ /**
+ * This method is called if the expected intent (mWaitingForIntent) is not received and
+ * the timer for it expires. It fakes the receipt of the intent to unblock the state
+ * machine.
+ */
+ public void fakeNextAction() {
+ if (mWaitingForIntent != null) {
+ logeWithLocalLog("fakeNextAction: " + mWaitingForIntent.getAction(),
+ mInboundSmsTracker.getMessageId());
+ handleAction(mWaitingForIntent, false);
+ } else {
+ logeWithLocalLog("fakeNextAction: mWaitingForIntent is null",
+ mInboundSmsTracker.getMessageId());
+ }
}
@Override
public void onReceive(Context context, Intent intent) {
+ handleAction(intent, true);
+ }
+
+ private synchronized void handleAction(Intent intent, boolean onReceive) {
String action = intent.getAction();
+ if (mWaitingForIntent == null || !mWaitingForIntent.getAction().equals(action)) {
+ logeWithLocalLog("handleAction: Received " + action + " when expecting "
+ + mWaitingForIntent == null ? "none" : mWaitingForIntent.getAction(),
+ mInboundSmsTracker.getMessageId());
+ return;
+ }
+
+ if (onReceive) {
+ int durationMillis = (int) (System.currentTimeMillis() - mBroadcastTimeMillis);
+ if (durationMillis >= 5000) {
+ loge("Slow ordered broadcast completion time for " + action + ": "
+ + durationMillis + " ms");
+ } else if (DBG) {
+ log("Ordered broadcast completed for " + action + " in: "
+ + durationMillis + " ms");
+ }
+ }
+
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (action.equals(Intents.SMS_DELIVER_ACTION)) {
@@ -1554,6 +1738,7 @@ public abstract class InboundSmsHandler extends StateMachine {
// All running users will be notified of the received sms.
Bundle options = handleSmsWhitelisting(null, false /* bgActivityStartAllowed */);
+ setWaitingForIntent(intent);
dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
AppOpsManager.OPSTR_RECEIVE_SMS,
options, this, UserHandle.ALL, subId);
@@ -1566,12 +1751,18 @@ public abstract class InboundSmsHandler extends StateMachine {
long duration = mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
mContext.getPackageName(),
PowerWhitelistManager.EVENT_MMS,
+ REASON_EVENT_MMS,
"mms-broadcast");
BroadcastOptions bopts = BroadcastOptions.makeBasic();
- bopts.setTemporaryAppWhitelistDuration(duration);
+ bopts.setTemporaryAppAllowlist(duration,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_EVENT_MMS,
+ "");
Bundle options = bopts.toBundle();
String mimeType = intent.getType();
+
+ setWaitingForIntent(intent);
dispatchIntent(intent, WapPushOverSms.getPermissionForType(mimeType),
WapPushOverSms.getAppOpsStringPermissionForIntent(mimeType), options, this,
UserHandle.SYSTEM, subId);
@@ -1579,28 +1770,24 @@ public abstract class InboundSmsHandler extends StateMachine {
// Now that the intents have been deleted we can clean up the PDU data.
if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
&& !Intents.SMS_RECEIVED_ACTION.equals(action)
- && !Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
&& !Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) {
loge("unexpected BroadcastReceiver action: " + action);
}
- int rc = getResultCode();
- if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) {
- loge("a broadcast receiver set the result code to " + rc
- + ", deleting from raw table anyway!");
- } else if (DBG) {
- log("successful broadcast, deleting from raw table.");
+ if (onReceive) {
+ int rc = getResultCode();
+ if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) {
+ loge("a broadcast receiver set the result code to " + rc
+ + ", deleting from raw table anyway!");
+ } else if (DBG) {
+ log("successful broadcast, deleting from raw table.");
+ }
}
deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs, MARK_DELETED);
+ mWaitingForIntent = null;
+ removeMessages(EVENT_RECEIVER_TIMEOUT);
sendMessage(EVENT_BROADCAST_COMPLETE);
-
- int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000);
- if (durationMillis >= 5000) {
- loge("Slow ordered broadcast completion time: " + durationMillis + " ms");
- } else if (DBG) {
- log("ordered broadcast completed in: " + durationMillis + " ms");
- }
}
}
}
@@ -1612,53 +1799,82 @@ public abstract class InboundSmsHandler extends StateMachine {
CarrierServicesSmsFilter.CarrierServicesSmsFilterCallbackInterface {
private final byte[][] mPdus;
private final int mDestPort;
+ private final InboundSmsTracker mTracker;
private final String mSmsFormat;
private final SmsBroadcastReceiver mSmsBroadcastReceiver;
private final boolean mUserUnlocked;
private final boolean mIsClass0;
private final int mSubId;
private final long mMessageId;
+ private final boolean mBlock;
+ private final List<SmsFilter> mRemainingFilters;
- CarrierServicesSmsFilterCallback(byte[][] pdus, int destPort, String smsFormat,
- SmsBroadcastReceiver smsBroadcastReceiver, boolean userUnlocked,
- boolean isClass0, int subId, long messageId) {
+ CarrierServicesSmsFilterCallback(byte[][] pdus, int destPort, InboundSmsTracker tracker,
+ String smsFormat, SmsBroadcastReceiver smsBroadcastReceiver, boolean userUnlocked,
+ boolean isClass0, int subId, long messageId, boolean block,
+ List<SmsFilter> remainingFilters) {
mPdus = pdus;
mDestPort = destPort;
+ mTracker = tracker;
mSmsFormat = smsFormat;
mSmsBroadcastReceiver = smsBroadcastReceiver;
mUserUnlocked = userUnlocked;
mIsClass0 = isClass0;
mSubId = subId;
mMessageId = messageId;
+ mBlock = block;
+ mRemainingFilters = remainingFilters;
}
@Override
public void onFilterComplete(int result) {
- log("onFilterComplete: result is " + result, mMessageId);
- if ((result & CarrierMessagingService.RECEIVE_OPTIONS_DROP) == 0) {
- if (VisualVoicemailSmsFilter.filter(mContext, mPdus,
- mSmsFormat, mDestPort, mSubId)) {
- logWithLocalLog("Visual voicemail SMS dropped", mMessageId);
- dropSms(mSmsBroadcastReceiver);
- return;
- }
+ log("onFilterComplete: result is " + result, mTracker.getMessageId());
+
+ boolean carrierRequestedDrop =
+ (result & CarrierMessagingService.RECEIVE_OPTIONS_DROP) != 0;
+ if (carrierRequestedDrop) {
+ // Carrier app asked the platform to drop the SMS. Drop it from the database and
+ // complete processing.
+ dropFilteredSms(mTracker, mSmsBroadcastReceiver, mBlock);
+ return;
+ }
+
+ boolean filterInvoked = filterSms(mPdus, mDestPort, mTracker, mSmsBroadcastReceiver,
+ mUserUnlocked, mBlock, mRemainingFilters);
+ if (filterInvoked) {
+ // A remaining filter has assumed responsibility for further message processing.
+ return;
+ }
+ // Now that all filters have been invoked, drop the message if it is blocked.
+ if (mBlock) {
+ // Only delete the message if the user is unlocked. Otherwise, we should reprocess
+ // the message after unlock so the filter has a chance to run while credential-
+ // encrypted storage is available.
if (mUserUnlocked) {
- dispatchSmsDeliveryIntent(
- mPdus, mSmsFormat, mDestPort, mSmsBroadcastReceiver, mIsClass0, mSubId,
- mMessageId);
+ log("onFilterComplete: dropping message as the sender is blocked",
+ mTracker.getMessageId());
+ dropFilteredSms(mTracker, mSmsBroadcastReceiver, mBlock);
} else {
- // Don't do anything further, leave the message in the raw table if the
- // credential-encrypted storage is still locked and show the new message
- // notification if the message is visible to the user.
- if (!isSkipNotifyFlagSet(result)) {
- showNewMessageNotification();
- }
+ // Just complete handling of the message without dropping it.
sendMessage(EVENT_BROADCAST_COMPLETE);
}
+ return;
+ }
+
+ // Message matched no filters and is not blocked, so complete processing.
+ if (mUserUnlocked) {
+ dispatchSmsDeliveryIntent(
+ mPdus, mSmsFormat, mDestPort, mSmsBroadcastReceiver, mIsClass0, mSubId,
+ mMessageId);
} else {
- // Drop this SMS.
- dropSms(mSmsBroadcastReceiver);
+ // Don't do anything further, leave the message in the raw table if the
+ // credential-encrypted storage is still locked and show the new message
+ // notification if the message is visible to the user.
+ if (!isSkipNotifyFlagSet(result)) {
+ showNewMessageNotification();
+ }
+ sendMessage(EVENT_BROADCAST_COMPLETE);
}
}
}
@@ -1672,7 +1888,7 @@ public abstract class InboundSmsHandler extends StateMachine {
/** Checks whether the flag to skip new message notification is set in the bitmask returned
* from the carrier app.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isSkipNotifyFlagSet(int callbackResult) {
return (callbackResult
& RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE) > 0;
@@ -1694,7 +1910,7 @@ public abstract class InboundSmsHandler extends StateMachine {
*/
protected void logWithLocalLog(String logMsg, long id) {
log(logMsg, id);
- mLocalLog.log(logMsg + ", id: " + id);
+ mLocalLog.log(logMsg + ", " + SmsController.formatCrossStackMessageId(id));
}
/**
@@ -1713,14 +1929,14 @@ public abstract class InboundSmsHandler extends StateMachine {
*/
protected void logeWithLocalLog(String logMsg, long id) {
loge(logMsg, id);
- mLocalLog.log(logMsg + ", id: " + id);
+ mLocalLog.log(logMsg + ", " + SmsController.formatCrossStackMessageId(id));
}
/**
* Log with debug level.
* @param s the string to log
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void log(String s) {
Rlog.d(getName(), s);
@@ -1732,14 +1948,14 @@ public abstract class InboundSmsHandler extends StateMachine {
* @param id unique message id
*/
protected void log(String s, long id) {
- log(s + ", id: " + id);
+ log(s + ", " + SmsController.formatCrossStackMessageId(id));
}
/**
* Log with error level.
* @param s the string to log
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void loge(String s) {
Rlog.e(getName(), s);
@@ -1751,7 +1967,7 @@ public abstract class InboundSmsHandler extends StateMachine {
* @param id unique message id
*/
protected void loge(String s, long id) {
- loge(s + ", id: " + id);
+ loge(s + ", " + SmsController.formatCrossStackMessageId(id));
}
/**
@@ -1765,63 +1981,6 @@ public abstract class InboundSmsHandler extends StateMachine {
}
/**
- * Store a received SMS into Telephony provider
- *
- * @param intent The intent containing the received SMS
- * @return The URI of written message
- */
- @UnsupportedAppUsage
- private Uri writeInboxMessage(Intent intent) {
- final SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent);
- if (messages == null || messages.length < 1) {
- loge("Failed to parse SMS pdu");
- return null;
- }
- // Sometimes, SmsMessage is null if it can’t be parsed correctly.
- for (final SmsMessage sms : messages) {
- if (sms == null) {
- loge("Can’t write null SmsMessage");
- return null;
- }
- }
- final ContentValues values = parseSmsMessage(messages);
- final long identity = Binder.clearCallingIdentity();
- try {
- return mContext.getContentResolver().insert(Telephony.Sms.Inbox.CONTENT_URI, values);
- } catch (Exception e) {
- loge("Failed to persist inbox message", e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return null;
- }
-
- /**
- * Convert SmsMessage[] into SMS database schema columns
- *
- * @param msgs The SmsMessage array of the received SMS
- * @return ContentValues representing the columns of parsed SMS
- */
- private static ContentValues parseSmsMessage(SmsMessage[] msgs) {
- final SmsMessage sms = msgs[0];
- final ContentValues values = new ContentValues();
- values.put(Telephony.Sms.Inbox.ADDRESS, sms.getDisplayOriginatingAddress());
- values.put(Telephony.Sms.Inbox.BODY, buildMessageBodyFromPdus(msgs));
- values.put(Telephony.Sms.Inbox.DATE_SENT, sms.getTimestampMillis());
- values.put(Telephony.Sms.Inbox.DATE, System.currentTimeMillis());
- values.put(Telephony.Sms.Inbox.PROTOCOL, sms.getProtocolIdentifier());
- values.put(Telephony.Sms.Inbox.SEEN, 0);
- values.put(Telephony.Sms.Inbox.READ, 0);
- final String subject = sms.getPseudoSubject();
- if (!TextUtils.isEmpty(subject)) {
- values.put(Telephony.Sms.Inbox.SUBJECT, subject);
- }
- values.put(Telephony.Sms.Inbox.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0);
- values.put(Telephony.Sms.Inbox.SERVICE_CENTER, sms.getServiceCenterAddress());
- return values;
- }
-
- /**
* Build up the SMS message body from the SmsMessage array of received SMS
*
* @param msgs The SmsMessage array of the received SMS
@@ -1885,6 +2044,20 @@ public abstract class InboundSmsHandler extends StateMachine {
}
/**
+ * Set the SMS filters used by {@link #filterSms} for testing purposes.
+ *
+ * @param smsFilters List of SMS filters, or null to restore the default filters.
+ */
+ @VisibleForTesting
+ public void setSmsFiltersForTesting(@Nullable List<SmsFilter> smsFilters) {
+ if (smsFilters == null) {
+ mSmsFilters = createDefaultSmsFilters();
+ } else {
+ mSmsFilters = smsFilters;
+ }
+ }
+
+ /**
* Handler for the broadcast sent when the new message notification is clicked. It launches the
* default SMS app.
*/
@@ -1963,4 +2136,27 @@ public abstract class InboundSmsHandler extends StateMachine {
}
}
}
+
+ /** A filter for incoming messages allowing the normal processing flow to be skipped. */
+ @VisibleForTesting
+ public interface SmsFilter {
+ /**
+ * Returns true if a filter is invoked and the SMS processing flow should be diverted, false
+ * otherwise.
+ *
+ * <p>If the filter can immediately determine that the message matches, it must call
+ * {@link #dropFilteredSms} to drop the message from the database once it has been
+ * processed.
+ *
+ * <p>If the filter must perform some asynchronous work to determine if the message matches,
+ * it should return true to defer processing. Once it has made a determination, if it finds
+ * the message matches, it must call {@link #dropFilteredSms}. If the message does not
+ * match, it must be passed through {@code remainingFilters} and either dropped if the
+ * remaining filters all return false or if {@code block} is true, or else it must be
+ * broadcast.
+ */
+ boolean filterSms(byte[][] pdus, int destPort, InboundSmsTracker tracker,
+ SmsBroadcastReceiver resultReceiver, boolean userUnlocked, boolean block,
+ List<SmsFilter> remainingFilters);
+ }
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsTracker.java b/src/java/com/android/internal/telephony/InboundSmsTracker.java
index 8a1f6bdacd..7a73c16974 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.os.Build;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -55,6 +56,7 @@ public class InboundSmsTracker {
private final boolean mIsClass0;
private final int mSubId;
private final long mMessageId;
+ private final @InboundSmsHandler.SmsSource int mSmsSource;
// Fields for concatenating multi-part SMS messages
private final String mAddress;
@@ -66,6 +68,8 @@ public class InboundSmsTracker {
private String mDeleteWhere;
private String[] mDeleteWhereArgs;
+ // BroadcastReceiver associated with this tracker
+ private InboundSmsHandler.SmsBroadcastReceiver mSmsBroadcastReceiver;
/**
* Copied from SmsMessageBase#getDisplayOriginatingAddress used for blocking messages.
* DisplayAddress could be email address if this message was from an email gateway, otherwise
@@ -116,10 +120,12 @@ public class InboundSmsTracker {
* @param address originating address
* @param displayAddress email address if this message was from an email gateway, otherwise same
* as originating address
+ * @param smsSource the source of the SMS message
*/
public InboundSmsTracker(Context context, byte[] pdu, long timestamp, int destPort,
boolean is3gpp2, boolean is3gpp2WapPdu, String address, String displayAddress,
- String messageBody, boolean isClass0, int subId) {
+ String messageBody, boolean isClass0, int subId,
+ @InboundSmsHandler.SmsSource int smsSource) {
mPdu = pdu;
mTimestamp = timestamp;
mDestPort = destPort;
@@ -135,6 +141,7 @@ public class InboundSmsTracker {
mMessageCount = 1;
mSubId = subId;
mMessageId = createMessageId(context, timestamp, subId);
+ mSmsSource = smsSource;
}
/**
@@ -155,11 +162,12 @@ public class InboundSmsTracker {
* @param sequenceNumber the sequence number of this segment (0-based)
* @param messageCount the total number of segments
* @param is3gpp2WapPdu true for 3GPP2 format WAP PDU; false otherwise
+ * @param smsSource the source of the SMS message
*/
public InboundSmsTracker(Context context, byte[] pdu, long timestamp, int destPort,
boolean is3gpp2, String address, String displayAddress, int referenceNumber,
int sequenceNumber, int messageCount, boolean is3gpp2WapPdu, String messageBody,
- boolean isClass0, int subId) {
+ boolean isClass0, int subId, @InboundSmsHandler.SmsSource int smsSource) {
mPdu = pdu;
mTimestamp = timestamp;
mDestPort = destPort;
@@ -176,6 +184,7 @@ public class InboundSmsTracker {
mMessageCount = messageCount;
mSubId = subId;
mMessageId = createMessageId(context, timestamp, subId);
+ mSmsSource = smsSource;
}
/**
@@ -240,6 +249,8 @@ public class InboundSmsTracker {
}
mMessageBody = cursor.getString(InboundSmsHandler.MESSAGE_BODY_COLUMN);
mMessageId = createMessageId(context, mTimestamp, mSubId);
+ // TODO(b/167713264): Use the correct SMS source
+ mSmsSource = InboundSmsHandler.SOURCE_NOT_INJECTED;
}
public ContentValues getContentValues() {
@@ -317,9 +328,9 @@ public class InboundSmsTracker {
builder.append(") deleteArgs=(").append(Arrays.toString(mDeleteWhereArgs));
builder.append(')');
}
- builder.append(" id=");
- builder.append(mMessageId);
- builder.append('}');
+ builder.append(" ");
+ builder.append(SmsController.formatCrossStackMessageId(mMessageId));
+ builder.append("}");
return builder.toString();
}
@@ -347,7 +358,7 @@ public class InboundSmsTracker {
return mSubId;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getFormat() {
return mIs3gpp2 ? SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
}
@@ -456,7 +467,7 @@ public class InboundSmsTracker {
* messages, which use a 0-based index.
* @return the offset to use to convert between mIndex and the sequence number
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getIndexOffset() {
return (mIs3gpp2 && mIs3gpp2WapPdu) ? 0 : 1;
}
@@ -496,4 +507,20 @@ public class InboundSmsTracker {
public long getMessageId() {
return mMessageId;
}
+
+ public @InboundSmsHandler.SmsSource int getSource() {
+ return mSmsSource;
+ }
+
+ /**
+ * Get/create the SmsBroadcastReceiver corresponding to the current tracker.
+ */
+ public InboundSmsHandler.SmsBroadcastReceiver getSmsBroadcastReceiver(
+ InboundSmsHandler handler) {
+ // lazy initialization
+ if (mSmsBroadcastReceiver == null) {
+ mSmsBroadcastReceiver = handler.new SmsBroadcastReceiver(this);
+ }
+ return mSmsBroadcastReceiver;
+ }
}
diff --git a/src/java/com/android/internal/telephony/IntRangeManager.java b/src/java/com/android/internal/telephony/IntRangeManager.java
index 9cf562716e..12ea96f74f 100644
--- a/src/java/com/android/internal/telephony/IntRangeManager.java
+++ b/src/java/com/android/internal/telephony/IntRangeManager.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import java.util.ArrayList;
import java.util.Iterator;
@@ -183,7 +184,7 @@ public abstract class IntRangeManager {
/**
* List of integer ranges, one per client, sorted by start id.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ArrayList<IntRange> mRanges = new ArrayList<IntRange>();
protected IntRangeManager() {}
diff --git a/src/java/com/android/internal/telephony/LinkCapacityEstimate.java b/src/java/com/android/internal/telephony/LinkCapacityEstimate.java
deleted file mode 100644
index 07fa373bec..0000000000
--- a/src/java/com/android/internal/telephony/LinkCapacityEstimate.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-/**
- * Link Bandwidth Information from the Radio
- */
-public class LinkCapacityEstimate {
- /** Any field that is not reported shall be set to INVALID */
- public static final int INVALID = -1;
-
- /** LCE is active; Deprecated in HAL 1.2 */
- public static final int STATUS_ACTIVE = 0;
-
- /** LCE is suspended; Deprecated in HAL 1.2 */
- public static final int STATUS_SUSPENDED = 1;
-
- /** Downlink radio link capacity in kbps */
- public final int downlinkCapacityKbps;
-
- /** Uplink radio link capacity; added in HAL 1.2 */
- public final int uplinkCapacityKbps;
-
- /** Confidence of the downlink estimate as a percentage [1, 100]; deprecated in HAL 1.2 */
- public final int confidence;
-
- /** Status of the LCE; deprecated in HAL 1.2 */
- public final int status; // either STATUS_ACTIVE, STATUS_SUSPENDED, or INVALID
-
- /** Constructor matching the estimate in Radio HAL v1.0 */
- public LinkCapacityEstimate(int downlinkCapacityKbps, int confidence, int status) {
- this.downlinkCapacityKbps = downlinkCapacityKbps;
- this.confidence = confidence;
- this.status = status;
- this.uplinkCapacityKbps = INVALID;
- }
-
- /** Constructor matching the estimate in Radio HAL v1.2 */
- public LinkCapacityEstimate(int downlinkCapacityKbps, int uplinkCapacityKbps) {
- this.downlinkCapacityKbps = downlinkCapacityKbps;
- this.uplinkCapacityKbps = uplinkCapacityKbps;
- this.confidence = INVALID;
- this.status = INVALID;
- }
-
- @Override
- public String toString() {
- return new StringBuilder()
- .append("{downlinkCapacityKbps=")
- .append(downlinkCapacityKbps)
- .append(", uplinkCapacityKbps=")
- .append(uplinkCapacityKbps)
- .append(", confidence=")
- .append(confidence)
- .append(", status=")
- .append(status)
- .toString();
- }
-}
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index 89778e6e52..3797288760 100755
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -49,7 +49,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@@ -86,10 +85,12 @@ public class LocaleTracker extends Handler {
* <p> This broadcast is not effective on user build.
*
* <p>Example: To override the current country <code>
+ * adb root
* adb shell am broadcast -a com.android.internal.telephony.action.COUNTRY_OVERRIDE
* --es country us </code>
*
* <p> To remove the override <code>
+ * adb root
* adb shell am broadcast -a com.android.internal.telephony.action.COUNTRY_OVERRIDE
* --ez reset true</code>
*/
@@ -259,17 +260,6 @@ public class LocaleTracker extends Handler {
mPhone.registerForCellInfo(this, EVENT_UNSOL_CELL_INFO, null);
}
- private @NonNull String getCarrierCountry() {
- // The locale from the "ro.carrier" system property or R.array.carrier_properties.
- // This will be overwritten by the Locale from the SIM language settings (EF-PL, EF-LI)
- // if applicable.
- final Locale carrierLocale = mPhone.getLocaleFromCarrierProperties();
- if (carrierLocale != null && !TextUtils.isEmpty(carrierLocale.getCountry())) {
- return carrierLocale.getCountry();
- }
- return "";
- }
-
/**
* Get the device's current country.
*
@@ -492,8 +482,8 @@ public class LocaleTracker extends Handler {
*/
private synchronized void updateLocale() {
// If MCC is available from network service state, use it first.
- String countryIso = getCarrierCountry();
- String countryIsoDebugInfo = "getCarrierCountry()";
+ String countryIso = "";
+ String countryIsoDebugInfo = "empty as default";
// For time zone detection we want the best geographical match we can get, which may differ
// from the countryIso.
@@ -539,8 +529,9 @@ public class LocaleTracker extends Handler {
timeZoneCountryIsoDebugInfo = countryIsoDebugInfo;
}
- if (mLastServiceState == ServiceState.STATE_POWER_OFF) {
+ if (!mPhone.isRadioOn()) {
countryIso = "";
+ countryIsoDebugInfo = "radio off";
}
log("updateLocale: countryIso = " + countryIso
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 9d0c8c630c..b36a19ef9e 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -62,7 +62,7 @@ public final class MccTable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
publicAlternatives = "There is no alternative for {@code MccTable.MccEntry.mIso}, "
+ "but it was included in hidden APIs due to a static analysis false "
- + "positive and has been made greylist-max-q. Please file a bug if you "
+ + "positive and has been made max Q. Please file a bug if you "
+ "still require this API.")
public final String mIso;
final int mSmallestDigitsMnc;
@@ -253,7 +253,7 @@ public final class MccTable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
publicAlternatives = "There is no alternative for {@code MccTable"
+ ".smallestDigitsMccForMnc}, but it was included in hidden APIs due to a "
- + "static analysis false positive and has been made greylist-max-q. Please "
+ + "static analysis false positive and has been made max Q. Please "
+ "file a bug if you still require this API.")
public static int smallestDigitsMccForMnc(int mcc) {
MccEntry entry = entryForMcc(mcc);
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index a90daf9907..26fcfd6e0b 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -21,6 +21,7 @@ import static android.telephony.TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIS
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
+import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_NAMES;
import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE;
@@ -37,6 +38,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.ParcelUuid;
import android.provider.Settings;
@@ -80,6 +82,8 @@ public class MultiSimSettingController extends Handler {
private static final int EVENT_DEFAULT_DATA_SUBSCRIPTION_CHANGED = 6;
private static final int EVENT_CARRIER_CONFIG_CHANGED = 7;
private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 8;
+ @VisibleForTesting
+ public static final int EVENT_RADIO_STATE_CHANGED = 9;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"PRIMARY_SUB_"},
@@ -90,7 +94,8 @@ public class MultiSimSettingController extends Handler {
PRIMARY_SUB_SWAPPED,
PRIMARY_SUB_SWAPPED_IN_GROUP,
PRIMARY_SUB_MARKED_OPPT,
- PRIMARY_SUB_INITIALIZED
+ PRIMARY_SUB_INITIALIZED,
+ PRIMARY_SUB_REMOVED_IN_GROUP
})
private @interface PrimarySubChangeType {}
@@ -108,6 +113,9 @@ public class MultiSimSettingController extends Handler {
private static final int PRIMARY_SUB_MARKED_OPPT = 5;
// Subscription information is initially loaded.
private static final int PRIMARY_SUB_INITIALIZED = 6;
+ // One or more primary subscriptions are deactivated but within the same group as another active
+ // sub.
+ private static final int PRIMARY_SUB_REMOVED_IN_GROUP = 7;
protected final Context mContext;
protected final SubscriptionController mSubController;
@@ -138,6 +146,12 @@ public class MultiSimSettingController extends Handler {
// mCarrierConfigLoadedSubIds[0] = INVALID_SUBSCRIPTION_ID.
private int[] mCarrierConfigLoadedSubIds;
+ // It indicates whether "Ask every time" option for default SMS subscription is supported by the
+ // device.
+ private final boolean mIsAskEverytimeSupportedForSms;
+
+ private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
+
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -192,6 +206,8 @@ public class MultiSimSettingController extends Handler {
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
+ mIsAskEverytimeSupportedForSms = mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support);
context.registerReceiver(mIntentReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
}
@@ -226,6 +242,7 @@ public class MultiSimSettingController extends Handler {
* Notify subscription info change.
*/
public void notifySubscriptionInfoChanged() {
+ log("notifySubscriptionInfoChanged");
obtainMessage(EVENT_SUBSCRIPTION_INFO_CHANGED).sendToTarget();
}
@@ -279,6 +296,16 @@ public class MultiSimSettingController extends Handler {
case EVENT_MULTI_SIM_CONFIG_CHANGED:
int activeModems = (int) ((AsyncResult) msg.obj).result;
onMultiSimConfigChanged(activeModems);
+ break;
+ case EVENT_RADIO_STATE_CHANGED:
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if (phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
+ if (DBG) log("Radio unavailable. Clearing sub info initialized flag.");
+ mSubInfoInitialized = false;
+ break;
+ }
+ }
+ break;
}
}
@@ -296,6 +323,8 @@ public class MultiSimSettingController extends Handler {
// If user is enabling a non-default non-opportunistic subscription, make it default.
if (mSubController.getDefaultDataSubId() != subId && !mSubController.isOpportunistic(subId)
&& enable && mSubController.isActiveSubId(subId)) {
+ android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+ SETTING_USER_PREF_DATA_SUB, subId);
mSubController.setDefaultDataSubId(subId);
}
}
@@ -318,6 +347,9 @@ public class MultiSimSettingController extends Handler {
private void onAllSubscriptionsLoaded() {
if (DBG) log("onAllSubscriptionsLoaded");
mSubInfoInitialized = true;
+ for (Phone phone : PhoneFactory.getPhones()) {
+ phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
+ }
reEvaluateAll();
}
@@ -332,6 +364,22 @@ public class MultiSimSettingController extends Handler {
}
/**
+ * This method is called when a phone object is removed (for example when going from multi-sim
+ * to single-sim).
+ * NOTE: This method does not post a message to self, instead it calls reEvaluateAll() directly.
+ * so it should only be called from the main thread. The reason is to update defaults asap
+ * after multi_sim_config property has been updated (see b/163582235).
+ */
+ public void onPhoneRemoved() {
+ if (DBG) log("onPhoneRemoved");
+ if (Looper.myLooper() != this.getLooper()) {
+ throw new RuntimeException("This method must be called from the same looper as "
+ + "MultiSimSettingController.");
+ }
+ reEvaluateAll();
+ }
+
+ /**
* Called when carrier config changes on any phone.
*/
@VisibleForTesting
@@ -391,6 +439,9 @@ public class MultiSimSettingController extends Handler {
for (int phoneId = activeModems; phoneId < mCarrierConfigLoadedSubIds.length; phoneId++) {
mCarrierConfigLoadedSubIds[phoneId] = INVALID_SUBSCRIPTION_ID;
}
+ for (Phone phone : PhoneFactory.getPhones()) {
+ phone.mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
+ }
}
/**
@@ -518,6 +569,7 @@ public class MultiSimSettingController extends Handler {
mSubController.setDefaultDataSubId(subId);
mSubController.setDefaultVoiceSubId(subId);
mSubController.setDefaultSmsSubId(subId);
+ sendDefaultSubConfirmedNotification(subId);
return;
}
@@ -539,9 +591,20 @@ public class MultiSimSettingController extends Handler {
if (DBG) log("[updateDefaultValues] Update default sms subscription");
boolean smsSelected = updateDefaultValue(mPrimarySubList,
mSubController.getDefaultSmsSubId(),
- (newValue -> mSubController.setDefaultSmsSubId(newValue)));
-
- sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected);
+ (newValue -> mSubController.setDefaultSmsSubId(newValue)),
+ mIsAskEverytimeSupportedForSms);
+
+ boolean autoFallbackEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_voice_data_sms_auto_fallback);
+
+ // Based on config config_voice_data_sms_auto_fallback value choose voice/data/sms
+ // preference auto selection logic or display notification for end used to
+ // select voice/data/SMS preferences.
+ if (!autoFallbackEnabled) {
+ sendSubChangeNotificationIfNeeded(change, dataSelected, voiceSelected, smsSelected);
+ } else {
+ updateUserPreferences(mPrimarySubList, dataSelected, voiceSelected, smsSelected);
+ }
}
@PrimarySubChangeType
@@ -580,7 +643,14 @@ public class MultiSimSettingController extends Handler {
// any previous primary subscription becomes inactive, we consider it
for (int subId : prevPrimarySubList) {
if (mPrimarySubList.contains(subId)) continue;
- if (!mSubController.isActiveSubId(subId)) return PRIMARY_SUB_REMOVED;
+ if (!mSubController.isActiveSubId(subId)) {
+ for (int currentSubId : mPrimarySubList) {
+ if (areSubscriptionsInSameGroup(currentSubId, subId)) {
+ return PRIMARY_SUB_REMOVED_IN_GROUP;
+ }
+ }
+ return PRIMARY_SUB_REMOVED;
+ }
if (!mSubController.isOpportunistic(subId)) {
// Should never happen.
loge("[updatePrimarySubListAndGetChangeType]: missing active primary subId "
@@ -591,6 +661,19 @@ public class MultiSimSettingController extends Handler {
}
}
+ private void sendDefaultSubConfirmedNotification(int defaultSubId) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED);
+ intent.setClassName("com.android.settings",
+ "com.android.settings.sim.SimSelectNotification");
+
+ intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE,
+ EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS);
+ intent.putExtra(EXTRA_SUBSCRIPTION_ID, defaultSubId);
+
+ mContext.sendBroadcast(intent);
+ }
+
private void sendSubChangeNotificationIfNeeded(int change, boolean dataSelected,
boolean voiceSelected, boolean smsSelected) {
@TelephonyManager.DefaultSubscriptionSelectType
@@ -634,8 +717,9 @@ public class MultiSimSettingController extends Handler {
if (mPrimarySubList.size() == 1 && change == PRIMARY_SUB_REMOVED
&& (!dataSelected || !smsSelected || !voiceSelected)) {
dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
- } else if (mPrimarySubList.size() > 1 && isUserVisibleChange(change)) {
- // If change is SWAPPED_IN_GROUP or MARKED_OPPT orINITIALIZED, don't ask user again.
+ } else if (mPrimarySubList.size() > 1 && (isUserVisibleChange(change)
+ || (change == PRIMARY_SUB_INITIALIZED && !dataSelected))) {
+ // If change is SWAPPED_IN_GROUP or MARKED_OPPT, don't ask user again.
dialogType = EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
}
@@ -699,7 +783,8 @@ public class MultiSimSettingController extends Handler {
&& phone.isUserDataEnabled()
&& !areSubscriptionsInSameGroup(defaultDataSub, phone.getSubId())) {
log("setting data to false on " + phone.getSubId());
- phone.getDataEnabledSettings().setUserDataEnabled(false);
+ phone.getDataEnabledSettings().setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
}
}
}
@@ -771,14 +856,23 @@ public class MultiSimSettingController extends Handler {
// Returns whether the new default value is valid.
private boolean updateDefaultValue(List<Integer> primarySubList, int oldValue,
UpdateDefaultAction action) {
+ return updateDefaultValue(primarySubList, oldValue, action, true);
+ }
+
+ private boolean updateDefaultValue(List<Integer> primarySubList, int oldValue,
+ UpdateDefaultAction action, boolean allowInvalidSubId) {
int newValue = INVALID_SUBSCRIPTION_ID;
if (primarySubList.size() > 0) {
for (int subId : primarySubList) {
if (DBG) log("[updateDefaultValue] Record.id: " + subId);
- // If the old subId is still active, or there's another active primary subscription
- // that is in the same group, that should become the new default subscription.
- if (areSubscriptionsInSameGroup(subId, oldValue)) {
+ // 1) If the old subId is still active, or there's another active primary
+ // subscription that is in the same group, that should become the new default
+ // subscription.
+ // 2) If the old subId is INVALID_SUBSCRIPTION_ID and allowInvalidSubId is false,
+ // first active subscription is used for new default always.
+ if (areSubscriptionsInSameGroup(subId, oldValue)
+ || (!allowInvalidSubId && oldValue == INVALID_SUBSCRIPTION_ID)) {
newValue = subId;
log("[updateDefaultValue] updates to subId=" + newValue);
break;
@@ -827,6 +921,75 @@ public class MultiSimSettingController extends Handler {
}
}
+ // Voice/Data/SMS preferences would be auto selected without any user
+ // confirmation in following scenarios,
+ // 1. When device powered-up with only one SIM Inserted or while two SIM cards
+ // present if one SIM is removed(or turned OFF) the reaiming SIM would be
+ // selected as preferred voice/data/sms SIM.
+ // 2. When device powered-up with two SIM cards or if two SIM cards
+ // present on device with new SIM insert(or SIM turn ON) the first inserted SIM
+ // would be selected as preferred voice/data/sms SIM.
+ private void updateUserPreferences(List<Integer> primarySubList, boolean dataSelected,
+ boolean voiceSelected, boolean smsSelected) {
+ // In Single SIM case or if there are no activated subs available, no need to update. EXIT.
+ if ((primarySubList.isEmpty()) || (mSubController.getActiveSubInfoCountMax() == 1)) return;
+
+ if (!isRadioAvailableOnAllSubs()) {
+ log("Radio is in Invalid state, Ignore Updating User Preference!!!");
+ return;
+ }
+ final int defaultDataSubId = mSubController.getDefaultDataSubId();
+
+ if (DBG) log("updateUserPreferences: dds = " + defaultDataSubId + " voice = "
+ + mSubController.getDefaultVoiceSubId() +
+ " sms = " + mSubController.getDefaultSmsSubId());
+
+ int autoDefaultSubId = primarySubList.get(0);
+
+ if ((primarySubList.size() == 1) && !smsSelected) {
+ mSubController.setDefaultSmsSubId(autoDefaultSubId);
+ }
+
+ if ((primarySubList.size() == 1) && !voiceSelected) {
+ mSubController.setDefaultVoiceSubId(autoDefaultSubId);
+ }
+
+ int userPrefDataSubId = getUserPrefDataSubIdFromDB();
+
+ if (DBG) log("User pref subId = " + userPrefDataSubId + " current dds " + defaultDataSubId
+ + " next active subId " + autoDefaultSubId);
+
+ // If earlier user selected DDS is now available, set that as DDS subId.
+ if (primarySubList.contains(userPrefDataSubId) &&
+ SubscriptionManager.isValidSubscriptionId(userPrefDataSubId) &&
+ (defaultDataSubId != userPrefDataSubId)) {
+ mSubController.setDefaultDataSubId(userPrefDataSubId);
+ } else if (!dataSelected) {
+ mSubController.setDefaultDataSubId(autoDefaultSubId);
+ }
+
+
+ if (DBG) log("updateUserPreferences: after dds = " + mSubController.getDefaultDataSubId() +
+ " voice = " + mSubController.getDefaultVoiceSubId() + " sms = " +
+ mSubController.getDefaultSmsSubId());
+ }
+
+ private int getUserPrefDataSubIdFromDB() {
+ return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+ SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ private boolean isRadioAvailableOnAllSubs() {
+ for (Phone phone : PhoneFactory.getPhones()) {
+ if ((phone.mCi != null &&
+ phone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_UNAVAILABLE) ||
+ phone.isShuttingDown()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void log(String msg) {
Log.d(LOG_TAG, msg);
}
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 69f64f1bc4..3535678a30 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -182,8 +182,8 @@ public class NetworkRegistrationManager extends Handler {
@Override
public void binderDied() {
// TODO: try to restart the service.
- logd("NetworkService(" + mComponentName + " transport type "
- + mTransportType + ") died.");
+ logd("Network service " + mComponentName + " for transport type "
+ + AccessNetworkConstants.transportTypeToString(mTransportType) + " died.");
}
}
@@ -202,7 +202,6 @@ public class NetworkRegistrationManager extends Handler {
new NetworkRegStateCallback());
} catch (RemoteException exception) {
// Remote exception means that the binder already died.
- mDeathRecipient.binderDied();
logd("RemoteException " + exception);
}
}
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index 6d10a86ba6..92f552a8ff 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -68,6 +68,7 @@ public final class NetworkScanRequestTracker {
private static final int CMD_INTERRUPT_NETWORK_SCAN = 6;
private static final int EVENT_INTERRUPT_NETWORK_SCAN_DONE = 7;
private static final int EVENT_MODEM_RESET = 8;
+ private static final int EVENT_RADIO_UNAVAILABLE = 9;
private final Handler mHandler = new Handler() {
@Override
@@ -102,6 +103,8 @@ public final class NetworkScanRequestTracker {
mScheduler.interruptScanDone((AsyncResult) msg.obj);
break;
+ case EVENT_RADIO_UNAVAILABLE:
+ // Fallthrough
case EVENT_MODEM_RESET:
AsyncResult ar = (AsyncResult) msg.obj;
mScheduler.deleteScanAndMayNotify(
@@ -442,7 +445,10 @@ public final class NetworkScanRequestTracker {
.setCallingPackage(nsri.mCallingPackage)
.setCallingPid(nsri.mPid)
.setCallingUid(nsri.mUid)
+ .setCallingFeatureId(nsri.mPhone.getContext().getAttributionTag())
.setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
+ .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
.setMethod("NetworkScanTracker#onResult")
.build();
if (ar.exception == null && ar.result != null) {
@@ -570,6 +576,7 @@ public final class NetworkScanRequestTracker {
nsri.mPhone.startNetworkScan(nsri.getRequest(),
mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri));
nsri.mPhone.mCi.registerForModemReset(mHandler, EVENT_MODEM_RESET, nsri);
+ nsri.mPhone.mCi.registerForNotAvailable(mHandler, EVENT_RADIO_UNAVAILABLE, nsri);
return true;
}
return false;
@@ -590,6 +597,7 @@ public final class NetworkScanRequestTracker {
}
}
mLiveRequestInfo.mPhone.mCi.unregisterForModemReset(mHandler);
+ mLiveRequestInfo.mPhone.mCi.unregisterForNotAvailable(mHandler);
mLiveRequestInfo = null;
if (mPendingRequestInfo != null) {
startNewScan(mPendingRequestInfo);
@@ -604,8 +612,8 @@ public final class NetworkScanRequestTracker {
*
* This method is similar to stopNetworkScan, since they both stops an ongoing scan. The
* difference is that stopNetworkScan is only used by the callers to stop their own scans, so
- * sanity check will be done to make sure the request is valid; while this method is only
- * internally used by NetworkScanRequestTracker so sanity check is not needed.
+ * correctness check will be done to make sure the request is valid; while this method is only
+ * internally used by NetworkScanRequestTracker so correctness check is not needed.
*/
private void interruptNetworkScan(int scanId) {
// scanId will be stored at Message.arg1
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index c9022c4cc5..a1724a6822 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -27,15 +27,20 @@ import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
-import android.telephony.RadioAccessFamily;
+import android.telephony.PcoData;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.text.TextUtils;
+import com.android.internal.telephony.dataconnection.DataConnection;
import com.android.internal.telephony.dataconnection.DcController;
import com.android.internal.telephony.dataconnection.DcController.PhysicalLinkState;
+import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.util.IState;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
@@ -45,11 +50,13 @@ import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.IntStream;
/**
* The NetworkTypeController evaluates the override network type of {@link TelephonyDisplayInfo}
@@ -63,14 +70,14 @@ public class NetworkTypeController extends StateMachine {
private static final String TAG = "NetworkTypeController";
private static final String ICON_5G = "5g";
private static final String ICON_5G_PLUS = "5g_plus";
- private static final String STATE_CONNECTED_MMWAVE = "connected_mmwave";
+ private static final String STATE_CONNECTED_NR_ADVANCED = "connected_mmwave";
private static final String STATE_CONNECTED = "connected";
private static final String STATE_NOT_RESTRICTED_RRC_IDLE = "not_restricted_rrc_idle";
private static final String STATE_NOT_RESTRICTED_RRC_CON = "not_restricted_rrc_con";
private static final String STATE_RESTRICTED = "restricted";
private static final String STATE_ANY = "any";
private static final String STATE_LEGACY = "legacy";
- private static final String[] ALL_STATES = { STATE_CONNECTED_MMWAVE, STATE_CONNECTED,
+ private static final String[] ALL_STATES = {STATE_CONNECTED_NR_ADVANCED, STATE_CONNECTED,
STATE_NOT_RESTRICTED_RRC_IDLE, STATE_NOT_RESTRICTED_RRC_CON, STATE_RESTRICTED,
STATE_LEGACY };
@@ -89,13 +96,10 @@ public class NetworkTypeController extends StateMachine {
private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
private static final int EVENT_INITIALIZE = 12;
- // events that don't reset the timer
- private static final int[] ALL_EVENTS = { EVENT_DATA_RAT_CHANGED, EVENT_NR_STATE_CHANGED,
- EVENT_NR_FREQUENCY_CHANGED, EVENT_PHYSICAL_LINK_STATE_CHANGED,
- EVENT_PHYSICAL_CHANNEL_CONFIG_NOTIF_CHANGED, EVENT_PRIMARY_TIMER_EXPIRED,
- EVENT_SECONDARY_TIMER_EXPIRED};
+ private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
+ private static final int EVENT_PCO_DATA_CHANGED = 14;
- private static final String[] sEvents = new String[EVENT_INITIALIZE + 1];
+ private static final String[] sEvents = new String[EVENT_PCO_DATA_CHANGED + 1];
static {
sEvents[EVENT_UPDATE] = "EVENT_UPDATE";
sEvents[EVENT_QUIT] = "EVENT_QUIT";
@@ -111,6 +115,8 @@ public class NetworkTypeController extends StateMachine {
sEvents[EVENT_RADIO_OFF_OR_UNAVAILABLE] = "EVENT_RADIO_OFF_OR_UNAVAILABLE";
sEvents[EVENT_PREFERRED_NETWORK_MODE_CHANGED] = "EVENT_PREFERRED_NETWORK_MODE_CHANGED";
sEvents[EVENT_INITIALIZE] = "EVENT_INITIALIZE";
+ sEvents[EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED] = "EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED";
+ sEvents[EVENT_PCO_DATA_CHANGED] = "EVENT_PCO_DATA_CHANGED";
}
private final Phone mPhone;
@@ -134,10 +140,17 @@ public class NetworkTypeController extends StateMachine {
private boolean mIsPhysicalChannelConfigOn;
private boolean mIsPrimaryTimerActive;
private boolean mIsSecondaryTimerActive;
+ private boolean mIsTimerResetEnabledForLegacyStateRRCIdle;
+ private int mLtePlusThresholdBandwidth;
+ private int[] mAdditionalNrAdvancedBandsList;
private String mPrimaryTimerState;
private String mSecondaryTimerState;
private String mPreviousState;
private @PhysicalLinkState int mPhysicalLinkState;
+ private boolean mIsPhysicalChannelConfig16Supported;
+ private Boolean mIsNrAdvancedAllowedByPco = false;
+ private int mNrAdvancedCapablePcoId = 0;
+ private boolean mIsUsingUserDataForRrcDetection = false;
/**
* NetworkTypeController constructor.
@@ -169,17 +182,32 @@ public class NetworkTypeController extends StateMachine {
return mOverrideNetworkType;
}
+ /**
+ * @return True if either the primary or secondary 5G hysteresis timer is active,
+ * and false if neither are.
+ */
+ public boolean is5GHysteresisActive() {
+ return mIsPrimaryTimerActive || mIsSecondaryTimerActive;
+ }
+
private void registerForAllEvents() {
mPhone.registerForRadioOffOrNotAvailable(getHandler(),
EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
mPhone.registerForPreferredNetworkTypeChanged(getHandler(),
EVENT_PREFERRED_NETWORK_MODE_CHANGED, null);
+ mPhone.registerForPhysicalChannelConfig(getHandler(),
+ EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null);
mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
AccessNetworkConstants.TRANSPORT_TYPE_WWAN, getHandler(),
EVENT_DATA_RAT_CHANGED, null);
- mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- .registerForPhysicalLinkStateChanged(getHandler(),
- EVENT_PHYSICAL_LINK_STATE_CHANGED);
+ mIsPhysicalChannelConfig16Supported = mPhone.getContext().getSystemService(
+ TelephonyManager.class).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ if (!mIsPhysicalChannelConfig16Supported) {
+ mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .registerForPhysicalLinkStateChanged(getHandler(),
+ EVENT_PHYSICAL_LINK_STATE_CHANGED);
+ }
mPhone.getServiceStateTracker().registerForNrStateChanged(getHandler(),
EVENT_NR_STATE_CHANGED, null);
mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
@@ -189,6 +217,7 @@ public class NetworkTypeController extends StateMachine {
IntentFilter filter = new IntentFilter();
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
+ mPhone.mCi.registerForPcoData(getHandler(), EVENT_PCO_DATA_CHANGED, null);
}
private void unRegisterForAllEvents() {
@@ -200,6 +229,7 @@ public class NetworkTypeController extends StateMachine {
mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
mPhone.getDeviceStateMonitor().unregisterForPhysicalChannelConfigNotifChanged(getHandler());
mPhone.getContext().unregisterReceiver(mIntentReceiver);
+ mPhone.mCi.unregisterForPcoData(getHandler());
}
private void parseCarrierConfigs() {
@@ -211,6 +241,11 @@ public class NetworkTypeController extends StateMachine {
CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING);
mLteEnhancedPattern = CarrierConfigManager.getDefaultConfig().getString(
CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
+ mIsTimerResetEnabledForLegacyStateRRCIdle =
+ CarrierConfigManager.getDefaultConfig().getBoolean(
+ CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
+ mLtePlusThresholdBandwidth = CarrierConfigManager.getDefaultConfig().getInt(
+ CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT);
CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -236,6 +271,22 @@ public class NetworkTypeController extends StateMachine {
mLteEnhancedPattern = b.getString(
CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING);
}
+ mIsTimerResetEnabledForLegacyStateRRCIdle = b.getBoolean(
+ CarrierConfigManager.KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL);
+ mLtePlusThresholdBandwidth = b.getInt(
+ CarrierConfigManager.KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT,
+ mLtePlusThresholdBandwidth);
+ mAdditionalNrAdvancedBandsList = b.getIntArray(
+ CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY);
+ mNrAdvancedCapablePcoId = b.getInt(
+ CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT);
+ mIsUsingUserDataForRrcDetection = b.getBoolean(
+ CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL);
+ if (mIsPhysicalChannelConfig16Supported && mIsUsingUserDataForRrcDetection) {
+ mPhone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .registerForPhysicalLinkStateChanged(getHandler(),
+ EVENT_PHYSICAL_LINK_STATE_CHANGED);
+ }
}
}
createTimerRules(nrIconConfiguration, overrideTimerRule, overrideSecondaryTimerRule);
@@ -255,7 +306,7 @@ public class NetworkTypeController extends StateMachine {
if (kv[1].equals(ICON_5G)) {
icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA;
} else if (kv[1].equals(ICON_5G_PLUS)) {
- icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE;
+ icon = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED;
} else {
if (DBG) loge("Invalid 5G icon = " + kv[1]);
}
@@ -339,13 +390,15 @@ public class NetworkTypeController extends StateMachine {
private @Annotation.OverrideNetworkType int getCurrentOverrideNetworkType() {
int displayNetworkType = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
- int dataNetworkType = mPhone.getServiceState().getDataNetworkType();
+ int dataNetworkType = getDataNetworkType();
+ boolean nrNsa = isLte(dataNetworkType)
+ && mPhone.getServiceState().getNrState() != NetworkRegistrationInfo.NR_STATE_NONE;
+ boolean nrSa = dataNetworkType == TelephonyManager.NETWORK_TYPE_NR;
+
// NR display is not accurate when physical channel config notifications are off
- if (mIsPhysicalChannelConfigOn
- && (mPhone.getServiceState().getNrState() != NetworkRegistrationInfo.NR_STATE_NONE
- || dataNetworkType == TelephonyManager.NETWORK_TYPE_NR)) {
+ if (mIsPhysicalChannelConfigOn && (nrNsa || nrSa)) {
// Process NR display network type
- displayNetworkType = getNrDisplayType();
+ displayNetworkType = getNrDisplayType(nrSa);
if (displayNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE) {
// Use LTE values if 5G values aren't defined
displayNetworkType = getLteDisplayType();
@@ -357,29 +410,32 @@ public class NetworkTypeController extends StateMachine {
return displayNetworkType;
}
- private @Annotation.OverrideNetworkType int getNrDisplayType() {
+ private @Annotation.OverrideNetworkType int getNrDisplayType(boolean isNrSa) {
// Don't show 5G icon if preferred network type does not include 5G
- if ((RadioAccessFamily.getRafFromNetworkType(mPhone.getCachedPreferredNetworkType())
+ if ((mPhone.getCachedAllowedNetworkTypesBitmask()
& TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
return TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
}
// Icon display keys in order of priority
List<String> keys = new ArrayList<>();
- // TODO: Update for NR SA
- switch (mPhone.getServiceState().getNrState()) {
- case NetworkRegistrationInfo.NR_STATE_CONNECTED:
- if (isNrMmwave()) {
- keys.add(STATE_CONNECTED_MMWAVE);
- }
- keys.add(STATE_CONNECTED);
- break;
- case NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED:
- keys.add(isPhysicalLinkActive() ? STATE_NOT_RESTRICTED_RRC_CON
- : STATE_NOT_RESTRICTED_RRC_IDLE);
- break;
- case NetworkRegistrationInfo.NR_STATE_RESTRICTED:
- keys.add(STATE_RESTRICTED);
- break;
+ if (isNrSa && isNrAdvanced()) {
+ keys.add(STATE_CONNECTED_NR_ADVANCED);
+ } else {
+ switch (mPhone.getServiceState().getNrState()) {
+ case NetworkRegistrationInfo.NR_STATE_CONNECTED:
+ if (isNrAdvanced()) {
+ keys.add(STATE_CONNECTED_NR_ADVANCED);
+ }
+ keys.add(STATE_CONNECTED);
+ break;
+ case NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED:
+ keys.add(isPhysicalLinkActive() ? STATE_NOT_RESTRICTED_RRC_CON
+ : STATE_NOT_RESTRICTED_RRC_IDLE);
+ break;
+ case NetworkRegistrationInfo.NR_STATE_RESTRICTED:
+ keys.add(STATE_RESTRICTED);
+ break;
+ }
}
for (String key : keys) {
@@ -394,8 +450,10 @@ public class NetworkTypeController extends StateMachine {
private @Annotation.OverrideNetworkType int getLteDisplayType() {
int value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE;
- if (mPhone.getServiceState().getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA
- || mPhone.getServiceState().isUsingCarrierAggregation()) {
+ if ((getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA
+ || mPhone.getServiceState().isUsingCarrierAggregation())
+ && (IntStream.of(mPhone.getServiceState().getCellBandwidths()).sum()
+ > mLtePlusThresholdBandwidth)) {
value = TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA;
}
if (isLteEnhancedAvailable()) {
@@ -430,6 +488,7 @@ public class NetworkTypeController extends StateMachine {
if (DBG) log("DefaultState: process " + getEventName(msg.what));
switch (msg.what) {
case EVENT_UPDATE:
+ case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
resetAllTimers();
transitionToCurrentState();
break;
@@ -448,8 +507,14 @@ public class NetworkTypeController extends StateMachine {
case EVENT_DATA_RAT_CHANGED:
case EVENT_NR_STATE_CHANGED:
case EVENT_NR_FREQUENCY_CHANGED:
+ case EVENT_PCO_DATA_CHANGED:
// ignored
break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+ }
+ break;
case EVENT_PHYSICAL_LINK_STATE_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
mPhysicalLinkState = (int) ar.result;
@@ -460,18 +525,12 @@ public class NetworkTypeController extends StateMachine {
if (DBG) {
log("mIsPhysicalChannelConfigOn changed to: " + mIsPhysicalChannelConfigOn);
}
- for (int event : ALL_EVENTS) {
- removeMessages(event);
- }
if (!mIsPhysicalChannelConfigOn) {
resetAllTimers();
}
transitionToCurrentState();
break;
case EVENT_CARRIER_CONFIG_CHANGED:
- for (int event : ALL_EVENTS) {
- removeMessages(event);
- }
parseCarrierConfigs();
resetAllTimers();
transitionToCurrentState();
@@ -489,10 +548,6 @@ public class NetworkTypeController extends StateMachine {
resetAllTimers();
transitionTo(mLegacyState);
break;
- case EVENT_PREFERRED_NETWORK_MODE_CHANGED:
- resetAllTimers();
- transitionToCurrentState();
- break;
default:
throw new RuntimeException("Received invalid event: " + msg.what);
}
@@ -529,7 +584,7 @@ public class NetworkTypeController extends StateMachine {
public boolean processMessage(Message msg) {
if (DBG) log("LegacyState: process " + getEventName(msg.what));
updateTimers();
- int rat = mPhone.getServiceState().getDataNetworkType();
+ int rat = getDataNetworkType();
switch (msg.what) {
case EVENT_DATA_RAT_CHANGED:
if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
@@ -538,6 +593,10 @@ public class NetworkTypeController extends StateMachine {
transitionWithTimerTo(isPhysicalLinkActive()
? mLteConnectedState : mIdleState);
} else {
+ if (!isLte(rat)) {
+ // Rat is 3G or 2G, and it doesn't need NR timer.
+ resetAllTimers();
+ }
updateOverrideNetworkType();
}
mIsNrRestricted = isNrRestricted();
@@ -556,6 +615,24 @@ public class NetworkTypeController extends StateMachine {
case EVENT_NR_FREQUENCY_CHANGED:
// ignored
break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+ if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
+ resetAllTimers();
+ }
+ }
+ // Update in case of LTE/LTE+ switch
+ updateOverrideNetworkType();
+ break;
+ case EVENT_PHYSICAL_LINK_STATE_CHANGED:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ mPhysicalLinkState = (int) ar.result;
+ if (mIsTimerResetEnabledForLegacyStateRRCIdle && !isPhysicalLinkActive()) {
+ resetAllTimers();
+ updateOverrideNetworkType();
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -593,7 +670,7 @@ public class NetworkTypeController extends StateMachine {
updateTimers();
switch (msg.what) {
case EVENT_DATA_RAT_CHANGED:
- int rat = mPhone.getServiceState().getDataNetworkType();
+ int rat = getDataNetworkType();
if (rat == TelephonyManager.NETWORK_TYPE_NR) {
transitionTo(mNrConnectedState);
} else if (!isLte(rat) || !isNrNotRestricted()) {
@@ -610,6 +687,23 @@ public class NetworkTypeController extends StateMachine {
case EVENT_NR_FREQUENCY_CHANGED:
// ignore
break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+ if (isNrNotRestricted()) {
+ // NOT_RESTRICTED_RRC_IDLE -> NOT_RESTRICTED_RRC_CON
+ if (isPhysicalLinkActive()) {
+ transitionWithTimerTo(mLteConnectedState);
+ break;
+ }
+ } else {
+ log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
+ sendMessage(EVENT_NR_STATE_CHANGED);
+ }
+ }
+ // Update in case of LTE/LTE+ switch
+ updateOverrideNetworkType();
+ break;
case EVENT_PHYSICAL_LINK_STATE_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
mPhysicalLinkState = (int) ar.result;
@@ -660,7 +754,7 @@ public class NetworkTypeController extends StateMachine {
updateTimers();
switch (msg.what) {
case EVENT_DATA_RAT_CHANGED:
- int rat = mPhone.getServiceState().getDataNetworkType();
+ int rat = getDataNetworkType();
if (rat == TelephonyManager.NETWORK_TYPE_NR) {
transitionTo(mNrConnectedState);
} else if (!isLte(rat) || !isNrNotRestricted()) {
@@ -677,6 +771,23 @@ public class NetworkTypeController extends StateMachine {
case EVENT_NR_FREQUENCY_CHANGED:
// ignore
break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
+ if (isNrNotRestricted()) {
+ // NOT_RESTRICTED_RRC_CON -> NOT_RESTRICTED_RRC_IDLE
+ if (!isPhysicalLinkActive()) {
+ transitionWithTimerTo(mIdleState);
+ break;
+ }
+ } else {
+ log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
+ sendMessage(EVENT_NR_STATE_CHANGED);
+ }
+ }
+ // Update in case of LTE/LTE+ switch
+ updateOverrideNetworkType();
+ break;
case EVENT_PHYSICAL_LINK_STATE_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
mPhysicalLinkState = (int) ar.result;
@@ -711,7 +822,7 @@ public class NetworkTypeController extends StateMachine {
* Device is connected to 5G NR as the secondary cell.
*/
private final class NrConnectedState extends State {
- private Boolean mIsNrMmwave = false;
+ private Boolean mIsNrAdvanced = false;
@Override
public void enter() {
@@ -719,7 +830,7 @@ public class NetworkTypeController extends StateMachine {
updateTimers();
updateOverrideNetworkType();
if (!mIsPrimaryTimerActive && !mIsSecondaryTimerActive) {
- mIsNrMmwave = isNrMmwave();
+ mIsNrAdvanced = isNrAdvanced();
mPreviousState = getName();
}
}
@@ -728,7 +839,7 @@ public class NetworkTypeController extends StateMachine {
public boolean processMessage(Message msg) {
if (DBG) log("NrConnectedState: process " + getEventName(msg.what));
updateTimers();
- int rat = mPhone.getServiceState().getDataNetworkType();
+ int rat = getDataNetworkType();
switch (msg.what) {
case EVENT_DATA_RAT_CHANGED:
if (rat == TelephonyManager.NETWORK_TYPE_NR || isLte(rat) && isNrConnected()) {
@@ -748,20 +859,15 @@ public class NetworkTypeController extends StateMachine {
transitionWithTimerTo(mLegacyState);
}
break;
+ case EVENT_PCO_DATA_CHANGED:
+ handlePcoData((AsyncResult) msg.obj);
+ break;
case EVENT_NR_FREQUENCY_CHANGED:
- if (!isNrConnected()) {
- log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
- sendMessage(EVENT_NR_STATE_CHANGED);
- break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ if (isUsingPhysicalChannelConfigForRrcDetection()) {
+ mPhysicalLinkState = getPhysicalLinkStateFromPhysicalChannelConfig();
}
- if (!isNrMmwave()) {
- // STATE_CONNECTED_MMWAVE -> STATE_CONNECTED
- transitionWithTimerTo(mNrConnectedState);
- } else {
- // STATE_CONNECTED -> STATE_CONNECTED_MMWAVE
- transitionTo(mNrConnectedState);
- }
- mIsNrMmwave = isNrMmwave();
+ updateNrAdvancedState();
break;
case EVENT_PHYSICAL_LINK_STATE_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
@@ -782,7 +888,48 @@ public class NetworkTypeController extends StateMachine {
@Override
public String getName() {
- return mIsNrMmwave ? STATE_CONNECTED_MMWAVE : STATE_CONNECTED;
+ return mIsNrAdvanced ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
+ }
+
+ private void updateNrAdvancedState() {
+ if (!isNrConnected()) {
+ log("NR state changed. Sending EVENT_NR_STATE_CHANGED");
+ sendMessage(EVENT_NR_STATE_CHANGED);
+ return;
+ }
+ if (!isNrAdvanced()) {
+ // STATE_CONNECTED_NR_ADVANCED -> STATE_CONNECTED
+ transitionWithTimerTo(mNrConnectedState);
+ } else {
+ // STATE_CONNECTED -> STATE_CONNECTED_NR_ADVANCED
+ transitionTo(mNrConnectedState);
+ }
+ mIsNrAdvanced = isNrAdvanced();
+ }
+
+ private void handlePcoData(AsyncResult ar) {
+ if (ar.exception != null) {
+ loge("PCO_DATA exception: " + ar.exception);
+ return;
+ }
+ PcoData pcodata = (PcoData) ar.result;
+ if (pcodata == null) {
+ return;
+ }
+ log("EVENT_PCO_DATA_CHANGED: pco data: " + pcodata);
+ DcTracker dcTracker = mPhone.getDcTracker(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ DataConnection dc =
+ dcTracker != null ? dcTracker.getDataConnectionByContextId(pcodata.cid) : null;
+ ApnSetting apnSettings = dc != null ? dc.getApnSetting() : null;
+ if (apnSettings != null && apnSettings.canHandleType(ApnSetting.TYPE_DEFAULT)
+ && mNrAdvancedCapablePcoId > 0
+ && pcodata.pcoId == mNrAdvancedCapablePcoId
+ ) {
+ log("EVENT_PCO_DATA_CHANGED: Nr Advanced is allowed by PCO.");
+ mIsNrAdvancedAllowedByPco = pcodata.contents[0] == 1;
+ updateNrAdvancedState();
+ }
}
}
@@ -818,11 +965,11 @@ public class NetworkTypeController extends StateMachine {
}
private void transitionToCurrentState() {
- int dataRat = mPhone.getServiceState().getDataNetworkType();
+ int dataRat = getDataNetworkType();
IState transitionState;
if (dataRat == TelephonyManager.NETWORK_TYPE_NR || isNrConnected()) {
transitionState = mNrConnectedState;
- mPreviousState = isNrMmwave() ? STATE_CONNECTED_MMWAVE : STATE_CONNECTED;
+ mPreviousState = isNrAdvanced() ? STATE_CONNECTED_NR_ADVANCED : STATE_CONNECTED;
} else if (isLte(dataRat) && isNrNotRestricted()) {
if (isPhysicalLinkActive()) {
transitionState = mLteConnectedState;
@@ -867,12 +1014,21 @@ public class NetworkTypeController extends StateMachine {
mSecondaryTimerState = "";
}
- if (currentState.equals(STATE_CONNECTED_MMWAVE)) {
+ if (currentState.equals(STATE_CONNECTED_NR_ADVANCED)) {
+ resetAllTimers();
+ }
+
+ int rat = getDataNetworkType();
+ if (!isLte(rat) && rat != TelephonyManager.NETWORK_TYPE_NR) {
+ // Rat is 3G or 2G, and it doesn't need NR timer.
resetAllTimers();
}
}
private void resetAllTimers() {
+ if (DBG) {
+ log("Remove all timers");
+ }
removeMessages(EVENT_PRIMARY_TIMER_EXPIRED);
removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
mIsPrimaryTimerActive = false;
@@ -977,11 +1133,38 @@ public class NetworkTypeController extends StateMachine {
== NetworkRegistrationInfo.NR_STATE_RESTRICTED;
}
+ private boolean isNrAdvanced() {
+ return isNrAdvancedCapable() && (isNrMmwave() || isAdditionalNrAdvancedBand());
+ }
+
private boolean isNrMmwave() {
return mPhone.getServiceState().getNrFrequencyRange()
== ServiceState.FREQUENCY_RANGE_MMWAVE;
}
+ private boolean isAdditionalNrAdvancedBand() {
+ List<PhysicalChannelConfig> physicalChannelConfigList =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ if (ArrayUtils.isEmpty(mAdditionalNrAdvancedBandsList)
+ || physicalChannelConfigList == null) {
+ return false;
+ }
+ for (PhysicalChannelConfig item : physicalChannelConfigList) {
+ if (item.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR
+ && ArrayUtils.contains(mAdditionalNrAdvancedBandsList, item.getBand())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isNrAdvancedCapable() {
+ if (mNrAdvancedCapablePcoId > 0) {
+ return mIsNrAdvancedAllowedByPco;
+ }
+ return true;
+ }
+
private boolean isLte(int rat) {
return rat == TelephonyManager.NETWORK_TYPE_LTE
|| rat == TelephonyManager.NETWORK_TYPE_LTE_CA;
@@ -991,6 +1174,20 @@ public class NetworkTypeController extends StateMachine {
return mPhysicalLinkState == DcController.PHYSICAL_LINK_ACTIVE;
}
+ private int getPhysicalLinkStateFromPhysicalChannelConfig() {
+ List<PhysicalChannelConfig> physicalChannelConfigList =
+ mPhone.getServiceStateTracker().getPhysicalChannelConfigList();
+ return (physicalChannelConfigList == null || physicalChannelConfigList.isEmpty())
+ ? DcController.PHYSICAL_LINK_NOT_ACTIVE : DcController.PHYSICAL_LINK_ACTIVE;
+ }
+
+ private int getDataNetworkType() {
+ NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
+ : nri.getAccessNetworkTechnology();
+ }
+
private String getEventName(int event) {
try {
return sEvents[event];
@@ -999,6 +1196,10 @@ public class NetworkTypeController extends StateMachine {
}
}
+ private boolean isUsingPhysicalChannelConfigForRrcDetection() {
+ return mIsPhysicalChannelConfig16Supported && !mIsUsingUserDataForRrcDetection;
+ }
+
protected void log(String s) {
Rlog.d(TAG, "[" + mPhone.getPhoneId() + "] " + s);
}
@@ -1016,7 +1217,8 @@ public class NetworkTypeController extends StateMachine {
+ ", mIsSecondaryTimerActive=" + mIsSecondaryTimerActive
+ ", mPrimaryTimerState=" + mPrimaryTimerState
+ ", mSecondaryTimerState=" + mSecondaryTimerState
- + ", mPreviousState=" + mPreviousState;
+ + ", mPreviousState=" + mPreviousState
+ + ", misNrAdvanced=" + isNrAdvanced();
}
@Override
@@ -1032,9 +1234,16 @@ public class NetworkTypeController extends StateMachine {
pw.println("mIsPhysicalChannelConfigOn=" + mIsPhysicalChannelConfigOn);
pw.println("mIsPrimaryTimerActive=" + mIsPrimaryTimerActive);
pw.println("mIsSecondaryTimerActive=" + mIsSecondaryTimerActive);
+ pw.println("mIsTimerRestEnabledForLegacyStateRRCIdle="
+ + mIsTimerResetEnabledForLegacyStateRRCIdle);
+ pw.println("mLtePlusThresholdBandwidth=" + mLtePlusThresholdBandwidth);
pw.println("mPrimaryTimerState=" + mPrimaryTimerState);
pw.println("mSecondaryTimerState=" + mSecondaryTimerState);
pw.println("mPreviousState=" + mPreviousState);
+ pw.println("mPhysicalLinkState=" + mPhysicalLinkState);
+ pw.println("mAdditionalNrAdvancedBandsList="
+ + Arrays.toString(mAdditionalNrAdvancedBandsList));
+ pw.println("mNrAdvancedCapablePcoId=" + mNrAdvancedCapablePcoId);
pw.decreaseIndent();
pw.flush();
}
diff --git a/src/java/com/android/internal/telephony/NitzData.java b/src/java/com/android/internal/telephony/NitzData.java
index f508d9e5a4..20f9e6a933 100644
--- a/src/java/com/android/internal/telephony/NitzData.java
+++ b/src/java/com/android/internal/telephony/NitzData.java
@@ -21,8 +21,11 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACK
import com.android.internal.annotations.VisibleForTesting;
import com.android.telephony.Rlog;
-import java.util.Calendar;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
import java.util.TimeZone;
+import java.util.regex.Pattern;
/**
* Represents NITZ data. Various static methods are provided to help with parsing and interpretation
@@ -39,6 +42,8 @@ public final class NitzData {
/* Time stamp after 19 January 2038 is not supported under 32 bit */
private static final int MAX_NITZ_YEAR = 2037;
+ private static final Pattern NITZ_SPLIT_PATTERN = Pattern.compile("[/:,+-]");
+
// Stored For logging / debugging only.
private final String mOriginalString;
@@ -70,13 +75,7 @@ public final class NitzData {
// tz, dt are in number of quarter-hours
try {
- /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
- * offset as well (which we won't worry about until later) */
- Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
- c.clear();
- c.set(Calendar.DST_OFFSET, 0);
-
- String[] nitzSubs = nitz.split("[/:,+-]");
+ String[] nitzSubs = NITZ_SPLIT_PATTERN.split(nitz);
int year = 2000 + Integer.parseInt(nitzSubs[0]);
if (year > MAX_NITZ_YEAR) {
@@ -85,23 +84,18 @@ public final class NitzData {
}
return null;
}
- c.set(Calendar.YEAR, year);
-
- // month is 0 based!
- int month = Integer.parseInt(nitzSubs[1]) - 1;
- c.set(Calendar.MONTH, month);
+ int month = Integer.parseInt(nitzSubs[1]);
int date = Integer.parseInt(nitzSubs[2]);
- c.set(Calendar.DATE, date);
-
int hour = Integer.parseInt(nitzSubs[3]);
- c.set(Calendar.HOUR, hour);
-
int minute = Integer.parseInt(nitzSubs[4]);
- c.set(Calendar.MINUTE, minute);
-
int second = Integer.parseInt(nitzSubs[5]);
- c.set(Calendar.SECOND, second);
+
+ /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone
+ * offset as well (which we won't worry about until later) */
+ long epochMillis = LocalDateTime.of(year, month, date, hour, minute, second)
+ .toInstant(ZoneOffset.UTC)
+ .toEpochMilli();
// The offset received from NITZ is the offset to add to get current local time.
boolean sign = (nitz.indexOf('-') == -1);
@@ -119,7 +113,7 @@ public final class NitzData {
}
// As a special extension, the Android emulator appends the name of
- // the host computer's timezone to the nitz string. this is zoneinfo
+ // the host computer's timezone to the nitz string. This is zoneinfo
// timezone name of the form Area!Location or Area!Location!SubLocation
// so we need to convert the ! into /
TimeZone zone = null;
@@ -127,8 +121,7 @@ public final class NitzData {
String tzname = nitzSubs[8].replace('!', '/');
zone = TimeZone.getTimeZone(tzname);
}
- return new NitzData(nitz, totalUtcOffsetMillis, dstAdjustmentMillis,
- c.getTimeInMillis(), zone);
+ return new NitzData(nitz, totalUtcOffsetMillis, dstAdjustmentMillis, epochMillis, zone);
} catch (RuntimeException ex) {
Rlog.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz + " ex=" + ex);
return null;
@@ -227,12 +220,10 @@ public final class NitzData {
if (!mOriginalString.equals(nitzData.mOriginalString)) {
return false;
}
- if (mDstOffset != null ? !mDstOffset.equals(nitzData.mDstOffset)
- : nitzData.mDstOffset != null) {
+ if (!Objects.equals(mDstOffset, nitzData.mDstOffset)) {
return false;
}
- return mEmulatorHostTimeZone != null ? mEmulatorHostTimeZone
- .equals(nitzData.mEmulatorHostTimeZone) : nitzData.mEmulatorHostTimeZone == null;
+ return Objects.equals(mEmulatorHostTimeZone, nitzData.mEmulatorHostTimeZone);
}
@Override
@@ -240,7 +231,7 @@ public final class NitzData {
int result = mOriginalString.hashCode();
result = 31 * result + mZoneOffset;
result = 31 * result + (mDstOffset != null ? mDstOffset.hashCode() : 0);
- result = 31 * result + (int) (mCurrentTimeMillis ^ (mCurrentTimeMillis >>> 32));
+ result = 31 * result + Long.hashCode(mCurrentTimeMillis);
result = 31 * result + (mEmulatorHostTimeZone != null ? mEmulatorHostTimeZone.hashCode()
: 0);
return result;
diff --git a/src/java/com/android/internal/telephony/OemHookIndication.java b/src/java/com/android/internal/telephony/OemHookIndication.java
deleted file mode 100644
index 122a70e8dd..0000000000
--- a/src/java/com/android/internal/telephony/OemHookIndication.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.hardware.radio.deprecated.V1_0.IOemHookIndication;
-import android.os.AsyncResult;
-
-import java.util.ArrayList;
-
-import static com.android.internal.telephony.RILConstants.RIL_UNSOL_OEM_HOOK_RAW;
-
-/**
- * Class containing oem hook indication callbacks
- */
-public class OemHookIndication extends IOemHookIndication.Stub {
- RIL mRil;
-
- public OemHookIndication(RIL ril) {
- mRil = ril;
- }
-
- /**
- * @param indicationType RadioIndicationType
- * @param data Data sent by oem
- */
- public void oemHookRaw(int indicationType, ArrayList<Byte> data) {
- mRil.processIndication(indicationType);
-
- byte[] response = RIL.arrayListToPrimitiveArray(data);
- if (RIL.RILJ_LOGD) {
- mRil.unsljLogvRet(RIL_UNSOL_OEM_HOOK_RAW,
- com.android.internal.telephony.uicc.IccUtils.bytesToHexString(response));
- }
-
- if (mRil.mUnsolOemHookRawRegistrant != null) {
- mRil.mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, response, null));
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/OemHookResponse.java b/src/java/com/android/internal/telephony/OemHookResponse.java
deleted file mode 100644
index 0afeac8d39..0000000000
--- a/src/java/com/android/internal/telephony/OemHookResponse.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.hardware.radio.deprecated.V1_0.IOemHookResponse;
-import android.hardware.radio.V1_0.RadioError;
-import android.hardware.radio.V1_0.RadioResponseInfo;
-
-import java.util.ArrayList;
-
-/**
- * Class containing oem hook response callbacks
- */
-public class OemHookResponse extends IOemHookResponse.Stub {
- RIL mRil;
-
- public OemHookResponse(RIL ril) {
- mRil = ril;
- }
-
- /**
- * @param responseInfo Response info struct containing response type, serial no. and error
- * @param data Data returned by oem
- */
- public void sendRequestRawResponse(RadioResponseInfo responseInfo, ArrayList<Byte> data) {
- RILRequest rr = mRil.processResponse(responseInfo);
-
- if (rr != null) {
- byte[] ret = null;
- if (responseInfo.error == RadioError.NONE) {
- ret = RIL.arrayListToPrimitiveArray(data);
- RadioResponse.sendMessageResponse(rr.mResult, ret);
- }
- mRil.processResponseDone(rr, responseInfo, ret);
- }
- }
-
- /**
- * @param responseInfo Response info struct containing response type, serial no. and error
- * @param data Data returned by oem
- */
- public void sendRequestStringsResponse(RadioResponseInfo responseInfo, ArrayList<String> data) {
- RadioResponse.responseStringArrayList(mRil, responseInfo, data);
- }
-}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index ca080c8e1d..bce4f2171a 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -23,10 +23,12 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -41,13 +43,13 @@ import android.sysprop.TelephonyProperties;
import android.telecom.VideoProfile;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.DataFailureCause;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.ClientRequestStats;
import android.telephony.ImsiEncryptionInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.PhoneStateListener;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseDataConnectionState;
@@ -65,6 +67,7 @@ import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.SparseArray;
+import android.util.Xml;
import com.android.ims.ImsCall;
import com.android.ims.ImsConfig;
@@ -75,9 +78,11 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.dataconnection.DataConnectionReasons;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
+import com.android.internal.telephony.metrics.SmsStats;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -89,12 +94,21 @@ import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UsimServiceTable;
import com.android.internal.telephony.util.TelephonyUtils;
+import com.android.internal.util.XmlUtils;
import com.android.telephony.Rlog;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -103,6 +117,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* (<em>Not for SDK use</em>)
@@ -135,7 +150,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
public static final String DATA_DISABLED_ON_BOOT_KEY = "disabled_on_boot_key";
// Key used to read/write data_roaming_is_user_setting pref
- public static final String DATA_ROAMING_IS_USER_SETTING_KEY = "data_roaming_is_user_setting_key";
+ public static final String DATA_ROAMING_IS_USER_SETTING_KEY =
+ "data_roaming_is_user_setting_key";
// Default value when there has been no last emergency SMS time recorded yet.
private static final int EMERGENCY_SMS_NO_TIME_RECORDED = -1;
@@ -172,7 +188,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
protected static final int EVENT_RUIM_RECORDS_LOADED = 22;
protected static final int EVENT_NV_READY = 23;
private static final int EVENT_SET_ENHANCED_VP = 24;
- protected static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 25;
+ @VisibleForTesting
+ public static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 25;
protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 26;
protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 27;
// other
@@ -183,7 +200,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
// Single Radio Voice Call Continuity
@VisibleForTesting
protected static final int EVENT_SRVCC_STATE_CHANGED = 31;
- private static final int EVENT_INITIATE_SILENT_REDIAL = 32;
+ @VisibleForTesting
+ public static final int EVENT_INITIATE_SILENT_REDIAL = 32;
private static final int EVENT_RADIO_NOT_AVAILABLE = 33;
private static final int EVENT_UNSOL_OEM_HOOK_RAW = 34;
protected static final int EVENT_GET_RADIO_CAPABILITY = 35;
@@ -213,8 +231,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
protected static final int EVENT_REAPPLY_UICC_APPS_ENABLEMENT_DONE = 56;
protected static final int EVENT_REGISTRATION_FAILED = 57;
protected static final int EVENT_BARRING_INFO_CHANGED = 58;
+ protected static final int EVENT_LINK_CAPACITY_CHANGED = 59;
+ protected static final int EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION = 60;
- protected static final int EVENT_LAST = EVENT_BARRING_INFO_CHANGED;
+ protected static final int EVENT_LAST = EVENT_RESET_CARRIER_KEY_IMSI_ENCRYPTION;
// For shared prefs.
private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -273,7 +293,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/* Instance Variables */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public CommandsInterface mCi;
protected int mVmCount = 0;
private boolean mDnsCheckDisabled;
@@ -303,14 +323,14 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
// Variable to cache the video capability. When RAT changes, we lose this info and are unable
// to recover from the state. We cache it and notify listeners when they register.
protected boolean mIsVideoCapable = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected UiccController mUiccController = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public SmsStorageMonitor mSmsStorageMonitor;
public SmsUsageMonitor mSmsUsageMonitor;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected AtomicReference<UiccCardApplication> mUiccApplication =
new AtomicReference<UiccCardApplication>();
TelephonyTester mTelephonyTester;
@@ -324,10 +344,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
// Used for identify the carrier of current subscription
protected CarrierResolver mCarrierResolver;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected int mPhoneId;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Phone mImsPhone = null;
private final AtomicReference<RadioCapability> mRadioCapability =
@@ -337,6 +357,11 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
private static final boolean LCE_PULL_MODE = true;
private int mLceStatus = RILConstants.LCE_NOT_AVAILABLE;
protected TelephonyComponentFactory mTelephonyComponentFactory;
+ /**
+ * Should ALWAYS be {@code true} in production code. This is used only used in tests so that we
+ * can disable the read checks which interfer with unit testing.
+ */
+ private boolean mAreThreadChecksEnabled = true;
//IMS
/**
@@ -345,6 +370,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* TODO: Replace this with a proper exception; {@link CallStateException} doesn't make sense.
*/
public static final String CS_FALLBACK = "cs_fallback";
+
+ // Used for retry over cs for supplementary services
+ public static final String CS_FALLBACK_SS = "cs_fallback_ss";
+
/**
* @deprecated Use {@link android.telephony.ims.ImsManager#EXTRA_WFC_REGISTRATION_FAILURE_TITLE}
* instead.
@@ -409,7 +438,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
private Looper mLooper; /* to insure registrants are in correct thread*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final Context mContext;
/**
@@ -417,16 +446,26 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* state change notification. DefaultPhoneNotifier is
* used here unless running we're inside a unit test.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected PhoneNotifier mNotifier;
protected SimulatedRadioControl mSimulatedRadioControl;
- private boolean mUnitTestMode;
private Map<Integer, Long> mAllowedNetworkTypesForReasons = new HashMap<>();
- private final CarrierPrivilegesTracker mCarrierPrivilegesTracker;
+ private static final String ALLOWED_NETWORK_TYPES_TEXT_USER = "user";
+ private static final String ALLOWED_NETWORK_TYPES_TEXT_POWER = "power";
+ private static final String ALLOWED_NETWORK_TYPES_TEXT_CARRIER = "carrier";
+ private static final String ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G = "enable_2g";
+ private static final int INVALID_ALLOWED_NETWORK_TYPES = -1;
+ protected boolean mIsCarrierNrSupported = false;
+ protected boolean mIsAllowedNetworkTypesLoadedFromDb = false;
+ private boolean mUnitTestMode;
+ private CarrierPrivilegesTracker mCarrierPrivilegesTracker = null;
protected VoiceCallSessionStats mVoiceCallSessionStats;
+ protected SmsStats mSmsStats;
+
+ protected LinkBandwidthEstimator mLinkBandwidthEstimator;
public IccRecords getIccRecords() {
return mIccRecords.get();
@@ -449,7 +488,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Retrieves Nai for phones. Returns null if Nai is not set.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getNai(){
return null;
}
@@ -480,7 +519,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* Set a system property, unless we're in unit test mode
*/
// CAF_MSIM TODO this need to be replated with TelephonyManager API ?
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getSystemProperty(String property, String defValue) {
if(getUnitTestMode()) {
return null;
@@ -549,8 +588,6 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mIsVoiceCapable = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
.isVoiceCapable();
- mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(mLooper, this, mContext);
-
/**
* Some RIL's don't always send RIL_UNSOL_CALL_RING so it needs
* to be generated locally. Ideally all ring tones should be loops
@@ -568,6 +605,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mCallRingDelay = TelephonyProperties.call_ring_delay().orElse(3000);
Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
+ // Initialize SMS stats
+ mSmsStats = new SmsStats(this);
+
if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
return;
}
@@ -583,6 +623,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mSimActivationTracker = mTelephonyComponentFactory
.inject(SimActivationTracker.class.getName())
.makeSimActivationTracker(this);
+ mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(mLooper, this, mContext);
if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) {
mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
}
@@ -702,23 +743,22 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
break;
case EVENT_INITIATE_SILENT_REDIAL:
- Rlog.d(LOG_TAG, "Event EVENT_INITIATE_SILENT_REDIAL Received");
+ Rlog.i(LOG_TAG, "Event EVENT_INITIATE_SILENT_REDIAL Received");
ar = (AsyncResult) msg.obj;
if ((ar.exception == null) && (ar.result != null)) {
- String dialString = (String) ar.result;
+ SilentRedialParam result = (SilentRedialParam) ar.result;
+ String dialString = result.dialString;
+ int causeCode = result.causeCode;
+ DialArgs dialArgs = result.dialArgs;
if (TextUtils.isEmpty(dialString)) return;
try {
- Connection cn = dialInternal(dialString, new DialArgs.Builder().build());
- Rlog.d(LOG_TAG, "Notify redial connection changed cn: " + cn);
- if (mImsPhone != null) {
- // Don't care it is null or not.
- mImsPhone.notifyRedialConnectionChanged(cn);
- }
+ Connection cn = dialInternal(dialString, dialArgs);
+ Rlog.i(LOG_TAG, "Notify redial connection changed cn: " + cn);
+ notifyRedialConnectionChanged(cn);
} catch (CallStateException e) {
- Rlog.e(LOG_TAG, "silent redial failed: " + e);
- if (mImsPhone != null) {
- mImsPhone.notifyRedialConnectionChanged(null);
- }
+ Rlog.e(LOG_TAG, "Notify redial connection changed - silent redial failed: "
+ + e);
+ notifyRedialConnectionChanged(null);
}
}
break;
@@ -1696,6 +1736,17 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
+ * Version of notifyServiceStateChangedP which allows us to specify the subId. This is used when
+ * we send out a final ServiceState update when a phone's subId becomes invalid.
+ */
+ protected void notifyServiceStateChangedPForSubId(ServiceState ss, int subId) {
+ AsyncResult ar = new AsyncResult(null, ss, null);
+ mServiceStateRegistrants.notifyRegistrants(ar);
+
+ mNotifier.notifyServiceStateForSubId(this, ss, subId);
+ }
+
+ /**
* If this is a simulated phone interface, returns a SimulatedRadioControl.
* @return SimulatedRadioControl if this is a simulated interface;
* otherwise, null.
@@ -1713,29 +1764,15 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* the thread that originally obtained this Phone instance.
*/
private void checkCorrectThread(Handler h) {
+ if (!mAreThreadChecksEnabled) {
+ return;
+ }
if (h.getLooper() != mLooper) {
throw new RuntimeException(
"com.android.internal.telephony.Phone must be used from within one thread");
}
}
- private @TelephonyManager.NetworkTypeBitMask long getAllowedNetworkTypes() {
- long allowedNetworkTypes = TelephonyManager.getAllNetworkTypesBitmask();
- if (SubscriptionController.getInstance() != null) {
- String result = SubscriptionController.getInstance().getSubscriptionProperty(
- getSubId(),
- SubscriptionManager.ALLOWED_NETWORK_TYPES);
-
- if (result != null) {
- try {
- allowedNetworkTypes = Long.parseLong(result);
- } catch (NumberFormatException err) {
- Rlog.e(LOG_TAG, "allowedNetworkTypes NumberFormat exception");
- }
- }
- }
- return allowedNetworkTypes;
- }
/**
* Set the properties by matching the carrier string in
* a string-array resource
@@ -1776,7 +1813,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Retrieves the IccFileHandler of the Phone instance
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IccFileHandler getIccFileHandler(){
UiccCardApplication uiccApplication = mUiccApplication.get();
IccFileHandler fh;
@@ -1810,12 +1847,23 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Retrieves the ServiceStateTracker of the phone instance.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ServiceStateTracker getServiceStateTracker() {
return null;
}
/**
+ * Check whether the radio is off for thermal reason.
+ *
+ * @return {@code true} only if thermal mitigation is one of the reason for which radio is off.
+ */
+ public boolean isRadioOffForThermalMitigation() {
+ ServiceStateTracker sst = getServiceStateTracker();
+ return sst != null && sst.getRadioPowerOffReasons()
+ .contains(Phone.RADIO_POWER_REASON_THERMAL);
+ }
+
+ /**
* Retrieves the EmergencyNumberTracker of the phone instance.
*/
public EmergencyNumberTracker getEmergencyNumberTracker() {
@@ -1825,7 +1873,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Get call tracker
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public CallTracker getCallTracker() {
return null;
}
@@ -1954,9 +2002,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
- * @return the current cell location if known
+ * Returns the current CellIdentity if known
*/
- public CellIdentity getCellIdentity() {
+ public CellIdentity getCurrentCellIdentity() {
return getServiceStateTracker().getCellIdentity();
}
@@ -1997,7 +2045,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* Retrieves manually selected network info.
*/
public String getManualNetworkSelectionPlmn() {
- return "";
+ return null;
}
@@ -2053,16 +2101,6 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
editor.apply();
}
- private @TelephonyManager.NetworkTypeBitMask long getAllowedNetworkTypesForAllReasons() {
- long allowedNetworkTypes = TelephonyManager.getAllNetworkTypesBitmask();
- synchronized (mAllowedNetworkTypesForReasons) {
- for (long networkTypes: mAllowedNetworkTypesForReasons.values()) {
- allowedNetworkTypes = allowedNetworkTypes & networkTypes;
- }
- }
- return allowedNetworkTypes;
- }
-
public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {
setCallForwardingIndicatorInSharedPref(enable);
IccRecords r = getIccRecords();
@@ -2123,15 +2161,24 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
- * Query the CDMA roaming preference setting
+ * Query the CDMA roaming preference setting.
*
- * @param response is callback message to report one of CDMA_RM_*
+ * @param response is callback message to report one of TelephonyManager#CDMA_ROAMING_MODE_*
*/
public void queryCdmaRoamingPreference(Message response) {
mCi.queryCdmaRoamingPreference(response);
}
/**
+ * Get the CDMA subscription mode setting.
+ *
+ * @param response is callback message to report one of TelephonyManager#CDMA_SUBSCRIPTION_*
+ */
+ public void queryCdmaSubscriptionMode(Message response) {
+ mCi.getCdmaSubscriptionSource(response);
+ }
+
+ /**
* Get current signal strength. No change notification available on this
* interface. Use <code>PhoneStateNotifier</code> or an equivalent.
* An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
@@ -2160,8 +2207,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
- * Requests to set the CDMA roaming preference
- * @param cdmaRoamingType one of CDMA_RM_*
+ * Requests to set the CDMA roaming preference
+ * @param cdmaRoamingType one of TelephonyManager#CDMA_ROAMING_MODE_*
* @param response is callback message
*/
public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
@@ -2169,21 +2216,67 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
- * Requests to set the CDMA subscription mode
- * @param cdmaSubscriptionType one of CDMA_SUBSCRIPTION_*
+ * Requests to set the CDMA subscription mode
+ * @param cdmaSubscriptionType one of TelephonyManager#CDMA_SUBSCRIPTION_*
* @param response is callback message
*/
- public void setCdmaSubscription(int cdmaSubscriptionType, Message response) {
+ public void setCdmaSubscriptionMode(int cdmaSubscriptionType, Message response) {
mCi.setCdmaSubscriptionSource(cdmaSubscriptionType, response);
}
/**
* Get the effective allowed network types on the device.
+ *
* @return effective network type
*/
- public @TelephonyManager.NetworkTypeBitMask long getEffectiveAllowedNetworkTypes() {
- long allowedNetworkTypes = getAllowedNetworkTypes();
- return allowedNetworkTypes & getAllowedNetworkTypesForAllReasons();
+ private @TelephonyManager.NetworkTypeBitMask long getEffectiveAllowedNetworkTypes() {
+ long allowedNetworkTypes = TelephonyManager.getAllNetworkTypesBitmask();
+ synchronized (mAllowedNetworkTypesForReasons) {
+ for (long networkTypes : mAllowedNetworkTypesForReasons.values()) {
+ allowedNetworkTypes = allowedNetworkTypes & networkTypes;
+ }
+ }
+ if (!mIsCarrierNrSupported) {
+ allowedNetworkTypes &= ~TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+ }
+ logd("SubId" + getSubId() + ",getEffectiveAllowedNetworkTypes: "
+ + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes));
+ return allowedNetworkTypes;
+ }
+
+ /**
+ * Notify the latest allowed network types changed.
+ */
+ public void notifyAllowedNetworkTypesChanged(
+ @TelephonyManager.AllowedNetworkTypesReason int reason) {
+ logd("SubId" + getSubId() + ",notifyAllowedNetworkTypesChanged: reason: " + reason
+ + " value:" + TelephonyManager.convertNetworkTypeBitmaskToString(
+ getAllowedNetworkTypes(reason)));
+ mNotifier.notifyAllowedNetworkTypesChanged(this, reason, getAllowedNetworkTypes(reason));
+ }
+
+ /**
+ * Is E-UTRA-NR Dual Connectivity enabled
+ */
+ public void isNrDualConnectivityEnabled(Message message, WorkSource workSource) {
+ mCi.isNrDualConnectivityEnabled(message, workSource);
+ }
+
+ /**
+ * Enable/Disable E-UTRA-NR Dual Connectivity
+ * @param nrDualConnectivityState expected NR dual connectivity state
+ * This can be passed following states
+ * <ol>
+ * <li>Enable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_ENABLE}
+ * <li>Disable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE}
+ * <li>Disable NR dual connectivity and force secondary cell to be released
+ * {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
+ * </ol>
+ */
+ public void setNrDualConnectivityState(
+ @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState,
+ Message message, WorkSource workSource) {
+ mCi.setNrDualConnectivityState(nrDualConnectivityState, message, workSource);
}
/**
@@ -2193,59 +2286,177 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
*/
public @TelephonyManager.NetworkTypeBitMask long getAllowedNetworkTypes(
@TelephonyManager.AllowedNetworkTypesReason int reason) {
+ long allowedNetworkTypes;
+ long defaultAllowedNetworkTypes = RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.PREFERRED_NETWORK_MODE);
+
+ if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
+ throw new IllegalArgumentException("AllowedNetworkTypes NumberFormat exception");
+ }
+
synchronized (mAllowedNetworkTypesForReasons) {
- switch (reason) {
- case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
- return mAllowedNetworkTypesForReasons.getOrDefault(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER,
- TelephonyManager.getAllNetworkTypesBitmask());
- default:
- Rlog.e(LOG_TAG, "Invalid allowed network type reason: " + reason);
- return TelephonyManager.getAllNetworkTypesBitmask();
+ allowedNetworkTypes = mAllowedNetworkTypesForReasons.getOrDefault(
+ reason,
+ defaultAllowedNetworkTypes);
+ }
+ if (!mIsCarrierNrSupported
+ && reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER) {
+ allowedNetworkTypes = updateAllowedNetworkTypeForCarrierWithCarrierConfig();
+ }
+ logd("SubId" + getSubId() + ",get allowed network types "
+ + convertAllowedNetworkTypeMapIndexToDbName(reason)
+ + ": value = " + TelephonyManager.convertNetworkTypeBitmaskToString(
+ allowedNetworkTypes));
+ return allowedNetworkTypes;
+ }
+
+ /**
+ * Loads the allowed network type from subscription database.
+ */
+ public void loadAllowedNetworksFromSubscriptionDatabase() {
+ mIsAllowedNetworkTypesLoadedFromDb = false;
+ // Try to load ALLOWED_NETWORK_TYPES from SIMINFO.
+ if (SubscriptionController.getInstance() == null) {
+ return;
+ }
+
+ String result = SubscriptionController.getInstance().getSubscriptionProperty(
+ getSubId(),
+ SubscriptionManager.ALLOWED_NETWORK_TYPES);
+ if (result == null) {
+ return;
+ }
+
+ logd("SubId" + getSubId() + ",load allowed network types : value = " + result);
+ Map<Integer, Long> oldAllowedNetworkTypes = new HashMap<>(mAllowedNetworkTypesForReasons);
+ mAllowedNetworkTypesForReasons.clear();
+ try {
+ // Format: "REASON=VALUE,REASON2=VALUE2"
+ for (String pair : result.trim().split(",")) {
+ String[] networkTypesValues = (pair.trim().toLowerCase()).split("=");
+ if (networkTypesValues.length != 2) {
+ Rlog.e(LOG_TAG, "Invalid ALLOWED_NETWORK_TYPES from DB, value = " + pair);
+ continue;
+ }
+ int key = convertAllowedNetworkTypeDbNameToMapIndex(networkTypesValues[0]);
+ long value = Long.parseLong(networkTypesValues[1]);
+ if (key != INVALID_ALLOWED_NETWORK_TYPES
+ && value != INVALID_ALLOWED_NETWORK_TYPES) {
+ synchronized (mAllowedNetworkTypesForReasons) {
+ mAllowedNetworkTypesForReasons.put(key, value);
+ }
+ if (!oldAllowedNetworkTypes.containsKey(key)
+ || oldAllowedNetworkTypes.get(key) != value) {
+ if (oldAllowedNetworkTypes.containsKey(key)) {
+ oldAllowedNetworkTypes.remove(key);
+ }
+ notifyAllowedNetworkTypesChanged(key);
+ }
+ }
}
+ mIsAllowedNetworkTypesLoadedFromDb = true;
+ } catch (NumberFormatException e) {
+ Rlog.e(LOG_TAG, "allowedNetworkTypes NumberFormat exception" + e);
+ }
+
+ for (int key : oldAllowedNetworkTypes.keySet()) {
+ notifyAllowedNetworkTypesChanged(key);
+ }
+ }
+
+ private int convertAllowedNetworkTypeDbNameToMapIndex(String name) {
+ switch (name) {
+ case ALLOWED_NETWORK_TYPES_TEXT_USER:
+ return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER;
+ case ALLOWED_NETWORK_TYPES_TEXT_POWER:
+ return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER;
+ case ALLOWED_NETWORK_TYPES_TEXT_CARRIER:
+ return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER;
+ case ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G:
+ return TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G;
+ default:
+ return INVALID_ALLOWED_NETWORK_TYPES;
}
}
+ private String convertAllowedNetworkTypeMapIndexToDbName(int reason) {
+ switch (reason) {
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER:
+ return ALLOWED_NETWORK_TYPES_TEXT_USER;
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
+ return ALLOWED_NETWORK_TYPES_TEXT_POWER;
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
+ return ALLOWED_NETWORK_TYPES_TEXT_CARRIER;
+ case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
+ return ALLOWED_NETWORK_TYPES_TEXT_ENABLE_2G;
+ default:
+ return Integer.toString(INVALID_ALLOWED_NETWORK_TYPES);
+ }
+ }
+
+ private @TelephonyManager.NetworkTypeBitMask long
+ updateAllowedNetworkTypeForCarrierWithCarrierConfig() {
+ long defaultAllowedNetworkTypes = RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.PREFERRED_NETWORK_MODE);
+ long allowedNetworkTypes;
+ synchronized (mAllowedNetworkTypesForReasons) {
+ allowedNetworkTypes = mAllowedNetworkTypesForReasons.getOrDefault(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
+ defaultAllowedNetworkTypes);
+ }
+ if (mIsCarrierNrSupported) {
+ return allowedNetworkTypes;
+ }
+ allowedNetworkTypes = allowedNetworkTypes & ~TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+
+ logd("Allowed network types for 'carrier' reason is changed by carrier config = "
+ + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes));
+ return allowedNetworkTypes;
+ }
+
/**
* Requests to set the allowed network types for a specific reason
+ *
* @param reason reason to configure allowed network type
* @param networkTypes one of the network types
*/
public void setAllowedNetworkTypes(@TelephonyManager.AllowedNetworkTypesReason int reason,
- @TelephonyManager.NetworkTypeBitMask long networkTypes) {
+ @TelephonyManager.NetworkTypeBitMask long networkTypes, Message response) {
+ int subId = getSubId();
+ if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
+ loge("setAllowedNetworkTypes: Invalid allowed network type reason: " + reason);
+ return;
+ }
+ if (!SubscriptionManager.isUsableSubscriptionId(subId)
+ || !mIsAllowedNetworkTypesLoadedFromDb) {
+ loge("setAllowedNetworkTypes: no sim or network type is not loaded. SubscriptionId: "
+ + subId + ", isNetworkTypeLoaded" + mIsAllowedNetworkTypesLoadedFromDb);
+ return;
+ }
+ String mapAsString = "";
synchronized (mAllowedNetworkTypesForReasons) {
- switch (reason) {
- case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER:
- mAllowedNetworkTypesForReasons.put(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER, networkTypes);
- break;
- default:
- Rlog.e(LOG_TAG, "Invalid allowed network type reason: " + reason);
- break;
- }
+ mAllowedNetworkTypesForReasons.put(reason, networkTypes);
+ mapAsString = mAllowedNetworkTypesForReasons.keySet().stream()
+ .map(key -> convertAllowedNetworkTypeMapIndexToDbName(key) + "="
+ + mAllowedNetworkTypesForReasons.get(key))
+ .collect(Collectors.joining(","));
}
+ SubscriptionManager.setSubscriptionProperty(subId,
+ SubscriptionManager.ALLOWED_NETWORK_TYPES,
+ mapAsString);
+ logd("setAllowedNetworkTypes: SubId" + subId + ",setAllowedNetworkTypes " + mapAsString);
+
+ updateAllowedNetworkTypes(response);
+ notifyAllowedNetworkTypesChanged(reason);
}
- /**
- * Requests to set the preferred network type for searching and registering
- * (CS/PS domain, RAT, and operation mode)
- * @param networkType one of NT_*_TYPE
- * @param response is callback message
- */
- @UnsupportedAppUsage
- public void setPreferredNetworkType(int networkType, Message response) {
- // Only set preferred network types to that which the modem supports
+ protected void updateAllowedNetworkTypes(Message response) {
int modemRaf = getRadioAccessFamily();
- int rafFromType = RadioAccessFamily.getRafFromNetworkType(networkType);
-
- long allowedNetworkTypes = getAllowedNetworkTypes();
- if (modemRaf == RadioAccessFamily.RAF_UNKNOWN
- || rafFromType == RadioAccessFamily.RAF_UNKNOWN) {
+ if (modemRaf == RadioAccessFamily.RAF_UNKNOWN) {
Rlog.d(LOG_TAG, "setPreferredNetworkType: Abort, unknown RAF: "
- + modemRaf + " " + rafFromType);
+ + modemRaf);
if (response != null) {
CommandException ex;
-
ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
AsyncResult.forMessage(response, null, ex);
response.sendToTarget();
@@ -2253,29 +2464,24 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
return;
}
- int filteredRaf = (int) (rafFromType & modemRaf & allowedNetworkTypes
- & getAllowedNetworkTypesForAllReasons());
- int filteredType = RadioAccessFamily.getNetworkTypeFromRaf(filteredRaf);
- long powerAllowedNetworkTypes = getAllowedNetworkTypes(
- TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER);
- Rlog.d(LOG_TAG, "setPreferredNetworkType: networkType = " + networkType
- + " modemRaf = " + modemRaf
- + " rafFromType = " + rafFromType
- + " allowedNetworkTypes = " + allowedNetworkTypes
- + " power allowedNetworkTypes = " + powerAllowedNetworkTypes
- + " filteredType = " + filteredType);
+ int filteredRaf = (int) (modemRaf & getEffectiveAllowedNetworkTypes());
- mCi.setPreferredNetworkType(filteredType, response);
+ logd("setAllowedNetworkTypes: modemRafBitMask = " + modemRaf
+ + " ,modemRaf = " + TelephonyManager.convertNetworkTypeBitmaskToString(modemRaf)
+ + " ,filteredRafBitMask = " + filteredRaf
+ + " ,filteredRaf = " + TelephonyManager.convertNetworkTypeBitmaskToString(
+ filteredRaf));
+ mCi.setAllowedNetworkTypesBitmap(filteredRaf, response);
mPreferredNetworkTypeRegistrants.notifyRegistrants();
}
/**
- * Query the preferred network type setting
+ * Query the allowed network types bitmask setting
*
- * @param response is callback message to report one of NT_*_TYPE
+ * @param response is callback message to report network types bitmask
*/
- public void getPreferredNetworkType(Message response) {
- mCi.getPreferredNetworkType(response);
+ public void getAllowedNetworkTypesBitmask(Message response) {
+ mCi.getAllowedNetworkTypesBitmap(response);
}
/**
@@ -2302,11 +2508,11 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Get the cached value of the preferred network type setting
*/
- public int getCachedPreferredNetworkType() {
+ public int getCachedAllowedNetworkTypesBitmask() {
if (mCi != null && mCi instanceof BaseCommands) {
- return ((BaseCommands) mCi).mPreferredNetworkType;
+ return ((BaseCommands) mCi).mAllowedNetworkTypesBitmask;
} else {
- return RILConstants.PREFERRED_NETWORK_MODE;
+ return RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE);
}
}
@@ -2517,11 +2723,32 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mCi.nvResetConfig(2 /* erase NV */, response);
}
+ /**
+ * Erase data saved in the SharedPreference. Used for network reset
+ *
+ */
+ public boolean eraseDataInSharedPreferences() {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ Rlog.d(LOG_TAG, "Erase all data saved in SharedPreferences");
+ editor.clear();
+ return editor.commit();
+ }
+
public void setSystemSelectionChannels(List<RadioAccessSpecifier> specifiers,
Message response) {
mCi.setSystemSelectionChannels(specifiers, response);
}
+ /**
+ * Get which bands the modem's background scan is acting on.
+ *
+ * @param response Callback message.
+ */
+ public void getSystemSelectionChannels(Message response) {
+ mCi.getSystemSelectionChannels(response);
+ }
+
public void notifyDataActivity() {
mNotifier.notifyDataActivity(this);
}
@@ -2536,19 +2763,11 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/** Send notification with an updated PreciseDataConnectionState to a single data connection */
- public void notifyDataConnection(String apnType) {
- mNotifier.notifyDataConnection(this, apnType, getPreciseDataConnectionState(apnType));
- }
-
- /** Send notification with an updated PreciseDataConnectionState to all data connections */
- public void notifyAllActiveDataConnections() {
- String types[] = getActiveApnTypes();
- for (String apnType : types) {
- mNotifier.notifyDataConnection(this, apnType, getPreciseDataConnectionState(apnType));
- }
+ public void notifyDataConnection(PreciseDataConnectionState state) {
+ mNotifier.notifyDataConnection(this, state);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void notifyOtaspChanged(int otaspMode) {
mOtaspRegistrants.notifyRegistrants(new AsyncResult(null, otaspMode, null));
}
@@ -2612,8 +2831,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/** Notify {@link PhysicalChannelConfig} changes. */
- public void notifyPhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
+ public void notifyPhysicalChannelConfig(List<PhysicalChannelConfig> configs) {
mPhysicalChannelConfigRegistrants.notifyRegistrants(new AsyncResult(null, configs, null));
+ mNotifier.notifyPhysicalChannelConfig(this, configs);
}
public List<PhysicalChannelConfig> getPhysicalChannelConfigList() {
@@ -2632,16 +2852,23 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mNotifier.notifyEmergencyNumberList(this);
}
- /** Notify the outgoing call {@link EmergencyNumber} changes. */
- public void notifyOutgoingEmergencyCall(EmergencyNumber emergencyNumber) {
- mNotifier.notifyOutgoingEmergencyCall(this, emergencyNumber);
- }
-
/** Notify the outgoing Sms {@link EmergencyNumber} changes. */
public void notifyOutgoingEmergencySms(EmergencyNumber emergencyNumber) {
mNotifier.notifyOutgoingEmergencySms(this, emergencyNumber);
}
+ /** Notify the data enabled changes. */
+ public void notifyDataEnabled(boolean enabled,
+ @TelephonyManager.DataEnabledReason int reason) {
+ mNotifier.notifyDataEnabled(this, enabled, reason);
+ }
+
+ /** Notify link capacity estimate has changed. */
+ public void notifyLinkCapacityEstimateChanged(
+ List<LinkCapacityEstimate> linkCapacityEstimateList) {
+ mNotifier.notifyLinkCapacityEstimateChanged(this, linkCapacityEstimateList);
+ }
+
/**
* @return true if a mobile originating emergency call is active
*/
@@ -2693,7 +2920,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mEcmCanceledForEmergency = isCanceled;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static int getVideoState(Call call) {
int videoState = VideoProfile.STATE_AUDIO_ONLY;
Connection conn = call.getEarliestConnection();
@@ -2898,6 +3125,13 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
+ * @return {@code true} if data is suspended.
+ */
+ public boolean isDataSuspended() {
+ return false;
+ }
+
+ /**
* send burst DTMF tone, it can send the string as single character or multiple character
* ignore if there is no active call or not valid digits string.
* Valid digit means only includes characters ISO-LATIN characters 0-9, *, #
@@ -3002,7 +3236,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Returns true if OTA Service Provisioning needs to be performed.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean needsOtaServiceProvisioning() {
return false;
}
@@ -3321,6 +3555,98 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
return activeApnTypes.toArray(new String[activeApnTypes.size()]);
}
+
+ /**
+ * Location to an updatable file listing carrier provisioning urls.
+ * An example:
+ *
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <provisioningUrls>
+ * <provisioningUrl mcc="310" mnc="4">http://myserver.com/foo?mdn=%3$s&amp;iccid=%1$s&amp;imei=%2$s</provisioningUrl>
+ * </provisioningUrls>
+ */
+ private static final String PROVISIONING_URL_PATH =
+ "/data/misc/radio/provisioning_urls.xml";
+ private final File mProvisioningUrlFile = new File(PROVISIONING_URL_PATH);
+
+ /** XML tag for root element. */
+ private static final String TAG_PROVISIONING_URLS = "provisioningUrls";
+ /** XML tag for individual url */
+ private static final String TAG_PROVISIONING_URL = "provisioningUrl";
+ /** XML attribute for mcc */
+ private static final String ATTR_MCC = "mcc";
+ /** XML attribute for mnc */
+ private static final String ATTR_MNC = "mnc";
+
+ private String getProvisioningUrlBaseFromFile() {
+ XmlPullParser parser;
+ final Configuration config = mContext.getResources().getConfiguration();
+
+ try (FileReader fileReader = new FileReader(mProvisioningUrlFile)) {
+ parser = Xml.newPullParser();
+ parser.setInput(fileReader);
+ XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS);
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+
+ final String element = parser.getName();
+ if (element == null) break;
+
+ if (element.equals(TAG_PROVISIONING_URL)) {
+ String mcc = parser.getAttributeValue(null, ATTR_MCC);
+ try {
+ if (mcc != null && Integer.parseInt(mcc) == config.mcc) {
+ String mnc = parser.getAttributeValue(null, ATTR_MNC);
+ if (mnc != null && Integer.parseInt(mnc) == config.mnc) {
+ parser.next();
+ if (parser.getEventType() == XmlPullParser.TEXT) {
+ return parser.getText();
+ }
+ }
+ }
+ } catch (NumberFormatException e) {
+ Rlog.e(LOG_TAG, "Exception in getProvisioningUrlBaseFromFile: " + e);
+ }
+ }
+ }
+ return null;
+ } catch (FileNotFoundException e) {
+ Rlog.e(LOG_TAG, "Carrier Provisioning Urls file not found");
+ } catch (XmlPullParserException e) {
+ Rlog.e(LOG_TAG, "Xml parser exception reading Carrier Provisioning Urls file: " + e);
+ } catch (IOException e) {
+ Rlog.e(LOG_TAG, "I/O exception reading Carrier Provisioning Urls file: " + e);
+ }
+ return null;
+ }
+
+ /**
+ * Get the mobile provisioning url.
+ */
+ public String getMobileProvisioningUrl() {
+ String url = getProvisioningUrlBaseFromFile();
+ if (TextUtils.isEmpty(url)) {
+ url = mContext.getResources().getString(R.string.mobile_provisioning_url);
+ Rlog.d(LOG_TAG, "getMobileProvisioningUrl: url from resource =" + url);
+ } else {
+ Rlog.d(LOG_TAG, "getMobileProvisioningUrl: url from File =" + url);
+ }
+ // Populate the iccid, imei and phone number in the provisioning url.
+ if (!TextUtils.isEmpty(url)) {
+ String phoneNumber = getLine1Number();
+ if (TextUtils.isEmpty(phoneNumber)) {
+ phoneNumber = "0000000000";
+ }
+ url = String.format(url,
+ getIccSerialNumber() /* ICCID */,
+ getDeviceId() /* IMEI */,
+ phoneNumber /* Phone number */);
+ }
+
+ return url;
+ }
+
/**
* Check if there are matching tethering (i.e DUN) for the carrier.
* @return true if there is a matching DUN APN.
@@ -3523,7 +3849,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* Return an interface to retrieve the ISIM records for IMS, if available.
* @return the interface to retrieve the ISIM records, or null if not supported
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IsimRecords getIsimRecords() {
Rlog.e(LOG_TAG, "getIsimRecords() is only supported on LTE devices");
return null;
@@ -3534,7 +3860,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* {@link #getLine1Number()}. For CDMA phones, {@link #getLine1Number()} returns
* the MDN, so this method is provided to return the MSISDN on CDMA/LTE phones.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getMsisdn() {
return null;
}
@@ -3551,32 +3877,14 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* exists at this interface -- use
* {@link android.telephony.PhoneStateListener} instead.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public PhoneConstants.DataState getDataConnectionState() {
- return getDataConnectionState(PhoneConstants.APN_TYPE_DEFAULT);
+ return getDataConnectionState(ApnSetting.TYPE_DEFAULT_STRING);
}
public void notifyCallForwardingIndicator() {
}
- /** Send a notification that a particular data connection has failed with specified cause. */
- public void notifyDataConnectionFailed(
- String apnType, String apn, @DataFailureCause int failCause) {
- mNotifier.notifyDataConnectionFailed(this, apnType, apn, failCause);
- }
-
- /**
- * Return if the current radio is LTE on CDMA. This
- * is a tri-state return value as for a period of time
- * the mode may be unknown.
- *
- * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
- * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
- */
- public int getLteOnCdmaMode() {
- return mCi.getLteOnCdmaMode();
- }
-
/**
* Sets the SIM voice message waiting indicator records.
* @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
@@ -3602,7 +3910,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* Gets the Uicc card corresponding to this phone.
* @return the UiccCard object corresponding to the phone ID.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public UiccCard getUiccCard() {
return mUiccController.getUiccCard(mPhoneId);
}
@@ -3632,7 +3940,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Return an instance of a IMS phone
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Phone getImsPhone() {
return mImsPhone;
}
@@ -3640,12 +3948,14 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
+ * @param fallback whether or not to fall back to the encryption key info stored in carrier
+ * config
* @return ImsiEncryptionInfo which includes the Key Type, the Public Key
* {@link java.security.PublicKey} and the Key Identifier.
* The keyIdentifier This is used by the server to help it locate the private key to
* decrypt the permanent identity.
*/
- public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+ public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback) {
return null;
}
@@ -3659,6 +3969,13 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
return;
}
+ /**
+ * Deletes all the keys for a given Carrier from the device keystore.
+ */
+ public void deleteCarrierInfoForImsiEncryption() {
+ return;
+ }
+
public int getCarrierId() {
return TelephonyManager.UNKNOWN_CARRIER_ID;
}
@@ -3702,7 +4019,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Return if UT capability of ImsPhone is enabled or not
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isUtEnabled() {
if (mImsPhone != null) {
return mImsPhone.isUtEnabled();
@@ -3710,7 +4027,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void dispose() {
}
@@ -3729,6 +4046,15 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/*
+ * This function is for CSFB SS. GsmCdmaPhone overrides this function.
+ */
+ public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
+ }
+
+ public void queryCLIP(Message onComplete) {
+ }
+
+ /*
* Returns the subscription id.
*/
@UnsupportedAppUsage
@@ -3746,7 +4072,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Returns the phone id.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getPhoneId() {
return mPhoneId;
}
@@ -3847,7 +4173,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
/**
* Get Wifi Calling Feature Availability
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isWifiCallingEnabled() {
Phone imsPhone = mImsPhone;
boolean isWifiCallingEnabled = false;
@@ -3868,21 +4194,31 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
if (imsPhone != null) {
isAvailable = imsPhone.isImsCapabilityAvailable(capability, regTech);
}
- Rlog.d(LOG_TAG, "isImsRegistered =" + isAvailable);
+ Rlog.d(LOG_TAG, "isImsCapabilityAvailable, capability=" + capability + ", regTech="
+ + regTech + ", isAvailable=" + isAvailable);
return isAvailable;
}
/**
* Get Volte Feature Availability
+ * @deprecated Use {@link #isVoiceOverCellularImsEnabled} instead.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
public boolean isVolteEnabled() {
+ return isVoiceOverCellularImsEnabled();
+ }
+
+ /**
+ * @return {@code true} if voice over IMS on cellular is enabled, {@code false} otherwise.
+ */
+ public boolean isVoiceOverCellularImsEnabled() {
Phone imsPhone = mImsPhone;
boolean isVolteEnabled = false;
if (imsPhone != null) {
- isVolteEnabled = imsPhone.isVolteEnabled();
+ isVolteEnabled = imsPhone.isVoiceOverCellularImsEnabled();
}
- Rlog.d(LOG_TAG, "isImsRegistered =" + isVolteEnabled);
+ Rlog.d(LOG_TAG, "isVoiceOverCellularImsEnabled=" + isVolteEnabled);
return isVolteEnabled;
}
@@ -3920,8 +4256,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
Phone imsPhone = mImsPhone;
if (imsPhone != null) {
imsPhone.getImsRegistrationState(callback);
+ } else {
+ callback.accept(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
}
- callback.accept(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
}
@@ -4021,8 +4358,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* here.
*
* @param rc the phone radio capability currently in effect for this phone.
+ * @param capabilitySwitched whether this method called after a radio capability switch
+ * completion or called when radios first become available.
*/
- public void radioCapabilityUpdated(RadioCapability rc) {
+ public void radioCapabilityUpdated(RadioCapability rc, boolean capabilitySwitched) {
// Called when radios first become available or after a capability switch
// Update the cached value
mRadioCapability.set(rc);
@@ -4032,12 +4371,19 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
com.android.internal.R.bool.skip_restoring_network_selection);
sendSubscriptionSettings(restoreSelection);
}
+
+ // When radio capability switch is done, query IMEI value and update it in Phone objects
+ // to make it in sync with the IMEI value currently used by Logical-Modem.
+ if (capabilitySwitched) {
+ mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
+ }
}
public void sendSubscriptionSettings(boolean restoreNetworkSelection) {
// Send settings down
- int type = PhoneFactory.calculatePreferredNetworkType(mContext, getSubId());
- setPreferredNetworkType(type, null);
+ if (mIsAllowedNetworkTypesLoadedFromDb) {
+ updateAllowedNetworkTypes(null);
+ }
if (restoreNetworkSelection) {
restoreSavedNetworkSelection(null);
@@ -4047,8 +4393,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
protected void setPreferredNetworkTypeIfSimLoaded() {
int subId = getSubId();
if (SubscriptionManager.isValidSubscriptionId(subId)) {
- int type = PhoneFactory.calculatePreferredNetworkType(mContext, getSubId());
- setPreferredNetworkType(type, null);
+ updateAllowedNetworkTypes(null);
}
}
@@ -4073,20 +4418,6 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
- * Determines if IMS is enabled for call.
- *
- * @return {@code true} if IMS calling is enabled.
- */
- public boolean isImsUseEnabled() {
- ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
- boolean imsUseEnabled = ((imsManager.isVolteEnabledByPlatform()
- && imsManager.isEnhanced4gLteModeSettingEnabledByUser())
- || (imsManager.isWfcEnabledByPlatform() && imsManager.isWfcEnabledByUser())
- && imsManager.isNonTtyOrTtyOnVolteEnabled());
- return imsUseEnabled;
- }
-
- /**
* Determines if the connection to IMS services are available yet.
* @return {@code true} if the connection to IMS services are available.
*/
@@ -4103,7 +4434,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
*
* @return {@code true} if video calling is enabled, {@code false} otherwise.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isVideoEnabled() {
Phone imsPhone = mImsPhone;
if (imsPhone != null) {
@@ -4136,6 +4467,21 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
+ * Control the data throttling at modem.
+ *
+ * @param result Message that will be sent back to the requester
+ * @param workSource calling Worksource
+ * @param dataThrottlingAction the DataThrottlingAction that is being requested. Defined in
+ * android.telephony.TelephonyManger.
+ * @param completionWindowMillis milliseconds in which data throttling action has to be
+ * achieved.
+ */
+ public void setDataThrottling(Message result, WorkSource workSource,
+ int dataThrottlingAction, long completionWindowMillis) {
+ mCi.setDataThrottling(result, workSource, dataThrottlingAction, completionWindowMillis);
+ }
+
+ /**
* Set allowed carriers
*/
public void setAllowedCarriers(CarrierRestrictionRules carrierRestrictionRules,
@@ -4184,7 +4530,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
public boolean areAllDataDisconnected() {
if (mTransportManager != null) {
for (int transport : mTransportManager.getAvailableTransports()) {
- if (getDcTracker(transport) != null && !getDcTracker(transport).isDisconnected()) {
+ if (getDcTracker(transport) != null
+ && !getDcTracker(transport).areAllDataDisconnected()) {
return false;
}
}
@@ -4196,7 +4543,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mAllDataDisconnectedRegistrants.addUnique(h, what, null);
if (mTransportManager != null) {
for (int transport : mTransportManager.getAvailableTransports()) {
- if (getDcTracker(transport) != null && !getDcTracker(transport).isDisconnected()) {
+ if (getDcTracker(transport) != null
+ && !getDcTracker(transport).areAllDataDisconnected()) {
getDcTracker(transport).registerForAllDataDisconnected(
this, EVENT_ALL_DATA_DISCONNECTED);
}
@@ -4296,8 +4644,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* - {@link android.telephony.TelephonyManager#CARD_POWER_UP}
* - {@link android.telephony.TelephonyManager#CARD_POWER_UP_PASS_THROUGH}
**/
- public void setSimPowerState(int state, WorkSource workSource) {
- mCi.setSimCardPower(state, null, workSource);
+ public void setSimPowerState(int state, Message result, WorkSource workSource) {
+ mCi.setSimCardPower(state, result, workSource);
}
public void setCarrierTestOverride(String mccmnc, String imsi, String iccid, String gid1,
@@ -4315,10 +4663,10 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
boolean isEmergencyCallOnly = false;
for (Phone phone : PhoneFactory.getPhones()) {
if (phone != null) {
- ServiceState ss = phone.getServiceStateTracker().getServiceState();
- // One of the phone is in service, hence the device is not emergency call only.
- if (ss.getState() == ServiceState.STATE_IN_SERVICE
- || ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) {
+ ServiceStateTracker sst = phone.getServiceStateTracker();
+ ServiceState ss = sst.getServiceState();
+ // Combined reg state is in service, hence the device is not emergency call only.
+ if (sst.getCombinedRegState(ss) == ServiceState.STATE_IN_SERVICE) {
return false;
}
isEmergencyCallOnly |= ss.isEmergencyOnly();
@@ -4389,11 +4737,96 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mVoiceCallSessionStats = voiceCallSessionStats;
}
+ /** Returns the {@link SmsStats} for this phone ID. */
+ public SmsStats getSmsStats() {
+ return mSmsStats;
+ }
+
+ /** Sets the {@link SmsStats} mock for this phone ID during unit testing. */
+ @VisibleForTesting
+ public void setSmsStats(SmsStats smsStats) {
+ mSmsStats = smsStats;
+ }
+
/** @hide */
public CarrierPrivilegesTracker getCarrierPrivilegesTracker() {
return mCarrierPrivilegesTracker;
}
+ public boolean useSsOverIms(Message onComplete) {
+ return false;
+ }
+
+ /**
+ * Check if device is idle. Device is idle when it is not in high power consumption mode.
+ *
+ * @see DeviceStateMonitor#shouldEnableHighPowerConsumptionIndications()
+ *
+ * @return true if device is idle
+ */
+ public boolean isDeviceIdle() {
+ DeviceStateMonitor dsm = getDeviceStateMonitor();
+ if (dsm == null) {
+ Rlog.e(LOG_TAG, "isDeviceIdle: DeviceStateMonitor is null");
+ return false;
+ }
+ return !dsm.shouldEnableHighPowerConsumptionIndications();
+ }
+
+ /**
+ * Get notified when device idleness state has changed
+ *
+ * @param isIdle true if the new state is idle
+ */
+ public void notifyDeviceIdleStateChanged(boolean isIdle) {
+ ServiceStateTracker sst = getServiceStateTracker();
+ if (sst == null) {
+ Rlog.e(LOG_TAG, "notifyDeviceIdleStateChanged: SST is null");
+ return;
+ }
+ sst.onDeviceIdleStateChanged(isIdle);
+ }
+
+ /**
+ * Returns a list of the equivalent home PLMNs (EF_EHPLMN) from the USIM app.
+ *
+ * @return A list of equivalent home PLMNs. Returns an empty list if EF_EHPLMN is empty or
+ * does not exist on the SIM card.
+ */
+ public @NonNull List<String> getEquivalentHomePlmns() {
+ return Collections.emptyList();
+ }
+
+ /**
+ *
+ * @return
+ */
+ public @NonNull List<String> getDataServicePackages() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Return link bandwidth estimator
+ */
+ public LinkBandwidthEstimator getLinkBandwidthEstimator() {
+ return mLinkBandwidthEstimator;
+ }
+
+ /**
+ * Request to get the current slicing configuration including URSP rules and
+ * NSSAIs (configured, allowed and rejected).
+ */
+ public void getSlicingConfig(Message response) {
+ mCi.getSlicingConfig(response);
+ }
+
+ /**
+ * Returns the InboundSmsHandler object for this phone
+ */
+ public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) {
+ return null;
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Phone: subId=" + getSubId());
pw.println(" mPhoneId=" + mPhoneId);
@@ -4570,6 +5003,12 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
pw.println("++++++++++++++++++++++++++++++++");
}
+ if (getLinkBandwidthEstimator() != null) {
+ pw.println("LinkBandwidthEstimator:");
+ getLinkBandwidthEstimator().dump(fd, pw, args);
+ pw.println("++++++++++++++++++++++++++++++++");
+ }
+
pw.println("Phone Local Log: ");
if (mLocalLog != null) {
try {
@@ -4581,4 +5020,29 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
pw.println("++++++++++++++++++++++++++++++++");
}
}
+
+ private void logd(String s) {
+ Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s);
+ }
+
+ private void logi(String s) {
+ Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s);
+ }
+
+ private static String pii(String s) {
+ return Rlog.pii(LOG_TAG, s);
+ }
+
+ /**
+ * Used in unit tests to disable the thread checks. Should not be used otherwise.
+ * @param enabled {@code true} if thread checks are enabled, {@code false} otherwise.
+ */
+ @VisibleForTesting
+ public void setAreThreadChecksEnabled(boolean enabled) {
+ mAreThreadChecksEnabled = enabled;
+ }
}
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 02fea56327..d92f96dd46 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -62,7 +62,9 @@ public class PhoneConfigurationManager {
private PhoneCapability mStaticCapability;
private final RadioConfig mRadioConfig;
private final Handler mHandler;
- private final Phone[] mPhones;
+ // mPhones is obtained from PhoneFactory and can have phones corresponding to inactive modems as
+ // well. That is, the array size can be 2 even if num of active modems is 1.
+ private Phone[] mPhones;
private final Map<Integer, Boolean> mPhoneStatusMap;
private MockableInterface mMi = new MockableInterface();
private TelephonyManager mTelephonyManager;
@@ -93,7 +95,7 @@ public class PhoneConfigurationManager {
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//initialize with default, it'll get updated when RADIO is ON/AVAILABLE
mStaticCapability = getDefaultCapability();
- mRadioConfig = RadioConfig.getInstance(mContext);
+ mRadioConfig = RadioConfig.getInstance();
mHandler = new ConfigManagerHandler();
mPhoneStatusMap = new HashMap<>();
@@ -101,14 +103,16 @@ public class PhoneConfigurationManager {
mPhones = PhoneFactory.getPhones();
+ for (Phone phone : mPhones) {
+ registerForRadioState(phone);
+ }
+ }
+
+ private void registerForRadioState(Phone phone) {
if (!StorageManager.inCryptKeeperBounce()) {
- for (Phone phone : mPhones) {
- phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone);
- }
+ phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone);
} else {
- for (Phone phone : mPhones) {
- phone.mCi.registerForOn(mHandler, Phone.EVENT_RADIO_ON, phone);
- }
+ phone.mCi.registerForOn(mHandler, Phone.EVENT_RADIO_ON, phone);
}
}
@@ -182,6 +186,7 @@ public class PhoneConfigurationManager {
} else {
log(msg.what + " failure. Not getting phone capability." + ar.exception);
}
+ break;
}
}
}
@@ -307,7 +312,7 @@ public class PhoneConfigurationManager {
}
public int getNumberOfModemsWithSimultaneousDataConnections() {
- return mStaticCapability.maxActiveData;
+ return mStaticCapability.getMaxActiveDataSubscriptions();
}
private void notifyCapabilityChanged() {
@@ -322,7 +327,7 @@ public class PhoneConfigurationManager {
*/
public void switchMultiSimConfig(int numOfSims) {
log("switchMultiSimConfig: with numOfSims = " + numOfSims);
- if (getStaticPhoneCapability().logicalModemList.size() < numOfSims) {
+ if (getStaticPhoneCapability().getLogicalModemList().size() < numOfSims) {
log("switchMultiSimConfig: Phone is not capable of enabling "
+ numOfSims + " sims, exiting!");
return;
@@ -347,6 +352,7 @@ public class PhoneConfigurationManager {
}
private void onMultiSimConfigChanged(int numOfActiveModems) {
+ int oldNumOfActiveModems = getPhoneCount();
setMultiSimProperties(numOfActiveModems);
if (isRebootRequiredForModemConfigChange()) {
@@ -357,10 +363,35 @@ public class PhoneConfigurationManager {
log("onMultiSimConfigChanged: Rebooting is not required.");
mMi.notifyPhoneFactoryOnMultiSimConfigChanged(mContext, numOfActiveModems);
broadcastMultiSimConfigChange(numOfActiveModems);
- // Register to RIL service if needed.
- for (int i = 0; i < mPhones.length; i++) {
- Phone phone = mPhones[i];
- phone.mCi.onSlotActiveStatusChange(SubscriptionManager.isValidPhoneId(i));
+ boolean subInfoCleared = false;
+ // if numOfActiveModems is decreasing, deregister old RILs
+ // eg if we are going from 2 phones to 1 phone, we need to deregister RIL for the
+ // second phone. This loop does nothing if numOfActiveModems is increasing.
+ for (int phoneId = numOfActiveModems; phoneId < oldNumOfActiveModems; phoneId++) {
+ SubscriptionController.getInstance().clearSubInfoRecord(phoneId);
+ subInfoCleared = true;
+ mPhones[phoneId].mCi.onSlotActiveStatusChange(
+ SubscriptionManager.isValidPhoneId(phoneId));
+ }
+ if (subInfoCleared) {
+ // This triggers update of default subs. This should be done asap after
+ // setMultiSimProperties() to avoid (minimize) duration for which default sub can be
+ // invalid and can map to a non-existent phone.
+ // If forexample someone calls a TelephonyManager API on default sub after
+ // setMultiSimProperties() and before onSubscriptionsChanged() below -- they can be
+ // using an invalid sub, which can map to a non-existent phone and can cause an
+ // exception (see b/163582235).
+ MultiSimSettingController.getInstance().onPhoneRemoved();
+ }
+ // old phone objects are not needed now; mPhones can be updated
+ mPhones = PhoneFactory.getPhones();
+ // if numOfActiveModems is increasing, register new RILs
+ // eg if we are going from 1 phone to 2 phones, we need to register RIL for the second
+ // phone. This loop does nothing if numOfActiveModems is decreasing.
+ for (int phoneId = oldNumOfActiveModems; phoneId < numOfActiveModems; phoneId++) {
+ Phone phone = mPhones[phoneId];
+ registerForRadioState(phone);
+ phone.mCi.onSlotActiveStatusChange(SubscriptionManager.isValidPhoneId(phoneId));
}
}
}
@@ -467,4 +498,12 @@ public class PhoneConfigurationManager {
private static void log(String s) {
Rlog.d(LOG_TAG, s);
}
+
+ private static void loge(String s) {
+ Rlog.e(LOG_TAG, s);
+ }
+
+ private static void loge(String s, Exception ex) {
+ Rlog.e(LOG_TAG, s, ex);
+ }
}
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 12a37cd991..d51fa91a1c 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -28,12 +28,14 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.LocalServerSocket;
+import android.os.Build;
import android.os.HandlerThread;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.telephony.AnomalyReporter;
+import android.telephony.RadioAccessFamily;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.LocalLog;
@@ -98,6 +100,7 @@ public class PhoneFactory {
static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
private static MetricsCollector sMetricsCollector;
+ private static RadioInterfaceCapabilityController sRadioHalCapabilities;
//***** Class Methods
@@ -169,10 +172,24 @@ public class PhoneFactory {
networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;
Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
- sCommandsInterfaces[i] = new RIL(context, networkModes[i],
+ sCommandsInterfaces[i] = new RIL(context,
+ RadioAccessFamily.getRafFromNetworkType(networkModes[i]),
cdmaSubscription, i);
}
+ if (numPhones > 0) {
+ final RadioConfig radioConfig = RadioConfig.make(context,
+ sCommandsInterfaces[0].getHalVersion());
+ sRadioHalCapabilities = RadioInterfaceCapabilityController.init(radioConfig,
+ sCommandsInterfaces[0]);
+ } else {
+ // There is no command interface to go off of
+ final RadioConfig radioConfig = RadioConfig.make(context, HalVersion.UNKNOWN);
+ sRadioHalCapabilities = RadioInterfaceCapabilityController.init(
+ radioConfig, null);
+ }
+
+
// Instantiate UiccController so that all other classes can just
// call getInstance()
sUiccController = UiccController.make(context);
@@ -220,7 +237,7 @@ public class PhoneFactory {
sSubInfoRecordUpdater = TelephonyComponentFactory.getInstance().inject(
SubscriptionInfoUpdater.class.getName()).
makeSubscriptionInfoUpdater(pfhandlerThread.
- getLooper(), context, sCommandsInterfaces);
+ getLooper(), context, SubscriptionController.getInstance());
// Only bring up IMS if the device supports having an IMS stack.
if (context.getPackageManager().hasSystemFeature(
@@ -282,7 +299,8 @@ public class PhoneFactory {
int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
for (int i = prevActiveModemCount; i < activeModemCount; i++) {
- sCommandsInterfaces[i] = new RIL(context, RILConstants.PREFERRED_NETWORK_MODE,
+ sCommandsInterfaces[i] = new RIL(context, RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.PREFERRED_NETWORK_MODE),
cdmaSubscription, i);
sPhones[i] = createPhone(context, i);
if (context.getPackageManager().hasSystemFeature(
@@ -348,7 +366,7 @@ public class PhoneFactory {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static Phone[] getPhones() {
synchronized (sLockProxyPhones) {
if (!sMadeDefaults) {
@@ -402,37 +420,27 @@ public class PhoneFactory {
}
/**
- * Returns the preferred network type that should be set in the modem.
+ * Returns the preferred network type bitmask that should be set in the modem.
*
- * @param context The current {@link Context}.
- * @return the preferred network mode that should be set.
+ * @param phoneId The phone's id.
+ * @return the preferred network mode bitmask that should be set.
*/
// TODO: Fix when we "properly" have TelephonyDevController/SubscriptionController ..
- @UnsupportedAppUsage
- public static int calculatePreferredNetworkType(Context context, int phoneSubId) {
- int networkType = android.provider.Settings.Global.getInt(context.getContentResolver(),
- android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
- -1 /* invalid network mode */);
- Rlog.d(LOG_TAG, "calculatePreferredNetworkType: phoneSubId = " + phoneSubId +
- " networkType = " + networkType);
-
- if (networkType == -1) {
- networkType = RILConstants.PREFERRED_NETWORK_MODE;
- try {
- networkType = TelephonyManager.getIntAtIndex(context.getContentResolver(),
- android.provider.Settings.Global.PREFERRED_NETWORK_MODE,
- SubscriptionController.getInstance().getPhoneId(phoneSubId));
- } catch (SettingNotFoundException retrySnfe) {
- Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for "
- + "Settings.Global.PREFERRED_NETWORK_MODE");
- }
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public static int calculatePreferredNetworkType(int phoneId) {
+ if (getPhone(phoneId) == null) {
+ Rlog.d(LOG_TAG, "Invalid phoneId return default network mode ");
+ return RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE);
}
-
+ int networkType = (int) getPhone(phoneId).getAllowedNetworkTypes(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER);
+ Rlog.d(LOG_TAG, "calculatePreferredNetworkType: phoneId = " + phoneId + " networkType = "
+ + networkType);
return networkType;
}
/* Gets the default subscription */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static int getDefaultSubscription() {
return SubscriptionController.getInstance().getDefaultSubId();
}
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 168506c3ff..a61954bcd8 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -16,21 +16,27 @@
package com.android.internal.telephony;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
+import android.os.WorkSource;
import android.telecom.VideoProfile;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.PhoneConstants.DataState;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -71,14 +77,37 @@ public interface PhoneInternalInterface {
public static class DialArgs {
public static class Builder<T extends Builder<T>> {
protected UUSInfo mUusInfo;
+ protected int mClirMode = CommandsInterface.CLIR_DEFAULT;
+ protected boolean mIsEmergency;
protected int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
protected Bundle mIntentExtras;
+ protected int mEccCategory = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+
+ public static DialArgs.Builder from(DialArgs dialArgs) {
+ return new DialArgs.Builder()
+ .setUusInfo(dialArgs.uusInfo)
+ .setClirMode(dialArgs.clirMode)
+ .setIsEmergency(dialArgs.isEmergency)
+ .setVideoState(dialArgs.videoState)
+ .setIntentExtras(dialArgs.intentExtras)
+ .setEccCategory(dialArgs.eccCategory);
+ }
public T setUusInfo(UUSInfo uusInfo) {
mUusInfo = uusInfo;
return (T) this;
}
+ public T setClirMode(int clirMode) {
+ mClirMode = clirMode;
+ return (T) this;
+ }
+
+ public T setIsEmergency(boolean isEmergency) {
+ mIsEmergency = isEmergency;
+ return (T) this;
+ }
+
public T setVideoState(int videoState) {
mVideoState = videoState;
return (T) this;
@@ -89,6 +118,11 @@ public interface PhoneInternalInterface {
return (T) this;
}
+ public T setEccCategory(int eccCategory) {
+ mEccCategory = eccCategory;
+ return (T) this;
+ }
+
public PhoneInternalInterface.DialArgs build() {
return new DialArgs(this);
}
@@ -97,16 +131,28 @@ public interface PhoneInternalInterface {
/** The UUSInfo */
public final UUSInfo uusInfo;
+ /** The CLIR mode to use */
+ public final int clirMode;
+
+ /** Indicates emergency call */
+ public final boolean isEmergency;
+
/** The desired video state for the connection. */
public final int videoState;
/** The extras from the original CALL intent. */
public final Bundle intentExtras;
+ /** Indicates emergency service category */
+ public final int eccCategory;
+
protected DialArgs(Builder b) {
this.uusInfo = b.mUusInfo;
+ this.clirMode = b.mClirMode;
+ this.isEmergency = b.mIsEmergency;
this.videoState = b.mVideoState;
this.intentExtras = b.mIntentExtras;
+ this.eccCategory = b.mEccCategory;
}
}
@@ -158,6 +204,19 @@ public interface PhoneInternalInterface {
static final String REASON_CSS_INDICATOR_CHANGED = "cssIndicatorChanged";
static final String REASON_RELEASED_BY_CONNECTIVITY_SERVICE = "releasedByConnectivityService";
static final String REASON_DATA_ENABLED_OVERRIDE = "dataEnabledOverride";
+ static final String REASON_IWLAN_DATA_SERVICE_DIED = "iwlanDataServiceDied";
+ static final String REASON_VCN_REQUESTED_TEARDOWN = "vcnRequestedTeardown";
+ static final String REASON_DATA_UNTHROTTLED = "dataUnthrottled";
+
+ // Reasons for Radio being powered off
+ int RADIO_POWER_REASON_USER = 0;
+ int RADIO_POWER_REASON_THERMAL = 1;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"RADIO_POWER_REASON_"},
+ value = {
+ RADIO_POWER_REASON_USER,
+ RADIO_POWER_REASON_THERMAL})
+ public @interface RadioPowerReason {}
// Used for band mode selection methods
static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
@@ -181,7 +240,7 @@ public interface PhoneInternalInterface {
static final int BM_US_2500M = RILConstants.BAND_MODE_USA_2500M;
static final int BM_NUM_BAND_MODES = 19; //Total number of band modes
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int PREFERRED_NT_MODE = RILConstants.PREFERRED_NETWORK_MODE;
// Used for CDMA roaming mode
@@ -510,7 +569,10 @@ public interface PhoneInternalInterface {
* <strong>Note: </strong>This request is asynchronous.
* getServiceState().getState() will not change immediately after this call.
* registerForServiceStateChanged() to find out when the
- * request is complete.
+ * request is complete. This will set the reason for radio power state as {@link
+ * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
+ * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+ * for details.
*
* @param power true means "on", false means "off".
*/
@@ -519,6 +581,14 @@ public interface PhoneInternalInterface {
}
/**
+ * Sets the radio power on for a test emergency number.
+ *
+ * @param isSelectedPhoneForEmergencyCall true means this phone / modem is selected to place
+ * emergency call after turning power on.
+ */
+ default void setRadioPowerOnForTestEmergencyCall(boolean isSelectedPhoneForEmergencyCall) {}
+
+ /**
* Sets the radio power on/off state with option to specify whether it's for emergency call
* (off is sometimes called "airplane mode"). Current state can be gotten via
* {@link #getServiceState()}.{@link
@@ -526,7 +596,10 @@ public interface PhoneInternalInterface {
* <strong>Note: </strong>This request is asynchronous.
* getServiceState().getState() will not change immediately after this call.
* registerForServiceStateChanged() to find out when the
- * request is complete.
+ * request is complete. This will set the reason for radio power state as {@link
+ * #RADIO_POWER_REASON_USER}. This will not guarantee that the requested radio power state will
+ * actually be set. See {@link #setRadioPowerForReason(boolean, boolean, boolean, boolean, int)}
+ * for details.
*
* @param power true means "on", false means "off".
* @param forEmergencyCall true means the purpose of turning radio power on is for emergency
@@ -540,7 +613,57 @@ public interface PhoneInternalInterface {
* power on again with forEmergencyCall being false.
*/
default void setRadioPower(boolean power, boolean forEmergencyCall,
- boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {}
+ boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
+ setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
+ RADIO_POWER_REASON_USER);
+ }
+
+ /**
+ * Sets the radio power on/off state (off is sometimes
+ * called "airplane mode") for the specified reason, if possible. Current state can be gotten
+ * via {@link #getServiceState()}.{@link
+ * android.telephony.ServiceState#getState() getState()}.
+ * <strong>Note: </strong>This request is asynchronous.
+ * getServiceState().getState() will not change immediately after this call.
+ * registerForServiceStateChanged() to find out when the
+ * request is complete. Radio power will not be set if it is currently off for a reason other
+ * than the reason for which it is being turned on. However, if forEmergency call is {@code
+ * true}, it will forcefully turn radio power on.
+ *
+ * @param power true means "on", false means "off".
+ * @param reason RadioPowerReason constant defining the reason why the radio power was set.
+ */
+ default void setRadioPowerForReason(boolean power, @RadioPowerReason int reason) {
+ setRadioPowerForReason(power, false, false, false, reason);
+ }
+
+ /**
+ * Sets the radio power on/off state with option to specify whether it's for emergency call
+ * (off is sometimes called "airplane mode") and option to set the reason for setting the power
+ * state. Current state can be gotten via {@link #getServiceState()}.
+ * {@link android.telephony.ServiceState#getState() getState()}.
+ * <strong>Note: </strong>This request is asynchronous.
+ * getServiceState().getState() will not change immediately after this call.
+ * registerForServiceStateChanged() to find out when the
+ * request is complete. Radio power will not be set if it is currently off for a reason other
+ * than the reason for which it is being turned on. However, if forEmergency call is {@code
+ * true}, it will forcefully turn radio power on.
+ *
+ * @param power true means "on", false means "off".
+ * @param forEmergencyCall true means the purpose of turning radio power on is for emergency
+ * call. No effect if power is set false.
+ * @param isSelectedPhoneForEmergencyCall true means this phone / modem is selected to place
+ * emergency call after turning power on. No effect if power
+ * or forEmergency is set false.
+ * @param forceApply true means always call setRadioPower HAL API without checking against
+ * current radio power state. It's needed when: radio was powered on into
+ * emergency call mode, to exit from that mode, we set radio
+ * power on again with forEmergencyCall being false.
+ * @param reason RadioPowerReason constant defining the reason why the radio power was set.
+ */
+ default void setRadioPowerForReason(boolean power, boolean forEmergencyCall,
+ boolean isSelectedPhoneForEmergencyCall, boolean forceApply,
+ @RadioPowerReason int reason) {}
/**
* Get the line 1 phone number (MSISDN). For CDMA phones, the MDN is returned
@@ -811,8 +934,15 @@ public interface PhoneInternalInterface {
/**
* Update the ServiceState CellLocation for current network registration.
+ *
+ * @param workSource the caller to be billed for work.
*/
- void updateServiceLocation();
+ default void updateServiceLocation(WorkSource workSource) {}
+
+ /**
+ * To be deleted.
+ */
+ default void updateServiceLocation() {}
/**
* Enable location update notifications.
@@ -926,15 +1056,22 @@ public interface PhoneInternalInterface {
/**
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
+ * @param fallback whether to fall back to the encryption key stored in carrier config
* @return ImsiEncryptionInfo which includes the Key Type, the Public Key
* {@link java.security.PublicKey} and the Key Identifier.
* The keyIdentifier This is used by the server to help it locate the private key to
* decrypt the permanent identity.
*/
- public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType);
+ ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback);
/**
* Resets the Carrier Keys, by deleting them from the database and sending a download intent.
*/
public void resetCarrierKeysForImsiEncryption();
+
+ /**
+ * Return the mobile provisioning url that is used to launch a browser to allow users to manage
+ * their mobile plan.
+ */
+ String getMobileProvisioningUrl();
}
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index ea6fb1fa56..701a157f98 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -18,16 +18,19 @@ package com.android.internal.telephony;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
-import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SrvccState;
import android.telephony.BarringInfo;
import android.telephony.CallQuality;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.PhoneCapability;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager.DataEnabledReason;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
@@ -40,9 +43,20 @@ public interface PhoneNotifier {
void notifyPhoneState(Phone sender);
+ /**
+ * Notify registrants of the given phone's current ServiceState.
+ */
void notifyServiceState(Phone sender);
/**
+ * Notify registrants with a given ServiceState. Passing in the subId allows us to
+ * send a final ServiceState update when the subId for the sender phone becomes invalid
+ * @param sender
+ * @param subId
+ */
+ void notifyServiceStateForSubId(Phone sender, ServiceState ss, int subId);
+
+ /**
* Notify registrants of the current CellLocation.
*
* <p>Use CellIdentity that is Parcellable to pass AIDL; convert to CellLocation in client code.
@@ -58,8 +72,7 @@ public interface PhoneNotifier {
void notifyCallForwardingChanged(Phone sender);
/** Send a notification that the Data Connection for a particular apnType has changed */
- void notifyDataConnection(
- Phone sender, String apnType, PreciseDataConnectionState preciseState);
+ void notifyDataConnection(Phone sender, PreciseDataConnectionState preciseState);
void notifyDataActivity(Phone sender);
@@ -71,10 +84,6 @@ public interface PhoneNotifier {
void notifyImsDisconnectCause(Phone sender, ImsReasonInfo imsReasonInfo);
- /** Send a notification that a particular data connection has failed with specified cause. */
- void notifyDataConnectionFailed(Phone sender, String apnType, String apn,
- @DataFailureCause int failCause);
-
/** Send a notification that the SRVCC state has changed.*/
void notifySrvccStateChanged(Phone sender, @SrvccState int state);
@@ -98,9 +107,6 @@ public interface PhoneNotifier {
/** Notify of change to EmergencyNumberList. */
void notifyEmergencyNumberList(Phone sender);
- /** Notify of a change for Outgoing Emergency Call. */
- void notifyOutgoingEmergencyCall(Phone sender, EmergencyNumber emergencyNumber);
-
/** Notify of a change for Outgoing Emergency Sms. */
void notifyOutgoingEmergencySms(Phone sender, EmergencyNumber emergencyNumber);
@@ -113,4 +119,17 @@ public interface PhoneNotifier {
/** Notify barring info has changed */
void notifyBarringInfoChanged(Phone sender, @NonNull BarringInfo barringInfo);
+
+ /** Notify of change to PhysicalChannelConfig. */
+ void notifyPhysicalChannelConfig(Phone sender, List<PhysicalChannelConfig> configs);
+
+ /** Notify DataEnabled has changed. */
+ void notifyDataEnabled(Phone sender, boolean enabled, @DataEnabledReason int reason);
+
+ /** Notify Allowed Network Type has changed. */
+ void notifyAllowedNetworkTypesChanged(Phone sender, int reason, long allowedNetworkType);
+
+ /** Notify link capacity estimate has changed. */
+ void notifyLinkCapacityEstimateChanged(Phone sender,
+ List<LinkCapacityEstimate> linkCapacityEstimateList);
}
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 1bf46b519a..8db71d99c0 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -23,13 +23,14 @@ import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.Build;
import android.os.RemoteException;
import android.os.TelephonyServiceManager.ServiceRegisterer;
+import android.os.UserHandle;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionManager;
@@ -45,9 +46,8 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
private static final boolean DBG = true;
private static final boolean VDBG = false; // STOPSHIP if true
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final Context mContext;
- private final AppOpsManager mAppOps;
public PhoneSubInfoController(Context context) {
ServiceRegisterer phoneSubServiceRegisterer = TelephonyFrameworkInitializer
@@ -57,7 +57,6 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
phoneSubServiceRegisterer.register(this);
}
mContext = context;
- mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
}
@Deprecated
@@ -91,7 +90,7 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
String callingPackage) {
return callPhoneMethodForSubIdWithPrivilegedCheck(subId,
"getCarrierInfoForImsiEncryption",
- (phone)-> phone.getCarrierInfoForImsiEncryption(keyType));
+ (phone)-> phone.getCarrierInfoForImsiEncryption(keyType, true));
}
public void setCarrierInfoForImsiEncryption(int subId, String callingPackage,
@@ -142,7 +141,10 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
public String getSubscriberIdForSubscriber(int subId, String callingPackage,
String callingFeatureId) {
- String message = "getSubscriberId";
+ String message = "getSubscriberIdForSubscriber";
+
+ enforceCallingPackage(callingPackage, Binder.getCallingUid(), message);
+
long identity = Binder.clearCallingIdentity();
boolean isActive;
try {
@@ -254,15 +256,26 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
/**
* get Phone object based on subId.
**/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Phone getPhone(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- phoneId = 0;
+ return null;
}
return PhoneFactory.getPhone(phoneId);
}
+ private boolean enforceIccSimChallengeResponsePermission(Context context, int subId,
+ String callingPackage, String callingFeatureId, String message) {
+ if (TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(context,
+ callingPackage, callingFeatureId, message)) {
+ return true;
+ }
+ if (VDBG) log("No USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER permission.");
+ enforcePrivilegedPermissionOrCarrierPrivilege(subId, message);
+ return true;
+ }
+
/**
* Make sure caller has either read privileged phone permission or carrier privilege.
*
@@ -289,7 +302,29 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
"Requires MODIFY_PHONE_STATE");
}
- @UnsupportedAppUsage
+ /**
+ * Make sure the caller is the calling package itself
+ *
+ * @throws SecurityException if the caller is not the calling package
+ */
+ private void enforceCallingPackage(String callingPackage, int callingUid, String message) {
+ int packageUid = -1;
+ PackageManager pm = mContext.createContextAsUser(
+ UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+ if (pm != null) {
+ try {
+ packageUid = pm.getPackageUid(callingPackage, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ // packageUid is -1
+ }
+ }
+ if (packageUid != callingUid) {
+ throw new SecurityException(message + ": Package " + callingPackage
+ + " does not belong to " + callingUid);
+ }
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int getDefaultSubscription() {
return PhoneFactory.getDefaultSubscription();
}
@@ -369,8 +404,9 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
});
}
- public String getIccSimChallengeResponse(int subId, int appType, int authType, String data)
- throws RemoteException {
+ @Override
+ public String getIccSimChallengeResponse(int subId, int appType, int authType, String data,
+ String callingPackage, String callingFeatureId) throws RemoteException {
CallPhoneMethodHelper<String> toExecute = (phone)-> {
UiccCard uiccCard = phone.getUiccCard();
if (uiccCard == null) {
@@ -395,12 +431,9 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
return uiccApp.getIccRecords().getIccSimChallengeResponse(authType, data);
};
- return callPhoneMethodWithPermissionCheck(subId, null, null, "getIccSimChallengeResponse",
- toExecute,
- (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage) -> {
- enforcePrivilegedPermissionOrCarrierPrivilege(aSubId, aMessage);
- return true;
- });
+ return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId,
+ "getIccSimChallengeResponse", toExecute,
+ this::enforceIccSimChallengeResponsePermission);
}
public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage,
@@ -538,7 +571,7 @@ public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
Rlog.d(TAG, s);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loge(String s) {
Rlog.e(TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index 3e156661c5..5f312dbb9e 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -24,6 +24,9 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
import static java.util.Arrays.copyOf;
@@ -41,6 +44,7 @@ import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.TelephonyNetworkSpecifier;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -51,12 +55,19 @@ import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyRegistryManager;
import android.telephony.data.ApnSetting;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.LocalLog;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.SubscriptionController.WatchedInt;
import com.android.internal.telephony.dataconnection.ApnConfigTypeRepository;
@@ -73,7 +84,9 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CompletableFuture;
/**
@@ -162,6 +175,7 @@ public class PhoneSwitcher extends Handler {
private final LocalLog mLocalLog;
protected PhoneState[] mPhoneStates;
protected int[] mPhoneSubscriptions;
+ private boolean mIsRegisteredForImsRadioTechChange;
@VisibleForTesting
protected final CellularNetworkValidator mValidator;
private int mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
@@ -183,7 +197,7 @@ public class PhoneSwitcher extends Handler {
}
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
// How many phones (correspondingly logical modems) are allowed for PS attach. This is used
// when we specifically use setDataAllowed to initiate on-demand PS(data) attach for each phone.
protected int mMaxDataAttachModemCount;
@@ -242,7 +256,7 @@ public class PhoneSwitcher extends Handler {
private static final int EVENT_EMERGENCY_TOGGLE = 105;
private static final int EVENT_RADIO_CAPABILITY_CHANGED = 106;
private static final int EVENT_OPPT_DATA_SUB_CHANGED = 107;
- private static final int EVENT_RADIO_AVAILABLE = 108;
+ private static final int EVENT_RADIO_ON = 108;
// A call has either started or ended. If an emergency ended and DDS is overridden using
// mEmergencyOverride, start the countdown to remove the override using the message
// EVENT_REMOVE_DDS_EMERGENCY_OVERRIDE. The only exception to this is if the device moves to
@@ -266,6 +280,12 @@ public class PhoneSwitcher extends Handler {
@VisibleForTesting
public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 117;
private static final int EVENT_NETWORK_AVAILABLE = 118;
+ private static final int EVENT_PROCESS_SIM_STATE_CHANGE = 119;
+ @VisibleForTesting
+ public static final int EVENT_IMS_RADIO_TECH_CHANGED = 120;
+
+ // List of events triggers re-evaluations
+ private static final String EVALUATION_REASON_RADIO_ON = "EVENT_RADIO_ON";
// Depending on version of IRadioConfig, we need to send either RIL_REQUEST_ALLOW_DATA if it's
// 1.0, or RIL_REQUEST_SET_PREFERRED_DATA if it's 1.1 or later. So internally mHalCommandToUse
@@ -285,6 +305,9 @@ public class PhoneSwitcher extends Handler {
private Boolean mHasRegisteredDefaultNetworkChangeCallback = false;
private ConnectivityManager mConnectivityManager;
+ private int mImsRegistrationTech = REGISTRATION_TECH_NONE;
+
+ private List<Set<CommandException.Error>> mCurrentDdsSwitchFailure;
private class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
public int mExpectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -305,9 +328,42 @@ public class PhoneSwitcher extends Handler {
}
}
+ private RegistrationManager.RegistrationCallback mRegistrationCallback =
+ new RegistrationManager.RegistrationCallback() {
+ @Override
+ public void onRegistered(ImsRegistrationAttributes attributes) {
+ int imsRegistrationTech = attributes.getRegistrationTechnology();
+ if (imsRegistrationTech != mImsRegistrationTech) {
+ mImsRegistrationTech = imsRegistrationTech;
+ sendMessage(obtainMessage(EVENT_IMS_RADIO_TECH_CHANGED));
+ }
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo info) {
+ if (mImsRegistrationTech != REGISTRATION_TECH_NONE) {
+ mImsRegistrationTech = REGISTRATION_TECH_NONE;
+ sendMessage(obtainMessage(EVENT_IMS_RADIO_TECH_CHANGED));
+ }
+ }
+ };
+
private final DefaultNetworkCallback mDefaultNetworkCallback = new DefaultNetworkCallback();
/**
+ * Interface to get ImsRegistrationTech. It's a wrapper of ImsManager#getRegistrationTech,
+ * to make it mock-able in unittests.
+ */
+ public interface ImsRegTechProvider {
+ /** Get IMS registration tech. */
+ @ImsRegistrationImplBase.ImsRegistrationTech int get(Context context, int phoneId);
+ }
+
+ @VisibleForTesting
+ public ImsRegTechProvider mImsRegTechProvider =
+ (context, phoneId) -> ImsManager.getInstance(context, phoneId).getRegistrationTech();
+
+ /**
* Method to get singleton instance.
*/
public static PhoneSwitcher getInstance() {
@@ -326,6 +382,27 @@ public class PhoneSwitcher extends Handler {
return sPhoneSwitcher;
}
+ /**
+ * Whether this phone IMS registration is on its original network. This result impacts
+ * whether we want to do DDS switch to the phone having voice call.
+ * If it's registered on IWLAN or cross SIM in multi-SIM case, return false. Otherwise,
+ * return true.
+ */
+ private boolean isImsOnOriginalNetwork(Phone phone) {
+ if (phone == null) return false;
+ int phoneId = phone.getPhoneId();
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) return false;
+
+ int imsRegTech = mImsRegTechProvider.get(mContext, phoneId);
+ // If IMS is registered on IWLAN or cross SIM, return false.
+ boolean isOnOriginalNetwork = (imsRegTech != REGISTRATION_TECH_IWLAN)
+ && (imsRegTech != REGISTRATION_TECH_CROSS_SIM);
+ if (!isOnOriginalNetwork) {
+ log("IMS call on IWLAN or cross SIM. Call will be ignored for DDS switch");
+ }
+ return isOnOriginalNetwork;
+ }
+
private boolean isPhoneInVoiceCallChanged() {
int oldPhoneIdInVoiceCall = mPhoneIdInVoiceCall;
// If there's no active call, the value will become INVALID_PHONE_INDEX
@@ -333,7 +410,8 @@ public class PhoneSwitcher extends Handler {
// subscription.
mPhoneIdInVoiceCall = SubscriptionManager.INVALID_PHONE_INDEX;
for (Phone phone : PhoneFactory.getPhones()) {
- if (isPhoneInVoiceCall(phone) || isPhoneInVoiceCall(phone.getImsPhone())) {
+ if (isPhoneInVoiceCall(phone) || (isPhoneInVoiceCall(phone.getImsPhone())
+ && isImsOnOriginalNetwork(phone))) {
mPhoneIdInVoiceCall = phone.getPhoneId();
break;
}
@@ -348,6 +426,35 @@ public class PhoneSwitcher extends Handler {
}
}
+ private void registerForImsRadioTechChange(Context context, int phoneId) {
+ try {
+ ImsManager.getInstance(context, phoneId).addRegistrationCallback(
+ mRegistrationCallback, this::post);
+ mIsRegisteredForImsRadioTechChange = true;
+ } catch (ImsException imsException) {
+ mIsRegisteredForImsRadioTechChange = false;
+ }
+ }
+
+ private void registerForImsRadioTechChange() {
+ // register for radio tech change to listen to radio tech handover.
+ if (!mIsRegisteredForImsRadioTechChange) {
+ for (int i = 0; i < mActiveModemCount; i++) {
+ registerForImsRadioTechChange(mContext, i);
+ }
+ }
+ }
+
+ private void evaluateIfDataSwitchIsNeeded(String reason) {
+ if (onEvaluate(REQUESTS_UNCHANGED, reason)) {
+ logDataSwitchEvent(mPreferredDataSubId.get(),
+ TelephonyEvent.EventState.EVENT_STATE_START,
+ DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+ registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(),
+ DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
+ }
+ }
+
@VisibleForTesting
public PhoneSwitcher(int maxActivePhones, Context context, Looper looper) {
super(looper);
@@ -359,9 +466,14 @@ public class PhoneSwitcher extends Handler {
mLocalLog = new LocalLog(MAX_LOCAL_LOG_LINES);
mSubscriptionController = SubscriptionController.getInstance();
- mRadioConfig = RadioConfig.getInstance(mContext);
+ mRadioConfig = RadioConfig.getInstance();
mValidator = CellularNetworkValidator.getInstance();
+ mCurrentDdsSwitchFailure = new ArrayList<Set<CommandException.Error>>();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ mContext.registerReceiver(mSimStateIntentReceiver, filter);
+
mActivePhoneRegistrants = new RegistrantList();
for (int i = 0; i < mActiveModemCount; i++) {
mPhoneStates[i] = new PhoneState();
@@ -377,11 +489,14 @@ public class PhoneSwitcher extends Handler {
}
PhoneFactory.getPhone(i).getDataEnabledSettings().registerForDataEnabledChanged(
this, EVENT_DATA_ENABLED_CHANGED, null);
+ registerForImsRadioTechChange(context, i);
}
+ Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
+ mCurrentDdsSwitchFailure.add(ddsFailure);
}
if (mActiveModemCount > 0) {
- PhoneFactory.getPhone(0).mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
+ PhoneFactory.getPhone(0).mCi.registerForOn(this, EVENT_RADIO_ON, null);
}
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
@@ -398,28 +513,30 @@ public class PhoneSwitcher extends Handler {
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
- NetworkCapabilities netCap = new NetworkCapabilities();
- netCap.addTransportType(TRANSPORT_CELLULAR);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_MCX);
- netCap.setNetworkSpecifier(new MatchAllNetworkSpecifier());
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_IA)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_MCX)
+ .setNetworkSpecifier(new MatchAllNetworkSpecifier());
NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context,
- netCap, this);
+ builder.build(), this);
// we want to see all requests
- networkFactory.setScoreFilter(101);
- networkFactory.register();
+ networkFactory.registerIgnoringScore();
+
+ updateHalCommandToUse();
log("PhoneSwitcher started");
}
@@ -432,6 +549,40 @@ public class PhoneSwitcher extends Handler {
}
};
+ private BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
+ TelephonyManager.SIM_STATE_UNKNOWN);
+ int slotIndex = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ log("mSimStateIntentReceiver: slotIndex = " + slotIndex + " state = " + state);
+ obtainMessage(EVENT_PROCESS_SIM_STATE_CHANGE, slotIndex, state).sendToTarget();
+ }
+ }
+ };
+
+ private boolean isSimApplicationReady(int slotIndex) {
+ if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ return false;
+ }
+
+ SubscriptionInfo info = SubscriptionController.getInstance()
+ .getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
+ mContext.getOpPackageName(), null);
+ boolean uiccAppsEnabled = info != null && info.areUiccApplicationsEnabled();
+
+ IccCard iccCard = PhoneFactory.getPhone(slotIndex).getIccCard();
+ if (!iccCard.isEmptyProfile() && uiccAppsEnabled) {
+ log("isSimApplicationReady: SIM is ready for slotIndex: " + slotIndex);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
@@ -496,14 +647,41 @@ public class PhoneSwitcher extends Handler {
setOpportunisticDataSubscription(subId, needValidation, callback);
break;
}
- case EVENT_RADIO_AVAILABLE: {
+ case EVENT_RADIO_ON: {
updateHalCommandToUse();
- onEvaluate(REQUESTS_UNCHANGED, "EVENT_RADIO_AVAILABLE");
+ onEvaluate(REQUESTS_UNCHANGED, EVALUATION_REASON_RADIO_ON);
break;
}
+ case EVENT_IMS_RADIO_TECH_CHANGED:
+ // register for radio tech change to listen to radio tech handover in case previous
+ // attempt was not successful
+ registerForImsRadioTechChange();
+ // If the phoneId in voice call didn't change, do nothing.
+ if (!isPhoneInVoiceCallChanged()) {
+ break;
+ }
+ evaluateIfDataSwitchIsNeeded("EVENT_IMS_RADIO_TECH_CHANGED");
+ break;
+
case EVENT_PRECISE_CALL_STATE_CHANGED: {
+ // register for radio tech change to listen to radio tech handover in case previous
+ // attempt was not successful
+ registerForImsRadioTechChange();
+
// If the phoneId in voice call didn't change, do nothing.
- if (!isPhoneInVoiceCallChanged()) break;
+ if (!isPhoneInVoiceCallChanged()) {
+ break;
+ }
+
+ if (!isAnyVoiceCallActiveOnDevice()) {
+ for (int i = 0; i < mActiveModemCount; i++) {
+ if (mCurrentDdsSwitchFailure.get(i).contains(
+ CommandException.Error.OP_NOT_ALLOWED_DURING_VOICE_CALL)
+ && isPhoneIdValidForRetry(i)) {
+ sendRilCommands(i);
+ }
+ }
+ }
// Only handle this event if we are currently waiting for the emergency call
// associated with the override request to start or end.
@@ -520,16 +698,12 @@ public class PhoneSwitcher extends Handler {
mEmergencyOverride.mPendingOriginatingCall = false;
}
}
+ evaluateIfDataSwitchIsNeeded("EVENT_PRECISE_CALL_STATE_CHANGED");
+ break;
}
- // fall through
+
case EVENT_DATA_ENABLED_CHANGED:
- if (onEvaluate(REQUESTS_UNCHANGED, "EVENT_PRECISE_CALL_STATE_CHANGED")) {
- logDataSwitchEvent(mPreferredDataSubId.get(),
- TelephonyEvent.EventState.EVENT_STATE_START,
- DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
- registerDefaultNetworkChangeCallback(mPreferredDataSubId.get(),
- DataSwitch.Reason.DATA_SWITCH_REASON_IN_CALL);
- }
+ evaluateIfDataSwitchIsNeeded("EVENT_DATA_ENABLED_CHANGED");
break;
case EVENT_NETWORK_VALIDATION_DONE: {
int subId = msg.arg1;
@@ -549,24 +723,18 @@ public class PhoneSwitcher extends Handler {
}
case EVENT_MODEM_COMMAND_DONE: {
AsyncResult ar = (AsyncResult) msg.obj;
- boolean commandSuccess = ar != null && ar.exception == null;
- if (mEmergencyOverride != null) {
- log("Emergency override result sent = " + commandSuccess);
- mEmergencyOverride.sendOverrideCompleteCallbackResultAndClear(commandSuccess);
- // Do not retry , as we do not allow changes in onEvaluate during an emergency
- // call. When the call ends, we will start the countdown to remove the override.
- } else if (!commandSuccess) {
- int phoneId = (int) ar.userObj;
- log("Modem command failed. with exception " + ar.exception);
- sendMessageDelayed(Message.obtain(this, EVENT_MODEM_COMMAND_RETRY,
- phoneId), MODEM_COMMAND_RETRY_PERIOD_MS);
- }
+ onDdsSwitchResponse(ar);
break;
}
case EVENT_MODEM_COMMAND_RETRY: {
int phoneId = (int) msg.obj;
- log("Resend modem command on phone " + phoneId);
- sendRilCommands(phoneId);
+ if (isPhoneIdValidForRetry(phoneId)) {
+ log("EVENT_MODEM_COMMAND_RETRY: resend modem command on phone " + phoneId);
+ sendRilCommands(phoneId);
+ } else {
+ log("EVENT_MODEM_COMMAND_RETRY: skip retry as DDS sub changed");
+ mCurrentDdsSwitchFailure.get(phoneId).clear();
+ }
break;
}
case EVENT_OVERRIDE_DDS_FOR_EMERGENCY: {
@@ -624,6 +792,21 @@ public class PhoneSwitcher extends Handler {
onMultiSimConfigChanged(activeModemCount);
break;
}
+ case EVENT_PROCESS_SIM_STATE_CHANGE: {
+ int slotIndex = (int) msg.arg1;
+ int simState = (int) msg.arg2;
+
+ if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ log("EVENT_PROCESS_SIM_STATE_CHANGE: skip processing due to invalid slotId: "
+ + slotIndex);
+ } else if (mCurrentDdsSwitchFailure.get(slotIndex).contains(
+ CommandException.Error.INVALID_SIM_STATE)
+ && (TelephonyManager.SIM_STATE_LOADED == simState)
+ && isSimApplicationReady(slotIndex)) {
+ sendRilCommands(slotIndex);
+ }
+ break;
+ }
}
}
@@ -636,7 +819,12 @@ public class PhoneSwitcher extends Handler {
mPhoneSubscriptions = copyOf(mPhoneSubscriptions, mActiveModemCount);
mPhoneStates = copyOf(mPhoneStates, mActiveModemCount);
- // Single SIM -> dual SIM switch.
+ // Dual SIM -> Single SIM switch.
+ for (int phoneId = oldActiveModemCount - 1; phoneId >= mActiveModemCount; phoneId--) {
+ mCurrentDdsSwitchFailure.remove(phoneId);
+ }
+
+ // Single SIM -> Dual SIM switch.
for (int phoneId = oldActiveModemCount; phoneId < mActiveModemCount; phoneId++) {
mPhoneStates[phoneId] = new PhoneState();
Phone phone = PhoneFactory.getPhone(phoneId);
@@ -651,6 +839,10 @@ public class PhoneSwitcher extends Handler {
}
phone.getDataEnabledSettings().registerForDataEnabledChanged(
this, EVENT_DATA_ENABLED_CHANGED, null);
+
+ Set<CommandException.Error> ddsFailure = new HashSet<CommandException.Error>();
+ mCurrentDdsSwitchFailure.add(ddsFailure);
+ registerForImsRadioTechChange(mContext, phoneId);
}
}
@@ -675,8 +867,8 @@ public class PhoneSwitcher extends Handler {
}
@Override
- protected void needNetworkFor(NetworkRequest networkRequest, int score) {
- if (VDBG) log("needNetworkFor " + networkRequest + ", " + score);
+ protected void needNetworkFor(NetworkRequest networkRequest) {
+ if (VDBG) log("needNetworkFor " + networkRequest);
Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK);
msg.obj = networkRequest;
msg.sendToTarget();
@@ -828,6 +1020,9 @@ public class PhoneSwitcher extends Handler {
// Check if phoneId for preferred data is changed.
int oldPreferredDataPhoneId = mPreferredDataPhoneId;
+ // Check if subId for preferred data is changed.
+ int oldPreferredDataSubId = mPreferredDataSubId.get();
+
// When there are no subscriptions, the preferred data phone ID is invalid, but we want
// to keep a valid phoneId for Emergency, so skip logic that updates for preferred data
// phone ID. Ideally there should be a single set of checks that evaluate the correct
@@ -839,9 +1034,16 @@ public class PhoneSwitcher extends Handler {
sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId)
.append("->").append(mPreferredDataPhoneId);
diffDetected = true;
+ } else if (oldPreferredDataSubId != mPreferredDataSubId.get()) {
+ log("SIM refresh, notify dds change");
+ // Inform connectivity about the active data phone
+ notifyPreferredDataSubIdChanged();
}
- if (diffDetected) {
+ // Always force DDS when radio on. This is to handle the corner cases that modem and android
+ // DDS are out of sync after APM, AP should force DDS when radio on. long term solution
+ // should be having API to query preferred data modem to detect the out-of-sync scenarios.
+ if (diffDetected || EVALUATION_REASON_RADIO_ON.equals(reason)) {
log("evaluating due to " + sb.toString());
if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) {
// With HAL_COMMAND_PREFERRED_DATA, all phones are assumed to allow PS attach.
@@ -907,11 +1109,6 @@ public class PhoneSwitcher extends Handler {
activate(phoneId);
}
}
-
- notifyPreferredDataSubIdChanged();
-
- // Notify all registrants.
- mActivePhoneRegistrants.notifyRegistrants();
}
return diffDetected;
}
@@ -921,12 +1118,12 @@ public class PhoneSwitcher extends Handler {
public long lastRequested = 0;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void activate(int phoneId) {
switchPhone(phoneId, true);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void deactivate(int phoneId) {
switchPhone(phoneId, false);
}
@@ -981,7 +1178,10 @@ public class PhoneSwitcher extends Handler {
}
protected void sendRilCommands(int phoneId) {
- if (!SubscriptionManager.isValidPhoneId(phoneId)) return;
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+ log("sendRilCommands: skip dds switch due to invalid phoneid=" + phoneId);
+ return;
+ }
Message message = Message.obtain(this, EVENT_MODEM_COMMAND_DONE, phoneId);
if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) {
@@ -990,7 +1190,8 @@ public class PhoneSwitcher extends Handler {
PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(isPhoneActive(phoneId), message);
}
} else if (phoneId == mPreferredDataPhoneId) {
- // Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId.
+ // Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId
+ log("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId);
mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, message);
}
}
@@ -1363,7 +1564,7 @@ public class PhoneSwitcher extends Handler {
return mPreferredDataPhoneId;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void log(String l) {
Rlog.d(LOG_TAG, l);
mLocalLog.log(l);
@@ -1415,4 +1616,68 @@ public class PhoneSwitcher extends Handler {
mLocalLog.dump(fd, pw, args);
pw.decreaseIndent();
}
+
+ private boolean isAnyVoiceCallActiveOnDevice() {
+ boolean ret = mPhoneIdInVoiceCall != SubscriptionManager.INVALID_PHONE_INDEX;
+ log("isAnyVoiceCallActiveOnDevice: " + ret);
+ return ret;
+ }
+
+ private void onDdsSwitchResponse(AsyncResult ar) {
+ boolean commandSuccess = ar != null && ar.exception == null;
+ int phoneId = (int) ar.userObj;
+ if (mEmergencyOverride != null) {
+ log("Emergency override result sent = " + commandSuccess);
+ mEmergencyOverride.sendOverrideCompleteCallbackResultAndClear(commandSuccess);
+ // Do not retry , as we do not allow changes in onEvaluate during an emergency
+ // call. When the call ends, we will start the countdown to remove the override.
+ } else if (!commandSuccess) {
+ log("onDdsSwitchResponse: DDS switch failed. with exception " + ar.exception);
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error = ((CommandException)
+ (ar.exception)).getCommandError();
+ mCurrentDdsSwitchFailure.get(phoneId).add(error);
+ if (error == CommandException.Error.OP_NOT_ALLOWED_DURING_VOICE_CALL) {
+ log("onDdsSwitchResponse: Wait for call end indication");
+ return;
+ } else if (error == CommandException.Error.INVALID_SIM_STATE) {
+ /* If there is a attach failure due to sim not ready then
+ hold the retry until sim gets ready */
+ log("onDdsSwitchResponse: Wait for SIM to get READY");
+ return;
+ }
+ }
+ log("onDdsSwitchResponse: Scheduling DDS switch retry");
+ sendMessageDelayed(Message.obtain(this, EVENT_MODEM_COMMAND_RETRY,
+ phoneId), MODEM_COMMAND_RETRY_PERIOD_MS);
+ return;
+ }
+ if (commandSuccess) log("onDdsSwitchResponse: DDS switch success on phoneId = " + phoneId);
+ mCurrentDdsSwitchFailure.get(phoneId).clear();
+ // Notify all registrants
+ mActivePhoneRegistrants.notifyRegistrants();
+ notifyPreferredDataSubIdChanged();
+ }
+
+ private boolean isPhoneIdValidForRetry(int phoneId) {
+ int phoneIdForRequest = INVALID_PHONE_INDEX;
+ int ddsPhoneId = mSubscriptionController.getPhoneId(
+ mSubscriptionController.getDefaultDataSubId());
+ if (ddsPhoneId != INVALID_PHONE_INDEX && ddsPhoneId == phoneId) {
+ return true;
+ } else {
+ if (mPrioritizedDcRequests.size() == 0) {
+ return false;
+ }
+ for (DcRequest dcRequest : mPrioritizedDcRequests) {
+ if (dcRequest != null) {
+ phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
+ if (phoneIdForRequest == phoneId) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index d6ed87b571..187f5e288a 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -22,6 +22,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
@@ -63,7 +64,7 @@ public class ProxyController {
private static final int SET_RC_TIMEOUT_WAITING_MSEC = (45 * 1000);
//***** Class Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static ProxyController sProxyController;
private Phone[] mPhones;
@@ -84,7 +85,7 @@ public class ProxyController {
WakeLock mWakeLock;
// record each phone's set radio capability status
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int[] mSetRadioAccessFamilyStatus;
private int mRadioAccessFamilyStatusCounter;
private boolean mTransactionFailed = false;
@@ -93,18 +94,18 @@ public class ProxyController {
private String[] mNewLogicalModemIds;
// Allows the generation of unique Id's for radio capability request session id
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private AtomicInteger mUniqueIdGenerator = new AtomicInteger(new Random().nextInt());
// on-going radio capability request session id
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mRadioCapabilitySessionId;
// Record new and old Radio Access Family (raf) configuration.
// The old raf configuration is used to restore each logical modem raf when FINISH is
// issued if any requests fail.
private int[] mNewRadioAccessFamily;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int[] mOldRadioAccessFamily;
@@ -116,7 +117,7 @@ public class ProxyController {
return sProxyController;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static ProxyController getInstance() {
return sProxyController;
}
@@ -480,7 +481,7 @@ public class ProxyController {
mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_SUCCESS;
// The modems may have been restarted and forgotten this
mPhoneSwitcher.onRadioCapChanged(id);
- mPhones[id].radioCapabilityUpdated(rc);
+ mPhones[id].radioCapabilityUpdated(rc, true);
}
mRadioAccessFamilyStatusCounter--;
@@ -567,7 +568,7 @@ public class ProxyController {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void completeRadioCapabilityTransaction() {
// Create the intent to broadcast
Intent intent;
@@ -627,7 +628,7 @@ public class ProxyController {
mRadioAccessFamilyStatusCounter = mPhones.length;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendRadioCapabilityRequest(int phoneId, int sessionId, int rcPhase,
int radioFamily, String logicalModemId, int status, int eventId) {
RadioCapability requestRC = new RadioCapability(
@@ -685,7 +686,7 @@ public class ProxyController {
return modemUuid;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void logd(String string) {
Rlog.d(LOG_TAG, string);
}
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 91c79cb305..bd71b0b11d 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -44,6 +44,7 @@ import android.hardware.radio.V1_0.RadioError;
import android.hardware.radio.V1_0.RadioIndicationType;
import android.hardware.radio.V1_0.RadioResponseInfo;
import android.hardware.radio.V1_0.RadioResponseType;
+import android.hardware.radio.V1_0.RadioTechnologyFamily;
import android.hardware.radio.V1_0.ResetNvType;
import android.hardware.radio.V1_0.SelectUiccSub;
import android.hardware.radio.V1_0.SimApdu;
@@ -55,12 +56,16 @@ import android.hardware.radio.V1_5.AccessNetwork;
import android.hardware.radio.V1_5.IndicationFilter;
import android.hardware.radio.V1_5.PersoSubstate;
import android.hardware.radio.V1_5.RadioAccessNetworks;
-import android.hardware.radio.deprecated.V1_0.IOemHook;
+import android.hardware.radio.V1_6.OptionalDnn;
+import android.hardware.radio.V1_6.OptionalOsAppId;
+import android.hardware.radio.V1_6.OptionalSliceInfo;
+import android.hardware.radio.V1_6.OptionalTrafficDescriptor;
import android.net.InetAddresses;
import android.net.KeepalivePacketData;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.HwBinder;
import android.os.Message;
@@ -83,6 +88,7 @@ import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.ClientRequestStats;
import android.telephony.ImsiEncryptionInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
@@ -99,8 +105,13 @@ import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.PrefNetworkMode;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataCallResponse.HandoverFailureMode;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.Qos;
+import android.telephony.data.QosBearerSession;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.Log;
@@ -112,10 +123,12 @@ import com.android.internal.telephony.cat.ComprehensionTlvTag;
import com.android.internal.telephony.cdma.CdmaInformationRecords;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.metrics.ModemRestartStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -190,6 +203,9 @@ public class RIL extends BaseCommands implements CommandsInterface {
/** @hide */
public static final HalVersion RADIO_HAL_VERSION_1_5 = new HalVersion(1, 5);
+ /** @hide */
+ public static final HalVersion RADIO_HAL_VERSION_1_6 = new HalVersion(1, 6);
+
// IRadio version
private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
@@ -223,16 +239,18 @@ public class RIL extends BaseCommands implements CommandsInterface {
volatile int mWlSequenceNum = 0;
volatile int mAckWlSequenceNum = 0;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
SparseArray<RILRequest> mRequestList = new SparseArray<RILRequest>();
static SparseArray<TelephonyHistogram> mRilTimeHistograms = new
SparseArray<TelephonyHistogram>();
Object[] mLastNITZTimeInfo;
+ int mLastRadioPowerResult = RadioError.NONE;
+
// When we are testing emergency calls using ril.test.emergencynumber, this will trigger test
// ECbM when the call is ended.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
AtomicBoolean mTestingEmergencyCall = new AtomicBoolean(false);
final Integer mPhoneId;
@@ -243,12 +261,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
*/
Set<Integer> mDisabledRadioServices = new HashSet();
- /**
- * A set that records if oem hook service is disabled in hal for
- * a specific phone id slot to avoid further getService request.
- */
- Set<Integer> mDisabledOemHookServices = new HashSet();
-
/* default work source which will blame phone process */
private WorkSource mRILDefaultWorkSource;
@@ -264,9 +276,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
RadioResponse mRadioResponse;
RadioIndication mRadioIndication;
volatile IRadio mRadioProxy = null;
- OemHookResponse mOemHookResponse;
- OemHookIndication mOemHookIndication;
- volatile IOemHook mOemHookProxy = null;
final AtomicLong mRadioProxyCookie = new AtomicLong(0);
final RadioProxyDeathRecipient mRadioProxyDeathRecipient;
final RilHandler mRilHandler;
@@ -404,7 +413,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
* @return A default object, such as the one generated by a normal response
* that is returned to the higher layers.
**/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static Object getResponseForTimedOutRILRequest(RILRequest rr) {
if (rr == null ) return null;
@@ -412,7 +421,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
switch(rr.mRequest) {
case RIL_REQUEST_GET_ACTIVITY_INFO:
timeoutResponse = new ModemActivityInfo(
- 0, 0, 0, new int [ModemActivityInfo.TX_POWER_LEVELS], 0);
+ 0, 0, 0, new int [ModemActivityInfo.getNumTxPowerLevels()], 0);
break;
};
return timeoutResponse;
@@ -429,7 +438,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
private synchronized void resetProxyAndRequestList() {
mRadioProxy = null;
- mOemHookProxy = null;
// increment the cookie so that death notification can be ignored
mRadioProxyCookie.incrementAndGet();
@@ -441,7 +449,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
clearRequestList(RADIO_NOT_AVAILABLE, false);
getRadioProxy(null);
- getOemHookProxy(null);
}
/** Set a radio HAL fallback compatibility override. */
@@ -487,14 +494,23 @@ public class RIL extends BaseCommands implements CommandsInterface {
+ " is disabled");
} else {
try {
- mRadioProxy = android.hardware.radio.V1_5.IRadio.getService(
+ mRadioProxy = android.hardware.radio.V1_6.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
- mRadioVersion = RADIO_HAL_VERSION_1_5;
+ mRadioVersion = RADIO_HAL_VERSION_1_6;
} catch (NoSuchElementException e) {
}
if (mRadioProxy == null) {
try {
+ mRadioProxy = android.hardware.radio.V1_5.IRadio.getService(
+ HIDL_SERVICE_NAME[mPhoneId], true);
+ mRadioVersion = RADIO_HAL_VERSION_1_5;
+ } catch (NoSuchElementException e) {
+ }
+ }
+
+ if (mRadioProxy == null) {
+ try {
mRadioProxy = android.hardware.radio.V1_4.IRadio.getService(
HIDL_SERVICE_NAME[mPhoneId], true);
mRadioVersion = RADIO_HAL_VERSION_1_4;
@@ -571,84 +587,30 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (active) {
// Try to connect to RIL services and set response functions.
getRadioProxy(null);
- getOemHookProxy(null);
} else {
resetProxyAndRequestList();
}
}
- /** Returns an {@link IOemHook} instance or null if the service is not available. */
- @VisibleForTesting
- public synchronized IOemHook getOemHookProxy(Message result) {
- if (!SubscriptionManager.isValidPhoneId((mPhoneId))) return null;
- if (!mIsCellularSupported) {
- if (RILJ_LOGV) riljLog("getOemHookProxy: Not calling getService(): wifi-only");
- if (result != null) {
- AsyncResult.forMessage(result, null,
- CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
- result.sendToTarget();
- }
- return null;
- }
-
- if (mOemHookProxy != null) {
- return mOemHookProxy;
- }
-
- try {
- if (mDisabledOemHookServices.contains(mPhoneId)) {
- riljLoge("getOemHookProxy: mOemHookProxy for " + HIDL_SERVICE_NAME[mPhoneId]
- + " is disabled");
- } else {
- mOemHookProxy = IOemHook.getService(HIDL_SERVICE_NAME[mPhoneId], true);
- if (mOemHookProxy != null) {
- // not calling linkToDeath() as ril service runs in the same process and death
- // notification for that should be sufficient
- mOemHookProxy.setResponseFunctions(mOemHookResponse, mOemHookIndication);
- } else {
- mDisabledOemHookServices.add(mPhoneId);
- riljLoge("getOemHookProxy: mOemHookProxy for " + HIDL_SERVICE_NAME[mPhoneId]
- + " is disabled");
- }
- }
- } catch (NoSuchElementException e) {
- mOemHookProxy = null;
- riljLoge("IOemHook service is not on the device HAL: " + e);
- } catch (RemoteException e) {
- mOemHookProxy = null;
- riljLoge("OemHookProxy getService/setResponseFunctions: " + e);
- }
-
- if (mOemHookProxy == null) {
- if (result != null) {
- AsyncResult.forMessage(result, null,
- CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
- result.sendToTarget();
- }
- }
-
- return mOemHookProxy;
- }
-
//***** Constructors
@UnsupportedAppUsage
- public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
- this(context, preferredNetworkType, cdmaSubscription, null);
+ public RIL(Context context, int allowedNetworkTypes, int cdmaSubscription) {
+ this(context, allowedNetworkTypes, cdmaSubscription, null);
}
@UnsupportedAppUsage
- public RIL(Context context, int preferredNetworkType,
+ public RIL(Context context, int allowedNetworkTypes,
int cdmaSubscription, Integer instanceId) {
super(context);
if (RILJ_LOGD) {
- riljLog("RIL: init preferredNetworkType=" + preferredNetworkType
+ riljLog("RIL: init allowedNetworkTypes=" + allowedNetworkTypes
+ " cdmaSubscription=" + cdmaSubscription + ")");
}
mContext = context;
mCdmaSubscription = cdmaSubscription;
- mPreferredNetworkType = preferredNetworkType;
+ mAllowedNetworkTypesBitmask = allowedNetworkTypes;
mPhoneType = RILConstants.NO_PHONE;
mPhoneId = instanceId == null ? 0 : instanceId;
if (isRadioBugDetectionEnabled()) {
@@ -661,8 +623,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
mRadioResponse = new RadioResponse(this);
mRadioIndication = new RadioIndication(this);
- mOemHookResponse = new OemHookResponse(this);
- mOemHookIndication = new OemHookIndication(this);
mRilHandler = new RilHandler();
mRadioProxyDeathRecipient = new RadioProxyDeathRecipient();
@@ -686,7 +646,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
// set radio callback; needed to set RadioIndication callback (should be done after
// wakelock stuff is initialized above as callbacks are received on separate binder threads)
getRadioProxy(null);
- getOemHookProxy(null);
if (RILJ_LOGD) {
riljLog("Radio HAL version: " + mRadioVersion);
@@ -816,14 +775,16 @@ public class RIL extends BaseCommands implements CommandsInterface {
RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK, result,
mRILDefaultWorkSource);
+ String pukStr = convertNullToEmptyString(puk);
if (RILJ_LOGD) {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " isPukEmpty = " + pukStr.isEmpty()
+ " aid = " + aid);
}
try {
radioProxy.supplyIccPukForApp(rr.mSerial,
- convertNullToEmptyString(puk),
+ pukStr,
convertNullToEmptyString(newPin),
convertNullToEmptyString(aid));
} catch (RemoteException | RuntimeException e) {
@@ -1085,7 +1046,14 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
try {
- radioProxy.getCurrentCalls(rr.mSerial);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ // IRadio V1.6
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ radioProxy16.getCurrentCalls_1_6(rr.mSerial);
+ } else {
+ radioProxy.getCurrentCalls(rr.mSerial);
+ }
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "getCurrentCalls", e);
}
@@ -1201,6 +1169,39 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
@Override
+ public void getSystemSelectionChannels(Message onComplete) {
+ IRadio radioProxy = getRadioProxy(onComplete);
+ if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+ if (RILJ_LOGV) riljLog("getSystemSelectionChannels: not supported.");
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ onComplete.sendToTarget();
+ }
+ return;
+ }
+
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, onComplete,
+ mRILDefaultWorkSource);
+
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+
+ if (radioProxy16 != null) {
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " getSystemSelectionChannels");
+ }
+
+ try {
+ radioProxy16.getSystemSelectionChannels(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getSystemSelectionChannels", e);
+ }
+ }
+ }
+
+ @Override
public void getModemStatus(Message result) {
IRadio radioProxy = getRadioProxy(result);
if (mRadioVersion.less(RADIO_HAL_VERSION_1_3)) {
@@ -1271,12 +1272,8 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
private void emergencyDial(String address, EmergencyNumber emergencyNumberInfo,
- boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo,
- Message result) {
+ boolean hasKnownUserIntentEmergency, int clirMode, UUSInfo uusInfo, Message result) {
IRadio radioProxy = getRadioProxy(result);
- // IRadio V1.4
- android.hardware.radio.V1_4.IRadio radioProxy14 =
- (android.hardware.radio.V1_4.IRadio) radioProxy;
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_EMERGENCY_DIAL, result,
mRILDefaultWorkSource);
@@ -1296,18 +1293,40 @@ public class RIL extends BaseCommands implements CommandsInterface {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
}
- try {
- radioProxy14.emergencyDial(rr.mSerial, dialInfo,
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ try {
+ radioProxy16.emergencyDial_1_6(rr.mSerial, dialInfo,
emergencyNumberInfo.getEmergencyServiceCategoryBitmaskInternalDial(),
emergencyNumberInfo.getEmergencyUrns() != null
? new ArrayList(emergencyNumberInfo.getEmergencyUrns())
- : new ArrayList<>(),
+ : new ArrayList<>(),
emergencyNumberInfo.getEmergencyCallRouting(),
hasKnownUserIntentEmergency,
emergencyNumberInfo.getEmergencyNumberSourceBitmask()
== EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "emergencyDial", e);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "emergencyDial_1_6", e);
+ }
+ } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+ android.hardware.radio.V1_4.IRadio radioProxy14 =
+ (android.hardware.radio.V1_4.IRadio) radioProxy;
+ try {
+ radioProxy14.emergencyDial(rr.mSerial, dialInfo,
+ emergencyNumberInfo.getEmergencyServiceCategoryBitmaskInternalDial(),
+ emergencyNumberInfo.getEmergencyUrns() != null
+ ? new ArrayList(emergencyNumberInfo.getEmergencyUrns())
+ : new ArrayList<>(),
+ emergencyNumberInfo.getEmergencyCallRouting(),
+ hasKnownUserIntentEmergency,
+ emergencyNumberInfo.getEmergencyNumberSourceBitmask()
+ == EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "emergencyDial", e);
+ }
+ } else {
+ riljLoge("emergencyDial is not supported with 1.4 below IRadio");
}
}
}
@@ -1356,7 +1375,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void hangupWaitingOrBackground(Message result) {
IRadio radioProxy = getRadioProxy(result);
@@ -1374,7 +1393,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void hangupForegroundResumeBackground(Message result) {
IRadio radioProxy = getRadioProxy(result);
@@ -1469,7 +1488,15 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ try {
+ radioProxy16.getSignalStrength_1_6(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getSignalStrength_1_6", e);
+ }
+ } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
android.hardware.radio.V1_4.IRadio radioProxy14 =
(android.hardware.radio.V1_4.IRadio) radioProxy;
try {
@@ -1500,7 +1527,18 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) {
riljLog("getVoiceRegistrationState: overrideHalVersion=" + overrideHalVersion);
}
+
if ((overrideHalVersion == null
+ || overrideHalVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6))
+ && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ final android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ try {
+ radioProxy16.getVoiceRegistrationState_1_6(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getVoiceRegistrationState_1_6", e);
+ }
+ } else if ((overrideHalVersion == null
|| overrideHalVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5))
&& mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
final android.hardware.radio.V1_5.IRadio radioProxy15 =
@@ -1533,7 +1571,18 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) {
riljLog("getDataRegistrationState: overrideHalVersion=" + overrideHalVersion);
}
+
if ((overrideHalVersion == null
+ || overrideHalVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6))
+ && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ final android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ try {
+ radioProxy16.getDataRegistrationState_1_6(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getDataRegistrationState_1_6", e);
+ }
+ } else if ((overrideHalVersion == null
|| overrideHalVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5))
&& mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
final android.hardware.radio.V1_5.IRadio radioProxy15 =
@@ -1585,7 +1634,16 @@ public class RIL extends BaseCommands implements CommandsInterface {
+ " preferredForEmergencyCall=" + preferredForEmergencyCall);
}
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ try {
+ radioProxy16.setRadioPower_1_6(rr.mSerial, on, forEmergencyCall,
+ preferredForEmergencyCall);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setRadioPower_1_6", e);
+ }
+ } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
android.hardware.radio.V1_5.IRadio radioProxy15 =
(android.hardware.radio.V1_5.IRadio) radioProxy;
try {
@@ -1642,17 +1700,48 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
GsmSmsMessage msg = constructGsmSendSmsRilRequest(smscPdu, pdu);
-
- try {
- radioProxy.sendSms(rr.mSerial, msg);
- mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
- SmsSession.Event.Format.SMS_FORMAT_3GPP);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "sendSMS", e);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ try {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ radioProxy16.sendSms_1_6(rr.mSerial, msg);
+ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+ SmsSession.Event.Format.SMS_FORMAT_3GPP,
+ getOutgoingSmsMessageId(result));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "sendSMS", e);
+ }
+ } else {
+ try {
+ radioProxy.sendSms(rr.mSerial, msg);
+ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+ SmsSession.Event.Format.SMS_FORMAT_3GPP,
+ getOutgoingSmsMessageId(result));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "sendSMS", e);
+ }
}
}
}
+ /**
+ * Extract the outgoing sms messageId from the tracker, if there is one. This is specifically
+ * for SMS related APIs.
+ * @param result the result Message
+ * @return messageId unique identifier or 0 if there is no message id
+ */
+ public static long getOutgoingSmsMessageId(Message result) {
+ if (result == null || !(result.obj instanceof SMSDispatcher.SmsTracker)) {
+ return 0L;
+ }
+ long messageId = ((SMSDispatcher.SmsTracker) result.obj).mMessageId;
+ if (RILJ_LOGV) {
+ Rlog.d(RILJ_LOG_TAG, "getOutgoingSmsMessageId "
+ + SmsController.formatCrossStackMessageId(messageId));
+ }
+ return messageId;
+ }
+
@Override
public void sendSMSExpectMore(String smscPdu, String pdu, Message result) {
IRadio radioProxy = getRadioProxy(result);
@@ -1664,13 +1753,26 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
GsmSmsMessage msg = constructGsmSendSmsRilRequest(smscPdu, pdu);
-
- try {
- radioProxy.sendSMSExpectMore(rr.mSerial, msg);
- mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
- SmsSession.Event.Format.SMS_FORMAT_3GPP);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "sendSMSExpectMore", e);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ try {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ radioProxy16.sendSmsExpectMore_1_6(rr.mSerial, msg);
+ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+ SmsSession.Event.Format.SMS_FORMAT_3GPP,
+ getOutgoingSmsMessageId(result));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "sendSMSExpectMore", e);
+ }
+ } else {
+ try {
+ radioProxy.sendSMSExpectMore(rr.mSerial, msg);
+ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+ SmsSession.Event.Format.SMS_FORMAT_3GPP,
+ getOutgoingSmsMessageId(result));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "sendSMSExpectMore", e);
+ }
}
}
}
@@ -1761,6 +1863,66 @@ public class RIL extends BaseCommands implements CommandsInterface {
return dpi;
}
+ private static OptionalSliceInfo convertToHalSliceInfo(@Nullable NetworkSliceInfo sliceInfo) {
+ OptionalSliceInfo optionalSliceInfo = new OptionalSliceInfo();
+ if (sliceInfo == null) {
+ return optionalSliceInfo;
+ }
+
+ android.hardware.radio.V1_6.SliceInfo si = new android.hardware.radio.V1_6.SliceInfo();
+ si.sst = (byte) sliceInfo.getSliceServiceType();
+ si.mappedHplmnSst = (byte) sliceInfo.getMappedHplmnSliceServiceType();
+ si.sliceDifferentiator = sliceInfo.getSliceDifferentiator();
+ si.mappedHplmnSD = sliceInfo.getMappedHplmnSliceDifferentiator();
+ optionalSliceInfo.value(si);
+ return optionalSliceInfo;
+ }
+
+ private static OptionalTrafficDescriptor convertToHalTrafficDescriptor(
+ @Nullable TrafficDescriptor trafficDescriptor) {
+ OptionalTrafficDescriptor optionalTrafficDescriptor = new OptionalTrafficDescriptor();
+ if (trafficDescriptor == null) {
+ return optionalTrafficDescriptor;
+ }
+
+ android.hardware.radio.V1_6.TrafficDescriptor td =
+ new android.hardware.radio.V1_6.TrafficDescriptor();
+
+ OptionalDnn optionalDnn = new OptionalDnn();
+ if (trafficDescriptor.getDataNetworkName() != null) {
+ optionalDnn.value(trafficDescriptor.getDataNetworkName());
+ }
+ td.dnn = optionalDnn;
+
+ OptionalOsAppId optionalOsAppId = new OptionalOsAppId();
+ if (trafficDescriptor.getOsAppId() != null) {
+ android.hardware.radio.V1_6.OsAppId osAppId = new android.hardware.radio.V1_6.OsAppId();
+ osAppId.osAppId = primitiveArrayToArrayList(trafficDescriptor.getOsAppId());
+ optionalOsAppId.value(osAppId);
+ }
+ td.osAppId = optionalOsAppId;
+
+ optionalTrafficDescriptor.value(td);
+ return optionalTrafficDescriptor;
+ }
+
+ private static ArrayList<android.hardware.radio.V1_5.LinkAddress> convertToHalLinkProperties15(
+ LinkProperties linkProperties) {
+ ArrayList<android.hardware.radio.V1_5.LinkAddress> addresses15 = new ArrayList<>();
+ if (linkProperties != null) {
+ for (LinkAddress la : linkProperties.getAllLinkAddresses()) {
+ android.hardware.radio.V1_5.LinkAddress linkAddress =
+ new android.hardware.radio.V1_5.LinkAddress();
+ linkAddress.address = la.getAddress().getHostAddress();
+ linkAddress.properties = la.getFlags();
+ linkAddress.deprecationTime = la.getDeprecationTime();
+ linkAddress.expirationTime = la.getExpirationTime();
+ addresses15.add(linkAddress);
+ }
+ }
+ return addresses15;
+ }
+
/**
* Convert to DataProfileInfo defined in radio/1.5/types.hal
* @param dp Data profile
@@ -1821,9 +1983,9 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- Message result) {
-
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
@@ -1843,7 +2005,36 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
try {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ // IRadio V1.6
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+
+ // Convert to HAL data profile
+ android.hardware.radio.V1_5.DataProfileInfo dpi =
+ convertToHalDataProfile15(dataProfile);
+
+ OptionalSliceInfo si = convertToHalSliceInfo(sliceInfo);
+
+ ArrayList<android.hardware.radio.V1_5.LinkAddress> addresses15 =
+ convertToHalLinkProperties15(linkProperties);
+
+ OptionalTrafficDescriptor td = convertToHalTrafficDescriptor(trafficDescriptor);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + ",accessNetworkType="
+ + AccessNetworkType.toString(accessNetworkType) + ",isRoaming="
+ + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile
+ + ",addresses=" + addresses15 + ",dnses=" + dnses
+ + ",pduSessionId=" + pduSessionId + ",sliceInfo=" + si
+ + ",trafficDescriptor=" + td + ",matchAllRuleAllowed="
+ + matchAllRuleAllowed);
+ }
+
+ radioProxy16.setupDataCall_1_6(rr.mSerial, accessNetworkType, dpi, allowRoaming,
+ reason, addresses15, dnses, pduSessionId, si, td, matchAllRuleAllowed);
+ } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
// IRadio V1.5
android.hardware.radio.V1_5.IRadio radioProxy15 =
(android.hardware.radio.V1_5.IRadio) radioProxy;
@@ -1853,18 +2044,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
convertToHalDataProfile15(dataProfile);
ArrayList<android.hardware.radio.V1_5.LinkAddress> addresses15 =
- new ArrayList<>();
- if (linkProperties != null) {
- for (LinkAddress la : linkProperties.getAllLinkAddresses()) {
- android.hardware.radio.V1_5.LinkAddress linkAddress =
- new android.hardware.radio.V1_5.LinkAddress();
- linkAddress.address = la.getAddress().getHostAddress();
- linkAddress.properties = la.getFlags();
- linkAddress.deprecationTime = la.getDeprecationTime();
- linkAddress.expirationTime = la.getExpirationTime();
- addresses15.add(linkAddress);
- }
- }
+ convertToHalLinkProperties15(linkProperties);
if (RILJ_LOGD) {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
@@ -1875,7 +2055,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
radioProxy15.setupDataCall_1_5(rr.mSerial, accessNetworkType, dpi, allowRoaming,
- reason, addresses15, dnses);
+ reason, addresses15, dnses);
} else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
// IRadio V1.4
android.hardware.radio.V1_4.IRadio radioProxy14 =
@@ -2807,65 +2987,28 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
try {
- radioProxy.getDataCallList(rr.mSerial);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ radioProxy16.getDataCallList_1_6(rr.mSerial);
+ } else {
+ radioProxy.getDataCallList(rr.mSerial);
+ }
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "getDataCallList", e);
}
}
}
- @UnsupportedAppUsage
+ // TODO(b/171260715) Remove when HAL definition is removed
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void invokeOemRilRequestRaw(byte[] data, Message response) {
- IOemHook oemHookProxy = getOemHookProxy(response);
- if (oemHookProxy != null) {
- RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_RAW, response,
- mRILDefaultWorkSource);
-
- if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
- + "[" + IccUtils.bytesToHexString(data) + "]");
- }
-
- try {
- oemHookProxy.sendRequestRaw(rr.mSerial, primitiveArrayToArrayList(data));
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestRaw", e);
- }
- } else {
- // OEM Hook service is disabled for P and later devices.
- // Deprecated OEM Hook APIs will perform dummy before being removed.
- if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
- }
}
+ // TODO(b/171260715) Remove when HAL definition is removed
@Override
public void invokeOemRilRequestStrings(String[] strings, Message result) {
- IOemHook oemHookProxy = getOemHookProxy(result);
- if (oemHookProxy != null) {
- RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_STRINGS, result,
- mRILDefaultWorkSource);
-
- String logStr = "";
- for (int i = 0; i < strings.length; i++) {
- logStr = logStr + strings[i] + " ";
- }
- if (RILJ_LOGD) {
- riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " strings = "
- + logStr);
- }
-
- try {
- oemHookProxy.sendRequestStrings(rr.mSerial,
- new ArrayList<String>(Arrays.asList(strings)));
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
- }
- } else {
- // OEM Hook service is disabled for P and later devices.
- // Deprecated OEM Hook APIs will perform dummy before being removed.
- if (RILJ_LOGD) riljLog("Radio Oem Hook Service is disabled for P and later devices. ");
- }
}
@Override
@@ -3089,7 +3232,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ " networkType = " + networkType);
}
- mPreferredNetworkType = networkType;
+ mAllowedNetworkTypesBitmask = RadioAccessFamily.getRafFromNetworkType(networkType);
mMetrics.writeSetPreferredNetworkType(mPhoneId, networkType);
if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_4)) {
@@ -3097,8 +3240,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
(android.hardware.radio.V1_4.IRadio) radioProxy;
try {
radioProxy14.setPreferredNetworkTypeBitmap(
- rr.mSerial, convertToHalRadioAccessFamily(
- RadioAccessFamily.getRafFromNetworkType(networkType)));
+ rr.mSerial, convertToHalRadioAccessFamily(mAllowedNetworkTypesBitmask));
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "setPreferredNetworkTypeBitmap", e);
}
@@ -3279,13 +3421,70 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
@Override
- public void setLocationUpdates(boolean enable, Message result) {
+ public void setAllowedNetworkTypesBitmap(
+ @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message result) {
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
- RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOCATION_UPDATES, result,
+ if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+ // For older HAL, redirects the call to setPreferredNetworkType.
+ setPreferredNetworkType(
+ RadioAccessFamily.getNetworkTypeFromRaf(networkTypeBitmask), result);
+ return;
+ }
+
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP, result,
mRILDefaultWorkSource);
if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+ mAllowedNetworkTypesBitmask = networkTypeBitmask;
+ try {
+ radioProxy16.setAllowedNetworkTypesBitmap(rr.mSerial,
+ convertToHalRadioAccessFamily(mAllowedNetworkTypesBitmask));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setAllowedNetworkTypeBitmask", e);
+ }
+ }
+ }
+
+ @Override
+ public void getAllowedNetworkTypesBitmap(Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+ // For older HAL, redirects the call to getPreferredNetworkType.
+ getPreferredNetworkType(result);
+ return;
+ }
+
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ try {
+ radioProxy16.getAllowedNetworkTypesBitmap(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getAllowedNetworkTypeBitmask", e);
+ }
+ }
+ }
+
+ @Override
+ public void setLocationUpdates(boolean enable, WorkSource workSource, Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOCATION_UPDATES, result,
+ workSource == null ? mRILDefaultWorkSource : workSource);
+
+ if (RILJ_LOGD) {
riljLog(rr.serialString() + "> "
+ requestToString(rr.mRequest) + " enable = " + enable);
}
@@ -3298,6 +3497,85 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
+ /**
+ * Is E-UTRA-NR Dual Connectivity enabled
+ */
+ @Override
+ public void isNrDualConnectivityEnabled(Message result, WorkSource workSource) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ return;
+ }
+
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+
+ RILRequest rr = obtainRequest(RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED, result,
+ workSource == null ? mRILDefaultWorkSource : workSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest));
+ }
+
+ try {
+ radioProxy16.isNrDualConnectivityEnabled(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "isNRDualConnectivityEnabled", e);
+ }
+ }
+ }
+
+ /**
+ * Enable/Disable E-UTRA-NR Dual Connectivity
+ * @param nrDualConnectivityState expected NR dual connectivity state
+ * This can be passed following states
+ * <ol>
+ * <li>Enable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_ENABLE}
+ * <li>Disable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE}
+ * <li>Disable NR dual connectivity and force secondary cell to be released
+ * {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE}
+ * </ol>
+ */
+ @Override
+ public void setNrDualConnectivityState(int nrDualConnectivityState,
+ Message result, WorkSource workSource) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ return;
+ }
+
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, result,
+ workSource == null ? mRILDefaultWorkSource : workSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest) + " enable = " + nrDualConnectivityState);
+ }
+
+ try {
+ radioProxy16.setNrDualConnectivityState(rr.mSerial,
+ (byte) nrDualConnectivityState);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "enableNRDualConnectivity", e);
+ }
+ }
+ }
+
@Override
public void setCdmaSubscriptionSource(int cdmaSubscription , Message result) {
IRadio radioProxy = getRadioProxy(result);
@@ -3509,31 +3787,41 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void sendCdmaSMSExpectMore(byte[] pdu, Message result) {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
- IRadio radioProxy = getRadioProxy(result);
- // IRadio V1.5
- android.hardware.radio.V1_5.IRadio radioProxy15 =
- (android.hardware.radio.V1_5.IRadio) radioProxy;
- if (radioProxy15 != null) {
- RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE, result,
- mRILDefaultWorkSource);
-
- // Do not log function arg for privacy
- if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE, result,
+ mRILDefaultWorkSource);
- CdmaSmsMessage msg = new CdmaSmsMessage();
- constructCdmaSendSmsRilRequest(msg, pdu);
+ // Do not log function arg for privacy
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ CdmaSmsMessage msg = new CdmaSmsMessage();
+ constructCdmaSendSmsRilRequest(msg, pdu);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ try {
+ radioProxy16.sendCdmaSmsExpectMore_1_6(rr.mSerial, msg);
+ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
+ SmsSession.Event.Format.SMS_FORMAT_3GPP2,
+ getOutgoingSmsMessageId(result));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "sendCdmaSMSExpectMore", e);
+ }
+ } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+ android.hardware.radio.V1_5.IRadio radioProxy15 =
+ (android.hardware.radio.V1_5.IRadio) radioProxy;
try {
radioProxy15.sendCdmaSmsExpectMore(rr.mSerial, msg);
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
- SmsSession.Event.Format.SMS_FORMAT_3GPP2);
+ SmsSession.Event.Format.SMS_FORMAT_3GPP2,
+ getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "sendCdmaSMSExpectMore", e);
}
+ } else {
+ sendCdmaSms(pdu, result);
}
- } else {
- sendCdmaSms(pdu, result);
}
}
@@ -3549,13 +3837,26 @@ public class RIL extends BaseCommands implements CommandsInterface {
CdmaSmsMessage msg = new CdmaSmsMessage();
constructCdmaSendSmsRilRequest(msg, pdu);
-
- try {
- radioProxy.sendCdmaSms(rr.mSerial, msg);
- mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
- SmsSession.Event.Format.SMS_FORMAT_3GPP2);
- } catch (RemoteException | RuntimeException e) {
- handleRadioProxyExceptionForRR(rr, "sendCdmaSms", e);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ try {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ radioProxy16.sendCdmaSms_1_6(rr.mSerial, msg);
+ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
+ SmsSession.Event.Format.SMS_FORMAT_3GPP2,
+ getOutgoingSmsMessageId(result));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "sendCdmaSms", e);
+ }
+ } else {
+ try {
+ radioProxy.sendCdmaSms(rr.mSerial, msg);
+ mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
+ SmsSession.Event.Format.SMS_FORMAT_3GPP2,
+ getOutgoingSmsMessageId(result));
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "sendCdmaSms", e);
+ }
}
}
}
@@ -3962,7 +4263,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void getCellInfoList(Message result, WorkSource workSource) {
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_CELL_INFO_LIST, result,
@@ -3973,7 +4274,14 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
try {
- radioProxy.getCellInfoList(rr.mSerial);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ radioProxy16.getCellInfoList_1_6(rr.mSerial);
+
+ } else {
+ radioProxy.getCellInfoList(rr.mSerial);
+ }
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "getCellInfoList", e);
}
@@ -3982,7 +4290,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void setCellInfoListRate(int rateInMillis, Message result, WorkSource workSource) {
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, result,
@@ -4003,7 +4311,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
-
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_INITIAL_ATTACH_APN, result,
@@ -4069,7 +4376,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
ImsSmsMessage msg = new ImsSmsMessage();
- msg.tech = RILConstants.GSM_PHONE;
+ msg.tech = RadioTechnologyFamily.THREE_GPP;
msg.retry = (byte) retry >= 1 ? true : false;
msg.messageRef = messageRef;
@@ -4078,7 +4385,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
try {
radioProxy.sendImsSms(rr.mSerial, msg);
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
- SmsSession.Event.Format.SMS_FORMAT_3GPP);
+ SmsSession.Event.Format.SMS_FORMAT_3GPP, getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "sendImsGsmSms", e);
}
@@ -4096,7 +4403,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
ImsSmsMessage msg = new ImsSmsMessage();
- msg.tech = RILConstants.CDMA_PHONE;
+ msg.tech = RadioTechnologyFamily.THREE_GPP2;
msg.retry = (byte) retry >= 1 ? true : false;
msg.messageRef = messageRef;
@@ -4107,7 +4414,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
try {
radioProxy.sendImsSms(rr.mSerial, msg);
mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
- SmsSession.Event.Format.SMS_FORMAT_3GPP2);
+ SmsSession.Event.Format.SMS_FORMAT_3GPP2, getOutgoingSmsMessageId(result));
} catch (RemoteException | RuntimeException e) {
handleRadioProxyExceptionForRR(rr, "sendImsCdmaSms", e);
}
@@ -4237,7 +4544,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void nvReadItem(int itemID, Message result, WorkSource workSource) {
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_NV_READ_ITEM, result,
@@ -4258,7 +4565,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void nvWriteItem(int itemId, String itemValue, Message result, WorkSource workSource) {
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_ITEM, result,
@@ -4417,20 +4724,16 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
-
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
-
- RILRequest rr = null;
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
+ mRILDefaultWorkSource);
try {
if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
// V1.5
android.hardware.radio.V1_5.IRadio radioProxy15 =
(android.hardware.radio.V1_5.IRadio) radioProxy;
- rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
- mRILDefaultWorkSource);
-
ArrayList<android.hardware.radio.V1_5.DataProfileInfo> dpis = new ArrayList<>();
for (DataProfile dp : dps) {
dpis.add(convertToHalDataProfile15(dp));
@@ -4450,9 +4753,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
android.hardware.radio.V1_4.IRadio radioProxy14 =
(android.hardware.radio.V1_4.IRadio) radioProxy;
- rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
- mRILDefaultWorkSource);
-
ArrayList<android.hardware.radio.V1_4.DataProfileInfo> dpis = new ArrayList<>();
for (DataProfile dp : dps) {
dpis.add(convertToHalDataProfile14(dp));
@@ -4479,9 +4779,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
if (!dpis.isEmpty()) {
- rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
- mRILDefaultWorkSource);
-
if (RILJ_LOGD) {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ " with data profiles : ");
@@ -4619,6 +4916,50 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
/**
+ * Control the data throttling at modem.
+ *
+ * @param result Message that will be sent back to the requester
+ * @param workSource calling Worksource
+ * @param dataThrottlingAction the DataThrottlingAction that is being requested. Defined in
+ * android.hardware.radio@1.6.types.
+ * @param completionWindowMillis milliseconds in which full throttling has to be achieved.
+ */
+ @Override
+ public void setDataThrottling(Message result, WorkSource workSource, int dataThrottlingAction,
+ long completionWindowMillis) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ if (mRadioVersion.less(RADIO_HAL_VERSION_1_6)) {
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ return;
+ }
+
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_THROTTLING, result,
+ workSource == null ? mRILDefaultWorkSource : workSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest)
+ + " dataThrottlingAction = " + dataThrottlingAction
+ + " completionWindowMillis " + completionWindowMillis);
+ }
+
+ try {
+ radioProxy16.setDataThrottling(rr.mSerial, (byte) dataThrottlingAction,
+ completionWindowMillis);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setDataThrottling", e);
+ }
+ }
+ }
+
+ /**
* This will only be called if the LCE service is started in PULL mode, which is
* only enabled when using Radio HAL versions 1.1 and earlier.
*
@@ -4650,7 +4991,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void getModemActivityInfo(Message result, WorkSource workSource) {
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_GET_ACTIVITY_INFO, result,
@@ -4716,7 +5057,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
riljLog("RIL.java - setAllowedCarriers");
checkNotNull(carrierRestrictionRules, "Carrier restriction cannot be null.");
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy == null) return;
@@ -4796,7 +5137,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void getAllowedCarriers(Message result, WorkSource workSource) {
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy == null) return;
@@ -4946,7 +5287,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
SignalThresholdInfo signalThresholdInfo) {
android.hardware.radio.V1_5.SignalThresholdInfo signalThresholdInfoHal =
new android.hardware.radio.V1_5.SignalThresholdInfo();
- signalThresholdInfoHal.signalMeasurement = signalThresholdInfo.getSignalMeasurement();
+ signalThresholdInfoHal.signalMeasurement = signalThresholdInfo.getSignalMeasurementType();
signalThresholdInfoHal.hysteresisMs = signalThresholdInfo.getHysteresisMs();
signalThresholdInfoHal.hysteresisDb = signalThresholdInfo.getHysteresisDb();
signalThresholdInfoHal.thresholds = primitiveArrayToArrayList(
@@ -5036,7 +5377,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
@Override
public void setSimCardPower(int state, Message result, WorkSource workSource) {
- workSource = getDeafultWorkSourceIfInvalid(workSource);
+ workSource = getDefaultWorkSourceIfInvalid(workSource);
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIM_CARD_POWER, result,
@@ -5046,7 +5387,15 @@ public class RIL extends BaseCommands implements CommandsInterface {
riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + state);
}
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ try {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio) radioProxy;
+ radioProxy16.setSimCardPower_1_6(rr.mSerial, state);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
+ }
+ } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
try {
android.hardware.radio.V1_1.IRadio radioProxy11 =
(android.hardware.radio.V1_1.IRadio) radioProxy;
@@ -5086,7 +5435,35 @@ public class RIL extends BaseCommands implements CommandsInterface {
checkNotNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
IRadio radioProxy = getRadioProxy(result);
if (radioProxy != null) {
- if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ (android.hardware.radio.V1_6.IRadio ) radioProxy;
+
+ RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
+ mRILDefaultWorkSource);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ android.hardware.radio.V1_6.ImsiEncryptionInfo halImsiInfo =
+ new android.hardware.radio.V1_6.ImsiEncryptionInfo();
+ halImsiInfo.base.mnc = imsiEncryptionInfo.getMnc();
+ halImsiInfo.base.mcc = imsiEncryptionInfo.getMcc();
+ halImsiInfo.base.keyIdentifier = imsiEncryptionInfo.getKeyIdentifier();
+ if (imsiEncryptionInfo.getExpirationTime() != null) {
+ halImsiInfo.base.expirationTime =
+ imsiEncryptionInfo.getExpirationTime().getTime();
+ }
+ for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) {
+ halImsiInfo.base.carrierKey.add(new Byte(b));
+ }
+ halImsiInfo.keyType = (byte) imsiEncryptionInfo.getKeyType();
+
+ radioProxy16.setCarrierInfoForImsiEncryption_1_6(
+ rr.mSerial, halImsiInfo);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "setCarrierInfoForImsiEncryption", e);
+ }
+ } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_1)) {
android.hardware.radio.V1_1.IRadio radioProxy11 =
(android.hardware.radio.V1_1.IRadio ) radioProxy;
@@ -5392,7 +5769,235 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void allocatePduSessionId(Message result) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 = getRadioV16(result);
+
+ if (radioProxy16 != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, result,
+ mRILDefaultWorkSource);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ radioProxy16.allocatePduSessionId(rr.mSerial);
+ } catch (RemoteException e) {
+ handleRadioProxyExceptionForRR(rr, "allocatePduSessionId", e);
+ }
+ } else {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void releasePduSessionId(Message result, int pduSessionId) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 = getRadioV16(result);
+
+ if (radioProxy16 != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_RELEASE_PDU_SESSION_ID, result,
+ mRILDefaultWorkSource);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ radioProxy16.releasePduSessionId(rr.mSerial, pduSessionId);
+ } catch (RemoteException e) {
+ handleRadioProxyExceptionForRR(rr, "releasePduSessionId", e);
+ }
+ } else {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void startHandover(Message result, int callId) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 = getRadioV16(result);
+
+ if (radioProxy16 != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_START_HANDOVER, result,
+ mRILDefaultWorkSource);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ radioProxy16.startHandover(rr.mSerial, callId);
+ } catch (RemoteException e) {
+ handleRadioProxyExceptionForRR(rr, "startHandover", e);
+ }
+ } else {
+ if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "startHandover: REQUEST_NOT_SUPPORTED");
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void cancelHandover(Message result, int callId) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 = getRadioV16(result);
+
+ if (radioProxy16 != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_HANDOVER, result,
+ mRILDefaultWorkSource);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ radioProxy16.cancelHandover(rr.mSerial, callId);
+ } catch (RemoteException e) {
+ handleRadioProxyExceptionForRR(rr, "cancelHandover", e);
+ }
+ } else {
+ if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "cancelHandover: REQUEST_NOT_SUPPORTED");
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void getSlicingConfig(Message result) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 = getRadioV16(result);
+
+ if (radioProxy16 != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLICING_CONFIG, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+ try {
+ radioProxy16.getSlicingConfig(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getSlicingConfig", e);
+ }
+ } else {
+ if (RILJ_LOGD) Rlog.d(RILJ_LOG_TAG, "getSlicingConfig: REQUEST_NOT_SUPPORTED");
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+
+ @Override
+ public void getSimPhonebookRecords(Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ android.hardware.radio.V1_6.IRadio.castFrom(radioProxy);
+ try {
+ radioProxy16.getSimPhonebookRecords(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getPhonebookRecords", e);
+ }
+ } else {
+ riljLog("Unsupported API in lower than version 1.6 radio HAL" );
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ android.hardware.radio.V1_6.IRadio.castFrom(radioProxy);
+ try {
+ radioProxy16.getSimPhonebookCapacity(rr.mSerial);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "getPhonebookRecords", e);
+ }
+ } else {
+ riljLog("Unsupported API in lower than version 1.6 radio HAL" );
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
+ IRadio radioProxy = getRadioProxy(result);
+ if (radioProxy != null) {
+ RILRequest rr = obtainRequest(RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD, result,
+ mRILDefaultWorkSource);
+
+ if (RILJ_LOGD) {
+ riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ + " with " + phonebookRecord.toString());
+ }
+
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ android.hardware.radio.V1_6.IRadio radioProxy16 =
+ android.hardware.radio.V1_6.IRadio.castFrom(radioProxy);
+
+ android.hardware.radio.V1_6.PhonebookRecordInfo pbRecordInfo =
+ phonebookRecord.toPhonebookRecordInfo();
+ try {
+ radioProxy16.updateSimPhonebookRecords(rr.mSerial, pbRecordInfo);
+ } catch (RemoteException | RuntimeException e) {
+ handleRadioProxyExceptionForRR(rr, "updatePhonebookRecord", e);
+ }
+ } else {
+ riljLog("Unsupported API in lower than version 1.6 radio HAL" );
+ if (result != null) {
+ AsyncResult.forMessage(result, null,
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ }
+ }
+ }
+ }
+
//***** Private Methods
+ /** Helper that gets V1.6 of the radio interface OR sends back REQUEST_NOT_SUPPORTED */
+ @Nullable private android.hardware.radio.V1_6.IRadio getRadioV16(Message msg) {
+ IRadio radioProxy = getRadioProxy(msg);
+ if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_6)) {
+ return (android.hardware.radio.V1_6.IRadio) radioProxy;
+ } else {
+ return (android.hardware.radio.V1_6.IRadio) null;
+ }
+ }
+
/**
* This is a helper function to be called when a RadioIndication callback is called.
@@ -5433,10 +6038,24 @@ public class RIL extends BaseCommands implements CommandsInterface {
*/
@VisibleForTesting
public RILRequest processResponse(RadioResponseInfo responseInfo) {
- int serial = responseInfo.serial;
- int error = responseInfo.error;
- int type = responseInfo.type;
+ return processResponseInternal(responseInfo.serial, responseInfo.error, responseInfo.type);
+ }
+ /**
+ * This is a helper function for V1_6.RadioResponseInfo to be called when a RadioResponse
+ * callback is called.
+ * It takes care of acks, wakelocks, and finds and returns RILRequest corresponding to the
+ * response if one is found.
+ * @param responseInfo RadioResponseInfo received in response callback
+ * @return RILRequest corresponding to the response
+ */
+ @VisibleForTesting
+ public RILRequest processResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
+ return processResponseInternal(responseInfo.serial, responseInfo.error, responseInfo.type);
+ }
+
+ private RILRequest processResponseInternal(int serial, int error, int type) {
RILRequest rr = null;
if (type == RadioResponseType.SOLICITED_ACK) {
@@ -5540,7 +6159,28 @@ public class RIL extends BaseCommands implements CommandsInterface {
*/
@VisibleForTesting
public void processResponseDone(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
- if (responseInfo.error == 0) {
+ processResponseDoneInternal(rr, responseInfo.error, responseInfo.type, ret);
+ }
+
+ /**
+ * This is a helper function to be called at the end of the RadioResponse callbacks using for
+ * V1_6.RadioResponseInfo.
+ * It takes care of sending error response, logging, decrementing wakelock if needed, and
+ * releases the request from memory pool.
+ * @param rr RILRequest for which response callback was called
+ * @param responseInfo RadioResponseInfo received in the callback
+ * @param ret object to be returned to request sender
+ */
+ @VisibleForTesting
+ public void processResponseDone_1_6(
+ RILRequest rr, android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ Object ret) {
+ processResponseDoneInternal(rr, responseInfo.error, responseInfo.type, ret);
+ }
+
+ private void processResponseDoneInternal(
+ RILRequest rr, int rilError, int responseType, Object ret) {
+ if (rilError == 0) {
if (RILJ_LOGD) {
riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
+ " " + retToString(rr.mRequest, ret));
@@ -5548,11 +6188,11 @@ public class RIL extends BaseCommands implements CommandsInterface {
} else {
if (RILJ_LOGD) {
riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
- + " error " + responseInfo.error);
+ + " error " + rilError);
}
- rr.onError(responseInfo.error, ret);
+ rr.onError(rilError, ret);
}
- processResponseCleanUp(rr, responseInfo, ret);
+ processResponseCleanUp(rr, rilError, responseType, ret);
}
/**
@@ -5570,14 +6210,13 @@ public class RIL extends BaseCommands implements CommandsInterface {
riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
+ " request not supported, falling back");
}
- processResponseCleanUp(rr, responseInfo, ret);
+ processResponseCleanUp(rr, responseInfo.error, responseInfo.type, ret);
}
- private void processResponseCleanUp(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
- mMetrics.writeOnRilSolicitedResponse(mPhoneId, rr.mSerial, responseInfo.error,
- rr.mRequest, ret);
+ private void processResponseCleanUp(RILRequest rr, int rilError, int responseType, Object ret) {
if (rr != null) {
- if (responseInfo.type == RadioResponseType.SOLICITED) {
+ mMetrics.writeOnRilSolicitedResponse(mPhoneId, rr.mSerial, rilError, rr.mRequest, ret);
+ if (responseType == RadioResponseType.SOLICITED) {
decrementWakeLock(rr);
}
rr.release();
@@ -5606,7 +6245,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
rr.release();
}
- private WorkSource getDeafultWorkSourceIfInvalid(WorkSource workSource) {
+ private WorkSource getDefaultWorkSourceIfInvalid(WorkSource workSource) {
if (workSource == null) {
workSource = mRILDefaultWorkSource;
}
@@ -5623,7 +6262,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
* There is a WAKE_LOCK_TIMEOUT to release the lock, though it shouldn't
* happen often.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void acquireWakeLock(RILRequest rr, int wakeLockType) {
synchronized (rr) {
if (rr.mWakeLockType != INVALID_WAKELOCK) {
@@ -5688,7 +6327,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
return mRequestList;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void decrementWakeLock(RILRequest rr) {
synchronized (rr) {
switch(rr.mWakeLockType) {
@@ -5723,7 +6362,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean clearWakeLock(int wakeLockType) {
if (wakeLockType == FOR_WAKELOCK) {
synchronized (mWakeLock) {
@@ -5750,7 +6389,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
* @param error is the RIL_Errno sent back
* @param loggable true means to print all requests in mRequestList
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void clearRequestList(int error, boolean loggable) {
RILRequest rr;
synchronized (mRequestList) {
@@ -5803,7 +6442,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
RadioCapability makeStaticRadioCapability() {
// default to UNKNOWN so we fail fast.
int raf = RadioAccessFamily.RAF_UNKNOWN;
@@ -5919,6 +6558,11 @@ public class RIL extends BaseCommands implements CommandsInterface {
void writeMetricsModemRestartEvent(String reason) {
mMetrics.writeModemRestartEvent(mPhoneId, reason);
+ // Write metrics to statsd. Generate metric only when modem reset is detected by the
+ // first instance of RIL to avoid duplicated events.
+ if (mPhoneId == 0) {
+ ModemRestartStats.onModemRestart(reason);
+ }
}
/**
@@ -5926,7 +6570,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
*
* @param rilVer is the version of the ril or -1 if disconnected.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void notifyRegistrantsRilConnectionChanged(int rilVer) {
mRilVersion = rilVer;
if (mRilConnectedRegistrants != null) {
@@ -6009,8 +6653,6 @@ public class RIL extends BaseCommands implements CommandsInterface {
return "GET_CURRENT_CALLS";
case RIL_REQUEST_DIAL:
return "DIAL";
- case RIL_REQUEST_EMERGENCY_DIAL:
- return "EMERGENCY_DIAL";
case RIL_REQUEST_GET_IMSI:
return "GET_IMSI";
case RIL_REQUEST_HANGUP:
@@ -6124,185 +6766,225 @@ public class RIL extends BaseCommands implements CommandsInterface {
case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE:
return "QUERY_AVAILABLE_BAND_MODE";
case RIL_REQUEST_STK_GET_PROFILE:
- return "REQUEST_STK_GET_PROFILE";
+ return "STK_GET_PROFILE";
case RIL_REQUEST_STK_SET_PROFILE:
- return "REQUEST_STK_SET_PROFILE";
+ return "STK_SET_PROFILE";
case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND:
- return "REQUEST_STK_SEND_ENVELOPE_COMMAND";
+ return "STK_SEND_ENVELOPE_COMMAND";
case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE:
- return "REQUEST_STK_SEND_TERMINAL_RESPONSE";
+ return "STK_SEND_TERMINAL_RESPONSE";
case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM:
- return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM";
- case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "REQUEST_EXPLICIT_CALL_TRANSFER";
+ return "STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM";
+ case RIL_REQUEST_EXPLICIT_CALL_TRANSFER:
+ return "EXPLICIT_CALL_TRANSFER";
case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
- return "REQUEST_SET_PREFERRED_NETWORK_TYPE";
+ return "SET_PREFERRED_NETWORK_TYPE";
case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
- return "REQUEST_GET_PREFERRED_NETWORK_TYPE";
+ return "GET_PREFERRED_NETWORK_TYPE";
case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS:
- return "REQUEST_GET_NEIGHBORING_CELL_IDS";
+ return "GET_NEIGHBORING_CELL_IDS";
case RIL_REQUEST_SET_LOCATION_UPDATES:
- return "REQUEST_SET_LOCATION_UPDATES";
+ return "SET_LOCATION_UPDATES";
case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE:
- return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE";
+ return "CDMA_SET_SUBSCRIPTION_SOURCE";
case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE:
- return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE";
+ return "CDMA_SET_ROAMING_PREFERENCE";
case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE:
- return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE";
+ return "CDMA_QUERY_ROAMING_PREFERENCE";
case RIL_REQUEST_SET_TTY_MODE:
- return "RIL_REQUEST_SET_TTY_MODE";
+ return "SET_TTY_MODE";
case RIL_REQUEST_QUERY_TTY_MODE:
- return "RIL_REQUEST_QUERY_TTY_MODE";
+ return "QUERY_TTY_MODE";
case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE:
- return "RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE";
+ return "CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE";
case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE:
- return "RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE";
+ return "CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE";
case RIL_REQUEST_CDMA_FLASH:
- return "RIL_REQUEST_CDMA_FLASH";
+ return "CDMA_FLASH";
case RIL_REQUEST_CDMA_BURST_DTMF:
- return "RIL_REQUEST_CDMA_BURST_DTMF";
+ return "CDMA_BURST_DTMF";
+ case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY:
+ return "CDMA_VALIDATE_AND_WRITE_AKEY";
case RIL_REQUEST_CDMA_SEND_SMS:
- return "RIL_REQUEST_CDMA_SEND_SMS";
+ return "CDMA_SEND_SMS";
case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE:
- return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE";
+ return "CDMA_SMS_ACKNOWLEDGE";
case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG:
- return "RIL_REQUEST_GSM_GET_BROADCAST_CONFIG";
+ return "GSM_GET_BROADCAST_CONFIG";
case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG:
- return "RIL_REQUEST_GSM_SET_BROADCAST_CONFIG";
+ return "GSM_SET_BROADCAST_CONFIG";
+ case RIL_REQUEST_GSM_BROADCAST_ACTIVATION:
+ return "GSM_BROADCAST_ACTIVATION";
case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG:
- return "RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG";
+ return "CDMA_GET_BROADCAST_CONFIG";
case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG:
- return "RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG";
- case RIL_REQUEST_GSM_BROADCAST_ACTIVATION:
- return "RIL_REQUEST_GSM_BROADCAST_ACTIVATION";
- case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY:
- return "RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY";
+ return "CDMA_SET_BROADCAST_CONFIG";
case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION:
- return "RIL_REQUEST_CDMA_BROADCAST_ACTIVATION";
+ return "CDMA_BROADCAST_ACTIVATION";
case RIL_REQUEST_CDMA_SUBSCRIPTION:
- return "RIL_REQUEST_CDMA_SUBSCRIPTION";
+ return "CDMA_SUBSCRIPTION";
case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM:
- return "RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM";
+ return "CDMA_WRITE_SMS_TO_RUIM";
case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM:
- return "RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM";
+ return "CDMA_DELETE_SMS_ON_RUIM";
case RIL_REQUEST_DEVICE_IDENTITY:
- return "RIL_REQUEST_DEVICE_IDENTITY";
+ return "DEVICE_IDENTITY";
+ case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE:
+ return "EXIT_EMERGENCY_CALLBACK_MODE";
case RIL_REQUEST_GET_SMSC_ADDRESS:
- return "RIL_REQUEST_GET_SMSC_ADDRESS";
+ return "GET_SMSC_ADDRESS";
case RIL_REQUEST_SET_SMSC_ADDRESS:
- return "RIL_REQUEST_SET_SMSC_ADDRESS";
- case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE:
- return "REQUEST_EXIT_EMERGENCY_CALLBACK_MODE";
+ return "SET_SMSC_ADDRESS";
case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS:
- return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS";
+ return "REPORT_SMS_MEMORY_STATUS";
case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING:
- return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING";
+ return "REPORT_STK_SERVICE_IS_RUNNING";
case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE:
- return "RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE";
+ return "CDMA_GET_SUBSCRIPTION_SOURCE";
case RIL_REQUEST_ISIM_AUTHENTICATION:
- return "RIL_REQUEST_ISIM_AUTHENTICATION";
+ return "ISIM_AUTHENTICATION";
case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU:
- return "RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU";
+ return "ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU";
case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS:
- return "RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS";
+ return "STK_SEND_ENVELOPE_WITH_STATUS";
case RIL_REQUEST_VOICE_RADIO_TECH:
- return "RIL_REQUEST_VOICE_RADIO_TECH";
+ return "VOICE_RADIO_TECH";
case RIL_REQUEST_GET_CELL_INFO_LIST:
- return "RIL_REQUEST_GET_CELL_INFO_LIST";
+ return "GET_CELL_INFO_LIST";
case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE:
- return "RIL_REQUEST_SET_CELL_INFO_LIST_RATE";
+ return "SET_CELL_INFO_LIST_RATE";
case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
- return "RIL_REQUEST_SET_INITIAL_ATTACH_APN";
- case RIL_REQUEST_SET_DATA_PROFILE:
- return "RIL_REQUEST_SET_DATA_PROFILE";
+ return "SET_INITIAL_ATTACH_APN";
case RIL_REQUEST_IMS_REGISTRATION_STATE:
- return "RIL_REQUEST_IMS_REGISTRATION_STATE";
+ return "IMS_REGISTRATION_STATE";
case RIL_REQUEST_IMS_SEND_SMS:
- return "RIL_REQUEST_IMS_SEND_SMS";
+ return "IMS_SEND_SMS";
case RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC:
- return "RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC";
+ return "SIM_TRANSMIT_APDU_BASIC";
case RIL_REQUEST_SIM_OPEN_CHANNEL:
- return "RIL_REQUEST_SIM_OPEN_CHANNEL";
+ return "SIM_OPEN_CHANNEL";
case RIL_REQUEST_SIM_CLOSE_CHANNEL:
- return "RIL_REQUEST_SIM_CLOSE_CHANNEL";
+ return "SIM_CLOSE_CHANNEL";
case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL:
- return "RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL";
+ return "SIM_TRANSMIT_APDU_CHANNEL";
case RIL_REQUEST_NV_READ_ITEM:
- return "RIL_REQUEST_NV_READ_ITEM";
+ return "NV_READ_ITEM";
case RIL_REQUEST_NV_WRITE_ITEM:
- return "RIL_REQUEST_NV_WRITE_ITEM";
+ return "NV_WRITE_ITEM";
case RIL_REQUEST_NV_WRITE_CDMA_PRL:
- return "RIL_REQUEST_NV_WRITE_CDMA_PRL";
+ return "NV_WRITE_CDMA_PRL";
case RIL_REQUEST_NV_RESET_CONFIG:
- return "RIL_REQUEST_NV_RESET_CONFIG";
+ return "NV_RESET_CONFIG";
case RIL_REQUEST_SET_UICC_SUBSCRIPTION:
- return "RIL_REQUEST_SET_UICC_SUBSCRIPTION";
+ return "SET_UICC_SUBSCRIPTION";
case RIL_REQUEST_ALLOW_DATA:
- return "RIL_REQUEST_ALLOW_DATA";
+ return "ALLOW_DATA";
case RIL_REQUEST_GET_HARDWARE_CONFIG:
return "GET_HARDWARE_CONFIG";
case RIL_REQUEST_SIM_AUTHENTICATION:
- return "RIL_REQUEST_SIM_AUTHENTICATION";
+ return "SIM_AUTHENTICATION";
+ case RIL_REQUEST_GET_DC_RT_INFO:
+ return "GET_DC_RT_INFO";
+ case RIL_REQUEST_SET_DC_RT_INFO_RATE:
+ return "SET_DC_RT_INFO_RATE";
+ case RIL_REQUEST_SET_DATA_PROFILE:
+ return "SET_DATA_PROFILE";
case RIL_REQUEST_SHUTDOWN:
- return "RIL_REQUEST_SHUTDOWN";
- case RIL_REQUEST_SET_RADIO_CAPABILITY:
- return "RIL_REQUEST_SET_RADIO_CAPABILITY";
+ return "SHUTDOWN";
case RIL_REQUEST_GET_RADIO_CAPABILITY:
- return "RIL_REQUEST_GET_RADIO_CAPABILITY";
+ return "GET_RADIO_CAPABILITY";
+ case RIL_REQUEST_SET_RADIO_CAPABILITY:
+ return "SET_RADIO_CAPABILITY";
case RIL_REQUEST_START_LCE:
- return "RIL_REQUEST_START_LCE";
+ return "START_LCE";
case RIL_REQUEST_STOP_LCE:
- return "RIL_REQUEST_STOP_LCE";
+ return "STOP_LCE";
case RIL_REQUEST_PULL_LCEDATA:
- return "RIL_REQUEST_PULL_LCEDATA";
+ return "PULL_LCEDATA";
case RIL_REQUEST_GET_ACTIVITY_INFO:
- return "RIL_REQUEST_GET_ACTIVITY_INFO";
+ return "GET_ACTIVITY_INFO";
case RIL_REQUEST_SET_ALLOWED_CARRIERS:
- return "RIL_REQUEST_SET_ALLOWED_CARRIERS";
+ return "SET_ALLOWED_CARRIERS";
case RIL_REQUEST_GET_ALLOWED_CARRIERS:
- return "RIL_REQUEST_GET_ALLOWED_CARRIERS";
- case RIL_REQUEST_SET_SIM_CARD_POWER:
- return "RIL_REQUEST_SET_SIM_CARD_POWER";
+ return "GET_ALLOWED_CARRIERS";
case RIL_REQUEST_SEND_DEVICE_STATE:
- return "RIL_REQUEST_SEND_DEVICE_STATE";
+ return "SEND_DEVICE_STATE";
case RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER:
- return "RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER";
- case RIL_RESPONSE_ACKNOWLEDGEMENT:
- return "RIL_RESPONSE_ACKNOWLEDGEMENT";
+ return "SET_UNSOLICITED_RESPONSE_FILTER";
+ case RIL_REQUEST_SET_SIM_CARD_POWER:
+ return "SET_SIM_CARD_POWER";
case RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION:
- return "RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION";
+ return "SET_CARRIER_INFO_IMSI_ENCRYPTION";
case RIL_REQUEST_START_NETWORK_SCAN:
- return "RIL_REQUEST_START_NETWORK_SCAN";
+ return "START_NETWORK_SCAN";
case RIL_REQUEST_STOP_NETWORK_SCAN:
- return "RIL_REQUEST_STOP_NETWORK_SCAN";
- case RIL_REQUEST_GET_SLOT_STATUS:
- return "RIL_REQUEST_GET_SLOT_STATUS";
- case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING:
- return "RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING";
+ return "STOP_NETWORK_SCAN";
case RIL_REQUEST_START_KEEPALIVE:
- return "RIL_REQUEST_START_KEEPALIVE";
+ return "START_KEEPALIVE";
case RIL_REQUEST_STOP_KEEPALIVE:
- return "RIL_REQUEST_STOP_KEEPALIVE";
- case RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA:
- return "RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA";
- case RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA:
- return "RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA";
+ return "STOP_KEEPALIVE";
case RIL_REQUEST_ENABLE_MODEM:
- return "RIL_REQUEST_ENABLE_MODEM";
+ return "ENABLE_MODEM";
case RIL_REQUEST_GET_MODEM_STATUS:
- return "RIL_REQUEST_GET_MODEM_STATUS";
+ return "GET_MODEM_STATUS";
+ case RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE:
+ return "CDMA_SEND_SMS_EXPECT_MORE";
+ case RIL_REQUEST_GET_SLOT_STATUS:
+ return "GET_SLOT_STATUS";
+ case RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING:
+ return "SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING";
+ case RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA:
+ return "SET_SIGNAL_STRENGTH_REPORTING_CRITERIA";
+ case RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA:
+ return "SET_LINK_CAPACITY_REPORTING_CRITERIA";
+ case RIL_REQUEST_SET_PREFERRED_DATA_MODEM:
+ return "SET_PREFERRED_DATA_MODEM";
+ case RIL_REQUEST_EMERGENCY_DIAL:
+ return "EMERGENCY_DIAL";
+ case RIL_REQUEST_GET_PHONE_CAPABILITY:
+ return "GET_PHONE_CAPABILITY";
+ case RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG:
+ return "SWITCH_DUAL_SIM_CONFIG";
case RIL_REQUEST_ENABLE_UICC_APPLICATIONS:
- return "RIL_REQUEST_ENABLE_UICC_APPLICATIONS";
+ return "ENABLE_UICC_APPLICATIONS";
case RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT:
- return "RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT";
+ return "GET_UICC_APPLICATIONS_ENABLEMENT";
case RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS:
- return "RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS";
- case RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE:
- return "RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE";
+ return "SET_SYSTEM_SELECTION_CHANNELS";
case RIL_REQUEST_GET_BARRING_INFO:
- return "RIL_REQUEST_GET_BARRING_INFO";
+ return "GET_BARRING_INFO";
case RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION:
- return "RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION";
-
+ return "ENTER_SIM_DEPERSONALIZATION";
+ case RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY:
+ return "ENABLE_NR_DUAL_CONNECTIVITY";
+ case RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED:
+ return "IS_NR_DUAL_CONNECTIVITY_ENABLED";
+ case RIL_REQUEST_ALLOCATE_PDU_SESSION_ID:
+ return "ALLOCATE_PDU_SESSION_ID";
+ case RIL_REQUEST_RELEASE_PDU_SESSION_ID:
+ return "RELEASE_PDU_SESSION_ID";
+ case RIL_REQUEST_START_HANDOVER:
+ return "START_HANDOVER";
+ case RIL_REQUEST_CANCEL_HANDOVER:
+ return "CANCEL_HANDOVER";
+ case RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS:
+ return "GET_SYSTEM_SELECTION_CHANNELS";
+ case RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES:
+ return "GET_HAL_DEVICE_CAPABILITIES";
+ case RIL_REQUEST_SET_DATA_THROTTLING:
+ return "SET_DATA_THROTTLING";
+ case RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP:
+ return "SET_ALLOWED_NETWORK_TYPES_BITMAP";
+ case RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP:
+ return "GET_ALLOWED_NETWORK_TYPES_BITMAP";
+ case RIL_REQUEST_GET_SLICING_CONFIG:
+ return "GET_SLICING_CONFIG";
+ case RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS:
+ return "GET_SIM_PHONEBOOK_RECORDS";
+ case RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD:
+ return "UPDATE_SIM_PHONEBOOK_RECORD";
+ case RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY:
+ return "GET_SIM_PHONEBOOK_CAPACITY";
default: return "<unknown request>";
}
}
@@ -6373,7 +7055,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
case RIL_UNSOL_RESEND_INCALL_MUTE:
return "UNSOL_RESEND_INCALL_MUTE";
case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
- return "CDMA_SUBSCRIPTION_SOURCE_CHANGED";
+ return "UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED";
case RIL_UNSOl_CDMA_PRL_CHANGED:
return "UNSOL_CDMA_PRL_CHANGED";
case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE:
@@ -6387,13 +7069,15 @@ public class RIL extends BaseCommands implements CommandsInterface {
case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED:
return "UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED";
case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED:
- return "RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED";
+ return "UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED";
case RIL_UNSOL_SRVCC_STATE_NOTIFY:
return "UNSOL_SRVCC_STATE_NOTIFY";
case RIL_UNSOL_HARDWARE_CONFIG_CHANGED:
- return "RIL_UNSOL_HARDWARE_CONFIG_CHANGED";
+ return "UNSOL_HARDWARE_CONFIG_CHANGED";
+ case RIL_UNSOL_DC_RT_INFO_CHANGED:
+ return "UNSOL_DC_RT_INFO_CHANGED";
case RIL_UNSOL_RADIO_CAPABILITY:
- return "RIL_UNSOL_RADIO_CAPABILITY";
+ return "UNSOL_RADIO_CAPABILITY";
case RIL_UNSOL_ON_SS:
return "UNSOL_ON_SS";
case RIL_UNSOL_STK_CC_ALPHA_NOTIFY:
@@ -6405,23 +7089,29 @@ public class RIL extends BaseCommands implements CommandsInterface {
case RIL_UNSOL_MODEM_RESTART:
return "UNSOL_MODEM_RESTART";
case RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION:
- return "RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION";
+ return "UNSOL_CARRIER_INFO_IMSI_ENCRYPTION";
case RIL_UNSOL_NETWORK_SCAN_RESULT:
- return "RIL_UNSOL_NETWORK_SCAN_RESULT";
- case RIL_UNSOL_ICC_SLOT_STATUS:
- return "RIL_UNSOL_ICC_SLOT_STATUS";
+ return "UNSOL_NETWORK_SCAN_RESULT";
case RIL_UNSOL_KEEPALIVE_STATUS:
- return "RIL_UNSOL_KEEPALIVE_STATUS";
+ return "UNSOL_KEEPALIVE_STATUS";
+ case RIL_UNSOL_UNTHROTTLE_APN:
+ return "UNSOL_UNTHROTTLE_APN";
+ case RIL_UNSOL_ICC_SLOT_STATUS:
+ return "UNSOL_ICC_SLOT_STATUS";
case RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG:
- return "RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG";
+ return "UNSOL_PHYSICAL_CHANNEL_CONFIG";
case RIL_UNSOL_EMERGENCY_NUMBER_LIST:
- return "RIL_UNSOL_EMERGENCY_NUMBER_LIST";
+ return "UNSOL_EMERGENCY_NUMBER_LIST";
case RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED:
- return "RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED";
+ return "UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED";
case RIL_UNSOL_REGISTRATION_FAILED:
- return "RIL_UNSOL_REGISTRATION_FAILED";
+ return "UNSOL_REGISTRATION_FAILED";
case RIL_UNSOL_BARRING_INFO_CHANGED:
- return "RIL_UNSOL_BARRING_INFO_CHANGED";
+ return "UNSOL_BARRING_INFO_CHANGED";
+ case RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED:
+ return "UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED";
+ case RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED:
+ return "UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED";
default:
return "<unknown response>";
}
@@ -6495,6 +7185,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo));
+ pw.println(" mLastRadioPowerResult=" + mLastRadioPowerResult);
pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
mClientWakelockTracker.dumpClientRequestTracker(pw);
}
@@ -6595,24 +7286,49 @@ public class RIL extends BaseCommands implements CommandsInterface {
return rc;
}
- static LinkCapacityEstimate convertHalLceData(LceDataInfo halData, RIL ril) {
- final LinkCapacityEstimate lce = new LinkCapacityEstimate(
+ static List<LinkCapacityEstimate> convertHalLceData(LceDataInfo halData, RIL ril) {
+ final List<LinkCapacityEstimate> lceList = new ArrayList<>();
+ lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED,
halData.lastHopCapacityKbps,
- Byte.toUnsignedInt(halData.confidenceLevel),
- halData.lceSuspended ? LinkCapacityEstimate.STATUS_SUSPENDED
- : LinkCapacityEstimate.STATUS_ACTIVE);
-
- ril.riljLog("LCE capacity information received:" + lce);
- return lce;
+ LinkCapacityEstimate.INVALID));
+ ril.riljLog("LCE capacity information received:" + lceList);
+ return lceList;
}
- static LinkCapacityEstimate convertHalLceData(
+ static List<LinkCapacityEstimate> convertHalLceData(
android.hardware.radio.V1_2.LinkCapacityEstimate halData, RIL ril) {
- final LinkCapacityEstimate lce = new LinkCapacityEstimate(
+ final List<LinkCapacityEstimate> lceList = new ArrayList<>();
+ lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED,
halData.downlinkCapacityKbps,
- halData.uplinkCapacityKbps);
- ril.riljLog("LCE capacity information received:" + lce);
- return lce;
+ halData.uplinkCapacityKbps));
+ ril.riljLog("LCE capacity information received:" + lceList);
+ return lceList;
+ }
+
+ static List<LinkCapacityEstimate> convertHalLceData(
+ android.hardware.radio.V1_6.LinkCapacityEstimate halData, RIL ril) {
+ final List<LinkCapacityEstimate> lceList = new ArrayList<>();
+ int primaryDownlinkCapacityKbps = halData.downlinkCapacityKbps;
+ int primaryUplinkCapacityKbps = halData.uplinkCapacityKbps;
+ if (primaryDownlinkCapacityKbps != LinkCapacityEstimate.INVALID
+ && halData.secondaryDownlinkCapacityKbps != LinkCapacityEstimate.INVALID) {
+ primaryDownlinkCapacityKbps =
+ halData.downlinkCapacityKbps - halData.secondaryDownlinkCapacityKbps;
+ }
+ if (primaryUplinkCapacityKbps != LinkCapacityEstimate.INVALID
+ && halData.secondaryUplinkCapacityKbps != LinkCapacityEstimate.INVALID) {
+ primaryUplinkCapacityKbps =
+ halData.uplinkCapacityKbps - halData.secondaryUplinkCapacityKbps;
+ }
+
+ lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_PRIMARY,
+ primaryDownlinkCapacityKbps,
+ primaryUplinkCapacityKbps));
+ lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_SECONDARY,
+ halData.secondaryDownlinkCapacityKbps,
+ halData.secondaryUplinkCapacityKbps));
+ ril.riljLog("LCE capacity information received:" + lceList);
+ return lceList;
}
/**
@@ -6738,6 +7454,23 @@ public class RIL extends BaseCommands implements CommandsInterface {
return response;
}
+ /**
+ * Convert CellInfo defined in 1.6/types.hal to CellInfo type.
+ * @param records List of CellInfo defined in 1.6/types.hal.
+ * @return List of converted CellInfo object.
+ */
+ @VisibleForTesting
+ public static ArrayList<CellInfo> convertHalCellInfoList_1_6(
+ ArrayList<android.hardware.radio.V1_6.CellInfo> records) {
+ ArrayList<CellInfo> response = new ArrayList<>(records.size());
+
+ final long nanotime = SystemClock.elapsedRealtimeNanos();
+ for (android.hardware.radio.V1_6.CellInfo record : records) {
+ response.add(CellInfo.create(record, nanotime));
+ }
+ return response;
+ }
+
private static LinkAddress createLinkAddressFromString(String addressString) {
return createLinkAddressFromString(addressString, 0, LinkAddress.LIFETIME_UNKNOWN,
LinkAddress.LIFETIME_UNKNOWN);
@@ -6771,7 +7504,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
/**
- * Convert SetupDataCallResult defined in 1.0, 1.4, or 1.5 types.hal into DataCallResponse
+ * Convert SetupDataCallResult defined in 1.0, 1.4, 1.5 or 1.6 types.hal into DataCallResponse
* @param dcResult setup data call result
* @return converted DataCallResponse object
*/
@@ -6779,15 +7512,25 @@ public class RIL extends BaseCommands implements CommandsInterface {
public static DataCallResponse convertDataCallResult(Object dcResult) {
if (dcResult == null) return null;
- int cause, suggestedRetryTime, cid, active, mtu, mtuV4, mtuV6;
+ int cause, cid, active, mtu, mtuV4, mtuV6;
+ long suggestedRetryTime;
String ifname;
int protocolType;
String[] addresses = null;
String[] dnses = null;
String[] gateways = null;
String[] pcscfs = null;
+ Qos defaultQos = null;
+
+ @HandoverFailureMode
+ int handoverFailureMode = DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY;
+
+ int pduSessionId = DataCallResponse.PDU_SESSION_ID_NOT_SET;
List<LinkAddress> laList = new ArrayList<>();
+ List<QosBearerSession> qosSessions = new ArrayList<>();
+ NetworkSliceInfo sliceInfo = null;
+ List<TrafficDescriptor> trafficDescriptors = new ArrayList<>();
if (dcResult instanceof android.hardware.radio.V1_0.SetupDataCallResult) {
final android.hardware.radio.V1_0.SetupDataCallResult result =
@@ -6854,6 +7597,32 @@ public class RIL extends BaseCommands implements CommandsInterface {
mtu = Math.max(result.mtuV4, result.mtuV6);
mtuV4 = result.mtuV4;
mtuV6 = result.mtuV6;
+ } else if (dcResult instanceof android.hardware.radio.V1_6.SetupDataCallResult) {
+ final android.hardware.radio.V1_6.SetupDataCallResult result =
+ (android.hardware.radio.V1_6.SetupDataCallResult) dcResult;
+ cause = result.cause;
+ suggestedRetryTime = result.suggestedRetryTime;
+ cid = result.cid;
+ active = result.active;
+ protocolType = result.type;
+ ifname = result.ifname;
+ laList = result.addresses.stream().map(la -> createLinkAddressFromString(
+ la.address, la.properties, la.deprecationTime, la.expirationTime))
+ .collect(Collectors.toList());
+ dnses = result.dnses.stream().toArray(String[]::new);
+ gateways = result.gateways.stream().toArray(String[]::new);
+ pcscfs = result.pcscf.stream().toArray(String[]::new);
+ mtu = Math.max(result.mtuV4, result.mtuV6);
+ mtuV4 = result.mtuV4;
+ mtuV6 = result.mtuV6;
+ handoverFailureMode = result.handoverFailureMode;
+ pduSessionId = result.pduSessionId;
+ defaultQos = Qos.create(result.defaultQos);
+ qosSessions = result.qosSessions.stream().map(session ->
+ QosBearerSession.create(session)).collect(Collectors.toList());
+ sliceInfo = convertToSliceInfo(result.sliceInfo);
+ trafficDescriptors = result.trafficDescriptors.stream().map(td ->
+ convertToTrafficDescriptor(td)).collect(Collectors.toList());
} else {
Rlog.e(RILJ_LOG_TAG, "Unsupported SetupDataCallResult " + dcResult);
return null;
@@ -6906,7 +7675,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
return new DataCallResponse.Builder()
.setCause(cause)
- .setSuggestedRetryTime(suggestedRetryTime)
+ .setRetryDurationMillis(suggestedRetryTime)
.setId(cid)
.setLinkStatus(active)
.setProtocolType(protocolType)
@@ -6918,9 +7687,49 @@ public class RIL extends BaseCommands implements CommandsInterface {
.setMtu(mtu)
.setMtuV4(mtuV4)
.setMtuV6(mtuV6)
+ .setHandoverFailureMode(handoverFailureMode)
+ .setPduSessionId(pduSessionId)
+ .setDefaultQos(defaultQos)
+ .setQosBearerSessions(qosSessions)
+ .setSliceInfo(sliceInfo)
+ .setTrafficDescriptors(trafficDescriptors)
.build();
}
+ private static NetworkSliceInfo convertToSliceInfo(OptionalSliceInfo optionalSliceInfo) {
+ if (optionalSliceInfo.getDiscriminator() == OptionalSliceInfo.hidl_discriminator.noinit) {
+ return null;
+ }
+
+ android.hardware.radio.V1_6.SliceInfo si = optionalSliceInfo.value();
+ NetworkSliceInfo.Builder builder =
+ new NetworkSliceInfo.Builder()
+ .setSliceServiceType(si.sst)
+ .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
+ if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
+ builder
+ .setSliceDifferentiator(si.sliceDifferentiator)
+ .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD);
+ }
+ return builder.build();
+ }
+
+ private static TrafficDescriptor convertToTrafficDescriptor(
+ android.hardware.radio.V1_6.TrafficDescriptor td) {
+ String dnn = td.dnn.getDiscriminator() == OptionalDnn.hidl_discriminator.noinit
+ ? null : td.dnn.value();
+ byte[] osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit
+ ? null : arrayListToPrimitiveArray(td.osAppId.value().osAppId);
+ TrafficDescriptor.Builder builder = new TrafficDescriptor.Builder();
+ if (dnn != null) {
+ builder.setDataNetworkName(dnn);
+ }
+ if (osAppId != null) {
+ builder.setOsAppId(osAppId);
+ }
+ return builder.build();
+ }
+
/**
* Convert SetupDataCallResult defined in 1.0 or 1.4/types.hal into DataCallResponse
* @param dataCallResultList List of SetupDataCallResult defined in 1.0 or 1.4/types.hal
diff --git a/src/java/com/android/internal/telephony/RILRequest.java b/src/java/com/android/internal/telephony/RILRequest.java
index 6e2bafe03a..fff8de690a 100644
--- a/src/java/com/android/internal/telephony/RILRequest.java
+++ b/src/java/com/android/internal/telephony/RILRequest.java
@@ -232,20 +232,20 @@ public class RILRequest {
}
@UnsupportedAppUsage
- void onError(int error, Object ret) {
- CommandException ex;
-
- ex = CommandException.fromRilErrno(error);
+ void onError(final int error, final Object ret) {
+ final CommandException ex = CommandException.fromRilErrno(error);
+ final Message result = mResult;
if (RIL.RILJ_LOGD) {
Rlog.d(LOG_TAG, serialString() + "< "
+ RIL.requestToString(mRequest)
- + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret));
+ + " error: " + ex + " ret=" + RIL.retToString(mRequest, ret)
+ + " result=" + result);
}
- if (mResult != null) {
- AsyncResult.forMessage(mResult, ret, ex);
- mResult.sendToTarget();
+ if (result != null && result.getTarget() != null) {
+ AsyncResult.forMessage(result, ret, ex);
+ result.sendToTarget();
}
}
}
diff --git a/src/java/com/android/internal/telephony/RadioCapability.java b/src/java/com/android/internal/telephony/RadioCapability.java
index 34752011ca..23f7f39b8c 100644
--- a/src/java/com/android/internal/telephony/RadioCapability.java
+++ b/src/java/com/android/internal/telephony/RadioCapability.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.telephony.TelephonyManager;
/**
@@ -181,7 +182,7 @@ public class RadioCapability {
*
* @return radio access family
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getRadioAccessFamily() {
return mRadioAccessFamily;
}
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 3ee4d4818c..32b24fe379 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -16,12 +16,15 @@
package com.android.internal.telephony;
+import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_NSA;
+import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_SA;
+
import static com.android.internal.telephony.RILConstants.RADIO_NOT_AVAILABLE;
import static com.android.internal.telephony.RILConstants.REQUEST_NOT_SUPPORTED;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_PHONE_CAPABILITY;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLOT_STATUS;
-import static com.android.internal.telephony.RILConstants
- .RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG;
@@ -30,7 +33,6 @@ import android.hardware.radio.V1_0.RadioResponseInfo;
import android.hardware.radio.V1_0.RadioResponseType;
import android.hardware.radio.config.V1_0.IRadioConfig;
import android.hardware.radio.config.V1_1.ModemsConfig;
-import android.net.ConnectivityManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HwBinder;
@@ -38,6 +40,7 @@ import android.os.Message;
import android.os.Registrant;
import android.os.RemoteException;
import android.os.WorkSource;
+import android.telephony.TelephonyManager;
import android.util.SparseArray;
import com.android.internal.telephony.uicc.IccSlotStatus;
@@ -45,6 +48,7 @@ import com.android.telephony.Rlog;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicLong;
@@ -64,6 +68,8 @@ public class RadioConfig extends Handler {
private static final HalVersion RADIO_CONFIG_HAL_VERSION_1_1 = new HalVersion(1, 1);
+ private static final HalVersion RADIO_CONFIG_HAL_VERSION_1_3 = new HalVersion(1, 3);
+
private final boolean mIsMobileNetworkSupported;
private volatile IRadioConfig mRadioConfigProxy = null;
// IRadioConfig version
@@ -75,7 +81,9 @@ public class RadioConfig extends Handler {
private final SparseArray<RILRequest> mRequestList = new SparseArray<RILRequest>();
/* default work source which will blame phone process */
private final WorkSource mDefaultWorkSource;
+ private final int[] mDeviceNrCapabilities;
private static RadioConfig sRadioConfig;
+ private static final Object sLock = new Object();
protected Registrant mSimSlotStatusRegistrant;
@@ -88,27 +96,67 @@ public class RadioConfig extends Handler {
}
}
- private RadioConfig(Context context) {
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- mIsMobileNetworkSupported = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ private boolean isMobileDataCapable(Context context) {
+ final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ if (tm == null) {
+ return false;
+ }
+ return tm.isDataCapable();
+ }
+
+ private RadioConfig(Context context, HalVersion radioHalVersion) {
+ mIsMobileNetworkSupported = isMobileDataCapable(context);
- mRadioConfigResponse = new RadioConfigResponse(this);
+ mRadioConfigResponse = new RadioConfigResponse(this, radioHalVersion);
mRadioConfigIndication = new RadioConfigIndication(this);
mServiceDeathRecipient = new ServiceDeathRecipient();
mDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid,
context.getPackageName());
+
+ boolean is5gStandalone = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_telephony5gStandalone);
+ boolean is5gNonStandalone = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_telephony5gNonStandalone);
+
+ if (!is5gStandalone && !is5gNonStandalone) {
+ mDeviceNrCapabilities = new int[0];
+ } else {
+ List<Integer> list = new ArrayList<>();
+ if (is5gNonStandalone) {
+ list.add(DEVICE_NR_CAPABILITY_NSA);
+ }
+ if (is5gStandalone) {
+ list.add(DEVICE_NR_CAPABILITY_SA);
+ }
+ mDeviceNrCapabilities = list.stream().mapToInt(Integer::valueOf).toArray();
+ }
}
/**
* Returns the singleton static instance of RadioConfig
*/
- public static RadioConfig getInstance(Context context) {
- if (sRadioConfig == null) {
- sRadioConfig = new RadioConfig(context);
+ public static RadioConfig getInstance() {
+ synchronized (sLock) {
+ if (sRadioConfig == null) {
+ throw new RuntimeException(
+ "RadioConfig.getInstance can't be called before make()");
+ }
+ return sRadioConfig;
+ }
+ }
+
+ /**
+ * Makes the radio config based on the context and the radio hal version passed in
+ */
+ public static RadioConfig make(Context c, HalVersion radioHalVersion) {
+ synchronized (sLock) {
+ if (sRadioConfig != null) {
+ throw new RuntimeException("RadioConfig.make() should only be called once");
+ }
+ sRadioConfig = new RadioConfig(c, radioHalVersion);
+ return sRadioConfig;
}
- return sRadioConfig;
}
@Override
@@ -194,14 +242,26 @@ public class RadioConfig extends Handler {
private void updateRadioConfigProxy() {
try {
+
// Try to get service from different versions.
try {
- mRadioConfigProxy = android.hardware.radio.config.V1_1.IRadioConfig.getService(
+ mRadioConfigProxy = android.hardware.radio.config.V1_3.IRadioConfig.getService(
true);
- mRadioConfigVersion = RADIO_CONFIG_HAL_VERSION_1_1;
+ mRadioConfigVersion = RADIO_CONFIG_HAL_VERSION_1_3;
} catch (NoSuchElementException e) {
}
+
+ if (mRadioConfigProxy == null) {
+ // Try to get service from different versions.
+ try {
+ mRadioConfigProxy = android.hardware.radio.config.V1_1.IRadioConfig.getService(
+ true);
+ mRadioConfigVersion = RADIO_CONFIG_HAL_VERSION_1_1;
+ } catch (NoSuchElementException e) {
+ }
+ }
+
if (mRadioConfigProxy == null) {
try {
mRadioConfigProxy = android.hardware.radio.config.V1_0
@@ -273,6 +333,31 @@ public class RadioConfig extends Handler {
}
/**
+ * This is a helper function to be called when a RadioConfigResponse callback is called.
+ * It finds and returns RILRequest corresponding to the response if one is found.
+ * @param responseInfo RadioResponseInfo received in response callback
+ * @return RILRequest corresponding to the response
+ */
+ public RILRequest processResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
+ int serial = responseInfo.serial;
+ int error = responseInfo.error;
+ int type = responseInfo.type;
+
+ if (type != RadioResponseType.SOLICITED) {
+ loge("processResponse: Unexpected response type " + type);
+ }
+
+ RILRequest rr = findAndRemoveRequestFromList(serial);
+ if (rr == null) {
+ loge("processResponse: Unexpected response! serial: " + serial + " error: " + error);
+ return null;
+ }
+
+ return rr;
+ }
+
+ /**
* Wrapper function for IRadioConfig.getSimSlotsStatus().
*/
public void getSimSlotsStatus(Message result) {
@@ -403,6 +488,8 @@ public class RadioConfig extends Handler {
return "SET_PREFERRED_DATA_MODEM";
case RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG:
return "SWITCH_DUAL_SIM_CONFIG";
+ case RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES:
+ return "GET_HAL_DEVICE_CAPABILITIES";
default:
return "<unknown request " + request + ">";
}
@@ -456,6 +543,56 @@ public class RadioConfig extends Handler {
}
}
+ /**
+ * Gets the hal capabilities from the device.
+ */
+ public void getHalDeviceCapabilities(Message result) {
+ IRadioConfig radioConfigProxy = getRadioConfigProxy(Message.obtain(result));
+ if (radioConfigProxy != null
+ && mRadioConfigVersion.greaterOrEqual(RADIO_CONFIG_HAL_VERSION_1_3)) {
+ android.hardware.radio.config.V1_3.IRadioConfig radioConfigProxy13 =
+ (android.hardware.radio.config.V1_3.IRadioConfig) radioConfigProxy;
+ RILRequest rr = obtainRequest(RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES,
+ result, mDefaultWorkSource);
+
+ if (DBG) {
+ logd(rr.serialString() + "> " + requestToString(rr.mRequest));
+ }
+
+ try {
+ mRadioConfigVersion = RADIO_CONFIG_HAL_VERSION_1_3;
+ radioConfigProxy13.getHalDeviceCapabilities(rr.mSerial);
+
+ } catch (RemoteException | RuntimeException e) {
+ resetProxyAndRequestList("getHalDeviceCapabilities", e);
+ }
+ } else {
+ if (result != null) {
+ if (DBG) {
+ logd("RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES > REQUEST_NOT_SUPPORTED");
+ }
+ AsyncResult.forMessage(result,
+ /* Send response such that all capabilities are supported (depending on
+ the hal version of course.) */
+ mRadioConfigResponse.getFullCapabilitySet(),
+ CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+ result.sendToTarget();
+ } else {
+ if (DBG) {
+ logd("RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES > REQUEST_NOT_SUPPORTED "
+ + "on complete message not set.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the device's nr capability.
+ */
+ public int[] getDeviceNrCapabilities() {
+ return mDeviceNrCapabilities;
+ }
+
static ArrayList<IccSlotStatus> convertHalSlotStatus(
ArrayList<android.hardware.radio.config.V1_0.SimSlotStatus> halSlotStatusList) {
ArrayList<IccSlotStatus> response = new ArrayList<IccSlotStatus>(halSlotStatusList.size());
diff --git a/src/java/com/android/internal/telephony/RadioConfigResponse.java b/src/java/com/android/internal/telephony/RadioConfigResponse.java
index 8e509de77e..3829c168d6 100644
--- a/src/java/com/android/internal/telephony/RadioConfigResponse.java
+++ b/src/java/com/android/internal/telephony/RadioConfigResponse.java
@@ -16,18 +16,31 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager
+ .CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE;
+import static android.telephony.TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED;
+import static android.telephony.TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE;
+import static android.telephony.TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM;
+import static android.telephony.TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED;
+import static android.telephony.TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING;
+import static android.telephony.TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK;
+import static android.telephony.TelephonyManager.RadioInterfaceCapability;
+
import android.hardware.radio.V1_0.RadioError;
import android.hardware.radio.V1_0.RadioResponseInfo;
import android.hardware.radio.config.V1_1.ModemsConfig;
-import android.hardware.radio.config.V1_2.IRadioConfigResponse;
+import android.hardware.radio.config.V1_3.IRadioConfigResponse;
import android.telephony.ModemInfo;
import android.telephony.PhoneCapability;
-import com.android.telephony.Rlog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.uicc.IccSlotStatus;
+import com.android.telephony.Rlog;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* This class is the implementation of IRadioConfigResponse interface.
@@ -36,9 +49,11 @@ public class RadioConfigResponse extends IRadioConfigResponse.Stub {
private static final String TAG = "RadioConfigResponse";
private final RadioConfig mRadioConfig;
+ private final HalVersion mRadioHalVersion;
- public RadioConfigResponse(RadioConfig radioConfig) {
+ public RadioConfigResponse(RadioConfig radioConfig, HalVersion radioHalVersion) {
mRadioConfig = radioConfig;
+ mRadioHalVersion = radioHalVersion;
}
/**
@@ -120,7 +135,6 @@ public class RadioConfigResponse extends IRadioConfigResponse.Stub {
// TODO b/121394331: clean up V1_1.PhoneCapability fields.
int maxActiveVoiceCalls = 0;
int maxActiveData = phoneCapability.maxActiveData;
- int max5G = 0;
boolean validationBeforeSwitchSupported = phoneCapability.isInternetLingeringSupported;
List<ModemInfo> logicalModemList = new ArrayList();
@@ -129,8 +143,8 @@ public class RadioConfigResponse extends IRadioConfigResponse.Stub {
logicalModemList.add(new ModemInfo(modemInfo.modemId));
}
- return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
- validationBeforeSwitchSupported);
+ return new PhoneCapability(maxActiveVoiceCalls, maxActiveData, logicalModemList,
+ validationBeforeSwitchSupported, mRadioConfig.getDeviceNrCapabilities());
}
/**
* Response function for IRadioConfig.getPhoneCapability().
@@ -226,4 +240,85 @@ public class RadioConfigResponse extends IRadioConfigResponse.Stub {
Rlog.e(TAG, "getModemsConfigResponse: Error " + responseInfo.toString());
}
}
+
+ /**
+ * Response function IRadioConfig.getHalDeviceCapabilities()
+ */
+ public void getHalDeviceCapabilitiesResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ boolean modemReducedFeatureSet1) {
+
+ // convert hal device capabilities to RadioInterfaceCapabilities
+
+ RILRequest rr = mRadioConfig.processResponse_1_6(responseInfo);
+ if (rr != null) {
+ // The response is compatible with Radio 1.6, it means the modem
+ // supports setAllowedNetworkTypeBitmap.
+
+ final Set<String> ret = getCaps(mRadioHalVersion, modemReducedFeatureSet1);
+
+ if (responseInfo.error == RadioError.NONE) {
+ // send response
+ RadioResponse.sendMessageResponse(rr.mResult, ret);
+ Rlog.d(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest));
+ } else {
+ rr.onError(responseInfo.error, ret);
+ Rlog.e(TAG, rr.serialString() + "< "
+ + mRadioConfig.requestToString(rr.mRequest) + " error "
+ + responseInfo.error);
+ }
+ } else {
+ Rlog.e(TAG, "getHalDeviceCapabilities: Error " + responseInfo.toString());
+ }
+ }
+
+ /**
+ * Returns all capabilities supported in the most recent radio hal version.
+ * <p/>
+ * Used in the {@link RILConstants.REQUEST_NOT_SUPPORTED} case.
+ *
+ * @return all capabilities
+ */
+ @RadioInterfaceCapability
+ public Set<String> getFullCapabilitySet() {
+ return getCaps(mRadioHalVersion, false);
+ }
+
+ /**
+ * Create capabilities based off of the radio hal version and feature set configurations.
+ */
+ @VisibleForTesting
+ public static Set<String> getCaps(HalVersion radioHalVersion,
+ boolean modemReducedFeatureSet1) {
+ final Set<String> caps = new HashSet<>();
+
+ if (radioHalVersion.equals(RIL.RADIO_HAL_VERSION_UNKNOWN)) {
+ // If the Radio HAL is UNKNOWN, no capabilities will present themselves.
+ Rlog.e(TAG, "Radio Hal Version is UNKNOWN!");
+ }
+
+ Rlog.d(TAG, "Radio Hal Version = " + radioHalVersion.toString());
+ if (radioHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)) {
+ caps.add(CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK);
+ Rlog.d(TAG, "CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK");
+
+ if (!modemReducedFeatureSet1) {
+ caps.add(CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE);
+ Rlog.d(TAG, "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE");
+ caps.add(CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE);
+ Rlog.d(TAG, "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE");
+ caps.add(CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING);
+ Rlog.d(TAG, "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING");
+ caps.add(CAPABILITY_SLICING_CONFIG_SUPPORTED);
+ Rlog.d(TAG, "CAPABILITY_SLICING_CONFIG_SUPPORTED");
+ caps.add(CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ Rlog.d(TAG, "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED");
+ } else {
+ caps.add(CAPABILITY_SIM_PHONEBOOK_IN_MODEM);
+ Rlog.d(TAG, "CAPABILITY_SIM_PHONEBOOK_IN_MODEM");
+ }
+ }
+ return caps;
+ }
}
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index bf8ae5f025..d18efdcb9c 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -49,6 +49,8 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESTRICTED_STATE_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RIL_CONNECTED;
@@ -65,6 +67,7 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_SESSION_
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SUPP_SVC_NOTIFICATION;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UNTHROTTLE_APN;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOl_CDMA_PRL_CHANGED;
@@ -84,14 +87,18 @@ import android.hardware.radio.V1_0.SsInfoData;
import android.hardware.radio.V1_0.StkCcUnsolSsResult;
import android.hardware.radio.V1_0.SuppSvcNotification;
import android.hardware.radio.V1_2.CellConnectionStatus;
-import android.hardware.radio.V1_5.IRadioIndication;
+import android.hardware.radio.V1_6.IRadioIndication;
+import android.hardware.radio.V1_6.PhonebookRecordInfo;
+import android.hardware.radio.V1_6.PhysicalChannelConfig.Band;
import android.os.AsyncResult;
+import android.os.RemoteException;
import android.sysprop.TelephonyProperties;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.AnomalyReporter;
import android.telephony.BarringInfo;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PcoData;
import android.telephony.PhysicalChannelConfig;
@@ -111,6 +118,8 @@ import com.android.internal.telephony.gsm.SsData;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.uicc.IccRefreshResponse;
import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.ReceivedPhonebookRecords;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import java.util.ArrayList;
import java.util.List;
@@ -166,9 +175,10 @@ public class RadioIndication extends IRadioIndication.Stub {
byte[] pduArray = RIL.arrayListToPrimitiveArray(pdu);
if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
- SmsMessage sms = SmsMessage.newFromCMT(pduArray);
+ SmsMessageBase smsb = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pduArray);
if (mRil.mGsmSmsRegistrant != null) {
- mRil.mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
+ mRil.mGsmSmsRegistrant.notifyRegistrant(
+ new AsyncResult(null, smsb == null ? null : new SmsMessage(smsb), null));
}
}
@@ -253,7 +263,23 @@ public class RadioIndication extends IRadioIndication.Stub {
android.hardware.radio.V1_2.LinkCapacityEstimate lce) {
mRil.processIndication(indicationType);
- LinkCapacityEstimate response = RIL.convertHalLceData(lce, mRil);
+ List<LinkCapacityEstimate> response = RIL.convertHalLceData(lce, mRil);
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+
+ if (mRil.mLceInfoRegistrants != null) {
+ mRil.mLceInfoRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+ }
+ }
+
+ /**
+ * Indicates current link capacity estimate.
+ */
+ public void currentLinkCapacityEstimate_1_6(int indicationType,
+ android.hardware.radio.V1_6.LinkCapacityEstimate lce) {
+ mRil.processIndication(indicationType);
+
+ List<LinkCapacityEstimate> response = RIL.convertHalLceData(lce, mRil);
if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
@@ -296,6 +322,23 @@ public class RadioIndication extends IRadioIndication.Stub {
}
/**
+ * Indicates the current signal strength of the camped or primary serving cell.
+ */
+ public void currentSignalStrength_1_6(int indicationType,
+ android.hardware.radio.V1_6.SignalStrength signalStrength) {
+
+ mRil.processIndication(indicationType);
+
+ SignalStrength ss = new SignalStrength(signalStrength);
+
+ if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+
+ if (mRil.mSignalStrengthRegistrant != null) {
+ mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult(null, ss, null));
+ }
+ }
+
+ /**
* Indicates current physical channel configuration.
*/
public void currentPhysicalChannelConfigs_1_4(int indicationType,
@@ -307,6 +350,15 @@ public class RadioIndication extends IRadioIndication.Stub {
/**
* Indicates current physical channel configuration.
*/
+ public void currentPhysicalChannelConfigs_1_6(int indicationType,
+ ArrayList<android.hardware.radio.V1_6.PhysicalChannelConfig> configs) {
+ mRil.processIndication(indicationType);
+ physicalChannelConfigsIndication(configs);
+ }
+
+ /**
+ * Indicates current physical channel configuration.
+ */
public void currentPhysicalChannelConfigs(int indicationType,
ArrayList<android.hardware.radio.V1_2.PhysicalChannelConfig> configs) {
mRil.processIndication(indicationType);
@@ -358,6 +410,18 @@ public class RadioIndication extends IRadioIndication.Stub {
responseDataCallListChanged(indicationType, dcList);
}
+ /** Indicates current data call list with radio HAL 1.6. */
+ public void dataCallListChanged_1_6(int indicationType,
+ ArrayList<android.hardware.radio.V1_6.SetupDataCallResult> dcList) {
+ responseDataCallListChanged(indicationType, dcList);
+ }
+
+ @Override
+ public void unthrottleApn(int indicationType, String apn)
+ throws RemoteException {
+ responseApnUnthrottled(indicationType, apn);
+ }
+
public void suppSvcNotify(int indicationType, SuppSvcNotification suppSvcNotification) {
mRil.processIndication(indicationType);
@@ -777,6 +841,18 @@ public class RadioIndication extends IRadioIndication.Stub {
mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
+ /** Get unsolicited message for cellInfoList using HAL V1_5 */
+ public void cellInfoList_1_6(int indicationType,
+ ArrayList<android.hardware.radio.V1_6.CellInfo> records) {
+ mRil.processIndication(indicationType);
+
+ ArrayList<CellInfo> response = RIL.convertHalCellInfoList_1_6(records);
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
+
+ mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+ }
+
/** Get unsolicited message for uicc applications enablement changes. */
public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) {
mRil.processIndication(indicationType);
@@ -812,6 +888,12 @@ public class RadioIndication extends IRadioIndication.Stub {
responseNetworkScan_1_5(indicationType, result);
}
+ /** Incremental network scan results with HAL V1_6 */
+ public void networkScanResult_1_6(int indicationType,
+ android.hardware.radio.V1_6.NetworkScanResult result) {
+ responseNetworkScan_1_6(indicationType, result);
+ }
+
public void imsNetworkStateChanged(int indicationType) {
mRil.processIndication(indicationType);
@@ -932,7 +1014,7 @@ public class RadioIndication extends IRadioIndication.Stub {
public void lceData(int indicationType, LceDataInfo lce) {
mRil.processIndication(indicationType);
- LinkCapacityEstimate response = RIL.convertHalLceData(lce, mRil);
+ List<LinkCapacityEstimate> response = RIL.convertHalLceData(lce, mRil);
if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
@@ -995,6 +1077,47 @@ public class RadioIndication extends IRadioIndication.Stub {
}
/**
+ * Indicates when the phonebook is changed.
+ *
+ * @param indicationType RadioIndicationType
+ */
+ public void simPhonebookChanged(int indicationType) {
+ mRil.processIndication(indicationType);
+
+ if (RIL.RILJ_LOGD) {
+ mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED);
+ }
+
+ mRil.mSimPhonebookChangedRegistrants.notifyRegistrants();
+ }
+
+ /**
+ * Indicates the content of all the used records in the SIM phonebook..
+ * @param indicationType RadioIndicationType
+ * @param records Content of the SIM phonebook records
+ */
+ public void simPhonebookRecordsReceived(int indicationType, byte status,
+ ArrayList<PhonebookRecordInfo> records) {
+ mRil.processIndication(indicationType);
+
+ List<SimPhonebookRecord> simPhonebookRecords = new ArrayList<SimPhonebookRecord>();
+
+ for (PhonebookRecordInfo record : records) {
+ simPhonebookRecords.add(new SimPhonebookRecord(record));
+ }
+
+ if (RIL.RILJ_LOGD) {
+ mRil.unsljLogRet(RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED,
+ "status = " + status +
+ " received " + records.size() + " records");
+ }
+
+ mRil.mSimPhonebookRecordsReceivedRegistrants.notifyRegistrants(
+ new AsyncResult(null,
+ new ReceivedPhonebookRecords(status, simPhonebookRecords), null));
+ }
+
+ /**
* Indicate that a registration failure has occurred.
*
* @param cellIdentity a CellIdentity the CellIdentity of the Cell
@@ -1009,8 +1132,8 @@ public class RadioIndication extends IRadioIndication.Stub {
@NetworkRegistrationInfo.Domain int domain,
int causeCode, int additionalCauseCode) {
mRil.processIndication(indicationType);
-
- if (cellIdentity == null
+ CellIdentity ci = CellIdentity.create(cellIdentity);
+ if (ci == null
|| TextUtils.isEmpty(chosenPlmn)
|| (domain & NetworkRegistrationInfo.DOMAIN_CS_PS) == 0
|| (domain & ~NetworkRegistrationInfo.DOMAIN_CS_PS) != 0
@@ -1024,8 +1147,6 @@ public class RadioIndication extends IRadioIndication.Stub {
return;
}
- CellIdentity ci = CellIdentity.create(cellIdentity);
-
mRil.mRegistrationFailedRegistrant.notifyRegistrant(
new AsyncResult(
null,
@@ -1100,7 +1221,7 @@ public class RadioIndication extends IRadioIndication.Stub {
builder.setFrequencyRange(config.rfInfo.range());
break;
case android.hardware.radio.V1_4.RadioFrequencyInfo.hidl_discriminator.channelNumber:
- builder.setChannelNumber(config.rfInfo.channelNumber());
+ builder.setDownlinkChannelNumber(config.rfInfo.channelNumber());
break;
default:
mRil.riljLoge("Unsupported frequency type " + config.rfInfo.getDiscriminator());
@@ -1121,33 +1242,87 @@ public class RadioIndication extends IRadioIndication.Stub {
}
}
+ /**
+ * Set the band from the physical channel config.
+ *
+ * @param builder the builder of {@link PhysicalChannelConfig}.
+ * @param config physical channel config from ril.
+ */
+ public void setBandToBuilder(PhysicalChannelConfig.Builder builder,
+ android.hardware.radio.V1_6.PhysicalChannelConfig config) {
+
+ android.hardware.radio.V1_6.PhysicalChannelConfig.Band band = config.band;
+
+ switch (band.getDiscriminator()) {
+ case Band.hidl_discriminator.geranBand:
+ builder.setBand(band.geranBand());
+ break;
+ case Band.hidl_discriminator.utranBand:
+ builder.setBand(band.utranBand());
+ break;
+ case Band.hidl_discriminator.eutranBand:
+ builder.setBand(band.eutranBand());
+ break;
+ case Band.hidl_discriminator.ngranBand:
+ builder.setBand(band.ngranBand());
+ break;
+ default:
+ mRil.riljLoge("Unsupported band type " + band.getDiscriminator());
+ }
+ }
+
private void physicalChannelConfigsIndication(List<? extends Object> configs) {
List<PhysicalChannelConfig> response = new ArrayList<>(configs.size());
- for (Object obj : configs) {
- if (obj instanceof android.hardware.radio.V1_2.PhysicalChannelConfig) {
- android.hardware.radio.V1_2.PhysicalChannelConfig config =
- (android.hardware.radio.V1_2.PhysicalChannelConfig) obj;
-
- response.add(new PhysicalChannelConfig.Builder()
- .setCellConnectionStatus(
- convertConnectionStatusFromCellConnectionStatus(config.status))
- .setCellBandwidthDownlinkKhz(config.cellBandwidthDownlink)
- .build());
- } else if (obj instanceof android.hardware.radio.V1_4.PhysicalChannelConfig) {
- android.hardware.radio.V1_4.PhysicalChannelConfig config =
- (android.hardware.radio.V1_4.PhysicalChannelConfig) obj;
- PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder();
- setFrequencyRangeOrChannelNumber(builder, config);
- response.add(builder.setCellConnectionStatus(
- convertConnectionStatusFromCellConnectionStatus(config.base.status))
- .setCellBandwidthDownlinkKhz(config.base.cellBandwidthDownlink)
- .setRat(ServiceState.rilRadioTechnologyToNetworkType(config.rat))
- .setPhysicalCellId(config.physicalCellId)
- .setContextIds(config.contextIds.stream().mapToInt(x -> x).toArray())
- .build());
- } else {
- mRil.riljLoge("Unsupported PhysicalChannelConfig " + obj);
+ try {
+ for (Object obj : configs) {
+ if (obj instanceof android.hardware.radio.V1_2.PhysicalChannelConfig) {
+ android.hardware.radio.V1_2.PhysicalChannelConfig config =
+ (android.hardware.radio.V1_2.PhysicalChannelConfig) obj;
+
+ response.add(new PhysicalChannelConfig.Builder()
+ .setCellConnectionStatus(
+ convertConnectionStatusFromCellConnectionStatus(config.status))
+ .setCellBandwidthDownlinkKhz(config.cellBandwidthDownlink)
+ .build());
+ } else if (obj instanceof android.hardware.radio.V1_4.PhysicalChannelConfig) {
+ android.hardware.radio.V1_4.PhysicalChannelConfig config =
+ (android.hardware.radio.V1_4.PhysicalChannelConfig) obj;
+ PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder();
+ setFrequencyRangeOrChannelNumber(builder, config);
+ response.add(builder.setCellConnectionStatus(
+ convertConnectionStatusFromCellConnectionStatus(config.base.status))
+ .setCellBandwidthDownlinkKhz(config.base.cellBandwidthDownlink)
+ .setNetworkType(
+ ServiceState.rilRadioTechnologyToNetworkType(config.rat))
+ .setPhysicalCellId(config.physicalCellId)
+ .setContextIds(config.contextIds.stream().mapToInt(x -> x).toArray())
+ .build());
+ } else if (obj instanceof android.hardware.radio.V1_6.PhysicalChannelConfig) {
+ android.hardware.radio.V1_6.PhysicalChannelConfig config =
+ (android.hardware.radio.V1_6.PhysicalChannelConfig) obj;
+ PhysicalChannelConfig.Builder builder = new PhysicalChannelConfig.Builder();
+ setBandToBuilder(builder, config);
+ response.add(builder.setCellConnectionStatus(
+ convertConnectionStatusFromCellConnectionStatus(config.status))
+ .setDownlinkChannelNumber(config.downlinkChannelNumber)
+ .setUplinkChannelNumber(config.uplinkChannelNumber)
+ .setCellBandwidthDownlinkKhz(config.cellBandwidthDownlinkKhz)
+ .setCellBandwidthUplinkKhz(config.cellBandwidthUplinkKhz)
+ .setNetworkType(
+ ServiceState.rilRadioTechnologyToNetworkType(config.rat))
+ .setPhysicalCellId(config.physicalCellId)
+ .setContextIds(config.contextIds.stream().mapToInt(x -> x).toArray())
+ .build());
+ } else {
+ mRil.riljLoge("Unsupported PhysicalChannelConfig " + obj);
+ }
}
+ } catch (IllegalArgumentException iae) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("918f0970-9aa9-4bcd-a28e-e49a83fe77d5"),
+ "Invalid PhysicalChannelConfig reported by HAL");
+ mRil.riljLoge("Invalid PhysicalChannelConfig " + iae);
+ return;
}
if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG, response);
@@ -1198,6 +1373,16 @@ public class RadioIndication extends IRadioIndication.Stub {
mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
}
+ private void responseNetworkScan_1_6(int indicationType,
+ android.hardware.radio.V1_6.NetworkScanResult result) {
+ mRil.processIndication(indicationType);
+
+ ArrayList<CellInfo> cellInfos = RIL.convertHalCellInfoList_1_6(result.networkInfos);
+ NetworkScanResult nsr = new NetworkScanResult(result.status, result.error, cellInfos);
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+ mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
+ }
+
private void responseDataCallListChanged(int indicationType, List<?> dcList) {
mRil.processIndication(indicationType);
@@ -1207,4 +1392,13 @@ public class RadioIndication extends IRadioIndication.Stub {
mRil.mDataCallListChangedRegistrants.notifyRegistrants(
new AsyncResult(null, response, null));
}
+
+ private void responseApnUnthrottled(int indicationType, String apn) {
+ mRil.processIndication(indicationType);
+
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UNTHROTTLE_APN, apn);
+
+ mRil.mApnUnthrottledRegistrants.notifyRegistrants(
+ new AsyncResult(null, apn, null));
+ }
}
diff --git a/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
new file mode 100644
index 0000000000..de62c70e14
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioInterfaceCapabilityController.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.storage.StorageManager;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.telephony.Rlog;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Provides the capabilities that the Radio Interface supports on the current device.
+ */
+public class RadioInterfaceCapabilityController extends Handler {
+ private static final String LOG_TAG =
+ RadioInterfaceCapabilityController.class.getSimpleName();
+
+ private static RadioInterfaceCapabilityController sInstance;
+ private final RadioConfig mRadioConfig;
+ private final CommandsInterface mCommandsInterface;
+ private final boolean mRegisterForOn;
+ private Set<String> mRadioInterfaceCapabilities;
+ private final Object mLockRadioInterfaceCapabilities = new Object();
+ private static final int EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE = 100;
+
+ /**
+ * Init method to instantiate the object
+ * Should only be called once.
+ */
+ public static RadioInterfaceCapabilityController init(final RadioConfig radioConfig,
+ final CommandsInterface commandsInterface) {
+ synchronized (RadioInterfaceCapabilityController.class) {
+ if (sInstance == null) {
+ final HandlerThread handlerThread = new HandlerThread("RHC");
+ handlerThread.start();
+ sInstance = new RadioInterfaceCapabilityController(radioConfig, commandsInterface,
+ handlerThread.getLooper());
+ } else {
+ Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
+ }
+ return sInstance;
+ }
+ }
+
+ /**
+ * Static method to get instance.
+ */
+ public static RadioInterfaceCapabilityController getInstance() {
+ if (sInstance == null) {
+ Log.wtf(LOG_TAG, "getInstance null");
+ }
+
+ return sInstance;
+ }
+
+ @VisibleForTesting
+ public RadioInterfaceCapabilityController(final RadioConfig radioConfig,
+ final CommandsInterface commandsInterface, final Looper looper) {
+ super(looper);
+ mRadioConfig = radioConfig;
+ mCommandsInterface = commandsInterface;
+ mRegisterForOn = StorageManager.inCryptKeeperBounce();
+ register();
+ }
+
+ /**
+ * Gets the radio interface capabilities for the device
+ */
+ @NonNull
+ public Set<String> getCapabilities() {
+ if (mRadioInterfaceCapabilities == null) {
+ // Only incur cost of synchronization block if mRadioInterfaceCapabilities isn't null
+ synchronized (mLockRadioInterfaceCapabilities) {
+ if (mRadioInterfaceCapabilities == null) {
+ mRadioConfig.getHalDeviceCapabilities(
+ obtainMessage(EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE));
+ try {
+ if (Looper.myLooper() != getLooper()) {
+ mLockRadioInterfaceCapabilities.wait(2000);
+ }
+ } catch (final InterruptedException ignored) {
+ }
+
+ if (mRadioInterfaceCapabilities == null) {
+ loge("getRadioInterfaceCapabilities: Radio Capabilities not "
+ + "loaded in time");
+ return new ArraySet<>();
+ }
+ }
+ }
+ }
+ return mRadioInterfaceCapabilities;
+ }
+
+ private void setupCapabilities(final @NonNull AsyncResult ar) {
+ if (mRadioInterfaceCapabilities == null) {
+ synchronized (mLockRadioInterfaceCapabilities) {
+ if (mRadioInterfaceCapabilities == null) {
+ if (ar.exception != null) {
+ loge("setupRadioInterfaceCapabilities: " + ar.exception);
+ }
+ if (ar.result == null) {
+ loge("setupRadioInterfaceCapabilities: ar.result is null");
+ return;
+ }
+ log("setupRadioInterfaceCapabilities: "
+ + "mRadioInterfaceCapabilities now setup");
+ mRadioInterfaceCapabilities =
+ Collections.unmodifiableSet((Set<String>) ar.result);
+ if (mRadioInterfaceCapabilities != null) {
+ unregister();
+ }
+ }
+ mLockRadioInterfaceCapabilities.notify();
+ }
+ }
+ }
+
+ private void register() {
+ // There is no radio HAL, capabilities are irrelevant in this case.
+ if (mCommandsInterface == null) {
+ mRadioInterfaceCapabilities = Collections.unmodifiableSet(new ArraySet<>());
+ return;
+ }
+
+ if (mRegisterForOn) {
+ mCommandsInterface.registerForOn(this, Phone.EVENT_RADIO_ON, null);
+ } else {
+ mCommandsInterface.registerForAvailable(this, Phone.EVENT_RADIO_AVAILABLE, null);
+ }
+ }
+
+ private void unregister() {
+ if (mRegisterForOn) {
+ mCommandsInterface.unregisterForOn(this);
+ } else {
+ mCommandsInterface.unregisterForAvailable(this);
+ }
+ }
+
+ @Override
+ public void handleMessage(final Message msg) {
+ switch (msg.what) {
+ case Phone.EVENT_RADIO_AVAILABLE:
+ case Phone.EVENT_RADIO_ON:
+ getCapabilities();
+ break;
+ case EVENT_GET_HAL_DEVICE_CAPABILITIES_DONE:
+ setupCapabilities((AsyncResult) msg.obj);
+ break;
+ }
+ }
+
+ private static void log(final String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+
+ private static void loge(final String s) {
+ Rlog.e(LOG_TAG, s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 0afbd13a41..9e08b9c400 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -31,31 +31,39 @@ import android.hardware.radio.V1_0.LceStatusInfo;
import android.hardware.radio.V1_0.NeighboringCell;
import android.hardware.radio.V1_0.RadioError;
import android.hardware.radio.V1_0.RadioResponseInfo;
+import android.hardware.radio.V1_0.RadioTechnologyFamily;
import android.hardware.radio.V1_0.SendSmsResult;
import android.hardware.radio.V1_0.VoiceRegStateResult;
import android.hardware.radio.V1_4.CarrierRestrictionsWithPriority;
import android.hardware.radio.V1_4.SimLockMultiSimPolicy;
-import android.hardware.radio.V1_5.IRadioResponse;
+import android.hardware.radio.V1_6.IRadioResponse;
+import android.hardware.radio.V1_6.SetupDataCallResult;
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemClock;
import android.service.carrier.CarrierIdentifier;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AnomalyReporter;
import android.telephony.BarringInfo;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellInfo;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
import android.telephony.PhoneNumberUtils;
import android.telephony.RadioAccessFamily;
+import android.telephony.RadioAccessSpecifier;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.NetworkSlicingConfig;
import android.text.TextUtils;
import com.android.internal.telephony.dataconnection.KeepaliveStatus;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -64,6 +72,7 @@ import com.android.internal.telephony.uicc.IccUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.UUID;
public class RadioResponse extends IRadioResponse.Stub {
// The number of the required config values for broadcast SMS stored in the C struct
@@ -72,6 +81,13 @@ public class RadioResponse extends IRadioResponse.Stub {
private static final int CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES = 31;
+ private static final String RADIO_POWER_FAILURE_BUGREPORT_UUID =
+ "316f3801-fa21-4954-a42f-0041eada3b31";
+ private static final String RADIO_POWER_FAILURE_RF_HARDWARE_ISSUE_UUID =
+ "316f3801-fa21-4954-a42f-0041eada3b32";
+ private static final String RADIO_POWER_FAILURE_NO_RF_CALIBRATION_UUID =
+ "316f3801-fa21-4954-a42f-0041eada3b33";
+
RIL mRil;
public RadioResponse(RIL ril) {
@@ -114,7 +130,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param cardStatus ICC card status as defined by CardStatus in 1.2/types.hal
*/
public void getIccCardStatusResponse_1_2(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_2.CardStatus cardStatus) {
+ android.hardware.radio.V1_2.CardStatus cardStatus) {
responseIccCardStatus_1_2(responseInfo, cardStatus);
}
@@ -123,7 +139,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param cardStatus ICC card status as defined by CardStatus in 1.4/types.hal
*/
public void getIccCardStatusResponse_1_4(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_4.CardStatus cardStatus) {
+ android.hardware.radio.V1_4.CardStatus cardStatus) {
responseIccCardStatus_1_4(responseInfo, cardStatus);
}
@@ -189,7 +205,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param retriesRemaining Number of retries remaining, must be equal to -1 if unknown.
*/
public void supplyNetworkDepersonalizationResponse(RadioResponseInfo responseInfo,
- int retriesRemaining) {
+ int retriesRemaining) {
responseInts(responseInfo, retriesRemaining);
}
@@ -210,7 +226,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param calls Current call list
*/
public void getCurrentCallsResponse(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.Call> calls) {
+ ArrayList<android.hardware.radio.V1_0.Call> calls) {
responseCurrentCalls(responseInfo, calls);
}
@@ -219,12 +235,22 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param calls Current call list
*/
public void getCurrentCallsResponse_1_2(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_2.Call> calls) {
+ ArrayList<android.hardware.radio.V1_2.Call> calls) {
responseCurrentCalls_1_2(responseInfo, calls);
}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param calls Current call list
+ */
+ public void getCurrentCallsResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_6.Call> calls) {
+ responseCurrentCalls_1_6(responseInfo, calls);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
*/
public void dialResponse(RadioResponseInfo responseInfo) {
responseVoid(responseInfo);
@@ -288,12 +314,12 @@ public class RadioResponse extends IRadioResponse.Stub {
* described in the "CDMA IS-2000 Release A (C.S0005-A v6.0)" standard.
*/
public void getLastCallFailCauseResponse(RadioResponseInfo responseInfo,
- LastCallFailCauseInfo fcInfo) {
+ LastCallFailCauseInfo fcInfo) {
responseLastCallFailCauseInfo(responseInfo, fcInfo);
}
public void getSignalStrengthResponse(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.SignalStrength sigStrength) {
+ android.hardware.radio.V1_0.SignalStrength sigStrength) {
responseSignalStrength(responseInfo, sigStrength);
}
@@ -306,7 +332,8 @@ public class RadioResponse extends IRadioResponse.Stub {
android.hardware.radio.V1_2.SignalStrength signalStrength) {
responseSignalStrength_1_2(responseInfo, signalStrength);
}
- /**
+
+ /**
* @param responseInfo Response info struct containing response type, serial no. and error
* @param signalStrength Current signal strength of camped/connected cells
*/
@@ -318,11 +345,21 @@ public class RadioResponse extends IRadioResponse.Stub {
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param signalStrength Current signal strength of camped/connected cells
+ */
+ public void getSignalStrengthResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_6.SignalStrength signalStrength) {
+ responseSignalStrength_1_6(responseInfo, signalStrength);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
* in types.hal
*/
public void getVoiceRegistrationStateResponse(RadioResponseInfo responseInfo,
- VoiceRegStateResult voiceRegResponse) {
+ VoiceRegStateResult voiceRegResponse) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -382,11 +419,28 @@ public class RadioResponse extends IRadioResponse.Stub {
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
+ * in 1.6/types.hal
+ */
+ public void getVoiceRegistrationStateResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_6.RegStateResult voiceRegResponse) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, voiceRegResponse);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, voiceRegResponse);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
* types.hal
*/
public void getDataRegistrationStateResponse(RadioResponseInfo responseInfo,
- DataRegStateResult dataRegResponse) {
+ DataRegStateResult dataRegResponse) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -463,14 +517,32 @@ public class RadioResponse extends IRadioResponse.Stub {
/**
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
+ * 1.6/types.hal
+ */
+ public void getDataRegistrationStateResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_6.RegStateResult dataRegResponse) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, dataRegResponse);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, dataRegResponse);
+ }
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param longName is long alpha ONS or EONS or empty string if unregistered
* @param shortName is short alpha ONS or EONS or empty string if unregistered
* @param numeric is 5 or 6 digit numeric code (MCC + MNC) or empty string if unregistered
*/
public void getOperatorResponse(RadioResponseInfo responseInfo,
- String longName,
- String shortName,
- String numeric) {
+ String longName,
+ String shortName,
+ String numeric) {
responseStrings(responseInfo, longName, shortName, numeric);
}
@@ -479,6 +551,7 @@ public class RadioResponse extends IRadioResponse.Stub {
*/
public void setRadioPowerResponse(RadioResponseInfo responseInfo) {
responseVoid(responseInfo);
+ mRil.mLastRadioPowerResult = responseInfo.error;
}
/**
@@ -493,20 +566,41 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param sms Response to sms sent as defined by SendSmsResult in types.hal
*/
public void sendSmsResponse(RadioResponseInfo responseInfo,
- SendSmsResult sms) {
+ SendSmsResult sms) {
responseSms(responseInfo, sms);
}
/**
+ * @param responseInfo Response info struct containing response type, serial no. and error which
+ * is defined in 1.6/types.hal
+ * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+ */
+ public void sendSmsResponse_1_6(android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ SendSmsResult sms) {
+ responseSms_1_6(responseInfo, sms);
+ }
+
+ /**
* @param responseInfo Response info struct containing response type, serial no. and error
* @param sms Response to sms sent as defined by SendSmsResult in types.hal
*/
public void sendSMSExpectMoreResponse(RadioResponseInfo responseInfo,
- SendSmsResult sms) {
+ SendSmsResult sms) {
responseSms(responseInfo, sms);
}
/**
+ * @param responseInfo Response info struct containing response type, serial no. and error which
+ * is defined in 1.6/types.hal
+ * @param sms Response to sms sent as defined by SendSmsResult in 1.6/types.hal
+ */
+ public void sendSmsExpectMoreResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ SendSmsResult sms) {
+ responseSms_1_6(responseInfo, sms);
+ }
+
+ /**
* @param responseInfo Response info struct containing response type, serial no. and error
* @param setupDataCallResult Response to data call setup as defined by setupDataCallResult in
* types.hal
@@ -536,12 +630,50 @@ public class RadioResponse extends IRadioResponse.Stub {
responseSetupDataCall(responseInfo, setupDataCallResult);
}
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param setupDataCallResult Response to data call setup as defined by setupDataCallResult in
+ * 1.6/types.hal
+ */
+ public void setupDataCallResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_6.SetupDataCallResult setupDataCallResult) {
+ responseSetupDataCall_1_6(responseInfo, setupDataCallResult);
+ }
+
+ @Override
+ public void getDataCallListResponse_1_6(android.hardware.radio.V1_6.RadioResponseInfo info,
+ ArrayList<SetupDataCallResult> dcResponse) {
+ responseDataCallList(info, dcResponse);
+ }
+
+ @Override
+ public void setSimCardPowerResponse_1_6(android.hardware.radio.V1_6.RadioResponseInfo info) {
+ responseVoid_1_6(info);
+ }
+
+ @Override
+ public void setAllowedNetworkTypesBitmapResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo info) {
+ responseVoid_1_6(info);
+ }
+
+ @Override
+ public void getAllowedNetworkTypesBitmapResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo info,
+ int halRadioAccessFamilyBitmap) {
+ int networkTypeBitmask = RIL.convertToNetworkTypeBitMask(halRadioAccessFamilyBitmap);
+ mRil.mAllowedNetworkTypesBitmask = networkTypeBitmask;
+ responseInts_1_6(info, networkTypeBitmask);
+ }
+
/**
* @param responseInfo Response info struct containing response type, serial no. and error
* @param iccIo ICC io operation response as defined by IccIoResult in types.hal
*/
public void iccIOForAppResponse(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.IccIoResult iccIo) {
+ android.hardware.radio.V1_0.IccIoResult iccIo) {
responseIccIo(responseInfo, iccIo);
}
@@ -581,8 +713,8 @@ public class RadioResponse extends IRadioResponse.Stub {
* each distinct registered phone number.
*/
public void getCallForwardStatusResponse(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.CallForwardInfo>
- callForwardInfos) {
+ ArrayList<android.hardware.radio.V1_0.CallForwardInfo>
+ callForwardInfos) {
responseCallForwardInfo(responseInfo, callForwardInfos);
}
@@ -604,8 +736,8 @@ public class RadioResponse extends IRadioResponse.Stub {
* and voice and disabled for everything else.
*/
public void getCallWaitingResponse(RadioResponseInfo responseInfo,
- boolean enable,
- int serviceClass) {
+ boolean enable,
+ int serviceClass) {
responseInts(responseInfo, enable ? 1 : 0, serviceClass);
}
@@ -701,8 +833,8 @@ public class RadioResponse extends IRadioResponse.Stub {
* types.hal
*/
public void getAvailableNetworksResponse(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.OperatorInfo>
- networkInfos) {
+ ArrayList<android.hardware.radio.V1_0.OperatorInfo>
+ networkInfos) {
responseOperatorInfos(responseInfo, networkInfos);
}
@@ -827,7 +959,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
public void sendOemRilRequestRawResponse(RadioResponseInfo responseInfo,
- ArrayList<Byte> var2) {}
+ ArrayList<Byte> var2) {}
/**
* @param responseInfo Response info struct containing response type, serial no. and error
@@ -864,7 +996,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param bandModes List of RadioBandMode listing supported modes
*/
public void getAvailableBandModesResponse(RadioResponseInfo responseInfo,
- ArrayList<Integer> bandModes) {
+ ArrayList<Integer> bandModes) {
responseIntArrayList(responseInfo, bandModes);
}
@@ -920,8 +1052,8 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param nwType RadioPreferredNetworkType defined in types.hal
*/
public void getPreferredNetworkTypeResponse(RadioResponseInfo responseInfo, int nwType) {
- mRil.mPreferredNetworkType = nwType;
- responseInts(responseInfo, nwType);
+ mRil.mAllowedNetworkTypesBitmask = RadioAccessFamily.getRafFromNetworkType(nwType);
+ responseInts(responseInfo, RadioAccessFamily.getRafFromNetworkType(nwType));
}
/**
@@ -933,10 +1065,9 @@ public class RadioResponse extends IRadioResponse.Stub {
public void getPreferredNetworkTypeBitmapResponse(
RadioResponseInfo responseInfo, int halRadioAccessFamilyBitmap) {
- int networkType = RadioAccessFamily.getNetworkTypeFromRaf(
- RIL.convertToNetworkTypeBitMask(halRadioAccessFamilyBitmap));
- mRil.mPreferredNetworkType = networkType;
- responseInts(responseInfo, networkType);
+ int networkTypeBitmask = RIL.convertToNetworkTypeBitMask(halRadioAccessFamilyBitmap);
+ mRil.mAllowedNetworkTypesBitmask = networkTypeBitmask;
+ responseInts(responseInfo, networkTypeBitmask);
}
/**
@@ -945,7 +1076,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param cells Vector of neighboring radio cell information
*/
public void getNeighboringCidsResponse(RadioResponseInfo responseInfo,
- ArrayList<NeighboringCell> cells) {
+ ArrayList<NeighboringCell> cells) {
responseCellList(responseInfo, cells);
}
@@ -1009,7 +1140,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* true for Enhanced Privacy Mode (Private Long Code Mask)
*/
public void getPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo,
- boolean enable) {
+ boolean enable) {
responseInts(responseInfo, enable ? 1 : 0);
}
@@ -1038,14 +1169,44 @@ public class RadioResponse extends IRadioResponse.Stub {
/**
*
- * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param responseInfo Response info struct containing response type, serial no. and error which
+ * is defined in 1.6/types.hal
* @param sms Sms result struct as defined by SendSmsResult in types.hal
*/
- public void sendCdmaSMSExpectMoreResponse(RadioResponseInfo responseInfo, SendSmsResult sms) {
+ public void sendCdmaSmsResponse_1_6(android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ SendSmsResult sms) {
+ responseSms_1_6(responseInfo, sms);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+ */
+ public void sendCdmaSmsExpectMoreResponse(RadioResponseInfo responseInfo,
+ SendSmsResult sms) {
responseSms(responseInfo, sms);
}
/**
+ *
+ * @param responseInfo Response info struct containing response type, serial no. and error which
+ * is defined in 1.6/types.hal
+ * @param sms Sms result struct as defined by SendSmsResult in types.hal
+ */
+ public void sendCdmaSmsExpectMoreResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo, SendSmsResult sms) {
+ responseSms_1_6(responseInfo, sms);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error
+ */
+ public void setDataThrottlingResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
+ responseVoid_1_6(responseInfo);
+ }
+
+ /**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void acknowledgeLastIncomingCdmaSmsResponse(RadioResponseInfo responseInfo) {
@@ -1058,7 +1219,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param configs Vector of GSM/WCDMA Cell broadcast configs
*/
public void getGsmBroadcastConfigResponse(RadioResponseInfo responseInfo,
- ArrayList<GsmBroadcastSmsConfigInfo> configs) {
+ ArrayList<GsmBroadcastSmsConfigInfo> configs) {
responseGmsBroadcastConfig(responseInfo, configs);
}
@@ -1082,7 +1243,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param configs Vector of CDMA Broadcast SMS configs.
*/
public void getCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo,
- ArrayList<CdmaBroadcastSmsConfigInfo> configs) {
+ ArrayList<CdmaBroadcastSmsConfigInfo> configs) {
responseCdmaBroadcastConfig(responseInfo, configs);
}
@@ -1112,7 +1273,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param prl PRL version if CDMA subscription is available
*/
public void getCDMASubscriptionResponse(RadioResponseInfo responseInfo, String mdn,
- String hSid, String hNid, String min, String prl) {
+ String hSid, String hNid, String min, String prl) {
responseStrings(responseInfo, mdn, hSid, hNid, min, prl);
}
@@ -1140,7 +1301,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param meid MEID if CDMA subscription is available
*/
public void getDeviceIdentityResponse(RadioResponseInfo responseInfo, String imei,
- String imeisv, String esn, String meid) {
+ String imeisv, String esn, String meid) {
responseStrings(responseInfo, imei, imeisv, esn, meid);
}
@@ -1212,7 +1373,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param iccIo IccIoResult as defined in types.hal corresponding to ICC IO response
*/
public void sendEnvelopeWithStatusResponse(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.IccIoResult iccIo) {
+ android.hardware.radio.V1_0.IccIoResult iccIo) {
responseIccIo(responseInfo, iccIo);
}
@@ -1226,7 +1387,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
public void getCellInfoListResponse(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.CellInfo> cellInfo) {
+ ArrayList<android.hardware.radio.V1_0.CellInfo> cellInfo) {
responseCellInfoList(responseInfo, cellInfo);
}
@@ -1261,6 +1422,16 @@ public class RadioResponse extends IRadioResponse.Stub {
}
/**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ * @param cellInfo List of current cell information known to radio.
+ */
+ public void getCellInfoListResponse_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_6.CellInfo> cellInfo) {
+ responseCellInfoList_1_6(responseInfo, cellInfo);
+ }
+
+ /**
* @param responseInfo Response info struct containing response type, serial no. and error
*/
public void setCellInfoListRateResponse(RadioResponseInfo responseInfo) {
@@ -1289,8 +1460,13 @@ public class RadioResponse extends IRadioResponse.Stub {
* isRegistered is true.
*/
public void getImsRegistrationStateResponse(RadioResponseInfo responseInfo,
- boolean isRegistered, int ratFamily) {
- responseInts(responseInfo, isRegistered ? 1 : 0, ratFamily);
+ boolean isRegistered, int ratFamily) {
+ responseInts(
+ responseInfo,
+ isRegistered ? 1 : 0,
+ ratFamily == RadioTechnologyFamily.THREE_GPP
+ ? PhoneConstants.PHONE_TYPE_GSM
+ : PhoneConstants.PHONE_TYPE_CDMA);
}
/**
@@ -1308,8 +1484,8 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param result IccIoResult as defined in types.hal
*/
public void iccTransmitApduBasicChannelResponse(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.IccIoResult
- result) {
+ android.hardware.radio.V1_0.IccIoResult
+ result) {
responseIccIo(responseInfo, result);
}
@@ -1321,7 +1497,7 @@ public class RadioResponse extends IRadioResponse.Stub {
* byte per integer
*/
public void iccOpenLogicalChannelResponse(RadioResponseInfo responseInfo, int channelId,
- ArrayList<Byte> selectResponse) {
+ ArrayList<Byte> selectResponse) {
ArrayList<Integer> arr = new ArrayList<>();
arr.add(channelId);
for (int i = 0; i < selectResponse.size(); i++) {
@@ -1404,8 +1580,8 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param result IccIoResult as defined in types.hal
*/
public void requestIccSimAuthenticationResponse(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.IccIoResult
- result) {
+ android.hardware.radio.V1_0.IccIoResult
+ result) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -1443,7 +1619,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
public void getRadioCapabilityResponse(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.RadioCapability rc) {
+ android.hardware.radio.V1_0.RadioCapability rc) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -1463,7 +1639,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
public void setRadioCapabilityResponse(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.RadioCapability rc) {
+ android.hardware.radio.V1_0.RadioCapability rc) {
responseRadioCapability(responseInfo, rc);
}
@@ -1495,13 +1671,41 @@ public class RadioResponse extends IRadioResponse.Stub {
* @param activityInfo modem activity information
*/
public void getModemActivityInfoResponse(RadioResponseInfo responseInfo,
- ActivityStatsInfo activityInfo) {
+ ActivityStatsInfo activityInfo) {
responseActivityData(responseInfo, activityInfo);
}
/**
*
* @param responseInfo Response info struct containing response type, serial no. and error
+ * @param isEnabled Indicates whether NR dual connectivity is enabled or not, True if enabled
+ * else false.
+ */
+ public void isNrDualConnectivityEnabledResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ boolean isEnabled) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, isEnabled);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, isEnabled);
+ }
+ }
+
+ /**
+ *
+ * @param info Response info struct containing response type, serial no. and error
+ */
+ public void setNrDualConnectivityStateResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo info) {
+ responseVoid_1_6(info);
+ }
+
+ /**
+ *
+ * @param responseInfo Response info struct containing response type, serial no. and error
* @param numAllowed number of allowed carriers which have been set correctly.
* On success, it must match the length of list Carriers->allowedCarriers.
* if Length of allowed carriers list is 0, numAllowed = 0.
@@ -1733,7 +1937,7 @@ public class RadioResponse extends IRadioResponse.Stub {
if (numApplications
> com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS) {
numApplications =
- com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS;
+ com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS;
}
iccCardStatus.mApplications = new IccCardApplicationStatus[numApplications];
for (int i = 0; i < numApplications; i++) {
@@ -1742,7 +1946,7 @@ public class RadioResponse extends IRadioResponse.Stub {
appStatus.app_type = appStatus.AppTypeFromRILInt(rilAppStatus.appType);
appStatus.app_state = appStatus.AppStateFromRILInt(rilAppStatus.appState);
appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(
- rilAppStatus.persoSubstate);
+ rilAppStatus.persoSubstate);
appStatus.aid = rilAppStatus.aidPtr;
appStatus.app_label = rilAppStatus.appLabelPtr;
appStatus.pin1_replaced = rilAppStatus.pin1Replaced;
@@ -1796,6 +2000,47 @@ public class RadioResponse extends IRadioResponse.Stub {
return iccCardStatus;
}
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ */
+ public void getSimPhonebookRecordsResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
+ responseVoid_1_6(responseInfo);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ * @param pbCapacity Contains the adn, email, anr capacities in the sim card.
+ */
+ public void getSimPhonebookCapacityResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_6.PhonebookCapacity pbCapacity) {
+ AdnCapacity capacity = new AdnCapacity(pbCapacity);
+ responseAdnCapacity(responseInfo, capacity);
+ }
+
+ /**
+ * @param responseInfo Response info struct containing response type, serial no. and error.
+ * @param updatedRecordIndex The index of the updated record.
+ */
+ public void updateSimPhonebookRecordsResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ int updatedRecordIndex) {
+ responseInts_1_6(responseInfo, updatedRecordIndex);
+ }
+
+ private void responseAdnCapacity(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ AdnCapacity capacity) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+ if (rr != null) {
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, capacity);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, capacity);
+ }
+ }
+
private void responseIccCardStatus(RadioResponseInfo responseInfo, CardStatus cardStatus) {
RILRequest rr = mRil.processResponse(responseInfo);
@@ -1810,7 +2055,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseIccCardStatus_1_2(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_2.CardStatus cardStatus) {
+ android.hardware.radio.V1_2.CardStatus cardStatus) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -1827,7 +2072,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseIccCardStatus_1_4(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_4.CardStatus cardStatus) {
+ android.hardware.radio.V1_4.CardStatus cardStatus) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -1873,6 +2118,15 @@ public class RadioResponse extends IRadioResponse.Stub {
responseIntArrayList(responseInfo, ints);
}
+ private void responseInts_1_6(android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ int ...var) {
+ final ArrayList<Integer> ints = new ArrayList<>();
+ for (int i = 0; i < var.length; i++) {
+ ints.add(var[i]);
+ }
+ responseIntArrayList_1_6(responseInfo, ints);
+ }
+
private void responseIntArrayList(RadioResponseInfo responseInfo, ArrayList<Integer> var) {
RILRequest rr = mRil.processResponse(responseInfo);
@@ -1888,8 +2142,24 @@ public class RadioResponse extends IRadioResponse.Stub {
}
}
+ private void responseIntArrayList_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo, ArrayList<Integer> var) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ int[] ret = new int[var.size()];
+ for (int i = 0; i < var.size(); i++) {
+ ret[i] = var.get(i);
+ }
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, ret);
+ }
+ }
+
private void responseCurrentCalls(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.Call> calls) {
+ ArrayList<android.hardware.radio.V1_0.Call> calls) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -1898,46 +2168,7 @@ public class RadioResponse extends IRadioResponse.Stub {
DriverCall dc;
for (int i = 0; i < num; i++) {
- dc = new DriverCall();
- // TODO: change name of function stateFromCLCC() in DriverCall.java to name
- // clarifying what is CLCC
- dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).state));
- dc.index = calls.get(i).index;
- dc.TOA = calls.get(i).toa;
- dc.isMpty = calls.get(i).isMpty;
- dc.isMT = calls.get(i).isMT;
- dc.als = calls.get(i).als;
- dc.isVoice = calls.get(i).isVoice;
- dc.isVoicePrivacy = calls.get(i).isVoicePrivacy;
- dc.number = calls.get(i).number;
- dc.numberPresentation =
- DriverCall.presentationFromCLIP(
- (int) (calls.get(i).numberPresentation));
- dc.name = calls.get(i).name;
- dc.namePresentation =
- DriverCall.presentationFromCLIP((int) (calls.get(i).namePresentation));
- if (calls.get(i).uusInfo.size() == 1) {
- dc.uusInfo = new UUSInfo();
- dc.uusInfo.setType(calls.get(i).uusInfo.get(0).uusType);
- dc.uusInfo.setDcs(calls.get(i).uusInfo.get(0).uusDcs);
- if (!TextUtils.isEmpty(calls.get(i).uusInfo.get(0).uusData)) {
- byte[] userData = calls.get(i).uusInfo.get(0).uusData.getBytes();
- dc.uusInfo.setUserData(userData);
- } else {
- mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");
- }
-
- mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
- dc.uusInfo.getType(), dc.uusInfo.getDcs(),
- dc.uusInfo.getUserData().length));
- mRil.riljLogv("Incoming UUS : data (hex): "
- + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
- } else {
- mRil.riljLogv("Incoming UUS : NOT present!");
- }
-
- // Make sure there's a leading + on addresses with a TOA of 145
- dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
+ dc = convertToDriverCall(calls.get(i));
dcCalls.add(dc);
@@ -1968,7 +2199,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseCurrentCalls_1_2(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_2.Call> calls) {
+ ArrayList<android.hardware.radio.V1_2.Call> calls) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -1977,48 +2208,48 @@ public class RadioResponse extends IRadioResponse.Stub {
DriverCall dc;
for (int i = 0; i < num; i++) {
- dc = new DriverCall();
- // TODO: change name of function stateFromCLCC() in DriverCall.java to name
- // clarifying what is CLCC
- dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).base.state));
- dc.index = calls.get(i).base.index;
- dc.TOA = calls.get(i).base.toa;
- dc.isMpty = calls.get(i).base.isMpty;
- dc.isMT = calls.get(i).base.isMT;
- dc.als = calls.get(i).base.als;
- dc.isVoice = calls.get(i).base.isVoice;
- dc.isVoicePrivacy = calls.get(i).base.isVoicePrivacy;
- dc.number = calls.get(i).base.number;
- dc.numberPresentation =
- DriverCall.presentationFromCLIP(
- (int) (calls.get(i).base.numberPresentation));
- dc.name = calls.get(i).base.name;
- dc.namePresentation =
- DriverCall.presentationFromCLIP((int) (calls.get(i).base.namePresentation));
- if (calls.get(i).base.uusInfo.size() == 1) {
- dc.uusInfo = new UUSInfo();
- dc.uusInfo.setType(calls.get(i).base.uusInfo.get(0).uusType);
- dc.uusInfo.setDcs(calls.get(i).base.uusInfo.get(0).uusDcs);
- if (!TextUtils.isEmpty(calls.get(i).base.uusInfo.get(0).uusData)) {
- byte[] userData = calls.get(i).base.uusInfo.get(0).uusData.getBytes();
- dc.uusInfo.setUserData(userData);
- } else {
- mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");
- }
+ dc = convertToDriverCall_1_2(calls.get(i));
- mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
- dc.uusInfo.getType(), dc.uusInfo.getDcs(),
- dc.uusInfo.getUserData().length));
- mRil.riljLogv("Incoming UUS : data (hex): "
- + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+ dcCalls.add(dc);
+
+ if (dc.isVoicePrivacy) {
+ mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
+ mRil.riljLog("InCall VoicePrivacy is enabled");
} else {
- mRil.riljLogv("Incoming UUS : NOT present!");
+ mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
+ mRil.riljLog("InCall VoicePrivacy is disabled");
}
+ }
- // Make sure there's a leading + on addresses with a TOA of 145
- dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
+ Collections.sort(dcCalls);
- dc.audioQuality = (int) (calls.get(i).audioQuality);
+ if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
+ if (mRil.mEmergencyCallbackModeRegistrant != null) {
+ mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
+ + " notify ECM Registrants");
+ mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
+ }
+ }
+
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, dcCalls);
+ }
+ mRil.processResponseDone(rr, responseInfo, dcCalls);
+ }
+ }
+
+ private void responseCurrentCalls_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_6.Call> calls) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ int num = calls.size();
+ ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);
+ DriverCall dc;
+
+ for (int i = 0; i < num; i++) {
+ dc = convertToDriverCall_1_6(calls.get(i));
dcCalls.add(dc);
@@ -2044,10 +2275,65 @@ public class RadioResponse extends IRadioResponse.Stub {
if (responseInfo.error == RadioError.NONE) {
sendMessageResponse(rr.mResult, dcCalls);
}
- mRil.processResponseDone(rr, responseInfo, dcCalls);
+ mRil.processResponseDone_1_6(rr, responseInfo, dcCalls);
+ }
+ }
+
+ private DriverCall convertToDriverCall(android.hardware.radio.V1_0.Call call) {
+ DriverCall dc = new DriverCall();
+ // TODO: change name of function stateFromCLCC() in DriverCall.java to name
+ // clarifying what is CLCC
+ dc.state = DriverCall.stateFromCLCC((int) (call.state));
+ dc.index = call.index;
+ dc.TOA = call.toa;
+ dc.isMpty = call.isMpty;
+ dc.isMT = call.isMT;
+ dc.als = call.als;
+ dc.isVoice = call.isVoice;
+ dc.isVoicePrivacy = call.isVoicePrivacy;
+ dc.number = call.number;
+ dc.numberPresentation = DriverCall.presentationFromCLIP((int) (call.numberPresentation));
+ dc.name = call.name;
+ dc.namePresentation = DriverCall.presentationFromCLIP((int) (call.namePresentation));
+ if (call.uusInfo.size() == 1) {
+ dc.uusInfo = new UUSInfo();
+ dc.uusInfo.setType(call.uusInfo.get(0).uusType);
+ dc.uusInfo.setDcs(call.uusInfo.get(0).uusDcs);
+ if (!TextUtils.isEmpty(call.uusInfo.get(0).uusData)) {
+ byte[] userData = call.uusInfo.get(0).uusData.getBytes();
+ dc.uusInfo.setUserData(userData);
+ } else {
+ mRil.riljLog("convertToDriverCall: uusInfo data is null or empty");
+ }
+
+ mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
+ dc.uusInfo.getType(), dc.uusInfo.getDcs(),
+ dc.uusInfo.getUserData().length));
+ mRil.riljLogv("Incoming UUS : data (hex): "
+ + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+ } else {
+ mRil.riljLogv("Incoming UUS : NOT present!");
}
+
+ // Make sure there's a leading + on addresses with a TOA of 145
+ dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
+
+ return dc;
+ }
+
+ private DriverCall convertToDriverCall_1_2(android.hardware.radio.V1_2.Call call) {
+ android.hardware.radio.V1_0.Call earlierVersionCall = call.base;
+ DriverCall dc = convertToDriverCall(earlierVersionCall);
+ dc.audioQuality = (int) (call.audioQuality);
+ return dc;
}
+ private DriverCall convertToDriverCall_1_6(android.hardware.radio.V1_6.Call call) {
+ android.hardware.radio.V1_2.Call earlierVersionCall = call.base;
+ DriverCall dc = convertToDriverCall_1_2(earlierVersionCall);
+ dc.forwardedNumber = call.forwardedNumber;
+ return dc;
+ }
private void responseVoid(RadioResponseInfo responseInfo) {
RILRequest rr = mRil.processResponse(responseInfo);
@@ -2061,6 +2347,18 @@ public class RadioResponse extends IRadioResponse.Stub {
}
}
+ private void responseVoid_1_6(android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ Object ret = null;
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, ret);
+ }
+ }
+
private void responseString(RadioResponseInfo responseInfo, String str) {
RILRequest rr = mRil.processResponse(responseInfo);
@@ -2081,7 +2379,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
static void responseStringArrayList(RIL ril, RadioResponseInfo responseInfo,
- ArrayList<String> strings) {
+ ArrayList<String> strings) {
RILRequest rr = ril.processResponse(responseInfo);
if (rr != null) {
@@ -2097,7 +2395,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseLastCallFailCauseInfo(RadioResponseInfo responseInfo,
- LastCallFailCauseInfo fcInfo) {
+ LastCallFailCauseInfo fcInfo) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2153,11 +2451,26 @@ public class RadioResponse extends IRadioResponse.Stub {
}
}
+ private void responseSignalStrength_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ android.hardware.radio.V1_6.SignalStrength signalStrength) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ SignalStrength ret = new SignalStrength(signalStrength);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, ret);
+ }
+ }
+
private void responseSms(RadioResponseInfo responseInfo, SendSmsResult sms) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
- SmsResponse ret = new SmsResponse(sms.messageRef, sms.ackPDU, sms.errorCode);
+ long messageId = RIL.getOutgoingSmsMessageId(rr.mResult);
+ SmsResponse ret = new SmsResponse(sms.messageRef, sms.ackPDU, sms.errorCode, messageId);
if (responseInfo.error == RadioError.NONE) {
sendMessageResponse(rr.mResult, ret);
}
@@ -2165,8 +2478,22 @@ public class RadioResponse extends IRadioResponse.Stub {
}
}
+ private void responseSms_1_6(android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ SendSmsResult sms) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ long messageId = RIL.getOutgoingSmsMessageId(rr.mResult);
+ SmsResponse ret = new SmsResponse(sms.messageRef, sms.ackPDU, sms.errorCode, messageId);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, ret);
+ }
+ }
+
private void responseSetupDataCall(RadioResponseInfo responseInfo,
- Object setupDataCallResult) {
+ Object setupDataCallResult) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2178,8 +2505,22 @@ public class RadioResponse extends IRadioResponse.Stub {
}
}
+ private void responseSetupDataCall_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ Object setupDataCallResult) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ DataCallResponse response = RIL.convertDataCallResult(setupDataCallResult);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, response);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, response);
+ }
+ }
+
private void responseIccIo(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.IccIoResult result) {
+ android.hardware.radio.V1_0.IccIoResult result) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2192,8 +2533,8 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseCallForwardInfo(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.CallForwardInfo>
- callForwardInfos) {
+ ArrayList<android.hardware.radio.V1_0.CallForwardInfo>
+ callForwardInfos) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
CallForwardInfo[] ret = new CallForwardInfo[callForwardInfos.size()];
@@ -2228,8 +2569,8 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseOperatorInfos(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.OperatorInfo>
- networkInfos) {
+ ArrayList<android.hardware.radio.V1_0.OperatorInfo>
+ networkInfos) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2280,7 +2621,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseDataCallList(RadioResponseInfo responseInfo,
- List<? extends Object> dataCallResultList) {
+ List<? extends Object> dataCallResultList) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2293,8 +2634,22 @@ public class RadioResponse extends IRadioResponse.Stub {
}
}
+ private void responseDataCallList(android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ List<? extends Object> dataCallResultList) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ ArrayList<DataCallResponse> response =
+ RIL.convertDataCallResultList(dataCallResultList);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, response);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, response);
+ }
+ }
+
private void responseCellList(RadioResponseInfo responseInfo,
- ArrayList<NeighboringCell> cells) {
+ ArrayList<NeighboringCell> cells) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2324,7 +2679,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseGmsBroadcastConfig(RadioResponseInfo responseInfo,
- ArrayList<GsmBroadcastSmsConfigInfo> configs) {
+ ArrayList<GsmBroadcastSmsConfigInfo> configs) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2342,7 +2697,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseCdmaBroadcastConfig(RadioResponseInfo responseInfo,
- ArrayList<CdmaBroadcastSmsConfigInfo> configs) {
+ ArrayList<CdmaBroadcastSmsConfigInfo> configs) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2390,7 +2745,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseCellInfoList(RadioResponseInfo responseInfo,
- ArrayList<android.hardware.radio.V1_0.CellInfo> cellInfo) {
+ ArrayList<android.hardware.radio.V1_0.CellInfo> cellInfo) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2443,8 +2798,22 @@ public class RadioResponse extends IRadioResponse.Stub {
}
}
+ private void responseCellInfoList_1_6(
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo,
+ ArrayList<android.hardware.radio.V1_6.CellInfo> cellInfo) {
+ RILRequest rr = mRil.processResponse_1_6(responseInfo);
+
+ if (rr != null) {
+ ArrayList<CellInfo> ret = RIL.convertHalCellInfoList_1_6(cellInfo);
+ if (responseInfo.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone_1_6(rr, responseInfo, ret);
+ }
+ }
+
private void responseActivityData(RadioResponseInfo responseInfo,
- ActivityStatsInfo activityInfo) {
+ ActivityStatsInfo activityInfo) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2452,16 +2821,16 @@ public class RadioResponse extends IRadioResponse.Stub {
if (responseInfo.error == RadioError.NONE) {
final int sleepModeTimeMs = activityInfo.sleepModeTimeMs;
final int idleModeTimeMs = activityInfo.idleModeTimeMs;
- int [] txModeTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
- for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+ int [] txModeTimeMs = new int[ModemActivityInfo.getNumTxPowerLevels()];
+ for (int i = 0; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
txModeTimeMs[i] = activityInfo.txmModetimeMs[i];
}
final int rxModeTimeMs = activityInfo.rxModeTimeMs;
ret = new ModemActivityInfo(SystemClock.elapsedRealtime(), sleepModeTimeMs,
idleModeTimeMs, txModeTimeMs, rxModeTimeMs);
} else {
- ret = new ModemActivityInfo(0, 0, 0, new int [ModemActivityInfo.TX_POWER_LEVELS],
- 0);
+ ret = new ModemActivityInfo(0, 0, 0,
+ new int[ModemActivityInfo.getNumTxPowerLevels()], 0);
responseInfo.error = RadioError.NONE;
}
sendMessageResponse(rr.mResult, ret);
@@ -2484,7 +2853,7 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseRadioCapability(RadioResponseInfo responseInfo,
- android.hardware.radio.V1_0.RadioCapability rc) {
+ android.hardware.radio.V1_0.RadioCapability rc) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
@@ -2514,7 +2883,7 @@ public class RadioResponse extends IRadioResponse.Stub {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr != null) {
- LinkCapacityEstimate ret = RIL.convertHalLceData(lceInfo, mRil);
+ List<LinkCapacityEstimate> ret = RIL.convertHalLceData(lceInfo, mRil);
if (responseInfo.error == RadioError.NONE) {
sendMessageResponse(rr.mResult, ret);
}
@@ -2545,8 +2914,8 @@ public class RadioResponse extends IRadioResponse.Stub {
}
private void responseCarrierRestrictions(RadioResponseInfo responseInfo, boolean allAllowed,
- CarrierRestrictionsWithPriority carriers,
- int multiSimPolicy) {
+ CarrierRestrictionsWithPriority carriers,
+ int multiSimPolicy) {
RILRequest rr = mRil.processResponse(responseInfo);
if (rr == null) {
return;
@@ -2639,6 +3008,31 @@ public class RadioResponse extends IRadioResponse.Stub {
*/
public void setRadioPowerResponse_1_5(RadioResponseInfo info) {
responseVoid(info);
+ mRil.mLastRadioPowerResult = info.error;
+ if (info.error != RadioError.RADIO_NOT_AVAILABLE && info.error != RadioError.NONE) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString(RADIO_POWER_FAILURE_BUGREPORT_UUID), "Radio power failure");
+ }
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ */
+ public void setRadioPowerResponse_1_6(android.hardware.radio.V1_6.RadioResponseInfo info) {
+ responseVoid_1_6(info);
+ mRil.mLastRadioPowerResult = info.error;
+ if (info.error == android.hardware.radio.V1_6.RadioError.RF_HARDWARE_ISSUE) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString(RADIO_POWER_FAILURE_RF_HARDWARE_ISSUE_UUID), "RF HW damaged");
+ } else if (info.error == android.hardware.radio.V1_6.RadioError.NO_RF_CALIBRATION_INFO) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString(RADIO_POWER_FAILURE_NO_RF_CALIBRATION_UUID),
+ "No RF calibration data");
+ } else if (info.error != android.hardware.radio.V1_6.RadioError.RADIO_NOT_AVAILABLE
+ && info.error != android.hardware.radio.V1_6.RadioError.NONE) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString(RADIO_POWER_FAILURE_BUGREPORT_UUID), "Radio power failure");
+ }
}
/**
@@ -2648,6 +3042,74 @@ public class RadioResponse extends IRadioResponse.Stub {
responseVoid(info);
}
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error.
+ * @param halSpecifiers List of RadioAccessSpecifiers that are scanned.
+ */
+ public void getSystemSelectionChannelsResponse(
+ android.hardware.radio.V1_6.RadioResponseInfo info,
+ ArrayList<android.hardware.radio.V1_5.RadioAccessSpecifier> halSpecifiers) {
+ RILRequest rr = mRil.processResponse_1_6(info);
+
+ if (rr != null) {
+ ArrayList<RadioAccessSpecifier> specifiers = new ArrayList<>();
+ for (android.hardware.radio.V1_5.RadioAccessSpecifier specifier : halSpecifiers) {
+ specifiers.add(convertRadioAccessSpecifier(specifier));
+ }
+ mRil.riljLog("getSystemSelectionChannelsResponse: from HIDL: " + specifiers);
+ if (info.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, specifiers);
+ }
+ mRil.processResponseDone_1_6(rr, info, specifiers);
+ }
+ }
+
+ private static RadioAccessSpecifier convertRadioAccessSpecifier(
+ android.hardware.radio.V1_5.RadioAccessSpecifier specifier) {
+ if (specifier == null) return null;
+ ArrayList<Integer> halBands = new ArrayList<>();
+ switch (specifier.bands.getDiscriminator()) {
+ case android.hardware.radio.V1_5.RadioAccessSpecifier.Bands.hidl_discriminator
+ .geranBands:
+ halBands = specifier.bands.geranBands();
+ break;
+ case android.hardware.radio.V1_5.RadioAccessSpecifier.Bands.hidl_discriminator
+ .utranBands:
+ halBands = specifier.bands.utranBands();
+ break;
+ case android.hardware.radio.V1_5.RadioAccessSpecifier.Bands.hidl_discriminator
+ .eutranBands:
+ halBands = specifier.bands.eutranBands();
+ break;
+ case android.hardware.radio.V1_5.RadioAccessSpecifier.Bands.hidl_discriminator
+ .ngranBands:
+ halBands = specifier.bands.ngranBands();
+ break;
+ }
+ return new RadioAccessSpecifier(convertRanToAnt(specifier.radioAccessNetwork),
+ halBands.stream().mapToInt(Integer::intValue).toArray(),
+ specifier.channels.stream().mapToInt(Integer::intValue).toArray());
+ }
+
+ private static int convertRanToAnt(int ran) {
+ switch (ran) {
+ case android.hardware.radio.V1_5.RadioAccessNetworks.GERAN:
+ return AccessNetworkConstants.AccessNetworkType.GERAN;
+ case android.hardware.radio.V1_5.RadioAccessNetworks.UTRAN:
+ return AccessNetworkConstants.AccessNetworkType.UTRAN;
+ case android.hardware.radio.V1_5.RadioAccessNetworks.EUTRAN:
+ return AccessNetworkConstants.AccessNetworkType.EUTRAN;
+ case android.hardware.radio.V1_5.RadioAccessNetworks.NGRAN:
+ return AccessNetworkConstants.AccessNetworkType.NGRAN;
+ case android.hardware.radio.V1_5.RadioAccessNetworks.CDMA2000:
+ return AccessNetworkConstants.AccessNetworkType.CDMA2000;
+ case android.hardware.radio.V1_5.RadioAccessNetworks.UNKNOWN:
+ default:
+ return AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+ }
+ }
+
/**
* @param responseInfo Response info struct containing response type, serial no. and error.
* @param cellIdentity CellIdentity for the barringInfos.
@@ -2671,11 +3133,55 @@ public class RadioResponse extends IRadioResponse.Stub {
}
/**
- * @param responseInfo Response info struct containing response type, serial no. and error
- * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+ * @param info Response info struct containing response type, serial no. and error
+ * @param id The pdu session id allocated
*/
- public void sendCdmaSmsExpectMoreResponse(RadioResponseInfo responseInfo,
- SendSmsResult sms) {
- responseSms(responseInfo, sms);
+ public void allocatePduSessionIdResponse(android.hardware.radio.V1_6.RadioResponseInfo info,
+ int id) {
+ RILRequest rr = mRil.processResponse_1_6(info);
+ if (rr != null) {
+ if (info.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, id);
+ }
+ mRil.processResponseDone_1_6(rr, info, id);
+ }
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ */
+ public void releasePduSessionIdResponse(android.hardware.radio.V1_6.RadioResponseInfo info) {
+ responseVoid_1_6(info);
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ */
+ public void startHandoverResponse(android.hardware.radio.V1_6.RadioResponseInfo info) {
+ responseVoid_1_6(info);
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ */
+ public void cancelHandoverResponse(android.hardware.radio.V1_6.RadioResponseInfo info) {
+ responseVoid_1_6(info);
+ }
+
+ /**
+ * @param info Response info struct containing response type, serial no. and error
+ * @param slicingConfig Current slicing configuration
+ */
+ public void getSlicingConfigResponse(android.hardware.radio.V1_6.RadioResponseInfo info,
+ android.hardware.radio.V1_6.SlicingConfig slicingConfig) {
+ RILRequest rr = mRil.processResponse_1_6(info);
+
+ if (rr != null) {
+ NetworkSlicingConfig ret = new NetworkSlicingConfig(slicingConfig);
+ if (info.error == RadioError.NONE) {
+ sendMessageResponse(rr.mResult, ret);
+ }
+ mRil.processResponseDone_1_6(rr, info, ret);
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index 4d9cc3ef1d..24a8ac5eff 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -54,8 +54,6 @@ public class RatRatcheter {
private final SparseArray<SparseIntArray> mRatFamilyMap = new SparseArray<>();
private final Phone mPhone;
- private boolean mVoiceRatchetEnabled = true;
- private boolean mDataRatchetEnabled = true;
/**
* Updates the ServiceState with a new set of cell bandwidths IFF the new bandwidth list has a
@@ -119,55 +117,35 @@ public class RatRatcheter {
}
}
- /** Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family. */
- public void ratchet(@NonNull ServiceState oldSS, @NonNull ServiceState newSS,
- boolean locationChange) {
- // temporarily disable rat ratchet on location change.
- if (locationChange) {
- mVoiceRatchetEnabled = false;
- mDataRatchetEnabled = false;
- return;
- }
-
+ /**
+ * Ratchets RATs and cell bandwidths if oldSS and newSS have the same RAT family.
+ *
+ * Ensure that a device on the same cell reports the best-seen capability to the user.
+ */
+ public void ratchet(@NonNull ServiceState oldSS, @NonNull ServiceState newSS) {
// Different rat family, don't need rat ratchet and update cell bandwidths.
if (!isSameRatFamily(oldSS, newSS)) {
+ Rlog.e(LOG_TAG, "Same cell cannot have different RAT Families. Likely bug.");
return;
}
- updateBandwidths(oldSS.getCellBandwidths(), newSS);
-
- boolean newUsingCA = oldSS.isUsingCarrierAggregation()
- || newSS.isUsingCarrierAggregation()
- || newSS.getCellBandwidths().length > 1;
- NetworkRegistrationInfo oldCsNri = oldSS.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- NetworkRegistrationInfo newCsNri = newSS.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (mVoiceRatchetEnabled) {
- int newPsNetworkType = ratchetRat(oldCsNri.getAccessNetworkTechnology(),
- newCsNri.getAccessNetworkTechnology());
- newCsNri.setAccessNetworkTechnology(newPsNetworkType);
- newSS.addNetworkRegistrationInfo(newCsNri);
- } else if (oldCsNri.getAccessNetworkTechnology() != oldCsNri.getAccessNetworkTechnology()) {
- // resume rat ratchet on following rat change within the same location
- mVoiceRatchetEnabled = true;
+ final int[] domains = {
+ NetworkRegistrationInfo.DOMAIN_CS, NetworkRegistrationInfo.DOMAIN_PS};
+ for (int domain : domains) {
+ NetworkRegistrationInfo oldNri = oldSS.getNetworkRegistrationInfo(
+ domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ NetworkRegistrationInfo newNri = newSS.getNetworkRegistrationInfo(
+ domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ int newNetworkType = ratchetRat(oldNri.getAccessNetworkTechnology(),
+ newNri.getAccessNetworkTechnology());
+ newNri.setAccessNetworkTechnology(newNetworkType);
+ if (oldNri.isUsingCarrierAggregation()) newNri.setIsUsingCarrierAggregation(true);
+ newSS.addNetworkRegistrationInfo(newNri);
}
- NetworkRegistrationInfo oldPsNri = oldSS.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- NetworkRegistrationInfo newPsNri = newSS.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (mDataRatchetEnabled) {
- int newPsNetworkType = ratchetRat(oldPsNri.getAccessNetworkTechnology(),
- newPsNri.getAccessNetworkTechnology());
- newPsNri.setAccessNetworkTechnology(newPsNetworkType);
- newSS.addNetworkRegistrationInfo(newPsNri);
- } else if (oldPsNri.getAccessNetworkTechnology() != newPsNri.getAccessNetworkTechnology()) {
- // resume rat ratchet on following rat change within the same location
- mDataRatchetEnabled = true;
- }
-
- newSS.setIsUsingCarrierAggregation(newUsingCA);
+ // Ratchet Cell Bandwidths
+ updateBandwidths(oldSS.getCellBandwidths(), newSS);
}
private boolean isSameRatFamily(ServiceState ss1, ServiceState ss2) {
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
index 09485a87f2..83864e46a0 100644
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -16,16 +16,22 @@
package com.android.internal.telephony;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.os.SystemProperties;
+import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
import android.text.TextUtils;
import android.util.Pair;
+import com.android.internal.telephony.dataconnection.DataThrottler;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
@@ -117,34 +123,33 @@ public class RetryManager {
private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000;
/**
- * The value indicating no retry is needed
+ * The value indicating retry should not occur.
*/
- public static final long NO_RETRY = -1;
+ public static final long NO_RETRY = Long.MAX_VALUE;
/**
- * The value indicating modem did not suggest any retry delay
+ * The value indicating network did not suggest any retry delay
*/
- public static final long NO_SUGGESTED_RETRY_DELAY = -2;
+ public static final long NO_SUGGESTED_RETRY_DELAY = DataCallResponse.RETRY_DURATION_UNDEFINED;
/**
- * If the modem suggests a retry delay in the data call setup response, we will retry
- * the current APN setting again. However, if the modem keeps suggesting retrying the same
- * APN setting, we'll fall into an infinite loop. Therefore adding a counter to retry up to
- * MAX_SAME_APN_RETRY times can avoid it.
+ * If the network suggests a retry delay in the data call setup response, we will retry
+ * the current APN setting again. The maximum retry count is to prevent that network
+ * keeps asking device to retry data setup forever and causes power consumption issue.
*/
- private static final int MAX_SAME_APN_RETRY = 3;
+ private static final int DEFAULT_MAX_SAME_APN_RETRY = 3;
/**
* The delay (in milliseconds) between APN trying within the same round
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mInterApnDelay;
/**
* The delay (in milliseconds) between APN trying within the same round when we are in
* fail fast mode
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mFailFastInterApnDelay;
/**
@@ -154,26 +159,27 @@ public class RetryManager {
private long mApnRetryAfterDisconnectDelay;
/**
- * Modem suggested delay for retrying the current APN
+ * The counter for same APN retrying. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details.
*/
- private long mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY;
+ private int mSameApnRetryCount = 0;
/**
- * The counter for same APN retrying. See MAX_SAME_APN_RETRY for the details.
+ * The maximum times that frameworks retries data setup with the same APN. This value could be
+ * changed via carrier config. See {@link #DEFAULT_MAX_SAME_APN_RETRY} for the details.
*/
- private int mSameApnRetryCount = 0;
+ private int mMaxSameApnRetry = DEFAULT_MAX_SAME_APN_RETRY;
/**
* Retry record with times in milli-seconds
*/
private static class RetryRec {
- RetryRec(int delayTime, int randomizationTime) {
+ long mDelayTime;
+ long mRandomizationTime;
+
+ RetryRec(long delayTime, long randomizationTime) {
mDelayTime = delayTime;
mRandomizationTime = randomizationTime;
}
-
- int mDelayTime;
- int mRandomizationTime;
}
/**
@@ -181,9 +187,11 @@ public class RetryManager {
*/
private ArrayList<RetryRec> mRetryArray = new ArrayList<RetryRec>();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Phone mPhone;
+ private final DataThrottler mDataThrottler;
+
/**
* Flag indicating whether retrying forever regardless the maximum retry count mMaxRetryCount
*/
@@ -214,7 +222,7 @@ public class RetryManager {
* The list to store APN setting candidates for data call setup. Most of the carriers only have
* one APN, but few carriers have more than one.
*/
- private ArrayList<ApnSetting> mWaitingApns = null;
+ private ArrayList<ApnSetting> mWaitingApns = new ArrayList<>();
/**
* Index pointing to the current trying APN from mWaitingApns
@@ -222,19 +230,24 @@ public class RetryManager {
private int mCurrentApnIndex = -1;
/**
- * Apn context type. Could be "default, "mms", "supl", etc...
+ * Apn context type.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mApnType;
+ private final @ApnType int apnType;
+
/**
* Retry manager constructor
* @param phone Phone object
+ * @param dataThrottler Data throttler
* @param apnType APN type
*/
- public RetryManager(Phone phone, String apnType) {
+ public RetryManager(@NonNull Phone phone, @NonNull DataThrottler dataThrottler,
+ @ApnType int apnType) {
mPhone = phone;
- mApnType = apnType;
+ mDataThrottler = dataThrottler;
+ this.apnType = apnType;
}
/**
@@ -258,7 +271,7 @@ public class RetryManager {
mConfig = configStr;
if (!TextUtils.isEmpty(configStr)) {
- int defaultRandomization = 0;
+ long defaultRandomization = 0;
if (VDBG) log("configure: not empty");
@@ -356,6 +369,10 @@ public class RetryManager {
mApnRetryAfterDisconnectDelay = b.getLong(
CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG,
DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY);
+ mMaxSameApnRetry = b.getInt(
+ CarrierConfigManager
+ .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT,
+ DEFAULT_MAX_SAME_APN_RETRY);
// Load all retry patterns for all different APNs.
String[] allConfigStrings = b.getStringArray(
@@ -365,14 +382,14 @@ public class RetryManager {
if (!TextUtils.isEmpty(s)) {
String splitStr[] = s.split(":", 2);
if (splitStr.length == 2) {
- String apnType = splitStr[0].trim();
+ String apnTypeStr = splitStr[0].trim();
// Check if this retry pattern is for the APN we want.
- if (apnType.equals(mApnType)) {
+ if (apnTypeStr.equals(ApnSetting.getApnTypeString(apnType))) {
// Extract the config string. Note that an empty string is valid
// here, meaning no retry for the specified APN.
configString = splitStr[1];
break;
- } else if (apnType.equals(OTHERS_APN_TYPE)) {
+ } else if (apnTypeStr.equals(OTHERS_APN_TYPE)) {
// Extract the config string. Note that an empty string is valid
// here, meaning no retry for all other APNs.
otherConfigString = splitStr[1];
@@ -413,7 +430,7 @@ public class RetryManager {
* Return the timer that should be used to trigger the data reconnection
*/
@UnsupportedAppUsage
- private int getRetryTimer() {
+ private long getRetryTimer() {
int index;
if (mRetryCount < mRetryArray.size()) {
index = mRetryCount;
@@ -421,7 +438,7 @@ public class RetryManager {
index = mRetryArray.size() - 1;
}
- int retVal;
+ long retVal;
if ((index >= 0) && (index < mRetryArray.size())) {
retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index);
} else {
@@ -443,13 +460,13 @@ public class RetryManager {
Pair<Boolean, Integer> retVal;
try {
value = Integer.parseInt(stringValue);
- retVal = new Pair<Boolean, Integer>(validateNonNegativeInt(name, value), value);
+ retVal = new Pair<>(validateNonNegativeInt(name, value), value);
} catch (NumberFormatException e) {
Rlog.e(LOG_TAG, name + " bad value: " + stringValue, e);
- retVal = new Pair<Boolean, Integer>(false, 0);
+ retVal = new Pair<>(false, 0);
}
if (VDBG) {
- log("parseNonNetativeInt: " + name + ", " + stringValue + ", "
+ log("parseNonNegativeInt: " + name + ", " + stringValue + ", "
+ retVal.first + ", " + retVal.second);
}
return retVal;
@@ -461,7 +478,7 @@ public class RetryManager {
* @param value Value
* @return Pair.first
*/
- private boolean validateNonNegativeInt(String name, int value) {
+ private boolean validateNonNegativeInt(String name, long value) {
boolean retVal;
if (value < 0) {
Rlog.e(LOG_TAG, name + " bad value: is < 0");
@@ -477,15 +494,26 @@ public class RetryManager {
* Return next random number for the index
* @param index Retry index
*/
- private int nextRandomizationTime(int index) {
- int randomTime = mRetryArray.get(index).mRandomizationTime;
+ private long nextRandomizationTime(int index) {
+ long randomTime = mRetryArray.get(index).mRandomizationTime;
if (randomTime == 0) {
return 0;
} else {
- return mRng.nextInt(randomTime);
+ return mRng.nextInt((int) randomTime);
}
}
+ private long getNetworkSuggestedRetryDelay() {
+ long retryElapseTime = mDataThrottler.getRetryTime(apnType);
+ if (retryElapseTime == NO_RETRY || retryElapseTime == NO_SUGGESTED_RETRY_DELAY) {
+ return retryElapseTime;
+ }
+
+ // The time from data throttler is system's elapsed time. We need to return the delta. If
+ // less than 0, then return 0 (i.e. retry immediately).
+ return Math.max(0, retryElapseTime - SystemClock.elapsedRealtime());
+ }
+
/**
* Get the next APN setting for data call setup.
* @return APN setting to try. {@code null} if cannot find any APN,
@@ -496,12 +524,18 @@ public class RetryManager {
return null;
}
- // If the modem had suggested a retry delay, we should retry the current APN again
- // (up to MAX_SAME_APN_RETRY times) instead of getting the next APN setting from
+ long networkSuggestedRetryDelay = getNetworkSuggestedRetryDelay();
+ if (networkSuggestedRetryDelay == NO_RETRY) {
+ log("Network suggested no retry.");
+ return null;
+ }
+
+ // If the network had suggested a retry delay, we should retry the current APN again
+ // (up to mMaxSameApnRetry times) instead of getting the next APN setting from
// our own list. If the APN waiting list has been reset before a setup data responses
- // arrive (i.e. mCurrentApnIndex=-1), then ignore the modem suggested retry.
- if (mCurrentApnIndex != -1 && mModemSuggestedDelay != NO_SUGGESTED_RETRY_DELAY
- && mSameApnRetryCount < MAX_SAME_APN_RETRY) {
+ // arrive (i.e. mCurrentApnIndex=-1), then ignore the network suggested retry.
+ if (mCurrentApnIndex != -1 && networkSuggestedRetryDelay != NO_SUGGESTED_RETRY_DELAY
+ && mSameApnRetryCount < mMaxSameApnRetry) {
mSameApnRetryCount++;
return mWaitingApns.get(mCurrentApnIndex);
}
@@ -541,17 +575,20 @@ public class RetryManager {
return NO_RETRY;
}
- if (mModemSuggestedDelay == NO_RETRY) {
- log("Modem suggested not retrying.");
+ long networkSuggestedDelay = getNetworkSuggestedRetryDelay();
+ log("Network suggested delay=" + networkSuggestedDelay + "ms");
+
+ if (networkSuggestedDelay == NO_RETRY) {
+ log("Network suggested not retrying.");
return NO_RETRY;
}
- if (mModemSuggestedDelay != NO_SUGGESTED_RETRY_DELAY &&
- mSameApnRetryCount < MAX_SAME_APN_RETRY) {
- // If the modem explicitly suggests a retry delay, we should use it, even in fail fast
+ if (networkSuggestedDelay != NO_SUGGESTED_RETRY_DELAY
+ && mSameApnRetryCount < mMaxSameApnRetry) {
+ // If the network explicitly suggests a retry delay, we should use it, even in fail fast
// mode.
- log("Modem suggested retry in " + mModemSuggestedDelay + " ms.");
- return mModemSuggestedDelay;
+ log("Network suggested retry in " + networkSuggestedDelay + " ms.");
+ return networkSuggestedDelay;
}
// In order to determine the delay to try next APN, we need to peek the next available APN.
@@ -619,7 +656,6 @@ public class RetryManager {
mRetryCount = 0;
mCurrentApnIndex = -1;
mSameApnRetryCount = 0;
- mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY;
mRetryArray.clear();
}
@@ -656,24 +692,11 @@ public class RetryManager {
* Get the list of waiting APNs.
* @return the list of waiting APNs
*/
- public ArrayList<ApnSetting> getWaitingApns() {
+ public @NonNull ArrayList<ApnSetting> getWaitingApns() {
return mWaitingApns;
}
/**
- * Save the modem suggested delay for retrying the current APN.
- * This method is called when we get the suggested delay from RIL.
- * @param delay The delay in milliseconds
- */
- public void setModemSuggestedDelay(long delay) {
- if (mCurrentApnIndex == -1) {
- log("Waiting APN list has been reset. Ignore the value from modem.");
- return;
- }
- mModemSuggestedDelay = delay;
- }
-
- /**
* Get the delay in milliseconds for APN retry after disconnect
* @return The delay in milliseconds
*/
@@ -683,16 +706,18 @@ public class RetryManager {
public String toString() {
if (mConfig == null) return "";
- return "RetryManager: mApnType=" + mApnType + " mRetryCount=" + mRetryCount
- + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex
- + " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay="
- + mModemSuggestedDelay + " mRetryForever=" + mRetryForever + " mInterApnDelay="
- + mInterApnDelay + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
+ return "RetryManager: apnType=" + ApnSetting.getApnTypeString(apnType)
+ + " mRetryCount="
+ + mRetryCount + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex="
+ + mCurrentApnIndex + " mSameApnRtryCount=" + mSameApnRetryCount
+ + " networkSuggestedDelay=" + getNetworkSuggestedRetryDelay() + " mRetryForever="
+ + mRetryForever + " mInterApnDelay=" + mInterApnDelay
+ + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
+ " mConfig={" + mConfig + "}";
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String s) {
- Rlog.d(LOG_TAG, "[" + mApnType + "] " + s);
+ Rlog.d(LOG_TAG, "[" + ApnSetting.getApnTypeString(apnType) + "] " + s);
}
}
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index c7f8c1b0c7..d60e357b73 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -17,14 +17,6 @@
package com.android.internal.telephony;
import static android.Manifest.permission.SEND_SMS_NO_CONFIRMATION;
-import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
-import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
-import static android.telephony.SmsManager.RESULT_ERROR_NONE;
-import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
-import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
-import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
-import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
-import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
@@ -49,7 +41,9 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
@@ -59,7 +53,8 @@ import android.provider.Telephony;
import android.provider.Telephony.Sms;
import android.service.carrier.CarrierMessagingService;
import android.service.carrier.CarrierMessagingServiceWrapper;
-import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper;
+import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback;
+import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
@@ -69,6 +64,8 @@ import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -86,10 +83,14 @@ import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
import com.android.telephony.Rlog;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -144,17 +145,17 @@ public abstract class SMSDispatcher extends Handler {
protected static final int EVENT_ICC_CHANGED = 15;
protected static final int EVENT_GET_IMS_SERVICE = 16;
-
@UnsupportedAppUsage
protected Phone mPhone;
@UnsupportedAppUsage
protected final Context mContext;
@UnsupportedAppUsage
protected final ContentResolver mResolver;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final CommandsInterface mCi;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final TelephonyManager mTelephonyManager;
+ protected final LocalLog mLocalLog = new LocalLog(16);
/** Maximum number of times to retry sending a failed SMS. */
private static final int MAX_SEND_RETRIES = 3;
@@ -162,6 +163,12 @@ public abstract class SMSDispatcher extends Handler {
private static final int SEND_RETRY_DELAY = 2000;
/** Message sending queue limit */
private static final int MO_MSG_QUEUE_LIMIT = 5;
+ /** SMS anomaly uuid -- CarrierMessagingService did not respond */
+ private static final UUID sAnomalyNoResponseFromCarrierMessagingService =
+ UUID.fromString("279d9fbc-462d-4fc2-802c-bf21ddd9dd90");
+ /** SMS anomaly uuid -- CarrierMessagingService unexpected callback */
+ private static final UUID sAnomalyUnexpectedCallback =
+ UUID.fromString("0103b6d2-ad07-4d86-9102-14341b9074ef");
/**
* Message reference for a CONCATENATED_8_BIT_REFERENCE or
@@ -180,7 +187,10 @@ public abstract class SMSDispatcher extends Handler {
protected boolean mSmsCapable = true;
protected boolean mSmsSendDisabled;
- @UnsupportedAppUsage
+ @VisibleForTesting
+ public int mCarrierMessagingTimeout = 10 * 60 * 1000; //10 minutes
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected static int getNextConcatenatedRef() {
sConcatenatedRef += 1;
return sConcatenatedRef;
@@ -230,7 +240,7 @@ public abstract class SMSDispatcher extends Handler {
}
/** Unregister for incoming SMS events. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void dispose() {
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
}
@@ -260,15 +270,6 @@ public abstract class SMSDispatcher extends Handler {
Rlog.d(TAG, "handleStatusReport() called with no subclass.");
}
- /* TODO: Need to figure out how to keep track of status report routing in a
- * persistent manner. If the phone process restarts (reboot or crash),
- * we will lose this list and any status reports that come in after
- * will be dropped.
- */
- /** Sent messages awaiting a delivery status report. */
- @UnsupportedAppUsage
- protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
-
/**
* Handles events coming from the phone stack. Overridden from handler.
*
@@ -315,7 +316,7 @@ public abstract class SMSDispatcher extends Handler {
Rlog.d(TAG, "SMSDispatcher: EVENT_SENDING_NOT_ALLOWED - "
+ "sending SHORT_CODE_NEVER_ALLOWED error code.");
handleSmsTrackersFailure(
- trackers, RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE);
+ trackers, SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, NO_ERROR_CODE);
break;
}
@@ -325,21 +326,21 @@ public abstract class SMSDispatcher extends Handler {
int error;
if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
- error = RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+ error = SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ "sending SHORT_CODE_NEVER_ALLOWED error code.");
} else {
- error = RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
+ error = SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ "sending SHORT_CODE_NOT_ALLOWED error code.");
}
} else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
- error = RESULT_ERROR_LIMIT_EXCEEDED;
+ error = SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ "sending LIMIT_EXCEEDED error code.");
} else {
- error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING;
- Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+ error = SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING;
+ Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
}
handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE);
@@ -359,83 +360,157 @@ public abstract class SMSDispatcher extends Handler {
/**
* Use the carrier messaging service to send a data or text SMS.
*/
- protected abstract class SmsSender extends CarrierMessagingServiceWrapper {
- protected final SmsTracker mTracker;
+ protected abstract class SmsSender extends Handler {
+ private static final int EVENT_TIMEOUT = 1;
// Initialized in sendSmsByCarrierApp
- protected volatile SmsSenderCallback mSenderCallback;
+ protected volatile CarrierMessagingCallback mSenderCallback;
+ protected final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper =
+ new CarrierMessagingServiceWrapper();
+ private String mCarrierPackageName;
- protected SmsSender(SmsTracker tracker) {
- mTracker = tracker;
+ protected SmsSender() {
+ super(Looper.getMainLooper());
}
- public void sendSmsByCarrierApp(String carrierPackageName,
- SmsSenderCallback senderCallback) {
+ /**
+ * Bind to carrierPackageName to send message through it
+ */
+ public synchronized void sendSmsByCarrierApp(String carrierPackageName,
+ CarrierMessagingCallback senderCallback) {
+ mCarrierPackageName = carrierPackageName;
mSenderCallback = senderCallback;
- if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
+ if (!mCarrierMessagingServiceWrapper.bindToCarrierMessagingService(
+ mContext, carrierPackageName, runnable -> runnable.run(),
+ ()->onServiceReady())) {
Rlog.e(TAG, "bindService() for carrier messaging service failed");
- mSenderCallback.onSendSmsComplete(
- CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- 0 /* messageRef */);
+ onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
} else {
Rlog.d(TAG, "bindService() for carrier messaging service succeeded");
+ sendMessageDelayed(obtainMessage(EVENT_TIMEOUT), mCarrierMessagingTimeout);
}
}
+
+ /**
+ * Callback received from mCarrierPackageName on binding to it is done.
+ * NOTE: the implementations of this method must be synchronized to make sure it does not
+ * get called before {@link #sendSmsByCarrierApp} completes and {@link #EVENT_TIMEOUT} is
+ * posted
+ */
+ public abstract void onServiceReady();
+
+ /**
+ * Method to call message send callback with passed in result and default parameters
+ */
+ public abstract void onSendComplete(@CarrierMessagingService.SendResult int result);
+
+ /**
+ * Used to get the SmsTracker for single part messages
+ */
+ public abstract SmsTracker getSmsTracker();
+
+ /**
+ * Used to get the SmsTrackers for multi part messages
+ */
+ public abstract SmsTracker[] getSmsTrackers();
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == EVENT_TIMEOUT) {
+ logWithLocalLog("handleMessage: No response from " + mCarrierPackageName
+ + " for " + mCarrierMessagingTimeout + " ms");
+ AnomalyReporter.reportAnomaly(sAnomalyNoResponseFromCarrierMessagingService,
+ "No response from " + mCarrierPackageName);
+ onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
+ } else {
+ logWithLocalLog("handleMessage: received unexpected message " + msg.what);
+ }
+ }
+
+ public void removeTimeout() {
+ removeMessages(EVENT_TIMEOUT);
+ }
+ }
+
+ private void logWithLocalLog(String logStr) {
+ mLocalLog.log(logStr);
+ Rlog.d(TAG, logStr);
}
/**
* Use the carrier messaging service to send a text SMS.
*/
protected final class TextSmsSender extends SmsSender {
+ private final SmsTracker mTracker;
public TextSmsSender(SmsTracker tracker) {
- super(tracker);
+ super();
+ mTracker = tracker;
}
@Override
- public void onServiceReady() {
+ public synchronized void onServiceReady() {
+ Rlog.d(TAG, "TextSmsSender::onServiceReady");
HashMap<String, Object> map = mTracker.getData();
String text = (String) map.get(MAP_KEY_TEXT);
if (text != null) {
try {
- sendTextSms(
+ mCarrierMessagingServiceWrapper.sendTextSms(
text,
getSubId(),
mTracker.mDestAddress,
(mTracker.mDeliveryIntent != null)
? CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS
: 0,
+ runnable -> runnable.run(),
mSenderCallback);
} catch (RuntimeException e) {
- Rlog.e(TAG, "Exception sending the SMS: " + e);
- mSenderCallback.onSendSmsComplete(
- CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- 0 /* messageRef */);
+ Rlog.e(TAG, "TextSmsSender::onServiceReady: Exception sending the SMS: "
+ + e.getMessage());
+ onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
}
} else {
- mSenderCallback.onSendSmsComplete(
- CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- 0 /* messageRef */);
+ Rlog.d(TAG, "TextSmsSender::onServiceReady: text == null");
+ onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
}
}
+
+ @Override
+ public void onSendComplete(int result) {
+ mSenderCallback.onSendSmsComplete(result, 0 /* messageRef */);
+ }
+
+ @Override
+ public SmsTracker getSmsTracker() {
+ return mTracker;
+ }
+
+ @Override
+ public SmsTracker[] getSmsTrackers() {
+ Rlog.e(TAG, "getSmsTrackers: Unexpected call for TextSmsSender");
+ return null;
+ }
}
/**
* Use the carrier messaging service to send a data SMS.
*/
protected final class DataSmsSender extends SmsSender {
+ private final SmsTracker mTracker;
public DataSmsSender(SmsTracker tracker) {
- super(tracker);
+ super();
+ mTracker = tracker;
}
@Override
- public void onServiceReady() {
+ public synchronized void onServiceReady() {
+ Rlog.d(TAG, "DataSmsSender::onServiceReady");
HashMap<String, Object> map = mTracker.getData();
byte[] data = (byte[]) map.get(MAP_KEY_DATA);
int destPort = (int) map.get(MAP_KEY_DEST_PORT);
if (data != null) {
try {
- sendDataSms(
+ mCarrierMessagingServiceWrapper.sendDataSms(
data,
getSubId(),
mTracker.mDestAddress,
@@ -443,29 +518,43 @@ public abstract class SMSDispatcher extends Handler {
(mTracker.mDeliveryIntent != null)
? CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS
: 0,
+ runnable -> runnable.run(),
mSenderCallback);
} catch (RuntimeException e) {
- Rlog.e(TAG, "Exception sending the SMS: " + e
- + " id: " + mTracker.mMessageId);
- mSenderCallback.onSendSmsComplete(
- CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- 0 /* messageRef */);
+ Rlog.e(TAG, "DataSmsSender::onServiceReady: Exception sending the SMS: " + e
+ + " " + SmsController.formatCrossStackMessageId(mTracker.mMessageId));
+ onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
}
} else {
- mSenderCallback.onSendSmsComplete(
- CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- 0 /* messageRef */);
+ Rlog.d(TAG, "DataSmsSender::onServiceReady: data == null");
+ onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
}
}
+
+ @Override
+ public void onSendComplete(int result) {
+ mSenderCallback.onSendSmsComplete(result, 0 /* messageRef */);
+ }
+
+ @Override
+ public SmsTracker getSmsTracker() {
+ return mTracker;
+ }
+
+ @Override
+ public SmsTracker[] getSmsTrackers() {
+ Rlog.e(TAG, "getSmsTrackers: Unexpected call for DataSmsSender");
+ return null;
+ }
}
/**
* Callback for TextSmsSender and DataSmsSender from the carrier messaging service.
* Once the result is ready, the carrier messaging service connection is disposed.
*/
- protected final class SmsSenderCallback extends
- CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper {
+ protected final class SmsSenderCallback implements CarrierMessagingCallback {
private final SmsSender mSmsSender;
+ private boolean mCallbackCalled = false;
public SmsSenderCallback(SmsSender smsSender) {
mSmsSender = smsSender;
@@ -476,11 +565,19 @@ public abstract class SMSDispatcher extends Handler {
*/
@Override
public void onSendSmsComplete(int result, int messageRef) {
- checkCallerIsPhoneOrCarrierApp();
+ Rlog.d(TAG, "onSendSmsComplete: result=" + result + " messageRef=" + messageRef);
+ if (mCallbackCalled) {
+ logWithLocalLog("onSendSmsComplete: unexpected call");
+ AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
+ "Unexpected onSendSmsComplete");
+ return;
+ }
+ mCallbackCalled = true;
final long identity = Binder.clearCallingIdentity();
try {
- mSmsSender.disposeConnection(mContext);
- processSendSmsResponse(mSmsSender.mTracker, result, messageRef);
+ mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
+ processSendSmsResponse(mSmsSender.getSmsTracker(), result, messageRef);
+ mSmsSender.removeTimeout();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -492,8 +589,8 @@ public abstract class SMSDispatcher extends Handler {
}
@Override
- public void onFilterComplete(int result) {
- Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + result);
+ public void onReceiveSmsComplete(int result) {
+ Rlog.e(TAG, "Unexpected onReceiveSmsComplete call with result: " + result);
}
@Override
@@ -507,39 +604,43 @@ public abstract class SMSDispatcher extends Handler {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void processSendSmsResponse(SmsTracker tracker, int result, int messageRef) {
if (tracker == null) {
Rlog.e(TAG, "processSendSmsResponse: null tracker");
return;
}
- SmsResponse smsResponse = new SmsResponse(messageRef, null /* ackPdu */, NO_ERROR_CODE);
+ SmsResponse smsResponse = new SmsResponse(messageRef, null /* ackPdu */, NO_ERROR_CODE,
+ tracker.mMessageId);
switch (result) {
case CarrierMessagingService.SEND_STATUS_OK:
- Rlog.d(TAG, "Sending SMS by IP succeeded."
- + " id: " + tracker.mMessageId);
+ Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService "
+ + "succeeded. "
+ + SmsController.formatCrossStackMessageId(tracker.mMessageId));
sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
new AsyncResult(tracker,
smsResponse,
null /* exception*/)));
break;
case CarrierMessagingService.SEND_STATUS_ERROR:
- Rlog.d(TAG, "Sending SMS by IP failed."
- + " id: " + tracker.mMessageId);
+ Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService failed."
+ + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
new AsyncResult(tracker, smsResponse,
new CommandException(CommandException.Error.GENERIC_FAILURE))));
break;
case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK:
- Rlog.d(TAG, "Sending SMS by IP failed. Retry on carrier network."
- + " id: " + tracker.mMessageId);
+ Rlog.d(TAG, "processSendSmsResponse: Sending SMS by CarrierMessagingService failed."
+ + " Retry on carrier network. "
+ + SmsController.formatCrossStackMessageId(tracker.mMessageId));
sendSubmitPdu(tracker);
break;
default:
- Rlog.d(TAG, "Unknown result " + result + " Retry on carrier network."
- + " id: " + tracker.mMessageId);
+ Rlog.d(TAG, "processSendSmsResponse: Unknown result " + result + " Retry on carrier"
+ + " network. "
+ + SmsController.formatCrossStackMessageId(tracker.mMessageId));
sendSubmitPdu(tracker);
}
}
@@ -547,33 +648,25 @@ public abstract class SMSDispatcher extends Handler {
/**
* Use the carrier messaging service to send a multipart text SMS.
*/
- private final class MultipartSmsSender extends CarrierMessagingServiceWrapper {
+ private final class MultipartSmsSender extends SmsSender {
private final List<String> mParts;
public final SmsTracker[] mTrackers;
- // Initialized in sendSmsByCarrierApp
- private volatile MultipartSmsSenderCallback mSenderCallback;
MultipartSmsSender(ArrayList<String> parts, SmsTracker[] trackers) {
+ super();
mParts = parts;
mTrackers = trackers;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void sendSmsByCarrierApp(String carrierPackageName,
MultipartSmsSenderCallback senderCallback) {
- mSenderCallback = senderCallback;
- if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
- Rlog.e(TAG, "bindService() for carrier messaging service failed");
- mSenderCallback.onSendMultipartSmsComplete(
- CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- null /* smsResponse */);
- } else {
- Rlog.d(TAG, "bindService() for carrier messaging service succeeded");
- }
+ super.sendSmsByCarrierApp(carrierPackageName, senderCallback);
}
@Override
- public void onServiceReady() {
+ public synchronized void onServiceReady() {
+ Rlog.d(TAG, "MultipartSmsSender::onServiceReady");
boolean statusReportRequested = false;
for (SmsTracker tracker : mTrackers) {
if (tracker.mDeliveryIntent != null) {
@@ -583,29 +676,45 @@ public abstract class SMSDispatcher extends Handler {
}
try {
- sendMultipartTextSms(
+ mCarrierMessagingServiceWrapper.sendMultipartTextSms(
mParts,
getSubId(),
mTrackers[0].mDestAddress,
statusReportRequested
? CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS
: 0,
+ runnable -> runnable.run(),
mSenderCallback);
} catch (RuntimeException e) {
- Rlog.e(TAG, "Exception sending the SMS: " + e);
- mSenderCallback.onSendMultipartSmsComplete(
- CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
- null /* smsResponse */);
+ Rlog.e(TAG, "MultipartSmsSender::onServiceReady: Exception sending the SMS: " + e);
+ onSendComplete(CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK);
}
}
+
+ @Override
+ public void onSendComplete(int result) {
+ mSenderCallback.onSendMultipartSmsComplete(result, null /* messageRefs */);
+ }
+
+ @Override
+ public SmsTracker getSmsTracker() {
+ Rlog.e(TAG, "getSmsTracker: Unexpected call for MultipartSmsSender");
+ return null;
+ }
+
+ @Override
+ public SmsTracker[] getSmsTrackers() {
+ return mTrackers;
+ }
}
/**
* Callback for MultipartSmsSender from the carrier messaging service.
* Once the result is ready, the carrier messaging service connection is disposed.
*/
- private final class MultipartSmsSenderCallback extends CarrierMessagingCallbackWrapper {
+ private final class MultipartSmsSenderCallback implements CarrierMessagingCallback {
private final MultipartSmsSender mSmsSender;
+ private boolean mCallbackCalled = false;
MultipartSmsSenderCallback(MultipartSmsSender smsSender) {
mSmsSender = smsSender;
@@ -621,31 +730,34 @@ public abstract class SMSDispatcher extends Handler {
*/
@Override
public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
- mSmsSender.disposeConnection(mContext);
+ Rlog.d(TAG, "onSendMultipartSmsComplete: result=" + result + " messageRefs="
+ + Arrays.toString(messageRefs));
+ if (mCallbackCalled) {
+ logWithLocalLog("onSendMultipartSmsComplete: unexpected call");
+ AnomalyReporter.reportAnomaly(sAnomalyUnexpectedCallback,
+ "Unexpected onSendMultipartSmsComplete");
+ return;
+ }
+ mCallbackCalled = true;
+ mSmsSender.removeTimeout();
+ mSmsSender.mCarrierMessagingServiceWrapper.disconnect();
if (mSmsSender.mTrackers == null) {
Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with null trackers.");
return;
}
- checkCallerIsPhoneOrCarrierApp();
final long identity = Binder.clearCallingIdentity();
try {
- for (int i = 0; i < mSmsSender.mTrackers.length; i++) {
- int messageRef = 0;
- if (messageRefs != null && messageRefs.length > i) {
- messageRef = messageRefs[i];
- }
- processSendSmsResponse(mSmsSender.mTrackers[i], result, messageRef);
- }
+ processSendMultipartSmsResponse(mSmsSender.mTrackers, result, messageRefs);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public void onFilterComplete(int result) {
- Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + result);
+ public void onReceiveSmsComplete(int result) {
+ Rlog.e(TAG, "Unexpected onReceiveSmsComplete call with result: " + result);
}
@Override
@@ -659,8 +771,75 @@ public abstract class SMSDispatcher extends Handler {
}
}
+ private void processSendMultipartSmsResponse(
+ SmsTracker[] trackers, int result, int[] messageRefs) {
+ if (trackers == null) {
+ Rlog.e(TAG, "processSendMultipartSmsResponse: null trackers");
+ return;
+ }
+
+ switch (result) {
+ case CarrierMessagingService.SEND_STATUS_OK:
+ Rlog.d(TAG, "processSendMultipartSmsResponse: Sending SMS by "
+ + "CarrierMessagingService succeeded. "
+ + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
+ // Sending a multi-part SMS by CarrierMessagingService successfully completed.
+ // Send EVENT_SEND_SMS_COMPLETE for all the parts one by one.
+ for (int i = 0; i < trackers.length; i++) {
+ int messageRef = 0;
+ if (messageRefs != null && messageRefs.length > i) {
+ messageRef = messageRefs[i];
+ }
+ sendMessage(
+ obtainMessage(
+ EVENT_SEND_SMS_COMPLETE,
+ new AsyncResult(
+ trackers[i],
+ new SmsResponse(
+ messageRef, null /* ackPdu */, NO_ERROR_CODE),
+ null /* exception */)));
+ }
+ break;
+ case CarrierMessagingService.SEND_STATUS_ERROR:
+ Rlog.d(TAG, "processSendMultipartSmsResponse: Sending SMS by "
+ + "CarrierMessagingService failed. "
+ + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
+ // Sending a multi-part SMS by CarrierMessagingService failed.
+ // Send EVENT_SEND_SMS_COMPLETE with GENERIC_FAILURE for all the parts one by one.
+ for (int i = 0; i < trackers.length; i++) {
+ int messageRef = 0;
+ if (messageRefs != null && messageRefs.length > i) {
+ messageRef = messageRefs[i];
+ }
+ sendMessage(
+ obtainMessage(
+ EVENT_SEND_SMS_COMPLETE,
+ new AsyncResult(
+ trackers[i],
+ new SmsResponse(
+ messageRef, null /* ackPdu */, NO_ERROR_CODE),
+ new CommandException(
+ CommandException.Error.GENERIC_FAILURE))));
+ }
+ break;
+ case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK:
+ Rlog.d(TAG, "processSendMultipartSmsResponse: Sending SMS by "
+ + "CarrierMessagingService failed. Retry on carrier network. "
+ + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
+ // All the parts for a multi-part SMS are handled together for retry. It helps to
+ // check user confirmation once also if needed.
+ sendSubmitPdu(trackers);
+ break;
+ default:
+ Rlog.d(TAG, "processSendMultipartSmsResponse: Unknown result " + result
+ + ". Retry on carrier network. "
+ + SmsController.formatCrossStackMessageId(trackers[0].mMessageId));
+ sendSubmitPdu(trackers);
+ }
+ }
+
/** Send a single SMS PDU. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendSubmitPdu(SmsTracker tracker) {
sendSubmitPdu(new SmsTracker[] {tracker});
}
@@ -693,9 +872,10 @@ public abstract class SMSDispatcher extends Handler {
protected void handleSendComplete(AsyncResult ar) {
SmsTracker tracker = (SmsTracker) ar.userObj;
PendingIntent sentIntent = tracker.mSentIntent;
+ SmsResponse smsResponse = (SmsResponse) ar.result;
- if (ar.result != null) {
- tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef;
+ if (smsResponse != null) {
+ tracker.mMessageRef = smsResponse.mMessageRef;
} else {
Rlog.d(TAG, "SmsResponse was null");
}
@@ -703,27 +883,37 @@ public abstract class SMSDispatcher extends Handler {
if (ar.exception == null) {
if (DBG) {
Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent
- + " id: " + tracker.mMessageId);
+ + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
}
if (tracker.mDeliveryIntent != null) {
- // Expecting a status report. Add it to the list.
- deliveryPendingList.add(tracker);
+ // Expecting a status report. Put this tracker to the map.
+ mSmsDispatchersController.putDeliveryPendingTracker(tracker);
}
tracker.onSent(mContext);
mPhone.notifySmsSent(tracker.mDestAddress);
+
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ SmsManager.RESULT_ERROR_NONE,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
} else {
if (DBG) {
- Rlog.d(TAG, "SMS send failed"
- + " id: " + tracker.mMessageId);
+ Rlog.d(TAG, "SMS send failed "
+ + SmsController.formatCrossStackMessageId(tracker.mMessageId));
}
int ss = mPhone.getServiceState().getState();
+ int error = rilErrorToSmsManagerResult(
+ ((CommandException) (ar.exception)).getCommandError());
- if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
+ if (tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
// This is retry after failure over IMS but voice is not available.
- // Set retry to max allowed, so no retry is sent and
- // cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.
+ // Set retry to max allowed, so no retry is sent and cause
+ // SmsManager.RESULT_ERROR_GENERIC_FAILURE to be returned to app.
tracker.mRetryCount = MAX_SEND_RETRIES;
Rlog.d(TAG, "handleSendComplete: Skipping retry: "
@@ -732,15 +922,21 @@ public abstract class SMSDispatcher extends Handler {
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ " SS= " + mPhone.getServiceState().getState()
- + " id=" + tracker.mMessageId);
+ + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
}
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE);
- } else if ((((CommandException)(ar.exception)).getCommandError()
- == CommandException.Error.SMS_FAIL_RETRY) &&
- tracker.mRetryCount < MAX_SEND_RETRIES) {
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ getNotInServiceError(ss),
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
+ } else if (error == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY
+ && tracker.mRetryCount < MAX_SEND_RETRIES) {
// Retry after a delay if needed.
// TODO: According to TS 23.040, 9.2.3.6, we should resend
// with the same TP-MR as the failed message, and
@@ -750,20 +946,33 @@ public abstract class SMSDispatcher extends Handler {
// message, depending on the failure). Also, in some
// implementations this retry is handled by the baseband.
tracker.mRetryCount++;
+ int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE;
Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY,
+ errorCode,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
} else {
- int errorCode = NO_ERROR_CODE;
- if (ar.result != null) {
- errorCode = ((SmsResponse)ar.result).mErrorCode;
- }
- int error = rilErrorToSmsManagerResult(((CommandException) (ar.exception))
- .getCommandError());
+ int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE;
tracker.onFailed(mContext, error, errorCode);
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ error,
+ errorCode,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
}
}
}
+ @SmsManager.Result
private static int rilErrorToSmsManagerResult(CommandException.Error rilError) {
switch (rilError) {
case RADIO_NOT_AVAILABLE:
@@ -808,31 +1017,14 @@ public abstract class SMSDispatcher extends Handler {
return SmsManager.RESULT_RIL_SIM_ABSENT;
case FDN_CHECK_FAILURE:
return SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
+ case SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED:
+ return SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED;
+ case ACCESS_BARRED:
+ return SmsManager.RESULT_RIL_ACCESS_BARRED;
+ case BLOCKED_DUE_TO_CALL:
+ return SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL;
default:
- return RESULT_ERROR_GENERIC_FAILURE;
- }
- }
-
- /**
- * Handles outbound message when the phone is not in service.
- *
- * @param ss Current service state. Valid values are:
- * OUT_OF_SERVICE
- * EMERGENCY_ONLY
- * POWER_OFF
- * @param sentIntent the PendingIntent to send the error to
- */
- protected static void handleNotInService(int ss, PendingIntent sentIntent) {
- if (sentIntent != null) {
- try {
- if (ss == ServiceState.STATE_POWER_OFF) {
- sentIntent.send(RESULT_ERROR_RADIO_OFF);
- } else {
- sentIntent.send(RESULT_ERROR_NO_SERVICE);
- }
- } catch (CanceledException ex) {
- Rlog.e(TAG, "Failed to send result");
- }
+ return SmsManager.RESULT_ERROR_GENERIC_FAILURE;
}
}
@@ -840,11 +1032,12 @@ public abstract class SMSDispatcher extends Handler {
* @param ss service state
* @return The result error based on input service state for not in service error
*/
+ @SmsManager.Result
protected static int getNotInServiceError(int ss) {
if (ss == ServiceState.STATE_POWER_OFF) {
- return RESULT_ERROR_RADIO_OFF;
+ return SmsManager.RESULT_ERROR_RADIO_OFF;
}
- return RESULT_ERROR_NO_SERVICE;
+ return SmsManager.RESULT_ERROR_NO_SERVICE;
}
/**
@@ -860,13 +1053,65 @@ public abstract class SMSDispatcher extends Handler {
* broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * <code>RESULT_ERROR_NO_SERVICE</code><br>.
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -897,7 +1142,8 @@ public abstract class SMSDispatcher extends Handler {
/**
* Send a text based SMS.
- * @param destAddr the address to send the message to
+ *
+ * @param destAddr the address to send the message to
* @param scAddr is the service center address or null to use
* the current default SMSC
* @param text the body of the message to send
@@ -905,13 +1151,65 @@ public abstract class SMSDispatcher extends Handler {
* broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * <code>RESULT_ERROR_NO_SERVICE</code><br>.
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -920,7 +1218,7 @@ public abstract class SMSDispatcher extends Handler {
* @param messageUri optional URI of the message if it is already stored in the system
* @param callingPkg the calling package name
* @param persistMessage whether to save the sent message into SMS DB for a
- * non-default SMS app.
+ * non-default SMS app.
*
* @param priority Priority level of the message
* Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
@@ -969,7 +1267,7 @@ public abstract class SMSDispatcher extends Handler {
private void triggerSentIntentForFailure(PendingIntent sentIntent) {
if (sentIntent != null) {
try {
- sentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
+ sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
} catch (CanceledException ex) {
Rlog.e(TAG, "Intent has been canceled!");
}
@@ -989,7 +1287,7 @@ public abstract class SMSDispatcher extends Handler {
private boolean sendSmsByCarrierApp(boolean isDataSms, SmsTracker tracker ) {
String carrierPackage = getCarrierAppPackageName();
if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package.");
+ Rlog.d(TAG, "Found carrier package " + carrierPackage);
SmsSender smsSender;
if (isDataSms) {
smsSender = new DataSmsSender(tracker);
@@ -1018,33 +1316,89 @@ public abstract class SMSDispatcher extends Handler {
* @param use7bitOnly ignore (but still count) illegal characters if true
* @return TextEncodingDetails
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected abstract TextEncodingDetails calculateLength(CharSequence messageBody,
boolean use7bitOnly);
/**
* Send a multi-part text based SMS.
- * @param destAddr the address to send the message to
+ *
+ * @param destAddr the address to send the message to
* @param scAddr is the service center address or null to use
- * the current default SMSC
+ * the current default SMSC
* @param parts an <code>ArrayList</code> of strings that, in order,
- * comprise the original message
+ * comprise the original message
* @param sentIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been sent.
- * The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>
- * <code>RESULT_ERROR_NO_SERVICE</code>.
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been delivered
- * to the recipient. The raw pdu of the status report is in the
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
* @param messageUri optional URI of the message if it is already stored in the system
* @param callingPkg the calling package name
* @param persistMessage whether to save the sent message into SMS DB for a
@@ -1144,7 +1498,7 @@ public abstract class SMSDispatcher extends Handler {
String carrierPackage = getCarrierAppPackageName();
if (carrierPackage != null) {
- Rlog.d(TAG, "Found carrier package."
+ Rlog.d(TAG, "Found carrier package " + carrierPackage
+ " id: " + getMultiTrackermessageId(trackers));
MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers);
smsSender.sendSmsByCarrierApp(carrierPackage,
@@ -1231,6 +1585,7 @@ public abstract class SMSDispatcher extends Handler {
/**
* Send a single or a multi-part SMS
+ *
* @param trackers each tracker will contain:
* -smsc the SMSC to send the message through, or NULL for the
* default SMSC
@@ -1239,10 +1594,65 @@ public abstract class SMSDispatcher extends Handler {
* broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>
- * <code>RESULT_ERROR_NO_SERVICE</code>.
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -1253,21 +1663,21 @@ public abstract class SMSDispatcher extends Handler {
*/
@VisibleForTesting
public void sendRawPdu(SmsTracker[] trackers) {
- int error = RESULT_ERROR_NONE;
+ @SmsManager.Result int error = SmsManager.RESULT_ERROR_NONE;
PackageInfo appInfo = null;
if (mSmsSendDisabled) {
Rlog.e(TAG, "Device does not support sending sms.");
- error = RESULT_ERROR_NO_SERVICE;
+ error = SmsManager.RESULT_ERROR_NO_SERVICE;
} else {
for (SmsTracker tracker : trackers) {
if (tracker.getData().get(MAP_KEY_PDU) == null) {
Rlog.e(TAG, "Empty PDU");
- error = RESULT_ERROR_NULL_PDU;
+ error = SmsManager.RESULT_ERROR_NULL_PDU;
break;
}
}
- if (error == RESULT_ERROR_NONE) {
+ if (error == SmsManager.RESULT_ERROR_NONE) {
UserHandle userHandle = UserHandle.of(trackers[0].mUserId);
PackageManager pm = mContext.createContextAsUser(userHandle, 0).getPackageManager();
@@ -1280,12 +1690,12 @@ public abstract class SMSDispatcher extends Handler {
} catch (PackageManager.NameNotFoundException e) {
Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS"
+ " id: " + getMultiTrackermessageId(trackers));
- error = RESULT_ERROR_GENERIC_FAILURE;
+ error = SmsManager.RESULT_ERROR_GENERIC_FAILURE;
}
}
}
- if (error != RESULT_ERROR_NONE) {
+ if (error != SmsManager.RESULT_ERROR_NONE) {
handleSmsTrackersFailure(trackers, error, NO_ERROR_CODE);
return;
}
@@ -1307,7 +1717,7 @@ public abstract class SMSDispatcher extends Handler {
}
}
- if (PhoneNumberUtils.isLocalEmergencyNumber(mContext, trackers[0].mDestAddress)) {
+ if (mTelephonyManager.isEmergencyNumber(trackers[0].mDestAddress)) {
new AsyncEmergencyContactNotifier(mContext).execute();
}
}
@@ -1424,7 +1834,8 @@ public abstract class SMSDispatcher extends Handler {
// Deny sending message when the queue limit is reached.
Rlog.e(TAG, "Denied because queue limit reached"
+ " id: " + getMultiTrackermessageId(trackers));
- handleSmsTrackersFailure(trackers, RESULT_ERROR_LIMIT_EXCEEDED, NO_ERROR_CODE);
+ handleSmsTrackersFailure(
+ trackers, SmsManager.RESULT_ERROR_LIMIT_EXCEEDED, NO_ERROR_CODE);
return true;
}
mPendingTrackerCount++;
@@ -1440,7 +1851,7 @@ public abstract class SMSDispatcher extends Handler {
PackageManager pm = mContext.getPackageManager();
try {
ApplicationInfo appInfo = pm.getApplicationInfoAsUser(appPackage, 0,
- UserHandle.getUserHandleForUid(userId));
+ UserHandle.of(userId));
return appInfo.loadSafeLabel(pm);
} catch (PackageManager.NameNotFoundException e) {
Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
@@ -1485,7 +1896,7 @@ public abstract class SMSDispatcher extends Handler {
* @param isPremium true if the destination is known to be a premium short code
* @param trackers the SmsTracker array for the current message.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void handleConfirmShortCode(boolean isPremium, SmsTracker[] trackers) {
if (denyIfQueueLimitReached(trackers)) {
return; // queue limit reached; error was returned to caller
@@ -1565,14 +1976,26 @@ public abstract class SMSDispatcher extends Handler {
mSmsDispatchersController.sendRetrySms(tracker);
} else {
Rlog.e(TAG, mSmsDispatchersController + " is null. Retry failed"
- + " id: " + tracker.mMessageId);
+ + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
}
}
- private void handleSmsTrackersFailure(SmsTracker[] trackers, int error, int errorCode) {
+ private void handleSmsTrackersFailure(SmsTracker[] trackers, @SmsManager.Result int error,
+ int errorCode) {
for (SmsTracker tracker : trackers) {
tracker.onFailed(mContext, error, errorCode);
}
+ if (trackers.length > 0) {
+ // This error occurs before the SMS is sent. Make an assumption if it would have
+ // been sent over IMS or not.
+ mPhone.getSmsStats().onOutgoingSms(
+ isIms(),
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ error,
+ trackers[0].mMessageId,
+ trackers[0].isFromDefaultSmsApplication(mContext));
+ }
}
/**
@@ -1603,16 +2026,16 @@ public abstract class SMSDispatcher extends Handler {
@UnsupportedAppUsage
public final PendingIntent mDeliveryIntent;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final PackageInfo mAppInfo;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final String mDestAddress;
public final SmsHeader mSmsHeader;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mTimestamp = System.currentTimeMillis();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Uri mMessageUri; // Uri of persisted message if we wrote one
// Reference to states of a multipart message that this part belongs to
@@ -1627,7 +2050,7 @@ public abstract class SMSDispatcher extends Handler {
// If this is a text message (instead of data message)
private boolean mIsText;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mPersistMessage;
// User who sends the SMS.
@@ -1637,6 +2060,12 @@ public abstract class SMSDispatcher extends Handler {
public final long mMessageId;
+ private Boolean mIsFromDefaultSmsApplication;
+
+ // SMS anomaly uuid -- unexpected error from RIL
+ private final UUID mAnomalyUnexpectedErrorFromRilUUID =
+ UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886");
+
private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
@@ -1681,10 +2110,20 @@ public abstract class SMSDispatcher extends Handler {
return mAppInfo != null ? mAppInfo.packageName : null;
}
+ /** Return if the SMS was originated from the default SMS application. */
+ public boolean isFromDefaultSmsApplication(Context context) {
+ if (mIsFromDefaultSmsApplication == null) {
+ // Perform a lazy initialization, due to the cost of the operation.
+ mIsFromDefaultSmsApplication =
+ SmsApplication.isDefaultSmsApplication(context, getAppPackageName());
+ }
+ return mIsFromDefaultSmsApplication;
+ }
+
/**
* Update the status of this message if we persisted it
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void updateSentMessageStatus(Context context, int status) {
if (mMessageUri != null) {
// If we wrote this message in writeSentMessage, update it now
@@ -1731,8 +2170,7 @@ public abstract class SMSDispatcher extends Handler {
* @return The telephony provider URI if stored
*/
private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
- if (!mIsText || !mPersistMessage ||
- !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) {
+ if (!mIsText || !mPersistMessage || isFromDefaultSmsApplication(context)) {
return null;
}
Rlog.d(TAG, "Persist SMS into "
@@ -1799,7 +2237,7 @@ public abstract class SMSDispatcher extends Handler {
* @param error The error to send back with
* @param errorCode
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void onFailed(Context context, int error, int errorCode) {
if (mAnyPartFailed != null) {
mAnyPartFailed.set(true);
@@ -1835,9 +2273,36 @@ public abstract class SMSDispatcher extends Handler {
mSentIntent.send(context, error, fillIn);
} catch (CanceledException ex) {
Rlog.e(TAG, "Failed to send result"
- + " id: " + mMessageId);
+ + " " + SmsController.formatCrossStackMessageId(mMessageId));
}
}
+ reportAnomaly(error, errorCode);
+ }
+
+ private void reportAnomaly(int error, int errorCode) {
+ switch (error) {
+ // Exclude known failed reason
+ case SmsManager.RESULT_ERROR_NO_SERVICE:
+ case SmsManager.RESULT_ERROR_RADIO_OFF:
+ case SmsManager.RESULT_ERROR_LIMIT_EXCEEDED:
+ case SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED:
+ case SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED:
+ case SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY:
+ break;
+ // Dump bugreport for analysis
+ default:
+ String message = "SMS failed";
+ Rlog.d(TAG, message + " with error " + error + ", errorCode " + errorCode);
+ AnomalyReporter.reportAnomaly(generateUUID(error, errorCode), message);
+ }
+ }
+
+ private UUID generateUUID(int error, int errorCode) {
+ long lerror = error;
+ long lerrorCode = errorCode;
+ return new UUID(mAnomalyUnexpectedErrorFromRilUUID.getMostSignificantBits(),
+ mAnomalyUnexpectedErrorFromRilUUID.getLeastSignificantBits()
+ + ((lerrorCode << 32) + lerror));
}
/**
@@ -1845,7 +2310,7 @@ public abstract class SMSDispatcher extends Handler {
*
* @param context The Context
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void onSent(Context context) {
// is single part or last part of multipart message
boolean isSinglePartOrLastPart = true;
@@ -1956,12 +2421,12 @@ public abstract class SMSDispatcher extends Handler {
CompoundButton.OnCheckedChangeListener {
private final SmsTracker[] mTrackers;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Button mPositiveButton;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Button mNegativeButton;
private boolean mRememberChoice; // default is unchecked
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final TextView mRememberUndoInstruction;
private int mConfirmationType; // 0 - Short Code Msg Sending; 1 - Rate Limit Exceeded
private static final int SHORT_CODE_MSG = 0; // Short Code Msg
@@ -2055,12 +2520,12 @@ public abstract class SMSDispatcher extends Handler {
if (mSmsDispatchersController != null) {
return mSmsDispatchersController.isIms();
} else {
- Rlog.e(TAG, "mSmsDispatchersController is null");
+ Rlog.e(TAG, "mSmsDispatchersController is null");
return false;
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String getMultipartMessageText(ArrayList<String> parts) {
final StringBuilder sb = new StringBuilder();
for (String part : parts) {
@@ -2071,7 +2536,7 @@ public abstract class SMSDispatcher extends Handler {
return sb.toString();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected String getCarrierAppPackageName() {
UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
if (card == null) {
@@ -2083,18 +2548,18 @@ public abstract class SMSDispatcher extends Handler {
if (carrierPackages != null && carrierPackages.size() == 1) {
return carrierPackages.get(0);
}
- // If there is no carrier package which implements CarrierMessagingService, then lookup if
- // for a carrierImsPackage that implements CarrierMessagingService.
- return CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
+ // If there is no carrier package which implements CarrierMessagingService, then lookup
+ // an ImsService implementing RCS that also implements CarrierMessagingService.
+ return CarrierSmsUtils.getImsRcsPackageForIntent(mContext, mPhone,
new Intent(CarrierMessagingService.SERVICE_INTERFACE));
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected int getSubId() {
return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.getPhoneId());
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void checkCallerIsPhoneOrCarrierApp() {
int uid = Binder.getCallingUid();
int appId = UserHandle.getAppId(uid);
@@ -2133,4 +2598,18 @@ public abstract class SMSDispatcher extends Handler {
Binder.restoreCallingIdentity(token);
}
}
+
+ /**
+ * Dump local logs
+ */
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println(TAG);
+ pw.increaseIndent();
+ pw.println("mLocalLog:");
+ pw.increaseIndent();
+ mLocalLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index b61f41f407..e0b2e25f8a 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -25,10 +25,8 @@ import static com.android.internal.telephony.uicc.IccRecords.CARRIER_NAME_DISPLA
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
-import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -42,12 +40,15 @@ import android.hardware.radio.V1_0.CellInfoType;
import android.net.NetworkCapabilities;
import android.os.AsyncResult;
import android.os.BaseBundle;
+import android.os.Build;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.TimestampedValue;
@@ -64,6 +65,7 @@ import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityNr;
import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
@@ -72,15 +74,18 @@ import android.telephony.CellSignalStrengthNr;
import android.telephony.DataSpecificRegistrationInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhysicalChannelConfig;
+import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.SignalStrength;
+import android.telephony.SignalStrengthUpdateRequest;
import android.telephony.SignalThresholdInfo;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.telephony.VoiceSpecificRegistrationInfo;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.LocalLog;
@@ -88,6 +93,7 @@ import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriInfo;
@@ -97,6 +103,7 @@ import com.android.internal.telephony.cdnr.CarrierDisplayNameResolver;
import com.android.internal.telephony.dataconnection.DataConnection;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -122,11 +129,12 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -145,21 +153,25 @@ public class ServiceStateTracker extends Handler {
private static final long SIGNAL_STRENGTH_REFRESH_THRESHOLD_IN_MS =
TimeUnit.SECONDS.toMillis(10);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CommandsInterface mCi;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private UiccController mUiccController = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private UiccCardApplication mUiccApplcation = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IccRecords mIccRecords = null;
private boolean mVoiceCapable;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ServiceState mSS;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ServiceState mNewSS;
+ // A placeholder service state which will always be out of service. This is broadcast to
+ // listeners when the subscription ID for a phone becomes invalid so that they get a final
+ // state update.
+ private final ServiceState mOutOfServiceSS;
// This is the minimum interval at which CellInfo requests will be serviced by the modem.
// Any requests that arrive within MinInterval of the previous reuqest will simply receive the
@@ -180,6 +192,8 @@ public class ServiceStateTracker extends Handler {
private List<CellInfo> mLastCellInfoList = null;
private List<PhysicalChannelConfig> mLastPhysicalChannelConfigList = null;
+ private final Set<Integer> mRadioPowerOffReasons = new HashSet();
+
@UnsupportedAppUsage
private SignalStrength mSignalStrength;
private long mSignalStrengthUpdatedTime;
@@ -224,6 +238,9 @@ public class ServiceStateTracker extends Handler {
private RegistrantList mImsCapabilityChangedRegistrants = new RegistrantList();
private RegistrantList mNrStateChangedRegistrants = new RegistrantList();
private RegistrantList mNrFrequencyChangedRegistrants = new RegistrantList();
+ private RegistrantList mCssIndicatorChangedRegistrants = new RegistrantList();
+ private final RegistrantList mAirplaneModeChangedRegistrants = new RegistrantList();
+ private final RegistrantList mAreaCodeChangedRegistrants = new RegistrantList();
/* Radio power off pending flag and tag counter */
private boolean mPendingRadioPowerOffAfterDataOff = false;
@@ -232,6 +249,12 @@ public class ServiceStateTracker extends Handler {
/** Signal strength poll rate. */
private static final int POLL_PERIOD_MILLIS = 20 * 1000;
+ /**
+ * The time we wait for IMS to deregister before executing a pending radio power off request.
+ */
+ @VisibleForTesting
+ public static final int DELAY_RADIO_OFF_FOR_IMS_DEREG_TIMEOUT = 3 * 1000;
+
/** Waiting period before recheck gprs and voice registration. */
public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
@@ -251,9 +274,9 @@ public class ServiceStateTracker extends Handler {
protected static final int EVENT_SIM_RECORDS_LOADED = 16;
protected static final int EVENT_SIM_READY = 17;
protected static final int EVENT_LOCATION_UPDATES_ENABLED = 18;
- protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19;
- protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20;
- protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21;
+ protected static final int EVENT_GET_ALLOWED_NETWORK_TYPES = 19;
+ protected static final int EVENT_SET_ALLOWED_NETWORK_TYPES = 20;
+ protected static final int EVENT_RESET_ALLOWED_NETWORK_TYPES = 21;
protected static final int EVENT_CHECK_REPORT_GPRS = 22;
protected static final int EVENT_RESTRICTED_STATE_CHANGED = 23;
@@ -271,6 +294,7 @@ public class ServiceStateTracker extends Handler {
public static final int EVENT_ICC_CHANGED = 42;
protected static final int EVENT_GET_CELL_INFO_LIST = 43;
protected static final int EVENT_UNSOL_CELL_INFO_LIST = 44;
+ // Only sent if the IMS state is moving from true -> false
protected static final int EVENT_CHANGE_IMS_STATE = 45;
protected static final int EVENT_IMS_STATE_CHANGED = 46;
protected static final int EVENT_IMS_STATE_DONE = 47;
@@ -284,6 +308,11 @@ public class ServiceStateTracker extends Handler {
protected static final int EVENT_CELL_LOCATION_RESPONSE = 56;
protected static final int EVENT_CARRIER_CONFIG_CHANGED = 57;
private static final int EVENT_POLL_STATE_REQUEST = 58;
+ private static final int EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST = 59;
+ private static final int EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST = 60;
+ private static final int EVENT_ON_DEVICE_IDLE_STATE_CHANGED = 61;
+ // Timeout event used when delaying radio power off to wait for IMS deregistration to happen.
+ private static final int EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT = 62;
/**
* The current service state.
@@ -318,39 +347,35 @@ public class ServiceStateTracker extends Handler {
private CarrierDisplayNameResolver mCdnr;
private boolean mImsRegistrationOnOff = false;
- private boolean mAlarmSwitch = false;
/** Radio is disabled by carrier. Radio power will not be override if this field is set */
private boolean mRadioDisabledByCarrier = false;
- private PendingIntent mRadioOffIntent = null;
- private static final String ACTION_RADIO_OFF = "android.intent.action.ACTION_RADIO_OFF";
- private boolean mPowerOffDelayNeed = true;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mDeviceShuttingDown = false;
/** Keep track of SPN display rules, so we only broadcast intent if something changes. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mSpnUpdatePending = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mCurSpn = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mCurDataSpn = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mCurPlmn = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mCurShowPlmn = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mCurShowSpn = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private int mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mImsRegistered = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private SubscriptionManager mSubscriptionManager;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private SubscriptionController mSubscriptionController;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final SstSubscriptionsChangedListener mOnSubscriptionsChangedListener =
new SstSubscriptionsChangedListener();
@@ -369,8 +394,6 @@ public class ServiceStateTracker extends Handler {
private Pattern mOperatorNameStringPattern;
private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
- public final AtomicInteger mPreviousSubId =
- new AtomicInteger(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
/**
* Callback invoked when there is any change to any SubscriptionInfo. Typically
@@ -379,81 +402,103 @@ public class ServiceStateTracker extends Handler {
@Override
public void onSubscriptionsChanged() {
if (DBG) log("SubscriptionListener.onSubscriptionInfoChanged");
- // Set the network type, in case the radio does not restore it.
- int subId = mPhone.getSubId();
- ServiceStateTracker.this.mPrevSubId = mPreviousSubId.get();
- if (mPreviousSubId.getAndSet(subId) != subId) {
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- Context context = mPhone.getContext();
-
- mPhone.notifyPhoneStateChanged();
- mPhone.notifyCallForwardingIndicator();
- if (!SubscriptionManager.isValidSubscriptionId(
- ServiceStateTracker.this.mPrevSubId)) {
- // just went from invalid to valid subId, so notify with current service
- // state in case our service stat was never broadcasted (we don't notify
- // service states when the subId is invalid)
- mPhone.notifyServiceStateChanged(mSS);
- }
- boolean restoreSelection = !context.getResources().getBoolean(
- com.android.internal.R.bool.skip_restoring_network_selection);
- mPhone.sendSubscriptionSettings(restoreSelection);
+ final int curSubId = mPhone.getSubId();
+
+ // If the sub info changed, but the subId is the same, then we're done.
+ if (mSubId == curSubId) return;
+
+ // If not, then the subId has changed, so we need to remember the old subId,
+ // even if the new subId is invalid (likely).
+ mPrevSubId = mSubId;
+ mSubId = curSubId;
+
+ // Update voicemail count and notify message waiting changed regardless of
+ // whether the new subId is valid. This is an exception to the general logic
+ // of only updating things if the new subscription is valid. The result is that
+ // VoiceMail counts (and UI indicators) are cleared when the SIM is removed,
+ // which seems desirable.
+ mPhone.updateVoiceMail();
+
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ if (SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
+ // just went from valid to invalid subId, so notify phone state listeners
+ // with final broadcast
+ mPhone.notifyServiceStateChangedForSubId(mOutOfServiceSS,
+ ServiceStateTracker.this.mPrevSubId);
+ }
+ // If the new subscription ID isn't valid, then we don't need to do all the
+ // UI updating, so we're done.
+ return;
+ }
- setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology());
+ Context context = mPhone.getContext();
- if (mSpnUpdatePending) {
- mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(), mCurShowPlmn,
- mCurPlmn, mCurShowSpn, mCurSpn);
- mSpnUpdatePending = false;
- }
+ mPhone.notifyPhoneStateChanged();
- // Remove old network selection sharedPreferences since SP key names are now
- // changed to include subId. This will be done only once when upgrading from an
- // older build that did not include subId in the names.
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
- context);
- String oldNetworkSelection = sp.getString(
- Phone.NETWORK_SELECTION_KEY, "");
- String oldNetworkSelectionName = sp.getString(
- Phone.NETWORK_SELECTION_NAME_KEY, "");
- String oldNetworkSelectionShort = sp.getString(
- Phone.NETWORK_SELECTION_SHORT_KEY, "");
- if (!TextUtils.isEmpty(oldNetworkSelection) ||
- !TextUtils.isEmpty(oldNetworkSelectionName) ||
- !TextUtils.isEmpty(oldNetworkSelectionShort)) {
- SharedPreferences.Editor editor = sp.edit();
- editor.putString(Phone.NETWORK_SELECTION_KEY + subId,
- oldNetworkSelection);
- editor.putString(Phone.NETWORK_SELECTION_NAME_KEY + subId,
- oldNetworkSelectionName);
- editor.putString(Phone.NETWORK_SELECTION_SHORT_KEY + subId,
- oldNetworkSelectionShort);
- editor.remove(Phone.NETWORK_SELECTION_KEY);
- editor.remove(Phone.NETWORK_SELECTION_NAME_KEY);
- editor.remove(Phone.NETWORK_SELECTION_SHORT_KEY);
- editor.commit();
- }
+ if (!SubscriptionManager.isValidSubscriptionId(mPrevSubId)) {
+ // just went from invalid to valid subId, so notify with current service
+ // state in case our service state was never broadcasted (we don't notify
+ // service states when the subId is invalid)
+ mPhone.notifyServiceStateChanged(mSS);
+ }
- // Once sub id becomes valid, we need to update the service provider name
- // displayed on the UI again. The old SPN update intents sent to
- // MobileSignalController earlier were actually ignored due to invalid sub id.
- updateSpnDisplay();
- }
- // update voicemail count and notify message waiting changed
- mPhone.updateVoiceMail();
+ boolean restoreSelection = !context.getResources().getBoolean(
+ com.android.internal.R.bool.skip_restoring_network_selection);
+ mPhone.sendSubscriptionSettings(restoreSelection);
+
+ setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology());
+
+ if (mSpnUpdatePending) {
+ mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(), mCurShowPlmn,
+ mCurPlmn, mCurShowSpn, mCurSpn);
+ mSpnUpdatePending = false;
}
+
+ // Remove old network selection sharedPreferences since SP key names are now
+ // changed to include subId. This will be done only once when upgrading from an
+ // older build that did not include subId in the names.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ context);
+ String oldNetworkSelection = sp.getString(
+ Phone.NETWORK_SELECTION_KEY, "");
+ String oldNetworkSelectionName = sp.getString(
+ Phone.NETWORK_SELECTION_NAME_KEY, "");
+ String oldNetworkSelectionShort = sp.getString(
+ Phone.NETWORK_SELECTION_SHORT_KEY, "");
+ if (!TextUtils.isEmpty(oldNetworkSelection)
+ || !TextUtils.isEmpty(oldNetworkSelectionName)
+ || !TextUtils.isEmpty(oldNetworkSelectionShort)) {
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(Phone.NETWORK_SELECTION_KEY + mSubId,
+ oldNetworkSelection);
+ editor.putString(Phone.NETWORK_SELECTION_NAME_KEY + mSubId,
+ oldNetworkSelectionName);
+ editor.putString(Phone.NETWORK_SELECTION_SHORT_KEY + mSubId,
+ oldNetworkSelectionShort);
+ editor.remove(Phone.NETWORK_SELECTION_KEY);
+ editor.remove(Phone.NETWORK_SELECTION_NAME_KEY);
+ editor.remove(Phone.NETWORK_SELECTION_SHORT_KEY);
+ editor.commit();
+ }
+
+ // Once sub id becomes valid, we need to update the service provider name
+ // displayed on the UI again. The old SPN update intents sent to
+ // MobileSignalController earlier were actually ignored due to invalid sub id.
+ updateSpnDisplay();
}
};
//Common
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final GsmCdmaPhone mPhone;
private CellIdentity mCellIdentity;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
private final NitzStateMachine mNitzState;
+ private ServiceStateStats mServiceStateStats;
+
/**
* Holds the last NITZ signal received. Used only for trying to determine an MCC from a CDMA
* SID.
@@ -462,19 +507,19 @@ public class ServiceStateTracker extends Handler {
private NitzData mLastNitzData;
private final EriManager mEriManager;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ContentResolver mCr;
//GSM
- @UnsupportedAppUsage
- private int mPreferredNetworkType;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ private int mAllowedNetworkTypes;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mMaxDataCalls = 1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mNewMaxDataCalls = 1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mReasonDataDenied = -1;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mNewReasonDataDenied = -1;
/**
@@ -497,13 +542,15 @@ public class ServiceStateTracker extends Handler {
/**
* Mark when service state is in emergency call only mode
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mEmergencyOnly = false;
+ private boolean mCSEmergencyOnly = false;
+ private boolean mPSEmergencyOnly = false;
/** Started the recheck process after finding gprs should registered but not. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mStartedGprsRegCheck;
/** Already sent the event-log for no gprs register. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mReportedGprsNoReg;
private CarrierServiceStateTracker mCSST;
@@ -529,7 +576,9 @@ public class ServiceStateTracker extends Handler {
/** To identify whether EVENT_SIM_READY is received or not */
private boolean mIsSimReady = false;
- @UnsupportedAppUsage
+ private String mLastKnownNetworkCountry = "";
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -542,18 +591,15 @@ public class ServiceStateTracker extends Handler {
return;
}
- // TODO: Remove this weird check left over from CDMA/GSM service state tracker merge.
- if (!mPhone.isPhoneTypeGsm()) {
- loge("Ignoring intent " + intent + " received on CDMA phone");
- return;
- }
-
if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
- // update emergency string whenever locale changed
- updateSpnDisplay();
- } else if (intent.getAction().equals(ACTION_RADIO_OFF)) {
- mAlarmSwitch = false;
- powerOffRadioSafely();
+ // Update emergency string or operator name, polling service state.
+ pollState();
+ } else if (intent.getAction().equals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) {
+ String lastKnownNetworkCountry = intent.getStringExtra(
+ TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY);
+ if (!mLastKnownNetworkCountry.equals(lastKnownNetworkCountry)) {
+ updateSpnDisplay();
+ }
}
}
};
@@ -564,10 +610,10 @@ public class ServiceStateTracker extends Handler {
public static final String UNACTIVATED_MIN_VALUE = "1111110111";
// Current Otasp value
private int mCurrentOtaspMode = TelephonyManager.OTASP_UNINITIALIZED;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mRoamingIndicator;
private boolean mIsInPrl;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mDefaultRoamingIndicator;
/**
* Initially assume no data connection.
@@ -582,7 +628,7 @@ public class ServiceStateTracker extends Handler {
private boolean mIsMinInfoReady = false;
private boolean mIsEriTextLoaded = false;
private String mEriText;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mIsSubscriptionFromRuim = false;
private CdmaSubscriptionSourceManager mCdmaSSM;
public static final String INVALID_MCC = "000";
@@ -599,11 +645,22 @@ public class ServiceStateTracker extends Handler {
* Reference: 3GPP TS 36.104 5.4.3)
* inclusive ranges for which the lte rsrp boost is applied */
private ArrayList<Pair<Integer, Integer>> mEarfcnPairListForRsrpBoost = null;
-
private int mLteRsrpBoost = 0; // offset which is reduced from the rsrp threshold
// while calculating signal strength level.
- private final Object mLteRsrpBoostLock = new Object();
- private static final int INVALID_LTE_EARFCN = -1;
+
+ /* Ranges of NR ARFCNs (5G Absolute Radio Frequency Channel Number,
+ * Reference: 3GPP TS 38.104)
+ * inclusive ranges for which the corresponding nr rsrp boost is applied */
+ private ArrayList<Pair<Integer, Integer>> mNrarfcnRangeListForRsrpBoost = null;
+ private int[] mNrRsrpBoost;
+
+ private final Object mRsrpBoostLock = new Object();
+ private static final int INVALID_ARFCN = -1;
+
+ private final List<SignalRequestRecord> mSignalRequestRecords = new ArrayList<>();
+
+ /* Last known TAC/LAC */
+ private int mLastKnownAreaCode = CellInfo.UNAVAILABLE;
public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
mNitzState = TelephonyComponentFactory.getInstance()
@@ -612,6 +669,8 @@ public class ServiceStateTracker extends Handler {
mPhone = phone;
mCi = ci;
+ mServiceStateStats = new ServiceStateStats(mPhone);
+
mCdnr = new CarrierDisplayNameResolver(mPhone);
mEriManager = TelephonyComponentFactory.getInstance().inject(EriManager.class.getName())
@@ -623,6 +682,9 @@ public class ServiceStateTracker extends Handler {
.isVoiceCapable();
mUiccController = UiccController.getInstance();
+ mOutOfServiceSS = new ServiceState();
+ mOutOfServiceSS.setStateOutOfService();
+
mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
mCi.registerForCellInfoList(this, EVENT_UNSOL_CELL_INFO_LIST, null);
@@ -630,8 +692,8 @@ public class ServiceStateTracker extends Handler {
mSubscriptionController = SubscriptionController.getInstance();
mSubscriptionManager = SubscriptionManager.from(phone.getContext());
- mSubscriptionManager
- .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+ mSubscriptionManager.addOnSubscriptionsChangedListener(
+ new android.os.HandlerExecutor(this), mOnSubscriptionsChangedListener);
mRestrictedState = new RestrictedState();
mTransportManager = mPhone.getTransportManager();
@@ -656,6 +718,9 @@ public class ServiceStateTracker extends Handler {
int enableCellularOnBoot = Settings.Global.getInt(mCr,
Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1);
mDesiredPowerState = (enableCellularOnBoot > 0) && ! (airplaneMode > 0);
+ if (!mDesiredPowerState) {
+ mRadioPowerOffReasons.add(Phone.RADIO_POWER_REASON_USER);
+ }
mRadioPowerLog.log("init : airplane mode = " + airplaneMode + " enableCellularOnBoot = " +
enableCellularOnBoot);
@@ -668,12 +733,8 @@ public class ServiceStateTracker extends Handler {
Context context = mPhone.getContext();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- context.registerReceiver(mIntentReceiver, filter);
- filter = new IntentFilter();
- filter.addAction(ACTION_RADIO_OFF);
- context.registerReceiver(mIntentReceiver, filter);
- filter = new IntentFilter();
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
context.registerReceiver(mIntentReceiver, filter);
mPhone.notifyOtaspChanged(TelephonyManager.OTASP_UNINITIALIZED);
@@ -812,13 +873,14 @@ public class ServiceStateTracker extends Handler {
mCi.unregisterForImsNetworkStateChanged(this);
mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
CARRIER_ACTION_SET_RADIO_ENABLED);
+ mPhone.getContext().unregisterReceiver(mIntentReceiver);
if (mCSST != null) {
mCSST.dispose();
mCSST = null;
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean getDesiredPowerState() {
return mDesiredPowerState;
}
@@ -829,7 +891,7 @@ public class ServiceStateTracker extends Handler {
}
private SignalStrength mLastSignalStrength = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected boolean notifySignalStrength() {
boolean notified = false;
if (!mSignalStrength.equals(mLastSignalStrength)) {
@@ -898,7 +960,7 @@ public class ServiceStateTracker extends Handler {
* Some operators have been known to report registration failure
* data only devices, to fix that use DataRegState.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void useDataRegStateForDataOnlyDevices() {
if (mVoiceCapable == false) {
if (DBG) {
@@ -910,7 +972,7 @@ public class ServiceStateTracker extends Handler {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void updatePhoneObject() {
if (mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_switch_phone_on_voice_reg_state_change)) {
@@ -1019,10 +1081,25 @@ public class ServiceStateTracker extends Handler {
* an AsyncResult, and onComplete.obj.exception will be non-null
* on failure.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void reRegisterNetwork(Message onComplete) {
- mCi.getPreferredNetworkType(
- obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
+ mCi.getAllowedNetworkTypesBitmap(
+ obtainMessage(EVENT_GET_ALLOWED_NETWORK_TYPES, onComplete));
+ }
+
+ /**
+ * @return the current reasons for which the radio is off.
+ */
+ public Set<Integer> getRadioPowerOffReasons() {
+ return mRadioPowerOffReasons;
+ }
+
+ /**
+ * Clear all the radio off reasons. This should be done when turning radio off for genuine or
+ * test emergency calls.
+ */
+ public void clearAllRadioOffReasons() {
+ mRadioPowerOffReasons.clear();
}
/**
@@ -1039,11 +1116,42 @@ public class ServiceStateTracker extends Handler {
*/
public void setRadioPower(boolean power, boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
- log("setRadioPower forEmergencyCall " + forEmergencyCall + " forceApply " + forceApply);
+ setRadioPowerForReason(power, forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply,
+ Phone.RADIO_POWER_REASON_USER);
+ }
+
+ /**
+ * Turn on or off radio power with option to specify whether it's for emergency call and specify
+ * a reason for setting the power state.
+ * More details check {@link PhoneInternalInterface#setRadioPower(
+ * boolean, boolean, boolean, boolean, int)}.
+ */
+ public void setRadioPowerForReason(boolean power, boolean forEmergencyCall,
+ boolean isSelectedPhoneForEmergencyCall, boolean forceApply, int reason) {
+ log("setRadioPower power " + power + " forEmergencyCall " + forEmergencyCall
+ + " forceApply " + forceApply + " reason " + reason);
+
+ if (power) {
+ if (forEmergencyCall) {
+ clearAllRadioOffReasons();
+ } else {
+ mRadioPowerOffReasons.remove(reason);
+ }
+ } else {
+ mRadioPowerOffReasons.add(reason);
+ }
if (power == mDesiredPowerState && !forceApply) {
log("setRadioPower mDesiredPowerState is already " + power + " Do nothing.");
return;
}
+ if (power && !mRadioPowerOffReasons.isEmpty()) {
+ log("setRadioPowerForReason " + "power: " + power + " forEmergencyCall= "
+ + forEmergencyCall + " isSelectedPhoneForEmergencyCall: "
+ + isSelectedPhoneForEmergencyCall + " forceApply " + forceApply + "reason:"
+ + reason + " will not power on the radio as it is powered off for the "
+ + "following reasons: " + mRadioPowerOffReasons + ".");
+ return;
+ }
mDesiredPowerState = power;
setPowerStateToDesired(forEmergencyCall, isSelectedPhoneForEmergencyCall, forceApply);
@@ -1079,47 +1187,33 @@ public class ServiceStateTracker extends Handler {
private boolean mWantContinuousLocationUpdates;
private boolean mWantSingleLocationUpdate;
- public void enableSingleLocationUpdate() {
+ /**
+ * Request a single update of the device's current registered cell.
+ */
+ public void enableSingleLocationUpdate(WorkSource workSource) {
if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
mWantSingleLocationUpdate = true;
- mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
+ mCi.setLocationUpdates(true, workSource, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
}
public void enableLocationUpdates() {
if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
mWantContinuousLocationUpdates = true;
- mCi.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
+ mCi.setLocationUpdates(true, null, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
}
protected void disableSingleLocationUpdate() {
mWantSingleLocationUpdate = false;
if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
- mCi.setLocationUpdates(false, null);
+ mCi.setLocationUpdates(false, null, null);
}
}
public void disableLocationUpdates() {
mWantContinuousLocationUpdates = false;
if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
- mCi.setLocationUpdates(false, null);
- }
- }
-
- private int getLteEarfcn(CellIdentity cellIdentity) {
- int lteEarfcn = INVALID_LTE_EARFCN;
- if (cellIdentity != null) {
- switch (cellIdentity.getType()) {
- case CellInfoType.LTE: {
- lteEarfcn = ((CellIdentityLte) cellIdentity).getEarfcn();
- break;
- }
- default: {
- break;
- }
- }
+ mCi.setLocationUpdates(false, null, null);
}
-
- return lteEarfcn;
}
@Override
@@ -1160,8 +1254,8 @@ public class ServiceStateTracker extends Handler {
mCdnr.updateEfFromUsim(null /* Usim */);
}
onUpdateIccAvailability();
- if (mUiccApplcation != null
- && mUiccApplcation.getState() != AppState.APPSTATE_READY) {
+ if (mUiccApplcation == null
+ || mUiccApplcation.getState() != AppState.APPSTATE_READY) {
mIsSimReady = false;
updateSpnDisplay();
}
@@ -1230,8 +1324,10 @@ public class ServiceStateTracker extends Handler {
case EVENT_IMS_STATE_DONE:
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- int[] responseArray = (int[])ar.result;
- mImsRegistered = (responseArray[0] == 1) ? true : false;
+ final int[] responseArray = (int[]) ar.result;
+ final boolean imsRegistered = responseArray[0] == 1;
+ mPhone.setImsRegistrationState(imsRegistered);
+ mImsRegistered = imsRegistered;
}
break;
@@ -1248,10 +1344,8 @@ public class ServiceStateTracker extends Handler {
// GSM
case EVENT_SIM_READY:
- // Reset the mPreviousSubId so we treat a SIM power bounce
+ // Reset the mPrevSubId so we treat a SIM power bounce
// as a first boot. See b/19194287
- mOnSubscriptionsChangedListener.mPreviousSubId.set(
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mIsSimReady = true;
pollStateInternal(false);
@@ -1379,14 +1473,14 @@ public class ServiceStateTracker extends Handler {
}
break;
- case EVENT_SET_PREFERRED_NETWORK_TYPE:
+ case EVENT_SET_ALLOWED_NETWORK_TYPES:
ar = (AsyncResult) msg.obj;
// Don't care the result, only use for dereg network (COPS=2)
- message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj);
- mCi.setPreferredNetworkType(mPreferredNetworkType, message);
+ message = obtainMessage(EVENT_RESET_ALLOWED_NETWORK_TYPES, ar.userObj);
+ mCi.setAllowedNetworkTypesBitmap(mAllowedNetworkTypes, message);
break;
- case EVENT_RESET_PREFERRED_NETWORK_TYPE:
+ case EVENT_RESET_ALLOWED_NETWORK_TYPES:
ar = (AsyncResult) msg.obj;
if (ar.userObj != null) {
AsyncResult.forMessage(((Message) ar.userObj)).exception
@@ -1395,19 +1489,21 @@ public class ServiceStateTracker extends Handler {
}
break;
- case EVENT_GET_PREFERRED_NETWORK_TYPE:
+ case EVENT_GET_ALLOWED_NETWORK_TYPES:
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- mPreferredNetworkType = ((int[])ar.result)[0];
+ mAllowedNetworkTypes = ((int[]) ar.result)[0];
} else {
- mPreferredNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
+ mAllowedNetworkTypes = RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.NETWORK_MODE_GLOBAL);
}
- message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj);
- int toggledNetworkType = RILConstants.NETWORK_MODE_GLOBAL;
+ message = obtainMessage(EVENT_SET_ALLOWED_NETWORK_TYPES, ar.userObj);
+ int toggledNetworkType = RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.NETWORK_MODE_GLOBAL);
- mCi.setPreferredNetworkType(toggledNetworkType, message);
+ mCi.setAllowedNetworkTypesBitmap(toggledNetworkType, message);
break;
case EVENT_CHECK_REPORT_GPRS:
@@ -1444,6 +1540,7 @@ public class ServiceStateTracker extends Handler {
if (mPendingRadioPowerOffAfterDataOff) {
if (DBG) log("EVENT_ALL_DATA_DISCONNECTED, turn radio off now.");
hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOffTag += 1;
mPendingRadioPowerOffAfterDataOff = false;
} else {
log("EVENT_ALL_DATA_DISCONNECTED is stale");
@@ -1616,7 +1713,6 @@ public class ServiceStateTracker extends Handler {
log("EVENT_PHYSICAL_CHANNEL_CONFIG: size=" + list.size() + " list="
+ list);
}
- mPhone.notifyPhysicalChannelConfiguration(list);
mLastPhysicalChannelConfigList = list;
boolean hasChanged = false;
if (updateNrStateFromPhysicalChannelConfigs(list, mSS)) {
@@ -1630,12 +1726,14 @@ public class ServiceStateTracker extends Handler {
hasChanged |= RatRatcheter
.updateBandwidths(getBandwidthsFromConfigs(list), mSS);
+ mPhone.notifyPhysicalChannelConfig(list);
// Notify NR frequency, NR connection status or bandwidths changed.
if (hasChanged) {
mPhone.notifyServiceStateChanged(mSS);
TelephonyMetrics.getInstance().writeServiceStateChanged(
mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
+ mServiceStateStats.onServiceStateChanged(mSS);
}
}
break;
@@ -1663,6 +1761,81 @@ public class ServiceStateTracker extends Handler {
pollStateInternal(false);
break;
+ case EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST: {
+ Pair<SignalRequestRecord, Message> pair =
+ (Pair<SignalRequestRecord, Message>) msg.obj;
+ SignalRequestRecord record = pair.first;
+ Message onCompleted = pair.second;
+ AsyncResult ret = AsyncResult.forMessage(onCompleted);
+
+ // TODO(b/177956310): Check subId to filter out old request until a better solution
+ boolean dupRequest = mSignalRequestRecords.stream().anyMatch(
+ srr -> srr.mCallingUid == record.mCallingUid
+ && srr.mSubId == record.mSubId);
+ if (dupRequest) {
+ ret.exception = new IllegalStateException(
+ "setSignalStrengthUpdateRequest called again with same subId");
+ onCompleted.sendToTarget();
+ break;
+ }
+
+ try {
+ record.mRequest.getLiveToken().linkToDeath(record, 0);
+ } catch (RemoteException | NullPointerException ex) {
+ ret.exception = new IllegalStateException(
+ "Signal request client is already dead.");
+ onCompleted.sendToTarget();
+ break;
+ }
+
+ mSignalRequestRecords.add(record);
+ updateAlwaysReportSignalStrength();
+ updateReportingCriteria(getCarrierConfig());
+
+ onCompleted.sendToTarget();
+
+ // Always poll signal strength after setting the update request which has waken up
+ // modem if it was idle. An additional signal strength polling is almost cost free.
+ obtainMessage(EVENT_POLL_SIGNAL_STRENGTH).sendToTarget();
+ break;
+ }
+
+ case EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST: {
+ Pair<SignalRequestRecord, Message> pair =
+ (Pair<SignalRequestRecord, Message>) msg.obj;
+ SignalRequestRecord record = pair.first;
+ Message onCompleted = pair.second;
+
+ // for loop with removal may cause ConcurrentModificationException
+ Iterator<SignalRequestRecord> it = mSignalRequestRecords.iterator();
+ while (it.hasNext()) {
+ SignalRequestRecord srr = it.next();
+ if (srr.mRequest.getLiveToken().equals(record.mRequest.getLiveToken())) {
+ it.remove();
+ }
+ }
+
+ updateAlwaysReportSignalStrength();
+ updateReportingCriteria(getCarrierConfig());
+
+ if (onCompleted != null) {
+ AsyncResult ret = AsyncResult.forMessage(onCompleted);
+ onCompleted.sendToTarget();
+ }
+ break;
+ }
+
+ case EVENT_ON_DEVICE_IDLE_STATE_CHANGED: {
+ updateReportingCriteria(getCarrierConfig());
+ break;
+ }
+
+ case EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT: {
+ if (DBG) log("EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT triggered");
+ powerOffRadioSafely();
+ break;
+ }
+
default:
log("Unhandled message with number: " + msg.what);
break;
@@ -1684,9 +1857,9 @@ public class ServiceStateTracker extends Handler {
return simAbsent;
}
- private int[] getBandwidthsFromConfigs(List<PhysicalChannelConfig> list) {
+ private static int[] getBandwidthsFromConfigs(List<PhysicalChannelConfig> list) {
return list.stream()
- .map(PhysicalChannelConfig::getCellBandwidthDownlink)
+ .map(PhysicalChannelConfig::getCellBandwidthDownlinkKhz)
.mapToInt(Integer::intValue)
.toArray();
}
@@ -1824,7 +1997,7 @@ public class ServiceStateTracker extends Handler {
if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void updateOtaspState() {
int otaspMode = getOtasp();
int oldOtaspMode = mCurrentOtaspMode;
@@ -1842,6 +2015,7 @@ public class ServiceStateTracker extends Handler {
public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
mLastNitzData = null;
mNitzState.handleAirplaneModeChanged(isAirplaneModeOn);
+ mAirplaneModeChangedRegistrants.notifyResult(isAirplaneModeOn);
}
protected Phone getPhone() {
@@ -2066,7 +2240,7 @@ public class ServiceStateTracker extends Handler {
}
private boolean isNrPhysicalChannelConfig(PhysicalChannelConfig config) {
- return config.getRat() == TelephonyManager.NETWORK_TYPE_NR;
+ return config.getNetworkType() == TelephonyManager.NETWORK_TYPE_NR;
}
/**
@@ -2117,15 +2291,16 @@ public class ServiceStateTracker extends Handler {
int cssIndicator = voiceSpecificStates.cssSupported ? 1 : 0;
int newVoiceRat = ServiceState.networkTypeToRilRadioTechnology(
networkRegState.getAccessNetworkTechnology());
-
mNewSS.setVoiceRegState(regCodeToServiceState(registrationState));
mNewSS.setCssIndicator(cssIndicator);
mNewSS.addNetworkRegistrationInfo(networkRegState);
+
setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
//Denial reason if registrationState = 3
int reasonForDenial = networkRegState.getRejectCause();
- mEmergencyOnly = networkRegState.isEmergencyEnabled();
+ mCSEmergencyOnly = networkRegState.isEmergencyEnabled();
+ mEmergencyOnly = (mCSEmergencyOnly || mPSEmergencyOnly);
if (mPhone.isPhoneTypeGsm()) {
mGsmVoiceRoaming = regCodeIsRoaming(registrationState);
@@ -2198,7 +2373,6 @@ public class ServiceStateTracker extends Handler {
int serviceState = regCodeToServiceState(registrationState);
int newDataRat = ServiceState.networkTypeToRilRadioTechnology(
networkRegState.getAccessNetworkTechnology());
- boolean nrHasChanged = false;
if (DBG) {
log("handlePollStateResultMessage: PS cellular. " + networkRegState);
@@ -2210,20 +2384,10 @@ public class ServiceStateTracker extends Handler {
if (serviceState == ServiceState.STATE_OUT_OF_SERVICE) {
mLastPhysicalChannelConfigList = null;
}
- nrHasChanged |= updateNrFrequencyRangeFromPhysicalChannelConfigs(
- mLastPhysicalChannelConfigList, mNewSS);
- nrHasChanged |= updateNrStateFromPhysicalChannelConfigs(
- mLastPhysicalChannelConfigList, mNewSS);
- setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
-
- if (nrHasChanged) {
- TelephonyMetrics.getInstance().writeServiceStateChanged(
- mPhone.getPhoneId(), mSS);
- mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
- }
+ mPSEmergencyOnly = networkRegState.isEmergencyEnabled();
+ mEmergencyOnly = (mCSEmergencyOnly || mPSEmergencyOnly);
if (mPhone.isPhoneTypeGsm()) {
-
mNewReasonDataDenied = networkRegState.getRejectCause();
mNewMaxDataCalls = dataSpecificStates.maxDataCalls;
mGsmDataRoaming = regCodeIsRoaming(registrationState);
@@ -2262,8 +2426,7 @@ public class ServiceStateTracker extends Handler {
mNewSS.setDataRoamingFromRegistration(isDataRoaming);
}
- updateServiceStateLteEarfcnBoost(mNewSS,
- getLteEarfcn(networkRegState.getCellIdentity()));
+ updateServiceStateArfcnRsrpBoost(mNewSS, networkRegState.getCellIdentity());
break;
}
@@ -2357,27 +2520,66 @@ public class ServiceStateTracker extends Handler {
}
}
+ private static boolean isValidNrBandwidthKhz(int bandwidth) {
+ // Valid bandwidths, see 3gpp 38.101 sec 5.3
+ switch (bandwidth) {
+ case 5000:
+ case 10000:
+ case 15000:
+ case 20000:
+ case 25000:
+ case 30000:
+ case 40000:
+ case 50000:
+ case 60000:
+ case 70000:
+ case 80000:
+ case 90000:
+ case 100000:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/**
* Extract the CID/CI for GSM/UTRA/EUTRA
*
* @returns the cell ID (unique within a PLMN for a given tech) or -1 if invalid
*/
- private static int getCidFromCellIdentity(CellIdentity id) {
+ private static long getCidFromCellIdentity(CellIdentity id) {
if (id == null) return -1;
- int cid = -1;
+ long cid = -1;
switch(id.getType()) {
case CellInfo.TYPE_GSM: cid = ((CellIdentityGsm) id).getCid(); break;
case CellInfo.TYPE_WCDMA: cid = ((CellIdentityWcdma) id).getCid(); break;
case CellInfo.TYPE_TDSCDMA: cid = ((CellIdentityTdscdma) id).getCid(); break;
case CellInfo.TYPE_LTE: cid = ((CellIdentityLte) id).getCi(); break;
+ case CellInfo.TYPE_NR: cid = ((CellIdentityNr) id).getNci(); break;
default: break;
}
// If the CID is unreported
- if (cid == Integer.MAX_VALUE) cid = -1;
+ if (cid == (id.getType() == CellInfo.TYPE_NR
+ ? CellInfo.UNAVAILABLE_LONG : CellInfo.UNAVAILABLE)) {
+ cid = -1;
+ }
return cid;
}
+ //TODO: Move this and getCidFromCellIdentity to CellIdentityUtils.
+ private static int getAreaCodeFromCellIdentity(CellIdentity id) {
+ if (id == null) return CellInfo.UNAVAILABLE;
+ switch(id.getType()) {
+ case CellInfo.TYPE_GSM: return ((CellIdentityGsm) id).getLac();
+ case CellInfo.TYPE_WCDMA: return ((CellIdentityWcdma) id).getLac();
+ case CellInfo.TYPE_TDSCDMA: return ((CellIdentityTdscdma) id).getLac();
+ case CellInfo.TYPE_LTE: return ((CellIdentityLte) id).getTac();
+ case CellInfo.TYPE_NR: return ((CellIdentityNr) id).getTac();
+ default: return CellInfo.UNAVAILABLE;
+ }
+ }
+
private void setPhyCellInfoFromCellIdentity(ServiceState ss, CellIdentity cellIdentity) {
if (cellIdentity == null) {
if (DBG) {
@@ -2390,13 +2592,14 @@ public class ServiceStateTracker extends Handler {
if (VDBG) {
log("Setting channel number: " + cellIdentity.getChannelNumber());
}
-
+ int[] bandwidths = null;
+ PhysicalChannelConfig primaryPcc = getPrimaryPhysicalChannelConfigForCell(
+ mLastPhysicalChannelConfigList, cellIdentity);
if (cellIdentity instanceof CellIdentityLte) {
- CellIdentityLte cl = (CellIdentityLte) cellIdentity;
- int[] bandwidths = null;
+ CellIdentityLte ci = (CellIdentityLte) cellIdentity;
// Prioritize the PhysicalChannelConfig list because we might already be in carrier
// aggregation by the time poll state is performed.
- if (!ArrayUtils.isEmpty(mLastPhysicalChannelConfigList)) {
+ if (primaryPcc != null) {
bandwidths = getBandwidthsFromConfigs(mLastPhysicalChannelConfigList);
for (int bw : bandwidths) {
if (!isValidLteBandwidthKhz(bw)) {
@@ -2405,6 +2608,8 @@ public class ServiceStateTracker extends Handler {
break;
}
}
+ } else {
+ if (VDBG) log("No primary LTE PhysicalChannelConfig");
}
// If we don't have a PhysicalChannelConfig[] list, then pull from CellIdentityLte.
// This is normal if we're in idle mode and the PhysicalChannelConfig[] has already
@@ -2417,7 +2622,7 @@ public class ServiceStateTracker extends Handler {
// channel active). In the normal case of single-carrier non-return-to-idle, the
// values *must* be the same, so it doesn't matter which is chosen.
if (bandwidths == null || bandwidths.length == 1) {
- final int cbw = cl.getBandwidth();
+ final int cbw = ci.getBandwidth();
if (isValidLteBandwidthKhz(cbw)) {
bandwidths = new int[] {cbw};
} else if (cbw == Integer.MAX_VALUE) {
@@ -2427,12 +2632,62 @@ public class ServiceStateTracker extends Handler {
loge("Invalid LTE Bandwidth in RegistrationState, " + cbw);
}
}
- if (bandwidths != null) {
- ss.setCellBandwidths(bandwidths);
+ } else if (cellIdentity instanceof CellIdentityNr) {
+ // Prioritize the PhysicalChannelConfig list because we might already be in carrier
+ // aggregation by the time poll state is performed.
+ if (primaryPcc != null) {
+ bandwidths = getBandwidthsFromConfigs(mLastPhysicalChannelConfigList);
+ for (int bw : bandwidths) {
+ if (!isValidNrBandwidthKhz(bw)) {
+ loge("Invalid NR Bandwidth in RegistrationState, " + bw);
+ bandwidths = null;
+ break;
+ }
+ }
+ } else {
+ if (VDBG) log("No primary NR PhysicalChannelConfig");
}
+ // TODO: update bandwidths from CellIdentityNr if the field is added
} else {
- if (VDBG) log("Skipping bandwidth update for Non-LTE cell.");
+ if (VDBG) log("Skipping bandwidth update for Non-LTE and Non-NR cell.");
+ }
+
+ if (bandwidths == null && primaryPcc != null && primaryPcc.getCellBandwidthDownlinkKhz()
+ != PhysicalChannelConfig.CELL_BANDWIDTH_UNKNOWN) {
+ bandwidths = new int[] {primaryPcc.getCellBandwidthDownlinkKhz()};
+ } else if (VDBG) {
+ log("Skipping bandwidth update because no primary PhysicalChannelConfig exists.");
+ }
+
+ if (bandwidths != null) {
+ ss.setCellBandwidths(bandwidths);
+ }
+ }
+
+ private static PhysicalChannelConfig getPrimaryPhysicalChannelConfigForCell(
+ List<PhysicalChannelConfig> pccs, CellIdentity cellIdentity) {
+ if (ArrayUtils.isEmpty(pccs) || !(cellIdentity instanceof CellIdentityLte
+ || cellIdentity instanceof CellIdentityNr)) {
+ return null;
+ }
+
+ int networkType, pci;
+ if (cellIdentity instanceof CellIdentityLte) {
+ networkType = TelephonyManager.NETWORK_TYPE_LTE;
+ pci = ((CellIdentityLte) cellIdentity).getPci();
+ } else {
+ networkType = TelephonyManager.NETWORK_TYPE_NR;
+ pci = ((CellIdentityNr) cellIdentity).getPci();
+ }
+
+ for (PhysicalChannelConfig pcc : pccs) {
+ if (pcc.getConnectionStatus() == PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING
+ && pcc.getNetworkType() == networkType && pcc.getPhysicalCellId() == pci) {
+ return pcc;
+ }
}
+
+ return null;
}
/**
@@ -2472,7 +2727,7 @@ public class ServiceStateTracker extends Handler {
* Query the carrier configuration to determine if there any network overrides
* for roaming or not roaming for the current service state.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void updateRoamingState() {
PersistableBundle bundle = getCarrierConfig();
@@ -2625,7 +2880,7 @@ public class ServiceStateTracker extends Handler {
log("updateSpnDisplayCdnr-");
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public void updateSpnDisplay() {
PersistableBundle config = getCarrierConfig();
@@ -2697,6 +2952,36 @@ public class ServiceStateTracker extends Handler {
wfcFlightSpnFormat = wfcSpnFormats[flightModeIdx];
}
+ String crossSimSpnFormat = null;
+ if (mPhone.getImsPhone() != null
+ && (mPhone.getImsPhone() != null)
+ && (mPhone.getImsPhone().getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) {
+ // In Cros SIM Calling mode show SPN or PLMN + Cross SIM Calling
+ //
+ // 1) Show SPN + Cross SIM Calling If SIM has SPN and SPN display condition
+ // is satisfied or SPN override is enabled for this carrier
+ //
+ // 2) Show PLMN + Cross SIM Calling if there is no valid SPN in case 1
+ PersistableBundle bundle = getCarrierConfig();
+ int crossSimSpnFormatIdx =
+ bundle.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT);
+ boolean useRootLocale =
+ bundle.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
+
+ String[] crossSimSpnFormats = SubscriptionManager.getResourcesForSubId(
+ mPhone.getContext(),
+ mPhone.getSubId(), useRootLocale)
+ .getStringArray(R.array.crossSimSpnFormats);
+
+ if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) {
+ loge("updateSpnDisplay: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: "
+ + crossSimSpnFormatIdx);
+ crossSimSpnFormatIdx = 0;
+ }
+ crossSimSpnFormat = crossSimSpnFormats[crossSimSpnFormatIdx];
+ }
+
if (mPhone.isPhoneTypeGsm()) {
// The values of plmn/showPlmn change in different scenarios.
// 1) No service but emergency call allowed -> expected
@@ -2713,7 +2998,7 @@ public class ServiceStateTracker extends Handler {
// EXTRA_PLMN = plmn
// 4) No service due to power off, aka airplane mode
- // EXTRA_SHOW_PLMN = false
+ // EXTRA_SHOW_PLMN = true
// EXTRA_PLMN = null
IccRecords iccRecords = mIccRecords;
@@ -2747,11 +3032,9 @@ public class ServiceStateTracker extends Handler {
== CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN);
if (DBG) log("updateSpnDisplay: rawPlmn = " + plmn);
} else {
- // Power off state, such as airplane mode, show plmn as "No service"
+ // Power off state, such as airplane mode, show plmn as null
showPlmn = true;
- plmn = Resources.getSystem()
- .getText(com.android.internal.R.string.lockscreen_carrier_default)
- .toString();
+ plmn = null;
if (DBG) log("updateSpnDisplay: radio is off w/ showPlmn="
+ showPlmn + " plmn=" + plmn);
}
@@ -2766,9 +3049,27 @@ public class ServiceStateTracker extends Handler {
&& ((rule & CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN)
== CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN);
if (DBG) log("updateSpnDisplay: rawSpn = " + spn);
-
- if (!TextUtils.isEmpty(spn) && !TextUtils.isEmpty(wfcVoiceSpnFormat) &&
- !TextUtils.isEmpty(wfcDataSpnFormat)) {
+ if (!TextUtils.isEmpty(crossSimSpnFormat)) {
+ if (!TextUtils.isEmpty(spn)) {
+ // Show SPN + Cross-SIM Calling If SIM has SPN and SPN display condition
+ // is satisfied or SPN override is enabled for this carrier.
+ String originalSpn = spn.trim();
+ spn = String.format(crossSimSpnFormat, originalSpn);
+ dataSpn = spn;
+ showSpn = true;
+ showPlmn = false;
+ } else if (!TextUtils.isEmpty(plmn)) {
+ // Show PLMN + Cross-SIM Calling if there is no valid SPN in the above case
+ String originalPlmn = plmn.trim();
+ PersistableBundle config = getCarrierConfig();
+ if (mIccRecords != null && config.getBoolean(
+ CarrierConfigManager.KEY_WFC_CARRIER_NAME_OVERRIDE_BY_PNN_BOOL)) {
+ originalPlmn = mIccRecords.getPnnHomeName();
+ }
+ plmn = String.format(crossSimSpnFormat, originalPlmn);
+ }
+ } else if (!TextUtils.isEmpty(spn) && !TextUtils.isEmpty(wfcVoiceSpnFormat)
+ && !TextUtils.isEmpty(wfcDataSpnFormat)) {
// Show SPN + Wi-Fi Calling If SIM has SPN and SPN display condition
// is satisfied or SPN override is enabled for this carrier.
@@ -2854,9 +3155,9 @@ public class ServiceStateTracker extends Handler {
if (ArrayUtils.isEmpty(countriesWithNoService)) {
return false;
}
- String currentCountry = mLocaleTracker.getCurrentCountry();
+ mLastKnownNetworkCountry = mLocaleTracker.getLastKnownCountryIso();
for (String country : countriesWithNoService) {
- if (country.equalsIgnoreCase(currentCountry)) {
+ if (country.equalsIgnoreCase(mLastKnownNetworkCountry)) {
return true;
}
}
@@ -2870,24 +3171,16 @@ public class ServiceStateTracker extends Handler {
protected void setPowerStateToDesired(boolean forEmergencyCall,
boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
if (DBG) {
- String tmpLog = "mDeviceShuttingDown=" + mDeviceShuttingDown +
+ String tmpLog = "setPowerStateToDesired: mDeviceShuttingDown=" + mDeviceShuttingDown +
", mDesiredPowerState=" + mDesiredPowerState +
", getRadioState=" + mCi.getRadioState() +
- ", mPowerOffDelayNeed=" + mPowerOffDelayNeed +
- ", mAlarmSwitch=" + mAlarmSwitch +
- ", mRadioDisabledByCarrier=" + mRadioDisabledByCarrier;
+ ", mRadioDisabledByCarrier=" + mRadioDisabledByCarrier +
+ ", IMS reg state=" + mImsRegistrationOnOff +
+ ", pending radio off=" + hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT);
log(tmpLog);
mRadioPowerLog.log(tmpLog);
}
- if (mPhone.isPhoneTypeGsm() && mAlarmSwitch) {
- if(DBG) log("mAlarmSwitch == true");
- Context context = mPhone.getContext();
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- am.cancel(mRadioOffIntent);
- mAlarmSwitch = false;
- }
-
// If we want it on and it's off, turn it on
if (mDesiredPowerState && !mRadioDisabledByCarrier
&& (forceApply || mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF)) {
@@ -2895,30 +3188,50 @@ public class ServiceStateTracker extends Handler {
} else if ((!mDesiredPowerState || mRadioDisabledByCarrier) && mCi.getRadioState()
== TelephonyManager.RADIO_POWER_ON) {
// If it's on and available and we want it off gracefully
- if (mPhone.isPhoneTypeGsm() && mPowerOffDelayNeed) {
- if (mImsRegistrationOnOff && !mAlarmSwitch) {
- if(DBG) log("mImsRegistrationOnOff == true");
- Context context = mPhone.getContext();
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-
- Intent intent = new Intent(ACTION_RADIO_OFF);
- mRadioOffIntent = PendingIntent.getBroadcast(
- context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
-
- mAlarmSwitch = true;
- if (DBG) log("Alarm setting");
- am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + 3000, mRadioOffIntent);
- } else {
- powerOffRadioSafely();
- }
+ if (mImsRegistrationOnOff) {
+ if (DBG) log("setPowerStateToDesired: delaying power off until IMS dereg.");
+ startDelayRadioOffWaitingForImsDeregTimeout();
+ // Return early here as we do not want to hit the cancel timeout code below.
+ return;
} else {
+ if (DBG) log("setPowerStateToDesired: powering off");
powerOffRadioSafely();
}
} else if (mDeviceShuttingDown
&& (mCi.getRadioState() != TelephonyManager.RADIO_POWER_UNAVAILABLE)) {
+ // !mDesiredPowerState condition above will happen first if the radio is on, so we will
+ // see the following: (delay for IMS dereg) -> RADIO_POWER_OFF ->
+ // RADIO_POWER_UNAVAILABLE
mCi.requestShutdown(null);
}
+ // Cancel any pending timeouts because the state has been re-evaluated.
+ cancelDelayRadioOffWaitingForImsDeregTimeout();
+ }
+
+ /**
+ * Cancel the EVENT_POWER_OFF_RADIO_DELAYED event if it is currently pending to be completed.
+ * @return true if there was a pending timeout message in the queue, false otherwise.
+ */
+ private void cancelDelayRadioOffWaitingForImsDeregTimeout() {
+ if (hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT)) {
+ if (DBG) log("cancelDelayRadioOffWaitingForImsDeregTimeout: cancelling.");
+ removeMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT);
+ }
+ }
+
+ /**
+ * Start a timer to turn off the radio if IMS does not move to deregistered after the
+ * radio power off event occurred. If this event already exists in the message queue, then
+ * ignore the new request and use the existing one.
+ */
+ private void startDelayRadioOffWaitingForImsDeregTimeout() {
+ if (hasMessages(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT)) {
+ if (DBG) log("startDelayRadioOffWaitingForImsDeregTimeout: timer exists, ignoring");
+ return;
+ }
+ if (DBG) log("startDelayRadioOffWaitingForImsDeregTimeout: starting timer");
+ sendEmptyMessageDelayed(EVENT_POWER_OFF_RADIO_IMS_DEREG_TIMEOUT,
+ DELAY_RADIO_OFF_FOR_IMS_DEREG_TIMEOUT);
}
protected void onUpdateIccAvailability() {
@@ -2981,12 +3294,12 @@ public class ServiceStateTracker extends Handler {
mRatLog.log(mSS.toString());
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final void log(String s) {
Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + s);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final void loge(String s) {
Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + s);
}
@@ -2995,7 +3308,7 @@ public class ServiceStateTracker extends Handler {
* @return The current GPRS state. IN_SERVICE is the same as "attached"
* and OUT_OF_SERVICE is the same as detached.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getCurrentDataConnectionState() {
return mSS.getDataRegistrationState();
}
@@ -3004,7 +3317,7 @@ public class ServiceStateTracker extends Handler {
* @return true if phone is camping on a technology (eg UMTS)
* that could support voice and data simultaneously.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isConcurrentVoiceAndDataAllowed() {
if (mSS.getCssIndicator() == 1) {
// Checking the Concurrent Service Supported flag first for all phone types.
@@ -3032,21 +3345,21 @@ public class ServiceStateTracker extends Handler {
sendMessage(obtainMessage(EVENT_IMS_SERVICE_STATE_CHANGED));
}
- public void setImsRegistrationState(boolean registered) {
- log("ImsRegistrationState - registered : " + registered);
-
- if (mImsRegistrationOnOff && !registered) {
- if (mAlarmSwitch) {
- mImsRegistrationOnOff = registered;
+ /**
+ * Sets the Ims registration state. If the 3 second shut down timer has begun and the state
+ * is set to unregistered, the timer is cancelled and the radio is shutdown immediately.
+ *
+ * @param registered whether ims is registered
+ */
+ public void setImsRegistrationState(final boolean registered) {
+ log("setImsRegistrationState: {registered=" + registered
+ + " mImsRegistrationOnOff=" + mImsRegistrationOnOff
+ + "}");
- Context context = mPhone.getContext();
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- am.cancel(mRadioOffIntent);
- mAlarmSwitch = false;
- sendMessage(obtainMessage(EVENT_CHANGE_IMS_STATE));
- return;
- }
+ if (mImsRegistrationOnOff && !registered) {
+ // moving to deregistered, only send this event if we need to re-evaluate
+ sendMessage(obtainMessage(EVENT_CHANGE_IMS_STATE));
}
mImsRegistrationOnOff = registered;
}
@@ -3067,7 +3380,7 @@ public class ServiceStateTracker extends Handler {
* and start over again if the radio notifies us that some
* event has changed
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void pollState() {
sendEmptyMessage(EVENT_POLL_STATE_REQUEST);
}
@@ -3180,6 +3493,12 @@ public class ServiceStateTracker extends Handler {
mPhone.mTelephonyTester.overrideServiceState(mNewSS);
}
+ NetworkRegistrationInfo networkRegState = mNewSS.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ updateNrFrequencyRangeFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
+ updateNrStateFromPhysicalChannelConfigs(mLastPhysicalChannelConfigList, mNewSS);
+ setPhyCellInfoFromCellIdentity(mNewSS, networkRegState.getCellIdentity());
+
if (DBG) {
log("Poll ServiceState done: "
+ " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]"
@@ -3200,6 +3519,9 @@ public class ServiceStateTracker extends Handler {
boolean hasAirplaneModeOnChanged =
mSS.getState() != ServiceState.STATE_POWER_OFF
&& mNewSS.getState() == ServiceState.STATE_POWER_OFF;
+ boolean hasAirplaneModeOffChanged =
+ mSS.getState() == ServiceState.STATE_POWER_OFF
+ && mNewSS.getState() != ServiceState.STATE_POWER_OFF;
SparseBooleanArray hasDataAttached = new SparseBooleanArray(
mTransportManager.getAvailableTransports().length);
@@ -3212,8 +3534,9 @@ public class ServiceStateTracker extends Handler {
boolean anyDataRegChanged = false;
boolean anyDataRatChanged = false;
boolean hasAlphaRawChanged =
- mSS.getOperatorAlphaLongRaw() != mNewSS.getOperatorAlphaLongRaw()
- || mSS.getOperatorAlphaShortRaw() != mNewSS.getOperatorAlphaShortRaw();
+ !TextUtils.equals(mSS.getOperatorAlphaLongRaw(), mNewSS.getOperatorAlphaLongRaw())
+ || !TextUtils.equals(mSS.getOperatorAlphaShortRaw(),
+ mNewSS.getOperatorAlphaShortRaw());
for (int transport : mTransportManager.getAvailableTransports()) {
NetworkRegistrationInfo oldNrs = mSS.getNetworkRegistrationInfo(
@@ -3239,10 +3562,8 @@ public class ServiceStateTracker extends Handler {
int newRAT = newNrs != null ? newNrs.getAccessNetworkTechnology()
: TelephonyManager.NETWORK_TYPE_UNKNOWN;
- boolean isOldCA = oldNrs != null ? (oldNrs.getDataSpecificInfo() != null
- ? oldNrs.getDataSpecificInfo().isUsingCarrierAggregation() : false) : false;
- boolean isNewCA = newNrs != null ? (newNrs.getDataSpecificInfo() != null
- ? newNrs.getDataSpecificInfo().isUsingCarrierAggregation() : false) : false;
+ boolean isOldCA = oldNrs != null ? oldNrs.isUsingCarrierAggregation() : false;
+ boolean isNewCA = newNrs != null ? newNrs.isUsingCarrierAggregation() : false;
// If the carrier enable KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING and the operator name
// match this pattern, the data rat display LteAdvanced indicator.
@@ -3283,12 +3604,15 @@ public class ServiceStateTracker extends Handler {
boolean hasLocationChanged = mCellIdentity == null
? primaryCellIdentity != null : !mCellIdentity.isSameCell(primaryCellIdentity);
- // ratchet the new tech up through its rat family but don't drop back down
- // until cell change or device is OOS
- boolean isDataInService = mNewSS.getDataRegistrationState()
- == ServiceState.STATE_IN_SERVICE;
- if (isDataInService) {
- mRatRatcheter.ratchet(mSS, mNewSS, hasLocationChanged);
+ boolean isRegisteredOnWwan = false;
+ for (NetworkRegistrationInfo nri : mNewSS.getNetworkRegistrationInfoListForTransportType(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
+ isRegisteredOnWwan |= nri.isRegistered();
+ }
+
+ // Ratchet if the device is in service on the same cell
+ if (isRegisteredOnWwan && !hasLocationChanged) {
+ mRatRatcheter.ratchet(mSS, mNewSS);
}
boolean hasRilVoiceRadioTechnologyChanged =
@@ -3367,7 +3691,7 @@ public class ServiceStateTracker extends Handler {
// TODO: we may add filtering to reduce the event logged,
// i.e. check preferred network setting, only switch to 2G, etc
if (hasRilVoiceRadioTechnologyChanged) {
- int cid = getCidFromCellIdentity(primaryCellIdentity);
+ long cid = getCidFromCellIdentity(primaryCellIdentity);
// NOTE: this code was previously located after mSS and mNewSS are swapped, so
// existing logs were incorrectly using the new state for "network_from"
// and STATE_OUT_OF_SERVICE for "network_to". To avoid confusion, use a new log tag
@@ -3385,10 +3709,6 @@ public class ServiceStateTracker extends Handler {
}
}
- if (hasCssIndicatorChanged) {
- mPhone.notifyAllActiveDataConnections();
- }
-
mReasonDataDenied = mNewReasonDataDenied;
mMaxDataCalls = mNewMaxDataCalls;
mRejectCode = mNewRejectCode;
@@ -3405,6 +3725,12 @@ public class ServiceStateTracker extends Handler {
mCellIdentity = primaryCellIdentity;
+ int areaCode = getAreaCodeFromCellIdentity(mCellIdentity);
+ if (areaCode != mLastKnownAreaCode && areaCode != CellInfo.UNAVAILABLE) {
+ mLastKnownAreaCode = areaCode;
+ mAreaCodeChangedRegistrants.notifyRegistrants();
+ }
+
if (hasRilVoiceRadioTechnologyChanged) {
updatePhoneObject();
}
@@ -3420,12 +3746,14 @@ public class ServiceStateTracker extends Handler {
if (hasRegistered) {
mNetworkAttachedRegistrants.notifyRegistrants();
- mNitzState.handleNetworkAvailable();
}
if (hasDeregistered) {
mNetworkDetachedRegistrants.notifyRegistrants();
- mNitzState.handleNetworkUnavailable();
+ }
+
+ if (hasCssIndicatorChanged) {
+ mCssIndicatorChangedRegistrants.notifyRegistrants();
}
if (hasRejectCauseChanged) {
@@ -3456,13 +3784,15 @@ public class ServiceStateTracker extends Handler {
tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
- // If the OPERATOR command hasn't returned a valid operator, but if the device has
- // camped on a cell either to attempt registration or for emergency services, then
- // for purposes of setting the locale, we don't care if registration fails or is
+ // If the OPERATOR command hasn't returned a valid operator or the device is on IWLAN (
+ // because operatorNumeric would be SIM's mcc/mnc when device is on IWLAN), but if the
+ // device has camped on a cell either to attempt registration or for emergency services,
+ // then for purposes of setting the locale, we don't care if registration fails or is
// incomplete.
// CellIdentity can return a null MCC and MNC in CDMA
String localeOperator = operatorNumeric;
- if (isInvalidOperatorNumeric(operatorNumeric)) {
+ if (isInvalidOperatorNumeric(operatorNumeric)
+ || mSS.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN) {
for (CellIdentity cid : prioritizedCids) {
if (!TextUtils.isEmpty(cid.getPlmn())) {
localeOperator = cid.getPlmn();
@@ -3500,11 +3830,10 @@ public class ServiceStateTracker extends Handler {
mPhone.getContext().getContentResolver()
.insert(getUriForSubscriptionId(mPhone.getSubId()),
getContentValuesForServiceState(mSS));
- }
- if (hasChanged || hasNrStateChanged) {
TelephonyMetrics.getInstance().writeServiceStateChanged(mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
+ mServiceStateStats.onServiceStateChanged(mSS);
}
boolean shouldLogAttachedChange = false;
@@ -3538,7 +3867,6 @@ public class ServiceStateTracker extends Handler {
|| hasDataTransportPreferenceChanged) {
setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology());
notifyDataRegStateRilRadioTechnologyChanged(transport);
- mPhone.notifyAllActiveDataConnections();
}
if (hasDataAttached.get(transport)) {
@@ -3555,6 +3883,14 @@ public class ServiceStateTracker extends Handler {
}
}
+ // Before starting to poll network state, the signal strength will be
+ // reset under radio power off, so here expects to query it again
+ // because the signal strength might come earlier RAT and radio state
+ // changed.
+ if (hasAirplaneModeOffChanged) {
+ mCi.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
+ }
+
if (shouldLogAttachedChange) {
logAttachChange();
}
@@ -3775,7 +4111,7 @@ public class ServiceStateTracker extends Handler {
*
* @return true if provided sid/nid pair belongs to operator's home network.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isInHomeSidNid(int sid, int nid) {
// if SID/NID is not available, assume this is home network.
if (isSidsAllZeros()) return true;
@@ -3798,7 +4134,7 @@ public class ServiceStateTracker extends Handler {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void setOperatorIdd(String operatorNumeric) {
if (mPhone.getUnitTestMode()) {
return;
@@ -3816,13 +4152,13 @@ public class ServiceStateTracker extends Handler {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isInvalidOperatorNumeric(String operatorNumeric) {
return operatorNumeric == null || operatorNumeric.length() < 5 ||
operatorNumeric.startsWith(INVALID_MCC);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String fixUnknownMcc(String operatorNumeric, int sid) {
if (sid <= 0) {
// no cdma information is available, do nothing
@@ -3855,7 +4191,7 @@ public class ServiceStateTracker extends Handler {
* @param voiceRegState i.e. CREG in GSM
* @return false if device only register to voice but not gprs
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isGprsConsistent(int dataRegState, int voiceRegState) {
return !((voiceRegState == ServiceState.STATE_IN_SERVICE) &&
(dataRegState != ServiceState.STATE_IN_SERVICE));
@@ -4184,6 +4520,12 @@ public class ServiceStateTracker extends Handler {
notificationManager.cancel(Integer.toString(mPrevSubId), PS_NOTIFICATION);
notificationManager.cancel(Integer.toString(mPrevSubId), CS_NOTIFICATION);
notificationManager.cancel(Integer.toString(mPrevSubId), CS_REJECT_CAUSE_NOTIFICATION);
+
+ // Cancel Emergency call warning and network preference notifications
+ notificationManager.cancel(
+ CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG, mPrevSubId);
+ notificationManager.cancel(
+ CarrierServiceStateTracker.PREF_NETWORK_NOTIFICATION_TAG, mPrevSubId);
}
}
@@ -4559,6 +4901,27 @@ public class ServiceStateTracker extends Handler {
}
/**
+ * Registration for Airplane Mode changing. The state of Airplane Mode will be returned
+ * {@link AsyncResult#result} as a {@link Boolean} Object.
+ * The {@link AsyncResult} will be in the notification {@link Message#obj}.
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in {@link AsyncResult#userObj}
+ */
+ public void registerForAirplaneModeChanged(Handler h, int what, Object obj) {
+ mAirplaneModeChangedRegistrants.add(h, what, obj);
+ }
+
+ /**
+ * Unregister for Airplane Mode changed event.
+ *
+ * @param h The handler
+ */
+ public void unregisterForAirplaneModeChanged(Handler h) {
+ mAirplaneModeChangedRegistrants.remove(h);
+ }
+
+ /**
* Registration point for transition into network attached.
* @param h handler to notify
* @param what what code of message when delivered
@@ -4675,7 +5038,9 @@ public class ServiceStateTracker extends Handler {
Phone.REASON_RADIO_TURNED_OFF);
}
}
- if (DBG) log("Data disconnected, turn off radio right away.");
+ if (DBG) {
+ log("powerOffRadioSafely: Data disconnected, turn off radio now.");
+ }
hangupAndPowerOff();
} else {
// hang up all active voice calls first
@@ -4693,7 +5058,10 @@ public class ServiceStateTracker extends Handler {
if (dds != mPhone.getSubId()
&& !ProxyController.getInstance().areAllDataDisconnected(dds)) {
- if (DBG) log("Data is active on DDS. Wait for all data disconnect");
+ if (DBG) {
+ log(String.format("powerOffRadioSafely: Data is active on DDS (%d)."
+ + " Wait for all data disconnect", dds));
+ }
// Data is not disconnected on DDS. Wait for the data disconnect complete
// before sending the RADIO_POWER off.
ProxyController.getInstance().registerForAllDataDisconnected(dds, this,
@@ -4704,10 +5072,14 @@ public class ServiceStateTracker extends Handler {
msg.what = EVENT_SET_RADIO_POWER_OFF;
msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
if (sendMessageDelayed(msg, 30000)) {
- if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+ if (DBG) {
+ log("powerOffRadioSafely: Wait up to 30s for data to isconnect, then"
+ + " turn off radio.");
+ }
mPendingRadioPowerOffAfterDataOff = true;
} else {
- log("Cannot send delayed Msg, turn off radio right away.");
+ log("powerOffRadioSafely: Cannot send delayed Msg, turn off radio right"
+ + " away.");
hangupAndPowerOff();
mPendingRadioPowerOffAfterDataOff = false;
}
@@ -4725,8 +5097,8 @@ public class ServiceStateTracker extends Handler {
synchronized(this) {
if (mPendingRadioPowerOffAfterDataOff) {
if (DBG) log("Process pending request to turn radio off.");
- mPendingRadioPowerOffAfterDataOffTag += 1;
hangupAndPowerOff();
+ mPendingRadioPowerOffAfterDataOffTag += 1;
mPendingRadioPowerOffAfterDataOff = false;
return true;
}
@@ -4737,19 +5109,21 @@ public class ServiceStateTracker extends Handler {
/**
* Checks if the provided earfcn falls withing the range of earfcns.
*
- * return true if earfcn falls within the provided range; false otherwise.
+ * return int index in earfcnPairList if earfcn falls within the provided range; -1 otherwise.
*/
- private boolean containsEarfcnInEarfcnRange(ArrayList<Pair<Integer, Integer>> earfcnPairList,
+ private int containsEarfcnInEarfcnRange(ArrayList<Pair<Integer, Integer>> earfcnPairList,
int earfcn) {
+ int index = 0;
if (earfcnPairList != null) {
for (Pair<Integer, Integer> earfcnPair : earfcnPairList) {
if ((earfcn >= earfcnPair.first) && (earfcn <= earfcnPair.second)) {
- return true;
+ return index;
}
+ index++;
}
}
- return false;
+ return -1;
}
/**
@@ -4810,10 +5184,11 @@ public class ServiceStateTracker extends Handler {
mEriManager.loadEriFile();
mCdnr.updateEfForEri(getOperatorNameFromEri());
- updateLteEarfcnLists(config);
+ updateArfcnLists(config);
updateReportingCriteria(config);
updateOperatorNamePattern(config);
mCdnr.updateEfFromCarrierConfig(config);
+ mPhone.notifyCallForwardingIndicator();
// Sometimes the network registration information comes before carrier config is ready.
// For some cases like roaming/non-roaming overriding, we need carrier config. So it's
@@ -4821,66 +5196,108 @@ public class ServiceStateTracker extends Handler {
pollStateInternal(false);
}
- private void updateLteEarfcnLists(PersistableBundle config) {
- synchronized (mLteRsrpBoostLock) {
+ private void updateArfcnLists(PersistableBundle config) {
+ synchronized (mRsrpBoostLock) {
mLteRsrpBoost = config.getInt(CarrierConfigManager.KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
String[] earfcnsStringArrayForRsrpBoost = config.getStringArray(
CarrierConfigManager.KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY);
mEarfcnPairListForRsrpBoost = convertEarfcnStringArrayToPairList(
earfcnsStringArrayForRsrpBoost);
+
+ mNrRsrpBoost = config.getIntArray(
+ CarrierConfigManager.KEY_NRARFCNS_RSRP_BOOST_INT_ARRAY);
+ String[] nrarfcnsStringArrayForRsrpBoost = config.getStringArray(
+ CarrierConfigManager.KEY_BOOSTED_NRARFCNS_STRING_ARRAY);
+ mNrarfcnRangeListForRsrpBoost = convertEarfcnStringArrayToPairList(
+ nrarfcnsStringArrayForRsrpBoost);
+
+ if ((mNrRsrpBoost == null && mNrarfcnRangeListForRsrpBoost != null)
+ || (mNrRsrpBoost != null && mNrarfcnRangeListForRsrpBoost == null)
+ || (mNrRsrpBoost != null && mNrarfcnRangeListForRsrpBoost != null
+ && mNrRsrpBoost.length != mNrarfcnRangeListForRsrpBoost.size())) {
+ loge("Invalid parameters for NR RSRP boost");
+ mNrRsrpBoost = null;
+ mNrarfcnRangeListForRsrpBoost = null;
+ }
}
}
private void updateReportingCriteria(PersistableBundle config) {
int lteMeasurementEnabled = config.getInt(CarrierConfigManager
.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
config.getIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY),
AccessNetworkType.EUTRAN,
(lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRP) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSCP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
config.getIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY),
AccessNetworkType.UTRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
config.getIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY),
AccessNetworkType.GERAN, true);
if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
config.getIntArray(CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY),
AccessNetworkType.EUTRAN,
(lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRQ) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSNR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
config.getIntArray(CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY),
AccessNetworkType.EUTRAN,
(lteMeasurementEnabled & CellSignalStrengthLte.USE_RSSNR) != 0);
int measurementEnabled = config.getInt(CarrierConfigManager
.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, CellSignalStrengthNr.USE_SSRSRP);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRP,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY),
AccessNetworkType.NGRAN,
(measurementEnabled & CellSignalStrengthNr.USE_SSRSRP) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY),
AccessNetworkType.NGRAN,
(measurementEnabled & CellSignalStrengthNr.USE_SSRSRQ) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSSINR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY),
AccessNetworkType.NGRAN,
(measurementEnabled & CellSignalStrengthNr.USE_SSSINR) != 0);
}
}
- private void updateServiceStateLteEarfcnBoost(ServiceState serviceState, int lteEarfcn) {
- synchronized (mLteRsrpBoostLock) {
- if ((lteEarfcn != INVALID_LTE_EARFCN)
- && containsEarfcnInEarfcnRange(mEarfcnPairListForRsrpBoost, lteEarfcn)) {
- serviceState.setLteEarfcnRsrpBoost(mLteRsrpBoost);
- } else {
- serviceState.setLteEarfcnRsrpBoost(0);
+ private void updateServiceStateArfcnRsrpBoost(ServiceState serviceState,
+ CellIdentity cellIdentity) {
+ int rsrpBoost = 0;
+ int arfcn;
+
+ synchronized (mRsrpBoostLock) {
+ switch (cellIdentity.getType()) {
+ case CellInfo.TYPE_LTE:
+ arfcn = ((CellIdentityLte) cellIdentity).getEarfcn();
+ if (arfcn != INVALID_ARFCN
+ && containsEarfcnInEarfcnRange(mEarfcnPairListForRsrpBoost,
+ arfcn) != -1) {
+ rsrpBoost = mLteRsrpBoost;
+ }
+ break;
+ case CellInfo.TYPE_NR:
+ arfcn = ((CellIdentityNr) cellIdentity).getNrarfcn();
+ if (arfcn != INVALID_ARFCN) {
+ int index = containsEarfcnInEarfcnRange(mNrarfcnRangeListForRsrpBoost,
+ arfcn);
+ if (index != -1) {
+ rsrpBoost = mNrRsrpBoost[index];
+ }
+ }
+ break;
+ default:
+ break;
}
}
+ serviceState.setArfcnRsrpBoost(rsrpBoost);
}
/**
@@ -4893,8 +5310,10 @@ public class ServiceStateTracker extends Handler {
// This signal is used for both voice and data radio signal so parse
// all fields
-
- if ((ar.exception == null) && (ar.result != null)) {
+ // Under power off, let's suppress valid signal strength report, which is
+ // beneficial to avoid icon flickering.
+ if ((ar.exception == null) && (ar.result != null)
+ && mSS.getState() != ServiceState.STATE_POWER_OFF) {
mSignalStrength = (SignalStrength) ar.result;
PersistableBundle config = getCarrierConfig();
@@ -4964,7 +5383,7 @@ public class ServiceStateTracker extends Handler {
return iccCardExist;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getSystemProperty(String property, String defValue) {
return TelephonyManager.getTelephonyProperty(mPhone.getPhoneId(), property, defValue);
}
@@ -5127,11 +5546,12 @@ public class ServiceStateTracker extends Handler {
}
}
- private void dumpEarfcnPairList(PrintWriter pw) {
- pw.print(" mEarfcnPairListForRsrpBoost={");
- if (mEarfcnPairListForRsrpBoost != null) {
- int i = mEarfcnPairListForRsrpBoost.size();
- for (Pair<Integer, Integer> earfcnPair : mEarfcnPairListForRsrpBoost) {
+ private void dumpEarfcnPairList(PrintWriter pw, ArrayList<Pair<Integer, Integer>> pairList,
+ String name) {
+ pw.print(" " + name + "={");
+ if (pairList != null) {
+ int i = pairList.size();
+ for (Pair<Integer, Integer> earfcnPair : pairList) {
pw.print("(");
pw.print(earfcnPair.first);
pw.print(",");
@@ -5180,7 +5600,7 @@ public class ServiceStateTracker extends Handler {
pw.println(" mLastCellInfoReqTime=" + mLastCellInfoReqTime);
dumpCellInfoList(pw);
pw.flush();
- pw.println(" mPreferredNetworkType=" + mPreferredNetworkType);
+ pw.println(" mAllowedNetworkTypes=" + mAllowedNetworkTypes);
pw.println(" mMaxDataCalls=" + mMaxDataCalls);
pw.println(" mNewMaxDataCalls=" + mNewMaxDataCalls);
pw.println(" mReasonDataDenied=" + mReasonDataDenied);
@@ -5188,6 +5608,8 @@ public class ServiceStateTracker extends Handler {
pw.println(" mGsmVoiceRoaming=" + mGsmVoiceRoaming);
pw.println(" mGsmDataRoaming=" + mGsmDataRoaming);
pw.println(" mEmergencyOnly=" + mEmergencyOnly);
+ pw.println(" mCSEmergencyOnly=" + mCSEmergencyOnly);
+ pw.println(" mPSEmergencyOnly=" + mPSEmergencyOnly);
pw.flush();
mNitzState.dumpState(pw);
pw.println(" mLastNitzData=" + mLastNitzData);
@@ -5220,15 +5642,17 @@ public class ServiceStateTracker extends Handler {
pw.flush();
pw.println(" mImsRegistered=" + mImsRegistered);
pw.println(" mImsRegistrationOnOff=" + mImsRegistrationOnOff);
- pw.println(" mAlarmSwitch=" + mAlarmSwitch);
+ pw.println(" pending radio off event="
+ + hasMessages(DELAY_RADIO_OFF_FOR_IMS_DEREG_TIMEOUT));
pw.println(" mRadioDisabledByCarrier" + mRadioDisabledByCarrier);
- pw.println(" mPowerOffDelayNeed=" + mPowerOffDelayNeed);
pw.println(" mDeviceShuttingDown=" + mDeviceShuttingDown);
pw.println(" mSpnUpdatePending=" + mSpnUpdatePending);
pw.println(" mLteRsrpBoost=" + mLteRsrpBoost);
+ pw.println(" mNrRsrpBoost=" + Arrays.toString(mNrRsrpBoost));
pw.println(" mCellInfoMinIntervalMs=" + mCellInfoMinIntervalMs);
pw.println(" mEriManager=" + mEriManager);
- dumpEarfcnPairList(pw);
+ dumpEarfcnPairList(pw, mEarfcnPairListForRsrpBoost, "mEarfcnPairListForRsrpBoost");
+ dumpEarfcnPairList(pw, mNrarfcnRangeListForRsrpBoost, "mNrarfcnRangeListForRsrpBoost");
mLocaleTracker.dump(fd, pw, args);
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
@@ -5270,7 +5694,7 @@ public class ServiceStateTracker extends Handler {
ipw.flush();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isImsRegistered() {
return mImsRegistered;
}
@@ -5334,7 +5758,7 @@ public class ServiceStateTracker extends Handler {
* Set both voice and data roaming type,
* judging from the ISO country of SIM VS network.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void setRoamingType(ServiceState currentServiceState) {
final boolean isVoiceInService =
(currentServiceState.getState() == ServiceState.STATE_IN_SERVICE);
@@ -5426,7 +5850,7 @@ public class ServiceStateTracker extends Handler {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void setSignalStrengthDefaultValues() {
mSignalStrength = new SignalStrength();
mSignalStrengthUpdatedTime = System.currentTimeMillis();
@@ -5442,7 +5866,7 @@ public class ServiceStateTracker extends Handler {
return numeric;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected int getPhoneId() {
return mPhone.getPhoneId();
}
@@ -5582,7 +6006,7 @@ public class ServiceStateTracker extends Handler {
* If dataRegState is in service on IWLAN, also check for wifi calling enabled.
* @param ss service state.
*/
- protected int getCombinedRegState(ServiceState ss) {
+ public int getCombinedRegState(ServiceState ss) {
int regState = ss.getState();
int dataRegState = ss.getDataRegistrationState();
if ((regState == ServiceState.STATE_OUT_OF_SERVICE
@@ -5761,6 +6185,25 @@ public class ServiceStateTracker extends Handler {
}
/**
+ * Registers for CSS indicator changed.
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForCssIndicatorChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mCssIndicatorChangedRegistrants.add(r);
+ }
+
+ /**
+ * Unregisters for CSS indicator changed.
+ * @param h handler to notify
+ */
+ public void unregisterForCssIndicatorChanged(Handler h) {
+ mCssIndicatorChangedRegistrants.remove(h);
+ }
+
+ /**
* Get the NR data connection context ids.
*
* @return data connection context ids.
@@ -5791,6 +6234,17 @@ public class ServiceStateTracker extends Handler {
tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), type);
}
+ /** Returns the {@link ServiceStateStats} for the phone tracked. */
+ public ServiceStateStats getServiceStateStats() {
+ return mServiceStateStats;
+ }
+
+ /** Replaces the {@link ServiceStateStats} for testing purposes. */
+ @VisibleForTesting
+ public void setServiceStateStats(ServiceStateStats serviceStateStats) {
+ mServiceStateStats = serviceStateStats;
+ }
+
/**
* Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
*
@@ -5808,4 +6262,169 @@ public class ServiceStateTracker extends Handler {
values.put(SERVICE_STATE, p.marshall());
return values;
}
+
+ /**
+ * Set a new request to update the signal strength thresholds.
+ */
+ public void setSignalStrengthUpdateRequest(int subId, int callingUid,
+ SignalStrengthUpdateRequest request, @NonNull Message onCompleted) {
+ SignalRequestRecord record = new SignalRequestRecord(subId, callingUid, request);
+ sendMessage(obtainMessage(EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST,
+ new Pair<SignalRequestRecord, Message>(record, onCompleted)));
+ }
+
+ /**
+ * Clear the previously set request.
+ */
+ public void clearSignalStrengthUpdateRequest(int subId, int callingUid,
+ SignalStrengthUpdateRequest request, @Nullable Message onCompleted) {
+ SignalRequestRecord record = new SignalRequestRecord(subId, callingUid, request);
+ sendMessage(obtainMessage(EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST,
+ new Pair<SignalRequestRecord, Message>(record, onCompleted)));
+ }
+
+ /**
+ * Align all the qualified thresholds set from applications to the {@code systemThresholds}
+ * and consolidate a new thresholds array, follow rules below:
+ * 1. All threshold values (whose interval is guaranteed to be larger than hysteresis) in
+ * {@code systemThresholds} will keep as it.
+ * 2. Any threshold from apps that has interval less than hysteresis from any threshold in
+ * {@code systemThresholds} will be removed.
+ * 3. The target thresholds will be {@code systemThresholds} + all qualified thresholds from
+ * apps, sorted in ascending order.
+ */
+ int[] getConsolidatedSignalThresholds(int ran, int measurement,
+ int[] systemThresholds, int hysteresis) {
+
+ // TreeSet with comparator that will filter element with interval less than hysteresis
+ // from any current element
+ Set<Integer> target = new TreeSet<>((x, y) -> {
+ if (y >= x - hysteresis && y <= x + hysteresis) {
+ return 0;
+ }
+ return Integer.compare(x, y);
+ });
+
+ for (int systemThreshold : systemThresholds) {
+ target.add(systemThreshold);
+ }
+
+ final boolean isDeviceIdle = mPhone.isDeviceIdle();
+ final int curSubId = mPhone.getSubId();
+ // The total number of record is small (10~15 tops). With each request has at most 5
+ // SignalThresholdInfo which has at most 8 thresholds arrays. So the nested loop should
+ // not be a concern here.
+ for (SignalRequestRecord record : mSignalRequestRecords) {
+ if (curSubId != record.mSubId
+ || (isDeviceIdle && !record.mRequest.isReportingRequestedWhileIdle())) {
+ continue;
+ }
+ for (SignalThresholdInfo info : record.mRequest.getSignalThresholdInfos()) {
+ if (isRanAndSignalMeasurementTypeMatch(ran, measurement, info)) {
+ for (int appThreshold : info.getThresholds()) {
+ target.add(appThreshold);
+ }
+ }
+ }
+ }
+
+ int[] targetArray = new int[target.size()];
+ int i = 0;
+ for (int element : target) {
+ targetArray[i++] = element;
+ }
+ return targetArray;
+ }
+
+ boolean shouldHonorSystemThresholds() {
+ if (!mPhone.isDeviceIdle()) {
+ return true;
+ }
+
+ final int curSubId = mPhone.getSubId();
+ return mSignalRequestRecords.stream().anyMatch(
+ srr -> curSubId == srr.mSubId
+ && srr.mRequest.isSystemThresholdReportingRequestedWhileIdle());
+ }
+
+ void onDeviceIdleStateChanged(boolean isDeviceIdle) {
+ sendMessage(obtainMessage(EVENT_ON_DEVICE_IDLE_STATE_CHANGED, isDeviceIdle));
+ }
+
+ boolean shouldEnableSignalThresholdForAppRequest(
+ @AccessNetworkConstants.RadioAccessNetworkType int ran,
+ @SignalThresholdInfo.SignalMeasurementType int measurement,
+ int subId,
+ boolean isDeviceIdle) {
+ for (SignalRequestRecord record : mSignalRequestRecords) {
+ if (subId != record.mSubId) {
+ continue;
+ }
+ for (SignalThresholdInfo info : record.mRequest.getSignalThresholdInfos()) {
+ if (isRanAndSignalMeasurementTypeMatch(ran, measurement, info)
+ && (!isDeviceIdle || isSignalReportRequestedWhileIdle(record.mRequest))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean isRanAndSignalMeasurementTypeMatch(
+ @AccessNetworkConstants.RadioAccessNetworkType int ran,
+ @SignalThresholdInfo.SignalMeasurementType int measurement,
+ SignalThresholdInfo info) {
+ return ran == info.getRadioAccessNetworkType()
+ && measurement == info.getSignalMeasurementType();
+ }
+
+ private static boolean isSignalReportRequestedWhileIdle(SignalStrengthUpdateRequest request) {
+ return request.isSystemThresholdReportingRequestedWhileIdle()
+ || request.isReportingRequestedWhileIdle();
+ }
+
+ private class SignalRequestRecord implements IBinder.DeathRecipient {
+ final int mSubId; // subId the request originally applied to
+ final int mCallingUid;
+ final SignalStrengthUpdateRequest mRequest;
+
+ SignalRequestRecord(int subId, int uid, @NonNull SignalStrengthUpdateRequest request) {
+ this.mCallingUid = uid;
+ this.mSubId = subId;
+ this.mRequest = request;
+ }
+
+ @Override
+ public void binderDied() {
+ clearSignalStrengthUpdateRequest(mSubId, mCallingUid, mRequest, null /*onCompleted*/);
+ }
+ }
+
+ private void updateAlwaysReportSignalStrength() {
+ final int curSubId = mPhone.getSubId();
+ boolean alwaysReport = mSignalRequestRecords.stream().anyMatch(
+ srr -> srr.mSubId == curSubId && isSignalReportRequestedWhileIdle(srr.mRequest));
+
+ // TODO(b/177924721): TM#setAlwaysReportSignalStrength will be removed and we will not
+ // worry about unset flag which was set by other client.
+ mPhone.setAlwaysReportSignalStrength(alwaysReport);
+ }
+
+ /**
+ * Registers for TAC/LAC changed event.
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForAreaCodeChanged(Handler h, int what, Object obj) {
+ mAreaCodeChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for TAC/LAC changed event.
+ * @param h handler to notify
+ */
+ public void unregisterForAreaCodeChanged(Handler h) {
+ mAreaCodeChangedRegistrants.remove(h);
+ }
}
diff --git a/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java b/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java
new file mode 100644
index 0000000000..7540ce6d9a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SlidingWindowEventCounter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.IntRange;
+import android.os.SystemClock;
+import android.util.LongArrayQueue;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Tracks whether an event occurs mNumOccurrences times within the time span mWindowSizeMillis.
+ * <p/>
+ * Stores all events in memory, use a small number of required occurrences when using.
+ */
+public class SlidingWindowEventCounter {
+ private final long mWindowSizeMillis;
+ private final int mNumOccurrences;
+ private final LongArrayQueue mTimestampQueueMillis;
+
+ public SlidingWindowEventCounter(@IntRange(from = 1) final long windowSizeMillis,
+ @IntRange(from = 2) final int numOccurrences) {
+ if (windowSizeMillis <= 0) {
+ throw new IllegalArgumentException("windowSizeMillis must be greater than 0");
+ }
+ if (numOccurrences <= 1) {
+ throw new IllegalArgumentException("numOccurrences must be greater than 1");
+ }
+
+ mWindowSizeMillis = windowSizeMillis;
+ mNumOccurrences = numOccurrences;
+ mTimestampQueueMillis = new LongArrayQueue(numOccurrences);
+ }
+
+ /**
+ * Increments the occurrence counter.
+ *
+ * Returns true if an event has occurred at least mNumOccurrences times within the
+ * time span mWindowSizeMillis.
+ */
+ public synchronized boolean addOccurrence() {
+ return addOccurrence(SystemClock.elapsedRealtime());
+ }
+
+ /**
+ * Increments the occurrence counter.
+ *
+ * Returns true if an event has occurred at least mNumOccurrences times within the
+ * time span mWindowSizeMillis.
+ * @param timestampMillis
+ */
+ public synchronized boolean addOccurrence(long timestampMillis) {
+ mTimestampQueueMillis.addLast(timestampMillis);
+ if (mTimestampQueueMillis.size() > mNumOccurrences) {
+ mTimestampQueueMillis.removeFirst();
+ }
+ return isInWindow();
+ }
+
+ /**
+ * Returns true if the conditions is satisfied.
+ */
+ public synchronized boolean isInWindow() {
+ return (mTimestampQueueMillis.size() == mNumOccurrences)
+ && mTimestampQueueMillis.peekFirst()
+ + mWindowSizeMillis >= mTimestampQueueMillis.peekLast();
+ }
+
+ @VisibleForTesting
+ int getNumOccurrences() {
+ return mTimestampQueueMillis.size();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index 5008665626..7c52a42817 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -92,12 +92,6 @@ public class SmsBroadcastUndelivered {
/** Content resolver to use to access raw table from SmsProvider. */
private final ContentResolver mResolver;
- /** Handler for 3GPP-format messages (may be null). */
- private final GsmInboundSmsHandler mGsmInboundSmsHandler;
-
- /** Handler for 3GPP2-format messages (may be null). */
- private final CdmaInboundSmsHandler mCdmaInboundSmsHandler;
-
/** Broadcast receiver that processes the raw table when the user unlocks the phone for the
* first time after reboot and the credential-encrypted storage is available.
*/
@@ -120,7 +114,7 @@ public class SmsBroadcastUndelivered {
@Override
public void run() {
- scanRawTable(context, mCdmaInboundSmsHandler, mGsmInboundSmsHandler,
+ scanRawTable(context,
System.currentTimeMillis() - getUndeliveredSmsExpirationTime(context));
InboundSmsHandler.cancelNewMessageNotification(context);
}
@@ -129,8 +123,7 @@ public class SmsBroadcastUndelivered {
public static void initialize(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
CdmaInboundSmsHandler cdmaInboundSmsHandler) {
if (instance == null) {
- instance = new SmsBroadcastUndelivered(
- context, gsmInboundSmsHandler, cdmaInboundSmsHandler);
+ instance = new SmsBroadcastUndelivered(context);
}
// Tell handlers to start processing new messages and transit from the startup state to the
@@ -145,11 +138,8 @@ public class SmsBroadcastUndelivered {
}
@UnsupportedAppUsage
- private SmsBroadcastUndelivered(Context context, GsmInboundSmsHandler gsmInboundSmsHandler,
- CdmaInboundSmsHandler cdmaInboundSmsHandler) {
+ private SmsBroadcastUndelivered(Context context) {
mResolver = context.getContentResolver();
- mGsmInboundSmsHandler = gsmInboundSmsHandler;
- mCdmaInboundSmsHandler = cdmaInboundSmsHandler;
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -165,8 +155,7 @@ public class SmsBroadcastUndelivered {
/**
* Scan the raw table for complete SMS messages to broadcast, and old PDUs to delete.
*/
- static void scanRawTable(Context context, CdmaInboundSmsHandler cdmaInboundSmsHandler,
- GsmInboundSmsHandler gsmInboundSmsHandler, long oldMessageTimestamp) {
+ static void scanRawTable(Context context, long oldMessageTimestamp) {
if (DBG) Rlog.d(TAG, "scanning raw table for undelivered messages");
long startTime = System.nanoTime();
ContentResolver contentResolver = context.getContentResolver();
@@ -199,7 +188,7 @@ public class SmsBroadcastUndelivered {
if (tracker.getMessageCount() == 1) {
// deliver single-part message
- broadcastSms(tracker, cdmaInboundSmsHandler, gsmInboundSmsHandler);
+ broadcastSms(tracker);
} else {
SmsReferenceKey reference = new SmsReferenceKey(tracker);
Integer receivedCount = multiPartReceivedCount.get(reference);
@@ -216,7 +205,7 @@ public class SmsBroadcastUndelivered {
// looks like we've got all the pieces; send a single tracker
// to state machine which will find the other pieces to broadcast
if (DBG) Rlog.d(TAG, "found complete multi-part message");
- broadcastSms(tracker, cdmaInboundSmsHandler, gsmInboundSmsHandler);
+ broadcastSms(tracker);
// don't delete this old message until after we broadcast it
oldMultiPartMessages.remove(reference);
} else {
@@ -225,8 +214,12 @@ public class SmsBroadcastUndelivered {
}
}
}
- // Retrieve the phone id, required for metrics
- int phoneId = getPhoneId(gsmInboundSmsHandler, cdmaInboundSmsHandler);
+ // Retrieve the phone and phone id, required for metrics
+ // TODO don't hardcode to the first phone (phoneId = 0) but this is no worse than
+ // earlier. Also phoneId for old messages may not be known (messages may be from an
+ // inactive sub)
+ Phone phone = PhoneFactory.getPhone(0);
+ int phoneId = 0;
// Delete old incomplete message segments
for (SmsReferenceKey message : oldMultiPartMessages) {
@@ -244,6 +237,10 @@ public class SmsBroadcastUndelivered {
TelephonyMetrics metrics = TelephonyMetrics.getInstance();
metrics.writeDroppedIncomingMultipartSms(phoneId, message.mFormat, rows,
message.mMessageCount);
+ if (phone != null) {
+ phone.getSmsStats().onDroppedIncomingMultipartSms(message.mIs3gpp2, rows,
+ message.mMessageCount);
+ }
}
}
} catch (SQLException e) {
@@ -258,31 +255,24 @@ public class SmsBroadcastUndelivered {
}
/**
- * Retrieve the phone id for the GSM or CDMA Inbound SMS handler
- */
- private static int getPhoneId(GsmInboundSmsHandler gsmInboundSmsHandler,
- CdmaInboundSmsHandler cdmaInboundSmsHandler) {
- int phoneId = SubscriptionManager.INVALID_PHONE_INDEX;
- if (gsmInboundSmsHandler != null) {
- phoneId = gsmInboundSmsHandler.getPhone().getPhoneId();
- } else if (cdmaInboundSmsHandler != null) {
- phoneId = cdmaInboundSmsHandler.getPhone().getPhoneId();
- }
- return phoneId;
- }
-
- /**
* Send tracker to appropriate (3GPP or 3GPP2) inbound SMS handler for broadcast.
*/
- private static void broadcastSms(InboundSmsTracker tracker,
- CdmaInboundSmsHandler cdmaInboundSmsHandler,
- GsmInboundSmsHandler gsmInboundSmsHandler) {
+ private static void broadcastSms(InboundSmsTracker tracker) {
InboundSmsHandler handler;
- if (tracker.is3gpp2()) {
- handler = cdmaInboundSmsHandler;
- } else {
- handler = gsmInboundSmsHandler;
+ int subId = tracker.getSubId();
+ // TODO consider other subs in this subId's group as well
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+ Rlog.e(TAG, "broadcastSms: ignoring message; no phone found for subId " + subId);
+ return;
+ }
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone == null) {
+ Rlog.e(TAG, "broadcastSms: ignoring message; no phone found for subId " + subId
+ + " phoneId " + phoneId);
+ return;
}
+ handler = phone.getInboundSmsHandler(tracker.is3gpp2());
if (handler != null) {
handler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS, tracker);
} else {
@@ -312,6 +302,7 @@ public class SmsBroadcastUndelivered {
final int mReferenceNumber;
final int mMessageCount;
final String mQuery;
+ final boolean mIs3gpp2;
final String mFormat;
SmsReferenceKey(InboundSmsTracker tracker) {
@@ -319,6 +310,7 @@ public class SmsBroadcastUndelivered {
mReferenceNumber = tracker.getReferenceNumber();
mMessageCount = tracker.getMessageCount();
mQuery = tracker.getQueryForSegments();
+ mIs3gpp2 = tracker.is3gpp2();
mFormat = tracker.getFormat();
}
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 49249707a7..e7feaf4cad 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -29,6 +29,7 @@ import android.content.Context;
import android.net.Uri;
import android.os.BaseBundle;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.TelephonyServiceManager.ServiceRegisterer;
import android.provider.Telephony.Sms.Intents;
@@ -80,7 +81,7 @@ public class SmsController extends ISmsImplBase {
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE));
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index,
int status, byte[] pdu) {
@@ -97,7 +98,7 @@ public class SmsController extends ISmsImplBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean copyMessageToIccEfForSubscriber(int subId, String callingPackage, int status,
byte[] pdu, byte[] smsc) {
@@ -114,7 +115,7 @@ public class SmsController extends ISmsImplBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage) {
if (callingPackage == null) {
@@ -135,7 +136,7 @@ public class SmsController extends ISmsImplBase {
* byte[], PendingIntent, PendingIntent)} instead
*/
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void sendDataForSubscriber(int subId, String callingPackage, String destAddr,
String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
@@ -312,14 +313,14 @@ public class SmsController extends ISmsImplBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType) {
return enableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
ranType);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
int endMessageId, int ranType) {
@@ -333,7 +334,7 @@ public class SmsController extends ISmsImplBase {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean disableCellBroadcastForSubscriber(int subId,
int messageIdentifier, int ranType) {
@@ -341,7 +342,7 @@ public class SmsController extends ISmsImplBase {
ranType);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
int endMessageId, int ranType) {
@@ -389,7 +390,7 @@ public class SmsController extends ISmsImplBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean isImsSmsSupportedForSubscriber(int subId) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
@@ -444,7 +445,7 @@ public class SmsController extends ISmsImplBase {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getImsSmsFormatForSubscriber(int subId) {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
@@ -476,7 +477,7 @@ public class SmsController extends ISmsImplBase {
* subscription if there is only one active. If no preference can be found, return
* {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public int getPreferredSmsSubscription() {
// If there is a default, choose that one.
@@ -771,7 +772,7 @@ public class SmsController extends ISmsImplBase {
indentingPW.flush();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
if (intent != null) {
try {
@@ -781,7 +782,7 @@ public class SmsController extends ISmsImplBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendErrorInPendingIntents(List<PendingIntent> intents, int errorCode) {
if (intents == null) {
return;
@@ -797,7 +798,7 @@ public class SmsController extends ISmsImplBase {
*
* @return ICC SMS manager
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
return getPhone(subId).getIccSmsInterfaceManager();
}
@@ -816,7 +817,7 @@ public class SmsController extends ISmsImplBase {
IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
if (iccSmsIntMgr != null ) {
- return iccSmsIntMgr.getSmsCapacityOnIcc();
+ return iccSmsIntMgr.getSmsCapacityOnIcc(getCallingPackage(), null);
} else {
Rlog.e(LOG_TAG, "iccSmsIntMgr is null for " + " subId: " + subId);
return 0;
@@ -840,4 +841,11 @@ public class SmsController extends ISmsImplBase {
return false;
}
}
+
+ /**
+ * Internal API to consistently format the debug log output of the cross-stack message id.
+ */
+ public static String formatCrossStackMessageId(long id) {
+ return "{x-message-id:" + id + "}";
+ }
}
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index d657eb0069..53556ac9f7 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -16,8 +16,6 @@
package com.android.internal.telephony;
-import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
-import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_NONE;
import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_TEMPORARY;
@@ -39,8 +37,8 @@ import android.provider.Telephony.Sms.Intents;
import android.telephony.ServiceState;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
-import android.util.Pair;
+import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
@@ -52,7 +50,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Map.Entry;
/**
*
@@ -110,6 +107,26 @@ public class SmsDispatchersController extends Handler {
private boolean mIms = false;
private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
+ /** 3GPP format sent messages awaiting a delivery status report. */
+ private HashMap<Integer, SMSDispatcher.SmsTracker> mDeliveryPendingMapFor3GPP = new HashMap<>();
+
+ /** 3GPP2 format sent messages awaiting a delivery status report. */
+ private HashMap<Integer, SMSDispatcher.SmsTracker> mDeliveryPendingMapFor3GPP2 =
+ new HashMap<>();
+
+ /**
+ * Puts a delivery pending tracker to the map based on the format.
+ *
+ * @param tracker the tracker awaiting a delivery status report.
+ */
+ public void putDeliveryPendingTracker(SMSDispatcher.SmsTracker tracker) {
+ if (isCdmaFormat(tracker.mFormat)) {
+ mDeliveryPendingMapFor3GPP2.put(tracker.mMessageRef, tracker);
+ } else {
+ mDeliveryPendingMapFor3GPP.put(tracker.mMessageRef, tracker);
+ }
+ }
+
public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
SmsUsageMonitor usageMonitor) {
Rlog.d(TAG, "SmsDispatchersController created");
@@ -121,7 +138,7 @@ public class SmsDispatchersController extends Handler {
// Create dispatchers, inbound SMS handlers and
// broadcast undelivered messages in raw table.
- mImsSmsDispatcher = new ImsSmsDispatcher(phone, this);
+ mImsSmsDispatcher = new ImsSmsDispatcher(phone, this, ImsManager::getConnector);
mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
storageMonitor, phone);
@@ -297,8 +314,7 @@ public class SmsDispatchersController extends Handler {
// Timer expired. This indicates that device has been in service for
// PARTIAL_SEGMENT_WAIT_DURATION since waitTimerStart. Delete orphaned message segments
// older than waitTimerStart.
- SmsBroadcastUndelivered.scanRawTable(mContext, mCdmaInboundSmsHandler,
- mGsmInboundSmsHandler, waitTimerStart);
+ SmsBroadcastUndelivered.scanRawTable(mContext, waitTimerStart);
if (VDBG) {
logd("handlePartialSegmentTimerExpiry: scanRawTable() done");
}
@@ -368,12 +384,13 @@ public class SmsDispatchersController extends Handler {
* the same time an SMS received from radio is responded back.
*/
@VisibleForTesting
- public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
+ public void injectSmsPdu(byte[] pdu, String format, boolean isOverIms,
+ SmsInjectionCallback callback) {
// TODO We need to decide whether we should allow injecting GSM(3gpp)
// SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
android.telephony.SmsMessage msg =
android.telephony.SmsMessage.createFromPdu(pdu, format);
- injectSmsPdu(msg, format, callback, false /* ignoreClass */);
+ injectSmsPdu(msg, format, callback, false /* ignoreClass */, isOverIms);
}
/**
@@ -388,7 +405,7 @@ public class SmsDispatchersController extends Handler {
*/
@VisibleForTesting
public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback,
- boolean ignoreClass) {
+ boolean ignoreClass, boolean isOverIms) {
Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
try {
if (msg == null) {
@@ -409,11 +426,13 @@ public class SmsDispatchersController extends Handler {
if (format.equals(SmsConstants.FORMAT_3GPP)) {
Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ ", format=" + format + "to mGsmInboundSmsHandler");
- mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+ mGsmInboundSmsHandler.sendMessage(
+ InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, 0, ar);
} else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ ", format=" + format + "to mCdmaInboundSmsHandler");
- mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+ mCdmaInboundSmsHandler.sendMessage(
+ InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, 0, ar);
} else {
// Invalid pdu format.
Rlog.e(TAG, "Invalid pdu format: " + format);
@@ -466,12 +485,18 @@ public class SmsDispatchersController extends Handler {
}
String scAddr = (String) map.get("scAddr");
String destAddr = (String) map.get("destAddr");
+ if (destAddr == null) {
+ Rlog.e(TAG, "sendRetrySms failed due to null destAddr");
+ tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
+ return;
+ }
SmsMessageBase.SubmitPduBase pdu = null;
// figure out from tracker if this was sendText/Data
if (map.containsKey("text")) {
- Rlog.d(TAG, "sms failed was text");
String text = (String) map.get("text");
+ Rlog.d(TAG, "sms failed was text with length: "
+ + (text == null ? null : text.length()));
if (isCdmaFormat(newFormat)) {
pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
@@ -481,9 +506,10 @@ public class SmsDispatchersController extends Handler {
scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
}
} else if (map.containsKey("data")) {
- Rlog.d(TAG, "sms failed was data");
byte[] data = (byte[]) map.get("data");
Integer destPort = (Integer) map.get("destPort");
+ Rlog.d(TAG, "sms failed was data with length: "
+ + (data == null ? null : data.length));
if (isCdmaFormat(newFormat)) {
pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
@@ -496,6 +522,13 @@ public class SmsDispatchersController extends Handler {
}
}
+ if (pdu == null) {
+ Rlog.e(TAG, String.format("sendRetrySms failed to encode message."
+ + "scAddr: %s, "
+ + "destPort: %s", scAddr, map.get("destPort")));
+ tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
+ return;
+ }
// replace old smsc and pdu with newly encoded ones
map.put("smsc", pdu.encodedScAddress);
map.put("pdu", pdu.encodedMessage);
@@ -569,13 +602,65 @@ public class SmsDispatchersController extends Handler {
* broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * <code>RESULT_ERROR_NO_SERVICE</code><br>.
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -599,7 +684,8 @@ public class SmsDispatchersController extends Handler {
/**
* Send a text based SMS.
- * @param destAddr the address to send the message to
+ *
+ * @param destAddr the address to send the message to
* @param scAddr is the service center address or null to use
* the current default SMSC
* @param text the body of the message to send
@@ -607,13 +693,65 @@ public class SmsDispatchersController extends Handler {
* broadcast when the message is successfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * <code>RESULT_ERROR_NO_SERVICE</code><br>.
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -622,7 +760,7 @@ public class SmsDispatchersController extends Handler {
* @param messageUri optional URI of the message if it is already stored in the system
* @param callingPkg the calling package name
* @param persistMessage whether to save the sent message into SMS DB for a
- * non-default SMS app.
+ * non-default SMS app.
* @param priority Priority level of the message
* Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
* ---------------------------------
@@ -647,9 +785,8 @@ public class SmsDispatchersController extends Handler {
long messageId) {
if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) {
mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
- messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
- false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm,
- messageId);
+ messageUri, callingPkg, persistMessage, priority, false /*expectMore*/,
+ validityPeriod, isForVvm, messageId);
} else {
if (isCdmaMo()) {
mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
@@ -665,31 +802,87 @@ public class SmsDispatchersController extends Handler {
/**
* Send a multi-part text based SMS.
- * @param destAddr the address to send the message to
+ *
+ * @param destAddr the address to send the message to
* @param scAddr is the service center address or null to use
- * the current default SMSC
+ * the current default SMSC
* @param parts an <code>ArrayList</code> of strings that, in order,
- * comprise the original message
+ * comprise the original message
* @param sentIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been sent.
- * The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>
- * <code>RESULT_ERROR_NO_SERVICE</code>.
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been sent.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>SmsManager.RESULT_ERROR_NULL_PDU</code><br>
+ * <code>SmsManager.RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>SmsManager.RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_SYSTEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_MODEM_ERROR</code><br>
+ * <code>SmsManager.RESULT_NETWORK_ERROR</code><br>
+ * <code>SmsManager.RESULT_ENCODING_ERROR</code><br>
+ * <code>SmsManager.RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_INTERNAL_ERROR</code><br>
+ * <code>SmsManager.RESULT_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>SmsManager.RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>SmsManager.RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>SmsManager.RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_MEMORY</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>SmsManager.RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>SmsManager.RESULT_RIL_MODEM_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>SmsManager.RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>SmsManager.RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>SmsManager.RESULT_RIL_CANCELLED</code><br>
+ * <code>SmsManager.RESULT_RIL_SIM_ABSENT</code><br>
+ * <code>SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED</code><br>
+ * <code>SmsManager.RESULT_RIL_ACCESS_BARRED</code><br>
+ * <code>SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL</code><br>
+ * For <code>SmsManager.RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been delivered
- * to the recipient. The raw pdu of the status report is in the
+ * <code>PendingIntent</code>s (one for each message part) that is
+ * broadcast when the corresponding message part has been delivered
+ * to the recipient. The raw pdu of the status report is in the
* @param messageUri optional URI of the message if it is already stored in the system
* @param callingPkg the calling package name
* @param persistMessage whether to save the sent message into SMS DB for a
- * non-default SMS app.
+ * non-default SMS app.
* @param priority Priority level of the message
* Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
* ---------------------------------
@@ -718,10 +911,8 @@ public class SmsDispatchersController extends Handler {
long messageId) {
if (mImsSmsDispatcher.isAvailable()) {
mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
- deliveryIntents, messageUri, callingPkg, persistMessage,
- SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
- false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
- messageId);
+ deliveryIntents, messageUri, callingPkg, persistMessage, priority,
+ false /*expectMore*/, validityPeriod, messageId);
} else {
if (isCdmaMo()) {
mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
@@ -766,86 +957,75 @@ public class SmsDispatchersController extends Handler {
}
/**
- * Handles the sms status report for the sent sms through ImsSmsDispatcher. Carriers can send
- * the report over CS even if the previously submitted SMS-SUBMIT was sent over IMS. For this
- * case, finds a corresponding tracker from the tracker map in ImsSmsDispatcher and handles it.
+ * Handles the sms status report based on the format.
*
- * @param messageRef the TP-MR of the previously submitted SMS-SUBMIT in the report.
* @param format the format.
* @param pdu the pdu of the report.
*/
- public void handleSentOverImsStatusReport(int messageRef, String format, byte[] pdu) {
- for (Entry<Integer, SMSDispatcher.SmsTracker> entry :
- mImsSmsDispatcher.mTrackers.entrySet()) {
- int token = entry.getKey();
- SMSDispatcher.SmsTracker tracker = entry.getValue();
- if (tracker.mMessageRef == messageRef) {
- Pair<Boolean, Boolean> result = handleSmsStatusReport(tracker, format, pdu);
- if (result.second) {
- mImsSmsDispatcher.mTrackers.remove(token);
+ public void handleSmsStatusReport(String format, byte[] pdu) {
+ int messageRef;
+ SMSDispatcher.SmsTracker tracker;
+ boolean handled = false;
+ if (isCdmaFormat(format)) {
+ com.android.internal.telephony.cdma.SmsMessage sms =
+ com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+ if (sms != null) {
+ boolean foundIn3GPPMap = false;
+ messageRef = sms.mMessageRef;
+ tracker = mDeliveryPendingMapFor3GPP2.get(messageRef);
+ if (tracker == null) {
+ // A tracker for this 3GPP2 report may be in the 3GPP map instead if the
+ // previously submitted SMS was 3GPP format.
+ // (i.e. Some carriers require that devices receive 3GPP2 SMS also even if IMS
+ // SMS format is 3GGP.)
+ tracker = mDeliveryPendingMapFor3GPP.get(messageRef);
+ if (tracker != null) {
+ foundIn3GPPMap = true;
+ }
+ }
+ if (tracker != null) {
+ // The status is composed of an error class (bits 25-24) and a status code
+ // (bits 23-16).
+ int errorClass = (sms.getStatus() >> 24) & 0x03;
+ if (errorClass != ERROR_TEMPORARY) {
+ // Update the message status (COMPLETE or FAILED)
+ tracker.updateSentMessageStatus(
+ mContext,
+ (errorClass == ERROR_NONE)
+ ? Sms.STATUS_COMPLETE
+ : Sms.STATUS_FAILED);
+ // No longer need to be kept.
+ if (foundIn3GPPMap) {
+ mDeliveryPendingMapFor3GPP.remove(messageRef);
+ } else {
+ mDeliveryPendingMapFor3GPP2.remove(messageRef);
+ }
+ }
+ handled = triggerDeliveryIntent(tracker, format, pdu);
}
- return;
}
- }
- }
-
- /**
- * Triggers the correct method for handling the sms status report based on the format.
- *
- * @param tracker the sms tracker.
- * @param format the format.
- * @param pdu the pdu of the report.
- * @return a Pair in which the first boolean is whether the report was handled successfully
- * or not and the second boolean is whether processing the sms is complete and the
- * tracker no longer need to be kept track of, false if we should expect more callbacks
- * and the tracker should be kept.
- */
- public Pair<Boolean, Boolean> handleSmsStatusReport(SMSDispatcher.SmsTracker tracker,
- String format, byte[] pdu) {
- if (isCdmaFormat(format)) {
- return handleCdmaStatusReport(tracker, format, pdu);
} else {
- return handleGsmStatusReport(tracker, format, pdu);
- }
- }
-
- private Pair<Boolean, Boolean> handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker,
- String format, byte[] pdu) {
- com.android.internal.telephony.cdma.SmsMessage sms =
- com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
- boolean complete = false;
- boolean success = false;
- if (sms != null) {
- // The status is composed of an error class (bits 25-24) and a status code (bits 23-16).
- int errorClass = (sms.getStatus() >> 24) & 0x03;
- if (errorClass != ERROR_TEMPORARY) {
- // Update the message status (COMPLETE or FAILED)
- tracker.updateSentMessageStatus(
- mContext,
- (errorClass == ERROR_NONE) ? Sms.STATUS_COMPLETE : Sms.STATUS_FAILED);
- complete = true;
+ com.android.internal.telephony.gsm.SmsMessage sms =
+ com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+ if (sms != null) {
+ messageRef = sms.mMessageRef;
+ tracker = mDeliveryPendingMapFor3GPP.get(messageRef);
+ if (tracker != null) {
+ int tpStatus = sms.getStatus();
+ if (tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING) {
+ // Update the message status (COMPLETE or FAILED)
+ tracker.updateSentMessageStatus(mContext, tpStatus);
+ // No longer need to be kept.
+ mDeliveryPendingMapFor3GPP.remove(messageRef);
+ }
+ handled = triggerDeliveryIntent(tracker, format, pdu);
+ }
}
- success = triggerDeliveryIntent(tracker, format, pdu);
}
- return new Pair(success, complete);
- }
- private Pair<Boolean, Boolean> handleGsmStatusReport(SMSDispatcher.SmsTracker tracker,
- String format, byte[] pdu) {
- com.android.internal.telephony.gsm.SmsMessage sms =
- com.android.internal.telephony.gsm.SmsMessage.newFromCDS(pdu);
- boolean complete = false;
- boolean success = false;
- if (sms != null) {
- int tpStatus = sms.getStatus();
- if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
- // Update the message status (COMPLETE or FAILED)
- tracker.updateSentMessageStatus(mContext, tpStatus);
- complete = true;
- }
- success = triggerDeliveryIntent(tracker, format, pdu);
+ if (!handled) {
+ Rlog.e(TAG, "handleSmsStatusReport: can not handle the status report!");
}
- return new Pair(success, complete);
}
private boolean triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format,
@@ -862,6 +1042,13 @@ public class SmsDispatchersController extends Handler {
}
}
+ /**
+ * Get InboundSmsHandler for the phone.
+ */
+ public InboundSmsHandler getInboundSmsHandler(boolean is3gpp2) {
+ if (is3gpp2) return mCdmaInboundSmsHandler;
+ else return mGsmInboundSmsHandler;
+ }
public interface SmsInjectionCallback {
void onSmsInjectedResult(int result);
@@ -870,6 +1057,9 @@ public class SmsDispatchersController extends Handler {
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mGsmInboundSmsHandler.dump(fd, pw, args);
mCdmaInboundSmsHandler.dump(fd, pw, args);
+ mGsmDispatcher.dump(fd, pw, args);
+ mCdmaDispatcher.dump(fd, pw, args);
+ mImsSmsDispatcher.dump(fd, pw, args);
}
private void logd(String msg) {
diff --git a/src/java/com/android/internal/telephony/SmsPermissions.java b/src/java/com/android/internal/telephony/SmsPermissions.java
index 55de517821..44751acf9d 100644
--- a/src/java/com/android/internal/telephony/SmsPermissions.java
+++ b/src/java/com/android/internal/telephony/SmsPermissions.java
@@ -22,6 +22,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
+import android.os.Build;
import android.service.carrier.CarrierMessagingService;
import com.android.internal.annotations.VisibleForTesting;
@@ -33,11 +34,11 @@ import com.android.telephony.Rlog;
public class SmsPermissions {
static final String LOG_TAG = "SmsPermissions";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final Phone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final Context mContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final AppOpsManager mAppOps;
public SmsPermissions(Phone phone, Context context, AppOpsManager appOps) {
@@ -77,14 +78,14 @@ public class SmsPermissions {
/**
* Enforces that the caller is one of the following apps:
* <ul>
- * <li> IMS App
+ * <li> IMS App determined by telephony to implement RCS features
* <li> Carrier App
* </ul>
*/
public void enforceCallerIsImsAppOrCarrierApp(String message) {
- String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext,
+ String imsRcsPackage = CarrierSmsUtils.getImsRcsPackageForIntent(mContext,
mPhone, new Intent(CarrierMessagingService.SERVICE_INTERFACE));
- if (carrierImsPackage != null && packageNameMatchesCallingUid(carrierImsPackage)) {
+ if (imsRcsPackage != null && packageNameMatchesCallingUid(imsRcsPackage)) {
return;
}
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
@@ -133,7 +134,7 @@ public class SmsPermissions {
// Allow it to the default SMS app always.
if (!isCallerDefaultSmsPackage(callingPackage)) {
TelephonyPermissions
- .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
mContext, mPhone.getSubId(), message);
}
return true;
@@ -184,7 +185,7 @@ public class SmsPermissions {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void log(String msg) {
Rlog.d(LOG_TAG, msg);
}
diff --git a/src/java/com/android/internal/telephony/SmsResponse.java b/src/java/com/android/internal/telephony/SmsResponse.java
index e2209bebbe..851d04be83 100644
--- a/src/java/com/android/internal/telephony/SmsResponse.java
+++ b/src/java/com/android/internal/telephony/SmsResponse.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
* Object returned by the RIL upon successful completion of sendSMS.
@@ -27,31 +28,38 @@ public class SmsResponse {
public static final int NO_ERROR_CODE = -1;
/** Message reference of the just-sent SMS. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mMessageRef;
/** ackPdu for the just-sent SMS. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mAckPdu;
/**
* errorCode: See 3GPP 27.005, 3.2.5 for GSM/UMTS,
* 3GPP2 N.S0005 (IS-41C) Table 171 for CDMA, -1 if unknown or not applicable.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int mErrorCode;
+ public long mMessageId;
+
@UnsupportedAppUsage
public SmsResponse(int messageRef, String ackPdu, int errorCode) {
+ this(messageRef, ackPdu, errorCode, /* messageId= */ 0L);
+ }
+
+ public SmsResponse(int messageRef, String ackPdu, int errorCode, long messageId) {
mMessageRef = messageRef;
mAckPdu = ackPdu;
mErrorCode = errorCode;
+ mMessageId = messageId;
}
@Override
public String toString() {
String ret = "{ mMessageRef = " + mMessageRef
+ ", mErrorCode = " + mErrorCode
- + ", mAckPdu = " + mAckPdu
- + "}";
+ + ", mAckPdu = " + mAckPdu
+ + ", " + SmsController.formatCrossStackMessageId(mMessageId) + "}";
return ret;
}
}
diff --git a/src/java/com/android/internal/telephony/SmsStorageMonitor.java b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
index 9eb43da3e5..b890a4da4d 100755
--- a/src/java/com/android/internal/telephony/SmsStorageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
@@ -60,7 +61,7 @@ public class SmsStorageMonitor extends Handler {
/** it is use to put in to extra value for SIM_FULL_ACTION and SMS_REJECTED_ACTION */
Phone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final CommandsInterface mCi; // accessed from inner class
boolean mStorageAvailable = true; // accessed from inner class
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 8f583d92b4..8bcdc07b55 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.app.role.RoleManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -24,12 +25,13 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
-import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
import android.util.AtomicFile;
import android.util.Xml;
@@ -51,6 +53,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Pattern;
@@ -125,6 +128,8 @@ public class SmsUsageMonitor {
/** Last modified time for pattern file */
private long mPatternFileLastModified = 0;
+ private RoleManager mRoleManager;
+
/** Directory for per-app SMS permission XML file. */
private static final String SMS_POLICY_FILE_DIRECTORY = "/data/misc/sms";
@@ -244,10 +249,11 @@ public class SmsUsageMonitor {
* Create SMS usage monitor.
* @param context the context to use to load resources and get TelephonyManager service
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public SmsUsageMonitor(Context context) {
mContext = context;
ContentResolver resolver = context.getContentResolver();
+ mRoleManager = (RoleManager) mContext.getSystemService(Context.ROLE_SERVICE);
mMaxAllowed = Settings.Global.getInt(resolver,
Settings.Global.SMS_OUTGOING_CHECK_MAX_COUNT,
@@ -345,14 +351,14 @@ public class SmsUsageMonitor {
/**
* Check to see if an application is allowed to send new SMS messages, and confirm with
* user if the send limit was reached or if a non-system app is potentially sending to a
- * premium SMS short code or number.
+ * premium SMS short code or number. If the app is the default SMS app, there's no send limit.
*
* @param appName the package name of the app requesting to send an SMS
* @param smsWaiting the number of new messages desired to send
* @return true if application is allowed to send the requested number
* of new sms messages
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean check(String appName, int smsWaiting) {
synchronized (mSmsStamp) {
removeExpiredTimestamps();
@@ -363,7 +369,12 @@ public class SmsUsageMonitor {
mSmsStamp.put(appName, sentList);
}
- return isUnderLimit(sentList, smsWaiting);
+ List<String> defaultApp = mRoleManager.getRoleHolders(RoleManager.ROLE_SMS);
+ if (defaultApp.contains(appName)) {
+ return true;
+ } else {
+ return isUnderLimit(sentList, smsWaiting);
+ }
}
}
@@ -386,8 +397,9 @@ public class SmsUsageMonitor {
*/
public int checkDestination(String destAddress, String countryIso) {
synchronized (mSettingsObserverHandler) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
// always allow emergency numbers
- if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
+ if (tm.isEmergencyNumber(destAddress)) {
if (DBG) Rlog.d(TAG, "isEmergencyNumber");
return SmsManager.SMS_CATEGORY_NOT_SHORT_CODE;
}
@@ -580,16 +592,19 @@ public class SmsUsageMonitor {
if (appId == Process.SYSTEM_UID || appId == Process.PHONE_UID || uid == 0) {
return;
}
+ // log string should be same in both exception scenarios below, otherwise it can be used to
+ // detect if a package is installed on the device which is a privacy/security issue
+ String errorLog = "Calling uid " + uid + " gave package " + pkg + " which is either "
+ + "unknown or owned by another uid";
try {
ApplicationInfo ai = mContext.getPackageManager().getApplicationInfoAsUser(
pkg, 0, UserHandle.getUserHandleForUid(uid));
- if (UserHandle.getAppId(ai.uid) != UserHandle.getAppId(uid)) {
- throw new SecurityException("Calling uid " + uid + " gave package"
- + pkg + " which is owned by uid " + ai.uid);
+ if (UserHandle.getAppId(ai.uid) != UserHandle.getAppId(uid)) {
+ throw new SecurityException(errorLog);
}
} catch (NameNotFoundException ex) {
- throw new SecurityException("Unknown package " + pkg + "\n" + ex);
+ throw new SecurityException(errorLog);
}
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 557e52596b..f03070fa19 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -31,13 +31,16 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
import android.os.RegistrantList;
import android.os.RemoteException;
import android.os.TelephonyServiceManager.ServiceRegisterer;
@@ -58,9 +61,11 @@ import android.telephony.UiccAccessRule;
import android.telephony.UiccSlotInfo;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
+import android.util.EventLog;
import android.util.LocalLog;
import android.util.Log;
+import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.dataconnection.DataEnabledOverride;
@@ -68,6 +73,7 @@ import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.telephony.util.TelephonyUtils;
@@ -89,6 +95,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Implementation of the ISub interface.
@@ -140,12 +147,12 @@ public class SubscriptionController extends ISub.Stub {
return flag;
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final Object mLock = new Object();
/** The singleton instance. */
protected static SubscriptionController sInstance = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Context mContext;
protected TelephonyManager mTelephonyManager;
protected UiccController mUiccController;
@@ -273,10 +280,10 @@ public class SubscriptionController extends ISub.Stub {
}
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int[] colorArr;
private long mLastISubServiceRegTime;
private RegistrantList mUiccAppsEnableChangeRegList = new RegistrantList();
@@ -293,7 +300,9 @@ public class SubscriptionController extends ISub.Stub {
SubscriptionManager.DISPLAY_NAME,
SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES,
SubscriptionManager.UICC_APPLICATIONS_ENABLED,
- SubscriptionManager.IMS_RCS_UCE_ENABLED));
+ SubscriptionManager.IMS_RCS_UCE_ENABLED,
+ SubscriptionManager.CROSS_SIM_CALLING_ENABLED
+ ));
public static SubscriptionController init(Context c) {
synchronized (SubscriptionController.class) {
@@ -306,7 +315,7 @@ public class SubscriptionController extends ISub.Stub {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static SubscriptionController getInstance() {
if (sInstance == null) {
Log.wtf(LOG_TAG, "getInstance null");
@@ -354,6 +363,29 @@ public class SubscriptionController extends ISub.Stub {
invalidateActiveDataSubIdCaches();
invalidateSlotIndexCaches();
+ mContext.getContentResolver().registerContentObserver(
+ SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI, false,
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri.equals(SubscriptionManager.SIM_INFO_SUW_RESTORE_CONTENT_URI)) {
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
+
+ SubscriptionManager subManager = SubscriptionManager.from(mContext);
+ for (SubscriptionInfo subInfo : getActiveSubscriptionInfoList(
+ mContext.getOpPackageName(), mContext.getAttributionTag())) {
+ if (SubscriptionController.getInstance()
+ .isActiveSubId(subInfo.getSubscriptionId())) {
+ ImsManager imsManager = ImsManager.getInstance(mContext,
+ subInfo.getSimSlotIndex());
+ imsManager.updateImsServiceConfig();
+ }
+ }
+ }
+ }
+ });
+
if (DBG) logdl("[SubscriptionController] init by Context");
}
@@ -365,7 +397,7 @@ public class SubscriptionController extends ISub.Stub {
sendDefaultChangedBroadcast(SubscriptionManager.getDefaultSubscriptionId());
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isSubInfoReady() {
return SubscriptionInfoUpdater.isSubInfoInitialized();
}
@@ -409,7 +441,7 @@ public class SubscriptionController extends ISub.Stub {
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void enforceModifyPhoneState(String message) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE, message);
@@ -426,10 +458,10 @@ public class SubscriptionController extends ISub.Stub {
* SecurityException.
*/
private boolean hasSubscriberIdentifierAccess(int subId, String callingPackage,
- String callingFeatureId, String message) {
+ String callingFeatureId, String message, boolean reportFailure) {
try {
return TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mContext, subId,
- callingPackage, callingFeatureId, message);
+ callingPackage, callingFeatureId, message, reportFailure);
} catch (SecurityException e) {
// A SecurityException indicates that the calling package is targeting at least the
// minimum level that enforces identifier access restrictions and the new access
@@ -466,7 +498,7 @@ public class SubscriptionController extends ISub.Stub {
/**
* Notify the changed of subscription info.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void notifySubscriptionInfoChanged() {
TelephonyRegistryManager trm =
(TelephonyRegistryManager)
@@ -488,9 +520,6 @@ public class SubscriptionController extends ISub.Stub {
notifyOpportunisticSubscriptionInfoChanged();
}
metrics.updateActiveSubscriptionInfoList(subInfos);
- for (Phone phone : PhoneFactory.getPhones()) {
- phone.getVoiceCallSessionStats().onActiveSubscriptionInfoChanged(subInfos);
- }
}
/**
@@ -498,7 +527,7 @@ public class SubscriptionController extends ISub.Stub {
* @param cursor
* @return the query result of desired SubInfoRecord
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private SubscriptionInfo getSubInfoRecord(Cursor cursor) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow(
SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
@@ -620,7 +649,7 @@ public class SubscriptionController extends ISub.Stub {
* @param queryKey query key content
* @return Array list of queried result from database
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<SubscriptionInfo> getSubInfo(String selection, Object queryKey) {
if (VDBG) logd("selection:" + selection + ", querykey: " + queryKey);
String[] selectionArgs = null;
@@ -683,7 +712,7 @@ public class SubscriptionController extends ISub.Stub {
}
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
return getActiveSubscriptionInfo(subId, callingPackage, null);
}
@@ -739,16 +768,19 @@ public class SubscriptionController extends ISub.Stub {
* @hide
*/
public SubscriptionInfo getSubscriptionInfo(int subId) {
- // check cache for active subscriptions first, before querying db
- for (SubscriptionInfo subInfo : mCacheActiveSubInfoList) {
- if (subInfo.getSubscriptionId() == subId) {
- return subInfo;
+ synchronized (mSubInfoListLock) {
+ // check cache for active subscriptions first, before querying db
+ for (SubscriptionInfo subInfo : mCacheActiveSubInfoList) {
+ if (subInfo.getSubscriptionId() == subId) {
+ return subInfo;
+ }
}
- }
- // check cache for opportunistic subscriptions too, before querying db
- for (SubscriptionInfo subInfo : mCacheOpportunisticSubInfoList) {
- if (subInfo.getSubscriptionId() == subId) {
- return subInfo;
+
+ // check cache for opportunistic subscriptions too, before querying db
+ for (SubscriptionInfo subInfo : mCacheOpportunisticSubInfoList) {
+ if (subInfo.getSubscriptionId() == subId) {
+ return subInfo;
+ }
}
}
@@ -885,25 +917,35 @@ public class SubscriptionController extends ISub.Stub {
// Now that all security checks passes, perform the operation as ourselves.
final long identity = Binder.clearCallingIdentity();
+ List<SubscriptionInfo> subList;
try {
- List<SubscriptionInfo> subList = null;
subList = getSubInfo(null, null);
- if (subList != null) {
- if (VDBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
- } else {
- if (VDBG) logd("[getAllSubInfoList]- no info return");
- }
- return subList;
} finally {
Binder.restoreCallingIdentity(identity);
}
+ if (subList != null) {
+ if (VDBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
+ subList.stream().map(
+ subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo,
+ callingPackage, callingFeatureId, "getAllSubInfoList"))
+ .collect(Collectors.toList());
+ } else {
+ if (VDBG) logd("[getAllSubInfoList]- no info return");
+ }
+ return subList;
+ }
+
+ private List<SubscriptionInfo> makeCacheListCopyWithLock(List<SubscriptionInfo> cacheSubList) {
+ synchronized (mSubInfoListLock) {
+ return new ArrayList<>(cacheSubList);
+ }
}
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
return getSubscriptionInfoListFromCacheHelper(callingPackage, null,
- mCacheActiveSubInfoList);
+ makeCacheListCopyWithLock(mCacheActiveSubInfoList));
}
/**
@@ -917,7 +959,7 @@ public class SubscriptionController extends ISub.Stub {
public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage,
String callingFeatureId) {
return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId,
- mCacheActiveSubInfoList);
+ makeCacheListCopyWithLock(mCacheActiveSubInfoList));
}
/**
@@ -928,13 +970,13 @@ public class SubscriptionController extends ISub.Stub {
public void refreshCachedActiveSubscriptionInfoList() {
boolean opptSubListChanged;
- synchronized (mSubInfoListLock) {
- List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
- SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
- + SubscriptionManager.SUBSCRIPTION_TYPE + "="
- + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM,
- null);
+ List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
+ SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
+ + SubscriptionManager.SUBSCRIPTION_TYPE + "="
+ + SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM,
+ null);
+ synchronized (mSubInfoListLock) {
if (activeSubscriptionInfoList != null) {
// Log when active sub info changes.
if (mCacheActiveSubInfoList.size() != activeSubscriptionInfoList.size()
@@ -949,10 +991,6 @@ public class SubscriptionController extends ISub.Stub {
logd("activeSubscriptionInfoList is null.");
mCacheActiveSubInfoList.clear();
}
-
- // Refresh cached opportunistic sub list and detect whether it's changed.
- refreshCachedOpportunisticSubscriptionInfoList();
-
if (DBG_CACHE) {
if (!mCacheActiveSubInfoList.isEmpty()) {
for (SubscriptionInfo si : mCacheActiveSubInfoList) {
@@ -964,10 +1002,13 @@ public class SubscriptionController extends ISub.Stub {
}
}
}
+
+ // Refresh cached opportunistic sub list and detect whether it's changed.
+ refreshCachedOpportunisticSubscriptionInfoList();
}
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getActiveSubInfoCount(String callingPackage) {
return getActiveSubInfoCount(callingPackage, null);
}
@@ -1045,12 +1086,18 @@ public class SubscriptionController extends ISub.Stub {
@Override
public List<SubscriptionInfo> getAvailableSubscriptionInfoList(String callingPackage,
String callingFeatureId) {
- // This API isn't public, so no need to provide a valid subscription ID - we're not worried
- // about carrier-privileged callers not having access.
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
- callingFeatureId, "getAvailableSubscriptionInfoList")) {
- throw new SecurityException("Need READ_PHONE_STATE to call "
+ try {
+ enforceReadPrivilegedPhoneState("getAvailableSubscriptionInfoList");
+ } catch (SecurityException e) {
+ try {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE, null);
+ // If caller doesn't have READ_PRIVILEGED_PHONE_STATE permission but only
+ // has READ_PHONE_STATE permission, log this event.
+ EventLog.writeEvent(0x534e4554, "185235454", Binder.getCallingUid());
+ } catch (SecurityException ex) {
+ // Ignore
+ }
+ throw new SecurityException("Need READ_PRIVILEGED_PHONE_STATE to call "
+ " getAvailableSubscriptionInfoList");
}
@@ -1530,12 +1577,14 @@ public class SubscriptionController extends ISub.Stub {
// validate the given info - does it exist in the active subscription list
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
int slotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- for (SubscriptionInfo info : mCacheActiveSubInfoList) {
- if ((info.getSubscriptionType() == subscriptionType)
- && info.getIccId().equalsIgnoreCase(uniqueId)) {
- subId = info.getSubscriptionId();
- slotIndex = info.getSimSlotIndex();
- break;
+ synchronized (mSubInfoListLock) {
+ for (SubscriptionInfo info : mCacheActiveSubInfoList) {
+ if ((info.getSubscriptionType() == subscriptionType)
+ && info.getIccId().equalsIgnoreCase(uniqueId)) {
+ subId = info.getSubscriptionId();
+ slotIndex = info.getSimSlotIndex();
+ break;
+ }
}
}
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -1616,12 +1665,20 @@ public class SubscriptionController extends ISub.Stub {
* Insert an empty SubInfo record into the database.
*
* <p>NOTE: This is not accessible to external processes, so it does not need a permission
- * check. It is only intended for use by {@link SubscriptionInfoUpdater}.
+ * check. It is only intended for use by {@link SubscriptionInfoUpdater}. If there is a
+ * subscription record exist with the same ICCID, no new empty record will be created.
*
- * <p>Precondition: No record exists with this iccId.
+ * @return the URL of the newly created row. Return <code>null</code> if no new empty record is
+ * created.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @Nullable
public Uri insertEmptySubInfoRecord(String iccId, int slotIndex) {
+ if (getSubInfoForIccId(iccId) != null) {
+ loge("insertEmptySubInfoRecord: Found existing record by ICCID. Do not create a "
+ + "new empty entry.");
+ return null;
+ }
return insertEmptySubInfoRecord(iccId, null, slotIndex,
SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
}
@@ -1650,6 +1707,9 @@ public class SubscriptionController extends ISub.Stub {
}
}
}
+ value.put(SubscriptionManager.ALLOWED_NETWORK_TYPES,
+ "user=" + RadioAccessFamily.getRafFromNetworkType(
+ RILConstants.PREFERRED_NETWORK_MODE));
Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
@@ -1667,7 +1727,7 @@ public class SubscriptionController extends ISub.Stub {
* @param spn spn to be included in carrier text
* @return true if carrier text is set, false otherwise
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,
String spn) {
synchronized (mLock) {
@@ -1717,17 +1777,26 @@ public class SubscriptionController extends ISub.Stub {
// Now that all security checks passes, perform the operation as ourselves.
final long identity = Binder.clearCallingIdentity();
try {
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.CARRIER_NAME, text);
-
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+ boolean update = true;
+ int result = 0;
+ SubscriptionInfo subInfo = getSubscriptionInfo(subId);
+ if (subInfo != null) {
+ update = !TextUtils.equals(text, subInfo.getCarrierName());
+ }
+ if (update) {
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.CARRIER_NAME, text);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
+ result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- notifySubscriptionInfoChanged();
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
+ } else {
+ if (DBG) logd("[setCarrierText]: no value update");
+ }
return result;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1786,6 +1855,66 @@ public class SubscriptionController extends ISub.Stub {
}
/**
+ * Validate whether the NAME_SOURCE_SIM_PNN, NAME_SOURCE_SIM_SPN and
+ * NAME_SOURCE_CARRIER exist or not.
+ */
+ @VisibleForTesting
+ public boolean isExistingNameSourceStillValid(SubscriptionInfo subInfo) {
+
+ int subId = subInfo.getSubscriptionId();
+ int phoneId = getPhoneId(subInfo.getSubscriptionId());
+
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone == null) {
+ return true;
+ }
+
+ String spn;
+
+ switch (subInfo.getNameSource()) {
+ case SubscriptionManager.NAME_SOURCE_SIM_PNN:
+ String pnn = phone.getPlmn();
+ return !TextUtils.isEmpty(pnn);
+ case SubscriptionManager.NAME_SOURCE_SIM_SPN:
+ spn = getServiceProviderName(phoneId);
+ return !TextUtils.isEmpty(spn);
+ case SubscriptionManager.NAME_SOURCE_CARRIER:
+ // Can not validate eSIM since it should not override with a lower priority source
+ // if the name is actually coming from eSIM and not from carrier config.
+ if (subInfo.isEmbedded()) {
+ return true;
+ }
+ CarrierConfigManager configLoader =
+ mContext.getSystemService(CarrierConfigManager.class);
+ PersistableBundle config =
+ configLoader.getConfigForSubId(subId);
+ if (config == null) {
+ return true;
+ }
+ boolean isCarrierNameOverride = config.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
+ String carrierName = config.getString(
+ CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+ spn = getServiceProviderName(phoneId);
+ return isCarrierNameOverride
+ || (TextUtils.isEmpty(spn) && !TextUtils.isEmpty(carrierName));
+ case SubscriptionManager.NAME_SOURCE_CARRIER_ID:
+ case SubscriptionManager.NAME_SOURCE_USER_INPUT:
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ public String getServiceProviderName(int phoneId) {
+ UiccProfile profile = mUiccController.getUiccProfileForPhone(phoneId);
+ if (profile == null) {
+ return null;
+ }
+ return profile.getServiceProviderName();
+ }
+
+ /**
* Set display name by simInfo index with name source
* @param displayName the display name of SIM card
* @param subId the unique SubInfoRecord index in database
@@ -1810,12 +1939,17 @@ public class SubscriptionController extends ISub.Stub {
// if there is no sub in the db, return 0 since subId does not exist in db
if (allSubInfo == null || allSubInfo.isEmpty()) return 0;
for (SubscriptionInfo subInfo : allSubInfo) {
+ int subInfoNameSource = subInfo.getNameSource();
+ boolean isHigherPriority = (getNameSourcePriority(subInfoNameSource)
+ > getNameSourcePriority(nameSource));
+ boolean isEqualPriorityAndName = (getNameSourcePriority(subInfoNameSource)
+ == getNameSourcePriority(nameSource))
+ && (TextUtils.equals(displayName, subInfo.getDisplayName()));
if (subInfo.getSubscriptionId() == subId
- && (getNameSourcePriority(subInfo.getNameSource())
- > getNameSourcePriority(nameSource)
- || (displayName != null && displayName.equals(subInfo.getDisplayName())))) {
- logd("Name source " + subInfo.getNameSource() + "'s priority "
- + getNameSourcePriority(subInfo.getNameSource()) + " is greater than "
+ && isExistingNameSourceStillValid(subInfo)
+ && (isHigherPriority || isEqualPriorityAndName)) {
+ logd("Name source " + subInfoNameSource + "'s priority "
+ + getNameSourcePriority(subInfoNameSource) + " is greater than "
+ "name source " + nameSource + "'s priority "
+ getNameSourcePriority(nameSource) + ", return now.");
return 0;
@@ -1856,7 +1990,8 @@ public class SubscriptionController extends ISub.Stub {
// we don't care about the result (hence 0 requestCode and no action
// specified on the intent).
PendingIntent.getService(
- mContext, 0 /* requestCode */, new Intent(), 0 /* flags */));
+ mContext, 0 /* requestCode */, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE /* flags */));
}
int result = updateDatabase(value, subId, true);
@@ -1888,30 +2023,35 @@ public class SubscriptionController extends ISub.Stub {
final long identity = Binder.clearCallingIdentity();
try {
validateSubId(subId);
- int result;
+ int result = 0;
int phoneId = getPhoneId(subId);
if (number == null || phoneId < 0 ||
phoneId >= mTelephonyManager.getPhoneCount()) {
- if (DBG) logd("[setDispalyNumber]- fail");
+ if (DBG) logd("[setDisplayNumber]- fail");
return -1;
}
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.NUMBER, number);
-
- // This function had a call to update number on the SIM (Phone.setLine1Number()) but
- // that was removed as there doesn't seem to be a reason for that. If it is added
- // back, watch out for deadlocks.
-
- result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- if (DBG) logd("[setDisplayNumber]- update result :" + result);
- notifySubscriptionInfoChanged();
-
+ boolean update = true;
+ SubscriptionInfo subInfo = getSubscriptionInfo(subId);
+ if (subInfo != null) {
+ update = !TextUtils.equals(subInfo.getNumber(), number);
+ }
+ if (update) {
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.NUMBER, number);
+
+ // This function had a call to update number on the SIM (Phone.setLine1Number()) but
+ // that was removed as there doesn't seem to be a reason for that. If it is added
+ // back, watch out for deadlocks.
+ result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+ if (DBG) logd("[setDisplayNumber]- update result :" + result);
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
+ } else {
+ if (DBG) logd("[setDisplayNumber]: no value update");
+ }
return result;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1932,21 +2072,36 @@ public class SubscriptionController extends ISub.Stub {
return;
}
- String formattedEhplmns = ehplmns == null ? "" : String.join(",", ehplmns);
- String formattedHplmns = hplmns == null ? "" : String.join(",", hplmns);
-
- ContentValues value = new ContentValues(2);
- value.put(SubscriptionManager.EHPLMNS, formattedEhplmns);
- value.put(SubscriptionManager.HPLMNS, formattedHplmns);
-
- int count = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- if (DBG) logd("[setAssociatedPlmns]- update result :" + count);
- notifySubscriptionInfoChanged();
+ // remove trailing empty strings which will also get stripped from
+ // SubscriptionInfo.getEhplmns() and SubscriptionInfo.getHplmns()
+ String formattedEhplmns = ehplmns == null ? "" :
+ Arrays.stream(ehplmns).filter(s -> s != null && !s.isEmpty())
+ .collect(Collectors.joining(","));
+ String formattedHplmns = hplmns == null ? "" :
+ Arrays.stream(hplmns).filter(s -> s != null && !s.isEmpty())
+ .collect(Collectors.joining(","));
+ boolean noChange = false;
+ SubscriptionInfo subInfo = getSubscriptionInfo(subId);
+ if (subInfo != null) {
+ noChange = (ehplmns == null && subInfo.getEhplmns().isEmpty())
+ || String.join(",", subInfo.getEhplmns()).equals(formattedEhplmns);
+ noChange = noChange && (hplmns == null && subInfo.getHplmns().isEmpty())
+ || String.join(",", subInfo.getHplmns()).equals(formattedHplmns);
+ }
+ if (!noChange) {
+ ContentValues value = new ContentValues(2);
+ value.put(SubscriptionManager.EHPLMNS, formattedEhplmns);
+ value.put(SubscriptionManager.HPLMNS, formattedHplmns);
+
+ int count = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+ if (DBG) logd("[setAssociatedPlmns]- update result :" + count);
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
+ } else {
+ if (DBG) logd("[setAssociatedPlmns]+ subId:" + subId + "no value update");
+ }
}
/**
@@ -1986,6 +2141,82 @@ public class SubscriptionController extends ISub.Stub {
}
}
+ /**
+ * Set device to device status sharing preference
+ * @param sharing the sharing preference to set
+ * @param subId
+ * @return the number of records updated
+ */
+ @Override
+ public int setDeviceToDeviceStatusSharing(int sharing, int subId) {
+ if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " subId:" + subId);
+
+ enforceModifyPhoneState("setDeviceToDeviceStatusSharing");
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ validateSubId(subId);
+ if (sharing < 0) {
+ if (DBG) logd("[setDeviceToDeviceStatusSharing]- fail");
+ return -1;
+ }
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.D2D_STATUS_SHARING, sharing);
+ if (DBG) logd("[setDeviceToDeviceStatusSharing]- sharing:" + sharing + " set");
+
+ int result = updateDatabase(value, subId, true);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
+ notifySubscriptionInfoChanged();
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set contacts that allow device to device status sharing.
+ * @param contacts contacts to set
+ * @param subscriptionId
+ * @return the number of records updated
+ */
+ @Override
+ public int setDeviceToDeviceStatusSharingContacts(String contacts, int subscriptionId) {
+ if (DBG) {
+ logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts
+ + " subId:" + subscriptionId);
+ }
+
+ enforceModifyPhoneState("setDeviceToDeviceStatusSharingContacts");
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ validateSubId(subscriptionId);
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS, contacts);
+ if (DBG) {
+ logd("[setDeviceToDeviceStatusSharingContacts]- contacts:" + contacts
+ + " set");
+ }
+
+ int result = updateDatabase(value, subscriptionId, true);
+
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+
+ notifySubscriptionInfoChanged();
+
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
public void syncGroupedSetting(int refSubId) {
logd("syncGroupedSetting");
try (Cursor cursor = mContext.getContentResolver().query(
@@ -2022,6 +2253,7 @@ public class SubscriptionController extends ISub.Stub {
case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
case SubscriptionManager.DATA_ROAMING:
case SubscriptionManager.IMS_RCS_UCE_ENABLED:
+ case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
values.put(propKey, cursor.getInt(columnIndex));
break;
case SubscriptionManager.DISPLAY_NAME:
@@ -2069,16 +2301,25 @@ public class SubscriptionController extends ISub.Stub {
final long identity = Binder.clearCallingIdentity();
try {
validateSubId(subId);
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.CARRIER_ID, carrierId);
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
+ int result = 0;
+ boolean update = true;
+ SubscriptionInfo subInfo = getSubscriptionInfo(subId);
+ if (subInfo != null) {
+ update = subInfo.getCarrierId() != carrierId;
+ }
+ if (update) {
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.CARRIER_ID, carrierId);
+ result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
- notifySubscriptionInfoChanged();
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
+ } else {
+ if (DBG) logd("[setCarrierId]: no value update");
+ }
return result;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2102,42 +2343,73 @@ public class SubscriptionController extends ISub.Stub {
} catch (NumberFormatException e) {
loge("[setMccMnc] - couldn't parse mcc/mnc: " + mccMnc);
}
- if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
- ContentValues value = new ContentValues(4);
- value.put(SubscriptionManager.MCC, mcc);
- value.put(SubscriptionManager.MNC, mnc);
- value.put(SubscriptionManager.MCC_STRING, mccString);
- value.put(SubscriptionManager.MNC_STRING, mncString);
-
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
+ SubscriptionInfo subInfo = getSubscriptionInfo(subId);
+ // check if there are any update
+ boolean update = true;
+ if (subInfo != null) {
+ update = (subInfo.getMcc() != mcc) || (subInfo.getMnc() != mnc)
+ || !mccString.equals(subInfo.getMccString())
+ || !mncString.equals(subInfo.getMncString());
+ }
+ int result = 0;
+ if (update) {
+ ContentValues value = new ContentValues(4);
+ value.put(SubscriptionManager.MCC, mcc);
+ value.put(SubscriptionManager.MNC, mnc);
+ value.put(SubscriptionManager.MCC_STRING, mccString);
+ value.put(SubscriptionManager.MNC_STRING, mncString);
+ result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+ if (DBG) logd("[setMccMnc]+ mcc/mnc:" + mcc + "/" + mnc + " subId:" + subId);
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
+ } else {
+ if (DBG) logd("[setMccMnc] - no values update");
+ }
return result;
}
/**
+ * Scrub given IMSI on production builds.
+ */
+ private String scrubImsi(String imsi) {
+ if (Build.IS_ENG) {
+ return imsi;
+ } else if (imsi != null) {
+ return imsi.substring(0, Math.min(6, imsi.length())) + "...";
+ } else {
+ return "null";
+ }
+ }
+
+ /**
* Set IMSI by subscription ID
* @param imsi IMSI (International Mobile Subscriber Identity)
* @return the number of records updated
*/
public int setImsi(String imsi, int subId) {
- if (DBG) logd("[setImsi]+ imsi:" + imsi + " subId:" + subId);
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.IMSI, imsi);
-
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
+ if (DBG) logd("[setImsi]+ imsi:" + scrubImsi(imsi) + " subId:" + subId);
+ boolean update = true;
+ int result = 0;
+ SubscriptionInfo subInfo = getSubscriptionInfo(subId);
+ if (subInfo != null) {
+ update = !TextUtils.equals(getImsiPrivileged(subId),imsi);
+ }
- notifySubscriptionInfoChanged();
+ if (update) {
+ ContentValues value = new ContentValues(1);
+ value.put(SubscriptionManager.IMSI, imsi);
+ result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
+ notifySubscriptionInfoChanged();
+ } else {
+ if (DBG) logd("[setImsi]: no value update");
+ }
return result;
}
@@ -2231,16 +2503,25 @@ public class SubscriptionController extends ISub.Stub {
*/
public int setCountryIso(String iso, int subId) {
if (DBG) logd("[setCountryIso]+ iso:" + iso + " subId:" + subId);
- ContentValues value = new ContentValues();
- value.put(SubscriptionManager.ISO_COUNTRY_CODE, iso);
-
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+ boolean update = true;
+ int result = 0;
+ SubscriptionInfo subInfo = getSubscriptionInfo(subId);
+ if (subInfo != null) {
+ update = !TextUtils.equals(subInfo.getCountryIso(), iso);
+ }
+ if (update) {
+ ContentValues value = new ContentValues();
+ value.put(SubscriptionManager.ISO_COUNTRY_CODE, iso);
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
+ result = mContext.getContentResolver().update(
+ SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
+ // Refresh the Cache of Active Subscription Info List
+ refreshCachedActiveSubscriptionInfoList();
- notifySubscriptionInfoChanged();
+ notifySubscriptionInfoChanged();
+ } else {
+ if (DBG) logd("[setCountryIso]: no value update");
+ }
return result;
}
@@ -2281,7 +2562,7 @@ public class SubscriptionController extends ISub.Stub {
* Return the subId for specified slot Id.
* @deprecated
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
@Deprecated
public int[] getSubId(int slotIndex) {
@@ -2330,7 +2611,7 @@ public class SubscriptionController extends ISub.Stub {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public int getPhoneId(int subId) {
if (VDBG) printStackTrace("[getPhoneId] subId=" + subId);
@@ -2409,13 +2690,13 @@ public class SubscriptionController extends ISub.Stub {
Rlog.v(LOG_TAG, msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void logdl(String msg) {
logd(msg);
mLocalLog.log(msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void logd(String msg) {
Rlog.d(LOG_TAG, msg);
}
@@ -2425,12 +2706,12 @@ public class SubscriptionController extends ISub.Stub {
mLocalLog.log(msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loge(String msg) {
Rlog.e(LOG_TAG, msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public int getDefaultSubId() {
int subId;
@@ -2450,7 +2731,7 @@ public class SubscriptionController extends ISub.Stub {
return subId;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setDefaultSmsSubId(int subId) {
enforceModifyPhoneState("setDefaultSmsSubId");
@@ -2472,7 +2753,7 @@ public class SubscriptionController extends ISub.Stub {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public int getDefaultSmsSubId() {
int subId = Settings.Global.getInt(mContext.getContentResolver(),
@@ -2482,7 +2763,7 @@ public class SubscriptionController extends ISub.Stub {
return subId;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setDefaultVoiceSubId(int subId) {
enforceModifyPhoneState("setDefaultVoiceSubId");
@@ -2531,7 +2812,7 @@ public class SubscriptionController extends ISub.Stub {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public int getDefaultVoiceSubId() {
int subId = Settings.Global.getInt(mContext.getContentResolver(),
@@ -2541,7 +2822,7 @@ public class SubscriptionController extends ISub.Stub {
return subId;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public int getDefaultDataSubId() {
int subId = Settings.Global.getInt(mContext.getContentResolver(),
@@ -2551,7 +2832,7 @@ public class SubscriptionController extends ISub.Stub {
return subId;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setDefaultDataSubId(int subId) {
enforceModifyPhoneState("setDefaultDataSubId");
@@ -2605,7 +2886,7 @@ public class SubscriptionController extends ISub.Stub {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void broadcastDefaultDataSubIdChanged(int subId) {
// Broadcast an Intent for default data sub change
if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
@@ -2619,7 +2900,7 @@ public class SubscriptionController extends ISub.Stub {
* sub is set as default subId. If two or more sub's are active
* the first sub is set as default subscription
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void setDefaultFallbackSubId(int subId, int subscriptionType) {
if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
@@ -2678,7 +2959,7 @@ public class SubscriptionController extends ISub.Stub {
// FIXME: We need we should not be assuming phoneId == slotIndex as it will not be true
// when there are multiple subscriptions per sim and probably for other reasons.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getSubIdUsingPhoneId(int phoneId) {
int[] subIds = getSubId(phoneId);
if (subIds == null || subIds.length == 0) {
@@ -2725,7 +3006,7 @@ public class SubscriptionController extends ISub.Stub {
return subList;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void validateSubId(int subId) {
if (DBG) logd("validateSubId subId: " + subId);
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -2752,11 +3033,13 @@ public class SubscriptionController extends ISub.Stub {
}
private boolean isSubscriptionVisible(int subId) {
- for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) {
- if (info.getSubscriptionId() == subId) {
- // If group UUID is null, it's stand alone opportunistic profile. So it's visible.
- // otherwise, it's bundled opportunistic profile, and is not visible.
- return info.getGroupUuid() == null;
+ synchronized (mSubInfoListLock) {
+ for (SubscriptionInfo info : mCacheOpportunisticSubInfoList) {
+ if (info.getSubscriptionId() == subId) {
+ // If group UUID is null, it's stand alone opportunistic profile. So it's
+ // visible. Otherwise, it's bundled opportunistic profile, and is not visible.
+ return info.getGroupUuid() == null;
+ }
}
}
@@ -2804,7 +3087,7 @@ public class SubscriptionController extends ISub.Stub {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated // This should be moved into isActiveSubId(int, String)
public boolean isActiveSubId(int subId) {
boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
@@ -2907,10 +3190,12 @@ public class SubscriptionController extends ISub.Stub {
case SubscriptionManager.WFC_IMS_ROAMING_MODE:
case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
case SubscriptionManager.IMS_RCS_UCE_ENABLED:
+ case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
+ case SubscriptionManager.VOIMS_OPT_IN_STATUS:
value.put(propKey, Integer.parseInt(propValue));
break;
case SubscriptionManager.ALLOWED_NETWORK_TYPES:
- value.put(propKey, Long.parseLong(propValue));
+ value.put(propKey, propValue);
break;
default:
if (DBG) logd("Invalid column name");
@@ -2979,10 +3264,14 @@ public class SubscriptionController extends ISub.Stub {
case SubscriptionManager.WFC_IMS_ROAMING_MODE:
case SubscriptionManager.WFC_IMS_ROAMING_ENABLED:
case SubscriptionManager.IMS_RCS_UCE_ENABLED:
+ case SubscriptionManager.CROSS_SIM_CALLING_ENABLED:
case SubscriptionManager.IS_OPPORTUNISTIC:
case SubscriptionManager.GROUP_UUID:
case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
case SubscriptionManager.ALLOWED_NETWORK_TYPES:
+ case SubscriptionManager.VOIMS_OPT_IN_STATUS:
+ case SubscriptionManager.D2D_STATUS_SHARING:
+ case SubscriptionManager.D2D_STATUS_SHARING_SELECTED_CONTACTS:
resultValue = cursor.getString(0);
break;
default:
@@ -3235,8 +3524,8 @@ public class SubscriptionController extends ISub.Stub {
@Override
public List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage,
String callingFeatureId) {
- return getSubscriptionInfoListFromCacheHelper(
- callingPackage, callingFeatureId, mCacheOpportunisticSubInfoList);
+ return getSubscriptionInfoListFromCacheHelper(callingPackage, callingFeatureId,
+ makeCacheListCopyWithLock(mCacheOpportunisticSubInfoList));
}
/**
@@ -3622,6 +3911,30 @@ public class SubscriptionController extends ISub.Stub {
}
+ /**
+ * Check if the passed in phoneId has a sub that belongs to the same group as the sub
+ * corresponding to the passed in iccid.
+ * @param phoneId phone id to check
+ * @param iccid ICCID to check
+ * @return true if sub/group is the same, false otherwise
+ */
+ public boolean checkPhoneIdAndIccIdMatch(int phoneId, String iccid) {
+ int subId = getSubIdUsingPhoneId(phoneId);
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) return false;
+ ParcelUuid groupUuid = getGroupUuid(subId);
+ List<SubscriptionInfo> subInfoList;
+ if (groupUuid != null) {
+ subInfoList = getSubInfo(SubscriptionManager.GROUP_UUID
+ + "=\'" + groupUuid.toString() + "\'", null);
+ } else {
+ subInfoList = getSubInfo(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+ + "=" + subId, null);
+ }
+ return subInfoList != null && subInfoList.stream().anyMatch(
+ subInfo -> IccUtils.stripTrailingFs(subInfo.getIccId()).equals(
+ IccUtils.stripTrailingFs(iccid)));
+ }
+
public ParcelUuid getGroupUuid(int subId) {
ParcelUuid groupUuid;
List<SubscriptionInfo> subInfo = getSubInfo(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
@@ -3899,8 +4212,14 @@ public class SubscriptionController extends ISub.Stub {
}
}
- // Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
- // They are doing similar things except operating on different cache.
+ /**
+ * Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
+ * They are doing similar things except operating on different cache.
+ *
+ * NOTE: the cacheSubList passed in is a *copy* of mCacheActiveSubInfoList or
+ * mCacheOpportunisticSubInfoList, so mSubInfoListLock is not required to access it. Also, this
+ * method may modify cacheSubList depending on the permissions the caller has.
+ */
private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper(
String callingPackage, String callingFeatureId, List<SubscriptionInfo> cacheSubList) {
boolean canReadPhoneState = false;
@@ -3917,7 +4236,7 @@ public class SubscriptionController extends ISub.Stub {
if (canReadPhoneState) {
canReadIdentifiers = hasSubscriberIdentifierAccess(
SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
- callingFeatureId, "getSubscriptionInfoList");
+ callingFeatureId, "getSubscriptionInfoList", false);
canReadPhoneNumber = hasPhoneNumberAccess(
SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
callingFeatureId, "getSubscriptionInfoList");
@@ -3929,37 +4248,30 @@ public class SubscriptionController extends ISub.Stub {
// the identifier and phone number access checks are not required.
}
- synchronized (mSubInfoListLock) {
- // If the caller can read all phone state, just return the full list.
- if (canReadIdentifiers && canReadPhoneNumber) {
- return new ArrayList<>(cacheSubList);
- }
- // Filter the list to only include subscriptions which the caller can manage.
- List<SubscriptionInfo> subscriptions = new ArrayList<>(cacheSubList.size());
- for (SubscriptionInfo subscriptionInfo : cacheSubList) {
- int subId = subscriptionInfo.getSubscriptionId();
- boolean hasCarrierPrivileges = TelephonyPermissions.checkCarrierPrivilegeForSubId(
- mContext, subId);
- // If the caller does not have the READ_PHONE_STATE permission nor carrier
- // privileges then they cannot access the current subscription.
- if (!canReadPhoneState && !hasCarrierPrivileges) {
- continue;
- }
- // If the caller has carrier privileges then they are granted access to all
- // identifiers for their subscription.
- if (hasCarrierPrivileges) {
- subscriptions.add(subscriptionInfo);
- } else {
- // The caller does not have carrier privileges for this subId, filter the
- // identifiers in the subscription based on the results of the initial
- // permission checks.
- subscriptions.add(
- conditionallyRemoveIdentifiers(subscriptionInfo, canReadIdentifiers,
- canReadPhoneNumber));
- }
+ if (canReadIdentifiers && canReadPhoneNumber) {
+ return cacheSubList;
+ }
+ // Filter the list to only include subscriptions which the caller can manage.
+ for (int subIndex = cacheSubList.size() - 1; subIndex >= 0; subIndex--) {
+ SubscriptionInfo subscriptionInfo = cacheSubList.get(subIndex);
+
+ int subId = subscriptionInfo.getSubscriptionId();
+ boolean hasCarrierPrivileges = TelephonyPermissions.checkCarrierPrivilegeForSubId(
+ mContext, subId);
+ // If the caller has carrier privileges then they are granted access to all
+ // identifiers for their subscription.
+ if (hasCarrierPrivileges) continue;
+
+ cacheSubList.remove(subIndex);
+ if (canReadPhoneState) {
+ // The caller does not have carrier privileges for this subId, filter the
+ // identifiers in the subscription based on the results of the initial
+ // permission checks.
+ cacheSubList.add(subIndex, conditionallyRemoveIdentifiers(
+ subscriptionInfo, canReadIdentifiers, canReadPhoneNumber));
}
- return subscriptions;
}
+ return cacheSubList;
}
/**
@@ -3976,7 +4288,7 @@ public class SubscriptionController extends ISub.Stub {
SubscriptionInfo result = subInfo;
int subId = subInfo.getSubscriptionId();
boolean hasIdentifierAccess = hasSubscriberIdentifierAccess(subId, callingPackage,
- callingFeatureId, message);
+ callingFeatureId, message, true);
boolean hasPhoneNumberAccess = hasPhoneNumberAccess(subId, callingPackage, callingFeatureId,
message);
return conditionallyRemoveIdentifiers(subInfo, hasIdentifierAccess, hasPhoneNumberAccess);
@@ -4000,6 +4312,7 @@ public class SubscriptionController extends ISub.Stub {
if (!hasIdentifierAccess) {
result.clearIccId();
result.clearCardString();
+ result.clearGroupUuid();
}
if (!hasPhoneNumberAccess) {
result.clearNumber();
@@ -4074,14 +4387,13 @@ public class SubscriptionController extends ISub.Stub {
}
private void refreshCachedOpportunisticSubscriptionInfoList() {
+ List<SubscriptionInfo> subList = getSubInfo(
+ SubscriptionManager.IS_OPPORTUNISTIC + "=1 AND ("
+ + SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
+ + SubscriptionManager.IS_EMBEDDED + "=1)", null);
synchronized (mSubInfoListLock) {
List<SubscriptionInfo> oldOpptCachedList = mCacheOpportunisticSubInfoList;
- List<SubscriptionInfo> subList = getSubInfo(
- SubscriptionManager.IS_OPPORTUNISTIC + "=1 AND ("
- + SubscriptionManager.SIM_SLOT_INDEX + ">=0 OR "
- + SubscriptionManager.IS_EMBEDDED + "=1)", null);
-
if (subList != null) {
subList.sort(SUBSCRIPTION_INFO_COMPARATOR);
} else {
@@ -4116,9 +4428,11 @@ public class SubscriptionController extends ISub.Stub {
private boolean shouldDisableSubGroup(ParcelUuid groupUuid) {
if (groupUuid == null) return false;
- for (SubscriptionInfo activeInfo : mCacheActiveSubInfoList) {
- if (!activeInfo.isOpportunistic() && groupUuid.equals(activeInfo.getGroupUuid())) {
- return false;
+ synchronized (mSubInfoListLock) {
+ for (SubscriptionInfo activeInfo : mCacheActiveSubInfoList) {
+ if (!activeInfo.isOpportunistic() && groupUuid.equals(activeInfo.getGroupUuid())) {
+ return false;
+ }
}
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index dc842d2ced..9132e5e2b2 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -28,6 +28,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -35,9 +36,6 @@ import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
import android.service.carrier.CarrierService;
import android.service.euicc.EuiccProfileInfo;
@@ -73,7 +71,7 @@ import java.util.List;
*/
public class SubscriptionInfoUpdater extends Handler {
private static final String LOG_TAG = "SubscriptionInfoUpdater";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static final int SUPPORTED_MODEM_COUNT = TelephonyManager.getDefault()
.getSupportedModemCount();
@@ -102,11 +100,12 @@ public class SubscriptionInfoUpdater extends Handler {
// Key used to read/write the current IMSI. Updated on SIM_STATE_CHANGED - LOADED.
public static final String CURR_SUBID = "curr_subid";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static Context sContext = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected static String[] sIccId = new String[SUPPORTED_MODEM_COUNT];
+ protected SubscriptionController mSubscriptionController = null;
private static String[] sInactiveIccIds = new String[SUPPORTED_MODEM_COUNT];
private static int[] sSimCardState = new int[SUPPORTED_MODEM_COUNT];
private static int[] sSimApplicationState = new int[SUPPORTED_MODEM_COUNT];
@@ -116,7 +115,7 @@ public class SubscriptionInfoUpdater extends Handler {
private Handler mBackgroundHandler;
// The current foreground user ID.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mCurrentlyActiveUserId;
private CarrierServiceBindHelper mCarrierServiceBindHelper;
@@ -133,14 +132,13 @@ public class SubscriptionInfoUpdater extends Handler {
void run(boolean hasChanges);
}
- // TODO: The SubscriptionController instance should be passed in here from PhoneFactory
- // rather than invoking the static getter all over the place.
- @VisibleForTesting public SubscriptionInfoUpdater(Looper looper, Context context,
- CommandsInterface[] ci) {
+ @VisibleForTesting
+ public SubscriptionInfoUpdater(Looper looper, Context context, SubscriptionController sc) {
logd("Constructor invoked");
mBackgroundHandler = new Handler(looper);
sContext = context;
+ mSubscriptionController = sc;
mSubscriptionManager = SubscriptionManager.from(sContext);
mEuiccManager = (EuiccManager) sContext.getSystemService(Context.EUICC_SERVICE);
@@ -216,7 +214,7 @@ public class SubscriptionInfoUpdater extends Handler {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected boolean isAllIccIdQueryDone() {
for (int i = 0; i < TelephonyManager.getDefault().getActiveModemCount(); i++) {
UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(i);
@@ -307,7 +305,7 @@ public class SubscriptionInfoUpdater extends Handler {
cardIds.add(getCardIdFromPhoneId(msg.arg1));
updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
if (hasChanges) {
- SubscriptionController.getInstance().notifySubscriptionInfoChanged();
+ mSubscriptionController.notifySubscriptionInfoChanged();
}
});
handleSimNotReady(msg.arg1);
@@ -318,7 +316,7 @@ public class SubscriptionInfoUpdater extends Handler {
Runnable r = (Runnable) msg.obj;
updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
if (hasChanges) {
- SubscriptionController.getInstance().notifySubscriptionInfoChanged();
+ mSubscriptionController.notifySubscriptionInfoChanged();
}
if (r != null) {
r.run();
@@ -340,7 +338,6 @@ public class SubscriptionInfoUpdater extends Handler {
Context.TELEPHONY_SERVICE)).getActiveModemCount();
// For inactive modems, reset its states.
for (int phoneId = activeModemCount; phoneId < SUPPORTED_MODEM_COUNT; phoneId++) {
- SubscriptionController.getInstance().clearSubInfoRecord(phoneId);
sIccId[phoneId] = null;
sSimCardState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN;
sSimApplicationState[phoneId] = TelephonyManager.SIM_STATE_UNKNOWN;
@@ -410,19 +407,38 @@ public class SubscriptionInfoUpdater extends Handler {
protected void handleSimReady(int phoneId) {
List<Integer> cardIds = new ArrayList<>();
+ logd("handleSimReady: phoneId: " + phoneId);
+
+ if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
+ logd(" SIM" + (phoneId + 1) + " hot plug in");
+ sIccId[phoneId] = null;
+ }
+
+ // ICCID is not available in IccRecords by the time SIM Ready event received
+ // hence get ICCID from UiccSlot.
+ UiccSlot uiccSlot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
+ String iccId = (uiccSlot != null) ? IccUtils.stripTrailingFs(uiccSlot.getIccId()) : null;
+ if (!TextUtils.isEmpty(iccId)) {
+ // Call updateSubscriptionInfoByIccId() only if was
+ // not done earlier from SIM Locked event
+ if (sIccId[phoneId] == null) {
+ sIccId[phoneId] = iccId;
+
+ updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
+ }
+ }
cardIds.add(getCardIdFromPhoneId(phoneId));
updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
- if (hasChanges) {
- SubscriptionController.getInstance().notifySubscriptionInfoChanged();
- }
+ if (hasChanges) {
+ mSubscriptionController.notifySubscriptionInfoChanged();
+ }
});
broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_READY, null);
broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
}
-
protected void handleSimNotReady(int phoneId) {
logd("handleSimNotReady: phoneId: " + phoneId);
boolean isFinalState = false;
@@ -441,8 +457,10 @@ public class SubscriptionInfoUpdater extends Handler {
// At this phase, the subscription list is accessible. Treating NOT_READY
// as equivalent to ABSENT, once the rest of the system can handle it.
sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
- updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */);
+ } else {
+ sIccId[phoneId] = null;
}
+ updateSubscriptionInfoByIccId(phoneId, false /* updateEmbeddedSubs */);
broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_NOT_READY,
null);
@@ -459,8 +477,9 @@ public class SubscriptionInfoUpdater extends Handler {
// handleSimNotReady so this will be evaluated again.
UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
if (slot == null || slot.getIccId() == null) return false;
- SubscriptionInfo info = SubscriptionController.getInstance()
- .getSubInfoForIccId(IccUtils.stripTrailingFs(slot.getIccId()));
+ SubscriptionInfo info =
+ mSubscriptionController.getSubInfoForIccId(
+ IccUtils.stripTrailingFs(slot.getIccId()));
return info != null && !info.areUiccApplicationsEnabled();
}
@@ -484,11 +503,16 @@ public class SubscriptionInfoUpdater extends Handler {
logd("handleSimLoaded: IccID null");
return;
}
- sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId());
- updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
- List<SubscriptionInfo> subscriptionInfos = SubscriptionController.getInstance()
- .getSubInfoUsingSlotIndexPrivileged(phoneId);
+ // Call updateSubscriptionInfoByIccId() only if was not done earlier from SIM READY event
+ if (sIccId[phoneId] == null) {
+ sIccId[phoneId] = IccUtils.stripTrailingFs(records.getFullIccId());
+
+ updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
+ }
+
+ List<SubscriptionInfo> subscriptionInfos =
+ mSubscriptionController.getSubInfoUsingSlotIndexPrivileged(phoneId);
if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
loge("empty subinfo for phoneId: " + phoneId + "could not update ContentResolver");
} else {
@@ -499,10 +523,10 @@ public class SubscriptionInfoUpdater extends Handler {
String operator = tm.getSimOperatorNumeric(subId);
if (!TextUtils.isEmpty(operator)) {
- if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
+ if (subId == mSubscriptionController.getDefaultSubId()) {
MccTable.updateMccMncConfiguration(sContext, operator);
}
- SubscriptionController.getInstance().setMccMnc(operator, subId);
+ mSubscriptionController.setMccMnc(operator, subId);
} else {
logd("EVENT_RECORDS_LOADED Operator name is null");
}
@@ -510,25 +534,25 @@ public class SubscriptionInfoUpdater extends Handler {
String iso = tm.getSimCountryIsoForPhone(phoneId);
if (!TextUtils.isEmpty(iso)) {
- SubscriptionController.getInstance().setCountryIso(iso, subId);
+ mSubscriptionController.setCountryIso(iso, subId);
} else {
logd("EVENT_RECORDS_LOADED sim country iso is null");
}
String msisdn = tm.getLine1Number(subId);
if (msisdn != null) {
- SubscriptionController.getInstance().setDisplayNumber(msisdn, subId);
+ mSubscriptionController.setDisplayNumber(msisdn, subId);
}
String imsi = tm.createForSubscriptionId(subId).getSubscriberId();
if (imsi != null) {
- SubscriptionController.getInstance().setImsi(imsi, subId);
+ mSubscriptionController.setImsi(imsi, subId);
}
String[] ehplmns = records.getEhplmns();
String[] hplmns = records.getPlmnsFromHplmnActRecord();
if (ehplmns != null || hplmns != null) {
- SubscriptionController.getInstance().setAssociatedPlmns(ehplmns, hplmns, subId);
+ mSubscriptionController.setAssociatedPlmns(ehplmns, hplmns, subId);
}
/* Update preferred network type and network selection mode on SIM change.
@@ -539,35 +563,10 @@ public class SubscriptionInfoUpdater extends Handler {
int storedSubId = sp.getInt(CURR_SUBID + phoneId, -1);
if (storedSubId != subId) {
- int networkType = Settings.Global.getInt(
- PhoneFactory.getPhone(phoneId).getContext().getContentResolver(),
- Settings.Global.PREFERRED_NETWORK_MODE + subId,
- -1 /* invalid network mode */);
-
- if (networkType == -1) {
- networkType = RILConstants.PREFERRED_NETWORK_MODE;
- try {
- networkType = TelephonyManager.getIntAtIndex(
- sContext.getContentResolver(),
- Settings.Global.PREFERRED_NETWORK_MODE, phoneId);
- } catch (SettingNotFoundException retrySnfe) {
- Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for "
- + "Settings.Global.PREFERRED_NETWORK_MODE");
- }
- Settings.Global.putInt(
- PhoneFactory.getPhone(phoneId).getContext().getContentResolver(),
- Global.PREFERRED_NETWORK_MODE + subId,
- networkType);
- }
-
- // Set the modem network mode
- PhoneFactory.getPhone(phoneId).setPreferredNetworkType(networkType, null);
-
// Only support automatic selection mode on SIM change.
PhoneFactory.getPhone(phoneId).getNetworkSelectionMode(
obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE,
new Integer(phoneId)));
-
// Update stored subId
SharedPreferences.Editor editor = sp.edit();
editor.putInt(CURR_SUBID + phoneId, subId);
@@ -576,25 +575,32 @@ public class SubscriptionInfoUpdater extends Handler {
}
}
- // Update set of enabled carrier apps now that the privilege rules may have changed.
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(),
- TelephonyManager.getDefault(), mCurrentlyActiveUserId, sContext);
-
/**
* The sim loading sequence will be
* 1. ACTION_SUBINFO_CONTENT_CHANGE happens through updateSubscriptionInfoByIccId() above.
* 2. ACTION_SIM_STATE_CHANGED/ACTION_SIM_CARD_STATE_CHANGED
* /ACTION_SIM_APPLICATION_STATE_CHANGED
* 3. ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED
- * 4. ACTION_CARRIER_CONFIG_CHANGED
+ * 4. restore sim-specific settings
+ * 5. ACTION_CARRIER_CONFIG_CHANGED
*/
broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_LOADED);
updateSubscriptionCarrierId(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
+ /* Sim-specific settings restore depends on knowing both the mccmnc and the carrierId of the
+ sim which is why it must be done after #updateSubscriptionCarrierId(). It is done before
+ carrier config update to avoid any race conditions with user settings that depend on
+ carrier config*/
+ restoreSimSpecificSettingsForPhone(phoneId);
updateCarrierServices(phoneId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
}
+ private void restoreSimSpecificSettingsForPhone(int phoneId) {
+ SubscriptionManager subManager = SubscriptionManager.from(sContext);
+ subManager.restoreSimSpecificSettingsForIccIdFromBackup(sIccId[phoneId]);
+ }
+
private void updateCarrierServices(int phoneId, String simState) {
CarrierConfigManager configManager =
(CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -619,32 +625,52 @@ public class SubscriptionInfoUpdater extends Handler {
if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd("Slot of SIM" + (phoneId + 1) + " becomes inactive");
}
- cleanSubscriptionInPhone(phoneId);
+ cleanSubscriptionInPhone(phoneId, false);
}
if (!TextUtils.isEmpty(iccId)) {
// If iccId is new, add a subscription record in the db.
String strippedIccId = IccUtils.stripTrailingFs(iccId);
- if (SubscriptionController.getInstance().getSubInfoForIccId(strippedIccId) == null) {
- SubscriptionController.getInstance().insertEmptySubInfoRecord(
+ if (mSubscriptionController.getSubInfoForIccId(strippedIccId) == null) {
+ mSubscriptionController.insertEmptySubInfoRecord(
strippedIccId, "CARD", SubscriptionManager.INVALID_PHONE_INDEX,
SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
}
}
}
- private void cleanSubscriptionInPhone(int phoneId) {
- sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
- if (sInactiveIccIds[phoneId] != null) {
+ /**
+ * Clean subscription info when sim state becomes ABSENT. There are 2 scenarios for this:
+ * 1. SIM is actually removed
+ * 2. Slot becomes inactive, which results in SIM being treated as ABSENT, but SIM may not
+ * have been removed.
+ * @param phoneId phoneId for which the cleanup needs to be done
+ * @param isSimAbsent boolean to indicate if the SIM is actually ABSENT (case 1 above)
+ */
+ private void cleanSubscriptionInPhone(int phoneId, boolean isSimAbsent) {
+ if (sInactiveIccIds[phoneId] != null || (isSimAbsent && sIccId[phoneId] != null
+ && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM))) {
// When a SIM is unplugged, mark uicc applications enabled. This is to make sure when
// user unplugs and re-inserts the SIM card, we re-enable it.
- logd("cleanSubscriptionInPhone " + phoneId + " inactive iccid "
+ // In certain cases this can happen before sInactiveIccIds is updated, which is why we
+ // check for sIccId as well (in case of isSimAbsent). The scenario is: after SIM
+ // deactivate request is sent to RIL, SIM is removed before SIM state is updated to
+ // NOT_READY. We do not need to check if this exact scenario is hit, because marking
+ // uicc applications enabled when SIM is removed should be okay to do regardless.
+ logd("cleanSubscriptionInPhone: " + phoneId + ", inactive iccid "
+ sInactiveIccIds[phoneId]);
+ if (sInactiveIccIds[phoneId] == null) {
+ logd("cleanSubscriptionInPhone: " + phoneId + ", isSimAbsent=" + isSimAbsent
+ + ", iccid=" + sIccId[phoneId]);
+ }
+ String iccId = sInactiveIccIds[phoneId] != null
+ ? sInactiveIccIds[phoneId] : sIccId[phoneId];
ContentValues value = new ContentValues(1);
value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, true);
sContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
- SubscriptionManager.ICC_ID + "=\'" + sInactiveIccIds[phoneId] + "\'", null);
+ SubscriptionManager.ICC_ID + "=\'" + iccId + "\'", null);
sInactiveIccIds[phoneId] = null;
}
+ sIccId[phoneId] = ICCID_STRING_FOR_NO_SIM;
updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
}
@@ -656,7 +682,7 @@ public class SubscriptionInfoUpdater extends Handler {
if (sIccId[phoneId] != null && !sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd("SIM" + (phoneId + 1) + " hot plug out");
}
- cleanSubscriptionInPhone(phoneId);
+ cleanSubscriptionInPhone(phoneId, true);
broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_ABSENT, null);
broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_ABSENT);
@@ -691,17 +717,17 @@ public class SubscriptionInfoUpdater extends Handler {
// Clear phoneId only when sim absent is not enough. It's possible to switch SIM profile
// within the same slot. Need to clear the slot index of the previous sub. Thus always clear
// for the changing slot first.
- SubscriptionController.getInstance().clearSubInfoRecord(phoneId);
+ mSubscriptionController.clearSubInfoRecord(phoneId);
// If SIM is not absent, insert new record or update existing record.
- if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId])) {
+ if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[phoneId]) && sIccId[phoneId] != null) {
logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
+ sIccId[phoneId] + ", phoneId:" + phoneId);
mSubscriptionManager.addSubscriptionInfoRecord(sIccId[phoneId], phoneId);
}
- List<SubscriptionInfo> subInfos = SubscriptionController.getInstance()
- .getSubInfoUsingSlotIndexPrivileged(phoneId);
+ List<SubscriptionInfo> subInfos =
+ mSubscriptionController.getSubInfoUsingSlotIndexPrivileged(phoneId);
if (subInfos != null) {
boolean changed = false;
for (int i = 0; i < subInfos.size(); i++) {
@@ -720,7 +746,7 @@ public class SubscriptionInfoUpdater extends Handler {
}
if (changed) {
// refresh Cached Active Subscription Info List
- SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
+ mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
}
}
@@ -750,22 +776,22 @@ public class SubscriptionInfoUpdater extends Handler {
}
updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
if (hasChanges) {
- SubscriptionController.getInstance().notifySubscriptionInfoChanged();
+ mSubscriptionController.notifySubscriptionInfoChanged();
}
if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete");
});
}
- SubscriptionController.getInstance().notifySubscriptionInfoChanged();
+ mSubscriptionController.notifySubscriptionInfoChanged();
if (DBG) logd("updateSubscriptionInfoByIccId: SubscriptionInfo update complete");
}
- private static void setSubInfoInitialized() {
+ private void setSubInfoInitialized() {
// Should only be triggered once.
if (!sIsSubInfoInitialized) {
if (DBG) logd("SubInfo Initialized");
sIsSubInfoInitialized = true;
- SubscriptionController.getInstance().notifySubInfoReady();
+ mSubscriptionController.notifySubInfoReady();
MultiSimSettingController.getInstance().notifyAllSubscriptionLoaded();
}
}
@@ -875,8 +901,9 @@ public class SubscriptionInfoUpdater extends Handler {
// Update or insert records for all embedded subscriptions (except non-removable ones if the
// current eUICC is non-removable, since we assume these are still accessible though not
// returned by the eUICC controller).
- List<SubscriptionInfo> existingSubscriptions = SubscriptionController.getInstance()
- .getSubscriptionInfoListForEmbeddedSubscriptionUpdate(embeddedIccids, isRemovable);
+ List<SubscriptionInfo> existingSubscriptions =
+ mSubscriptionController.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(
+ embeddedIccids, isRemovable);
ContentResolver contentResolver = sContext.getContentResolver();
for (EuiccProfileInfo embeddedProfile : embeddedProfiles) {
int index =
@@ -885,7 +912,7 @@ public class SubscriptionInfoUpdater extends Handler {
int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER_ID;
if (index < 0) {
// No existing entry for this ICCID; create an empty one.
- SubscriptionController.getInstance().insertEmptySubInfoRecord(
+ mSubscriptionController.insertEmptySubInfoRecord(
embeddedProfile.getIccid(), SubscriptionManager.SIM_NOT_INSERTED);
} else {
nameSource = existingSubscriptions.get(index).getNameSource();
@@ -947,7 +974,7 @@ public class SubscriptionInfoUpdater extends Handler {
SubscriptionManager.ICC_ID + "=\"" + embeddedProfile.getIccid() + "\"", null);
// refresh Cached Active Subscription Info List
- SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
+ mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
}
// Remove all remaining subscriptions which have embedded = true. We set embedded to false
@@ -975,7 +1002,7 @@ public class SubscriptionInfoUpdater extends Handler {
contentResolver.update(SubscriptionManager.CONTENT_URI, values, whereClause, null);
// refresh Cached Active Subscription Info List
- SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
+ mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
}
if (DBG) logd("updateEmbeddedSubscriptions done hasChanges=" + hasChanges);
@@ -1025,20 +1052,14 @@ public class SubscriptionInfoUpdater extends Handler {
return;
}
- SubscriptionController sc = SubscriptionController.getInstance();
- if (sc == null) {
- loge("SubscriptionController was null");
- return;
- }
-
- int currentSubId = sc.getSubIdUsingPhoneId(phoneId);
+ int currentSubId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
if (!SubscriptionManager.isValidSubscriptionId(currentSubId)
|| currentSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
if (DBG) logd("No subscription is active for phone being updated");
return;
}
- SubscriptionInfo currentSubInfo = sc.getSubscriptionInfo(currentSubId);
+ SubscriptionInfo currentSubInfo = mSubscriptionController.getSubscriptionInfo(currentSubId);
if (currentSubInfo == null) {
loge("Couldn't retrieve subscription info for current subscription");
return;
@@ -1051,15 +1072,10 @@ public class SubscriptionInfoUpdater extends Handler {
// this current package is not a CarrierServicePackage
String[] certs = config.getStringArray(
CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
- if (certs != null) {
- UiccAccessRule[] carrierConfigAccessRules = new UiccAccessRule[certs.length];
- for (int i = 0; i < certs.length; i++) {
- carrierConfigAccessRules[i] = new UiccAccessRule(IccUtils.hexStringToBytes(
- certs[i]), null, 0);
- }
- cv.put(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS,
- UiccAccessRule.encodeRules(carrierConfigAccessRules));
- }
+ UiccAccessRule[] carrierConfigAccessRules = UiccAccessRule.decodeRulesFromCarrierConfig(
+ certs);
+ cv.put(SubscriptionManager.ACCESS_RULES_FROM_CARRIER_CONFIGS,
+ UiccAccessRule.encodeRules(carrierConfigAccessRules));
if (!isCarrierServicePackage(phoneId, configPackageName)) {
loge("Cannot manage subId=" + currentSubId + ", carrierPackage=" + configPackageName);
@@ -1081,8 +1097,8 @@ public class SubscriptionInfoUpdater extends Handler {
&& currentSubInfo.getGroupUuid() != null) {
cv.put(SubscriptionManager.GROUP_UUID, (String) null);
if (DBG) logd("Group Removed for" + currentSubId);
- } else if (SubscriptionController.getInstance().canPackageManageGroup(groupUuid,
- configPackageName)) {
+ } else if (mSubscriptionController.canPackageManageGroup(
+ groupUuid, configPackageName)) {
cv.put(SubscriptionManager.GROUP_UUID, groupUuid.toString());
cv.put(SubscriptionManager.GROUP_OWNER, configPackageName);
if (DBG) logd("Group Added for" + currentSubId);
@@ -1097,8 +1113,8 @@ public class SubscriptionInfoUpdater extends Handler {
}
if (cv.size() > 0 && sContext.getContentResolver().update(SubscriptionManager
.getUriForSubscriptionId(currentSubId), cv, null, null) > 0) {
- sc.refreshCachedActiveSubscriptionInfoList();
- sc.notifySubscriptionInfoChanged();
+ mSubscriptionController.refreshCachedActiveSubscriptionInfoList();
+ mSubscriptionController.notifySubscriptionInfoChanged();
MultiSimSettingController.getInstance().notifySubscriptionGroupChanged(groupUuid);
}
}
@@ -1112,30 +1128,12 @@ public class SubscriptionInfoUpdater extends Handler {
return -1;
}
- private boolean isNewSim(String iccId, String decIccId, String[] oldIccId) {
- boolean newSim = true;
- for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
- if(iccId.equals(oldIccId[i])) {
- newSim = false;
- break;
- } else if (decIccId != null && decIccId.equals(oldIccId[i])) {
- newSim = false;
- break;
- }
- }
- logd("newSim = " + newSim);
-
- return newSim;
- }
-
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void broadcastSimStateChanged(int phoneId, String state, String reason) {
+ // Note: This intent is way deprecated and is only being kept around because there's no
+ // graceful way to deprecate a sticky broadcast that has a lot of listeners.
+ // DO NOT add any new extras to this broadcast -- it is not protected by any permissions.
Intent i = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- // TODO - we'd like this intent to have a single snapshot of all sim state,
- // but until then this should not use REPLACE_PENDING or we may lose
- // information
- // i.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- // | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
i.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
i.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, state);
@@ -1228,7 +1226,7 @@ public class SubscriptionInfoUpdater extends Handler {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static void logd(String message) {
Rlog.d(LOG_TAG, message);
}
diff --git a/src/java/com/android/internal/telephony/TelephonyCapabilities.java b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
index f2f7ddcb53..1b4a3a93e5 100644
--- a/src/java/com/android/internal/telephony/TelephonyCapabilities.java
+++ b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.telephony.Rlog;
@@ -178,7 +179,7 @@ public class TelephonyCapabilities {
* of public API, with which the argument should be replaced with
* something more appropriate.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static boolean supportsAdn(int phoneType) {
return phoneType == PhoneConstants.PHONE_TYPE_GSM;
}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 9a6e244913..4632a608a6 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -30,10 +30,12 @@ import android.system.StructStatVfs;
import android.telephony.AccessNetworkConstants.TransportType;
import android.text.TextUtils;
+import com.android.ims.ImsManager;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriManager;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
@@ -68,6 +70,7 @@ public class TelephonyComponentFactory {
private static final String TAG = TelephonyComponentFactory.class.getSimpleName();
private static TelephonyComponentFactory sInstance;
+ private final TelephonyFacade mTelephonyFacade = new TelephonyFacade();
private InjectedComponents mInjectedComponents;
@@ -79,6 +82,7 @@ public class TelephonyComponentFactory {
private static final String TAG_COMPONENT = "component";
private static final String SYSTEM = "/system/";
private static final String PRODUCT = "/product/";
+ private static final String SYSTEM_EXT = "/system_ext/";
private final Set<String> mComponentNames = new HashSet<>();
private TelephonyComponentFactory mInjectedInstance;
@@ -88,7 +92,7 @@ public class TelephonyComponentFactory {
/**
* @return paths correctly configured to inject.
* 1) PackageName and JarPath mustn't be empty.
- * 2) JarPath is restricted under /system or /product only.
+ * 2) JarPath is restricted under /system or /product or /system_ext only.
* 3) JarPath is on a READ-ONLY partition.
*/
private @Nullable String getValidatedPaths() {
@@ -97,7 +101,8 @@ public class TelephonyComponentFactory {
}
// filter out invalid paths
return Arrays.stream(mJarPath.split(File.pathSeparator))
- .filter(s -> (s.startsWith(SYSTEM) || s.startsWith(PRODUCT)))
+ .filter(s -> (s.startsWith(SYSTEM) || s.startsWith(PRODUCT)
+ || s.startsWith(SYSTEM_EXT)))
.filter(s -> {
try {
// This will also throw an error if the target doesn't exist.
@@ -347,9 +352,10 @@ public class TelephonyComponentFactory {
*/
public InboundSmsTracker makeInboundSmsTracker(Context context, byte[] pdu, long timestamp,
int destPort, boolean is3gpp2, boolean is3gpp2WapPdu, String address,
- String displayAddr, String messageBody, boolean isClass0, int subId) {
+ String displayAddr, String messageBody, boolean isClass0, int subId,
+ @InboundSmsHandler.SmsSource int smsSource) {
return new InboundSmsTracker(context, pdu, timestamp, destPort, is3gpp2, is3gpp2WapPdu,
- address, displayAddr, messageBody, isClass0, subId);
+ address, displayAddr, messageBody, isClass0, subId, smsSource);
}
/**
@@ -358,10 +364,10 @@ public class TelephonyComponentFactory {
public InboundSmsTracker makeInboundSmsTracker(Context context, byte[] pdu, long timestamp,
int destPort, boolean is3gpp2, String address, String displayAddr, int referenceNumber,
int sequenceNumber, int messageCount, boolean is3gpp2WapPdu, String messageBody,
- boolean isClass0, int subId) {
+ boolean isClass0, int subId, @InboundSmsHandler.SmsSource int smsSource) {
return new InboundSmsTracker(context, pdu, timestamp, destPort, is3gpp2, address,
displayAddr, referenceNumber, sequenceNumber, messageCount, is3gpp2WapPdu,
- messageBody, isClass0, subId);
+ messageBody, isClass0, subId, smsSource);
}
/**
@@ -373,7 +379,7 @@ public class TelephonyComponentFactory {
}
public ImsPhoneCallTracker makeImsPhoneCallTracker(ImsPhone imsPhone) {
- return new ImsPhoneCallTracker(imsPhone);
+ return new ImsPhoneCallTracker(imsPhone, ImsManager::getConnector);
}
public ImsExternalCallTracker makeImsExternalCallTracker(ImsPhone imsPhone) {
@@ -440,7 +446,14 @@ public class TelephonyComponentFactory {
}
public SubscriptionInfoUpdater makeSubscriptionInfoUpdater(Looper looper, Context context,
- CommandsInterface[] ci) {
- return new SubscriptionInfoUpdater(looper, context, ci);
+ SubscriptionController sc) {
+ return new SubscriptionInfoUpdater(looper, context, sc);
+ }
+
+ /**
+ * Create a new LinkBandwidthEstimator.
+ */
+ public LinkBandwidthEstimator makeLinkBandwidthEstimator(Phone phone) {
+ return new LinkBandwidthEstimator(phone, mTelephonyFacade);
}
}
diff --git a/src/java/com/android/internal/telephony/TelephonyFacade.java b/src/java/com/android/internal/telephony/TelephonyFacade.java
new file mode 100644
index 0000000000..31a2242f94
--- /dev/null
+++ b/src/java/com/android/internal/telephony/TelephonyFacade.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.net.TrafficStats;
+import android.os.SystemClock;
+
+/**
+ * This class is a wrapper of various static methods to simplify unit tests with static methods
+ */
+public class TelephonyFacade {
+ /**
+ * Returns milliseconds since boot, including time spent in sleep.
+ *
+ * @return Current time since boot in milliseconds.
+ */
+ public long getElapsedSinceBootMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Wrapper for {@link TrafficStats#getMobileTxBytes}.
+ */
+ public long getMobileTxBytes() {
+ return TrafficStats.getMobileTxBytes();
+ }
+
+ /**
+ * Wrapper for {@link TrafficStats#getMobileRxBytes}.
+ */
+ public long getMobileRxBytes() {
+ return TrafficStats.getMobileRxBytes();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index e1ada1d483..364b18d3ec 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -125,6 +125,14 @@ public class TelephonyTester {
"com.android.internal.telephony.TestImsECall";
/**
+ * Test-only intent used to trigger signalling that an IMS call received a DTMF tone.
+ */
+ private static final String ACTION_TEST_RECEIVE_DTMF =
+ "com.android.internal.telephony.TestReceiveDtmf";
+
+ private static final String EXTRA_DIGIT = "digit";
+
+ /**
* Test-only intent used to trigger a change to the current call's phone number.
* Use the {@link #EXTRA_NUMBER} extra to specify the new phone number.
*/
@@ -197,6 +205,9 @@ public class TelephonyTester {
} else if (action.equals(ACTION_TEST_IMS_E_CALL)) {
log("handle test IMS ecall intent");
testImsECall();
+ } else if (action.equals(ACTION_TEST_RECEIVE_DTMF)) {
+ log("handle test DTMF intent");
+ testImsReceiveDtmf(intent);
} else if (action.equals(ACTION_TEST_CHANGE_NUMBER)) {
log("handle test change number intent");
testChangeNumber(intent);
@@ -229,6 +240,7 @@ public class TelephonyTester {
filter.addAction(ACTION_TEST_HANDOVER_FAIL);
filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION);
filter.addAction(ACTION_TEST_IMS_E_CALL);
+ filter.addAction(ACTION_TEST_RECEIVE_DTMF);
mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
}
@@ -262,17 +274,7 @@ public class TelephonyTester {
private void handleHandoverFailedIntent() {
// Attempt to get the active IMS call
- ImsPhone imsPhone = (ImsPhone) mPhone;
- if (imsPhone == null) {
- return;
- }
-
- ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
- if (imsPhoneCall == null) {
- return;
- }
-
- ImsCall imsCall = imsPhoneCall.getImsCall();
+ ImsCall imsCall = getImsCall();
if (imsCall == null) {
return;
}
@@ -491,30 +493,50 @@ public class TelephonyTester {
void testImsECall() {
// Attempt to get the active IMS call before parsing the test XML file.
+ ImsCall imsCall = getImsCall();
+ if (imsCall == null) return;
+
+ ImsCallProfile callProfile = imsCall.getCallProfile();
+ Bundle extras = callProfile.getCallExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL, true);
+ callProfile.mCallExtras = extras;
+ imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(),
+ callProfile);
+ }
+
+ private ImsCall getImsCall() {
ImsPhone imsPhone = (ImsPhone) mPhone;
if (imsPhone == null) {
- return;
+ return null;
}
ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
if (imsPhoneCall == null) {
- return;
+ return null;
}
ImsCall imsCall = imsPhoneCall.getImsCall();
if (imsCall == null) {
+ return null;
+ }
+ return imsCall;
+ }
+
+ void testImsReceiveDtmf(Intent intent) {
+ if (!intent.hasExtra(EXTRA_DIGIT)) {
return;
}
+ char digit = intent.getStringExtra(EXTRA_DIGIT).charAt(0);
- ImsCallProfile callProfile = imsCall.getCallProfile();
- Bundle extras = callProfile.getCallExtras();
- if (extras == null) {
- extras = new Bundle();
+ ImsCall imsCall = getImsCall();
+ if (imsCall == null) {
+ return;
}
- extras.putBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL, true);
- callProfile.mCallExtras = extras;
- imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(),
- callProfile);
+
+ imsCall.getImsCallSessionListenerProxy().callSessionDtmfReceived(digit);
}
void testChangeNumber(Intent intent) {
diff --git a/src/java/com/android/internal/telephony/UUSInfo.java b/src/java/com/android/internal/telephony/UUSInfo.java
index 2b4f6749c3..f9608ae8b1 100644
--- a/src/java/com/android/internal/telephony/UUSInfo.java
+++ b/src/java/com/android/internal/telephony/UUSInfo.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
public class UUSInfo {
@@ -76,7 +77,7 @@ public class UUSInfo {
mUusData = uusData;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getDcs() {
return mUusDcs;
}
@@ -85,7 +86,7 @@ public class UUSInfo {
mUusDcs = uusDcs;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getType() {
return mUusType;
}
@@ -94,7 +95,7 @@ public class UUSInfo {
mUusType = uusType;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public byte[] getUserData() {
return mUusData;
}
diff --git a/src/java/com/android/internal/telephony/UiccPhoneBookController.java b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
index 54eae6fbcc..5b55c35631 100644
--- a/src/java/com/android/internal/telephony/UiccPhoneBookController.java
+++ b/src/java/com/android/internal/telephony/UiccPhoneBookController.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
- * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2013, 2021 The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,9 +19,12 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.TelephonyServiceManager.ServiceRegisterer;
import android.telephony.TelephonyFrameworkInitializer;
+import android.content.ContentValues;
+import com.android.internal.telephony.uicc.AdnCapacity;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.telephony.Rlog;
@@ -31,7 +34,7 @@ public class UiccPhoneBookController extends IIccPhoneBook.Stub {
private static final String TAG = "UiccPhoneBookController";
/* only one UiccPhoneBookController exists */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public UiccPhoneBookController() {
ServiceRegisterer iccPhoneBookServiceRegisterer = TelephonyFrameworkInitializer
.getTelephonyServiceManager()
@@ -45,44 +48,24 @@ public class UiccPhoneBookController extends IIccPhoneBook.Stub {
public boolean
updateAdnRecordsInEfBySearch (int efid, String oldTag, String oldPhoneNumber,
String newTag, String newPhoneNumber, String pin2) throws android.os.RemoteException {
- return updateAdnRecordsInEfBySearchForSubscriber(getDefaultSubscription(), efid, oldTag,
- oldPhoneNumber, newTag, newPhoneNumber, pin2);
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_TAG, oldTag);
+ values.put(IccProvider.STR_NUMBER, oldPhoneNumber);
+ values.put(IccProvider.STR_NEW_TAG, newTag);
+ values.put(IccProvider.STR_NEW_NUMBER, newPhoneNumber);
+ return updateAdnRecordsInEfBySearchForSubscriber(getDefaultSubscription(),
+ efid, values, pin2);
}
@Override
public boolean
- updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, String oldTag,
- String oldPhoneNumber, String newTag, String newPhoneNumber,
- String pin2) throws android.os.RemoteException {
+ updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, ContentValues values,
+ int index, String pin2) throws android.os.RemoteException {
IccPhoneBookInterfaceManager iccPbkIntMgr =
getIccPhoneBookInterfaceManager(subId);
if (iccPbkIntMgr != null) {
- return iccPbkIntMgr.updateAdnRecordsInEfBySearch(efid, oldTag,
- oldPhoneNumber, newTag, newPhoneNumber, pin2);
- } else {
- Rlog.e(TAG,"updateAdnRecordsInEfBySearch iccPbkIntMgr is" +
- " null for Subscription:"+subId);
- return false;
- }
- }
-
- @Override
- public boolean
- updateAdnRecordsInEfByIndex(int efid, String newTag,
- String newPhoneNumber, int index, String pin2) throws android.os.RemoteException {
- return updateAdnRecordsInEfByIndexForSubscriber(getDefaultSubscription(), efid, newTag,
- newPhoneNumber, index, pin2);
- }
-
- @Override
- public boolean
- updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, String newTag,
- String newPhoneNumber, int index, String pin2) throws android.os.RemoteException {
- IccPhoneBookInterfaceManager iccPbkIntMgr =
- getIccPhoneBookInterfaceManager(subId);
- if (iccPbkIntMgr != null) {
- return iccPbkIntMgr.updateAdnRecordsInEfByIndex(efid, newTag,
- newPhoneNumber, index, pin2);
+ return iccPbkIntMgr.updateAdnRecordsInEfByIndex(efid, values,
+ index, pin2);
} else {
Rlog.e(TAG,"updateAdnRecordsInEfByIndex iccPbkIntMgr is" +
" null for Subscription:"+subId);
@@ -128,10 +111,38 @@ public class UiccPhoneBookController extends IIccPhoneBook.Stub {
}
}
+ @Override
+ public AdnCapacity getAdnRecordsCapacityForSubscriber(int subId)
+ throws android.os.RemoteException {
+ IccPhoneBookInterfaceManager iccPbkIntMgr = getIccPhoneBookInterfaceManager(subId);
+ if (iccPbkIntMgr != null) {
+ return iccPbkIntMgr.getAdnRecordsCapacity();
+ } else {
+ Rlog.e(TAG, "getAdnRecordsCapacity iccPbkIntMgr is null for Subscription:" + subId);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean
+ updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid,
+ ContentValues values, String pin2)
+ throws android.os.RemoteException {
+ IccPhoneBookInterfaceManager iccPbkIntMgr = getIccPhoneBookInterfaceManager(subId);
+ if (iccPbkIntMgr != null) {
+ return iccPbkIntMgr.updateAdnRecordsInEfBySearchForSubscriber(
+ efid, values, pin2);
+ } else {
+ Rlog.e(TAG,"updateAdnRecordsInEfBySearchForSubscriber " +
+ "iccPbkIntMgr is null for Subscription:"+subId);
+ return false;
+ }
+ }
+
/**
* get phone book interface manager object based on subscription.
**/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IccPhoneBookInterfaceManager
getIccPhoneBookInterfaceManager(int subId) {
@@ -149,7 +160,7 @@ public class UiccPhoneBookController extends IIccPhoneBook.Stub {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int getDefaultSubscription() {
return PhoneFactory.getDefaultSubscription();
}
diff --git a/src/java/com/android/internal/telephony/WakeLockStateMachine.java b/src/java/com/android/internal/telephony/WakeLockStateMachine.java
index eab9b8e766..9ede7ea600 100644
--- a/src/java/com/android/internal/telephony/WakeLockStateMachine.java
+++ b/src/java/com/android/internal/telephony/WakeLockStateMachine.java
@@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.Message;
import android.os.PowerManager;
@@ -54,10 +55,10 @@ public abstract class WakeLockStateMachine extends StateMachine {
/** Broadcast not required due to geo-fencing check */
static final int EVENT_BROADCAST_NOT_REQUIRED = 4;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Phone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected Context mContext;
protected AtomicInteger mReceiverCount = new AtomicInteger(0);
@@ -66,7 +67,7 @@ public abstract class WakeLockStateMachine extends StateMachine {
private static final int WAKE_LOCK_TIMEOUT = 3000;
private final DefaultState mDefaultState = new DefaultState();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final IdleState mIdleState = new IdleState();
private final WaitingState mWaitingState = new WaitingState();
@@ -243,7 +244,7 @@ public abstract class WakeLockStateMachine extends StateMachine {
* Log with debug level.
* @param s the string to log
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void log(String s) {
Rlog.d(getName(), s);
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index d755a92223..d6f69e203e 100755
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -16,9 +16,10 @@
package com.android.internal.telephony;
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_DELIVERY_IND;
+import static android.os.PowerWhitelistManager.REASON_EVENT_MMS;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_ORIG_IND;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,43 +27,30 @@ import android.app.Activity;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteException;
-import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerWhitelistManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.telephony.SmsManager;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
-import android.util.Log;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.telephony.Rlog;
-import com.google.android.mms.MmsException;
-import com.google.android.mms.pdu.DeliveryInd;
import com.google.android.mms.pdu.GenericPdu;
import com.google.android.mms.pdu.NotificationInd;
-import com.google.android.mms.pdu.PduHeaders;
import com.google.android.mms.pdu.PduParser;
-import com.google.android.mms.pdu.PduPersister;
-import com.google.android.mms.pdu.ReadOrigInd;
import java.util.HashMap;
import java.util.List;
@@ -76,42 +64,16 @@ public class WapPushOverSms implements ServiceConnection {
private static final String TAG = "WAP PUSH";
private static final boolean DBG = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final Context mContext;
PowerWhitelistManager mPowerWhitelistManager;
private String mWapPushManagerPackage;
/** Assigned from ServiceConnection callback on main threaad. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private volatile IWapPushManager mWapPushManager;
- /** Broadcast receiver that binds to WapPushManager when the user unlocks the phone for the
- * first time after reboot and the credential-encrypted storage is available.
- */
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(final Context context, Intent intent) {
- Rlog.d(TAG, "Received broadcast " + intent.getAction());
- if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- new BindServiceThread(mContext).start();
- }
- }
- };
-
- private class BindServiceThread extends Thread {
- private final Context context;
-
- private BindServiceThread(Context context) {
- this.context = context;
- }
-
- @Override
- public void run() {
- bindWapPushManagerService(context);
- }
- }
-
private void bindWapPushManagerService(Context context) {
Intent intent = new Intent(IWapPushManager.class.getName());
ComponentName comp = resolveSystemService(context.getPackageManager(), intent);
@@ -171,13 +133,7 @@ public class WapPushOverSms implements ServiceConnection {
UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (userManager.isUserUnlocked()) {
- bindWapPushManagerService(mContext);
- } else {
- IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
- context.registerReceiver(mBroadcastReceiver, userFilter);
- }
+ bindWapPushManagerService(mContext);
}
public void dispose() {
@@ -360,8 +316,8 @@ public class WapPushOverSms implements ServiceConnection {
* {@link Activity#RESULT_OK} if the message has been broadcast
* to applications
*/
- public int dispatchWapPdu(byte[] pdu, BroadcastReceiver receiver, InboundSmsHandler handler,
- String address, int subId, long messageId) {
+ public int dispatchWapPdu(byte[] pdu, InboundSmsHandler.SmsBroadcastReceiver receiver,
+ InboundSmsHandler handler, String address, int subId, long messageId) {
DecodedResult result = decodeWapPdu(pdu, handler);
if (result.statusCode != Activity.RESULT_OK) {
return result.statusCode;
@@ -382,7 +338,8 @@ public class WapPushOverSms implements ServiceConnection {
} else {
synchronized (this) {
mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
- mWapPushManagerPackage, PowerWhitelistManager.EVENT_MMS, "mms-mgr");
+ mWapPushManagerPackage, PowerWhitelistManager.EVENT_MMS,
+ REASON_EVENT_MMS, "mms-mgr");
}
Intent intent = new Intent();
@@ -425,7 +382,6 @@ public class WapPushOverSms implements ServiceConnection {
intent.putExtra("header", result.header);
intent.putExtra("data", result.intentData);
intent.putExtra("contentTypeParameters", result.contentTypeParameters);
- SubscriptionManager.putPhoneIdAndSubIdExtra(intent, result.phoneId);
if (!TextUtils.isEmpty(address)) {
intent.putExtra("address", address);
}
@@ -443,9 +399,13 @@ public class WapPushOverSms implements ServiceConnection {
if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
" " + componentName.getClassName());
long duration = mPowerWhitelistManager.whitelistAppTemporarilyForEvent(
- componentName.getPackageName(), PowerWhitelistManager.EVENT_MMS, "mms-app");
+ componentName.getPackageName(), PowerWhitelistManager.EVENT_MMS,
+ REASON_EVENT_MMS, "mms-app");
BroadcastOptions bopts = BroadcastOptions.makeBasic();
- bopts.setTemporaryAppWhitelistDuration(duration);
+ bopts.setTemporaryAppAllowlist(duration,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_EVENT_MMS,
+ "");
options = bopts.toBundle();
}
@@ -458,7 +418,7 @@ public class WapPushOverSms implements ServiceConnection {
/**
* Check whether the pdu is a MMS WAP push pdu that should be dispatched to the SMS app.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isWapPushForMms(byte[] pdu, InboundSmsHandler handler) {
DecodedResult result = decodeWapPdu(pdu, handler);
return result.statusCode == Activity.RESULT_OK
@@ -472,165 +432,6 @@ public class WapPushOverSms implements ServiceConnection {
.getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
}
- private void writeInboxMessage(int subId, GenericPdu pdu) {
- if (pdu == null) {
- Rlog.e(TAG, "Invalid PUSH PDU");
- }
- final PduPersister persister = PduPersister.getPduPersister(mContext);
- final int type = pdu.getMessageType();
- try {
- switch (type) {
- case MESSAGE_TYPE_DELIVERY_IND:
- case MESSAGE_TYPE_READ_ORIG_IND: {
- final long threadId = getDeliveryOrReadReportThreadId(mContext, pdu);
- if (threadId == -1) {
- // The associated SendReq isn't found, therefore skip
- // processing this PDU.
- Rlog.e(TAG, "Failed to find delivery or read report's thread id");
- break;
- }
- final Uri uri = persister.persist(
- pdu,
- Telephony.Mms.Inbox.CONTENT_URI,
- true/*createThreadId*/,
- true/*groupMmsEnabled*/,
- null/*preOpenedFiles*/);
- if (uri == null) {
- Rlog.e(TAG, "Failed to persist delivery or read report");
- break;
- }
- // Update thread ID for ReadOrigInd & DeliveryInd.
- final ContentValues values = new ContentValues(1);
- values.put(Telephony.Mms.THREAD_ID, threadId);
- if (mContext.getContentResolver().update(
- uri,
- values,
- null/*where*/,
- null/*selectionArgs*/) != 1) {
- Rlog.e(TAG, "Failed to update delivery or read report thread id");
- }
- break;
- }
- case MESSAGE_TYPE_NOTIFICATION_IND: {
- final NotificationInd nInd = (NotificationInd) pdu;
-
- Bundle configs = SmsManager.getSmsManagerForSubscriptionId(subId)
- .getCarrierConfigValues();
- if (configs != null && configs.getBoolean(
- SmsManager.MMS_CONFIG_APPEND_TRANSACTION_ID, false)) {
- final byte [] contentLocation = nInd.getContentLocation();
- if ('=' == contentLocation[contentLocation.length - 1]) {
- byte [] transactionId = nInd.getTransactionId();
- byte [] contentLocationWithId = new byte [contentLocation.length
- + transactionId.length];
- System.arraycopy(contentLocation, 0, contentLocationWithId,
- 0, contentLocation.length);
- System.arraycopy(transactionId, 0, contentLocationWithId,
- contentLocation.length, transactionId.length);
- nInd.setContentLocation(contentLocationWithId);
- }
- }
- if (!isDuplicateNotification(mContext, nInd)) {
- final Uri uri = persister.persist(
- pdu,
- Telephony.Mms.Inbox.CONTENT_URI,
- true/*createThreadId*/,
- true/*groupMmsEnabled*/,
- null/*preOpenedFiles*/);
- if (uri == null) {
- Rlog.e(TAG, "Failed to save MMS WAP push notification ind");
- }
- } else {
- Rlog.d(TAG, "Skip storing duplicate MMS WAP push notification ind: "
- + new String(nInd.getContentLocation()));
- }
- break;
- }
- default:
- Log.e(TAG, "Received unrecognized WAP Push PDU.");
- }
- } catch (MmsException e) {
- Log.e(TAG, "Failed to save MMS WAP push data: type=" + type, e);
- } catch (RuntimeException e) {
- Log.e(TAG, "Unexpected RuntimeException in persisting MMS WAP push data", e);
- }
-
- }
-
- private static final String THREAD_ID_SELECTION =
- Telephony.Mms.MESSAGE_ID + "=? AND " + Telephony.Mms.MESSAGE_TYPE + "=?";
-
- @UnsupportedAppUsage
- private static long getDeliveryOrReadReportThreadId(Context context, GenericPdu pdu) {
- String messageId;
- if (pdu instanceof DeliveryInd) {
- messageId = new String(((DeliveryInd) pdu).getMessageId());
- } else if (pdu instanceof ReadOrigInd) {
- messageId = new String(((ReadOrigInd) pdu).getMessageId());
- } else {
- Rlog.e(TAG, "WAP Push data is neither delivery or read report type: "
- + pdu.getClass().getCanonicalName());
- return -1L;
- }
- Cursor cursor = null;
- try {
- cursor = context.getContentResolver().query(
- Telephony.Mms.CONTENT_URI,
- new String[]{ Telephony.Mms.THREAD_ID },
- THREAD_ID_SELECTION,
- new String[]{
- DatabaseUtils.sqlEscapeString(messageId),
- Integer.toString(PduHeaders.MESSAGE_TYPE_SEND_REQ)
- },
- null/*sortOrder*/);
- if (cursor != null && cursor.moveToFirst()) {
- return cursor.getLong(0);
- }
- } catch (SQLiteException e) {
- Rlog.e(TAG, "Failed to query delivery or read report thread id", e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return -1L;
- }
-
- private static final String LOCATION_SELECTION =
- Telephony.Mms.MESSAGE_TYPE + "=? AND " + Telephony.Mms.CONTENT_LOCATION + " =?";
-
- @UnsupportedAppUsage
- private static boolean isDuplicateNotification(Context context, NotificationInd nInd) {
- final byte[] rawLocation = nInd.getContentLocation();
- if (rawLocation != null) {
- String location = new String(rawLocation);
- String[] selectionArgs = new String[] { location };
- Cursor cursor = null;
- try {
- cursor = context.getContentResolver().query(
- Telephony.Mms.CONTENT_URI,
- new String[]{ Telephony.Mms._ID },
- LOCATION_SELECTION,
- new String[]{
- Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND),
- new String(rawLocation)
- },
- null/*sortOrder*/);
- if (cursor != null && cursor.getCount() > 0) {
- // We already received the same notification before.
- return true;
- }
- } catch (SQLiteException e) {
- Rlog.e(TAG, "failed to query existing notification ind", e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
- return false;
- }
-
public static String getPermissionForType(String mimeType) {
String permission;
if (WspTypeDecoder.CONTENT_TYPE_B_MMS.equals(mimeType)) {
diff --git a/src/java/com/android/internal/telephony/WspTypeDecoder.java b/src/java/com/android/internal/telephony/WspTypeDecoder.java
index 372aa73ec2..38b3837905 100755
--- a/src/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/src/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import java.util.HashMap;
@@ -198,7 +199,7 @@ public class WspTypeDecoder {
public static final String CONTENT_TYPE_B_MMS = "application/vnd.wap.mms-message";
public static final String CONTENT_TYPE_B_PUSH_SYNCML_NOTI = "application/vnd.syncml.notification";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
byte[] mWspData;
int mDataLength;
long mUnsigned32bit;
@@ -602,7 +603,7 @@ public class WspTypeDecoder {
* method
* length of data in pdu can be retrieved by getDecodedDataLength() method
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean decodeXWapApplicationId(int startIndex) {
if (decodeIntegerValue(startIndex) == true) {
mStringValue = null;
@@ -620,7 +621,7 @@ public class WspTypeDecoder {
* @return false when error(not a X-Wap-Application-Id) occur
* return value can be retrieved by getValue32()
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean seekXWapApplicationId(int startIndex, int endIndex) {
int index = startIndex;
@@ -736,7 +737,7 @@ public class WspTypeDecoder {
* unassigned parameter. If a parameter has No-Value the value will be null.
*
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public HashMap<String, String> getContentParameters() {
return mContentParameters;
}
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
index 079b3f6e22..5e92fb1edb 100755
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
+import android.os.Build;
/**
* Interface for communication between STK App and CAT Telephony
@@ -140,7 +141,7 @@ public interface AppInterface {
* value}. If no CommandType object has that value, null is
* returned.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static CommandType fromInt(int value) {
for (CommandType e : CommandType.values()) {
if (e.mValue == value) {
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 936158ad71..3d212709d0 100644
--- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,17 +28,17 @@ import android.os.Parcelable;
*/
public class CatCmdMessage implements Parcelable {
// members
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
CommandDetails mCmdDet;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private TextMessage mTextMsg;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Menu mMenu;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Input mInput;
private BrowserSettings mBrowserSettings = null;
private ToneSettings mToneSettings = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CallSettings mCallSettings = null;
private SetupEventListSettings mSetupEventListSettings = null;
private boolean mLoadIconFailed = false;
@@ -54,9 +55,9 @@ public class CatCmdMessage implements Parcelable {
* Container for Call Setup command settings.
*/
public class CallSettings {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public TextMessage confirmMsg;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public TextMessage callMsg;
}
@@ -215,7 +216,7 @@ public class CatCmdMessage implements Parcelable {
}
/* external API to be used by application */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AppInterface.CommandType getCmdType() {
return AppInterface.CommandType.fromInt(mCmdDet.typeOfCommand);
}
@@ -228,7 +229,7 @@ public class CatCmdMessage implements Parcelable {
return mInput;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public TextMessage geTextMessage() {
return mTextMsg;
}
@@ -241,7 +242,7 @@ public class CatCmdMessage implements Parcelable {
return mToneSettings;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public CallSettings getCallSettings() {
return mCallSettings;
}
diff --git a/src/java/com/android/internal/telephony/cat/CatLog.java b/src/java/com/android/internal/telephony/cat/CatLog.java
index 62bdf4e2b1..50798abc94 100644
--- a/src/java/com/android/internal/telephony/cat/CatLog.java
+++ b/src/java/com/android/internal/telephony/cat/CatLog.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.telephony.Rlog;
@@ -34,7 +35,7 @@ public abstract class CatLog {
+ msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static void d(String caller, String msg) {
if (!DEBUG) {
return;
@@ -42,7 +43,7 @@ public abstract class CatLog {
Rlog.d("CAT", caller + ": " + msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static void e(Object caller, String msg) {
String className = caller.getClass().getName();
Rlog.e("CAT", className.substring(className.lastIndexOf('.') + 1) + ": "
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 5544292292..9ed5eb9d84 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -29,6 +29,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources.NotFoundException;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.LocaleList;
import android.os.Message;
@@ -53,13 +54,13 @@ import java.util.List;
import java.util.Locale;
class RilMessage {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mId;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Object mData;
ResultCode mResCode;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
RilMessage(int msgId, String rawData) {
mId = msgId;
mData = rawData;
@@ -89,13 +90,13 @@ public class CatService extends Handler implements AppInterface {
// Protects singleton instance lazy initialization.
@UnsupportedAppUsage
private static final Object sInstanceLock = new Object();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static CatService[] sInstance = null;
@UnsupportedAppUsage
private CommandsInterface mCmdIf;
@UnsupportedAppUsage
private Context mContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CatCmdMessage mCurrntCmd = null;
@UnsupportedAppUsage
private CatCmdMessage mMenuCmd = null;
@@ -137,7 +138,7 @@ public class CatService extends Handler implements AppInterface {
static final String STK_DEFAULT = "Default Message";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mSlotId;
/* For multisim catservice should not be singleton */
@@ -560,7 +561,7 @@ public class CatService extends Handler implements AppInterface {
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendTerminalResponse(CommandDetails cmdDet,
ResultCode resultCode, boolean includeAdditionalInfo,
int additionalInfo, ResponseData resp) {
@@ -1103,7 +1104,7 @@ public class CatService extends Handler implements AppInterface {
mCurrntCmd = null;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isStkAppInstalled() {
Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
PackageManager pm = mContext.getPackageManager();
diff --git a/src/java/com/android/internal/telephony/cat/CommandDetails.java b/src/java/com/android/internal/telephony/cat/CommandDetails.java
index 51a6dd4e37..133567833c 100644
--- a/src/java/com/android/internal/telephony/cat/CommandDetails.java
+++ b/src/java/com/android/internal/telephony/cat/CommandDetails.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -29,13 +30,13 @@ abstract class ValueObject {
* {@hide}
*/
public class CommandDetails extends ValueObject implements Parcelable {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean compRequired;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int commandNumber;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int typeOfCommand;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int commandQualifier;
@Override
@@ -97,7 +98,7 @@ public class CommandDetails extends ValueObject implements Parcelable {
class DeviceIdentities extends ValueObject {
public int sourceId;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int destinationId;
@Override
@@ -108,7 +109,7 @@ class DeviceIdentities extends ValueObject {
// Container class to hold icon identifier value.
class IconId extends ValueObject {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int recordNumber;
boolean selfExplanatory;
diff --git a/src/java/com/android/internal/telephony/cat/CommandParams.java b/src/java/com/android/internal/telephony/cat/CommandParams.java
index 111f0cdea5..b9de4d1f2d 100755
--- a/src/java/com/android/internal/telephony/cat/CommandParams.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParams.java
@@ -18,23 +18,24 @@ package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
+import android.os.Build;
/**
* Container class for proactive command parameters.
*
*/
class CommandParams {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
CommandDetails mCmdDet;
// Variable to track if an optional icon load has failed.
boolean mLoadIconFailed = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
CommandParams(CommandDetails cmdDet) {
mCmdDet = cmdDet;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
AppInterface.CommandType getCommandType() {
return AppInterface.CommandType.fromInt(mCmdDet.typeOfCommand);
}
@@ -48,10 +49,10 @@ class CommandParams {
}
class DisplayTextParams extends CommandParams {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
TextMessage mTextMsg;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
DisplayTextParams(CommandDetails cmdDet, TextMessage textMsg) {
super(cmdDet);
mTextMsg = textMsg;
@@ -112,7 +113,7 @@ class PlayToneParams extends CommandParams {
TextMessage mTextMsg;
ToneSettings mSettings;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
PlayToneParams(CommandDetails cmdDet, TextMessage textMsg,
Tone tone, Duration duration, boolean vibrate) {
super(cmdDet);
@@ -170,7 +171,7 @@ class SelectItemParams extends CommandParams {
Menu mMenu = null;
boolean mLoadTitleIcon = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
SelectItemParams(CommandDetails cmdDet, Menu menu, boolean loadTitleIcon) {
super(cmdDet);
mMenu = menu;
@@ -200,7 +201,7 @@ class SelectItemParams extends CommandParams {
class GetInputParams extends CommandParams {
Input mInput = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
GetInputParams(CommandDetails cmdDet, Input input) {
super(cmdDet);
mInput = input;
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 9284c766ad..7fbebfad8e 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -26,6 +26,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
@@ -43,7 +44,7 @@ import java.util.Locale;
*/
class CommandParamsFactory extends Handler {
private static CommandParamsFactory sInstance = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IconLoader mIconLoader;
private CommandParams mCmdParams = null;
private int mIconLoadState = LOAD_NO_ICON;
@@ -295,7 +296,7 @@ class CommandParamsFactory extends Handler {
* @return A ComprehensionTlv object that has the tag value of {@code tag}.
* If no object is found with the tag, null is returned.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ComprehensionTlv searchForTag(ComprehensionTlvTag tag,
List<ComprehensionTlv> ctlvs) {
Iterator<ComprehensionTlv> iter = ctlvs.iterator();
@@ -314,7 +315,7 @@ class CommandParamsFactory extends Handler {
* @return A ComprehensionTlv object that has the tag value of {@code tag}.
* If no object is found with the tag, null is returned.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ComprehensionTlv searchForNextTag(ComprehensionTlvTag tag,
Iterator<ComprehensionTlv> iter) {
int tagValue = tag.value();
@@ -1109,7 +1110,7 @@ class CommandParamsFactory extends Handler {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void dispose() {
mIconLoader.dispose();
mIconLoader = null;
diff --git a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
index 3651a40cd2..5542b656b3 100644
--- a/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
+++ b/src/java/com/android/internal/telephony/cat/ComprehensionTlv.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.telephony.Rlog;
@@ -58,7 +59,7 @@ public class ComprehensionTlv {
mRawValue = data;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getTag() {
return mTag;
}
@@ -67,17 +68,17 @@ public class ComprehensionTlv {
return mCr;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getLength() {
return mLength;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getValueIndex() {
return mValueIndex;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public byte[] getRawValue() {
return mRawValue;
}
diff --git a/src/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java b/src/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java
index f8ba49f803..30aae46356 100644
--- a/src/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java
+++ b/src/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
* Enumeration for representing the tag value of COMPREHENSION-TLV objects. If
@@ -71,7 +72,7 @@ public enum ComprehensionTlvTag {
*
* @return Actual tag value of this object
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int value() {
return mValue;
}
diff --git a/src/java/com/android/internal/telephony/cat/Duration.java b/src/java/com/android/internal/telephony/cat/Duration.java
index 412be9acc4..f2f9b8389f 100644
--- a/src/java/com/android/internal/telephony/cat/Duration.java
+++ b/src/java/com/android/internal/telephony/cat/Duration.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,9 +28,9 @@ import android.os.Parcelable;
* {@hide}
*/
public class Duration implements Parcelable {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int timeInterval;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public TimeUnit timeUnit;
public enum TimeUnit {
@@ -43,7 +44,7 @@ public class Duration implements Parcelable {
mValue = value;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int value() {
return mValue;
}
diff --git a/src/java/com/android/internal/telephony/cat/IconLoader.java b/src/java/com/android/internal/telephony/cat/IconLoader.java
index 94b5348dff..c95f181968 100644
--- a/src/java/com/android/internal/telephony/cat/IconLoader.java
+++ b/src/java/com/android/internal/telephony/cat/IconLoader.java
@@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -101,7 +102,7 @@ class IconLoader extends Handler {
startLoadingIcon(recordNumbers[0]);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void loadIcon(int recordNumber, Message msg) {
if (msg == null) {
return;
diff --git a/src/java/com/android/internal/telephony/cat/Menu.java b/src/java/com/android/internal/telephony/cat/Menu.java
index 7606007e42..d9e24a7b42 100644
--- a/src/java/com/android/internal/telephony/cat/Menu.java
+++ b/src/java/com/android/internal/telephony/cat/Menu.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,7 +31,7 @@ import java.util.List;
*/
public class Menu implements Parcelable {
public List<Item> items;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<TextAttribute> titleAttrs;
public PresentationType presentationType;
public String title;
diff --git a/src/java/com/android/internal/telephony/cat/ResponseData.java b/src/java/com/android/internal/telephony/cat/ResponseData.java
index 8c7f24844c..222dc96752 100644
--- a/src/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/src/java/com/android/internal/telephony/cat/ResponseData.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.SystemProperties;
import android.text.TextUtils;
@@ -31,7 +32,7 @@ import java.util.TimeZone;
abstract class ResponseData {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ResponseData() {
}
@@ -39,7 +40,7 @@ abstract class ResponseData {
* Format the data appropriate for TERMINAL RESPONSE and write it into
* the ByteArrayOutputStream object.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public abstract void format(ByteArrayOutputStream buf);
public static void writeLength(ByteArrayOutputStream buf, int length) {
diff --git a/src/java/com/android/internal/telephony/cat/ResultCode.java b/src/java/com/android/internal/telephony/cat/ResultCode.java
index dca2999f21..6635eea1f0 100644
--- a/src/java/com/android/internal/telephony/cat/ResultCode.java
+++ b/src/java/com/android/internal/telephony/cat/ResultCode.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
@@ -197,7 +198,7 @@ public enum ResultCode {
* Retrieves the actual result code that this object represents.
* @return Actual result code
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int value() {
return mCode;
}
diff --git a/src/java/com/android/internal/telephony/cat/ResultException.java b/src/java/com/android/internal/telephony/cat/ResultException.java
index 208aa9aa10..0de9ffe578 100644
--- a/src/java/com/android/internal/telephony/cat/ResultException.java
+++ b/src/java/com/android/internal/telephony/cat/ResultException.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
@@ -29,7 +30,7 @@ public class ResultException extends CatException {
private int mAdditionalInfo;
private String mExplanation;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ResultException(ResultCode result) {
super();
diff --git a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
index bd8b0a8027..c25b59edac 100755
--- a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
+++ b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.telephony.SubscriptionManager;
@@ -39,13 +40,13 @@ class RilMessageDecoder extends StateMachine {
private static final int CMD_PARAMS_READY = 2;
// members
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CommandParamsFactory mCmdParamsFactory = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private RilMessage mCurrentRilMessage = null;
private Handler mCaller = null;
private static int mSimCount = 0;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static RilMessageDecoder[] mInstance = null;
// States
@@ -60,7 +61,7 @@ class RilMessageDecoder extends StateMachine {
* @param fh
* @return RilMesssageDecoder
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static synchronized RilMessageDecoder getInstance(Handler caller, IccFileHandler fh,
Context context, int slotId) {
if (null == mInstance) {
@@ -89,7 +90,7 @@ class RilMessageDecoder extends StateMachine {
*
* @param rilMsg
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void sendStartDecodingMessageParams(RilMessage rilMsg) {
Message msg = obtainMessage(CMD_START);
msg.obj = rilMsg;
@@ -109,7 +110,7 @@ class RilMessageDecoder extends StateMachine {
sendMessage(msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendCmdForExecution(RilMessage rilMsg) {
Message msg = mCaller.obtainMessage(CatService.MSG_ID_RIL_MSG_DECODED,
new RilMessage(rilMsg));
diff --git a/src/java/com/android/internal/telephony/cat/TextMessage.java b/src/java/com/android/internal/telephony/cat/TextMessage.java
index 96ecd94ef4..6991675d5c 100644
--- a/src/java/com/android/internal/telephony/cat/TextMessage.java
+++ b/src/java/com/android/internal/telephony/cat/TextMessage.java
@@ -18,22 +18,23 @@ package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
public class TextMessage implements Parcelable {
public String title = "";
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String text = null;
public Bitmap icon = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean iconSelfExplanatory = false;
public boolean isHighPriority = false;
public boolean responseNeeded = true;
public boolean userClear = false;
public Duration duration = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
TextMessage() {
}
diff --git a/src/java/com/android/internal/telephony/cat/ValueParser.java b/src/java/com/android/internal/telephony/cat/ValueParser.java
index 13ddee9500..7c0913629f 100644
--- a/src/java/com/android/internal/telephony/cat/ValueParser.java
+++ b/src/java/com/android/internal/telephony/cat/ValueParser.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cat;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.cat.Duration.TimeUnit;
@@ -61,7 +62,7 @@ abstract class ValueParser {
* Command Details object is found, ResultException is thrown.
* @throws ResultException
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static DeviceIdentities retrieveDeviceIdentities(ComprehensionTlv ctlv)
throws ResultException {
@@ -214,7 +215,7 @@ abstract class ValueParser {
* @return A list of TextAttribute objects
* @throws ResultException
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static List<TextAttribute> retrieveTextAttribute(ComprehensionTlv ctlv)
throws ResultException {
ArrayList<TextAttribute> lst = new ArrayList<TextAttribute>();
@@ -275,7 +276,7 @@ abstract class ValueParser {
* @return String corresponding to the alpha identifier
* @throws ResultException
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static String retrieveAlphaId(ComprehensionTlv ctlv, boolean noAlphaUsrCnf)
throws ResultException {
@@ -312,7 +313,7 @@ abstract class ValueParser {
* @return A Java String object decoded from the Text object
* @throws ResultException
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static String retrieveTextString(ComprehensionTlv ctlv) throws ResultException {
byte[] rawValue = ctlv.getRawValue();
int valueIndex = ctlv.getValueIndex();
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 61377dc2a1..fd604a0d2f 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -188,10 +188,11 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
* Process Cell Broadcast, Voicemail Notification, and other 3GPP/3GPP2-specific messages.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return true if the message was handled here; false to continue processing
*/
@Override
- protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) {
+ protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) {
SmsMessage sms = (SmsMessage) smsb;
boolean isBroadcastType = (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType());
@@ -217,7 +218,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
case SmsEnvelope.TELESERVICE_VMN:
case SmsEnvelope.TELESERVICE_MWI:
// handle voicemail indication
- handleVoicemailTeleservice(sms);
+ handleVoicemailTeleservice(sms, smsSource);
return Intents.RESULT_SMS_HANDLED;
case SmsEnvelope.TELESERVICE_WMT:
@@ -258,10 +259,10 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
if (SmsEnvelope.TELESERVICE_WAP == teleService) {
return processCdmaWapPdu(sms.getUserData(), sms.mMessageRef,
sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
- sms.getTimestampMillis());
+ sms.getTimestampMillis(), smsSource);
}
- return dispatchNormalMessage(smsb);
+ return dispatchNormalMessage(smsb, smsSource);
}
/**
@@ -309,7 +310,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
*
* @param sms the message to process
*/
- private void handleVoicemailTeleservice(SmsMessage sms) {
+ private void handleVoicemailTeleservice(SmsMessage sms, @SmsSource int smsSource) {
int voicemailCount = sms.getNumOfVoicemails();
if (DBG) log("Voicemail count=" + voicemailCount);
@@ -324,7 +325,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
// update voice mail count in phone
mPhone.setVoiceMessageCount(voicemailCount);
// update metrics
- addVoicemailSmsToMetrics();
+ addVoicemailSmsToMetrics(smsSource);
}
/**
@@ -338,7 +339,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
* to applications
*/
private int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address, String dispAddr,
- long timestamp) {
+ long timestamp, @SmsSource int smsSource) {
int index = 0;
int msgType = (0xFF & pdu[index++]);
@@ -386,7 +387,8 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
referenceNumber,
segment, totalSegments, true, HexDump.toHexString(userData),
false /* isClass0 */,
- mPhone.getSubId());
+ mPhone.getSubId(),
+ smsSource);
// de-duping is done only for text messages
return addTrackerToRawTableAndSendMessage(tracker, false /* don't de-dup */);
@@ -431,9 +433,10 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
/**
* Add voicemail indication SMS 0 to metrics.
*/
- private void addVoicemailSmsToMetrics() {
+ private void addVoicemailSmsToMetrics(@SmsSource int smsSource) {
mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP2);
+ mPhone.getSmsStats().onIncomingSmsVoicemail(true /* is3gpp2 */, smsSource);
}
/**
@@ -442,16 +445,16 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
*
* adb shell am broadcast -a com.android.internal.telephony.cdma.TEST_TRIGGER_CELL_BROADCAST \
* --ei service_category 4097 \
- * --es bearer_data_string 00031303900801C00D0101015C02D00002BFD1931054D208119313D3D10815D05 \
- * 493925391C81193D48814D3D555120810D3D0D3D3925393C810D3D5539516480B481393D495120810D1539514 \
- * 9053081054925693D390481553951253080D0C4D481413481354D500
+ * --es bearer_data_string 0003104D200801C00D010101510278000260A34C834E4208D327CF8882BC1A53A \
+ * 4CE8E8234FA4829CFAB52420873E1CF9D2674F410E7D59D52CA05A8274FA5524208716754A506620834A4DA9F \
+ * 3A0A0AB3AA499881A316A8284D41369D40
*
* adb shell am broadcast -a com.android.internal.telephony.cdma.TEST_TRIGGER_CELL_BROADCAST \
* --ei service_category 4097 \
- * --es bearer_data_string 00031303900801C00D0101015C02D00002BFD1931054D208119313D3D10815D05 \
- * 493925391C81193D48814D3D555120810D3D0D3D3925393C810D3D5539516480B481393D495120810D1539514 \
- * 9053081054925693D390481553951253080D0C4D481413481354D500 \
- * --ei phone_id 0 \
+ * --es bearer_data_string 0003104D200801C00D010101510278000260A34C834E4208D327CF8882BC1A53A \
+ * 4CE8E8234FA4829CFAB52420873E1CF9D2674F410E7D59D52CA05A8274FA5524208716754A506620834A4DA9F \
+ * 3A0A0AB3AA499881A316A8284D41369D40 \
+ * --ei phone_id 0
*/
private class CdmaCbTestBroadcastReceiver extends CbTestBroadcastReceiver {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
index 88872c92e9..6e06bd4d1c 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
@@ -16,9 +16,17 @@
package com.android.internal.telephony.cdma;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
@@ -65,7 +73,7 @@ public final class CdmaMmiCode extends Handler implements MmiCode {
UiccCardApplication mUiccApplication;
String mAction; // ACTION_REGISTER
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mSc; // Service Code
String mSia, mSib, mSic; // Service Info a,b,c
String mPoundString; // Entire MMI string up to and including #
@@ -136,7 +144,7 @@ public final class CdmaMmiCode extends Handler implements MmiCode {
/** make empty strings be null.
* Regexp returns empty strings for empty groups
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static String
makeEmptyNull (String s) {
if (s != null && s.length() == 0) return null;
@@ -381,6 +389,56 @@ public final class CdmaMmiCode extends Handler implements MmiCode {
return null;
}
+ public static String getCallForwardingPrefixAndNumber(int action, int reason, String number) {
+ String prefixWithNum = "";
+ switch(reason) {
+ case CF_REASON_UNCONDITIONAL: {
+ if (action == CF_ACTION_REGISTRATION) {
+ prefixWithNum = "*72" + number;
+ } else if (action == CF_ACTION_DISABLE) {
+ prefixWithNum = "*720";
+ }
+ break;
+ }
+ case CF_REASON_BUSY: {
+ if (action == CF_ACTION_REGISTRATION) {
+ prefixWithNum = "*90" + number;
+ } else if (action == CF_ACTION_DISABLE) {
+ prefixWithNum = "*900";
+ }
+ break;
+ }
+ case CF_REASON_NO_REPLY: {
+ if (action == CF_ACTION_REGISTRATION) {
+ prefixWithNum = "*92" + number;
+ } else if (action == CF_ACTION_DISABLE) {
+ prefixWithNum = "*920";
+ }
+ break;
+ }
+ case CF_REASON_NOT_REACHABLE: {
+ if (action == CF_ACTION_REGISTRATION) {
+ prefixWithNum = "*68" + number;
+ } else if (action == CF_ACTION_DISABLE) {
+ prefixWithNum = "*680";
+ }
+ break;
+ }
+ default:
+ Rlog.d(LOG_TAG, "getCallForwardingPrefix not match any prefix");
+ break;
+ }
+ return prefixWithNum;
+ }
+
+ public static String getCallWaitingPrefix(boolean enable) {
+ if (enable) {
+ return "*74";
+ } else {
+ return "*740";
+ }
+ }
+
@Override
public boolean isNetworkInitiatedUssd() {
Rlog.w(LOG_TAG, "isNetworkInitiated is not implemented in CdmaMmiCode");
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index cb7d5a162c..1742db7cfc 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -19,10 +19,10 @@ package com.android.internal.telephony.cdma;
import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Message;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
-import android.util.Pair;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.GsmCdmaPhone;
@@ -30,6 +30,7 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsController;
import com.android.internal.telephony.SmsDispatchersController;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
@@ -45,7 +46,7 @@ public class CdmaSMSDispatcher extends SMSDispatcher {
Rlog.d(TAG, "CdmaSMSDispatcher created");
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getFormat() {
return SmsConstants.FORMAT_3GPP2;
@@ -63,8 +64,9 @@ public class CdmaSMSDispatcher extends SMSDispatcher {
@Override
protected void handleStatusReport(Object o) {
if (o instanceof SmsMessage) {
- if (VDBG) Rlog.d(TAG, "calling handleCdmaStatusReport()");
- handleCdmaStatusReport((SmsMessage) o);
+ if (VDBG) Rlog.d(TAG, "calling handleSmsStatusReport()");
+ byte[] pdu = ((SmsMessage) o).getPdu();
+ mSmsDispatchersController.handleSmsStatusReport(SmsConstants.FORMAT_3GPP2, pdu);
} else {
Rlog.e(TAG, "handleStatusReport() called for object type " + o.getClass().getName());
}
@@ -95,32 +97,6 @@ public class CdmaSMSDispatcher extends SMSDispatcher {
protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
return SMSDispatcherUtil.calculateLengthCdma(messageBody, use7bitOnly);
}
- /**
- * Called from parent class to handle status report from {@code CdmaInboundSmsHandler}.
- * @param sms the CDMA SMS message to process
- */
- @UnsupportedAppUsage
- private void handleCdmaStatusReport(SmsMessage sms) {
- byte[] pdu = sms.getPdu();
- int messageRef = sms.mMessageRef;
- boolean handled = false;
- for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
- SmsTracker tracker = deliveryPendingList.get(i);
- if (tracker.mMessageRef == messageRef) {
- Pair<Boolean, Boolean> result =
- mSmsDispatchersController.handleSmsStatusReport(tracker, getFormat(), pdu);
- if (result.second) {
- deliveryPendingList.remove(i);
- }
- handled = true;
- break; // Only expect to see one tracker matching this message.
- }
- }
- if (!handled) {
- // Try to find the sent SMS from the map in ImsSmsDispatcher.
- mSmsDispatchersController.handleSentOverImsStatusReport(messageRef, getFormat(), pdu);
- }
- }
/** {@inheritDoc} */
@Override
@@ -134,7 +110,7 @@ public class CdmaSMSDispatcher extends SMSDispatcher {
+ " mMessageRef=" + tracker.mMessageRef
+ " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
+ " SS=" + ss
- + " id=" + tracker.mMessageId);
+ + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java b/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
index b261dc5307..0fac3343a1 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java
@@ -19,6 +19,7 @@ package com.android.internal.telephony.cdma;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -152,7 +153,7 @@ public class CdmaSubscriptionSourceManager extends Handler {
* Returns the current CDMA subscription source value
* @return CDMA subscription source value
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getCdmaSubscriptionSource() {
log("getcdmasubscriptionSource: " + mCdmaSubscriptionSource.get());
return mCdmaSubscriptionSource.get();
diff --git a/src/java/com/android/internal/telephony/cdma/EriInfo.java b/src/java/com/android/internal/telephony/cdma/EriInfo.java
index bd27f94370..bff4b0eb96 100644
--- a/src/java/com/android/internal/telephony/cdma/EriInfo.java
+++ b/src/java/com/android/internal/telephony/cdma/EriInfo.java
@@ -16,16 +16,16 @@
package com.android.internal.telephony.cdma;
-import android.telephony.CdmaEriInformation;
+import android.telephony.TelephonyManager;
public final class EriInfo {
- public static final int ROAMING_INDICATOR_ON = CdmaEriInformation.ERI_ON;
- public static final int ROAMING_INDICATOR_OFF = CdmaEriInformation.ERI_OFF;
- public static final int ROAMING_INDICATOR_FLASH = CdmaEriInformation.ERI_FLASH;
+ public static final int ROAMING_INDICATOR_ON = TelephonyManager.ERI_ON;
+ public static final int ROAMING_INDICATOR_OFF = TelephonyManager.ERI_OFF;
+ public static final int ROAMING_INDICATOR_FLASH = TelephonyManager.ERI_FLASH;
- public static final int ROAMING_ICON_MODE_NORMAL = CdmaEriInformation.ERI_ICON_MODE_NORMAL;
- public static final int ROAMING_ICON_MODE_FLASH = CdmaEriInformation.ERI_ICON_MODE_FLASH;
+ public static final int ROAMING_ICON_MODE_NORMAL = TelephonyManager.ERI_ICON_MODE_NORMAL;
+ public static final int ROAMING_ICON_MODE_FLASH = TelephonyManager.ERI_ICON_MODE_FLASH;
public int roamingIndicator;
public int iconIndex;
diff --git a/src/java/com/android/internal/telephony/cdnr/BrandOverrideEfData.java b/src/java/com/android/internal/telephony/cdnr/BrandOverrideEfData.java
index 447228c3c8..330525ad07 100644
--- a/src/java/com/android/internal/telephony/cdnr/BrandOverrideEfData.java
+++ b/src/java/com/android/internal/telephony/cdnr/BrandOverrideEfData.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.cdnr;
+import com.android.internal.telephony.uicc.IccRecords;
+
import java.util.Arrays;
import java.util.List;
@@ -35,9 +37,8 @@ public final class BrandOverrideEfData implements EfData {
}
@Override
- public int getServiceProviderNameDisplayCondition() {
- // No SPN in roaming network, no PLMN in home network
- return 0;
+ public int getServiceProviderNameDisplayCondition(boolean isRoaming) {
+ return IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN;
}
@Override
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java b/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java
index faa5e1f73d..0ded77e74b 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierConfigEfData.java
@@ -47,7 +47,7 @@ public final class CarrierConfigEfData implements EfData {
}
@Override
- public int getServiceProviderNameDisplayCondition() {
+ public int getServiceProviderNameDisplayCondition(boolean isRoaming) {
int condition = mConfig.getInt(
CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT,
IccRecords.INVALID_CARRIER_NAME_DISPLAY_CONDITION_BITMASK);
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
index dad9985e33..6299dacc20 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -29,14 +29,18 @@ import static com.android.internal.telephony.cdnr.EfData.EF_SOURCE_VOICE_OPERATO
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.SparseArray;
+import com.android.internal.R;
import com.android.internal.telephony.GsmCdmaPhone;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.cdnr.EfData.EFSource;
@@ -196,7 +200,8 @@ public class CarrierDisplayNameResolver {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEf.size(); i++) {
EfData p = mEf.valueAt(i);
- sb.append("{spnDisplayCondition = " + p.getServiceProviderNameDisplayCondition()
+ sb.append("{spnDisplayCondition = "
+ + p.getServiceProviderNameDisplayCondition(isRoaming())
+ ", spn = " + p.getServiceProviderName()
+ ", spdiList = " + p.getServiceProviderDisplayInformation()
+ ", pnnList = " + p.getPlmnNetworkNameList()
@@ -235,11 +240,12 @@ public class CarrierDisplayNameResolver {
@NonNull
private CarrierDisplayNameConditionRule getDisplayRule() {
+ boolean isRoaming = isRoaming();
for (int i = 0; i < mEf.size(); i++) {
- if (mEf.valueAt(i).getServiceProviderNameDisplayCondition()
+ if (mEf.valueAt(i).getServiceProviderNameDisplayCondition(isRoaming)
!= IccRecords.INVALID_CARRIER_NAME_DISPLAY_CONDITION_BITMASK) {
return new CarrierDisplayNameConditionRule(
- mEf.valueAt(i).getServiceProviderNameDisplayCondition());
+ mEf.valueAt(i).getServiceProviderNameDisplayCondition(isRoaming));
}
}
return DEFAULT_CARRIER_DISPLAY_NAME_RULE;
@@ -285,20 +291,21 @@ public class CarrierDisplayNameResolver {
return Collections.EMPTY_LIST;
}
+ private boolean isRoaming() {
+ // Currently use the roaming state from ServiceState.
+ // EF_SPDI is only used when determine the service provider name and PLMN network name
+ // display condition rule.
+ // All the PLMNs will be considered HOME PLMNs if there is a brand override.
+ return getServiceState().getRoaming()
+ && !getEfSpdi().contains(getServiceState().getOperatorNumeric());
+ }
+
private CarrierDisplayNameData getCarrierDisplayNameFromEf() {
CarrierDisplayNameConditionRule displayRule = getDisplayRule();
+ String registeredPlmnName = getServiceState().getOperatorAlpha();
String registeredPlmnNumeric = getServiceState().getOperatorNumeric();
- List<String> efSpdi = getEfSpdi();
- // Currently use the roaming state from ServiceState.
- // EF_SPDI is only used when determine the service provider name and PLMN network name
- // display condition rule.
- // All the PLMNs will be considered HOME PLMNs if there is a brand override.
- boolean isRoaming = getServiceState().getRoaming()
- && !efSpdi.contains(registeredPlmnNumeric);
- boolean showSpn = displayRule.shouldShowSpn(isRoaming);
- boolean showPlmn = displayRule.shouldShowPnn(isRoaming);
String spn = getEfSpn();
// Resolve the PLMN network name
@@ -306,21 +313,31 @@ public class CarrierDisplayNameResolver {
List<PlmnNetworkName> efPnn = getEfPnn();
String plmn = null;
- if (efOpl.isEmpty()) {
- // If the EF_OPL is not present, then the first record in EF_PNN is used for the
- // default network name when registered in the HPLMN or an EHPLMN(if the EHPLMN list
- // is present).
- plmn = efPnn.isEmpty() ? "" : getPlmnNetworkName(efPnn.get(0));
+ if (isRoaming()) {
+ plmn = registeredPlmnName;
} else {
- // TODO: Check the TAC/LAC & registered PLMN numeric in OPL list to determine which
- // PLMN name should be used to override the current one.
+ if (efOpl.isEmpty()) {
+ // If the EF_OPL is not present, then the first record in EF_PNN is used for the
+ // default network name when registered in the HPLMN or an EHPLMN(if the EHPLMN
+ // list is present).
+ plmn = efPnn.isEmpty() ? "" : getPlmnNetworkName(efPnn.get(0));
+ } else {
+ // TODO: Check the TAC/LAC & registered PLMN numeric in OPL list to determine which
+ // PLMN name should be used to override the current one.
+ }
}
- // If no PLMN override is present, then the PLMN should be displayed numerically.
+ // If no PLMN override is present, then the PLMN should be displayed:
+ // - operator alpha if it's not empty.
+ // - operator numeric.
if (TextUtils.isEmpty(plmn)) {
- plmn = registeredPlmnNumeric;
+ plmn = TextUtils.isEmpty(registeredPlmnName) ? registeredPlmnNumeric
+ : registeredPlmnName;
}
+ boolean showSpn = displayRule.shouldShowSpn(spn);
+ boolean showPlmn = TextUtils.isEmpty(spn) || displayRule.shouldShowPlmn(plmn);
+
return new CarrierDisplayNameData.Builder()
.setSpn(spn)
.setShowSpn(showSpn)
@@ -333,8 +350,14 @@ public class CarrierDisplayNameResolver {
CarrierDisplayNameData rawCarrierDisplayNameData) {
PersistableBundle config = getCarrierConfig();
boolean useRootLocale = config.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
- Resources r = mContext.getResources();
- if (useRootLocale) r.getConfiguration().setLocale(Locale.ROOT);
+ Context displayNameContext = mContext;
+ if (useRootLocale) {
+ Configuration displayNameConfig = mContext.getResources().getConfiguration();
+ displayNameConfig.setLocale(Locale.ROOT);
+ // Create a new Context for this temporary change
+ displayNameContext = mContext.createConfigurationContext(displayNameConfig);
+ }
+ Resources r = displayNameContext.getResources();
String[] wfcSpnFormats = r.getStringArray(com.android.internal.R.array.wfcSpnFormats);
WfcCarrierNameFormatter wfcFormatter = new WfcCarrierNameFormatter(config, wfcSpnFormats,
getServiceState().getState() == ServiceState.STATE_POWER_OFF);
@@ -342,7 +365,11 @@ public class CarrierDisplayNameResolver {
// Override the spn, data spn, plmn by wifi-calling
String wfcSpn = wfcFormatter.formatVoiceName(rawCarrierDisplayNameData.getSpn());
String wfcDataSpn = wfcFormatter.formatDataName(rawCarrierDisplayNameData.getSpn());
- String wfcPlmn = wfcFormatter.formatVoiceName(rawCarrierDisplayNameData.getPlmn());
+ List<PlmnNetworkName> efPnn = getEfPnn();
+ String plmn = efPnn.isEmpty() ? "" : getPlmnNetworkName(efPnn.get(0));
+ String wfcPlmn = wfcFormatter.formatVoiceName(
+ TextUtils.isEmpty(plmn) ? rawCarrierDisplayNameData.getPlmn() : plmn);
+
CarrierDisplayNameData result = rawCarrierDisplayNameData;
if (!TextUtils.isEmpty(wfcSpn) && !TextUtils.isEmpty(wfcDataSpn)) {
result = new CarrierDisplayNameData.Builder()
@@ -359,6 +386,48 @@ public class CarrierDisplayNameResolver {
return result;
}
+ private CarrierDisplayNameData getCarrierDisplayNameFromCrossSimCallingOverride(
+ CarrierDisplayNameData rawCarrierDisplayNameData) {
+ PersistableBundle config = getCarrierConfig();
+ int crossSimSpnFormatIdx =
+ config.getInt(CarrierConfigManager.KEY_CROSS_SIM_SPN_FORMAT_INT);
+ boolean useRootLocale =
+ config.getBoolean(CarrierConfigManager.KEY_WFC_SPN_USE_ROOT_LOCALE);
+
+ String[] crossSimSpnFormats = SubscriptionManager.getResourcesForSubId(
+ mPhone.getContext(),
+ mPhone.getSubId(), useRootLocale)
+ .getStringArray(R.array.crossSimSpnFormats);
+
+ if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) {
+ Rlog.e(TAG, "updateSpnDisplay: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: "
+ + crossSimSpnFormatIdx);
+ crossSimSpnFormatIdx = 0;
+ }
+ String crossSimSpnFormat = crossSimSpnFormats[crossSimSpnFormatIdx];
+ // Override the spn, data spn, plmn by Cross-SIM Calling
+ List<PlmnNetworkName> efPnn = getEfPnn();
+ String plmn = efPnn.isEmpty() ? "" : getPlmnNetworkName(efPnn.get(0));
+ CarrierDisplayNameData result = rawCarrierDisplayNameData;
+ String rawSpn = rawCarrierDisplayNameData.getSpn();
+ String rawPlmn = TextUtils.isEmpty(plmn) ? rawCarrierDisplayNameData.getPlmn() : plmn;
+ String crossSimSpn = String.format(crossSimSpnFormat, rawSpn);
+ String crossSimPlmn = String.format(crossSimSpnFormat, plmn);
+ if (!TextUtils.isEmpty(rawSpn) && !TextUtils.isEmpty(crossSimSpn)) {
+ result = new CarrierDisplayNameData.Builder()
+ .setSpn(crossSimSpn)
+ .setDataSpn(crossSimSpn)
+ .setShowSpn(true)
+ .build();
+ } else if (!TextUtils.isEmpty(rawPlmn) && !TextUtils.isEmpty(crossSimPlmn)) {
+ result = new CarrierDisplayNameData.Builder()
+ .setPlmn(crossSimPlmn)
+ .setShowPlmn(true)
+ .build();
+ }
+ return result;
+ }
+
/**
* Override the given carrier display name data {@code data} by out of service rule.
* @param data the carrier display name data need to be overridden.
@@ -366,12 +435,15 @@ public class CarrierDisplayNameResolver {
*/
private CarrierDisplayNameData getOutOfServiceDisplayName(CarrierDisplayNameData data) {
// Out of service/Power off/Emergency Only override
- // 1) In flight mode(service state is ServiceState.STATE_POWER_OFF), or the service
- // state is ServiceState.STATE_OUT_OF_SERVICE but emergency call is not allowed.
+ // 1) In flight mode (service state is ServiceState.STATE_POWER_OFF).
+ // showPlmn = true
+ // Only show null as PLMN
+ //
+ // 2) Service state is ServiceState.STATE_OUT_OF_SERVICE but emergency call is not allowed.
// showPlmn = true
// Only show "No Service" as PLMN
//
- // 2) Out of service but emergency call is allowed.
+ // 3) Out of service but emergency call is allowed.
// showPlmn = true
// Only show "Emergency call only" as PLMN
String plmn = null;
@@ -380,8 +452,10 @@ public class CarrierDisplayNameResolver {
boolean forceDisplayNoService =
mPhone.getServiceStateTracker().shouldForceDisplayNoService() && !isSimReady;
ServiceState ss = getServiceState();
- if (ss.getState() == ServiceState.STATE_POWER_OFF
- || forceDisplayNoService || !Phone.isEmergencyCallOnly()) {
+ if (ss.getState() == ServiceState.STATE_POWER_OFF && !forceDisplayNoService
+ && !Phone.isEmergencyCallOnly()) {
+ plmn = null;
+ } else if (forceDisplayNoService || !Phone.isEmergencyCallOnly()) {
plmn = mContext.getResources().getString(
com.android.internal.R.string.lockscreen_carrier_default);
} else {
@@ -400,12 +474,23 @@ public class CarrierDisplayNameResolver {
private void resolveCarrierDisplayName() {
CarrierDisplayNameData data = getCarrierDisplayNameFromEf();
if (DBG) Rlog.d(TAG, "CarrierName from EF: " + data);
- if (getCombinedRegState(getServiceState()) == ServiceState.STATE_IN_SERVICE) {
+ if ((mPhone.getImsPhone() != null) && (mPhone.getImsPhone().getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM)) {
+ data = getCarrierDisplayNameFromCrossSimCallingOverride(data);
+ if (DBG) {
+ Rlog.d(TAG, "CarrierName override by Cross-SIM Calling " + data);
+ }
+ } else if (mPhone.getServiceStateTracker().getCombinedRegState(getServiceState())
+ == ServiceState.STATE_IN_SERVICE) {
if (mPhone.isWifiCallingEnabled()) {
data = getCarrierDisplayNameFromWifiCallingOverride(data);
if (DBG) {
Rlog.d(TAG, "CarrierName override by wifi-calling " + data);
}
+ } else if (getServiceState().getState() == ServiceState.STATE_POWER_OFF) {
+ // data in service due to IWLAN but APM on and WFC not available
+ data = getOutOfServiceDisplayName(data);
+ if (DBG) Rlog.d(TAG, "Out of service carrierName (APM) " + data);
}
} else {
data = getOutOfServiceDisplayName(data);
@@ -452,16 +537,22 @@ public class CarrierDisplayNameResolver {
mDisplayConditionBitmask = carrierDisplayConditionBitmask;
}
- boolean shouldShowSpn(boolean isRoaming) {
- return !isRoaming || ((mDisplayConditionBitmask
+ boolean shouldShowSpn(String spn) {
+ //Check if show SPN is required.
+ Boolean showSpn = ((mDisplayConditionBitmask
& IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN)
== IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN);
+
+ return !TextUtils.isEmpty(spn) && showSpn;
}
- boolean shouldShowPnn(boolean isRoaming) {
- return isRoaming || ((mDisplayConditionBitmask
+ boolean shouldShowPlmn(String plmn) {
+ // Check if show PLMN is required.
+ Boolean showPlmn = ((mDisplayConditionBitmask
& IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_PLMN)
== IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_PLMN);
+
+ return !TextUtils.isEmpty(plmn) && showPlmn;
}
@Override
@@ -539,13 +630,4 @@ public class CarrierDisplayNameResolver {
return String.format(mDataFormat, name.trim());
}
}
-
- /**
- * Consider dataRegState if voiceRegState is OOS to determine SPN to be displayed.
- * @param ss service state.
- */
- private static int getCombinedRegState(ServiceState ss) {
- if (ss.getState() != ServiceState.STATE_IN_SERVICE) return ss.getDataRegistrationState();
- return ss.getState();
- }
}
diff --git a/src/java/com/android/internal/telephony/cdnr/EfData.java b/src/java/com/android/internal/telephony/cdnr/EfData.java
index 7fd82540e1..2e86862e8d 100644
--- a/src/java/com/android/internal/telephony/cdnr/EfData.java
+++ b/src/java/com/android/internal/telephony/cdnr/EfData.java
@@ -80,7 +80,7 @@ public interface EfData {
* .IccRecords.INVALID_CARRIER_NAME_DISPLAY_CONDITION_BITMASK} if it's not existed.
*/
@CarrierNameDisplayConditionBitmask
- default int getServiceProviderNameDisplayCondition() {
+ default int getServiceProviderNameDisplayCondition(boolean isRoaming) {
return IccRecords.INVALID_CARRIER_NAME_DISPLAY_CONDITION_BITMASK;
}
diff --git a/src/java/com/android/internal/telephony/cdnr/RuimEfData.java b/src/java/com/android/internal/telephony/cdnr/RuimEfData.java
index 3978ae8139..15bcb632a5 100644
--- a/src/java/com/android/internal/telephony/cdnr/RuimEfData.java
+++ b/src/java/com/android/internal/telephony/cdnr/RuimEfData.java
@@ -38,7 +38,7 @@ public final class RuimEfData implements EfData {
}
@Override
- public int getServiceProviderNameDisplayCondition() {
+ public int getServiceProviderNameDisplayCondition(boolean isRoaming) {
return mRuim.getCsimSpnDisplayCondition()
? IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN :
DEFAULT_CARRIER_NAME_DISPLAY_CONDITION_BITMASK;
diff --git a/src/java/com/android/internal/telephony/cdnr/UsimEfData.java b/src/java/com/android/internal/telephony/cdnr/UsimEfData.java
index 0604303cd8..2a9a07fe1a 100644
--- a/src/java/com/android/internal/telephony/cdnr/UsimEfData.java
+++ b/src/java/com/android/internal/telephony/cdnr/UsimEfData.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.cdnr;
import android.text.TextUtils;
+import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo;
import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName;
import com.android.internal.telephony.uicc.SIMRecords;
@@ -41,8 +42,16 @@ public final class UsimEfData implements EfData {
}
@Override
- public int getServiceProviderNameDisplayCondition() {
- return mUsim.getCarrierNameDisplayCondition();
+ public int getServiceProviderNameDisplayCondition(boolean isRoaming) {
+ if (isRoaming) {
+ // Show PLMN on roaming.
+ return IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_PLMN
+ | mUsim.getCarrierNameDisplayCondition();
+ } else {
+ // Show SPN on non-roaming.
+ return IccRecords.CARRIER_NAME_DISPLAY_CONDITION_BITMASK_SPN
+ | mUsim.getCarrierNameDisplayCondition();
+ }
}
@Override
diff --git a/src/java/com/android/internal/telephony/d2d/Communicator.java b/src/java/com/android/internal/telephony/d2d/Communicator.java
new file mode 100644
index 0000000000..1aac41be19
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/Communicator.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telecom.Connection;
+import android.telecom.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Responsible for facilitating device-to-device communication between both ends of a call.
+ */
+public class Communicator implements TransportProtocol.Callback {
+
+ /**
+ * Callback for events out of communicator.
+ */
+ public interface Callback {
+ void onMessagesReceived(@NonNull Set<Message> messages);
+ void onD2DAvailabilitychanged(boolean isAvailable);
+ }
+
+ public static final int MESSAGE_CALL_RADIO_ACCESS_TYPE = 1;
+ public static final int MESSAGE_CALL_AUDIO_CODEC = 2;
+ public static final int MESSAGE_DEVICE_BATTERY_STATE = 3;
+ public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4;
+
+ public static final int RADIO_ACCESS_TYPE_LTE = 1;
+ public static final int RADIO_ACCESS_TYPE_IWLAN = 2;
+ public static final int RADIO_ACCESS_TYPE_NR = 3;
+
+ public static final int AUDIO_CODEC_EVS = 1;
+ public static final int AUDIO_CODEC_AMR_WB = 2;
+ public static final int AUDIO_CODEC_AMR_NB = 3;
+
+ public static final int BATTERY_STATE_LOW = 1;
+ public static final int BATTERY_STATE_GOOD = 2;
+ public static final int BATTERY_STATE_CHARGING = 3;
+
+ public static final int COVERAGE_POOR = 1;
+ public static final int COVERAGE_GOOD = 2;
+
+ /**
+ * Encapsulates a D2D communication message.
+ */
+ public static class Message {
+ private int mType;
+ private int mValue;
+
+ public Message(int type, int value) {
+ mType = type;
+ mValue = value;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Message message = (Message) o;
+ return mType == message.mType && mValue == message.mValue;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mValue);
+ }
+
+ @Override
+ public String toString() {
+ return "Message{" + "mType=" + messageToString(mType) +", mValue="
+ + valueToString(mType, mValue) + '}';
+ }
+ }
+
+ private boolean mIsNegotiated;
+ private boolean mIsNegotiationAttempted;
+ private TransportProtocol mActiveTransport;
+ private List<TransportProtocol> mTransportProtocols = new ArrayList<>();
+ private Callback mCallback;
+
+ public Communicator(@NonNull List<TransportProtocol> transportProtocols,
+ @NonNull Callback callback) {
+ Log.i(this, "Initializing communicator with transports: %s",
+ transportProtocols.stream().map(p -> p.getClass().getSimpleName()).collect(
+ Collectors.joining(",")));
+ mTransportProtocols.addAll(transportProtocols);
+ mTransportProtocols.forEach(p -> p.setCallback(this));
+ mCallback = callback;
+ }
+
+ /**
+ * @return the active {@link TransportProtocol} which is being used for sending/receiving
+ * messages.
+ */
+ public @Nullable TransportProtocol getActiveTransport() {
+ return mActiveTransport;
+ }
+
+ /**
+ * Handles state changes for a call.
+ * @param id The call in question.
+ * @param state The new state.
+ */
+ public void onStateChanged(String id, @Connection.ConnectionState int state) {
+ Log.i(this, "onStateChanged: id=%s, newState=%d", id, state);
+ if (state == Connection.STATE_ACTIVE) {
+ // Protocol negotiation can start as we are active
+ if (mActiveTransport == null && !mIsNegotiationAttempted) {
+ mIsNegotiated = false;
+ mIsNegotiationAttempted = true;
+ Log.i(this, "onStateChanged: call active; negotiate D2D.");
+ negotiateNextProtocol();
+ }
+ }
+ }
+
+ /**
+ * Called by a {@link TransportProtocol} when negotiation of that protocol has succeeded.
+ * @param protocol The protocol.
+ */
+ @Override
+ public void onNegotiationSuccess(@NonNull TransportProtocol protocol) {
+ if (protocol != mActiveTransport) {
+ // Uh oh, shouldn't happen.
+ String activeTransportName = mActiveTransport == null ? "none"
+ : mActiveTransport.getClass().getSimpleName();
+ Log.w(this, "onNegotiationSuccess: ignored - %s negotiated but active transport is %s.",
+ protocol.getClass().getSimpleName(), activeTransportName);
+ }
+ Log.i(this, "onNegotiationSuccess: %s negotiated; setting active.",
+ protocol.getClass().getSimpleName());
+ mIsNegotiated = true;
+ notifyD2DStatus(true /* isAvailable */);
+ }
+
+ /**
+ * Called by a {@link TransportProtocol} when negotiation of that protocol has failed.
+ * @param protocol The protocol.
+ */
+ @Override
+ public void onNegotiationFailed(@NonNull TransportProtocol protocol) {
+ if (protocol != mActiveTransport) {
+ // Uh oh, shouldn't happen.
+ }
+ Log.i(this, "onNegotiationFailed: %s failed to negotiate.",
+ protocol.getClass().getSimpleName());
+ mIsNegotiated = false;
+ negotiateNextProtocol();
+ }
+
+ /**
+ * Called by a {@link TransportProtocol} to report incoming messages received via that
+ * transport.
+ * @param messages The received messages.
+ */
+ @Override
+ public void onMessagesReceived(@NonNull Set<Message> messages) {
+ if (mCallback != null) {
+ mCallback.onMessagesReceived(messages);
+ }
+ }
+
+ /**
+ * Use the {@link Communicator} to send a set of device-to-device messages.
+ * @param messages The {@link Message}s to send.
+ */
+ public void sendMessages(@NonNull Set<Message> messages) {
+ if (mActiveTransport == null || !mIsNegotiated) {
+ Log.w(this, "sendMessages: no active transport");
+ return;
+ }
+
+ Log.i(this, "sendMessages: msgs=%d, activeTransport=%s",
+ messages.size(), mActiveTransport.getClass().getSimpleName());
+ mActiveTransport.sendMessages(messages);
+ }
+
+ /**
+ * Find a new protocol to use and start negotiation.
+ */
+ private void negotiateNextProtocol() {
+ mActiveTransport = getNextCandidateProtocol();
+ if (mActiveTransport == null) {
+ // No more protocols, exit.
+ Log.i(this, "negotiateNextProtocol: no remaining transports.");
+ notifyD2DStatus(false /* isAvailable */);
+ return;
+ }
+ Log.i(this, "negotiateNextProtocol: trying %s",
+ mActiveTransport.getClass().getSimpleName());
+ mActiveTransport.startNegotiation();
+ }
+
+ /**
+ * @return the next protocol to attempt to use. If there is no active protocol, use the first
+ * one; otherwise use the one after the currently active one.
+ */
+ private TransportProtocol getNextCandidateProtocol() {
+ TransportProtocol candidateProtocol = null;
+ if (mActiveTransport == null) {
+ if (mTransportProtocols.size() > 0) {
+ candidateProtocol = mTransportProtocols.get(0);
+ } else {
+ mIsNegotiated = false;
+ }
+ } else {
+ for (int ix = 0; ix < mTransportProtocols.size(); ix++) {
+ TransportProtocol protocol = mTransportProtocols.get(ix);
+ if (protocol == mActiveTransport) {
+ if (ix + 1 < mTransportProtocols.size()) {
+ // Next one is candidate
+ candidateProtocol = mTransportProtocols.get(ix + 1);
+ }
+ break;
+ }
+ }
+ }
+ return candidateProtocol;
+ }
+
+ /**
+ * Notifies listeners (okay, {@link com.android.services.telephony.TelephonyConnection} when
+ * the availability of D2D communication changes.
+ * @param isAvailable {@code true} if D2D is available, {@code false} otherwise.
+ */
+ private void notifyD2DStatus(boolean isAvailable) {
+ if (mCallback != null) {
+ mCallback.onD2DAvailabilitychanged(isAvailable);
+ }
+ }
+
+ public static String messageToString(int messageType) {
+ switch (messageType) {
+ case MESSAGE_CALL_RADIO_ACCESS_TYPE:
+ return "MESSAGE_CALL_RADIO_ACCESS_TYPE";
+ case MESSAGE_CALL_AUDIO_CODEC:
+ return "MESSAGE_CALL_AUDIO_CODEC";
+ case MESSAGE_DEVICE_BATTERY_STATE:
+ return "MESSAGE_DEVICE_BATTERY_STATE";
+ case MESSAGE_DEVICE_NETWORK_COVERAGE:
+ return "MESSAGE_DEVICE_NETWORK_COVERAGE";
+ }
+ return "";
+ }
+
+ public static String valueToString(int messageType, int value) {
+ switch (messageType) {
+ case MESSAGE_CALL_RADIO_ACCESS_TYPE:
+ switch (value) {
+ case RADIO_ACCESS_TYPE_LTE:
+ return "RADIO_ACCESS_TYPE_LTE";
+ case RADIO_ACCESS_TYPE_IWLAN:
+ return "RADIO_ACCESS_TYPE_IWLAN";
+ case RADIO_ACCESS_TYPE_NR:
+ return "RADIO_ACCESS_TYPE_NR";
+ }
+ return "";
+ case MESSAGE_CALL_AUDIO_CODEC:
+ switch (value) {
+ case AUDIO_CODEC_EVS:
+ return "AUDIO_CODEC_EVS";
+ case AUDIO_CODEC_AMR_WB:
+ return "AUDIO_CODEC_AMR_WB";
+ case AUDIO_CODEC_AMR_NB:
+ return "AUDIO_CODEC_AMR_NB";
+ }
+ return "";
+ case MESSAGE_DEVICE_BATTERY_STATE:
+ switch (value) {
+ case BATTERY_STATE_LOW:
+ return "BATTERY_STATE_LOW";
+ case BATTERY_STATE_GOOD:
+ return "BATTERY_STATE_GOOD";
+ case BATTERY_STATE_CHARGING:
+ return "BATTERY_STATE_CHARGING";
+ }
+ return "";
+ case MESSAGE_DEVICE_NETWORK_COVERAGE:
+ switch (value) {
+ case COVERAGE_POOR:
+ return "COVERAGE_POOR";
+ case COVERAGE_GOOD:
+ return "COVERAGE_GOOD";
+ }
+ return "";
+ }
+ return "";
+ }
+
+ /**
+ * Test method used to force a transport type to be the active transport.
+ * @param transport The transport to activate.
+ */
+ public void setTransportActive(@NonNull String transport) {
+ Optional<TransportProtocol> tp = mTransportProtocols.stream()
+ .filter(t -> t.getClass().getSimpleName().equals(transport))
+ .findFirst();
+ if (!tp.isPresent()) {
+ Log.w(this, "setTransportActive: %s is not a valid transport.");
+ return;
+ }
+
+ mTransportProtocols.stream()
+ .filter(p -> p != tp.get())
+ .forEach(t -> t.forceNotNegotiated());
+ tp.get().forceNegotiated();
+ mActiveTransport = tp.get();
+ mIsNegotiated = true;
+ Log.i(this, "setTransportActive: %s has been forced active.", transport);
+ }
+
+ /**
+ * @return the list of {@link TransportProtocol} which are configured at the current time.
+ */
+ public @NonNull List<TransportProtocol> getTransportProtocols() {
+ return mTransportProtocols;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/d2d/DtmfAdapter.java b/src/java/com/android/internal/telephony/d2d/DtmfAdapter.java
new file mode 100644
index 0000000000..421be31eb6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/DtmfAdapter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.os.Message;
+
+/**
+ * Abstracts interaction with DTMF communication APIs.
+ */
+public interface DtmfAdapter {
+ /**
+ * Called when a DTMF digit should be sent to the network.
+ * <p>
+ * In concrete implementations, should be linked to
+ * {@link android.telephony.ims.stub.ImsCallSessionImplBase#sendDtmf(char, Message)}.
+ * @param digit The DTMF digit to send.
+ */
+ void sendDtmf(char digit);
+}
diff --git a/src/java/com/android/internal/telephony/d2d/DtmfTransport.java b/src/java/com/android/internal/telephony/d2d/DtmfTransport.java
new file mode 100644
index 0000000000..de26037c3c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/DtmfTransport.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.annotation.NonNull;
+import android.telecom.Log;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.BiMap;
+
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implements a DTMF-based transport for use with device-to-device communication.
+ *
+ * The DTMF-based transport is negotiated using a probe digit sent and confirmed by the remote side
+ * of the call.
+ * The {@link #DMTF_PROTOCOL_VERSION} message is sent upon initiation of the negotiation process.
+ * The protocol
+ * is considered negotiated if a valid negotiation sequence consisting of A[ABC]+D is received,
+ * where the [ABC]+ digits represent the protocol version.
+ *
+ * Note: Per RFC2833, transmission of DTMF digits provides a level of guarantee that where DTMF
+ * digits can be transmitted between two devices that they will be transmitted successfully. Thus
+ * the digits only need to be sent once to see if the other party can potentially use DTMF as a
+ * transport mechanism.
+ *
+ * The {@link #DMTF_PROTOCOL_VERSION} is used to negotiate D2D communication using DTMF. The
+ * message format assumes new message types and values can be added in the future; there is an
+ * assumption that if one side of the D2D communication pathway is unable to handle messages, it
+ * will ignore them. The DTMF protocol version sent as the probe is used to help each side respond
+ * to changes in the protocol over time.
+ *
+ * Protocol format:
+ * The DTMF protocol follows the following regular expression:
+ * ^A[ABC]+D[ABC]+D$
+ * A - start of message indicator, {@link #DTMF_MESSAGE_START}
+ * [ABC]+ - one or more message type digits [A-C]
+ * D - message delimiter {@link #DTMF_MESSAGE_DELIMITER} separating the type and value.
+ * [ABC]+ - one or more message value digits [A-C]
+ * D - end message delimiter {@link #DTMF_MESSAGE_DELIMITER}
+ *
+ * Valid message types and values are:
+ * A - call RAT
+ * ....A - LTE
+ * ....B - IWLAN
+ * ....C - NR
+ * B - call Audio codec
+ * ....A - EVS
+ * ....B - AMR-WB
+ * ....C - AMR-NB
+ * C - device battery level
+ * ....A - low
+ * ....B - good
+ * ....C - charging
+ * AA - device service state
+ * ....A - good
+ * ....B - poor
+ *
+ * Example message:
+ * Call RAT - NR --> A AD CD
+ * Service State - poor --> A AAD BD
+ */
+public class DtmfTransport implements TransportProtocol {
+ /**
+ * The DTMF probe and version string.
+ * Can be a string consisting of characters A-C.
+ * Thus, the current version is A, and following versions are B, C, AA, AB, AC, BC, etc.
+ */
+ public static final String DMTF_PROTOCOL_VERSION = "A";
+
+ /**
+ * All DTMF messages start with this digit.
+ */
+ public static final char DTMF_MESSAGE_START = 'A';
+
+ /**
+ * Delimits components of a DTMF message and also terminates a message.
+ */
+ public static final char DTMF_MESSAGE_DELIMITER = 'D';
+
+ /**
+ * The full DTMF probe message including the start digit, probe/version digit(s) and the message
+ * delimiter.
+ */
+ public static final String DMTF_PROBE_MESSAGE = DTMF_MESSAGE_START + DMTF_PROTOCOL_VERSION
+ + DTMF_MESSAGE_DELIMITER;
+
+ public static final String DTMF_MESSAGE_RAT = "A";
+ public static final String DTMF_MESSAGE_RAT_LTE = "A";
+ public static final String DTMF_MESSAGE_RAT_IWLAN = "B";
+ public static final String DTMF_MESSAGE_RAT_NR = "C";
+
+ public static final String DTMF_MESSAGE_CODEC = "B";
+ public static final String DTMF_MESSAGE_CODEC_EVS = "A";
+ public static final String DTMF_MESSAGE_CODEC_AMR_WB = "B";
+ public static final String DTMF_MESSAGE_CODEC_AMR_NB = "C";
+
+ public static final String DTMF_MESSAGE_BATERY = "C";
+ public static final String DTMF_MESSAGE_BATTERY_LOW = "A";
+ public static final String DTMF_MESSAGE_BATTERY_GOOD = "B";
+ public static final String DTMF_MESSAGE_BATTERY_CHARGING = "C";
+
+ public static final String DTMF_MESSAGE_SERVICE = "AA";
+ public static final String DTMF_MESSAGE_SERVICE_GOOD = "A";
+ public static final String DTMF_MESSAGE_SERVICE_POOR = "B";
+
+ public static final BiMap<Pair<String, String>, Communicator.Message> DTMF_TO_MESSAGE =
+ new BiMap<>();
+
+ static {
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_RAT, DTMF_MESSAGE_RAT_LTE),
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_LTE));
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_RAT, DTMF_MESSAGE_RAT_IWLAN),
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_IWLAN));
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_RAT, DTMF_MESSAGE_RAT_NR),
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_NR));
+
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_CODEC, DTMF_MESSAGE_CODEC_EVS),
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_EVS));
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_CODEC, DTMF_MESSAGE_CODEC_AMR_WB),
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_WB));
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_CODEC, DTMF_MESSAGE_CODEC_AMR_NB),
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_NB));
+
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_BATERY, DTMF_MESSAGE_BATTERY_LOW),
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_LOW));
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_BATERY, DTMF_MESSAGE_BATTERY_GOOD),
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_GOOD));
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_BATERY, DTMF_MESSAGE_BATTERY_CHARGING),
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_CHARGING));
+
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_SERVICE, DTMF_MESSAGE_SERVICE_GOOD),
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ Communicator.COVERAGE_GOOD));
+ DTMF_TO_MESSAGE.put(new Pair<>(DTMF_MESSAGE_SERVICE, DTMF_MESSAGE_SERVICE_POOR),
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ Communicator.COVERAGE_POOR));
+ }
+
+ public static final int STATE_IDLE = 0;
+ public static final int STATE_NEGOTIATING = 1;
+ public static final int STATE_NEGOTIATED = 2;
+ public static final int STATE_NEGOTIATION_FAILED = 3;
+
+ /**
+ * Indicates no message is being received yet.
+ */
+ public static final int RECEIVE_STATE_IDLE = 0;
+ /**
+ * Indicates receive of the message type is underway.
+ */
+ public static final int RECEIVE_STATE_MESSAGE_TYPE = 1;
+ /**
+ * Inidicates receive of the message value is underway.
+ */
+ public static final int RECEIVE_STATE_MESSAGE_VALUE = 2;
+
+ private final DtmfAdapter mDtmfAdapter;
+ private final long mIntervalBetweenDigitsMillis;
+ private final long mDurationOfDtmfMessageMillis;
+ private final long mDtmfDurationFuzzMillis;
+ private final long mNegotiationTimeoutMillis;
+ private final ScheduledExecutorService mScheduledExecutorService;
+ private TransportProtocol.Callback mCallback;
+ private int mTransportState = STATE_IDLE;
+ // The received probe digits
+ private StringBuffer mProbeDigits = new StringBuffer();
+ private String mProtocolVersion;
+
+ // Tracks the state of the received digits for an incoming message.
+ private int mMessageReceiveState = RECEIVE_STATE_IDLE;
+ private StringBuffer mMessageTypeDigits = new StringBuffer();
+ private StringBuffer mMessageValueDigits = new StringBuffer();
+ // Outgoing messages pending send.
+ private final ConcurrentLinkedQueue<char[]> mPendingMessages = new ConcurrentLinkedQueue<>();
+ // Locks to synchronize access to various data objects
+ private Object mProbeLock = new Object();
+ private Object mDtmfMessageTimeoutLock = new Object();
+ private Object mDigitSendLock = new Object();
+ private Object mNegotiationLock = new Object();
+ private Object mDigitsLock = new Object();
+ private ScheduledFuture<?> mNegotiationFuture;
+ private ScheduledFuture<?> mDigitSendScheduledFuture;
+ private ScheduledFuture<?> mDtmfMessageTimeoutFuture;
+ private char[] mMessageToSend;
+ private int mCharToSend = 0;
+ private Random mRandom = new Random();
+
+ public DtmfTransport(@NonNull DtmfAdapter dtmfAdapter, Timeouts.Adapter timeoutsAdapter,
+ ScheduledExecutorService executorService) {
+ mDtmfAdapter = dtmfAdapter;
+ mIntervalBetweenDigitsMillis = timeoutsAdapter.getDtmfMinimumIntervalMillis();
+ mDurationOfDtmfMessageMillis = timeoutsAdapter.getMaxDurationOfDtmfMessageMillis();
+ mDtmfDurationFuzzMillis = timeoutsAdapter.getDtmfDurationFuzzMillis();
+ mNegotiationTimeoutMillis = timeoutsAdapter.getDtmfNegotiationTimeoutMillis();
+ mScheduledExecutorService = executorService;
+ }
+
+ @Override
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void startNegotiation() {
+ if (mTransportState != STATE_IDLE) {
+ Log.w(this, "startNegotiation: can't start negotiation as not idle.");
+ return;
+ }
+ mTransportState = STATE_NEGOTIATING;
+ Log.i(this, "startNegotiation: starting negotiation.");
+ mPendingMessages.offer(DMTF_PROBE_MESSAGE.toCharArray());
+ maybeScheduleMessageSend();
+ scheduleNegotiationTimeout();
+ }
+
+ /**
+ * Given a set of messages to send, send them using the DTMF transport.
+ *
+ * @param messages the messages to send via the transport.
+ */
+ @Override
+ public void sendMessages(Set<Communicator.Message> messages) {
+ for (Communicator.Message msg : messages) {
+ char[] digits = getMessageDigits(msg);
+ if (digits == null) continue;
+ Log.i(this, "sendMessages: queueing message: %s", String.valueOf(digits));
+
+ mPendingMessages.offer(digits);
+ }
+ if (mPendingMessages.size() > 0) {
+ maybeScheduleMessageSend();
+ }
+ }
+
+ /**
+ * Checks for pending messages and schedules send of a pending message if one is available.
+ */
+ private void maybeScheduleMessageSend() {
+ synchronized (mDigitSendLock) {
+ if (mMessageToSend == null && mDigitSendScheduledFuture == null) {
+ mMessageToSend = mPendingMessages.poll();
+ mCharToSend = 0;
+
+ if (mMessageToSend != null) {
+ Log.i(this, "maybeScheduleMessageSend: toSend=%s",
+ String.valueOf(mMessageToSend));
+ // Schedule the message to send; the inital delay will be
+ // mDurationOfDtmfMessageMillis to ensure we separate messages with an
+ // adequate padding of space, and mIntervalBetweenDigitsMillis will be used to
+ // ensure there is enough time between each digit.
+ mDigitSendScheduledFuture = mScheduledExecutorService.scheduleAtFixedRate(
+ () -> {
+ handleDtmfSend();
+ }, mDurationOfDtmfMessageMillis + getDtmfDurationFuzzMillis(),
+ mIntervalBetweenDigitsMillis,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return random fuzz factor to add when delaying initial send of a DTMF message.
+ */
+ private long getDtmfDurationFuzzMillis() {
+ if (mDtmfDurationFuzzMillis == 0) {
+ return 0;
+ }
+ return mRandom.nextLong() % mDtmfDurationFuzzMillis;
+ }
+
+ /**
+ * Runs at fixed {@link #mIntervalBetweenDigitsMillis} intervals to send the individual DTMF
+ * digits in {@link #mMessageToSend}. When sending completes, the scheduled task is cancelled
+ * and {@link #maybeScheduleMessageSend()} is called to schedule send of any other pending
+ * message.
+ */
+ private void handleDtmfSend() {
+ if (mCharToSend < mMessageToSend.length) {
+ if (mDtmfAdapter != null) {
+ Log.i(this, "handleDtmfSend: char=%c", mMessageToSend[mCharToSend]);
+ mDtmfAdapter.sendDtmf(mMessageToSend[mCharToSend]);
+ }
+ mCharToSend++;
+
+ if (mCharToSend == mMessageToSend.length) {
+ Log.i(this, "handleDtmfSend: done");
+ synchronized (mDigitSendLock) {
+ mMessageToSend = null;
+ mDigitSendScheduledFuture.cancel(false);
+ mDigitSendScheduledFuture = null;
+
+ // If we're still in the negotiation phase, we can hold off on sending any other
+ // pending messages queued up.
+ if (mTransportState == STATE_NEGOTIATED) {
+ maybeScheduleMessageSend();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @return the current state of the transport.
+ */
+ public int getTransportState() {
+ return mTransportState;
+ }
+
+ /**
+ * Called by Telephony when a DTMF digit is received from the network.
+ *
+ * @param digit The received DTMF digit.
+ */
+ public void onDtmfReceived(char digit) {
+ if (!(digit >= 'A' && digit <= 'D')) {
+ Log.i(this, "onDtmfReceived: digit = %c ; invalid digit; not in A-D");
+ return;
+ }
+
+ if (mTransportState == STATE_NEGOTIATING) {
+ synchronized(mProbeLock) {
+ mProbeDigits.append(digit);
+ }
+
+ if (digit == DTMF_MESSAGE_DELIMITER) {
+ Log.i(this, "onDtmfReceived: received message %s", mProbeDigits);
+ handleProbeMessage();
+ }
+ } else {
+ handleReceivedDigit(digit);
+ }
+ }
+
+ /**
+ * Handles a received probe message by verifying that it is in a valid format and caching the
+ * version number indicated.
+ */
+ private void handleProbeMessage() {
+ String probe;
+ synchronized(mProbeLock) {
+ probe = mProbeDigits.toString();
+ if (mProbeDigits.length() > 0) {
+ mProbeDigits.delete(0, mProbeDigits.length());
+ }
+ }
+ cancelNegotiationTimeout();
+
+ if (probe.startsWith(String.valueOf(DTMF_MESSAGE_START))
+ && probe.endsWith(String.valueOf(DTMF_MESSAGE_DELIMITER))
+ && probe.length() > 2) {
+ mProtocolVersion = probe.substring(1,probe.length() - 1);
+ Log.i(this, "handleProbeMessage: got valid probe, remote version %s negotiated.",
+ probe);
+ negotiationSucceeded();
+ } else {
+ Log.i(this, "handleProbeMessage: got invalid probe %s - negotiation failed.", probe);
+ negotiationFailed();
+ }
+ cancelNegotiationTimeout();
+
+ }
+
+ /**
+ * Upon initiation of negotiation, schedule a timeout within which we expect to receive the
+ * incoming probe.
+ */
+ private void scheduleNegotiationTimeout() {
+ synchronized (mNegotiationLock) {
+ mNegotiationFuture = mScheduledExecutorService.schedule(() -> {
+ handleNegotiationTimeout();
+ },
+ mNegotiationTimeoutMillis, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Cancels a pending timeout for negotiation.
+ */
+ private void cancelNegotiationTimeout() {
+ Log.i(this, "cancelNegotiationTimeout");
+ synchronized (mNegotiationLock) {
+ if (mNegotiationFuture != null) {
+ mNegotiationFuture.cancel(false);
+ }
+ mNegotiationFuture = null;
+ }
+ }
+
+ /**
+ * Handle scheduled negotiation timeout.
+ */
+ private void handleNegotiationTimeout() {
+ Log.i(this, "handleNegotiationTimeout: no probe received, negotiation timeout.");
+ synchronized (mNegotiationLock) {
+ mNegotiationFuture = null;
+ }
+ negotiationFailed();
+ }
+
+ /**
+ * Handle failed negotiation by changing state and informing listeners.
+ */
+ private void negotiationFailed() {
+ mTransportState = STATE_NEGOTIATION_FAILED;
+ Log.i(this, "notifyNegotiationFailed");
+ if (mCallback != null) {
+ mCallback.onNegotiationFailed(this);
+ }
+ }
+
+ /**
+ * Handle successful negotiation by changing state and informing listeners.
+ */
+ private void negotiationSucceeded() {
+ mTransportState = STATE_NEGOTIATED;
+ Log.i(this, "negotiationSucceeded");
+ if (mCallback != null) {
+ mCallback.onNegotiationSuccess(this);
+ }
+ }
+
+ /**
+ * Handle received DTMF digits, taking into account current protocol state.
+ *
+ * @param digit the received digit.
+ */
+ private void handleReceivedDigit(char digit) {
+ if (mMessageReceiveState == RECEIVE_STATE_IDLE) {
+ if (digit == DTMF_MESSAGE_START) {
+ // First digit; start the timer
+ Log.i(this, "handleReceivedDigit: digit = %c ; message timeout started.", digit);
+ mMessageReceiveState = RECEIVE_STATE_MESSAGE_TYPE;
+ scheduleDtmfMessageTimeout();
+ } else {
+ Log.w(this, "handleReceivedDigit: digit = %c ; unexpected start digit, ignoring.",
+ digit);
+ }
+ } else if (digit == DTMF_MESSAGE_DELIMITER) {
+ if (mMessageReceiveState == RECEIVE_STATE_MESSAGE_TYPE) {
+ Log.i(this, "handleReceivedDigit: digit = %c ; msg = %s ; awaiting value.", digit,
+ mMessageTypeDigits.toString());
+ mMessageReceiveState = RECEIVE_STATE_MESSAGE_VALUE;
+ } else if (mMessageReceiveState == RECEIVE_STATE_MESSAGE_VALUE) {
+ maybeCancelDtmfMessageTimeout();
+ String messageType;
+ String messageValue;
+ synchronized(mDigitsLock) {
+ messageType = mMessageTypeDigits.toString();
+ messageValue = mMessageValueDigits.toString();
+ }
+ Log.i(this, "handleReceivedDigit: digit = %c ; msg = %s ; value = %s ; full msg",
+ digit, messageType, messageValue);
+ handleIncomingMessage(messageType, messageValue);
+ resetIncomingMessage();
+ }
+ } else {
+ synchronized(mDigitsLock) {
+ if (mMessageReceiveState == RECEIVE_STATE_MESSAGE_TYPE) {
+ mMessageTypeDigits.append(digit);
+ Log.i(this, "handleReceivedDigit: typeDigit = %c ; msg = %s",
+ digit, mMessageTypeDigits.toString());
+ } else if (mMessageReceiveState == RECEIVE_STATE_MESSAGE_VALUE) {
+ mMessageValueDigits.append(digit);
+ Log.i(this, "handleReceivedDigit: valueDigit = %c ; value = %s",
+ digit, mMessageValueDigits.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Schedule a timeout for receiving a complete DTMF message.
+ */
+ private void scheduleDtmfMessageTimeout() {
+ synchronized (mDtmfMessageTimeoutLock) {
+ maybeCancelDtmfMessageTimeout();
+
+ mDtmfMessageTimeoutFuture = mScheduledExecutorService.schedule(() -> {
+ handleDtmfMessageTimeout();
+ },
+ mDurationOfDtmfMessageMillis, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ /**
+ * Cancels any pending DTMF message timeout scheduled with
+ * {@link #scheduleDtmfMessageTimeout()}.
+ */
+ private void maybeCancelDtmfMessageTimeout() {
+ synchronized (mDtmfMessageTimeoutLock) {
+ if (mDtmfMessageTimeoutFuture != null) {
+ Log.i(this, "scheduleDtmfMessageTimeout: timeout pending; cancelling");
+ mDtmfMessageTimeoutFuture.cancel(false);
+ mDtmfMessageTimeoutFuture = null;
+ }
+ }
+ }
+
+ /**
+ * Called when a scheduled DTMF message timeout occurs to cleanup the incoming message and
+ * prepare for receiving a new message.
+ */
+ private void handleDtmfMessageTimeout() {
+ maybeCancelDtmfMessageTimeout();
+
+ Log.i(this, "handleDtmfMessageTimeout: timeout receiving DTMF string; got %s/%s so far",
+ mMessageTypeDigits.toString(), mMessageValueDigits.toString());
+
+ resetIncomingMessage();
+ }
+
+ /**
+ * Given a {@link Communicator.Message} to send, returns a string of DTMF digits to send for
+ * that message. This is the complete message, including the message start, and all delimiters.
+ *
+ * @param message The message to send.
+ * @return The DTMF digits to send, including all delimiters.
+ */
+ @VisibleForTesting
+ public char[] getMessageDigits(@NonNull Communicator.Message message) {
+ Pair<String, String> foundSequence = DTMF_TO_MESSAGE.getKey(message);
+ if (foundSequence == null) {
+ return null;
+ }
+ StringBuilder theMessage = new StringBuilder();
+ theMessage.append(DTMF_MESSAGE_START);
+ theMessage.append(foundSequence.first);
+ theMessage.append(DTMF_MESSAGE_DELIMITER);
+ theMessage.append(foundSequence.second);
+ theMessage.append(DTMF_MESSAGE_DELIMITER);
+ return theMessage.toString().toCharArray();
+ }
+
+ /**
+ * Translate a string of DTMF digits into a communicator message.
+ *
+ * @param message The string of DTMF digits with digit 0 being the start of the string of
+ * digits.
+ * @return The message received, or {@code null} if no valid message found.
+ */
+ @VisibleForTesting
+ public Communicator.Message extractMessage(String message, String value) {
+ return DTMF_TO_MESSAGE.getValue(new Pair<>(message, value));
+ }
+
+ /**
+ * Handles an incoming message received via DTMF digits; notifies interested parties of the
+ * message using the associated callback.
+ */
+ private void handleIncomingMessage(String message, String value) {
+
+ Communicator.Message msg = extractMessage(message, value);
+ if (msg == null) {
+ Log.w(this, "handleIncomingMessage: msgDigits = %s, msgValueDigits = %s; invalid msg",
+ message, value);
+ return;
+ }
+ Log.i(this, "handleIncomingMessage: msgDigits = %s, msgValueDigits = %s", message, value);
+
+ Set<Communicator.Message> msgs = new ArraySet<>(1);
+ msgs.add(msg);
+ if (mCallback != null) {
+ mCallback.onMessagesReceived(msgs);
+ }
+ }
+
+ /**
+ * Moves message receive state back to idle and clears received digits.
+ */
+ private void resetIncomingMessage() {
+ mMessageReceiveState = RECEIVE_STATE_IDLE;
+ synchronized(mDigitsLock) {
+ if (mMessageTypeDigits.length() != 0) {
+ mMessageTypeDigits.delete(0, mMessageTypeDigits.length());
+ }
+ if (mMessageValueDigits.length() != 0) {
+ mMessageValueDigits.delete(0, mMessageValueDigits.length());
+ }
+ }
+ }
+
+ @Override
+ public void forceNegotiated() {
+
+ }
+
+ @Override
+ public void forceNotNegotiated() {
+
+ }
+}
diff --git a/src/java/com/android/internal/telephony/d2d/MessageTypeAndValueHelper.java b/src/java/com/android/internal/telephony/d2d/MessageTypeAndValueHelper.java
new file mode 100644
index 0000000000..caf657541a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/MessageTypeAndValueHelper.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.telecom.Connection;
+import android.telecom.CallDiagnostics;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.BiMap;
+
+/**
+ * Helper class to map between the message types and values used in {@link Communicator} and those
+ * defined in the public API in {@link CallDiagnostics}.
+ */
+public class MessageTypeAndValueHelper {
+ // Maps between the local message and value types defined here and those defined in the
+ // DiagnosticCall class as part of the actual API.
+
+ /**
+ * Convert between the local message type (e.g.
+ * {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE})
+ * and
+ * the ones referred to in {@link CallDiagnostics}.
+ */
+ public static final BiMap<Integer, Integer> MSG_TYPE_TO_DC_MSG_TYPE = new BiMap<>();
+
+ /**
+ * Convert between the local RAT type (e.g. {@link Communicator#RADIO_ACCESS_TYPE_IWLAN}) and
+ * the ones
+ * referred to by {@link CallDiagnostics#MESSAGE_CALL_NETWORK_TYPE}.
+ */
+ public static final BiMap<Integer, Integer> RAT_TYPE_TO_DC_NETWORK_TYPE = new BiMap<>();
+
+ /**
+ * Convert between the local codec (e.g. {@link Communicator#AUDIO_CODEC_AMR_WB}) and the ones
+ * referred to by {@link CallDiagnostics#MESSAGE_CALL_AUDIO_CODEC}.
+ */
+ public static final BiMap<Integer, Integer> CODEC_TO_DC_CODEC = new BiMap<>();
+
+ /**
+ * Convert between the local battery state (e.g. {@link Communicator#BATTERY_STATE_GOOD}) and
+ * the ones referred to by {@link CallDiagnostics#MESSAGE_DEVICE_BATTERY_STATE}.
+ */
+ public static final BiMap<Integer, Integer> BATTERY_STATE_TO_DC_BATTERY_STATE = new BiMap();
+
+ /**
+ * Convert between the local battery state (e.g. {@link Communicator#COVERAGE_GOOD}) and the
+ * ones referred to by {@link CallDiagnostics#MESSAGE_DEVICE_NETWORK_COVERAGE}.
+ */
+ public static final BiMap<Integer, Integer> COVERAGE_TO_DC_COVERAGE = new BiMap();
+
+ static {
+ MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE);
+ MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC);
+ MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE);
+ MSG_TYPE_TO_DC_MSG_TYPE.put(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE);
+
+ RAT_TYPE_TO_DC_NETWORK_TYPE.put(Communicator.RADIO_ACCESS_TYPE_LTE,
+ TelephonyManager.NETWORK_TYPE_LTE);
+ RAT_TYPE_TO_DC_NETWORK_TYPE.put(Communicator.RADIO_ACCESS_TYPE_IWLAN,
+ TelephonyManager.NETWORK_TYPE_IWLAN);
+ RAT_TYPE_TO_DC_NETWORK_TYPE.put(Communicator.RADIO_ACCESS_TYPE_NR,
+ TelephonyManager.NETWORK_TYPE_NR);
+
+ CODEC_TO_DC_CODEC.put(Communicator.AUDIO_CODEC_EVS, Connection.AUDIO_CODEC_EVS_WB);
+ CODEC_TO_DC_CODEC.put(Communicator.AUDIO_CODEC_AMR_WB, Connection.AUDIO_CODEC_AMR_WB);
+ CODEC_TO_DC_CODEC.put(Communicator.AUDIO_CODEC_AMR_NB, Connection.AUDIO_CODEC_AMR);
+
+ BATTERY_STATE_TO_DC_BATTERY_STATE.put(Communicator.BATTERY_STATE_LOW,
+ CallDiagnostics.BATTERY_STATE_LOW);
+ BATTERY_STATE_TO_DC_BATTERY_STATE.put(Communicator.BATTERY_STATE_GOOD,
+ CallDiagnostics.BATTERY_STATE_GOOD);
+ BATTERY_STATE_TO_DC_BATTERY_STATE.put(Communicator.BATTERY_STATE_CHARGING,
+ CallDiagnostics.BATTERY_STATE_CHARGING);
+
+ COVERAGE_TO_DC_COVERAGE.put(Communicator.COVERAGE_POOR, CallDiagnostics.COVERAGE_POOR);
+ COVERAGE_TO_DC_COVERAGE.put(Communicator.COVERAGE_GOOD, CallDiagnostics.COVERAGE_GOOD);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/d2d/RtpAdapter.java b/src/java/com/android/internal/telephony/d2d/RtpAdapter.java
new file mode 100644
index 0000000000..8821ba928d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/RtpAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.annotation.NonNull;
+import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.RtpHeaderExtensionType;
+
+import java.util.Set;
+
+/**
+ * Abstracts out details of dealing with the RTP header extensions from the {@link RtpTransport} to
+ * facilitate easier testing.
+ */
+public interface RtpAdapter {
+ public interface Callback {
+ /**
+ * Used to indicate when RTP header extensions are received.
+ * @param extensions The extensions.
+ */
+ void onRtpHeaderExtensionsReceived(Set<RtpHeaderExtension> extensions);
+ }
+
+ /**
+ * Used to retrieve the accepted RTP header extensions by the {@link RtpTransport}.
+ * @return the accepted RTP header extensions.
+ */
+ Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions();
+
+ /**
+ * Used by the {@link RtpTransport} to send RTP header extension messages.
+ * @param rtpHeaderExtensions the rtp header extension messages to send.
+ */
+ void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions);
+}
diff --git a/src/java/com/android/internal/telephony/d2d/RtpTransport.java b/src/java/com/android/internal/telephony/d2d/RtpTransport.java
new file mode 100644
index 0000000000..595a94c32d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/RtpTransport.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Handler;
+import android.telecom.Log;
+import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.RtpHeaderExtensionType;
+import android.util.ArraySet;
+
+import com.android.internal.telephony.BiMap;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Implements a transport protocol which makes use of RTP header extensions to communicate between
+ * two devices.
+ * <p>
+ * Two types of device to device communications are supported,
+ * {@link #DEVICE_STATE_RTP_HEADER_EXTENSION} messages which communicate attributes such as the
+ * device service level and battery, and {@link #CALL_STATE_RTP_HEADER_EXTENSION} which communicates
+ * information pertaining to an ongoing call.
+ * <p>
+ * When the ImsService negotiates the media for a call using SDP it will also potentially negotiate
+ * the supported RTP header extension types which both parties of the call agree upon. When a call
+ * first begins if the accepted RTP header extension URIs matches the two well defined URIs we
+ * define here, then we can assume the other party supports device to device communication using
+ * RTP header extensions.
+ * <p>
+ * It is, however, possible that network signalling causes a failure to successfully negotiate the
+ * RTP header extension types which both sides agree upon. In this case we will wait for a short
+ * period of time to see if we receive an RTP header extension which corresponds to one of the
+ * local identifiers we would have expected to negotiation. If such an RTP header extension is
+ * received in a timely manner, we can assume that the other party is capable of using RTP header
+ * extensions to communicate even though the RTP extension type URIs did not successfully negotiate.
+ * If we do not receive a valid extension in this case, we will consider negotiation for this
+ * transport to have failed.
+ */
+public class RtpTransport implements TransportProtocol, RtpAdapter.Callback {
+ /**
+ * {@link Uri} identifier for an RTP header extension used to communicate device state during
+ * calls.
+ */
+ public static Uri DEVICE_STATE_RTP_HEADER_EXTENSION =
+ Uri.parse("http://develop.android.com/122020/d2dcomm#device-state");
+
+ /**
+ * {@link Uri} identifier for an RTP header extension used to communication call state during
+ * calls.
+ */
+ public static Uri CALL_STATE_RTP_HEADER_EXTENSION =
+ Uri.parse("http://develop.android.com/122020/d2dcomm#call-state");
+
+ /**
+ * Default local identifier for device state RTP header extensions.
+ */
+ public static int DEVICE_STATE_LOCAL_IDENTIFIER = 10;
+
+ /**
+ * Default local identifier for call state RTP header extensions.
+ */
+ public static int CALL_STATE_LOCAL_IDENTIFIER = 11;
+
+ /**
+ * {@link RtpHeaderExtensionType} for device state communication.
+ */
+ public static RtpHeaderExtensionType DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE =
+ new RtpHeaderExtensionType(DEVICE_STATE_LOCAL_IDENTIFIER,
+ DEVICE_STATE_RTP_HEADER_EXTENSION);
+
+ /**
+ * {@link RtpHeaderExtensionType} for call state communication.
+ */
+ public static RtpHeaderExtensionType CALL_STATE_RTP_HEADER_EXTENSION_TYPE =
+ new RtpHeaderExtensionType(CALL_STATE_LOCAL_IDENTIFIER,
+ CALL_STATE_RTP_HEADER_EXTENSION);
+
+
+ /**
+ * See {@link #generateRtpHeaderExtension(Communicator.Message)} for more information; indicates
+ * the offset of the parameter value in the RTP header extension payload.
+ */
+ public static final int RTP_PARAMETER_BIT_OFFSET = 4;
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} msg.
+ */
+ public static final byte RTP_CALL_STATE_MSG_RADIO_ACCESS_TYPE_BITS = 0b0001;
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_AUDIO_CODEC} msg.
+ */
+ public static final byte RTP_CALL_STATE_MSG_CODEC_BITS = 0b0010;
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_DEVICE_BATTERY_STATE} msg.
+ */
+ public static final byte RTP_DEVICE_STATE_MSG_BATTERY_BITS = 0b0001;
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_DEVICE_NETWORK_COVERAGE} msg.
+ */
+ public static final byte RTP_DEVICE_STATE_MSG_NETWORK_COVERAGE_BITS = 0b0010;
+
+ /**
+ * Provides a mapping between the various {@code Communicator#MESSAGE_*} values and their bit
+ * representation in an RTP header extension payload. Used to translate outgoing message types
+ * to an RTP payload and to interpret incoming RTP payloads and translate them back into message
+ * types.
+ */
+ private static final BiMap<Integer, Byte> CALL_STATE_MSG_TYPE_TO_RTP_BITS = new BiMap<>();
+ private static final BiMap<Integer, Byte> DEVICE_STATE_MSG_TYPE_TO_RTP_BITS = new BiMap<>();
+ static {
+ CALL_STATE_MSG_TYPE_TO_RTP_BITS.put(
+ Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ RTP_CALL_STATE_MSG_RADIO_ACCESS_TYPE_BITS);
+ CALL_STATE_MSG_TYPE_TO_RTP_BITS.put(
+ Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ RTP_CALL_STATE_MSG_CODEC_BITS);
+ DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.put(
+ Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ RTP_DEVICE_STATE_MSG_BATTERY_BITS);
+ DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.put(
+ Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ RTP_DEVICE_STATE_MSG_NETWORK_COVERAGE_BITS);
+ }
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} data
+ * payload. Corresponds to {@link Communicator#RADIO_ACCESS_TYPE_LTE}.
+ */
+ public static final byte RTP_RAT_VALUE_LTE_BITS = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} data
+ * payload. Corresponds to {@link Communicator#RADIO_ACCESS_TYPE_IWLAN}.
+ */
+ public static final byte RTP_RAT_VALUE_WLAN_BITS = 0b0010 << RTP_PARAMETER_BIT_OFFSET;
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_RADIO_ACCESS_TYPE} data
+ * payload. Corresponds to {@link Communicator#RADIO_ACCESS_TYPE_NR}.
+ */
+ public static final byte RTP_RAT_VALUE_NR_BITS = 0b0011 << RTP_PARAMETER_BIT_OFFSET;
+
+ /**
+ * Provides a mapping between the various {@code Communicator#RADIO_ACCESS_TYPE_*} values and
+ * their bit representation in an RTP header extension payload.
+ */
+ private static final BiMap<Integer, Byte> RAT_VALUE_TO_RTP_BITS = new BiMap<>();
+ static {
+ RAT_VALUE_TO_RTP_BITS.put(
+ Communicator.RADIO_ACCESS_TYPE_IWLAN, RTP_RAT_VALUE_WLAN_BITS);
+ RAT_VALUE_TO_RTP_BITS.put(
+ Communicator.RADIO_ACCESS_TYPE_LTE, RTP_RAT_VALUE_LTE_BITS);
+ RAT_VALUE_TO_RTP_BITS.put(
+ Communicator.RADIO_ACCESS_TYPE_NR, RTP_RAT_VALUE_NR_BITS);
+ }
+
+ /**
+ * RTP header extension bits set for {@link Communicator#MESSAGE_CALL_AUDIO_CODEC} data
+ * payload. Corresponds to {@link Communicator#AUDIO_CODEC_EVS}.
+ */
+ public static final byte RTP_CODEC_VALUE_EVS_BITS = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
+ public static final byte RTP_CODEC_VALUE_AMR_WB_BITS = 0b0010 << RTP_PARAMETER_BIT_OFFSET;
+ public static final byte RTP_CODEC_VALUE_AMR_NB_BITS = 0b0011 << RTP_PARAMETER_BIT_OFFSET;
+
+ /**
+ * Provides a mapping between the various {@code Communicator#AUDIO_CODEC_*} values and
+ * their bit representation in an RTP header extension payload.
+ */
+ private static final BiMap<Integer, Byte> CODEC_VALUE_TO_RTP_BITS = new BiMap<>();
+ static {
+ CODEC_VALUE_TO_RTP_BITS.put(
+ Communicator.AUDIO_CODEC_EVS, RTP_CODEC_VALUE_EVS_BITS);
+ CODEC_VALUE_TO_RTP_BITS.put(
+ Communicator.AUDIO_CODEC_AMR_WB, RTP_CODEC_VALUE_AMR_WB_BITS);
+ CODEC_VALUE_TO_RTP_BITS.put(
+ Communicator.AUDIO_CODEC_AMR_NB, RTP_CODEC_VALUE_AMR_NB_BITS);
+ }
+
+ public static final byte RTP_BATTERY_STATE_LOW_BITS = 0b0000 << RTP_PARAMETER_BIT_OFFSET;
+ public static final byte RTP_BATTERY_STATE_GOOD_BITS = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
+ public static final byte RTP_BATTERY_STATE_CHARGING_BITS = 0b0011 << RTP_PARAMETER_BIT_OFFSET;
+
+ /**
+ * Provides a mapping between the various {@code Communicator#BATTERY_STATE_*} values and
+ * their bit representation in an RTP header extension payload.
+ */
+ private static final BiMap<Integer, Byte> BATTERY_STATE_VALUE_TO_RTP_BITS = new BiMap<>();
+ static {
+ BATTERY_STATE_VALUE_TO_RTP_BITS.put(
+ Communicator.BATTERY_STATE_LOW, RTP_BATTERY_STATE_LOW_BITS);
+ BATTERY_STATE_VALUE_TO_RTP_BITS.put(
+ Communicator.BATTERY_STATE_GOOD, RTP_BATTERY_STATE_GOOD_BITS);
+ BATTERY_STATE_VALUE_TO_RTP_BITS.put(
+ Communicator.BATTERY_STATE_CHARGING, RTP_BATTERY_STATE_CHARGING_BITS);
+ }
+
+ public static final byte RTP_NETWORK_COVERAGE_POOR_BITS = 0b0000 << RTP_PARAMETER_BIT_OFFSET;
+ public static final byte RTP_NETWORK_COVERAGE_GOOD_BITS = 0b0001 << RTP_PARAMETER_BIT_OFFSET;
+
+ /**
+ * Provides a mapping between the various {@code Communicator#COVERAGE_*} values and
+ * their bit representation in an RTP header extension payload.
+ */
+ private static final BiMap<Integer, Byte> NETWORK_COVERAGE_VALUE_TO_RTP_BITS = new BiMap<>();
+ static {
+ NETWORK_COVERAGE_VALUE_TO_RTP_BITS.put(
+ Communicator.COVERAGE_POOR, RTP_NETWORK_COVERAGE_POOR_BITS);
+ NETWORK_COVERAGE_VALUE_TO_RTP_BITS.put(
+ Communicator.COVERAGE_GOOD, RTP_NETWORK_COVERAGE_GOOD_BITS);
+ }
+
+ /**
+ * Indicates that the transport is not yet ready for use and negotiation has not yet completed.
+ */
+ public static final int PROTOCOL_STATUS_NEGOTIATION_REQUIRED = 1;
+
+ /**
+ * Indicates that the agreed upon RTP header extension types (see
+ * {@link RtpAdapter#getAcceptedRtpHeaderExtensions()}) did not specify both the
+ * {@link #DEVICE_STATE_RTP_HEADER_EXTENSION} and {@link #CALL_STATE_RTP_HEADER_EXTENSION
+ * RTP header extension types. We are not going to wait a short time to see if we receive an
+ * incoming RTP packet with one of the local identifiers we'd normally expect associated with
+ * these header extension {@link Uri}s. If we do receive a valid RTP header extension we will
+ * move to the {@link #PROTOCOL_STATUS_NEGOTIATION_COMPLETE} status. If we do not receive a
+ * valid RTP header extension we move to {@link #PROTOCOL_STATUS_NEGOTIATION_FAILED} and report
+ * the failure via {@link Callback#onNegotiationFailed(TransportProtocol) }.
+ */
+ public static final int PROTOCOL_STATUS_NEGOTIATION_WAITING_ON_PACKET = 2;
+
+ /**
+ * Indicates we either agreed upon the required header extensions, or received a valid packet
+ * despite not having agreed upon required header extensions. The transport is ready to use at
+ * this point.
+ */
+ public static final int PROTOCOL_STATUS_NEGOTIATION_COMPLETE = 3;
+
+ /**
+ * Indicates protocol negotiation failed.
+ */
+ public static final int PROTOCOL_STATUS_NEGOTIATION_FAILED = 4;
+
+ /**
+ * Callback which is used to report back to the {@link Communicator} about the status of
+ * protocol negotiation and incoming messages.
+ */
+ private TransportProtocol.Callback mCallback;
+
+ /**
+ * Adapter which abstracts out the details of sending/receiving RTP header extensions.
+ */
+ private final RtpAdapter mRtpAdapter;
+
+ /**
+ * Configuration adapter for timeouts related to protocol setup.
+ */
+ private final Timeouts.Adapter mTimeoutsAdapter;
+
+ /**
+ * Handler for posting future events.
+ */
+ private final Handler mHandler;
+
+ /**
+ * {@code true} if the carrier supports negotiating the RTP header extensions using SDP.
+ * If {@code true}, we can expected the
+ * {@link ImsCallProfile#getAcceptedRtpHeaderExtensionTypes()} to contain the SDP negotiated RTP
+ * header extensions. If {@code false} we will assume the protocol is negotiated only after
+ * receiving an RTP header extension of the expected type.
+ */
+ private final boolean mIsSdpNegotiationSupported;
+
+ /**
+ * Protocol status.
+ */
+ private int mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_REQUIRED;
+
+ /**
+ * The supported RTP header extension types.
+ */
+ private ArraySet<RtpHeaderExtensionType> mSupportedRtpHeaderExtensionTypes = new ArraySet<>();
+
+ /**
+ * Initializes the {@link RtpTransport}.
+ * @param rtpAdapter Adapter for abstract send/receive of RTP header extension data.
+ * @param timeoutsAdapter Timeouts adapter for dealing with time based configurations.
+ * @param handler Handler for posting future events.
+ * @param isSdpNegotiationSupported Indicates whether SDP negotiation
+ */
+ public RtpTransport(RtpAdapter rtpAdapter, Timeouts.Adapter timeoutsAdapter, Handler handler,
+ boolean isSdpNegotiationSupported) {
+ mRtpAdapter = rtpAdapter;
+ mTimeoutsAdapter = timeoutsAdapter;
+ mHandler = handler;
+ mIsSdpNegotiationSupported = isSdpNegotiationSupported;
+ }
+
+ /**
+ * Sets the Callback for this transport. Used to report back received messages.
+ * @param callback The callback.
+ */
+ @Override
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Begin transport protocol negotiation at the request of the framework.
+ *
+ * If the {@link #DEVICE_STATE_RTP_HEADER_EXTENSION} and
+ * {@link #CALL_STATE_RTP_HEADER_EXTENSION} extension {@link Uri}s are part of the accepted
+ * extension types, we consider the negotiation successful.
+ *
+ * TODO: If they are not in the accepted extensions, we will wait a short period of time to
+ * receive a valid RTP header extension we recognize.
+ */
+ @Override
+ public void startNegotiation() {
+ Set<RtpHeaderExtensionType> acceptedExtensions =
+ mRtpAdapter.getAcceptedRtpHeaderExtensions();
+ mSupportedRtpHeaderExtensionTypes.addAll(acceptedExtensions);
+
+ Log.i(this, "startNegotiation: supportedExtensions=%s", mSupportedRtpHeaderExtensionTypes
+ .stream()
+ .map(e -> e.toString())
+ .collect(Collectors.joining(",")));
+
+ if (mIsSdpNegotiationSupported) {
+ boolean areExtensionsAvailable = acceptedExtensions.stream().anyMatch(
+ e -> e.getUri().equals(DEVICE_STATE_RTP_HEADER_EXTENSION))
+ && acceptedExtensions.stream().anyMatch(
+ e -> e.getUri().equals(CALL_STATE_RTP_HEADER_EXTENSION));
+
+ if (areExtensionsAvailable) {
+ // Headers were negotiated during SDP, so we can assume negotiation is complete and
+ // signal to the communicator that we can use this transport.
+ mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_COMPLETE;
+ Log.i(this, "startNegotiation: header extensions available, negotiation success");
+ notifyProtocolReady();
+ } else {
+ // Headers failed to be negotiated during SDP. Assume protocol is not available.
+ // TODO: Implement fallback logic where we still try an SDP probe/response.
+ mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_FAILED;
+ Log.i(this,
+ "startNegotiation: header extensions not available; negotiation failed");
+ notifyProtocolUnavailable();
+ }
+ } else {
+ Log.i(this, "startNegotiation: SDP negotiation not supported; negotiation complete");
+ // TODO: This is temporary; we will need to implement a probe/response in this scenario
+ // if SDP is not supported. For now we will just assume the protocol is ready.
+ notifyProtocolReady();
+ }
+ }
+
+ /**
+ * Handles sending messages using the RTP transport. Generates valid {@link RtpHeaderExtension}
+ * instances for each message to send.
+ * @param messages The messages to send.
+ */
+ @Override
+ public void sendMessages(Set<Communicator.Message> messages) {
+ Set<RtpHeaderExtension> toSend = messages.stream().map(m -> generateRtpHeaderExtension(m))
+ .collect(Collectors.toSet());
+ Log.i(this, "sendMessages: sending=%s", messages);
+ mRtpAdapter.sendRtpHeaderExtensions(toSend);
+ }
+
+ /**
+ * Forces the protocol status to negotiated; for test purposes.
+ */
+ @Override
+ public void forceNegotiated() {
+ // If there is no supported RTP header extensions we need to fake it.
+ if (mSupportedRtpHeaderExtensionTypes == null
+ || mSupportedRtpHeaderExtensionTypes.isEmpty()) {
+ mSupportedRtpHeaderExtensionTypes.add(DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE);
+ mSupportedRtpHeaderExtensionTypes.add(CALL_STATE_RTP_HEADER_EXTENSION_TYPE);
+ }
+ mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_COMPLETE;
+ }
+
+ /**
+ * Forces the protocol status to un-negotiated; for test purposes.
+ */
+ @Override
+ public void forceNotNegotiated() {
+ mProtocolStatus = PROTOCOL_STATUS_NEGOTIATION_REQUIRED;
+ }
+
+ /**
+ * Called by the platform when RTP header extensions are received and need to be translated to
+ * concrete messages.
+ * Results in a callback via
+ * {@link com.android.internal.telephony.d2d.TransportProtocol.Callback#onMessagesReceived(Set)}
+ * to notify when incoming messages are received.
+ * @param extensions The received RTP header extensions.
+ */
+ @Override
+ public void onRtpHeaderExtensionsReceived(@NonNull Set<RtpHeaderExtension> extensions) {
+ Set<Communicator.Message> messages = extensions.stream().map(e -> extractMessage(e))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ if (messages.size() == 0) {
+ return;
+ }
+ mCallback.onMessagesReceived(messages);
+ }
+
+ /**
+ * Given a {@link RtpHeaderExtension} received from the network, parse out an found message.
+ * @param extension The RTP header extension to parse.
+ * @return The message, or {@code null} if no valid message found.
+ */
+ private Communicator.Message extractMessage(@NonNull RtpHeaderExtension extension) {
+ // First determine the URI to figure out the general classification of the message.
+ Optional<Uri> foundUri = mSupportedRtpHeaderExtensionTypes.stream()
+ .filter(et -> et.getLocalIdentifier() == extension.getLocalIdentifier())
+ .map(et -> et.getUri())
+ .findFirst();
+ if (!foundUri.isPresent()) {
+ Log.w(this, "extractMessage: localIdentifier=%d not supported.",
+ extension.getLocalIdentifier());
+ return null;
+ }
+
+ if (extension.getExtensionData() == null || extension.getExtensionData().length != 1) {
+ Log.w(this, "extractMessage: localIdentifier=%d message with invalid data length.",
+ extension.getLocalIdentifier());
+ return null;
+ }
+
+ Uri uri = foundUri.get();
+
+ // Extract the bits which are the message type.
+ byte messageTypeBits = (byte) (extension.getExtensionData()[0] & 0b1111);
+ byte messageValueBits = (byte) (extension.getExtensionData()[0]
+ & (0b1111 << RTP_PARAMETER_BIT_OFFSET));
+
+ int messageType;
+ int messageValue;
+ if (DEVICE_STATE_RTP_HEADER_EXTENSION.equals(uri)) {
+ Integer type = DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.getKey(messageTypeBits);
+ if (type == null) {
+ Log.w(this, "extractMessage: localIdentifier=%d message with invalid type %s.",
+ extension.getLocalIdentifier(), Integer.toBinaryString(messageTypeBits));
+ return null;
+ }
+ messageType = type;
+ switch (messageType) {
+ case Communicator.MESSAGE_DEVICE_BATTERY_STATE:
+ Integer val = BATTERY_STATE_VALUE_TO_RTP_BITS.getKey(messageValueBits);
+ if (val == null) {
+ Log.w(this, "extractMessage: localIdentifier=%d, battery state msg with "
+ + "invalid value=%s",
+ extension.getLocalIdentifier(),
+ Integer.toBinaryString(messageValueBits));
+ return null;
+ }
+ messageValue = val;
+ break;
+ case Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE:
+ Integer val2 = NETWORK_COVERAGE_VALUE_TO_RTP_BITS.getKey(messageValueBits);
+ if (val2 == null) {
+ Log.w(this, "extractMessage: localIdentifier=%d, network coverage msg with "
+ + "invalid value=%s",
+ extension.getLocalIdentifier(),
+ Integer.toBinaryString(messageValueBits));
+ return null;
+ }
+ messageValue = val2;
+ break;
+ default:
+ Log.w(this, "messageType=%s, value=%s; invalid value",
+ Integer.toBinaryString(messageTypeBits),
+ Integer.toBinaryString(messageValueBits));
+ return null;
+ }
+ } else if (CALL_STATE_RTP_HEADER_EXTENSION.equals(uri)) {
+ Integer typeValue = CALL_STATE_MSG_TYPE_TO_RTP_BITS.getKey(messageTypeBits);
+ if (typeValue == null) {
+ Log.w(this, "extractMessage: localIdentifier=%d, network coverage msg with "
+ + "invalid type=%s",
+ extension.getLocalIdentifier(),
+ Integer.toBinaryString(messageTypeBits));
+ return null;
+ }
+ messageType = typeValue;
+ switch (messageType) {
+ case Communicator.MESSAGE_CALL_AUDIO_CODEC:
+ Integer val = CODEC_VALUE_TO_RTP_BITS.getKey(messageValueBits);
+ if (val == null) {
+ Log.w(this, "extractMessage: localIdentifier=%d, audio codec msg with "
+ + "invalid value=%s",
+ extension.getLocalIdentifier(),
+ Integer.toBinaryString(messageValueBits));
+ return null;
+ }
+ messageValue = val;
+ break;
+ case Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE:
+ Integer val2 = RAT_VALUE_TO_RTP_BITS.getKey(messageValueBits);
+ if (val2 == null) {
+ Log.w(this, "extractMessage: localIdentifier=%d, rat type msg with "
+ + "invalid value=%s",
+ extension.getLocalIdentifier(),
+ Integer.toBinaryString(messageValueBits));
+ return null;
+ }
+ messageValue = val2;
+ break;
+ default:
+ Log.w(this, "messageType=%s, value=%s; invalid value",
+ Integer.toBinaryString(messageTypeBits),
+ Integer.toBinaryString(messageValueBits));
+ return null;
+ }
+ } else {
+ Log.w(this, "invalid uri=%s", uri);
+ return null;
+ }
+ Log.i(this, "extractMessage: messageType=%s, value=%s --> message=%d, value=%d",
+ Integer.toBinaryString(messageTypeBits), Integer.toBinaryString(messageValueBits),
+ messageType, messageValue);
+ return new Communicator.Message(messageType, messageValue);
+ }
+
+
+ /**
+ * Generates an {@link RtpHeaderExtension} based on the specified message.
+ * Per RFC8285, RTP header extensions have the format:
+ * (bit)
+ * ___byte__ __byte__
+ * 1234 5678 12345678
+ * +----+----+--------+
+ * | ID | len| data |
+ * +----+----+--------+
+ * Where ID is the {@link RtpHeaderExtensionType#getLocalIdentifier()}, len indicates the
+ * number of data bytes being sent ({@link RtpHeaderExtension#getExtensionData()}.size()),
+ * and data is the payload of the RTP header extension.
+ * The {@link RtpHeaderExtension} generated here contains the bytes necessary to populate the
+ * data field; the "ID" and "len" fields in the send RTP header extension are populated by the
+ * IMS service.
+ *
+ * This method is responsible for generating the data field in the RTP header extension to be
+ * sent.
+ *
+ * Device to device communication assumes the following format for the single byte payload:
+ * 12345678
+ * +--------+
+ * |PPPPVVVV|
+ * +--------+
+ * Where:
+ * PPPP - 4 bits reserved for indicating the parameter encoded (e.g. it could be an audio
+ * codec ({@link Communicator#MESSAGE_CALL_AUDIO_CODEC})).
+ * VVVV - 4 bits reserved for indicating the value of the parameter encoded (e.g. it could be
+ * the EVS codec ({@link Communicator#AUDIO_CODEC_EVS})).
+ *
+ * @param message The message to be sent via RTP header extensions.
+ * @return An {@link RtpHeaderExtension} representing the message.
+ */
+ public RtpHeaderExtension generateRtpHeaderExtension(Communicator.Message message) {
+ byte[] payload = new byte[1];
+ switch (message.getType()) {
+ case Communicator.MESSAGE_CALL_AUDIO_CODEC:
+ payload[0] |= CALL_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
+ payload[0] |= CODEC_VALUE_TO_RTP_BITS.getValue(message.getValue());
+ return new RtpHeaderExtension(
+ getRtpHeaderExtensionIdentifier(CALL_STATE_RTP_HEADER_EXTENSION),
+ payload);
+ case Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE:
+ payload[0] |= CALL_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
+ payload[0] |= RAT_VALUE_TO_RTP_BITS.getValue(message.getValue());
+ return new RtpHeaderExtension(
+ getRtpHeaderExtensionIdentifier(CALL_STATE_RTP_HEADER_EXTENSION),
+ payload);
+ case Communicator.MESSAGE_DEVICE_BATTERY_STATE:
+ payload[0] |= DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
+ payload[0] |= BATTERY_STATE_VALUE_TO_RTP_BITS.getValue(message.getValue());
+ return new RtpHeaderExtension(
+ getRtpHeaderExtensionIdentifier(DEVICE_STATE_RTP_HEADER_EXTENSION),
+ payload);
+ case Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE:
+ payload[0] |= DEVICE_STATE_MSG_TYPE_TO_RTP_BITS.getValue(message.getType());
+ payload[0] |= NETWORK_COVERAGE_VALUE_TO_RTP_BITS.getValue(message.getValue());
+ return new RtpHeaderExtension(
+ getRtpHeaderExtensionIdentifier(DEVICE_STATE_RTP_HEADER_EXTENSION),
+ payload);
+ }
+ return null;
+ }
+
+ /**
+ * Given a {@link Uri} identifying an RTP header extension, return the associated local
+ * identifier.
+ * @param requestedUri the requested {@link Uri}.
+ * @return the local identifier.
+ */
+ private int getRtpHeaderExtensionIdentifier(Uri requestedUri) {
+ return mSupportedRtpHeaderExtensionTypes.stream()
+ .filter(t -> t.getUri().equals(requestedUri))
+ .findFirst().get().getLocalIdentifier();
+ }
+
+ /**
+ * Notifies the {@link Communicator} that the RTP-based protocol is available.}
+ */
+ private void notifyProtocolReady() {
+ if (mCallback != null) {
+ mCallback.onNegotiationSuccess(this);
+ }
+ }
+
+ /**
+ * Notifies the {@link Communicator} that the RTP-based protocol is unavailable.
+ */
+ private void notifyProtocolUnavailable() {
+ if (mCallback != null) {
+ mCallback.onNegotiationFailed(this);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/d2d/Timeouts.java b/src/java/com/android/internal/telephony/d2d/Timeouts.java
new file mode 100644
index 0000000000..e01c920562
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/Timeouts.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+/**
+ * Timeouts and configuration parameters related to the device-to-device communication code.
+ */
+public final class Timeouts {
+
+ public static class Adapter {
+ private final ContentResolver mContentResolver;
+
+ public Adapter(ContentResolver cr) {
+ mContentResolver = cr;
+ }
+
+ public long getRtpMessageAckDurationMillis() {
+ return Timeouts.getRtpMessageAckDurationMillis(mContentResolver);
+ }
+
+ /**
+ * The minimum interval between DTMF digits.
+ * @return minimum interval in millis.
+ */
+ public long getDtmfMinimumIntervalMillis() {
+ return Timeouts.getDtmfMinimumIntervalMillis(mContentResolver);
+ }
+
+ public long getMaxDurationOfDtmfMessageMillis() {
+ return Timeouts.getMaxDurationOfDtmfMessageMillis(mContentResolver);
+ }
+
+ public long getDtmfDurationFuzzMillis() {
+ return Timeouts.getDtmfDurationFuzzMillis(mContentResolver);
+ }
+
+ public long getDtmfNegotiationTimeoutMillis() {
+ return Timeouts.getDtmfNegotiationTimeoutMillis(mContentResolver);
+ }
+ }
+
+ /** A prefix to use for all keys so to not clobber the global namespace. */
+ private static final String PREFIX = "telephony.d2d.";
+
+ /**
+ * Returns the timeout value from Settings or the default value if it hasn't been changed. This
+ * method is safe to call from any thread, including the UI thread.
+ *
+ * @param contentResolver The content resolved.
+ * @param key Settings key to retrieve.
+ * @param defaultValue Default value, in milliseconds.
+ * @return The timeout value from Settings or the default value if it hasn't been changed.
+ */
+ private static long get(ContentResolver contentResolver, String key, long defaultValue) {
+ return Settings.Secure.getLong(contentResolver, PREFIX + key, defaultValue);
+ }
+
+ /**
+ * Determines the length of time to wait for acknowledgement of an RTP header extension before
+ * retrying a message send.
+ * @param cr
+ * @return
+ */
+ public static long getRtpMessageAckDurationMillis(ContentResolver cr) {
+ return get(cr, "rtp_message_ack_duration_millis", 1000L);
+ }
+
+ /**
+ * Determines the minimum duration between DTMF digits. Digits are sent with this much spacing
+ * between them.
+ * @param cr
+ * @return
+ */
+ public static long getDtmfMinimumIntervalMillis(ContentResolver cr) {
+ return get(cr, "dtmf_minimum_interval_millis", 100L);
+ }
+
+ /**
+ * Determines the maximum amount of time to wait for a single DTMF sequence.
+ * @param cr
+ * @return
+ */
+ public static long getMaxDurationOfDtmfMessageMillis(ContentResolver cr) {
+ return get(cr, "dtmf_max_message_duration_millis", 1000L);
+ }
+
+ /**
+ * Determines the maximum amount of time to wait for negotiation of the DTMF protocol.
+ * @param cr
+ * @return
+ */
+ public static long getDtmfNegotiationTimeoutMillis(ContentResolver cr) {
+ return get(cr, "dtmf_negotiation_timeout_millis", 3000L);
+ }
+
+ /**
+ * A random amount of time up to this amount will be added to
+ * {@link #getMaxDurationOfDtmfMessageMillis(ContentResolver)} when determining how long to
+ * wait before sending a DTMF message. This fuzz factor is used to account for timing
+ * discrepancies between devices.
+ * @param cr
+ * @return
+ */
+ public static long getDtmfDurationFuzzMillis(ContentResolver cr) {
+ return get(cr, "dtmf_duration_fuzz_millis", 10L);
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/d2d/TransportProtocol.java b/src/java/com/android/internal/telephony/d2d/TransportProtocol.java
new file mode 100644
index 0000000000..91bf7d3251
--- /dev/null
+++ b/src/java/com/android/internal/telephony/d2d/TransportProtocol.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import android.annotation.NonNull;
+
+import java.util.Set;
+
+/**
+ * Base definition for a device-to-device communication protocol.
+ */
+public interface TransportProtocol {
+
+ /**
+ * Callbacks from the {@link TransportProtocol} to the {@link Communicator} which indicates
+ * important events like protocol negotiation status as well as incoming messages.
+ */
+ public interface Callback {
+ /**
+ * The {@link TransportProtocol} calls this method when protocol negotiation has completed
+ * successfully.
+ * @param protocol The protocol which succeeded (should be {@code this}).
+ */
+ void onNegotiationSuccess(@NonNull TransportProtocol protocol);
+
+ /**
+ * The {@link TransportProtocol} calls this method when protocol negotiation has failed.
+ * @param protocol The protocol which failed (should be {@code this}).
+ */
+ void onNegotiationFailed(@NonNull TransportProtocol protocol);
+
+ /**
+ * The {@link TransportProtocol} calls this method when the protocol has received incoming
+ * messages.
+ * @param messages The received messages.
+ */
+ void onMessagesReceived(@NonNull Set<Communicator.Message> messages);
+ }
+
+ /**
+ * Called by the {@link Communicator} to register for callbacks regarding the transport's
+ * progress.
+ * @param callback the callback to use.
+ */
+ void setCallback(Callback callback);
+
+ /**
+ * Called by {@link Communicator} when negotiation of device-to-device communication using a
+ * protocol should be started.
+ */
+ void startNegotiation();
+
+ /**
+ * Called by {@link Communicator} when a message should be sent using device-to-device
+ * communication.
+ * @param messages the messages to send via the transport.
+ */
+ void sendMessages(Set<Communicator.Message> messages);
+
+ /**
+ * Forces this transport to be in a negotiated state.
+ */
+ void forceNegotiated();
+
+ /**
+ * Forces this transport to be in a non-negotiated state.
+ */
+ void forceNotNegotiated();
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
index 9460ddcbc5..53b7515328 100644
--- a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
@@ -26,7 +26,6 @@ import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
@@ -34,22 +33,27 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.Annotation.ApnType;
+import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.IQualifiedNetworksService;
import android.telephony.data.IQualifiedNetworksServiceCallback;
import android.telephony.data.QualifiedNetworksService;
+import android.telephony.data.ThrottleStatus;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import com.android.internal.telephony.Phone;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.UUID;
import java.util.stream.Collectors;
/**
@@ -58,8 +62,10 @@ import java.util.stream.Collectors;
* networks changes.
*/
public class AccessNetworksManager extends Handler {
- private static final String TAG = AccessNetworksManager.class.getSimpleName();
+ private final String mLogTag;
private static final boolean DBG = false;
+ private final UUID mAnomalyUUID = UUID.fromString("c2d1a639-00e2-4561-9619-6acf37d90590");
+ private String mLastBoundPackageName;
private static final int[] SUPPORTED_APN_TYPES = {
ApnSetting.TYPE_DEFAULT,
@@ -68,11 +74,10 @@ public class AccessNetworksManager extends Handler {
ApnSetting.TYPE_IMS,
ApnSetting.TYPE_CBS,
ApnSetting.TYPE_SUPL,
- ApnSetting.TYPE_EMERGENCY
+ ApnSetting.TYPE_EMERGENCY,
+ ApnSetting.TYPE_XCAP
};
- private static final int EVENT_BIND_QUALIFIED_NETWORKS_SERVICE = 1;
-
private final Phone mPhone;
private final CarrierConfigManager mCarrierConfigManager;
@@ -90,6 +95,8 @@ public class AccessNetworksManager extends Handler {
private final RegistrantList mQualifiedNetworksChangedRegistrants = new RegistrantList();
+ private final Set<DataThrottler> mDataThrottlers = new HashSet<>();
+
private final BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -101,12 +108,27 @@ public class AccessNetworksManager extends Handler {
// package name can come from the carrier config. Note that we still get this event
// even when SIM is absent.
if (DBG) log("Carrier config changed. Try to bind qualified network service.");
- sendEmptyMessage(EVENT_BIND_QUALIFIED_NETWORKS_SERVICE);
+ bindQualifiedNetworksService();
}
}
};
/**
+ * Registers the data throttler in order to receive APN status changes.
+ *
+ * @param dataThrottler the data throttler to register
+ */
+ public void registerDataThrottler(DataThrottler dataThrottler) {
+ this.post(() -> {
+ QualifiedNetworksServiceConnection serviceConnection = mServiceConnection;
+ this.mDataThrottlers.add(dataThrottler);
+ if (serviceConnection != null) {
+ serviceConnection.registerDataThrottler(dataThrottler);
+ }
+ });
+ }
+
+ /**
* Represents qualified network types list on a specific APN type.
*/
public static class QualifiedNetworks {
@@ -138,31 +160,95 @@ public class AccessNetworksManager extends Handler {
@Override
public void binderDied() {
// TODO: try to rebind the service.
- loge("QualifiedNetworksService(" + mTargetBindingPackageName + ") died.");
+ String message = "Qualified network service " + mLastBoundPackageName + " died.";
+ loge(message);
+ AnomalyReporter.reportAnomaly(mAnomalyUUID, message);
}
}
private final class QualifiedNetworksServiceConnection implements ServiceConnection {
+
+ /**
+ * The APN throttle status callback is attached to the service connection so that they have
+ * the same life cycle.
+ */
+ @NonNull
+ private final ThrottleStatusChangedCallback mThrottleStatusCallback;
+
+ QualifiedNetworksServiceConnection() {
+ mThrottleStatusCallback = new ThrottleStatusChangedCallback();
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DBG) log("onServiceConnected " + name);
mIQualifiedNetworksService = IQualifiedNetworksService.Stub.asInterface(service);
mDeathRecipient = new AccessNetworksManagerDeathRecipient();
+ mLastBoundPackageName = getQualifiedNetworksServicePackageName();
try {
service.linkToDeath(mDeathRecipient, 0 /* flags */);
mIQualifiedNetworksService.createNetworkAvailabilityProvider(mPhone.getPhoneId(),
new QualifiedNetworksServiceCallback());
+
+ registerDataThrottlersFirstTime();
+
} catch (RemoteException e) {
- mDeathRecipient.binderDied();
loge("Remote exception. " + e);
}
}
+
@Override
public void onServiceDisconnected(ComponentName name) {
if (DBG) log("onServiceDisconnected " + name);
+ unregisterForThrottleCallbacks();
mTargetBindingPackageName = null;
}
+
+ /**
+ * Runs on all of the data throttlers when the service is connected
+ */
+ private void registerDataThrottlersFirstTime() {
+ post(() -> {
+ for (DataThrottler dataThrottler : mDataThrottlers) {
+ dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
+ }
+ });
+ }
+
+ private void registerDataThrottler(DataThrottler dataThrottler) {
+ post(() -> {
+ dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
+ });
+ }
+
+ private void unregisterForThrottleCallbacks() {
+ post(() -> {
+ for (DataThrottler dataThrottler : mDataThrottlers) {
+ dataThrottler.unregisterForThrottleStatusChanges(mThrottleStatusCallback);
+ }
+ });
+ }
+ }
+
+ private class ThrottleStatusChangedCallback implements DataThrottler.Callback {
+ @Override
+ public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
+ post(() -> {
+ try {
+ List<ThrottleStatus> throttleStatusesBySlot =
+ throttleStatuses
+ .stream()
+ .filter(x -> x.getSlotIndex() == mPhone.getPhoneId())
+ .collect(Collectors.toList());
+
+ mIQualifiedNetworksService.reportThrottleStatusChanged(mPhone.getPhoneId(),
+ throttleStatusesBySlot);
+ } catch (Exception ex) {
+ loge("onThrottleStatusChanged", ex);
+ }
+ });
+ }
}
private final class QualifiedNetworksServiceCallback extends
@@ -209,6 +295,7 @@ public class AccessNetworksManager extends Handler {
mPhone = phone;
mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
Context.CARRIER_CONFIG_SERVICE);
+ mLogTag = "ANM-" + mPhone.getPhoneId();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
@@ -218,25 +305,9 @@ public class AccessNetworksManager extends Handler {
contextAsUser.registerReceiver(mConfigChangedReceiver, intentFilter,
null /* broadcastPermission */, null);
} catch (PackageManager.NameNotFoundException e) {
- Rlog.e(TAG, "Package name not found: " + e.getMessage());
- }
- sendEmptyMessage(EVENT_BIND_QUALIFIED_NETWORKS_SERVICE);
- }
-
- /**
- * Handle message events
- *
- * @param msg The message to handle
- */
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_BIND_QUALIFIED_NETWORKS_SERVICE:
- bindQualifiedNetworksService();
- break;
- default:
- loge("Unhandled event " + msg.what);
+ loge("Package name not found: ", e);
}
+ bindQualifiedNetworksService();
}
/**
@@ -244,54 +315,57 @@ public class AccessNetworksManager extends Handler {
* configuration from carrier config if it exists. If not, read it from resources.
*/
private void bindQualifiedNetworksService() {
- Intent intent = null;
- String packageName = getQualifiedNetworksServicePackageName();
- String className = getQualifiedNetworksServiceClassName();
-
- if (DBG) log("Qualified network service package = " + packageName);
- if (TextUtils.isEmpty(packageName)) {
- loge("Can't find the binding package");
- return;
- }
-
- if (TextUtils.isEmpty(className)) {
- intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE);
- intent.setPackage(packageName);
- } else {
- ComponentName cm = new ComponentName(packageName, className);
- intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE)
- .setComponent(cm);
- }
+ post(() -> {
+ Intent intent = null;
+ String packageName = getQualifiedNetworksServicePackageName();
+ String className = getQualifiedNetworksServiceClassName();
+
+ if (DBG) log("Qualified network service package = " + packageName);
+ if (TextUtils.isEmpty(packageName)) {
+ loge("Can't find the binding package");
+ return;
+ }
- if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
- if (DBG) log("Service " + packageName + " already bound or being bound.");
- return;
- }
+ if (TextUtils.isEmpty(className)) {
+ intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+ } else {
+ ComponentName cm = new ComponentName(packageName, className);
+ intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE)
+ .setComponent(cm);
+ }
- if (mIQualifiedNetworksService != null
- && mIQualifiedNetworksService.asBinder().isBinderAlive()) {
- // Remove the network availability updater and then unbind the service.
- try {
- mIQualifiedNetworksService.removeNetworkAvailabilityProvider(mPhone.getPhoneId());
- } catch (RemoteException e) {
- loge("Cannot remove network availability updater. " + e);
+ if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
+ if (DBG) log("Service " + packageName + " already bound or being bound.");
+ return;
}
- mPhone.getContext().unbindService(mServiceConnection);
- }
+ if (mIQualifiedNetworksService != null
+ && mIQualifiedNetworksService.asBinder().isBinderAlive()) {
+ // Remove the network availability updater and then unbind the service.
+ try {
+ mIQualifiedNetworksService.removeNetworkAvailabilityProvider(
+ mPhone.getPhoneId());
+ } catch (RemoteException e) {
+ loge("Cannot remove network availability updater. " + e);
+ }
- try {
- mServiceConnection = new QualifiedNetworksServiceConnection();
- log("bind to " + packageName);
- if (!mPhone.getContext().bindService(intent, mServiceConnection,
+ mPhone.getContext().unbindService(mServiceConnection);
+ }
+
+ try {
+ mServiceConnection = new QualifiedNetworksServiceConnection();
+ log("bind to " + packageName);
+ if (!mPhone.getContext().bindService(intent, mServiceConnection,
Context.BIND_AUTO_CREATE)) {
- loge("Cannot bind to the qualified networks service.");
- return;
+ loge("Cannot bind to the qualified networks service.");
+ return;
+ }
+ mTargetBindingPackageName = packageName;
+ } catch (Exception e) {
+ loge("Cannot bind to the qualified networks service. Exception: " + e);
}
- mTargetBindingPackageName = packageName;
- } catch (Exception e) {
- loge("Cannot bind to the qualified networks service. Exception: " + e);
- }
+ });
}
/**
@@ -399,9 +473,10 @@ public class AccessNetworksManager extends Handler {
pw.increaseIndent();
for (int i = 0; i < mAvailableNetworks.size(); i++) {
- pw.println("APN type " + ApnSetting.getApnTypeString(mAvailableNetworks.keyAt(i))
+ pw.println("APN type "
+ + ApnSetting.getApnTypeString(mAvailableNetworks.keyAt(i))
+ ": [" + Arrays.stream(mAvailableNetworks.valueAt(i))
- .mapToObj(type -> AccessNetworkType.toString(type))
+ .mapToObj(AccessNetworkType::toString)
.collect(Collectors.joining(",")) + "]");
}
pw.decreaseIndent();
@@ -409,11 +484,15 @@ public class AccessNetworksManager extends Handler {
}
private void log(String s) {
- Rlog.d(TAG, s);
+ Rlog.d(mLogTag, s);
}
private void loge(String s) {
- Rlog.e(TAG, s);
+ Rlog.e(mLogTag, s);
+ }
+
+ private void loge(String s, Exception ex) {
+ Rlog.e(mLogTag, s, ex);
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java b/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java
index f7dbcfcb8b..156ac926e5 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnConfigTypeRepository.java
@@ -80,6 +80,7 @@ public class ApnConfigTypeRepository {
add(ApnSetting.TYPE_EMERGENCY, apnTypeMap);
add(ApnSetting.TYPE_MCX, apnTypeMap);
add(ApnSetting.TYPE_XCAP, apnTypeMap);
+ add(ApnSetting.TYPE_ENTERPRISE, apnTypeMap);
}
@NonNull
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 75ba12098c..8d7bc0657e 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony.dataconnection;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -23,6 +24,7 @@ import android.os.Message;
import android.telephony.Annotation.ApnType;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.SparseIntArray;
@@ -113,8 +115,7 @@ public class ApnContext {
* @param tracker Data call tracker
* @param priority Priority of APN type
*/
- public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker,
- int priority) {
+ public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker, int priority) {
mPhone = phone;
mApnType = apnType;
mState = DctConstants.State.IDLE;
@@ -123,7 +124,8 @@ public class ApnContext {
mPriority = priority;
LOG_TAG = logTag;
mDcTracker = tracker;
- mRetryManager = new RetryManager(phone, apnType);
+ mRetryManager = new RetryManager(phone, tracker.getDataThrottler(),
+ ApnSetting.getApnTypesBitmaskFromString(apnType));
}
@@ -234,18 +236,9 @@ public class ApnContext {
}
/**
- * Save the modem suggested delay for retrying the current APN.
- * This method is called when we get the suggested delay from RIL.
- * @param delay The delay in milliseconds
- */
- public void setModemSuggestedDelay(long delay) {
- mRetryManager.setModemSuggestedDelay(delay);
- }
-
- /**
* Get the delay for trying the next APN setting if the current one failed.
* @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
- * delay.
+ * delay.
* @return The delay in milliseconds
*/
public long getDelayForNextApn(boolean failFastEnabled) {
@@ -264,7 +257,7 @@ public class ApnContext {
* Get the list of waiting APNs.
* @return the list of waiting APNs
*/
- public ArrayList<ApnSetting> getWaitingApns() {
+ public @NonNull ArrayList<ApnSetting> getWaitingApns() {
return mRetryManager.getWaitingApns();
}
@@ -297,10 +290,8 @@ public class ApnContext {
}
if (mState == DctConstants.State.FAILED) {
- if (mRetryManager.getWaitingApns() != null) {
- // when teardown the connection and set to IDLE
- mRetryManager.getWaitingApns().clear();
- }
+ // when teardown the connection and set to IDLE
+ mRetryManager.getWaitingApns().clear();
}
}
@@ -404,7 +395,7 @@ public class ApnContext {
}
private final LocalLog mLocalLog = new LocalLog(150);
- private final ArrayList<NetworkRequest> mNetworkRequests = new ArrayList<>();
+ private final ArraySet<NetworkRequest> mNetworkRequests = new ArraySet<>();
private final LocalLog mStateLocalLog = new LocalLog(50);
public void requestLog(String str) {
@@ -413,14 +404,22 @@ public class ApnContext {
}
}
+ /**
+ * Request a network
+ *
+ * @param networkRequest Network request from clients
+ * @param type The request type
+ * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
+ * handover is completed. For normal request, this should be null.
+ */
public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
- Message onCompleteMsg) {
+ Message onHandoverCompleteMsg) {
synchronized (mRefCountLock) {
mNetworkRequests.add(networkRequest);
logl("requestNetwork for " + networkRequest + ", type="
+ DcTracker.requestTypeToString(type));
mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type,
- onCompleteMsg);
+ onHandoverCompleteMsg);
if (mDataConnection != null) {
// New network request added. Should re-evaluate properties of
// the data connection. For example, the score may change.
@@ -459,12 +458,10 @@ public class ApnContext {
synchronized (mRefCountLock) {
for (NetworkRequest nr : mNetworkRequests) {
if (excludeDun &&
- nr.networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
continue;
}
- if (!nr.networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
+ if (!nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
return true;
}
}
@@ -543,10 +540,9 @@ public class ApnContext {
}
static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
- NetworkCapabilities nc = nr.networkCapabilities;
// For now, ignore the bandwidth stuff
- if (nc.getTransportTypes().length > 0 &&
- nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
+ if (nr.getTransportTypes().length > 0
+ && !nr.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return ApnSetting.TYPE_NONE;
}
@@ -555,49 +551,53 @@ public class ApnContext {
int apnType = ApnSetting.TYPE_NONE;
boolean error = false;
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
apnType = ApnSetting.TYPE_DEFAULT;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_MMS;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_SUPL;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_DUN;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_FOTA;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_IMS;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_CBS;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_IA;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_EMERGENCY;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MCX)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_MCX)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_MCX;
}
- if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
if (apnType != ApnSetting.TYPE_NONE) error = true;
apnType = ApnSetting.TYPE_XCAP;
}
+ if (nr.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) {
+ if (apnType != ApnSetting.TYPE_NONE) error = true;
+ apnType = ApnSetting.TYPE_ENTERPRISE;
+ }
if (error) {
// TODO: If this error condition is removed, the framework's handling of
// NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
diff --git a/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
index a4b5929ff0..b38e2be8b1 100644
--- a/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
+++ b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
@@ -16,6 +16,9 @@
package com.android.internal.telephony.dataconnection;
+import static android.telephony.data.DataServiceCallback.RESULT_SUCCESS;
+
+import android.annotation.Nullable;
import android.net.LinkProperties;
import android.os.AsyncResult;
import android.os.Handler;
@@ -26,7 +29,10 @@ import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
import android.telephony.data.DataServiceCallback;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.telephony.Rlog;
@@ -50,6 +56,9 @@ public class CellularDataService extends DataService {
private static final int SET_DATA_PROFILE_COMPLETE = 4;
private static final int REQUEST_DATA_CALL_LIST_COMPLETE = 5;
private static final int DATA_CALL_LIST_CHANGED = 6;
+ private static final int START_HANDOVER = 7;
+ private static final int CANCEL_HANDOVER = 8;
+ private static final int APN_UNTHROTTLED = 9;
private class CellularDataServiceProvider extends DataService.DataServiceProvider {
@@ -75,29 +84,29 @@ public class CellularDataService extends DataService {
DataCallResponse response = (DataCallResponse) ar.result;
callback.onSetupDataCallComplete(ar.exception != null
? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
- : DataServiceCallback.RESULT_SUCCESS,
+ : RESULT_SUCCESS,
response);
break;
case DEACTIVATE_DATA_ALL_COMPLETE:
callback.onDeactivateDataCallComplete(ar.exception != null
? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
- : DataServiceCallback.RESULT_SUCCESS);
+ : RESULT_SUCCESS);
break;
case SET_INITIAL_ATTACH_APN_COMPLETE:
callback.onSetInitialAttachApnComplete(ar.exception != null
? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
- : DataServiceCallback.RESULT_SUCCESS);
+ : RESULT_SUCCESS);
break;
case SET_DATA_PROFILE_COMPLETE:
callback.onSetDataProfileComplete(ar.exception != null
? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
- : DataServiceCallback.RESULT_SUCCESS);
+ : RESULT_SUCCESS);
break;
case REQUEST_DATA_CALL_LIST_COMPLETE:
callback.onRequestDataCallListComplete(
ar.exception != null
? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
- : DataServiceCallback.RESULT_SUCCESS,
+ : RESULT_SUCCESS,
ar.exception != null
? null : (List<DataCallResponse>) ar.result
);
@@ -105,21 +114,54 @@ public class CellularDataService extends DataService {
case DATA_CALL_LIST_CHANGED:
notifyDataCallListChanged((List<DataCallResponse>) ar.result);
break;
+ case START_HANDOVER:
+ callback.onHandoverStarted(toResultCode(ar.exception));
+ break;
+ case CANCEL_HANDOVER:
+ callback.onHandoverCancelled(toResultCode(ar.exception));
+ break;
+ case APN_UNTHROTTLED:
+ notifyApnUnthrottled((String) ar.result);
+ break;
default:
loge("Unexpected event: " + message.what);
- return;
}
}
};
if (DBG) log("Register for data call list changed.");
mPhone.mCi.registerForDataCallListChanged(mHandler, DATA_CALL_LIST_CHANGED, null);
+
+ if (DBG) log("Register for apn unthrottled.");
+ mPhone.mCi.registerForApnUnthrottled(mHandler, APN_UNTHROTTLED, null);
+ }
+
+
+ /* Converts the result code for start handover and cancel handover */
+ @DataServiceCallback.ResultCode private int toResultCode(@Nullable Throwable t) {
+ if (t == null) {
+ return RESULT_SUCCESS;
+ } else {
+ if (t instanceof CommandException) {
+ CommandException ce = (CommandException) t;
+ if (ce.getCommandError() == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+ return DataServiceCallback.RESULT_ERROR_UNSUPPORTED;
+ } else {
+ return DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE;
+ }
+ } else {
+ loge("Throwable is of type " + t.getClass().getSimpleName()
+ + " but should be CommandException");
+ return DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE;
+ }
+ }
}
@Override
- public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- DataServiceCallback callback) {
+ public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
+ boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties,
+ int pduSessionId, NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, DataServiceCallback callback) {
if (DBG) log("setupDataCall " + getSlotIndex());
Message message = null;
@@ -131,7 +173,8 @@ public class CellularDataService extends DataService {
}
mPhone.mCi.setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming,
- reason, linkProperties, message);
+ reason, linkProperties, pduSessionId, sliceInfo, trafficDescriptor,
+ matchAllRuleAllowed, message);
}
@Override
@@ -196,6 +239,31 @@ public class CellularDataService extends DataService {
}
@Override
+ public void startHandover(int cid, DataServiceCallback callback) {
+ if (DBG) log("startHandover " + getSlotIndex());
+ Message message = null;
+ // Only obtain the message when the caller wants a callback. If the caller doesn't care
+ // the request completed or results, then no need to pass the message down.
+ if (callback != null) {
+ message = Message.obtain(mHandler, START_HANDOVER);
+ mCallbackMap.put(message, callback);
+ }
+ mPhone.mCi.startHandover(message, cid);
+ }
+
+ @Override
+ public void cancelHandover(int cid, DataServiceCallback callback) {
+ Message message = null;
+ // Only obtain the message when the caller wants a callback. If the caller doesn't care
+ // the request completed or results, then no need to pass the message down.
+ if (callback != null) {
+ message = Message.obtain(mHandler, CANCEL_HANDOVER);
+ mCallbackMap.put(message, callback);
+ }
+ mPhone.mCi.cancelHandover(message, cid);
+ }
+
+ @Override
public void close() {
mPhone.mCi.unregisterForDataCallListChanged(mHandler);
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index f9f23c1896..b3453fb904 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -16,10 +16,12 @@
package com.android.internal.telephony.dataconnection;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
+import static android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET;
+
+import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
@@ -31,14 +33,17 @@ import android.net.LinkProperties;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
-import android.net.NetworkInfo;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.SocketKeepalive;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnManager;
+import android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener;
+import android.net.vcn.VcnNetworkPolicyResult;
import android.os.AsyncResult;
+import android.os.HandlerExecutor;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.SystemClock;
@@ -48,44 +53,53 @@ import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.DataState;
+import android.telephony.Annotation.NetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.DataFailCause;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataCallResponse.HandoverFailureMode;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
import android.telephony.data.DataServiceCallback;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.Qos;
+import android.telephony.data.QosBearerSession;
+import android.telephony.data.TrafficDescriptor;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Pair;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.CallTracker;
import com.android.internal.telephony.CarrierSignalAgent;
import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.LinkCapacityEstimate;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
+import com.android.internal.telephony.metrics.DataCallSessionStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
@@ -95,14 +109,18 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* {@hide}
@@ -126,6 +144,14 @@ public class DataConnection extends StateMachine {
private static final String RAT_NAME_EVDO = "evdo";
/**
+ * OSId for "Android", using UUID version 5 with namespace ISO OSI.
+ * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching.
+ */
+ private static final UUID OS_ID = UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47");
+
+ private static final int MIN_V6_MTU = 1280;
+
+ /**
* The data connection is not being or been handovered. Note this is the state for the source
* data connection, not destination data connection
*/
@@ -143,6 +169,7 @@ public class DataConnection extends StateMachine {
*/
private static final int HANDOVER_STATE_COMPLETED = 3;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"HANDOVER_STATE_"}, value = {
@@ -172,6 +199,9 @@ public class DataConnection extends StateMachine {
// The Tester for failing all bringup's
private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
+ // Whether or not the data connection should allocate its own pdu session id
+ private final boolean mDoAllocatePduSessionId;
+
private static AtomicInteger mInstanceNumber = new AtomicInteger(0);
private AsyncChannel mAc;
@@ -186,6 +216,9 @@ public class DataConnection extends StateMachine {
private int[] mAdministratorUids = new int[0];
+ // stats per data call
+ private DataCallSessionStats mDataCallSessionStats;
+
/**
* Used internally for saving connecting parameters.
*/
@@ -256,29 +289,42 @@ public class DataConnection extends StateMachine {
}
}
- private ApnSetting mApnSetting;
+ private volatile ApnSetting mApnSetting;
private ConnectionParams mConnectionParams;
private DisconnectParams mDisconnectParams;
@DataFailureCause
private int mDcFailCause;
+ @HandoverFailureMode
+ private int mHandoverFailureMode;
+
private Phone mPhone;
private DataServiceManager mDataServiceManager;
+ private VcnManager mVcnManager;
private final int mTransportType;
private LinkProperties mLinkProperties = new LinkProperties();
+ private int mPduSessionId;
private long mCreateTime;
private long mLastFailTime;
@DataFailureCause
private int mLastFailCause;
private static final String NULL_IP = "0.0.0.0";
private Object mUserData;
- private int mSubscriptionOverride;
+ private boolean mCongestedOverride;
private boolean mUnmeteredOverride;
private int mRilRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
private int mDataRegState = Integer.MAX_VALUE;
- private NetworkInfo mNetworkInfo;
+ // Indicating data connection is suspended due to temporary reasons, for example, out of
+ // service, concurrency voice/data not supported, etc.. Note this flag is only meaningful when
+ // data is in active state. When data is in inactive, connecting, or disconnecting, this flag
+ // is unmeaningful.
+ private boolean mIsSuspended;
private int mDownlinkBandwidth = 14;
private int mUplinkBandwidth = 14;
+ private Qos mDefaultQos = null;
+ private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
+ private NetworkSliceInfo mSliceInfo;
+ private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>();
/** The corresponding network agent for this data connection. */
private DcNetworkAgent mNetworkAgent;
@@ -297,10 +343,13 @@ public class DataConnection extends StateMachine {
public int mCid;
@HandoverState
- private int mHandoverState;
+ private int mHandoverState = HANDOVER_STATE_IDLE;
private final Map<ApnContext, ConnectionParams> mApnContexts = new ConcurrentHashMap<>();
PendingIntent mReconnectIntent = null;
+ /** Class used to track VCN-defined Network policies for this DcNetworkAgent. */
+ private final VcnNetworkPolicyChangeListener mVcnPolicyChangeListener =
+ new DataConnectionVcnNetworkPolicyChangeListener();
// ***** Event codes for driving the state machine, package visible for Dcc
static final int BASE = Protocol.BASE_DATA_CONNECTION;
@@ -308,7 +357,6 @@ public class DataConnection extends StateMachine {
static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
static final int EVENT_DEACTIVATE_DONE = BASE + 3;
static final int EVENT_DISCONNECT = BASE + 4;
- static final int EVENT_RIL_CONNECTED = BASE + 5;
static final int EVENT_DISCONNECT_ALL = BASE + 6;
static final int EVENT_DATA_STATE_CHANGED = BASE + 7;
static final int EVENT_TEAR_DOWN_NOW = BASE + 8;
@@ -319,7 +367,7 @@ public class DataConnection extends StateMachine {
static final int EVENT_BW_REFRESH_RESPONSE = BASE + 14;
static final int EVENT_DATA_CONNECTION_VOICE_CALL_STARTED = BASE + 15;
static final int EVENT_DATA_CONNECTION_VOICE_CALL_ENDED = BASE + 16;
- static final int EVENT_DATA_CONNECTION_OVERRIDE_CHANGED = BASE + 17;
+ static final int EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED = BASE + 17;
static final int EVENT_KEEPALIVE_STATUS = BASE + 18;
static final int EVENT_KEEPALIVE_STARTED = BASE + 19;
static final int EVENT_KEEPALIVE_STOPPED = BASE + 20;
@@ -334,7 +382,15 @@ public class DataConnection extends StateMachine {
static final int EVENT_NR_FREQUENCY_CHANGED = BASE + 29;
static final int EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED = BASE + 30;
static final int EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED = BASE + 31;
- private static final int CMD_TO_STRING_COUNT = EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED - BASE + 1;
+ static final int EVENT_CSS_INDICATOR_CHANGED = BASE + 32;
+ static final int EVENT_UPDATE_SUSPENDED_STATE = BASE + 33;
+ static final int EVENT_START_HANDOVER = BASE + 34;
+ static final int EVENT_CANCEL_HANDOVER = BASE + 35;
+ static final int EVENT_START_HANDOVER_ON_TARGET = BASE + 36;
+ static final int EVENT_ALLOCATE_PDU_SESSION_ID = BASE + 37;
+ static final int EVENT_RELEASE_PDU_SESSION_ID = BASE + 38;
+ static final int EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE = BASE + 39;
+ private static final int CMD_TO_STRING_COUNT = EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE + 1;
private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
static {
@@ -343,7 +399,6 @@ public class DataConnection extends StateMachine {
"EVENT_SETUP_DATA_CONNECTION_DONE";
sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
- sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
sCmdToString[EVENT_DATA_STATE_CHANGED - BASE] = "EVENT_DATA_STATE_CHANGED";
sCmdToString[EVENT_TEAR_DOWN_NOW - BASE] = "EVENT_TEAR_DOWN_NOW";
@@ -357,8 +412,8 @@ public class DataConnection extends StateMachine {
"EVENT_DATA_CONNECTION_VOICE_CALL_STARTED";
sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] =
"EVENT_DATA_CONNECTION_VOICE_CALL_ENDED";
- sCmdToString[EVENT_DATA_CONNECTION_OVERRIDE_CHANGED - BASE] =
- "EVENT_DATA_CONNECTION_OVERRIDE_CHANGED";
+ sCmdToString[EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED - BASE] =
+ "EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED";
sCmdToString[EVENT_KEEPALIVE_STATUS - BASE] = "EVENT_KEEPALIVE_STATUS";
sCmdToString[EVENT_KEEPALIVE_STARTED - BASE] = "EVENT_KEEPALIVE_STARTED";
sCmdToString[EVENT_KEEPALIVE_STOPPED - BASE] = "EVENT_KEEPALIVE_STOPPED";
@@ -378,6 +433,15 @@ public class DataConnection extends StateMachine {
"EVENT_CARRIER_CONFIG_LINK_BANDWIDTHS_CHANGED";
sCmdToString[EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED - BASE] =
"EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED";
+ sCmdToString[EVENT_CSS_INDICATOR_CHANGED - BASE] = "EVENT_CSS_INDICATOR_CHANGED";
+ sCmdToString[EVENT_UPDATE_SUSPENDED_STATE - BASE] = "EVENT_UPDATE_SUSPENDED_STATE";
+ sCmdToString[EVENT_START_HANDOVER - BASE] = "EVENT_START_HANDOVER";
+ sCmdToString[EVENT_CANCEL_HANDOVER - BASE] = "EVENT_CANCEL_HANDOVER";
+ sCmdToString[EVENT_START_HANDOVER_ON_TARGET - BASE] = "EVENT_START_HANDOVER_ON_TARGET";
+ sCmdToString[EVENT_ALLOCATE_PDU_SESSION_ID - BASE] = "EVENT_ALLOCATE_PDU_SESSION_ID";
+ sCmdToString[EVENT_RELEASE_PDU_SESSION_ID - BASE] = "EVENT_RELEASE_PDU_SESSION_ID";
+ sCmdToString[EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE - BASE] =
+ "EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE";
}
// Convert cmd to string or null if unknown
static String cmdToString(int cmd) {
@@ -402,14 +466,15 @@ public class DataConnection extends StateMachine {
public static DataConnection makeDataConnection(Phone phone, int id, DcTracker dct,
DataServiceManager dataServiceManager,
DcTesterFailBringUpAll failBringUpAll,
- DcController dcc) {
+ DcController dcc,
+ boolean doAllocatePduSessionId) {
String transportType = (dataServiceManager.getTransportType()
== AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
? "C" // Cellular
: "I"; // IWLAN
DataConnection dc = new DataConnection(phone, transportType + "-"
+ mInstanceNumber.incrementAndGet(), id, dct, dataServiceManager, failBringUpAll,
- dcc);
+ dcc, doAllocatePduSessionId);
dc.start();
if (DBG) dc.log("Made " + dc.getName());
return dc;
@@ -426,12 +491,9 @@ public class DataConnection extends StateMachine {
return new LinkProperties(mLinkProperties);
}
- boolean isInactive() {
- return getCurrentState() == mInactiveState;
- }
-
boolean isDisconnecting() {
- return getCurrentState() == mDisconnectingState;
+ return getCurrentState() == mDisconnectingState
+ || getCurrentState() == mDisconnectingErrorCreatingConnection;
}
@VisibleForTesting
@@ -439,6 +501,11 @@ public class DataConnection extends StateMachine {
return getCurrentState() == mActiveState;
}
+ @VisibleForTesting
+ public boolean isInactive() {
+ return getCurrentState() == mInactiveState;
+ }
+
boolean isActivating() {
return getCurrentState() == mActivatingState;
}
@@ -451,12 +518,33 @@ public class DataConnection extends StateMachine {
return mCid;
}
- ApnSetting getApnSetting() {
+ /**
+ * @return DataConnection's ApnSetting.
+ */
+ public ApnSetting getApnSetting() {
return mApnSetting;
}
- void setLinkPropertiesHttpProxy(ProxyInfo proxy) {
- mLinkProperties.setHttpProxy(proxy);
+ /**
+ * Update http proxy of link properties based on current apn setting
+ */
+ private void updateLinkPropertiesHttpProxy() {
+ if (mApnSetting == null
+ || TextUtils.isEmpty(mApnSetting.getProxyAddressAsString())) {
+ return;
+ }
+ try {
+ int port = mApnSetting.getProxyPort();
+ if (port == -1) {
+ port = 8080;
+ }
+ ProxyInfo proxy = ProxyInfo.buildDirectProxy(
+ mApnSetting.getProxyAddressAsString(), port);
+ mLinkProperties.setHttpProxy(proxy);
+ } catch (NumberFormatException e) {
+ loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("
+ + mApnSetting.getProxyPort() + "): " + e);
+ }
}
public static class UpdateLinkPropertyResult {
@@ -477,7 +565,9 @@ public class DataConnection extends StateMachine {
ERROR_RADIO_NOT_AVAILABLE,
ERROR_INVALID_ARG,
ERROR_STALE,
- ERROR_DATA_SERVICE_SPECIFIC_ERROR;
+ ERROR_DATA_SERVICE_SPECIFIC_ERROR,
+ ERROR_DUPLICATE_CID,
+ ERROR_NO_DEFAULT_CONNECTION;
public int mFailCause;
@@ -487,7 +577,7 @@ public class DataConnection extends StateMachine {
@Override
public String toString() {
- return name() + " SetupResult.mFailCause=" + mFailCause;
+ return name() + " SetupResult.mFailCause=" + DataFailCause.toString(mFailCause);
}
}
@@ -525,6 +615,69 @@ public class DataConnection extends StateMachine {
return ret;
}
+ public int getPduSessionId() {
+ return mPduSessionId;
+ }
+
+ public NetworkSliceInfo getSliceInfo() {
+ return mSliceInfo;
+ }
+
+ public List<TrafficDescriptor> getTrafficDescriptors() {
+ return mTrafficDescriptors;
+ }
+
+ /**
+ * Update DC fields based on a new DataCallResponse
+ * @param response the response to use to update DC fields
+ */
+ public void updateResponseFields(DataCallResponse response) {
+ updateQosParameters(response);
+ updateSliceInfo(response);
+ updateTrafficDescriptors(response);
+ }
+
+ public void updateQosParameters(final @Nullable DataCallResponse response) {
+ if (response == null) {
+ mDefaultQos = null;
+ mQosBearerSessions.clear();
+ return;
+ }
+
+ mDefaultQos = response.getDefaultQos();
+ mQosBearerSessions = response.getQosBearerSessions();
+
+ if (mNetworkAgent != null) {
+ syncQosToNetworkAgent();
+ }
+ }
+
+ private void syncQosToNetworkAgent() {
+ final DcNetworkAgent networkAgent = mNetworkAgent;
+ final List<QosBearerSession> qosBearerSessions = mQosBearerSessions;
+ if (qosBearerSessions == null) {
+ networkAgent.updateQosBearerSessions(new ArrayList<>());
+ return;
+ }
+ networkAgent.updateQosBearerSessions(qosBearerSessions);
+ }
+
+ /**
+ * Update the latest slice info on this data connection with
+ * {@link DataCallResponse#getSliceInfo}.
+ */
+ public void updateSliceInfo(DataCallResponse response) {
+ mSliceInfo = response.getSliceInfo();
+ }
+
+ /**
+ * Update the latest traffic descriptor on this data connection with
+ * {@link DataCallResponse#getTrafficDescriptors}.
+ */
+ public void updateTrafficDescriptors(DataCallResponse response) {
+ mTrafficDescriptors = response.getTrafficDescriptors();
+ }
+
@VisibleForTesting
public UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
@@ -562,6 +715,19 @@ public class DataConnection extends StateMachine {
}
/**
+ * Sets the pdu session id of the data connection
+ * @param pduSessionId pdu session id to set
+ */
+ @VisibleForTesting
+ public void setPduSessionId(int pduSessionId) {
+ if (mPduSessionId != pduSessionId) {
+ logd("Changing pdu session id from: " + mPduSessionId + " to: " + pduSessionId + ", "
+ + "Handover state: " + handoverStateToString(this.mHandoverState));
+ mPduSessionId = pduSessionId;
+ }
+ }
+
+ /**
* Read the MTU value from link properties where it can be set from network. In case
* not set by the network, set it again using the mtu szie value defined in the APN
* database for the connected APN
@@ -593,8 +759,9 @@ public class DataConnection extends StateMachine {
//***** Constructor (NOTE: uses dcc.getHandler() as its Handler)
private DataConnection(Phone phone, String tagSuffix, int id,
DcTracker dct, DataServiceManager dataServiceManager,
- DcTesterFailBringUpAll failBringUpAll, DcController dcc) {
- super("DC-" + tagSuffix, dcc.getHandler());
+ DcTesterFailBringUpAll failBringUpAll, DcController dcc,
+ boolean doAllocatePduSessionId) {
+ super("DC-" + tagSuffix, dcc);
mTagSuffix = tagSuffix;
setLogRecSize(300);
setLogOnlyTransitions(true);
@@ -603,25 +770,20 @@ public class DataConnection extends StateMachine {
mPhone = phone;
mDct = dct;
mDataServiceManager = dataServiceManager;
+ mVcnManager = mPhone.getContext().getSystemService(VcnManager.class);
mTransportType = dataServiceManager.getTransportType();
mDcTesterFailBringUpAll = failBringUpAll;
mDcController = dcc;
mId = id;
mCid = -1;
- ServiceState ss = mPhone.getServiceState();
mDataRegState = mPhone.getServiceState().getDataRegistrationState();
- int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-
- NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
- if (nri != null) {
- networkType = nri.getAccessNetworkTechnology();
- mRilRat = ServiceState.networkTypeToRilRadioTechnology(networkType);
- updateLinkBandwidthsFromCarrierConfig(mRilRat);
- }
+ mIsSuspended = false;
+ mDataCallSessionStats = new DataCallSessionStats(mPhone);
+ mDoAllocatePduSessionId = doAllocatePduSessionId;
- mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
- networkType, NETWORK_TYPE, TelephonyManager.getNetworkTypeName(networkType));
+ int networkType = getNetworkType();
+ mRilRat = ServiceState.networkTypeToRilRadioTechnology(networkType);
+ updateLinkBandwidthsFromCarrierConfig(mRilRat);
addState(mDefaultState);
addState(mInactiveState, mDefaultState);
@@ -632,6 +794,19 @@ public class DataConnection extends StateMachine {
setInitialState(mInactiveState);
}
+ private @NetworkType int getNetworkType() {
+ ServiceState ss = mPhone.getServiceState();
+ int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+ NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
+ if (nri != null) {
+ networkType = nri.getAccessNetworkTechnology();
+ }
+
+ return networkType;
+ }
+
/**
* Get the source transport for handover. For example, handover from WWAN to WLAN, WWAN is the
* source transport, and vice versa.
@@ -643,6 +818,27 @@ public class DataConnection extends StateMachine {
}
/**
+ * API to generate the OsAppId for enterprise traffic category.
+ * @return byte[] representing OsId + length of OsAppId + OsAppId
+ */
+ @VisibleForTesting
+ public static byte[] getEnterpriseOsAppId() {
+ byte[] osAppId = NetworkCapabilities.getCapabilityCarrierName(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE).getBytes();
+ // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId
+ ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length);
+ bb.putLong(OS_ID.getMostSignificantBits());
+ bb.putLong(OS_ID.getLeastSignificantBits());
+ bb.put((byte) osAppId.length);
+ bb.put(osAppId);
+ if (VDBG) {
+ Rlog.d("DataConnection", "getEnterpriseOsAppId: "
+ + IccUtils.bytesToHexString(bb.array()));
+ }
+ return bb.array();
+ }
+
+ /**
* Begin setting up a data connection, calls setupDataCall
* and the ConnectionParams will be returned with the
* EVENT_SETUP_DATA_CONNECTION_DONE
@@ -662,7 +858,7 @@ public class DataConnection extends StateMachine {
if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter > 0) {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause)
- .setSuggestedRetryTime(
+ .setRetryDurationMillis(
mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime)
.setMtuV4(PhoneConstants.UNSET_MTU)
.setMtuV6(PhoneConstants.UNSET_MTU)
@@ -705,6 +901,17 @@ public class DataConnection extends StateMachine {
|| (isModemRoaming && (!mPhone.getServiceState().getDataRoaming()
|| isUnmeteredApnType));
+ String dnn = null;
+ byte[] osAppId = null;
+ if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
+ osAppId = getEnterpriseOsAppId();
+ } else {
+ dnn = mApnSetting.getApnName();
+ }
+ final TrafficDescriptor td = osAppId == null && dnn == null ? null
+ : new TrafficDescriptor(dnn, osAppId);
+ final boolean matchAllRuleAllowed = td == null || td.getOsAppId() == null;
+
if (DBG) {
log("allowRoaming=" + allowRoaming
+ ", mPhone.getDataRoamingEnabled()=" + mPhone.getDataRoamingEnabled()
@@ -712,50 +919,157 @@ public class DataConnection extends StateMachine {
+ ", mPhone.getServiceState().getDataRoaming()="
+ mPhone.getServiceState().getDataRoaming()
+ ", isUnmeteredApnType=" + isUnmeteredApnType
+ + ", trafficDescriptor=" + td
+ + ", matchAllRuleAllowed=" + matchAllRuleAllowed
);
}
// Check if this data setup is a handover.
LinkProperties linkProperties = null;
int reason = DataService.REQUEST_REASON_NORMAL;
- if (cp.mRequestType == DcTracker.REQUEST_TYPE_HANDOVER) {
+ if (cp.mRequestType == REQUEST_TYPE_HANDOVER) {
// If this is a data setup for handover, we need to pass the link properties
// of the existing data connection to the modem.
- DcTracker dcTracker = mPhone.getDcTracker(getHandoverSourceTransport());
- if (dcTracker == null || cp.mApnContext == null) {
- loge("connect: Handover failed. dcTracker=" + dcTracker + ", apnContext="
+ DcTracker srcDcTracker = mPhone.getDcTracker(getHandoverSourceTransport());
+ if (srcDcTracker == null || cp.mApnContext == null) {
+ loge("connect: Handover failed. dcTracker=" + srcDcTracker + ", apnContext="
+ cp.mApnContext);
return DataFailCause.HANDOVER_FAILED;
}
- DataConnection dc = dcTracker.getDataConnectionByApnType(cp.mApnContext.getApnType());
- if (dc == null) {
+
+ // srcDc is the source data connection while the current instance is the target
+ DataConnection srcDc =
+ srcDcTracker.getDataConnectionByApnType(cp.mApnContext.getApnType());
+ if (srcDc == null) {
loge("connect: Can't find data connection for handover.");
return DataFailCause.HANDOVER_FAILED;
}
- // Preserve the potential network agent from the source data connection. The ownership
- // is not transferred at this moment.
- mHandoverSourceNetworkAgent = dc.getNetworkAgent();
- if (mHandoverSourceNetworkAgent == null) {
- loge("Cannot get network agent from the source dc " + dc.getName());
- return DataFailCause.HANDOVER_FAILED;
- }
+ // Helpful for logging purposes
+ DataServiceManager srcDsm = srcDc.mDataServiceManager;
+ String srcDsmTag = (srcDsm == null ? "(null)" : srcDsm.getTag());
+ logd("connect: REQUEST_TYPE_HANDOVER - Request handover from " + srcDc.getName()
+ + ", targetDsm=" + mDataServiceManager.getTag()
+ + ", sourceDsm=" + srcDsmTag);
+
+
+ /* startHandover is called on the source data connection, and if successful,
+ we ask the target data connection (which is the current instance) to call
+ #setupDataCall with request type handover.
+ */
+ Consumer<Integer> onCompleted = (dataServiceCallbackResultCode) ->
+ /* startHandover is called on the srcDc handler, but the callback needs to
+ be called on the current (which is the targetDc) handler which is why we
+ call sendRunnableMessage. */
+ sendRunnableMessage(EVENT_START_HANDOVER_ON_TARGET,
+ (inCorrectState) -> requestHandover(inCorrectState, srcDc,
+ dataServiceCallbackResultCode,
+ cp, msg, dp, isModemRoaming, allowRoaming));
+ srcDc.startHandover(onCompleted);
+ return DataFailCause.NONE;
+ }
- linkProperties = dc.getLinkProperties();
- if (linkProperties == null || linkProperties.getLinkAddresses().isEmpty()) {
- loge("connect: Can't find link properties of handover data connection. dc="
- + dc);
- return DataFailCause.HANDOVER_FAILED;
+ // setup data call for REQUEST_TYPE_NORMAL
+ allocatePduSessionId(psi -> {
+ this.setPduSessionId(psi);
+ mDataServiceManager.setupDataCall(
+ ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat),
+ dp,
+ isModemRoaming,
+ allowRoaming,
+ reason,
+ linkProperties,
+ psi,
+ null, //slice info is null since this is not a handover
+ td,
+ matchAllRuleAllowed,
+ msg);
+ TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
+ dp.getProfileId(), dp.getApn(), dp.getProtocolType());
+ });
+ return DataFailCause.NONE;
+ }
+
+ private void allocatePduSessionId(Consumer<Integer> allocateCallback) {
+ if (getDoAllocatePduSessionId()) {
+ Message msg = this.obtainMessage(EVENT_ALLOCATE_PDU_SESSION_ID);
+ msg.obj = allocateCallback;
+ mPhone.mCi.allocatePduSessionId(msg);
+ } else {
+ allocateCallback.accept(PDU_SESSION_ID_NOT_SET);
+ }
+ }
+
+ private void onRquestHandoverFailed(ConnectionParams cp) {
+ sendMessage(obtainMessage(EVENT_CANCEL_HANDOVER));
+ notifyConnectCompleted(cp, DataFailCause.UNKNOWN,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
+ }
+
+ private void requestHandover(boolean inCorrectState, DataConnection srcDc,
+ @DataServiceCallback.ResultCode int resultCode,
+ ConnectionParams cp, Message msg, DataProfile dp, boolean isModemRoaming,
+ boolean allowRoaming) {
+
+ if (!inCorrectState) {
+ logd("requestHandover: Not in correct state");
+ if (isResultCodeSuccess(resultCode)) {
+ if (srcDc != null) {
+ logd("requestHandover: Not in correct state - Success result code");
+ // We need to cancel the handover on source if we ended up in the wrong state.
+ srcDc.cancelHandover();
+ } else {
+ logd("requestHandover: Not in correct state - Success result code - "
+ + "srcdc = null");
+ }
+ }
+ onRquestHandoverFailed(cp);
+ return;
+ } else if (!isResultCodeSuccess(resultCode)) {
+ if (DBG) {
+ logd("requestHandover: Non success result code from DataService, "
+ + "setupDataCall will not be called, result code = "
+ + DataServiceCallback.resultCodeToString(resultCode));
}
+ onRquestHandoverFailed(cp);
+ return;
+ }
+
+ if (srcDc == null) {
+ return;
+ }
+
+ LinkProperties linkProperties;
+ int reason;
- mHandoverLocalLog.log("Handover started. Preserved the agent.");
- log("Get the handover source network agent: " + mHandoverSourceNetworkAgent);
+ // Preserve the potential network agent from the source data connection. The ownership
+ // is not transferred at this moment.
+ mHandoverSourceNetworkAgent = srcDc.getNetworkAgent();
+ if (mHandoverSourceNetworkAgent == null) {
+ loge("requestHandover: Cannot get network agent from the source dc " + srcDc.getName());
+ notifyConnectCompleted(cp, DataFailCause.UNKNOWN,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
+ return;
+ }
- dc.setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED);
- reason = DataService.REQUEST_REASON_HANDOVER;
+ linkProperties = srcDc.getLinkProperties();
+ if (linkProperties == null || linkProperties.getLinkAddresses().isEmpty()) {
+ loge("requestHandover: Can't find link properties of handover data connection. dc="
+ + srcDc);
+ onRquestHandoverFailed(cp);
+ return;
}
+ mHandoverLocalLog.log("Handover started. Preserved the agent.");
+ log("Get the handover source network agent: " + mHandoverSourceNetworkAgent);
+
+ reason = DataService.REQUEST_REASON_HANDOVER;
+
+ TrafficDescriptor td = dp.getApn() == null ? null
+ : new TrafficDescriptor(dp.getApn(), null);
+ boolean matchAllRuleAllowed = true;
+
mDataServiceManager.setupDataCall(
ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat),
dp,
@@ -763,20 +1077,66 @@ public class DataConnection extends StateMachine {
allowRoaming,
reason,
linkProperties,
+ srcDc.getPduSessionId(),
+ srcDc.getSliceInfo(),
+ td,
+ matchAllRuleAllowed,
msg);
TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
dp.getProfileId(), dp.getApn(), dp.getProtocolType());
- return DataFailCause.NONE;
}
- public void onSubscriptionOverride(int overrideMask, int overrideValue) {
- mSubscriptionOverride = (mSubscriptionOverride & ~overrideMask)
- | (overrideValue & overrideMask);
- sendMessage(obtainMessage(EVENT_DATA_CONNECTION_OVERRIDE_CHANGED));
+ /**
+ * Called on the source data connection from the target data connection.
+ */
+ private void startHandover(Consumer<Integer> onTargetDcComplete) {
+ logd("startHandover: " + toStringSimple());
+ // Set the handover state to being transferred on "this" data connection which is the src.
+ setHandoverState(HANDOVER_STATE_BEING_TRANSFERRED);
+
+ Consumer<Integer> onSrcDcComplete =
+ resultCode -> onHandoverStarted(resultCode, onTargetDcComplete);
+ /*
+ The flow here is:
+ srcDc#startHandover -> dataService#startHandover -> (onHandoverStarted) ->
+ onSrcDcComplete -> onTargetDcComplete
+ */
+ mDataServiceManager.startHandover(mCid,
+ this.obtainMessage(EVENT_START_HANDOVER,
+ onSrcDcComplete));
+ }
+
+ /**
+ * Called on the source data connection when the async call to start handover is complete
+ */
+ private void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode,
+ Consumer<Integer> onTargetDcComplete) {
+ logd("onHandoverStarted: " + toStringSimple());
+ if (!isResultCodeSuccess(resultCode)) {
+ setHandoverState(HANDOVER_STATE_IDLE);
+ }
+ onTargetDcComplete.accept(resultCode);
+ }
+
+ private void cancelHandover() {
+ if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED) {
+ logd("cancelHandover: handover state is " + handoverStateToString(mHandoverState)
+ + ", expecting HANDOVER_STATE_BEING_TRANSFERRED");
+ }
+ mDataServiceManager.cancelHandover(mCid, this.obtainMessage(EVENT_CANCEL_HANDOVER));
+ setHandoverState(HANDOVER_STATE_IDLE);
+ }
+
+ /**
+ * Update NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED based on congested override
+ * @param isCongested whether this DC should be set to congested or not
+ */
+ public void onCongestednessChanged(boolean isCongested) {
+ sendMessage(obtainMessage(EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED, isCongested));
}
/**
- * Update NetworkCapabilities.NET_CAPABILITY_NOT_METERED based on meteredness
+ * Update NetworkCapabilities.NET_CAPABILITY_NOT_METERED based on metered override
* @param isUnmetered whether this DC should be set to unmetered or not
*/
public void onMeterednessChanged(boolean isUnmetered) {
@@ -806,19 +1166,41 @@ public class DataConnection extends StateMachine {
String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
if (DBG) log(str);
if (apnContext != null) apnContext.requestLog(str);
- mDataServiceManager.deactivateDataCall(mCid, discReason,
- obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
+
+
+ //Needed to be final to work in a closure
+ final int fDiscReason = discReason;
+ releasePduSessionId(() -> {
+ // This is run after release pdu session id is complete
+ this.setPduSessionId(PDU_SESSION_ID_NOT_SET);
+ mDataServiceManager.deactivateDataCall(mCid, fDiscReason,
+ obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
+ mDataCallSessionStats.setDeactivateDataCallReason(fDiscReason);
+ });
+ }
+
+ private void releasePduSessionId(Runnable releaseCallback) {
+ // If we are not in the middle of a handover and have a real pdu session id, then we release
+ if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED
+ && this.getPduSessionId() != PDU_SESSION_ID_NOT_SET) {
+ Message msg = this.obtainMessage(EVENT_RELEASE_PDU_SESSION_ID);
+ msg.obj = releaseCallback;
+ mPhone.mCi.releasePduSessionId(msg, this.getPduSessionId());
+ } else {
+ // Just go and run the callback since we either have no pdu session id to release
+ // or we are in the middle of a handover
+ releaseCallback.run();
+ }
}
private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
- mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
- mNetworkInfo.getExtraInfo());
for (ConnectionParams cp : mApnContexts.values()) {
ApnContext apnContext = cp.mApnContext;
if (apnContext == alreadySent) continue;
if (reason != null) apnContext.setReason(reason);
Pair<ApnContext, Integer> pair = new Pair<>(apnContext, cp.mConnectionGeneration);
- Message msg = mDct.obtainMessage(event, mCid, cp.mRequestType, pair);
+ Message msg = mDct.obtainMessage(event, cp.mRequestType,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, pair);
AsyncResult.forMessage(msg);
msg.sendToTarget();
}
@@ -829,10 +1211,11 @@ public class DataConnection extends StateMachine {
*
* @param cp is the ConnectionParams
* @param cause and if no error the cause is DataFailCause.NONE
+ * @param handoverFailureMode The action on handover failure
* @param sendAll is true if all contexts are to be notified
*/
private void notifyConnectCompleted(ConnectionParams cp, @DataFailureCause int cause,
- boolean sendAll) {
+ @HandoverFailureMode int handoverFailureMode, boolean sendAll) {
ApnContext alreadySent = null;
if (cp != null && cp.mOnCompletedMsg != null) {
@@ -842,8 +1225,8 @@ public class DataConnection extends StateMachine {
alreadySent = cp.mApnContext;
long timeStamp = System.currentTimeMillis();
- connectionCompletedMsg.arg1 = mCid;
- connectionCompletedMsg.arg2 = cp.mRequestType;
+ connectionCompletedMsg.arg1 = cp.mRequestType;
+ connectionCompletedMsg.arg2 = handoverFailureMode;
if (cause == DataFailCause.NONE) {
mCreateTime = timeStamp;
@@ -858,8 +1241,9 @@ public class DataConnection extends StateMachine {
new Throwable(DataFailCause.toString(cause)));
}
if (DBG) {
- log("notifyConnectCompleted at " + timeStamp + " cause=" + cause
- + " connectionCompletedMsg=" + msgToString(connectionCompletedMsg));
+ log("notifyConnectCompleted at " + timeStamp + " cause="
+ + DataFailCause.toString(cause) + " connectionCompletedMsg="
+ + msgToString(connectionCompletedMsg));
}
connectionCompletedMsg.sendToTarget();
@@ -906,6 +1290,10 @@ public class DataConnection extends StateMachine {
if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
}
+ private void sendRunnableMessage(int eventCode, @NonNull final Consumer<Boolean> r) {
+ sendMessage(eventCode, r);
+ }
+
/*
* **************************************************************************
* Begin Members and methods owned by DataConnectionTracker but stored
@@ -948,14 +1336,23 @@ public class DataConnection extends StateMachine {
mApnContexts.clear();
mApnSetting = null;
mUnmeteredUseOnly = false;
+ mMmsUseOnly = false;
+ mEnterpriseUse = false;
mRestrictedNetworkOverride = false;
mDcFailCause = DataFailCause.NONE;
mDisabledApnTypeBitMask = 0;
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- mSubscriptionOverride = 0;
+ mCongestedOverride = false;
mUnmeteredOverride = false;
mDownlinkBandwidth = 14;
mUplinkBandwidth = 14;
+ mIsSuspended = false;
+ mHandoverState = HANDOVER_STATE_IDLE;
+ mHandoverFailureMode = DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN;
+ mSliceInfo = null;
+ mDefaultQos = null;
+ mQosBearerSessions.clear();
+ mTrafficDescriptors.clear();
}
/**
@@ -980,6 +1377,12 @@ public class DataConnection extends StateMachine {
} else if (resultCode == DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE) {
result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
result.mFailCause = DataFailCause.RADIO_NOT_AVAILABLE;
+ } else if (resultCode == DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE) {
+ result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
+ result.mFailCause = DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE;
+ } else if (resultCode == DataServiceCallback.RESULT_ERROR_INVALID_ARG) {
+ result = SetupResult.ERROR_INVALID_ARG;
+ result.mFailCause = DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER;
} else if (response.getCause() != 0) {
if (response.getCause() == DataFailCause.RADIO_NOT_AVAILABLE) {
result = SetupResult.ERROR_RADIO_NOT_AVAILABLE;
@@ -988,19 +1391,44 @@ public class DataConnection extends StateMachine {
result = SetupResult.ERROR_DATA_SERVICE_SPECIFIC_ERROR;
result.mFailCause = DataFailCause.getFailCause(response.getCause());
}
+ } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
+ && mDcController.getActiveDcByCid(response.getId()) != null) {
+ if (DBG) log("DataConnection already exists for cid: " + response.getId());
+ result = SetupResult.ERROR_DUPLICATE_CID;
+ result.mFailCause = DataFailCause.DUPLICATE_CID;
+ } else if (cp.mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE
+ && !mDcController.isDefaultDataActive()) {
+ if (DBG) log("No default data connection currently active");
+ mCid = response.getId();
+ result = SetupResult.ERROR_NO_DEFAULT_CONNECTION;
+ result.mFailCause = DataFailCause.NO_DEFAULT_DATA;
} else {
if (DBG) log("onSetupConnectionCompleted received successful DataCallResponse");
mCid = response.getId();
- mPcscfAddr = response.getPcscfAddresses().stream()
- .map(InetAddress::getHostAddress).toArray(String[]::new);
+ if (response.getPduSessionId() != getPduSessionId()) {
+ if (getDoAllocatePduSessionId()) {
+ loge("The pdu session id on DataCallResponse is different than the one "
+ + "allocated. response psi=" + response.getPduSessionId()
+ + ", allocated psi=" + getPduSessionId());
+ } else {
+ setPduSessionId(response.getPduSessionId());
+ }
+ }
+ updatePcscfAddr(response);
+ updateResponseFields(response);
result = updateLinkProperty(response).setupResult;
}
return result;
}
+ private static boolean isResultCodeSuccess(int resultCode) {
+ return resultCode == DataServiceCallback.RESULT_SUCCESS
+ || resultCode == DataServiceCallback.RESULT_ERROR_UNSUPPORTED;
+ }
+
private boolean isDnsOk(String[] domainNameServers) {
if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
&& !mPhone.isDnsCheckDisabled()) {
@@ -1012,7 +1440,7 @@ public class DataConnection extends StateMachine {
if (!isIpAddress(mApnSetting.getMmsProxyAddressAsString())) {
log(String.format(
"isDnsOk: return false apn.types=%d APN_TYPE_MMS=%s isIpAddress(%s)=%s",
- mApnSetting.getApnTypeBitmask(), PhoneConstants.APN_TYPE_MMS,
+ mApnSetting.getApnTypeBitmask(), ApnSetting.TYPE_MMS_STRING,
mApnSetting.getMmsProxyAddressAsString(),
isIpAddress(mApnSetting.getMmsProxyAddressAsString())));
return false;
@@ -1046,7 +1474,8 @@ public class DataConnection extends StateMachine {
private void updateTcpBufferSizes(int rilRat) {
String sizes = null;
ServiceState ss = mPhone.getServiceState();
- if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && ss.isUsingCarrierAggregation()) {
+ if (rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE &&
+ ss.isUsingCarrierAggregation()) {
rilRat = ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA;
}
String ratName = ServiceState.rilRadioTechnologyToString(rilRat).toLowerCase(Locale.ROOT);
@@ -1061,8 +1490,8 @@ public class DataConnection extends StateMachine {
// NR 5G Non-Standalone use LTE cell as the primary cell, the ril technology is LTE in this
// case. We use NR 5G TCP buffer size when connected to NR 5G Non-Standalone network.
if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
- && ((rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE
- || rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA) && isNRConnected())
+ && ((rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE ||
+ rilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA) && isNRConnected())
&& mPhone.getServiceStateTracker().getNrContextIds().contains(mCid)) {
ratName = RAT_NAME_5G;
}
@@ -1161,44 +1590,76 @@ public class DataConnection extends StateMachine {
}
- private void updateLinkBandwidthsFromModem(LinkCapacityEstimate lce) {
- if (DBG) log("updateLinkBandwidthsFromModem: lce=" + lce);
+ private void updateLinkBandwidthsFromModem(List<LinkCapacityEstimate> lceList) {
+ if (DBG) log("updateLinkBandwidthsFromModem: lceList=" + lceList);
boolean downlinkUpdated = false;
boolean uplinkUpdated = false;
+ LinkCapacityEstimate lce = lceList.get(0);
// LCE status deprecated in IRadio 1.2, so only check for IRadio < 1.2
if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_2)
|| mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
- if (lce.downlinkCapacityKbps != LinkCapacityEstimate.INVALID) {
- mDownlinkBandwidth = lce.downlinkCapacityKbps;
+ if (lce.getDownlinkCapacityKbps() != LinkCapacityEstimate.INVALID) {
+ mDownlinkBandwidth = lce.getDownlinkCapacityKbps();
downlinkUpdated = true;
}
- if (lce.uplinkCapacityKbps != LinkCapacityEstimate.INVALID) {
- mUplinkBandwidth = lce.uplinkCapacityKbps;
+ if (lce.getUplinkCapacityKbps() != LinkCapacityEstimate.INVALID) {
+ mUplinkBandwidth = lce.getUplinkCapacityKbps();
uplinkUpdated = true;
}
}
+
if (!downlinkUpdated || !uplinkUpdated) {
- String ratName = ServiceState.rilRadioTechnologyToString(mRilRat);
- if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) {
- ratName = mPhone.getServiceState().getNrFrequencyRange()
- == ServiceState.FREQUENCY_RANGE_MMWAVE
- ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA;
- }
- Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
- if (values != null) {
- if (!downlinkUpdated) {
- mDownlinkBandwidth = values.first;
- }
- if (!uplinkUpdated) {
- mUplinkBandwidth = values.second;
- }
- }
+ fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
+ }
+
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
+ }
+ }
+
+ private void updateLinkBandwidthsFromBandwidthEstimator(int uplinkBandwidthKbps,
+ int downlinkBandwidthKbps) {
+ if (DBG) {
+ log("updateLinkBandwidthsFromBandwidthEstimator, UL= "
+ + uplinkBandwidthKbps + " DL= " + downlinkBandwidthKbps);
+ }
+ boolean downlinkUpdated = false;
+ boolean uplinkUpdated = false;
+ if (downlinkBandwidthKbps > 0) {
+ mDownlinkBandwidth = downlinkBandwidthKbps;
+ downlinkUpdated = true;
+ }
+ if (uplinkBandwidthKbps > 0) {
+ mUplinkBandwidth = uplinkBandwidthKbps;
+ uplinkUpdated = true;
+ }
+
+ if (!downlinkUpdated || !uplinkUpdated) {
+ fallBackToCarrierConfigValues(downlinkUpdated, uplinkUpdated);
}
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(), DataConnection.this);
}
}
+ private void fallBackToCarrierConfigValues(boolean downlinkUpdated, boolean uplinkUpdated) {
+ String ratName = ServiceState.rilRadioTechnologyToString(mRilRat);
+ if (mRilRat == ServiceState.RIL_RADIO_TECHNOLOGY_LTE && isNRConnected()) {
+ ratName = mPhone.getServiceState().getNrFrequencyRange()
+ == ServiceState.FREQUENCY_RANGE_MMWAVE
+ ? DctConstants.RAT_NAME_NR_NSA_MMWAVE : DctConstants.RAT_NAME_NR_NSA;
+ }
+ Pair<Integer, Integer> values = mDct.getLinkBandwidthsFromCarrierConfig(ratName);
+ if (values != null) {
+ if (!downlinkUpdated) {
+ mDownlinkBandwidth = values.first;
+ }
+ if (!uplinkUpdated) {
+ mUplinkBandwidth = values.second;
+ }
+ }
+ }
+
private boolean isBandwidthSourceKey(String source) {
return source.equals(mPhone.getContext().getResources().getString(
com.android.internal.R.string.config_bandwidthEstimateSource));
@@ -1214,6 +1675,14 @@ public class DataConnection extends StateMachine {
private boolean mUnmeteredUseOnly = false;
/**
+ * Indicates if this data connection was established for MMS use only. This is true only when
+ * mobile data is disabled but the user allows sending and receiving MMS messages. If the data
+ * enabled settings indicate that MMS data is allowed unconditionally, MMS can be sent when data
+ * is disabled even if it is a metered APN type.
+ */
+ private boolean mMmsUseOnly = false;
+
+ /**
* Indicates if when this connection was established we had a restricted/privileged
* NetworkRequest and needed it to overcome data-enabled limitations.
*
@@ -1235,13 +1704,20 @@ public class DataConnection extends StateMachine {
private boolean mRestrictedNetworkOverride = false;
/**
+ * Indicates if this data connection supports enterprise use. Note that this flag should be
+ * populated when data becomes active. Once it is set, the value cannot be changed because
+ * setting it will cause this data connection to lose immutable network capabilities, which can
+ * cause issues in connectivity service.
+ */
+ private boolean mEnterpriseUse = false;
+
+ /**
* Check if this data connection should be restricted. We should call this when data connection
* becomes active, or when we want to re-evaluate the conditions to decide if we need to
* unstrict the data connection.
*
* @return True if this data connection needs to be restricted.
*/
-
private boolean shouldRestrictNetwork() {
// first, check if there is any network request that containing restricted capability
// (i.e. Do not have NET_CAPABILITY_NOT_RESTRICTED in the request)
@@ -1312,135 +1788,202 @@ public class DataConnection extends StateMachine {
}
/**
- * Get the network capabilities for this data connection.
+ * @return True if this data connection should only be used for MMS purposes.
+ */
+ private boolean isMmsUseOnly() {
+ // MMS use only if data is disabled, MMS is allowed unconditionally, and MMS is the only
+ // APN type for this data connection.
+ DataEnabledSettings des = mPhone.getDataEnabledSettings();
+ boolean mmsAllowedUnconditionally = !des.isDataEnabled() && des.isMmsAlwaysAllowed();
+ boolean mmsApnOnly = isApnContextAttached(ApnSetting.TYPE_MMS, true);
+ return mmsAllowedUnconditionally && mmsApnOnly;
+ }
+
+ /**
+ * Check if this data connection supports enterprise use. We call this when the data connection
+ * becomes active or when we want to reevaluate the conditions to decide if we need to update
+ * the network agent capabilities.
*
- * Note that this method reads fields from mNetworkInfo, so its output is only as fresh
- * as mNetworkInfo. Call updateNetworkInfoSuspendState before calling this.
+ * @return True if this data connection supports enterprise use.
+ */
+ private boolean isEnterpriseUse() {
+ boolean enterpriseTrafficDescriptor = mTrafficDescriptors
+ .stream()
+ .anyMatch(td -> td.getOsAppId() != null && Arrays.equals(td.getOsAppId(),
+ getEnterpriseOsAppId()));
+ boolean enterpriseApnContext = mApnContexts.keySet()
+ .stream()
+ .anyMatch(ac -> ac.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE);
+ return enterpriseTrafficDescriptor || enterpriseApnContext;
+ }
+
+ /**
+ * Get the network capabilities for this data connection.
*
* @return the {@link NetworkCapabilities} of this data connection.
*/
public NetworkCapabilities getNetworkCapabilities() {
- NetworkCapabilities result = new NetworkCapabilities();
- result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-
- if (mApnSetting != null) {
- final String[] types = ApnSetting.getApnTypesStringFromBitmask(
- mApnSetting.getApnTypeBitmask() & ~mDisabledApnTypeBitMask).split(",");
- for (String type : types) {
- if (!mRestrictedNetworkOverride && mUnmeteredUseOnly
- && ApnSettingUtils.isMeteredApnType(
- ApnSetting.getApnTypesBitmaskFromString(type), mPhone)) {
- log("Dropped the metered " + type + " for the unmetered data call.");
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ boolean unmeteredApns = false;
+
+ if (mApnSetting != null && !mEnterpriseUse && !mMmsUseOnly) {
+ final int[] types = ApnSetting.getApnTypesFromBitmask(
+ mApnSetting.getApnTypeBitmask() & ~mDisabledApnTypeBitMask);
+ for (int type : types) {
+ if ((!mRestrictedNetworkOverride && mUnmeteredUseOnly)
+ && ApnSettingUtils.isMeteredApnType(type, mPhone)) {
+ log("Dropped the metered " + ApnSetting.getApnTypeString(type)
+ + " type for the unmetered data call.");
continue;
}
switch (type) {
- case PhoneConstants.APN_TYPE_ALL: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+ case ApnSetting.TYPE_ALL: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
break;
}
- case PhoneConstants.APN_TYPE_DEFAULT: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ case ApnSetting.TYPE_DEFAULT: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
break;
}
- case PhoneConstants.APN_TYPE_MMS: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
+ case ApnSetting.TYPE_MMS: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
break;
}
- case PhoneConstants.APN_TYPE_SUPL: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ case ApnSetting.TYPE_SUPL: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
break;
}
- case PhoneConstants.APN_TYPE_DUN: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+ case ApnSetting.TYPE_DUN: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
break;
}
- case PhoneConstants.APN_TYPE_FOTA: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
+ case ApnSetting.TYPE_FOTA: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
break;
}
- case PhoneConstants.APN_TYPE_IMS: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+ case ApnSetting.TYPE_IMS: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
break;
}
- case PhoneConstants.APN_TYPE_CBS: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
+ case ApnSetting.TYPE_CBS: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
break;
}
- case PhoneConstants.APN_TYPE_IA: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
+ case ApnSetting.TYPE_IA: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
break;
}
- case PhoneConstants.APN_TYPE_EMERGENCY: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
+ case ApnSetting.TYPE_EMERGENCY: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
break;
}
- case PhoneConstants.APN_TYPE_MCX: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_MCX);
+ case ApnSetting.TYPE_MCX: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MCX);
break;
}
- case PhoneConstants.APN_TYPE_XCAP: {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
+ case ApnSetting.TYPE_XCAP: {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
break;
}
default:
}
}
- // DataConnection has the immutable NOT_METERED capability only if all APNs in the
- // APN setting are unmetered according to carrier config METERED_APN_TYPES_STRINGS.
- // All other cases should use the dynamic TEMPORARILY_NOT_METERED capability instead.
- result.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED,
- !ApnSettingUtils.isMetered(mApnSetting, mPhone));
+ if (!ApnSettingUtils.isMetered(mApnSetting, mPhone)) {
+ unmeteredApns = true;
+ }
+ }
+
+ // Mark NOT_METERED in the following cases:
+ // 1. All APNs in the APN settings are unmetered.
+ // 2. The non-restricted data is intended for unmetered use only.
+ if (unmeteredApns || (mUnmeteredUseOnly && !mRestrictedNetworkOverride)) {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ } else {
+ builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ }
+
+ if (mEnterpriseUse) {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+
+ if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())) {
+ builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ }
- if (result.deduceRestrictedCapability()) {
- result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ if (mMmsUseOnly) {
+ if (ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone)) {
+ log("Adding unmetered capability for the unmetered MMS-only data connection");
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}
+ log("Adding MMS capability for the MMS-only data connection");
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
}
if (mRestrictedNetworkOverride) {
- result.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
// don't use dun on restriction-overriden networks.
- result.removeCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+ builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
}
- result.setLinkDownstreamBandwidthKbps(mDownlinkBandwidth);
- result.setLinkUpstreamBandwidthKbps(mUplinkBandwidth);
+ builder.setLinkDownstreamBandwidthKbps(mDownlinkBandwidth);
+ builder.setLinkUpstreamBandwidthKbps(mUplinkBandwidth);
- result.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ builder.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(mSubId).build());
+ builder.setSubscriptionIds(Collections.singleton(mSubId));
- result.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING,
- !mPhone.getServiceState().getDataRoaming());
+ if (!mPhone.getServiceState().getDataRoaming()) {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+ }
- if ((mSubscriptionOverride & SUBSCRIPTION_OVERRIDE_CONGESTED) == 0) {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
+ if (!mCongestedOverride) {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED);
}
- // Mark TEMPORARILY_NOT_METERED in the following cases:
- // 1. The non-restricted data is intended for unmetered use only.
- // 2. DcTracker set an unmetered override due to network/location (eg. 5G).
- // 3. SubscriptionManager set an unmetered override as requested by policy.
- if ((mUnmeteredUseOnly && !mRestrictedNetworkOverride) || mUnmeteredOverride
- || (mSubscriptionOverride & SUBSCRIPTION_OVERRIDE_UNMETERED) != 0) {
- result.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
- } else {
- result.removeCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ if (mUnmeteredOverride) {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
}
- final boolean suspended =
- mNetworkInfo.getDetailedState() == NetworkInfo.DetailedState.SUSPENDED;
- result.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED, !suspended);
+ if (!mIsSuspended) {
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
+ }
- result.setAdministratorUids(mAdministratorUids);
+ builder.setAdministratorUids(mAdministratorUids);
- return result;
+ // Always start with NOT_VCN_MANAGED, then remove if VcnManager indicates this is part of a
+ // VCN.
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ if (isVcnManaged(builder.build())) {
+ builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Returns whether the Network represented by this DataConnection is VCN-managed.
+ *
+ * <p>Determining if the Network is VCN-managed requires polling VcnManager.
+ */
+ private boolean isVcnManaged(NetworkCapabilities networkCapabilities) {
+ VcnNetworkPolicyResult policyResult =
+ mVcnManager.applyVcnNetworkPolicy(networkCapabilities, getLinkProperties());
+
+ // if the Network does have capability NOT_VCN_MANAGED, return false to indicate it's not
+ // VCN-managed
+ return !policyResult
+ .getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
/** @return {@code true} if validation is required, {@code false} otherwise. */
@@ -1556,9 +2099,31 @@ public class DataConnection extends StateMachine {
}
}
+ boolean useLowerMtuValue = false;
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle bundle = configManager.getConfigForSubId(mSubId);
+ if (bundle != null) {
+ useLowerMtuValue = bundle.getBoolean(
+ CarrierConfigManager.KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED)
+ && response.getMtuV4() != PhoneConstants.UNSET_MTU
+ && response.getMtuV6() != PhoneConstants.UNSET_MTU;
+ }
+ }
+
+ int interfaceMtu = response.getMtu();
for (InetAddress gateway : response.getGatewayAddresses()) {
int mtu = gateway instanceof java.net.Inet6Address ? response.getMtuV6()
: response.getMtuV4();
+ if (useLowerMtuValue) {
+ mtu = Math.min(response.getMtuV4(), response.getMtuV6());
+ // Never set an MTU below MIN_V6_MTU on a network that has IPv6.
+ if (mtu < MIN_V6_MTU) {
+ mtu = MIN_V6_MTU;
+ }
+ interfaceMtu = mtu;
+ }
// Allow 0.0.0.0 or :: as a gateway;
// this indicates a point-to-point interface.
linkProperties.addRoute(new RouteInfo(null, gateway, null,
@@ -1568,7 +2133,7 @@ public class DataConnection extends StateMachine {
// set interface MTU
// this may clobber the setting read from the APN db, but that's ok
// TODO: remove once LinkProperties#setMtu is deprecated
- linkProperties.setMtu(response.getMtu());
+ linkProperties.setMtu(interfaceMtu);
result = SetupResult.SUCCESS;
} catch (UnknownHostException e) {
@@ -1591,6 +2156,10 @@ public class DataConnection extends StateMachine {
return result;
}
+ private boolean getDoAllocatePduSessionId() {
+ return mDoAllocatePduSessionId;
+ }
+
/**
* Initialize connection, this will fail if the
* apnSettings are not compatible.
@@ -1605,7 +2174,8 @@ public class DataConnection extends StateMachine {
// only NOT be set only if we're in DcInactiveState.
mApnSetting = apnContext.getApnSetting();
}
- if (mApnSetting == null || !mApnSetting.canHandleType(apnContext.getApnTypeBitmask())) {
+ if (mApnSetting == null || (!mApnSetting.canHandleType(apnContext.getApnTypeBitmask())
+ && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE)) {
if (DBG) {
log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
+ " dc=" + DataConnection.this);
@@ -1650,6 +2220,12 @@ public class DataConnection extends StateMachine {
DataConnection.EVENT_NR_STATE_CHANGED, null);
mPhone.getServiceStateTracker().registerForNrFrequencyChanged(getHandler(),
DataConnection.EVENT_NR_FREQUENCY_CHANGED, null);
+ mPhone.getServiceStateTracker().registerForCssIndicatorChanged(getHandler(),
+ DataConnection.EVENT_CSS_INDICATOR_CHANGED, null);
+ if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
+ mPhone.getLinkBandwidthEstimator().registerForBandwidthChanged(getHandler(),
+ DataConnection.EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE, null);
+ }
// Add ourselves to the list of data connections
mDcController.addDc(DataConnection.this);
@@ -1666,6 +2242,10 @@ public class DataConnection extends StateMachine {
mPhone.getServiceStateTracker().unregisterForDataRoamingOff(getHandler());
mPhone.getServiceStateTracker().unregisterForNrStateChanged(getHandler());
mPhone.getServiceStateTracker().unregisterForNrFrequencyChanged(getHandler());
+ mPhone.getServiceStateTracker().unregisterForCssIndicatorChanged(getHandler());
+ if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
+ mPhone.getLinkBandwidthEstimator().unregisterForBandwidthChanged(getHandler());
+ }
// Remove ourselves from the DC lists
mDcController.removeDc(DataConnection.this);
@@ -1703,7 +2283,8 @@ public class DataConnection extends StateMachine {
case EVENT_CONNECT:
if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
ConnectionParams cp = (ConnectionParams) msg.obj;
- notifyConnectCompleted(cp, DataFailCause.UNKNOWN, false);
+ notifyConnectCompleted(cp, DataFailCause.UNKNOWN,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
break;
case EVENT_DISCONNECT:
@@ -1719,6 +2300,8 @@ public class DataConnection extends StateMachine {
if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,
null);
+ mDataCallSessionStats.setDeactivateDataCallReason(
+ DataService.REQUEST_REASON_NORMAL);
break;
case EVENT_LOST_CONNECTION:
if (DBG) {
@@ -1738,11 +2321,74 @@ public class DataConnection extends StateMachine {
mRilRat = drsRatPair.second;
if (DBG) {
log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
- + " drs=" + mDataRegState
- + " mRilRat=" + mRilRat);
+ + " regState=" + ServiceState.rilServiceStateToString(mDataRegState)
+ + " RAT=" + ServiceState.rilRadioTechnologyToString(mRilRat));
+ }
+ mDataCallSessionStats.onDrsOrRatChanged(mRilRat);
+ break;
+
+ case EVENT_START_HANDOVER: //calls startHandover()
+ if (DBG) {
+ log("DcDefaultState: EVENT_START_HANDOVER not expected.");
+ }
+ Consumer<Integer> r = (Consumer<Integer>) msg.obj;
+ r.accept(DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ break;
+ case EVENT_START_HANDOVER_ON_TARGET:
+ if (DBG) {
+ log("DcDefaultState: EVENT_START_HANDOVER not expected, but will "
+ + "clean up, result code: "
+ + DataServiceCallback.resultCodeToString(msg.arg1));
+ }
+ ((Consumer<Boolean>) msg.obj).accept(false /* is in correct state*/);
+ break;
+ case EVENT_CANCEL_HANDOVER:
+ // We don't need to do anything in this case
+ if (DBG) {
+ log("DcDefaultState: EVENT_CANCEL_HANDOVER resultCode="
+ + DataServiceCallback.resultCodeToString(msg.arg1));
+ }
+ break;
+ case EVENT_RELEASE_PDU_SESSION_ID: {
+ // We do the same thing in all state in order to preserve the existing workflow
+ final AsyncResult asyncResult = (AsyncResult) msg.obj;
+ if (asyncResult == null) {
+ loge("EVENT_RELEASE_PDU_SESSION_ID: asyncResult is null!");
+ } else {
+ if (msg.obj != null) {
+ if (DBG) logd("EVENT_RELEASE_PDU_SESSION_ID: id released");
+ Runnable runnable = (Runnable) asyncResult.userObj;
+ runnable.run();
+ } else {
+ loge("EVENT_RELEASE_PDU_SESSION_ID: no runnable set");
+ }
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_ALLOCATE_PDU_SESSION_ID: {
+ // We do the same thing in all state in order to preserve the existing workflow
+ final AsyncResult asyncResult = (AsyncResult) msg.obj;
+ if (asyncResult == null) {
+ loge("EVENT_ALLOCATE_PDU_SESSION_ID: asyncResult is null!");
+ } else {
+ Consumer<Integer> onAllocated = (Consumer<Integer>) asyncResult.userObj;
+ if (asyncResult.exception != null) {
+ loge("EVENT_ALLOCATE_PDU_SESSION_ID: exception",
+ asyncResult.exception);
+ onAllocated.accept(PDU_SESSION_ID_NOT_SET);
+ } else if (asyncResult.result == null) {
+ loge("EVENT_ALLOCATE_PDU_SESSION_ID: result null, no id");
+ onAllocated.accept(PDU_SESSION_ID_NOT_SET);
+ } else {
+ int psi = (int) asyncResult.result;
+ if (DBG) logd("EVENT_ALLOCATE_PDU_SESSION_ID: psi=" + psi);
+ onAllocated.accept(psi);
+ }
}
- updateNetworkInfo();
+ retVal = HANDLED;
break;
+ }
default:
if (DBG) {
log("DcDefaultState: ignore msg.what=" + getWhatToString(msg.what));
@@ -1754,58 +2400,73 @@ public class DataConnection extends StateMachine {
}
}
- private void updateNetworkInfo() {
- final ServiceState state = mPhone.getServiceState();
-
- NetworkRegistrationInfo nri = state.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
- int subtype = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- if (nri != null) {
- subtype = nri.getAccessNetworkTechnology();
+ private void updateSuspendState() {
+ if (mNetworkAgent == null) {
+ Rlog.d(getName(), "Setting suspend state without a NetworkAgent");
+ }
+
+ boolean newSuspendedState = false;
+ // Data can only be (temporarily) suspended while data is in active state
+ if (getCurrentState() == mActiveState) {
+ // Never set suspended for emergency apn. Emergency data connection
+ // can work while device is not in service.
+ if (mApnSetting != null && mApnSetting.isEmergencyApn()) {
+ newSuspendedState = false;
+ // If we are not in service, change to suspended.
+ } else if (mDataRegState != ServiceState.STATE_IN_SERVICE) {
+ newSuspendedState = true;
+ // Check voice/data concurrency.
+ } else if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
+ newSuspendedState = mPhone.getCallTracker().getState() != PhoneConstants.State.IDLE;
+ }
}
- mNetworkInfo.setSubtype(subtype, TelephonyManager.getNetworkTypeName(subtype));
- }
+ // Only notify when there is a change.
+ if (mIsSuspended != newSuspendedState) {
+ mIsSuspended = newSuspendedState;
- private void updateNetworkInfoSuspendState() {
- // this is only called when we are either connected or suspended. Decide which.
- if (mNetworkAgent == null) {
- Rlog.e(getName(), "Setting suspend state without a NetworkAgent");
+ // If data connection is active, we need to notify the new data connection state
+ // changed event reflecting the latest suspended state.
+ if (isActive()) {
+ notifyDataConnectionState();
+ }
}
+ }
- // if we are not in-service change to SUSPENDED
- final ServiceStateTracker sst = mPhone.getServiceStateTracker();
- if (sst.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null,
- mNetworkInfo.getExtraInfo());
+ private void notifyDataConnectionState() {
+ // The receivers of this have no way to differentiate between default and enterprise
+ // connections. Do not notify for enterprise.
+ if (!isEnterpriseUse()) {
+ mPhone.notifyDataConnection(getPreciseDataConnectionState());
} else {
- // check for voice call and concurrency issues
- if (sst.isConcurrentVoiceAndDataAllowed() == false) {
- final CallTracker ct = mPhone.getCallTracker();
- if (ct.getState() != PhoneConstants.State.IDLE) {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null,
- mNetworkInfo.getExtraInfo());
- return;
- }
- }
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null,
- mNetworkInfo.getExtraInfo());
+ log("notifyDataConnectionState: Skipping for enterprise; state=" + getState());
}
}
private DcDefaultState mDefaultState = new DcDefaultState();
+ private int getApnTypeBitmask() {
+ return isEnterpriseUse() ? ApnSetting.TYPE_ENTERPRISE :
+ mApnSetting != null ? mApnSetting.getApnTypeBitmask() : 0;
+ }
+
+ private boolean canHandleDefault() {
+ return !isEnterpriseUse() && mApnSetting != null
+ ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false;
+ }
+
/**
* The state machine is inactive and expects a EVENT_CONNECT.
*/
private class DcInactiveState extends State {
// Inform all contexts we've failed connecting
- public void setEnterNotificationParams(ConnectionParams cp,
- @DataFailureCause int cause) {
+ public void setEnterNotificationParams(ConnectionParams cp, @DataFailureCause int cause,
+ @HandoverFailureMode int handoverFailureMode) {
if (VDBG) log("DcInactiveState: setEnterNotificationParams cp,cause");
mConnectionParams = cp;
mDisconnectParams = null;
mDcFailCause = cause;
+ mHandoverFailureMode = handoverFailureMode;
}
// Inform all contexts we've failed disconnected
@@ -1829,11 +2490,10 @@ public class DataConnection extends StateMachine {
if (DBG) log("DcInactiveState: enter() mTag=" + mTag);
TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__INACTIVE,
- mPhone.getPhoneId(), mId,
- mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
- mApnSetting != null
- ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+ mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
+ mDataCallSessionStats.onDataCallDisconnected(mDcFailCause);
if (mHandoverState == HANDOVER_STATE_BEING_TRANSFERRED) {
+ // This is from source data connection to set itself's state
setHandoverState(HANDOVER_STATE_COMPLETED);
}
@@ -1849,7 +2509,7 @@ public class DataConnection extends StateMachine {
mHandoverLocalLog.log(
"Handover failed. Reset the source dc " + sourceDc.getName()
+ " state to idle");
- sourceDc.setHandoverState(HANDOVER_STATE_IDLE);
+ sourceDc.cancelHandover();
} else {
// The agent is now a dangling agent. No data connection owns this agent.
// Gracefully notify connectivity service disconnected.
@@ -1857,15 +2517,8 @@ public class DataConnection extends StateMachine {
"Handover failed and dangling agent found.");
mHandoverSourceNetworkAgent.acquireOwnership(
DataConnection.this, mTransportType);
- NetworkInfo networkInfo = mHandoverSourceNetworkAgent.getNetworkInfo();
- if (networkInfo != null) {
- log("Cleared dangling network agent. " + mHandoverSourceNetworkAgent);
- mHandoverSourceNetworkAgent.unregister(DataConnection.this);
- } else {
- String str = "Failed to get network info.";
- loge(str);
- mHandoverLocalLog.log(str);
- }
+ log("Cleared dangling network agent. " + mHandoverSourceNetworkAgent);
+ mHandoverSourceNetworkAgent.unregister(DataConnection.this);
mHandoverSourceNetworkAgent.releaseOwnership(DataConnection.this);
}
mHandoverSourceNetworkAgent = null;
@@ -1874,14 +2527,15 @@ public class DataConnection extends StateMachine {
if (mConnectionParams != null) {
if (DBG) {
log("DcInactiveState: enter notifyConnectCompleted +ALL failCause="
- + mDcFailCause);
+ + DataFailCause.toString(mDcFailCause));
}
- notifyConnectCompleted(mConnectionParams, mDcFailCause, true);
+ notifyConnectCompleted(mConnectionParams, mDcFailCause, mHandoverFailureMode,
+ true);
}
if (mDisconnectParams != null) {
if (DBG) {
log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause="
- + mDcFailCause);
+ + DataFailCause.toString(mDcFailCause));
}
notifyDisconnectCompleted(mDisconnectParams, true);
}
@@ -1889,7 +2543,7 @@ public class DataConnection extends StateMachine {
&& mDcFailCause != DataFailCause.NONE) {
if (DBG) {
log("DcInactiveState: enter notifyAllDisconnectCompleted failCause="
- + mDcFailCause);
+ + DataFailCause.toString(mDcFailCause));
}
notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DONE,
DataFailCause.toString(mDcFailCause));
@@ -1898,6 +2552,13 @@ public class DataConnection extends StateMachine {
// Remove ourselves from cid mapping, before clearSettings
mDcController.removeActiveDcByCid(DataConnection.this);
+ // For the first time entering here (idle state before setup), do not notify
+ // disconnected state. Only notify data connection disconnected for data that is
+ // actually moving from disconnecting to disconnected, or setup failed. In both cases,
+ // APN setting will not be null.
+ if (mApnSetting != null) {
+ notifyDataConnectionState();
+ }
clearSettings();
}
@@ -1922,7 +2583,7 @@ public class DataConnection extends StateMachine {
if (!initConnection(cp)) {
log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
notifyConnectCompleted(cp, DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
- false);
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
transitionTo(mInactiveState);
return HANDLED;
}
@@ -1930,7 +2591,8 @@ public class DataConnection extends StateMachine {
int cause = connect(cp);
if (cause != DataFailCause.NONE) {
log("DcInactiveState: msg.what=EVENT_CONNECT connect failed");
- notifyConnectCompleted(cp, cause, false);
+ notifyConnectCompleted(cp, cause,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
transitionTo(mInactiveState);
return HANDLED;
}
@@ -1965,12 +2627,10 @@ public class DataConnection extends StateMachine {
private class DcActivatingState extends State {
@Override
public void enter() {
+ int apnTypeBitmask = getApnTypeBitmask();
TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVATING,
- mPhone.getPhoneId(), mId,
- mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
- mApnSetting != null
- ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+ mPhone.getPhoneId(), mId, apnTypeBitmask, canHandleDefault());
setHandoverState(HANDOVER_STATE_IDLE);
// restricted evaluation depends on network requests from apnContext. The evaluation
// should happen once entering connecting state rather than active state because it's
@@ -1984,6 +2644,8 @@ public class DataConnection extends StateMachine {
mPhone.getCarrierPrivilegesTracker()
.registerCarrierPrivilegesListener(
getHandler(), EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null);
+ notifyDataConnectionState();
+ mDataCallSessionStats.onSetupDataCall(apnTypeBitmask);
}
@Override
public boolean processMessage(Message msg) {
@@ -2029,9 +2691,34 @@ public class DataConnection extends StateMachine {
// Vendor ril rejected the command and didn't connect.
// Transition to inactive but send notifications after
// we've entered the mInactive state.
- mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
+ mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
+ transitionTo(mInactiveState);
+ break;
+ case ERROR_DUPLICATE_CID:
+ // TODO (b/180988471): Properly handle the case when an existing cid is
+ // returned by tearing down the network agent if enterprise changed.
+ long retry = RetryManager.NO_SUGGESTED_RETRY_DELAY;
+ if (cp.mApnContext != null) {
+ retry = RetryManager.NO_RETRY;
+ mDct.getDataThrottler().setRetryTime(
+ cp.mApnContext.getApnTypeBitmask(),
+ retry, DcTracker.REQUEST_TYPE_NORMAL);
+ }
+ String logStr = "DcActivatingState: "
+ + DataFailCause.toString(result.mFailCause)
+ + " retry=" + retry;
+ if (DBG) log(logStr);
+ if (cp.mApnContext != null) cp.mApnContext.requestLog(logStr);
+ mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
transitionTo(mInactiveState);
break;
+ case ERROR_NO_DEFAULT_CONNECTION:
+ // TODO (b/180988471): Properly handle the case when a default data
+ // connection doesn't exist (tear down connection and retry).
+ // Currently, this just tears down the connection without retry.
+ if (DBG) log("DcActivatingState: NO_DEFAULT_DATA");
case ERROR_INVALID_ARG:
// The addresses given from the RIL are bad
tearDownData(cp);
@@ -2045,7 +2732,16 @@ public class DataConnection extends StateMachine {
// NO_SUGGESTED_RETRY_DELAY here.
long delay = getSuggestedRetryDelay(dataCallResponse);
- cp.mApnContext.setModemSuggestedDelay(delay);
+ long retryTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
+ if (delay == RetryManager.NO_RETRY) {
+ retryTime = RetryManager.NO_RETRY;
+ } else if (delay >= 0) {
+ retryTime = SystemClock.elapsedRealtime() + delay;
+ }
+ int newRequestType = DcTracker.calculateNewRetryRequestType(
+ mHandoverFailureMode, cp.mRequestType, mDcFailCause);
+ mDct.getDataThrottler().setRetryTime(getApnTypeBitmask(),
+ retryTime, newRequestType);
String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
+ " delay=" + delay
@@ -2061,7 +2757,10 @@ public class DataConnection extends StateMachine {
// Save the cause. DcTracker.onDataSetupComplete will check this
// failure cause and determine if we need to retry this APN later
// or not.
- mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
+ mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
+ dataCallResponse != null
+ ? dataCallResponse.getHandoverFailureMode()
+ : DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
transitionTo(mInactiveState);
break;
case ERROR_STALE:
@@ -2072,6 +2771,10 @@ public class DataConnection extends StateMachine {
throw new RuntimeException("Unknown SetupResult, should not happen");
}
retVal = HANDLED;
+ mDataCallSessionStats
+ .onSetupDataCallResponse(dataCallResponse, cp.mRilRat,
+ getApnTypeBitmask(), mApnSetting.getProtocol(),
+ result.mFailCause);
break;
case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
AsyncResult asyncResult = (AsyncResult) msg.obj;
@@ -2079,6 +2782,15 @@ public class DataConnection extends StateMachine {
mAdministratorUids = Arrays.copyOf(administratorUids, administratorUids.length);
retVal = HANDLED;
break;
+ case EVENT_START_HANDOVER_ON_TARGET:
+ //called after startHandover on target transport
+ ((Consumer<Boolean>) msg.obj).accept(true /* is in correct state*/);
+ retVal = HANDLED;
+ break;
+ case EVENT_CANCEL_HANDOVER:
+ transitionTo(mInactiveState);
+ retVal = HANDLED;
+ break;
default:
if (VDBG) {
log("DcActivatingState not handled msg.what=" +
@@ -2101,13 +2813,7 @@ public class DataConnection extends StateMachine {
if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVE,
- mPhone.getPhoneId(), mId,
- mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
- mApnSetting != null
- ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
-
- updateNetworkInfo();
-
+ mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
// If we were retrying there maybe more than one, otherwise they'll only be one.
notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE,
Phone.REASON_CONNECTED);
@@ -2121,21 +2827,21 @@ public class DataConnection extends StateMachine {
// if it didn't then this is effectively a NOP.
mDcController.addActiveDcByCid(DataConnection.this);
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED,
- mNetworkInfo.getReason(), null);
- mNetworkInfo.setExtraInfo(mApnSetting.getApnName());
updateTcpBufferSizes(mRilRat);
updateLinkBandwidthsFromCarrierConfig(mRilRat);
final NetworkAgentConfig.Builder configBuilder = new NetworkAgentConfig.Builder();
configBuilder.setLegacyType(ConnectivityManager.TYPE_MOBILE);
configBuilder.setLegacyTypeName(NETWORK_TYPE);
+ int networkType = getNetworkType();
+ configBuilder.setLegacySubType(networkType);
+ configBuilder.setLegacySubTypeName(TelephonyManager.getNetworkTypeName(networkType));
configBuilder.setLegacyExtraInfo(mApnSetting.getApnName());
final CarrierSignalAgent carrierSignalAgent = mPhone.getCarrierSignalAgent();
if (carrierSignalAgent.hasRegisteredReceivers(TelephonyManager
.ACTION_CARRIER_SIGNAL_REDIRECTED)) {
// carrierSignal Receivers will place the carrier-specific provisioning notification
- configBuilder.disableProvisioningNotification();
+ configBuilder.setProvisioningNotificationEnabled(false);
}
final String subscriberId = mPhone.getSubscriberId();
@@ -2145,18 +2851,28 @@ public class DataConnection extends StateMachine {
// set skip464xlat if it is not default otherwise
if (shouldSkip464Xlat()) {
- configBuilder.disableNat64Detection();
+ configBuilder.setNat64DetectionEnabled(false);
}
mUnmeteredUseOnly = isUnmeteredUseOnly();
+ mMmsUseOnly = isMmsUseOnly();
+ mEnterpriseUse = isEnterpriseUse();
if (DBG) {
log("mRestrictedNetworkOverride = " + mRestrictedNetworkOverride
- + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly);
+ + ", mUnmeteredUseOnly = " + mUnmeteredUseOnly
+ + ", mMmsUseOnly = " + mMmsUseOnly
+ + ", mEnterpriseUse = " + mEnterpriseUse);
}
+ // Always register a VcnNetworkPolicyChangeListener, regardless of whether this is a
+ // handover
+ // or new Network.
+ mVcnManager.addVcnNetworkPolicyChangeListener(
+ new HandlerExecutor(getHandler()), mVcnPolicyChangeListener);
+
if (mConnectionParams != null
- && mConnectionParams.mRequestType == DcTracker.REQUEST_TYPE_HANDOVER) {
+ && mConnectionParams.mRequestType == REQUEST_TYPE_HANDOVER) {
// If this is a data setup for handover, we need to reuse the existing network agent
// instead of creating a new one. This should be transparent to connectivity
// service.
@@ -2183,6 +2899,7 @@ public class DataConnection extends StateMachine {
// do it now because connectivity service does not support dynamically removing
// immutable capabilities.
+ mNetworkAgent.updateLegacySubtype(DataConnection.this);
// Update the capability after handover
mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
DataConnection.this);
@@ -2201,63 +2918,80 @@ public class DataConnection extends StateMachine {
final NetworkProvider provider = (null == factory) ? null : factory.getProvider();
mDisabledApnTypeBitMask |= getDisallowedApnTypes();
+ updateLinkPropertiesHttpProxy();
+ // The suspended state is only meaningful when data is in active state. We need to
+ // make sure the suspended state is correct as soon as we enter active state.
+ // After this, the network agent will be created with the correct suspended state
+ // (i.e. NOT_SUSPENDED capability).
+ updateSuspendState();
+ mNetworkAgent = new DcNetworkAgent(DataConnection.this, mPhone, mScore,
+ configBuilder.build(), provider, mTransportType);
+
+ VcnNetworkPolicyResult policyResult =
+ mVcnManager.applyVcnNetworkPolicy(
+ getNetworkCapabilities(), getLinkProperties());
+ if (policyResult.isTeardownRequested()) {
+ tearDownAll(
+ Phone.REASON_VCN_REQUESTED_TEARDOWN,
+ DcTracker.RELEASE_TYPE_DETACH,
+ null /* onCompletedMsg */);
+ } else {
+ // All network agents start out in CONNECTING mode, but DcNetworkAgents are
+ // created when the network is already connected. Hence, send the connected
+ // notification immediately.
+ mNetworkAgent.markConnected();
+ }
- mNetworkAgent = new DcNetworkAgent(DataConnection.this,
- mPhone, mNetworkInfo, mScore, configBuilder.build(), provider,
- mTransportType);
- // All network agents start out in CONNECTING mode, but DcNetworkAgents are
- // created when the network is already connected. Hence, send the connected
- // notification immediately.
- mNetworkAgent.markConnected();
+ // The network agent is always created with NOT_SUSPENDED capability, but the
+ // network might be already out of service (or voice call is ongoing) just right
+ // before data connection is created. Connectivity service would not allow a network
+ // created with suspended state, so we create a non-suspended network first, and
+ // then immediately evaluate the suspended state.
+ sendMessage(obtainMessage(EVENT_UPDATE_SUSPENDED_STATE));
}
+ // The qos parameters are set when the call is connected
+ syncQosToNetworkAgent();
+
if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
mPhone.mCi.registerForNattKeepaliveStatus(
getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null);
mPhone.mCi.registerForLceInfo(
getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null);
}
+ notifyDataConnectionState();
TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
- mCid, mApnSetting.getApnTypeBitmask(), RilDataCall.State.CONNECTED);
+ mCid, getApnTypeBitmask(), RilDataCall.State.CONNECTED);
}
@Override
public void exit() {
if (DBG) log("DcActiveState: exit dc=" + this);
- String reason = mNetworkInfo.getReason();
- if(mDcController.isExecutingCarrierChange()) {
- reason = Phone.REASON_CARRIER_CHANGE;
- } else if (mDisconnectParams != null && mDisconnectParams.mReason != null) {
- reason = mDisconnectParams.mReason;
- } else {
- reason = DataFailCause.toString(mDcFailCause);
- }
mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
- // If the data connection is being handover to other transport, we should not notify
- // disconnected to connectivity service.
- if (mHandoverState != HANDOVER_STATE_BEING_TRANSFERRED) {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
- reason, mNetworkInfo.getExtraInfo());
- }
-
if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
mPhone.mCi.unregisterForNattKeepaliveStatus(getHandler());
mPhone.mCi.unregisterForLceInfo(getHandler());
}
// If we are still owning this agent, then we should inform connectivity service the
- // data connection is disconnected. If we don't own this agent at this point, that means
- // it has been transferred to the new data connection for IWLAN data handover case.
+ // data connection is disconnected. There is one exception that we shouldn't unregister,
+ // which is when IWLAN handover is ongoing. Instead of unregistering, the agent will
+ // be transferred to the new data connection on the other transport.
if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
+ syncQosToNetworkAgent();
+ if (mHandoverState == HANDOVER_STATE_IDLE) {
+ mNetworkAgent.unregister(DataConnection.this);
+ }
mNetworkAgent.releaseOwnership(DataConnection.this);
}
mNetworkAgent = null;
TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
- mCid, mApnSetting.getApnTypeBitmask(), RilDataCall.State.DISCONNECTED);
+ mCid, getApnTypeBitmask(), RilDataCall.State.DISCONNECTED);
+
+ mVcnManager.removeVcnNetworkPolicyChangeListener(mVcnPolicyChangeListener);
mPhone.getCarrierPrivilegesTracker().unregisterCarrierPrivilegesListener(getHandler());
}
@@ -2280,7 +3014,8 @@ public class DataConnection extends StateMachine {
if (DBG) {
log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this);
}
- notifyConnectCompleted(cp, DataFailCause.NONE, false);
+ notifyConnectCompleted(cp, DataFailCause.NONE,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, false);
retVal = HANDLED;
break;
}
@@ -2358,14 +3093,17 @@ public class DataConnection extends StateMachine {
+ " drs=" + mDataRegState
+ " mRilRat=" + mRilRat);
}
- updateNetworkInfoSuspendState();
+ updateSuspendState();
if (mNetworkAgent != null) {
+ mNetworkAgent.updateLegacySubtype(DataConnection.this);
+ // The new suspended state will be passed through connectivity service
+ // through NET_CAPABILITY_NOT_SUSPENDED.
mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
DataConnection.this);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
}
retVal = HANDLED;
+ mDataCallSessionStats.onDrsOrRatChanged(mRilRat);
break;
}
case EVENT_NR_FREQUENCY_CHANGED:
@@ -2387,11 +3125,42 @@ public class DataConnection extends StateMachine {
break;
}
mUnmeteredOverride = isUnmetered;
- // fallthrough
+ if (mNetworkAgent != null) {
+ mNetworkAgent.updateLegacySubtype(DataConnection.this);
+ mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
+ DataConnection.this);
+ }
+ retVal = HANDLED;
+ break;
+ case EVENT_DATA_CONNECTION_CONGESTEDNESS_CHANGED:
+ boolean isCongested = (boolean) msg.obj;
+ if (isCongested == mCongestedOverride) {
+ retVal = HANDLED;
+ break;
+ }
+ mCongestedOverride = isCongested;
+ if (mNetworkAgent != null) {
+ mNetworkAgent.updateLegacySubtype(DataConnection.this);
+ mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
+ DataConnection.this);
+ }
+ retVal = HANDLED;
+ break;
case EVENT_DATA_CONNECTION_ROAM_ON:
- case EVENT_DATA_CONNECTION_ROAM_OFF:
- case EVENT_DATA_CONNECTION_OVERRIDE_CHANGED: {
- updateNetworkInfo();
+ case EVENT_DATA_CONNECTION_ROAM_OFF: {
+ if (mNetworkAgent != null) {
+ mNetworkAgent.updateLegacySubtype(DataConnection.this);
+ mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
+ DataConnection.this);
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED:
+ case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED:
+ case EVENT_CSS_INDICATOR_CHANGED:
+ case EVENT_UPDATE_SUSPENDED_STATE: {
+ updateSuspendState();
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
DataConnection.this);
@@ -2405,24 +3174,12 @@ public class DataConnection extends StateMachine {
log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception);
} else {
if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) {
- updateLinkBandwidthsFromModem((LinkCapacityEstimate) ar.result);
+ updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result);
}
}
retVal = HANDLED;
break;
}
- case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED:
- case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED: {
- updateNetworkInfo();
- updateNetworkInfoSuspendState();
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities(),
- DataConnection.this);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo, DataConnection.this);
- }
- retVal = HANDLED;
- break;
- }
case EVENT_KEEPALIVE_START_REQUEST: {
KeepalivePacketData pkt = (KeepalivePacketData) msg.obj;
int slotId = msg.arg1;
@@ -2454,7 +3211,7 @@ public class DataConnection extends StateMachine {
if (handle < 0) {
loge("No slot found for stopSocketKeepalive! " + slotId);
mNetworkAgent.sendSocketKeepaliveEvent(
- slotId, SocketKeepalive.NO_KEEPALIVE);
+ slotId, SocketKeepalive.ERROR_NO_SUCH_SLOT);
retVal = HANDLED;
break;
} else {
@@ -2524,7 +3281,21 @@ public class DataConnection extends StateMachine {
loge("EVENT_LINK_CAPACITY_CHANGED e=" + ar.exception);
} else {
if (isBandwidthSourceKey(DctConstants.BANDWIDTH_SOURCE_MODEM_KEY)) {
- updateLinkBandwidthsFromModem((LinkCapacityEstimate) ar.result);
+ updateLinkBandwidthsFromModem((List<LinkCapacityEstimate>) ar.result);
+ }
+ }
+ retVal = HANDLED;
+ break;
+ }
+ case EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ loge("EVENT_LINK_BANDWIDTH_ESTIMATOR_UPDATE e=" + ar.exception);
+ } else {
+ Pair<Integer, Integer> pair = (Pair<Integer, Integer>) ar.result;
+ if (isBandwidthSourceKey(
+ DctConstants.BANDWIDTH_SOURCE_BANDWIDTH_ESTIMATOR_KEY)) {
+ updateLinkBandwidthsFromBandwidthEstimator(pair.first, pair.second);
}
}
retVal = HANDLED;
@@ -2560,6 +3331,8 @@ public class DataConnection extends StateMachine {
DataConnection.this);
}
+ mMmsUseOnly = isMmsUseOnly();
+
retVal = HANDLED;
break;
}
@@ -2595,6 +3368,12 @@ public class DataConnection extends StateMachine {
}
retVal = HANDLED;
break;
+ case EVENT_START_HANDOVER: //calls startHandover()
+ Consumer<Integer> r = (Consumer<Integer>) msg.obj;
+ r.accept(msg.arg1);
+ retVal = HANDLED;
+ break;
+
default:
if (VDBG) {
log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
@@ -2615,10 +3394,8 @@ public class DataConnection extends StateMachine {
public void enter() {
TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTING,
- mPhone.getPhoneId(), mId,
- mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
- mApnSetting != null
- ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+ mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
+ notifyDataConnectionState();
}
@Override
public boolean processMessage(Message msg) {
@@ -2637,9 +3414,13 @@ public class DataConnection extends StateMachine {
String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
+ mApnContexts.size();
+
if (DBG) log(str);
if (dp.mApnContext != null) dp.mApnContext.requestLog(str);
+ // Clear out existing qos sessions
+ updateQosParameters(null);
+
if (dp.mTag == mTag) {
// Transition to inactive but send notifications after
// we've entered the mInactive state.
@@ -2674,10 +3455,8 @@ public class DataConnection extends StateMachine {
TelephonyStatsLog.write(TelephonyStatsLog.MOBILE_CONNECTION_STATE_CHANGED,
TelephonyStatsLog
.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTION_ERROR_CREATING_CONNECTION,
- mPhone.getPhoneId(), mId,
- mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
- mApnSetting != null
- ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+ mPhone.getPhoneId(), mId, getApnTypeBitmask(), canHandleDefault());
+ notifyDataConnectionState();
}
@Override
public boolean processMessage(Message msg) {
@@ -2695,7 +3474,8 @@ public class DataConnection extends StateMachine {
// Transition to inactive but send notifications after
// we've entered the mInactive state.
mInactiveState.setEnterNotificationParams(cp,
- DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER);
+ DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
+ DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
transitionTo(mInactiveState);
} else {
if (DBG) {
@@ -2743,6 +3523,11 @@ public class DataConnection extends StateMachine {
if (DBG) {
log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg);
}
+
+ if (mApnSetting == null) {
+ mApnSetting = apnContext.getApnSetting();
+ }
+
sendMessage(DataConnection.EVENT_CONNECT,
new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,
connectionGeneration, requestType, subId, isApnPreferred));
@@ -2786,7 +3571,10 @@ public class DataConnection extends StateMachine {
*/
public void tearDownAll(String reason, @ReleaseNetworkType int releaseType,
Message onCompletedMsg) {
- if (DBG) log("tearDownAll: reason=" + reason + ", releaseType=" + releaseType);
+ if (DBG) {
+ log("tearDownAll: reason=" + reason + ", releaseType="
+ + DcTracker.releaseTypeToString(releaseType));
+ }
sendMessage(DataConnection.EVENT_DISCONNECT_ALL,
new DisconnectParams(null, reason, releaseType, onCompletedMsg));
}
@@ -2825,6 +3613,16 @@ public class DataConnection extends StateMachine {
}
/**
+ * Update PCSCF addresses
+ *
+ * @param response
+ */
+ public void updatePcscfAddr(DataCallResponse response) {
+ mPcscfAddr = response.getPcscfAddresses().stream()
+ .map(InetAddress::getHostAddress).toArray(String[]::new);
+ }
+
+ /**
* @return The list of PCSCF addresses
*/
public String[] getPcscfAddresses() {
@@ -2835,36 +3633,59 @@ public class DataConnection extends StateMachine {
* Using the result of the SETUP_DATA_CALL determine the retry delay.
*
* @param response The response from setup data call
- * @return NO_SUGGESTED_RETRY_DELAY if no retry is needed otherwise the delay to the
- * next SETUP_DATA_CALL
+ * @return {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} if not suggested.
+ * {@link RetryManager#NO_RETRY} if retry should not happen. Otherwise the delay in milliseconds
+ * to the next SETUP_DATA_CALL.
*/
private long getSuggestedRetryDelay(DataCallResponse response) {
/** According to ril.h
* The value < 0 means no value is suggested
* The value 0 means retry should be done ASAP.
- * The value of Integer.MAX_VALUE(0x7fffffff) means no retry.
+ * The value of Long.MAX_VALUE(0x7fffffffffffffff) means no retry.
*/
+ if (response == null) {
+ return RetryManager.NO_SUGGESTED_RETRY_DELAY;
+ }
+
+ long suggestedRetryTime = response.getRetryDurationMillis();
// The value < 0 means no value is suggested
- if (response.getSuggestedRetryTime() < 0) {
+ if (suggestedRetryTime < 0) {
if (DBG) log("No suggested retry delay.");
return RetryManager.NO_SUGGESTED_RETRY_DELAY;
- }
- // The value of Integer.MAX_VALUE(0x7fffffff) means no retry.
- else if (response.getSuggestedRetryTime() == Integer.MAX_VALUE) {
- if (DBG) log("Modem suggested not retrying.");
+ } else if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)
+ && suggestedRetryTime == Long.MAX_VALUE) {
+ if (DBG) log("Network suggested not retrying.");
+ return RetryManager.NO_RETRY;
+ } else if (mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_6)
+ && suggestedRetryTime == Integer.MAX_VALUE) {
+ if (DBG) log("Network suggested not retrying.");
return RetryManager.NO_RETRY;
}
- // We need to cast it to long because the value returned from RIL is a 32-bit integer,
- // but the time values used in AlarmManager are all 64-bit long.
- return (long) response.getSuggestedRetryTime();
+ return suggestedRetryTime;
}
public List<ApnContext> getApnContexts() {
return new ArrayList<>(mApnContexts.keySet());
}
+ /**
+ * Return whether there is an ApnContext for the given type in this DataConnection.
+ * @param type APN type to check
+ * @param exclusive true if the given APN type should be the only APN type that exists
+ * @return True if there is an ApnContext for the given type
+ */
+ private boolean isApnContextAttached(@ApnType int type, boolean exclusive) {
+ boolean attached = mApnContexts.keySet().stream()
+ .map(ApnContext::getApnTypeBitmask)
+ .anyMatch(bitmask -> bitmask == type);
+ if (exclusive) {
+ attached &= mApnContexts.size() == 1;
+ }
+ return attached;
+ }
+
/** Get the network agent of the data connection */
@Nullable
DcNetworkAgent getNetworkAgent() {
@@ -2873,12 +3694,20 @@ public class DataConnection extends StateMachine {
void setHandoverState(@HandoverState int state) {
if (mHandoverState != state) {
- mHandoverLocalLog.log("State changed from " + handoverStateToString(mHandoverState)
- + " to " + handoverStateToString(state));
+ String logStr = "State changed from " + handoverStateToString(mHandoverState)
+ + " to " + handoverStateToString(state);
+ mHandoverLocalLog.log(logStr);
+ logd(logStr);
mHandoverState = state;
}
}
+ /** Sets the {@link DataCallSessionStats} mock for this phone ID during unit testing. */
+ @VisibleForTesting
+ public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) {
+ mDataCallSessionStats = dataCallSessionStats;
+ }
+
/**
* @return the string for msg.what as our info.
*/
@@ -3009,7 +3838,7 @@ public class DataConnection extends StateMachine {
+ " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size()
+ " mCid=" + mCid + " mCreateTime=" + mCreateTime
+ " mLastastFailTime=" + mLastFailTime
- + " mLastFailCause=" + mLastFailCause
+ + " mLastFailCause=" + DataFailCause.toString(mLastFailCause)
+ " mTag=" + mTag
+ " mLinkProperties=" + mLinkProperties
+ " linkCapabilities=" + getNetworkCapabilities()
@@ -3108,11 +3937,46 @@ public class DataConnection extends StateMachine {
}
}
+ private @DataState int getState() {
+ if (isInactive()) {
+ return TelephonyManager.DATA_DISCONNECTED;
+ } else if (isActivating()) {
+ return TelephonyManager.DATA_CONNECTING;
+ } else if (isActive()) {
+ // The data connection can only be suspended when it's in active state.
+ if (mIsSuspended) {
+ return TelephonyManager.DATA_SUSPENDED;
+ }
+ return TelephonyManager.DATA_CONNECTED;
+ } else if (isDisconnecting()) {
+ return TelephonyManager.DATA_DISCONNECTING;
+ }
+
+ return TelephonyManager.DATA_UNKNOWN;
+ }
+
+ /**
+ * Get precise data connection state
+ *
+ * @return The {@link PreciseDataConnectionState}
+ */
+ public PreciseDataConnectionState getPreciseDataConnectionState() {
+ return new PreciseDataConnectionState.Builder()
+ .setTransportType(mTransportType)
+ .setId(mCid)
+ .setState(getState())
+ .setApnSetting(mApnSetting)
+ .setLinkProperties(mLinkProperties)
+ .setNetworkType(getNetworkType())
+ .setFailCause(mDcFailCause)
+ .build();
+ }
+
/**
* Dump the current state.
*
* @param fd
- * @param pw
+ * @param printWriter
* @param args
*/
@Override
@@ -3131,7 +3995,7 @@ public class DataConnection extends StateMachine {
pw.println("mCid=" + mCid);
pw.println("mConnectionParams=" + mConnectionParams);
pw.println("mDisconnectParams=" + mDisconnectParams);
- pw.println("mDcFailCause=" + mDcFailCause);
+ pw.println("mDcFailCause=" + DataFailCause.toString(mDcFailCause));
pw.println("mPhone=" + mPhone);
pw.println("mSubId=" + mSubId);
pw.println("mLinkProperties=" + mLinkProperties);
@@ -3142,14 +4006,18 @@ public class DataConnection extends StateMachine {
pw.println("mNetworkCapabilities=" + getNetworkCapabilities());
pw.println("mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
pw.println("mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
- pw.println("mLastFailCause=" + mLastFailCause);
+ pw.println("mLastFailCause=" + DataFailCause.toString(mLastFailCause));
pw.println("mUserData=" + mUserData);
- pw.println("mSubscriptionOverride=" + Integer.toHexString(mSubscriptionOverride));
pw.println("mRestrictedNetworkOverride=" + mRestrictedNetworkOverride);
pw.println("mUnmeteredUseOnly=" + mUnmeteredUseOnly);
+ pw.println("mMmsUseOnly=" + mMmsUseOnly);
+ pw.println("mEnterpriseUse=" + mEnterpriseUse);
pw.println("mUnmeteredOverride=" + mUnmeteredOverride);
+ pw.println("mCongestedOverride=" + mCongestedOverride);
pw.println("mDownlinkBandwidth" + mDownlinkBandwidth);
pw.println("mUplinkBandwidth=" + mUplinkBandwidth);
+ pw.println("mDefaultQos=" + mDefaultQos);
+ pw.println("mQosBearerSessions=" + mQosBearerSessions);
pw.println("disallowedApnTypes="
+ ApnSetting.getApnTypesStringFromBitmask(getDisallowedApnTypes()));
pw.println("mInstanceNumber=" + mInstanceNumber);
@@ -3166,5 +4034,32 @@ public class DataConnection extends StateMachine {
pw.println();
pw.flush();
}
+
+ /**
+ * Class used to track VCN-defined Network policies for this DataConnection.
+ *
+ * <p>MUST be registered with the associated DataConnection's Handler.
+ */
+ private class DataConnectionVcnNetworkPolicyChangeListener
+ implements VcnNetworkPolicyChangeListener {
+ @Override
+ public void onPolicyChanged() {
+ // Poll the current underlying Network policy from VcnManager and send to NetworkAgent.
+ final NetworkCapabilities networkCapabilities = getNetworkCapabilities();
+ VcnNetworkPolicyResult policyResult =
+ mVcnManager.applyVcnNetworkPolicy(
+ networkCapabilities, getLinkProperties());
+ if (policyResult.isTeardownRequested()) {
+ tearDownAll(
+ Phone.REASON_VCN_REQUESTED_TEARDOWN,
+ DcTracker.RELEASE_TYPE_DETACH,
+ null /* onCompletedMsg */);
+ }
+
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(networkCapabilities, DataConnection.this);
+ }
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
index 6aaadf56bf..68f2ab38ae 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.dataconnection;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.HashSet;
/**
@@ -50,12 +52,12 @@ public class DataConnectionReasons {
public String toString() {
StringBuilder reasonStr = new StringBuilder();
if (mDataDisallowedReasonSet.size() > 0) {
- reasonStr.append("Data disallowed, reasons:");
+ reasonStr.append("Data disallowed reasons:");
for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
reasonStr.append(" ").append(reason);
}
} else {
- reasonStr.append("Data allowed, reason:");
+ reasonStr.append("Data allowed reason:");
reasonStr.append(" ").append(mDataAllowedReason);
}
return reasonStr.toString();
@@ -70,7 +72,14 @@ public class DataConnectionReasons {
return mDataDisallowedReasonSet.size() == 0;
}
- boolean contains(DataDisallowedReasonType reason) {
+ /**
+ * Check if it contains a certain disallowed reason.
+ *
+ * @param reason The disallowed reason to check.
+ * @return {@code true} if the provided reason matches one of the disallowed reasons.
+ */
+ @VisibleForTesting
+ public boolean contains(DataDisallowedReasonType reason) {
return mDataDisallowedReasonSet.contains(reason);
}
@@ -100,9 +109,13 @@ public class DataConnectionReasons {
// Disallowed reasons. There could be multiple reasons if data connection is not allowed.
public enum DataDisallowedReasonType {
// Soft failure reasons. Normally the reasons from users or policy settings.
- DATA_DISABLED(false), // Data is disabled by the user or policy.
- ROAMING_DISABLED(false), // Data roaming is disabled by the user.
- DEFAULT_DATA_UNSELECTED(false), // Default data not selected.
+
+ // Data is disabled by the user or policy.
+ DATA_DISABLED(false),
+ // Data roaming is disabled by the user.
+ ROAMING_DISABLED(false),
+ // Default data not selected.
+ DEFAULT_DATA_UNSELECTED(false),
// Belows are all hard failure reasons.
NOT_ATTACHED(true),
@@ -113,12 +126,30 @@ public class DataConnectionReasons {
UNDESIRED_POWER_STATE(true),
INTERNAL_DATA_DISABLED(true),
RADIO_DISABLED_BY_CARRIER(true),
+ // Not in the right state for data call setup.
APN_NOT_CONNECTABLE(true),
+ // Data is in connecting state. No need to send another setup request.
+ DATA_IS_CONNECTING(true),
+ // Data is being disconnected. Telephony will retry after disconnected.
+ DATA_IS_DISCONNECTING(true),
+ // Data is already connected. No need to setup data again.
+ DATA_ALREADY_CONNECTED(true),
+ // certain APNs are not allowed on IWLAN in legacy mode.
ON_IWLAN(true),
+ // certain APNs are only allowed when the device is camped on NR.
+ NOT_ON_NR(true),
+ // Data is not allowed while device is in emergency callback mode.
IN_ECBM(true),
- ON_OTHER_TRANSPORT(true); // When data retry occurs, the given APN type's preferred
- // transport might be already changed. In this case we
- // should disallow data retry.
+ // The given APN type's preferred transport has switched.
+ ON_OTHER_TRANSPORT(true),
+ // Underlying data service is not bound.
+ DATA_SERVICE_NOT_READY(true),
+ // Qualified networks service does not allow certain types of APN brought up on either
+ // cellular or IWLAN.
+ DISABLED_BY_QNS(true),
+ // Data is throttled. The network explicitly requested device not to establish data
+ // connection for a certain period.
+ DATA_THROTTLED(true);
private boolean mIsHardReason;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
index a8145df751..ce04e884ad 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
@@ -365,6 +365,10 @@ public class DataEnabledOverride {
return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
}
+ public boolean isMmsAlwaysAllowed() {
+ return mRules.contains(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
+ }
+
private boolean canSatisfyAnyRule(@ApnType int apnType,
@Condition int providedConditions) {
for (OverrideRule rule : mRules) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index 10c9db653d..734dfff0b0 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -69,7 +69,8 @@ public class DataEnabledSettings {
REASON_PROVISIONED_CHANGED,
REASON_PROVISIONING_DATA_ENABLED_CHANGED,
REASON_OVERRIDE_RULE_CHANGED,
- REASON_OVERRIDE_CONDITION_CHANGED
+ REASON_OVERRIDE_CONDITION_CHANGED,
+ REASON_THERMAL_DATA_ENABLED
})
public @interface DataEnabledChangedReason {}
@@ -91,6 +92,8 @@ public class DataEnabledSettings {
public static final int REASON_OVERRIDE_CONDITION_CHANGED = 8;
+ public static final int REASON_THERMAL_DATA_ENABLED = 9;
+
/**
* responds to the setInternalDataEnabled call - used internally to turn off data.
* For example during emergency calls
@@ -108,6 +111,15 @@ public class DataEnabledSettings {
*/
private boolean mCarrierDataEnabled = true;
+ /**
+ * Flag indicating data allowed by Thermal service or not.
+ */
+ private boolean mThermalDataEnabled = true;
+
+ /**
+ * Flag indicating whether data is allowed or not for the device. It can be disabled by
+ * user, carrier, policy or thermal
+ */
private boolean mIsDataEnabled = false;
private final Phone mPhone;
@@ -172,6 +184,7 @@ public class DataEnabledSettings {
+ ", mPolicyDataEnabled=" + mPolicyDataEnabled
+ ", mCarrierDataEnabled=" + mCarrierDataEnabled
+ ", mIsDataEnabled=" + mIsDataEnabled
+ + ", mThermalDataEnabled=" + mThermalDataEnabled
+ ", " + mDataEnabledOverride
+ "]";
}
@@ -204,7 +217,7 @@ public class DataEnabledSettings {
return mInternalDataEnabled;
}
- public synchronized void setUserDataEnabled(boolean enabled) {
+ private synchronized void setUserDataEnabled(boolean enabled) {
// By default the change should propagate to the group.
setUserDataEnabled(enabled, true);
}
@@ -233,6 +246,32 @@ public class DataEnabledSettings {
}
}
+ /**
+ * Policy control of data connection with reason
+ * @param reason the reason the data enable change is taking place
+ * @param enabled True if enabling the data, otherwise disabling.
+ */
+ public synchronized void setDataEnabled(@TelephonyManager.DataEnabledReason int reason,
+ boolean enabled) {
+ switch (reason) {
+ case TelephonyManager.DATA_ENABLED_REASON_USER:
+ setUserDataEnabled(enabled);
+ break;
+ case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
+ setCarrierDataEnabled(enabled);
+ break;
+ case TelephonyManager.DATA_ENABLED_REASON_POLICY:
+ setPolicyDataEnabled(enabled);
+ break;
+ case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
+ setThermalDataEnabled(enabled);
+ break;
+ default:
+ log("Invalid data enable reason " + reason);
+ break;
+ }
+ }
+
public synchronized boolean isUserDataEnabled() {
// User data should always be true for opportunistic subscription.
if (isStandAloneOpportunistic(mPhone.getSubId(), mPhone.getContext())) return true;
@@ -300,7 +339,11 @@ public class DataEnabledSettings {
return mDataEnabledOverride.isDataAllowedInVoiceCall();
}
- public synchronized void setPolicyDataEnabled(boolean enabled) {
+ public synchronized boolean isMmsAlwaysAllowed() {
+ return mDataEnabledOverride.isMmsAlwaysAllowed();
+ }
+
+ private synchronized void setPolicyDataEnabled(boolean enabled) {
if (mPolicyDataEnabled != enabled) {
localLog("PolicyDataEnabled", enabled);
mPolicyDataEnabled = enabled;
@@ -312,7 +355,7 @@ public class DataEnabledSettings {
return mPolicyDataEnabled;
}
- public synchronized void setCarrierDataEnabled(boolean enabled) {
+ private synchronized void setCarrierDataEnabled(boolean enabled) {
if (mCarrierDataEnabled != enabled) {
localLog("CarrierDataEnabled", enabled);
mCarrierDataEnabled = enabled;
@@ -324,6 +367,18 @@ public class DataEnabledSettings {
return mCarrierDataEnabled;
}
+ private synchronized void setThermalDataEnabled(boolean enabled) {
+ if (mThermalDataEnabled != enabled) {
+ localLog("ThermalDataEnabled", enabled);
+ mThermalDataEnabled = enabled;
+ updateDataEnabledAndNotify(REASON_THERMAL_DATA_ENABLED);
+ }
+ }
+
+ public synchronized boolean isThermalDataEnabled() {
+ return mThermalDataEnabled;
+ }
+
public synchronized void updateProvisionedChanged() {
updateDataEnabledAndNotify(REASON_PROVISIONED_CHANGED);
}
@@ -336,6 +391,27 @@ public class DataEnabledSettings {
return mIsDataEnabled;
}
+ /**
+ * Check if data is enabled for a specific reason {@@TelephonyManager.DataEnabledReason}
+ *
+ * @return {@code true} if the overall data is enabled; {@code false} if not.
+ */
+ public synchronized boolean isDataEnabledForReason(
+ @TelephonyManager.DataEnabledReason int reason) {
+ switch (reason) {
+ case TelephonyManager.DATA_ENABLED_REASON_USER:
+ return isUserDataEnabled();
+ case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
+ return isCarrierDataEnabled();
+ case TelephonyManager.DATA_ENABLED_REASON_POLICY:
+ return isPolicyDataEnabled();
+ case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
+ return isThermalDataEnabled();
+ default:
+ return false;
+ }
+ }
+
private synchronized void updateDataEnabledAndNotify(int reason) {
boolean prevDataEnabled = mIsDataEnabled;
@@ -352,7 +428,7 @@ public class DataEnabledSettings {
} else {
mIsDataEnabled = mInternalDataEnabled && (isUserDataEnabled() || mDataEnabledOverride
.shouldOverrideDataEnabledSettings(mPhone, ApnSetting.TYPE_ALL))
- && mPolicyDataEnabled && mCarrierDataEnabled;
+ && mPolicyDataEnabled && mCarrierDataEnabled && mThermalDataEnabled;
}
}
@@ -418,6 +494,7 @@ public class DataEnabledSettings {
private void notifyDataEnabledChanged(boolean enabled, int reason) {
mOverallDataEnabledChangedRegistrants.notifyResult(new Pair<>(enabled, reason));
+ mPhone.notifyDataEnabled(enabled, reason);
}
public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
@@ -469,7 +546,7 @@ public class DataEnabledSettings {
.shouldOverrideDataEnabledSettings(mPhone, apnType);
return (mInternalDataEnabled && mPolicyDataEnabled && mCarrierDataEnabled
- && (userDataEnabled || isDataEnabledOverridden));
+ && mThermalDataEnabled && (userDataEnabled || isDataEnabledOverridden));
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
index 120a3e0b66..3ba577ace9 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -20,6 +20,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -38,7 +39,7 @@ import android.os.PersistableBundle;
import android.os.RegistrantList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.AnomalyReporter;
@@ -50,6 +51,8 @@ import android.telephony.data.DataService;
import android.telephony.data.DataServiceCallback;
import android.telephony.data.IDataService;
import android.telephony.data.IDataServiceCallback;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
import android.text.TextUtils;
import com.android.internal.telephony.Phone;
@@ -88,7 +91,7 @@ public class DataServiceManager extends Handler {
private final CarrierConfigManager mCarrierConfigManager;
private final AppOpsManager mAppOps;
- private final PermissionManager mPermissionManager;
+ private final LegacyPermissionManager mPermissionManager;
private final int mTransportType;
@@ -104,10 +107,25 @@ public class DataServiceManager extends Handler {
private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
+ private final RegistrantList mApnUnthrottledRegistrants = new RegistrantList();
+
private String mTargetBindingPackageName;
private CellularDataServiceConnection mServiceConnection;
+ private final UUID mAnomalyUUID = UUID.fromString("fc1956de-c080-45de-8431-a1faab687110");
+ private String mLastBoundPackageName;
+
+ /**
+ * Helpful for logging
+ * @return the tag name
+ *
+ * @hide
+ */
+ public String getTag() {
+ return mTag;
+ }
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -128,8 +146,10 @@ public class DataServiceManager extends Handler {
@Override
public void binderDied() {
// TODO: try to rebind the service.
- loge("DataService " + mTargetBindingPackageName + ", transport type " + mTransportType
- + " died.");
+ String message = "Data service " + mLastBoundPackageName + " for transport type "
+ + AccessNetworkConstants.transportTypeToString(mTransportType) + " died.";
+ loge(message);
+ AnomalyReporter.reportAnomaly(mAnomalyUUID, message);
}
}
@@ -148,7 +168,9 @@ public class DataServiceManager extends Handler {
});
TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS,
- UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
+ UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
+ mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION,
+ UserHandle.myUserId(), pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
} catch (RuntimeException e) {
loge("Binder to package manager died, permission grant for DataService failed.");
throw e;
@@ -183,6 +205,8 @@ public class DataServiceManager extends Handler {
for (String pkg : dataServices) {
mAppOps.setMode(AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS, UserHandle.myUserId(),
pkg, AppOpsManager.MODE_ERRORED);
+ mAppOps.setMode(AppOpsManager.OPSTR_FINE_LOCATION, UserHandle.myUserId(),
+ pkg, AppOpsManager.MODE_ERRORED);
}
} catch (RuntimeException e) {
loge("Binder to package manager died; failed to revoke DataService permissions.");
@@ -197,18 +221,20 @@ public class DataServiceManager extends Handler {
mIDataService = IDataService.Stub.asInterface(service);
mDeathRecipient = new DataServiceManagerDeathRecipient();
mBound = true;
+ mLastBoundPackageName = getDataServicePackageName();
+ removeMessages(EVENT_WATCHDOG_TIMEOUT);
try {
service.linkToDeath(mDeathRecipient, 0);
mIDataService.createDataServiceProvider(mPhone.getPhoneId());
mIDataService.registerForDataCallListChanged(mPhone.getPhoneId(),
new CellularDataServiceCallback("dataCallListChanged"));
+ mIDataService.registerForUnthrottleApn(mPhone.getPhoneId(),
+ new CellularDataServiceCallback("unthrottleApn"));
} catch (RemoteException e) {
- mDeathRecipient.binderDied();
loge("Remote exception. " + e);
return;
}
- removeMessages(EVENT_WATCHDOG_TIMEOUT);
mServiceBindingChangedRegistrants.notifyResult(true);
}
@Override
@@ -286,6 +312,31 @@ public class DataServiceManager extends Handler {
mDataCallListChangedRegistrants.notifyRegistrants(
new AsyncResult(null, dataCallList, null));
}
+
+ @Override
+ public void onHandoverStarted(@DataServiceCallback.ResultCode int resultCode) {
+ if (DBG) log("onHandoverStarted. resultCode = " + resultCode);
+ removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
+ Message msg = mMessageMap.remove(asBinder());
+ sendCompleteMessage(msg, resultCode);
+ }
+
+ @Override
+ public void onHandoverCancelled(@DataServiceCallback.ResultCode int resultCode) {
+ if (DBG) log("onHandoverCancelled. resultCode = " + resultCode);
+ removeMessages(EVENT_WATCHDOG_TIMEOUT, CellularDataServiceCallback.this);
+ Message msg = mMessageMap.remove(asBinder());
+ sendCompleteMessage(msg, resultCode);
+ }
+
+ public void onApnUnthrottled(String apn) {
+ if (apn != null) {
+ mApnUnthrottledRegistrants.notifyRegistrants(
+ new AsyncResult(null, apn, null));
+ } else {
+ loge("onApnUnthrottled: apn is null");
+ }
+ }
}
/**
@@ -305,8 +356,8 @@ public class DataServiceManager extends Handler {
// NOTE: Do NOT use AppGlobals to retrieve the permission manager; AppGlobals
// caches the service instance, but we need to explicitly request a new service
// so it can be mocked out for tests
- mPermissionManager =
- (PermissionManager) phone.getContext().getSystemService(Context.PERMISSION_SERVICE);
+ mPermissionManager = (LegacyPermissionManager) phone.getContext().getSystemService(
+ Context.LEGACY_PERMISSION_SERVICE);
mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
IntentFilter intentFilter = new IntentFilter();
@@ -450,7 +501,7 @@ public class DataServiceManager extends Handler {
*
* @return package name of the data service package for the the current transportType.
*/
- private String getDataServicePackageName() {
+ public String getDataServicePackageName() {
return getDataServicePackageName(mTransportType);
}
@@ -547,7 +598,7 @@ public class DataServiceManager extends Handler {
return className;
}
- private void sendCompleteMessage(Message msg, int code) {
+ private void sendCompleteMessage(Message msg, @DataServiceCallback.ResultCode int code) {
if (msg != null) {
msg.arg1 = code;
msg.sendToTarget();
@@ -568,15 +619,25 @@ public class DataServiceManager extends Handler {
* {@link DataService#REQUEST_REASON_HANDOVER}.
* @param linkProperties If {@code reason} is {@link DataService#REQUEST_REASON_HANDOVER}, this
* is the link properties of the existing data connection, otherwise null.
+ * @param pduSessionId The pdu session id to be used for this data call. A value of -1 means
+ * no pdu session id was attached to this call.
+ * Reference: 3GPP TS 24.007 Section 11.2.3.1b
+ * @param sliceInfo The slice that represents S-NSSAI.
+ * Reference: 3GPP TS 24.501
+ * @param trafficDescriptor The traffic descriptor for this data call, used for URSP matching.
+ * Reference: 3GPP TS TS 24.526 Section 5.2
+ * @param matchAllRuleAllowed True if using the default match-all URSP rule for this request is
+ * allowed.
* @param onCompleteMessage The result message for this request. Null if the client does not
* care about the result.
*/
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- Message onCompleteMessage) {
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ @Nullable NetworkSliceInfo sliceInfo, @Nullable TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, Message onCompleteMessage) {
if (DBG) log("setupDataCall");
if (!mBound) {
- loge("Data service not bound.");
+ loge("setupDataCall: Data service not bound.");
sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
return;
}
@@ -589,9 +650,10 @@ public class DataServiceManager extends Handler {
sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
REQUEST_UNRESPONDED_TIMEOUT);
mIDataService.setupDataCall(mPhone.getPhoneId(), accessNetworkType, dataProfile,
- isRoaming, allowRoaming, reason, linkProperties, callback);
+ isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
+ trafficDescriptor, matchAllRuleAllowed, callback);
} catch (RemoteException e) {
- loge("Cannot invoke setupDataCall on data service.");
+ loge("setupDataCall: Cannot invoke setupDataCall on data service.");
mMessageMap.remove(callback.asBinder());
sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
}
@@ -635,6 +697,91 @@ public class DataServiceManager extends Handler {
}
/**
+ * Indicates that a handover has begun. This is called on the source transport.
+ *
+ * Any resources being transferred cannot be released while a
+ * handover is underway.
+ *
+ * If a handover was unsuccessful, then the framework calls DataServiceManager#cancelHandover.
+ * The target transport retains ownership over any of the resources being transferred.
+ *
+ * If a handover was successful, the framework calls DataServiceManager#deactivateDataCall with
+ * reason HANDOVER. The target transport now owns the transferred resources and is
+ * responsible for releasing them.
+ *
+ * @param cid The identifier of the data call which is provided in DataCallResponse
+ * @param onCompleteMessage The result callback for this request.
+ */
+ public void startHandover(int cid, @NonNull Message onCompleteMessage) {
+ CellularDataServiceCallback callback =
+ setupCallbackHelper("startHandover", onCompleteMessage);
+ if (callback == null) {
+ loge("startHandover: callback == null");
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ return;
+ }
+
+ try {
+ sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
+ REQUEST_UNRESPONDED_TIMEOUT);
+ mIDataService.startHandover(mPhone.getPhoneId(), cid, callback);
+ } catch (RemoteException e) {
+ loge("Cannot invoke startHandover on data service.");
+ mMessageMap.remove(callback.asBinder());
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ }
+ }
+
+ /**
+ * Indicates that a handover was cancelled after a call to DataServiceManager#startHandover.
+ * This is called on the source transport.
+ *
+ * Since the handover was unsuccessful, the source transport retains ownership over any of
+ * the resources being transferred and is still responsible for releasing them.
+ *
+ * @param cid The identifier of the data call which is provided in DataCallResponse
+ * @param onCompleteMessage The result callback for this request.
+ */
+ public void cancelHandover(int cid, @NonNull Message onCompleteMessage) {
+ CellularDataServiceCallback callback =
+ setupCallbackHelper("cancelHandover", onCompleteMessage);
+ if (callback == null) {
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ return;
+ }
+
+ try {
+ sendMessageDelayed(obtainMessage(EVENT_WATCHDOG_TIMEOUT, callback),
+ REQUEST_UNRESPONDED_TIMEOUT);
+ mIDataService.cancelHandover(mPhone.getPhoneId(), cid, callback);
+ } catch (RemoteException e) {
+ loge("Cannot invoke cancelHandover on data service.");
+ mMessageMap.remove(callback.asBinder());
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ }
+ }
+
+ @Nullable
+ private CellularDataServiceCallback setupCallbackHelper(
+ @NonNull final String operationName, @NonNull final Message onCompleteMessage) {
+ if (DBG) log(operationName);
+ if (!mBound) {
+ sendCompleteMessage(onCompleteMessage, DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE);
+ return null;
+ }
+
+ CellularDataServiceCallback callback =
+ new CellularDataServiceCallback(operationName);
+ if (onCompleteMessage != null) {
+ if (DBG) log(operationName + ": onCompleteMessage set");
+ mMessageMap.put(callback.asBinder(), onCompleteMessage);
+ } else {
+ if (DBG) log(operationName + ": onCompleteMessage not set");
+ }
+ return callback;
+ }
+
+ /**
* Set an APN to initial attach network.
*
* @param dataProfile Data profile used for data call setup. See {@link DataProfile}.
@@ -752,6 +899,29 @@ public class DataServiceManager extends Handler {
}
/**
+ * Register apn unthrottled event
+ *
+ * @param h The target to post the event message to.
+ * @param what The event.
+ */
+ public void registerForApnUnthrottled(Handler h, int what) {
+ if (h != null) {
+ mApnUnthrottledRegistrants.addUnique(h, what, null);
+ }
+ }
+
+ /**
+ * Unregister for apn unthrottled event
+ *
+ * @param h The handler
+ */
+ public void unregisterForApnUnthrottled(Handler h) {
+ if (h != null) {
+ mApnUnthrottledRegistrants.remove(h);
+ }
+ }
+
+ /**
* Register for data service binding status changed event.
*
* @param h The target to post the event message to.
@@ -781,6 +951,7 @@ public class DataServiceManager extends Handler {
*
* @return
*/
+ @TransportType
public int getTransportType() {
return mTransportType;
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
new file mode 100644
index 0000000000..47b7d20090
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation;
+import android.telephony.Annotation.ApnType;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ThrottleStatus;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.RetryManager;
+import com.android.telephony.Rlog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Data throttler tracks the throttling status of the data network and notifies registrants when
+ * there are changes. The throttler is per phone and per transport type.
+ */
+public class DataThrottler extends Handler {
+ private static final String TAG = DataThrottler.class.getSimpleName();
+
+ private static final int EVENT_SET_RETRY_TIME = 1;
+ private static final int EVENT_CARRIER_CONFIG_CHANGED = 2;
+ private static final int EVENT_RESET = 3;
+ private static final int EVENT_AIRPLANE_MODE_CHANGED = 4;
+ private static final int EVENT_TRACING_AREA_CODE_CHANGED = 5;
+
+ private final Phone mPhone;
+ private final int mSlotIndex;
+ private final @AccessNetworkConstants.TransportType int mTransportType;
+ private boolean mResetWhenAreaCodeChanged = false;
+
+ /**
+ * Callbacks that report the apn throttle status.
+ */
+ private final List<DataThrottler.Callback> mCallbacks = new ArrayList<>();
+
+ /**
+ * Keeps track of detailed information of the throttle status that is meant to be
+ * reported to other components.
+ */
+ private final Map<Integer, ThrottleStatus> mThrottleStatus = new ConcurrentHashMap<>();
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+ if (mPhone.getPhoneId() == intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX)) {
+ if (intent.getBooleanExtra(
+ CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+ // Ignore the rebroadcast one to prevent multiple carrier config changed
+ // event during boot up.
+ return;
+ }
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ sendEmptyMessage(EVENT_CARRIER_CONFIG_CHANGED);
+ }
+ }
+ }
+ }
+ };
+
+ public DataThrottler(Phone phone, int transportType) {
+ super(null, false);
+ mPhone = phone;
+ mSlotIndex = phone.getPhoneId();
+ mTransportType = transportType;
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
+
+ mPhone.getServiceStateTracker().registerForAirplaneModeChanged(this,
+ EVENT_AIRPLANE_MODE_CHANGED, null);
+ mPhone.getServiceStateTracker().registerForAreaCodeChanged(this,
+ EVENT_TRACING_AREA_CODE_CHANGED, null);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_SET_RETRY_TIME:
+ int apnTypes = msg.arg1;
+ int newRequestType = msg.arg2;
+ long retryElapsedTime = (long) msg.obj;
+ setRetryTimeInternal(apnTypes, retryElapsedTime, newRequestType);
+ break;
+ case EVENT_CARRIER_CONFIG_CHANGED:
+ onCarrierConfigChanged();
+ break;
+ case EVENT_RESET:
+ resetInternal();
+ break;
+ case EVENT_AIRPLANE_MODE_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (!(Boolean) ar.result) {
+ resetInternal();
+ }
+ break;
+ case EVENT_TRACING_AREA_CODE_CHANGED:
+ if (mResetWhenAreaCodeChanged) {
+ resetInternal();
+ }
+ break;
+ }
+ }
+
+ @NonNull
+ private PersistableBundle getCarrierConfig() {
+ CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ // If an invalid subId is used, this bundle will contain default values.
+ PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+ if (config != null) {
+ return config;
+ }
+ }
+ // Return static default defined in CarrierConfigManager.
+ return CarrierConfigManager.getDefaultConfig();
+ }
+
+ private void onCarrierConfigChanged() {
+ PersistableBundle config = getCarrierConfig();
+ mResetWhenAreaCodeChanged = config.getBoolean(
+ CarrierConfigManager.KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
+ }
+
+ /**
+ * Set the retry time and handover failure mode for the give APN types.
+ *
+ * @param apnTypes APN types
+ * @param retryElapsedTime The elapsed time that data connection for APN types should not be
+ * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist.
+ * {@link RetryManager#NO_RETRY} indicates retry should never happen.
+ */
+ public void setRetryTime(@ApnType int apnTypes, @ElapsedRealtimeLong long retryElapsedTime,
+ @DcTracker.RequestNetworkType int newRequestType) {
+ sendMessage(obtainMessage(EVENT_SET_RETRY_TIME, apnTypes, newRequestType,
+ retryElapsedTime));
+ }
+
+ /**
+ * Set the retry time and handover failure mode for the give APN types. This is running on the
+ * handler thread.
+ *
+ * @param apnTypes APN types
+ * @param retryElapsedTime The elapsed time that data connection for APN types should not be
+ * retried. {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates throttling does not exist.
+ * {@link RetryManager#NO_RETRY} indicates retry should never happen.
+ */
+ private void setRetryTimeInternal(@ApnType int apnTypes, @ElapsedRealtimeLong
+ long retryElapsedTime, @DcTracker.RequestNetworkType int newRequestType) {
+ if (retryElapsedTime < 0) {
+ retryElapsedTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
+ }
+
+ List<ThrottleStatus> changedStatuses = new ArrayList<>();
+ while (apnTypes != 0) {
+
+ //Extract the least significant bit.
+ int apnType = apnTypes & -apnTypes;
+
+ //Update the apn throttle status
+ ThrottleStatus newStatus = createStatus(apnType, retryElapsedTime, newRequestType);
+
+ ThrottleStatus oldStatus = mThrottleStatus.get(apnType);
+
+ //Check to see if there is a change that needs to be applied
+ if (!newStatus.equals(oldStatus)) {
+ //Mark as changed status
+ changedStatuses.add(newStatus);
+
+ //Put the new status in the temp space
+ mThrottleStatus.put(apnType, newStatus);
+ }
+
+ //Remove the least significant bit.
+ apnTypes &= apnTypes - 1;
+ }
+
+ if (changedStatuses.size() > 0) {
+ sendThrottleStatusChanged(changedStatuses);
+ }
+ }
+
+ /**
+ * Get the earliest retry time for the given APN type. The time is the system's elapse time.
+ *
+ * Note the DataThrottler is running phone process's main thread, which is most of the telephony
+ * components running on. Calling this method from other threads might run into race conditions.
+ *
+ * @param apnType APN type
+ * @return The earliest retry time for APN type. The time is the system's elapse time.
+ * {@link RetryManager#NO_SUGGESTED_RETRY_DELAY} indicates there is no throttling for given APN
+ * type, {@link RetryManager#NO_RETRY} indicates retry should never happen.
+ */
+ @ElapsedRealtimeLong
+ public long getRetryTime(@ApnType int apnType) {
+ // This is the workaround to handle the mistake that
+ // ApnSetting.TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI.
+ if (apnType == ApnSetting.TYPE_DEFAULT) {
+ apnType &= ~(ApnSetting.TYPE_HIPRI);
+ }
+
+ ThrottleStatus status = mThrottleStatus.get(apnType);
+ if (status != null) {
+ if (status.getThrottleType() == ThrottleStatus.THROTTLE_TYPE_NONE) {
+ return RetryManager.NO_SUGGESTED_RETRY_DELAY;
+ } else {
+ return status.getThrottleExpiryTimeMillis();
+ }
+ }
+ return RetryManager.NO_SUGGESTED_RETRY_DELAY;
+ }
+
+ /**
+ * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}.
+ */
+ public void reset() {
+ sendEmptyMessage(EVENT_RESET);
+ }
+
+ /**
+ * Resets retry times for all APNs to {@link RetryManager.NO_SUGGESTED_RETRY_DELAY}.
+ */
+ private void resetInternal() {
+ final List<Integer> apnTypes = new ArrayList<>();
+ for (ThrottleStatus throttleStatus : mThrottleStatus.values()) {
+ apnTypes.add(throttleStatus.getApnType());
+ }
+
+ for (int apnType : apnTypes) {
+ setRetryTime(apnType, RetryManager.NO_SUGGESTED_RETRY_DELAY,
+ DcTracker.REQUEST_TYPE_NORMAL);
+ }
+ }
+
+ private ThrottleStatus createStatus(@Annotation.ApnType int apnType, long retryElapsedTime,
+ @DcTracker.RequestNetworkType int newRequestType) {
+ ThrottleStatus.Builder builder = new ThrottleStatus.Builder();
+
+ if (retryElapsedTime == RetryManager.NO_SUGGESTED_RETRY_DELAY) {
+ builder
+ .setNoThrottle()
+ .setRetryType(getRetryType(newRequestType));
+ } else if (retryElapsedTime == RetryManager.NO_RETRY) {
+ builder
+ .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_NONE);
+ } else {
+ builder
+ .setThrottleExpiryTimeMillis(retryElapsedTime)
+ .setRetryType(getRetryType(newRequestType));
+ }
+ return builder
+ .setSlotIndex(mSlotIndex)
+ .setTransportType(mTransportType)
+ .setApnType(apnType)
+ .build();
+ }
+
+ private static int getRetryType(@DcTracker.RequestNetworkType int newRequestType) {
+ if (newRequestType == DcTracker.REQUEST_TYPE_NORMAL) {
+ return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
+ }
+
+ if (newRequestType == DcTracker.REQUEST_TYPE_HANDOVER) {
+ return ThrottleStatus.RETRY_TYPE_HANDOVER;
+ }
+
+ loge("createStatus: Unknown requestType=" + newRequestType);
+ return ThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
+ }
+
+ private void sendThrottleStatusChanged(List<ThrottleStatus> statuses) {
+ synchronized (mCallbacks) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onThrottleStatusChanged(statuses);
+ }
+ }
+ }
+
+ private static void loge(String s) {
+ Rlog.e(TAG, s);
+ }
+
+ /**
+ * Reports changes to apn throttle statuses.
+ *
+ * Note: All statuses are sent when first registered.
+ *
+ * @param callback status changes callback
+ */
+ public void registerForThrottleStatusChanges(DataThrottler.Callback callback) {
+ synchronized (mCallbacks) {
+ //Only add if it's not there already
+ if (!mCallbacks.contains(callback)) {
+ //Report everything the first time
+ List<ThrottleStatus> throttleStatuses =
+ new ArrayList<>(mThrottleStatus.values());
+ callback.onThrottleStatusChanged(throttleStatuses);
+ mCallbacks.add(callback);
+ }
+ }
+ }
+
+ /**
+ * Unregister the callback
+ * @param callback the callback to unregister
+ */
+ public void unregisterForThrottleStatusChanges(DataThrottler.Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ /**
+ * Callback for when throttle statuses change
+ */
+ public interface Callback {
+ /**
+ * Called whenever the throttle status of an APN has changed.
+ *
+ * Note: Called with all statuses when first registered.
+ *
+ * @param throttleStatuses the status changes
+ */
+ void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index f2125d173b..8d60477004 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -17,28 +17,24 @@
package com.android.internal.telephony.dataconnection;
import android.annotation.IntDef;
-import android.content.Context;
import android.hardware.radio.V1_4.DataConnActiveStatus;
import android.net.LinkAddress;
import android.os.AsyncResult;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.RegistrantList;
import android.telephony.AccessNetworkConstants;
import android.telephony.DataFailCause;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult;
-import com.android.internal.telephony.util.HandlerExecutor;
import com.android.internal.telephony.util.TelephonyUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
import com.android.net.module.util.LinkPropertiesUtils;
-import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.NetUtils;
import com.android.telephony.Rlog;
@@ -49,13 +45,14 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Objects;
/**
* Data Connection Controller which is a package visible class and controls
* multiple data connections. For instance listening for unsolicited messages
* and then demultiplexing them to the appropriate DC.
*/
-public class DcController extends StateMachine {
+public class DcController extends Handler {
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -79,6 +76,7 @@ public class DcController extends StateMachine {
private final Phone mPhone;
private final DcTracker mDct;
+ private final String mTag;
private final DataServiceManager mDataServiceManager;
private final DcTesterDeactivateAll mDcTesterDeactivateAll;
@@ -88,16 +86,6 @@ public class DcController extends StateMachine {
// @GuardedBy("mDcListAll")
private final HashMap<Integer, DataConnection> mDcListActiveByCid = new HashMap<>();
- private DccDefaultState mDccDefaultState = new DccDefaultState();
-
- final TelephonyManager mTelephonyManager;
-
- private PhoneStateListener mPhoneStateListener;
-
- //mExecutingCarrierChange tracks whether the phone is currently executing
- //carrier network change
- private volatile boolean mExecutingCarrierChange;
-
/**
* Aggregated physical link state from all data connections. This reflects the device's RRC
* connection state.
@@ -115,50 +103,27 @@ public class DcController extends StateMachine {
* @param phone the phone associated with Dcc and Dct
* @param dct the DataConnectionTracker associated with Dcc
* @param dataServiceManager the data service manager that manages data services
- * @param handler defines the thread/looper to be used with Dcc
+ * @param looper looper for this handler
*/
private DcController(String name, Phone phone, DcTracker dct,
- DataServiceManager dataServiceManager, Handler handler) {
- super(name, handler);
- setLogRecSize(300);
- log("E ctor");
+ DataServiceManager dataServiceManager, Looper looper) {
+ super(looper);
mPhone = phone;
mDct = dct;
+ mTag = name;
mDataServiceManager = dataServiceManager;
- addState(mDccDefaultState);
- setInitialState(mDccDefaultState);
- log("X ctor");
-
- mPhoneStateListener = new PhoneStateListener(new HandlerExecutor(handler)) {
- @Override
- public void onCarrierNetworkChange(boolean active) {
- mExecutingCarrierChange = active;
- }
- };
-
- mTelephonyManager = (TelephonyManager) phone.getContext()
- .getSystemService(Context.TELEPHONY_SERVICE);
mDcTesterDeactivateAll = (TelephonyUtils.IS_DEBUGGABLE)
- ? new DcTesterDeactivateAll(mPhone, DcController.this, getHandler())
+ ? new DcTesterDeactivateAll(mPhone, DcController.this, this)
: null;
-
- if (mTelephonyManager != null) {
- mTelephonyManager.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
- }
+ mDataServiceManager.registerForDataCallListChanged(this,
+ DataConnection.EVENT_DATA_STATE_CHANGED);
}
public static DcController makeDcc(Phone phone, DcTracker dct,
- DataServiceManager dataServiceManager, Handler handler,
+ DataServiceManager dataServiceManager, Looper looper,
String tagSuffix) {
- return new DcController("Dcc" + tagSuffix, phone, dct, dataServiceManager, handler);
- }
-
- void dispose() {
- log("dispose: call quiteNow()");
- if(mTelephonyManager != null) mTelephonyManager.listen(mPhoneStateListener, 0);
- quitNow();
+ return new DcController("Dcc" + tagSuffix, phone, dct, dataServiceManager, looper);
}
void addDc(DataConnection dc) {
@@ -183,7 +148,7 @@ public class DcController extends StateMachine {
}
}
- public DataConnection getActiveDcByCid(int cid) {
+ DataConnection getActiveDcByCid(int cid) {
synchronized (mDcListAll) {
return mDcListActiveByCid.get(cid);
}
@@ -198,280 +163,248 @@ public class DcController extends StateMachine {
}
}
- boolean isExecutingCarrierChange() {
- return mExecutingCarrierChange;
+ boolean isDefaultDataActive() {
+ synchronized (mDcListAll) {
+ return mDcListActiveByCid.values().stream()
+ .anyMatch(dc -> dc.getApnContexts().stream()
+ .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT));
+ }
}
- private class DccDefaultState extends State {
- @Override
- public void enter() {
- if (mPhone != null && mDataServiceManager.getTransportType()
- == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.registerForRilConnected(getHandler(),
- DataConnection.EVENT_RIL_CONNECTED, null);
- }
-
- mDataServiceManager.registerForDataCallListChanged(getHandler(),
- DataConnection.EVENT_DATA_STATE_CHANGED);
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch (msg.what) {
+ case DataConnection.EVENT_DATA_STATE_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ onDataStateChanged((ArrayList<DataCallResponse>) ar.result);
+ } else {
+ log("EVENT_DATA_STATE_CHANGED: exception; likely radio not available, ignore");
+ }
+ break;
+ default:
+ loge("Unexpected event " + msg);
+ break;
}
+ }
- @Override
- public void exit() {
- if (mPhone != null & mDataServiceManager.getTransportType()
- == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mPhone.mCi.unregisterForRilConnected(getHandler());
- }
- mDataServiceManager.unregisterForDataCallListChanged(getHandler());
+ /**
+ * Process the new list of "known" Data Calls
+ * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
+ */
+ private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
+ final HashMap<Integer, DataConnection> dcListActiveByCid;
+ synchronized (mDcListAll) {
+ dcListActiveByCid = new HashMap<>(mDcListActiveByCid);
+ }
- if (mDcTesterDeactivateAll != null) {
- mDcTesterDeactivateAll.dispose();
- }
+ if (DBG) {
+ log("onDataStateChanged: dcsList=" + dcsList
+ + " dcListActiveByCid=" + dcListActiveByCid);
}
- @Override
- public boolean processMessage(Message msg) {
- AsyncResult ar;
-
- switch (msg.what) {
- case DataConnection.EVENT_RIL_CONNECTED:
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- if (DBG) {
- log("DccDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
- ar.result);
- }
- } else {
- log("DccDefaultState: Unexpected exception on EVENT_RIL_CONNECTED");
- }
- break;
+ // Create hashmap of cid to DataCallResponse
+ HashMap<Integer, DataCallResponse> dataCallResponseListByCid =
+ new HashMap<Integer, DataCallResponse>();
+ for (DataCallResponse dcs : dcsList) {
+ dataCallResponseListByCid.put(dcs.getId(), dcs);
+ }
- case DataConnection.EVENT_DATA_STATE_CHANGED:
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- onDataStateChanged((ArrayList<DataCallResponse>)ar.result);
- } else {
- log("DccDefaultState: EVENT_DATA_STATE_CHANGED:" +
- " exception; likely radio not available, ignore");
- }
- break;
+ // Add a DC that is active but not in the
+ // dcsList to the list of DC's to retry
+ ArrayList<DataConnection> dcsToRetry = new ArrayList<DataConnection>();
+ for (DataConnection dc : dcListActiveByCid.values()) {
+ if (dataCallResponseListByCid.get(dc.mCid) == null) {
+ if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
+ dcsToRetry.add(dc);
}
- return HANDLED;
}
+ if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);
- /**
- * Process the new list of "known" Data Calls
- * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED
- */
- private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) {
- final ArrayList<DataConnection> dcListAll;
- final HashMap<Integer, DataConnection> dcListActiveByCid;
- synchronized (mDcListAll) {
- dcListAll = new ArrayList<>(mDcListAll);
- dcListActiveByCid = new HashMap<>(mDcListActiveByCid);
- }
+ // Find which connections have changed state and send a notification or cleanup
+ // and any that are in active need to be retried.
+ ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>();
- if (DBG) {
- lr("onDataStateChanged: dcsList=" + dcsList
- + " dcListActiveByCid=" + dcListActiveByCid);
- }
- if (VDBG) {
- log("onDataStateChanged: mDcListAll=" + dcListAll);
- }
+ boolean isAnyDataCallDormant = false;
+ boolean isAnyDataCallActive = false;
- // Create hashmap of cid to DataCallResponse
- HashMap<Integer, DataCallResponse> dataCallResponseListByCid =
- new HashMap<Integer, DataCallResponse>();
- for (DataCallResponse dcs : dcsList) {
- dataCallResponseListByCid.put(dcs.getId(), dcs);
- }
+ for (DataCallResponse newState : dcsList) {
- // Add a DC that is active but not in the
- // dcsList to the list of DC's to retry
- ArrayList<DataConnection> dcsToRetry = new ArrayList<DataConnection>();
- for (DataConnection dc : dcListActiveByCid.values()) {
- if (dataCallResponseListByCid.get(dc.mCid) == null) {
- if (DBG) log("onDataStateChanged: add to retry dc=" + dc);
- dcsToRetry.add(dc);
- }
+ DataConnection dc = dcListActiveByCid.get(newState.getId());
+ if (dc == null) {
+ // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
+ loge("onDataStateChanged: no associated DC yet, ignore");
+ continue;
}
- if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry);
-
- // Find which connections have changed state and send a notification or cleanup
- // and any that are in active need to be retried.
- ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>();
-
- boolean isAnyDataCallDormant = false;
- boolean isAnyDataCallActive = false;
-
- for (DataCallResponse newState : dcsList) {
- DataConnection dc = dcListActiveByCid.get(newState.getId());
- if (dc == null) {
- // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed.
- loge("onDataStateChanged: no associated DC yet, ignore");
- continue;
+ List<ApnContext> apnContexts = dc.getApnContexts();
+ if (apnContexts.size() == 0) {
+ if (DBG) loge("onDataStateChanged: no connected apns, ignore");
+ } else {
+ // Determine if the connection/apnContext should be cleaned up
+ // or just a notification should be sent out.
+ if (DBG) {
+ log("onDataStateChanged: Found ConnId=" + newState.getId()
+ + " newState=" + newState.toString());
}
-
- List<ApnContext> apnContexts = dc.getApnContexts();
- if (apnContexts.size() == 0) {
- if (DBG) loge("onDataStateChanged: no connected apns, ignore");
- } else {
- // Determine if the connection/apnContext should be cleaned up
- // or just a notification should be sent out.
- if (DBG) {
- log("onDataStateChanged: Found ConnId=" + newState.getId()
- + " newState=" + newState.toString());
- }
- if (newState.getLinkStatus() == DataConnActiveStatus.INACTIVE) {
- if (mDct.isCleanupRequired.get()) {
+ if (newState.getLinkStatus() == DataConnActiveStatus.INACTIVE) {
+ if (mDct.isCleanupRequired.get()) {
+ apnsToCleanup.addAll(apnContexts);
+ mDct.isCleanupRequired.set(false);
+ } else {
+ int failCause = DataFailCause.getFailCause(newState.getCause());
+ if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), failCause,
+ mPhone.getSubId())) {
+ if (DBG) {
+ log("onDataStateChanged: X restart radio, failCause="
+ + failCause);
+ }
+ mDct.sendRestartRadio();
+ } else if (mDct.isPermanentFailure(failCause)) {
+ if (DBG) {
+ log("onDataStateChanged: inactive, add to cleanup list. "
+ + "failCause=" + failCause);
+ }
apnsToCleanup.addAll(apnContexts);
- mDct.isCleanupRequired.set(false);
} else {
- int failCause = DataFailCause.getFailCause(newState.getCause());
- if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), failCause,
- mPhone.getSubId())) {
- if (DBG) {
- log("onDataStateChanged: X restart radio, failCause="
- + failCause);
- }
- mDct.sendRestartRadio();
- } else if (mDct.isPermanentFailure(failCause)) {
- if (DBG) {
- log("onDataStateChanged: inactive, add to cleanup list. "
- + "failCause=" + failCause);
- }
- apnsToCleanup.addAll(apnContexts);
- } else {
- if (DBG) {
- log("onDataStateChanged: inactive, add to retry list. "
- + "failCause=" + failCause);
- }
- dcsToRetry.add(dc);
+ if (DBG) {
+ log("onDataStateChanged: inactive, add to retry list. "
+ + "failCause=" + failCause);
}
+ dcsToRetry.add(dc);
}
- } else {
- // Its active so update the DataConnections link properties
- UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
- if (result.oldLp.equals(result.newLp)) {
- if (DBG) log("onDataStateChanged: no change");
- } else {
- if (LinkPropertiesUtils.isIdenticalInterfaceName(
- result.oldLp, result.newLp)) {
- if (!LinkPropertiesUtils.isIdenticalDnses(
- result.oldLp, result.newLp)
- || !LinkPropertiesUtils.isIdenticalRoutes(
- result.oldLp, result.newLp)
- || !LinkPropertiesUtils.isIdenticalHttpProxy(
- result.oldLp, result.newLp)
- || !LinkPropertiesUtils.isIdenticalAddresses(
- result.oldLp, result.newLp)) {
- // If the same address type was removed and
- // added we need to cleanup
- CompareResult<LinkAddress> car =
- LinkPropertiesUtils.compareAddresses(result.oldLp,
- result.newLp);
- if (DBG) {
- log("onDataStateChanged: oldLp=" + result.oldLp +
- " newLp=" + result.newLp + " car=" + car);
- }
- boolean needToClean = false;
- for (LinkAddress added : car.added) {
- for (LinkAddress removed : car.removed) {
- if (NetUtils.addressTypeMatches(
- removed.getAddress(),
- added.getAddress())) {
- needToClean = true;
- break;
- }
- }
- }
- if (needToClean) {
- if (DBG) {
- log("onDataStateChanged: addr change,"
- + " cleanup apns=" + apnContexts
- + " oldLp=" + result.oldLp
- + " newLp=" + result.newLp);
- }
- apnsToCleanup.addAll(apnContexts);
- } else {
- if (DBG) log("onDataStateChanged: simple change");
+ }
+ } else {
+ // Update the pdu session id
+ dc.setPduSessionId(newState.getPduSessionId());
- for (ApnContext apnContext : apnContexts) {
- mPhone.notifyDataConnection(apnContext.getApnType());
+ dc.updatePcscfAddr(newState);
+
+ // Its active so update the DataConnections link properties
+ UpdateLinkPropertyResult result = dc.updateLinkProperty(newState);
+ dc.updateResponseFields(newState);
+ if (result.oldLp.equals(result.newLp)) {
+ if (DBG) log("onDataStateChanged: no change");
+ } else {
+ if (LinkPropertiesUtils.isIdenticalInterfaceName(
+ result.oldLp, result.newLp)) {
+ if (!LinkPropertiesUtils.isIdenticalDnses(
+ result.oldLp, result.newLp)
+ || !LinkPropertiesUtils.isIdenticalRoutes(
+ result.oldLp, result.newLp)
+ || !LinkPropertiesUtils.isIdenticalHttpProxy(
+ result.oldLp, result.newLp)
+ || !LinkPropertiesUtils.isIdenticalAddresses(
+ result.oldLp, result.newLp)) {
+ // If the same address type was removed and
+ // added we need to cleanup
+ CompareOrUpdateResult<Integer, LinkAddress> car =
+ new CompareOrUpdateResult(
+ result.oldLp != null
+ ? result.oldLp.getLinkAddresses() : null,
+ result.newLp != null
+ ? result.newLp.getLinkAddresses() : null,
+ (la) -> Objects.hash(((LinkAddress) la)
+ .getAddress(),
+ ((LinkAddress) la).getPrefixLength(),
+ ((LinkAddress) la).getScope()));
+ if (DBG) {
+ log("onDataStateChanged: oldLp=" + result.oldLp
+ + " newLp=" + result.newLp + " car=" + car);
+ }
+ boolean needToClean = false;
+ for (LinkAddress added : car.added) {
+ for (LinkAddress removed : car.removed) {
+ if (NetUtils.addressTypeMatches(
+ removed.getAddress(),
+ added.getAddress())) {
+ needToClean = true;
+ break;
}
}
- } else {
+ }
+ if (needToClean) {
if (DBG) {
- log("onDataStateChanged: no changes");
+ log("onDataStateChanged: addr change,"
+ + " cleanup apns=" + apnContexts
+ + " oldLp=" + result.oldLp
+ + " newLp=" + result.newLp);
}
+ apnsToCleanup.addAll(apnContexts);
}
} else {
- apnsToCleanup.addAll(apnContexts);
if (DBG) {
- log("onDataStateChanged: interface change, cleanup apns="
- + apnContexts);
+ log("onDataStateChanged: no changes");
}
}
+ } else {
+ apnsToCleanup.addAll(apnContexts);
+ if (DBG) {
+ log("onDataStateChanged: interface change, cleanup apns="
+ + apnContexts);
+ }
}
}
}
+ }
- if (newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) {
- isAnyDataCallActive = true;
- }
- if (newState.getLinkStatus() == DataConnActiveStatus.DORMANT) {
- isAnyDataCallDormant = true;
- }
+ if (newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) {
+ isAnyDataCallActive = true;
+ }
+ if (newState.getLinkStatus() == DataConnActiveStatus.DORMANT) {
+ isAnyDataCallDormant = true;
}
+ }
- if (mDataServiceManager.getTransportType()
- == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- int physicalLinkState = isAnyDataCallActive
- ? PHYSICAL_LINK_ACTIVE : PHYSICAL_LINK_NOT_ACTIVE;
- if (mPhysicalLinkState != physicalLinkState) {
- mPhysicalLinkState = physicalLinkState;
- mPhysicalLinkStateChangedRegistrants.notifyResult(mPhysicalLinkState);
+ if (mDataServiceManager.getTransportType()
+ == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ int physicalLinkState = isAnyDataCallActive
+ ? PHYSICAL_LINK_ACTIVE : PHYSICAL_LINK_NOT_ACTIVE;
+ if (mPhysicalLinkState != physicalLinkState) {
+ mPhysicalLinkState = physicalLinkState;
+ mPhysicalLinkStateChangedRegistrants.notifyResult(mPhysicalLinkState);
+ }
+ if (isAnyDataCallDormant && !isAnyDataCallActive) {
+ // There is no way to indicate link activity per APN right now. So
+ // Link Activity will be considered dormant only when all data calls
+ // are dormant.
+ // If a single data call is in dormant state and none of the data
+ // calls are active broadcast overall link state as dormant.
+ if (DBG) {
+ log("onDataStateChanged: Data activity DORMANT. stopNetStatePoll");
}
- if (isAnyDataCallDormant && !isAnyDataCallActive) {
- // There is no way to indicate link activity per APN right now. So
- // Link Activity will be considered dormant only when all data calls
- // are dormant.
- // If a single data call is in dormant state and none of the data
- // calls are active broadcast overall link state as dormant.
- if (DBG) {
- log("onDataStateChanged: Data activity DORMANT. stopNetStatePoll");
- }
- mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
- } else {
- if (DBG) {
- log("onDataStateChanged: Data Activity updated to NONE. "
- + "isAnyDataCallActive = " + isAnyDataCallActive
- + " isAnyDataCallDormant = " + isAnyDataCallDormant);
- }
- if (isAnyDataCallActive) {
- mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
- }
+ mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT);
+ } else {
+ if (DBG) {
+ log("onDataStateChanged: Data Activity updated to NONE. "
+ + "isAnyDataCallActive = " + isAnyDataCallActive
+ + " isAnyDataCallDormant = " + isAnyDataCallDormant);
+ }
+ if (isAnyDataCallActive) {
+ mDct.sendStartNetStatPoll(DctConstants.Activity.NONE);
}
}
+ }
- if (DBG) {
- lr("onDataStateChanged: dcsToRetry=" + dcsToRetry
- + " apnsToCleanup=" + apnsToCleanup);
- }
-
- // Cleanup connections that have changed
- for (ApnContext apnContext : apnsToCleanup) {
- mDct.cleanUpConnection(apnContext);
- }
+ if (DBG) {
+ log("onDataStateChanged: dcsToRetry=" + dcsToRetry
+ + " apnsToCleanup=" + apnsToCleanup);
+ }
- // Retry connections that have disappeared
- for (DataConnection dc : dcsToRetry) {
- if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag);
- dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
- }
+ // Cleanup connections that have changed
+ for (ApnContext apnContext : apnsToCleanup) {
+ mDct.cleanUpConnection(apnContext);
+ }
- if (VDBG) log("onDataStateChanged: X");
+ // Retry connections that have disappeared
+ for (DataConnection dc : dcsToRetry) {
+ if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag);
+ dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag);
}
+
+ if (VDBG) log("onDataStateChanged: X");
}
/**
@@ -480,7 +413,7 @@ public class DcController extends StateMachine {
* @param h The handler
* @param what The event
*/
- public void registerForPhysicalLinkStateChanged(Handler h, int what) {
+ void registerForPhysicalLinkStateChanged(Handler h, int what) {
mPhysicalLinkStateChangedRegistrants.addUnique(h, what, null);
}
@@ -489,36 +422,16 @@ public class DcController extends StateMachine {
*
* @param h The previously registered handler
*/
- public void unregisterForPhysicalLinkStateChanged(Handler h) {
+ void unregisterForPhysicalLinkStateChanged(Handler h) {
mPhysicalLinkStateChangedRegistrants.remove(h);
}
- /**
- * lr is short name for logAndAddLogRec
- * @param s
- */
- private void lr(String s) {
- logAndAddLogRec(s);
- }
-
- @Override
- protected void log(String s) {
- Rlog.d(getName(), s);
- }
-
- @Override
- protected void loge(String s) {
- Rlog.e(getName(), s);
+ private void log(String s) {
+ Rlog.d(mTag, s);
}
- /**
- * @return the string for msg.what as our info.
- */
- @Override
- protected String getWhatToString(int what) {
- String info = null;
- info = DataConnection.cmdToString(what);
- return info;
+ private void loge(String s) {
+ Rlog.e(mTag, s);
}
@Override
@@ -528,9 +441,7 @@ public class DcController extends StateMachine {
}
}
- @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
pw.println(" mPhone=" + mPhone);
synchronized (mDcListAll) {
pw.println(" mDcListAll=" + mDcListAll);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java b/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
index 420340d50e..3cdd2090d5 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
@@ -48,8 +48,8 @@ public class DcFailBringUp {
// suggestedRetryTime with its --ei option name and default value
static final String SUGGESTED_RETRY_TIME = "suggested_retry_time";
- static final int DEFAULT_SUGGESTED_RETRY_TIME = -1;
- int mSuggestedRetryTime;
+ static final long DEFAULT_SUGGESTED_RETRY_TIME = -1;
+ long mSuggestedRetryTime;
// Get the Extra Intent parameters
void saveParameters(Intent intent, String s) {
@@ -58,14 +58,14 @@ public class DcFailBringUp {
mFailCause = DataFailCause.getFailCause(
intent.getIntExtra(FAIL_CAUSE, DEFAULT_FAIL_CAUSE));
mSuggestedRetryTime =
- intent.getIntExtra(SUGGESTED_RETRY_TIME, DEFAULT_SUGGESTED_RETRY_TIME);
+ intent.getLongExtra(SUGGESTED_RETRY_TIME, DEFAULT_SUGGESTED_RETRY_TIME);
if (DBG) {
log(s + ".saveParameters: " + this);
}
}
public void saveParameters(int counter, @DataFailureCause int failCause,
- int suggestedRetryTime) {
+ long suggestedRetryTime) {
mCounter = counter;
mFailCause = DataFailCause.getFailCause(failCause);
mSuggestedRetryTime = suggestedRetryTime;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
index 7fa1f97e8d..ab04bc1302 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
@@ -17,38 +17,51 @@
package com.android.internal.telephony.dataconnection;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.KeepalivePacketData;
import android.net.LinkProperties;
import android.net.NattKeepalivePacketData;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
import android.net.NetworkProvider;
+import android.net.QosFilter;
+import android.net.QosSessionAttributes;
import android.net.SocketKeepalive;
import android.net.Uri;
import android.os.Message;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.NetworkType;
import android.telephony.AnomalyReporter;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
-import android.text.TextUtils;
+import android.telephony.data.NrQosSessionAttributes;
+import android.telephony.data.QosBearerSession;
+import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.SparseArray;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.SlidingWindowEventCounter;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.net.InetAddress;
import java.time.Duration;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.UUID;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
/**
* This class represents a network agent which is communication channel between
@@ -63,7 +76,9 @@ import java.util.UUID;
public class DcNetworkAgent extends NetworkAgent {
private final String mTag;
- private Phone mPhone;
+ private final int mId;
+
+ private final Phone mPhone;
private int mTransportType;
@@ -71,49 +86,70 @@ public class DcNetworkAgent extends NetworkAgent {
public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();
+ private final QosCallbackTracker mQosCallbackTracker;
+
+ private final Executor mQosCallbackExecutor = Executors.newSingleThreadExecutor();
+
private DataConnection mDataConnection;
private final LocalLog mNetCapsLocalLog = new LocalLog(50);
- private NetworkInfo mNetworkInfo;
+ // For interface duplicate detection. Key is the net id, value is the interface name in string.
+ private static Map<Integer, String> sInterfaceNames = new ArrayMap<>();
- // For debugging duplicate interface issue. Remove before R released.
- private static Set<String> sInterfaceNames = new HashSet<>();
+ private static final long NETWORK_UNWANTED_ANOMALY_WINDOW_MS = TimeUnit.MINUTES.toMillis(5);
+ private static final int NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES = 12;
- DcNetworkAgent(DataConnection dc, Phone phone, NetworkInfo ni, int score,
- NetworkAgentConfig config, NetworkProvider networkProvider, int transportType) {
+ DcNetworkAgent(DataConnection dc, Phone phone, int score, NetworkAgentConfig config,
+ NetworkProvider networkProvider, int transportType) {
super(phone.getContext(), dc.getHandler().getLooper(), "DcNetworkAgent",
dc.getNetworkCapabilities(), dc.getLinkProperties(), score, config,
networkProvider);
register();
- mTag = "DcNetworkAgent" + "-" + getNetwork().netId;
+ mId = getNetwork().getNetId();
+ mTag = "DcNetworkAgent" + "-" + mId;
mPhone = phone;
mNetworkCapabilities = dc.getNetworkCapabilities();
mTransportType = transportType;
mDataConnection = dc;
- mNetworkInfo = new NetworkInfo(ni);
- setLegacySubtype(ni.getSubtype(), ni.getSubtypeName());
- setLegacyExtraInfo(ni.getExtraInfo());
- // TODO: Remove before R is released.
- if (dc.getLinkProperties() != null
- && !TextUtils.isEmpty(dc.getLinkProperties().getInterfaceName())) {
- checkDuplicateInterface(dc.getLinkProperties().getInterfaceName());
+ if (dc.getLinkProperties() != null) {
+ checkDuplicateInterface(mId, dc.getLinkProperties().getInterfaceName());
+ logd("created for data connection " + dc.getName() + ", "
+ + dc.getLinkProperties().getInterfaceName());
+ } else {
+ loge("The connection does not have a valid link properties.");
}
- logd(mTag + " created for data connection " + dc.getName());
+ mQosCallbackTracker = new QosCallbackTracker(this);
}
- // This is a temp code to catch the duplicate network interface issue.
- // TODO: Remove before R is released.
- private void checkDuplicateInterface(String interfaceName) {
- if (sInterfaceNames.contains(interfaceName)) {
- String message = "Duplicate interface " + interfaceName + " is detected.";
- log(message);
- // Using fixed UUID to avoid duplicate bugreport notification
- AnomalyReporter.reportAnomaly(
- UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"),
- message);
+ private @NetworkType int getNetworkType() {
+ ServiceState ss = mPhone.getServiceState();
+ int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+ NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, mTransportType);
+ if (nri != null) {
+ networkType = nri.getAccessNetworkTechnology();
+ }
+
+ return networkType;
+ }
+
+ private void checkDuplicateInterface(int netId, @Nullable String interfaceName) {
+ for (Map.Entry<Integer, String> entry: sInterfaceNames.entrySet()) {
+ if (Objects.equals(interfaceName, entry.getValue())) {
+ String message = "Duplicate interface " + interfaceName
+ + " is detected. DcNetworkAgent-" + entry.getKey()
+ + " already used this interface name.";
+ loge(message);
+ // Using fixed UUID to avoid duplicate bugreport notification
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("02f3d3f6-4613-4415-b6cb-8d92c8a938a6"),
+ message);
+ return;
+ }
}
- sInterfaceNames.add(interfaceName);
+ sInterfaceNames.put(netId, interfaceName);
}
/**
@@ -144,7 +180,7 @@ public class DcNetworkAgent extends NetworkAgent {
loge("releaseOwnership called on no-owner DcNetworkAgent!");
return;
} else if (mDataConnection != dc) {
- log("releaseOwnership: This agent belongs to "
+ loge("releaseOwnership: This agent belongs to "
+ mDataConnection.getName() + ", ignored the request from " + dc.getName());
return;
}
@@ -159,8 +195,13 @@ public class DcNetworkAgent extends NetworkAgent {
return mDataConnection;
}
+ private static final SlidingWindowEventCounter sNetworkUnwantedCounter =
+ new SlidingWindowEventCounter(NETWORK_UNWANTED_ANOMALY_WINDOW_MS,
+ NETWORK_UNWANTED_ANOMALY_NUM_OCCURRENCES);
+
@Override
public synchronized void onNetworkUnwanted() {
+ trackNetworkUnwanted();
if (mDataConnection == null) {
loge("onNetworkUnwanted found called on no-owner DcNetworkAgent!");
return;
@@ -172,6 +213,27 @@ public class DcNetworkAgent extends NetworkAgent {
DcTracker.RELEASE_TYPE_DETACH, null);
}
+ /**
+ * There have been several bugs where a RECONNECT loop kicks off where a DataConnection
+ * connects to the Network, ConnectivityService indicates that the Network is unwanted,
+ * and then the DataConnection reconnects. By the time we get the bug report it's too late
+ * because there have already been hundreds of RECONNECTS. This is meant to capture the issue
+ * when it first starts.
+ *
+ * The unwanted counter is configured to only take an anomaly report in extreme cases.
+ * This is to avoid having the anomaly message show up on several devices.
+ *
+ * This is directly related to b/175845538. But, there have been several other occurrences of
+ * this issue.
+ */
+ private void trackNetworkUnwanted() {
+ if (sNetworkUnwantedCounter.addOccurrence()) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("3f578b5c-64e9-11eb-ae93-0242ac130002"),
+ "Network Unwanted called 12 times in 5 minutes.");
+ }
+ }
+
@Override
public synchronized void onBandwidthUpdateRequested() {
if (mDataConnection == null) {
@@ -215,6 +277,18 @@ public class DcNetworkAgent extends NetworkAgent {
}
/**
+ * Update the legacy sub type (i.e. data network type).
+ *
+ * @param dc The data connection that invokes this method.
+ */
+ public synchronized void updateLegacySubtype(DataConnection dc) {
+ if (!isOwned(dc, "updateLegacySubtype")) return;
+
+ int networkType = getNetworkType();
+ setLegacySubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
+ }
+
+ /**
* Set the network capabilities.
*
* @param networkCapabilities The network capabilities.
@@ -253,9 +327,11 @@ public class DcNetworkAgent extends NetworkAgent {
* @param linkProperties The link properties
* @param dc The data connection that invokes this method.
*/
- public synchronized void sendLinkProperties(LinkProperties linkProperties,
+ public synchronized void sendLinkProperties(@NonNull LinkProperties linkProperties,
DataConnection dc) {
if (!isOwned(dc, "sendLinkProperties")) return;
+
+ sInterfaceNames.put(mId, dc.getLinkProperties().getInterfaceName());
sendLinkProperties(linkProperties);
}
@@ -278,47 +354,11 @@ public class DcNetworkAgent extends NetworkAgent {
public synchronized void unregister(DataConnection dc) {
if (!isOwned(dc, "unregister")) return;
- if (dc.getLinkProperties() != null
- && !TextUtils.isEmpty(dc.getLinkProperties().getInterfaceName())) {
- sInterfaceNames.remove(dc.getLinkProperties().getInterfaceName());
- }
- logd("Unregister from connectivity service");
+ logd("Unregister from connectivity service. " + sInterfaceNames.get(mId) + " removed.");
+ sInterfaceNames.remove(mId);
super.unregister();
}
- /**
- * Set the network info.
- *
- * @param networkInfo The network info.
- * @param dc The data connection that invokes this method.
- */
- public synchronized void sendNetworkInfo(NetworkInfo networkInfo, DataConnection dc) {
- if (!isOwned(dc, "sendNetworkInfo")) return;
- final NetworkInfo.State oldState = mNetworkInfo.getState();
- final NetworkInfo.State state = networkInfo.getState();
- if (mNetworkInfo.getExtraInfo() != networkInfo.getExtraInfo()) {
- setLegacyExtraInfo(networkInfo.getExtraInfo());
- }
- int subType = networkInfo.getSubtype();
- if (mNetworkInfo.getSubtype() != subType) {
- setLegacySubtype(subType, TelephonyManager.getNetworkTypeName(subType));
- }
- if ((oldState == NetworkInfo.State.SUSPENDED || oldState == NetworkInfo.State.CONNECTED)
- && state == NetworkInfo.State.DISCONNECTED) {
- unregister(dc);
- }
- mNetworkInfo = new NetworkInfo(networkInfo);
- }
-
- /**
- * Get the latest sent network info.
- *
- * @return network info
- */
- public synchronized NetworkInfo getNetworkInfo() {
- return mNetworkInfo;
- }
-
@Override
public synchronized void onStartSocketKeepalive(int slot, @NonNull Duration interval,
@NonNull KeepalivePacketData packet) {
@@ -347,12 +387,51 @@ public class DcNetworkAgent extends NetworkAgent {
}
@Override
+ public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
+ mQosCallbackExecutor.execute(() -> mQosCallbackTracker.addFilter(qosCallbackId,
+ new QosCallbackTracker.IFilter() {
+ @Override
+ public boolean matchesLocalAddress(
+ InetAddress address, int startPort, int endPort) {
+ return filter.matchesLocalAddress(address, startPort, endPort);
+ }
+
+ @Override
+ public boolean matchesRemoteAddress(
+ InetAddress address, int startPort, int endPort) {
+ return filter.matchesRemoteAddress(address, startPort, endPort);
+ }
+ }));
+ }
+
+ @Override
+ public void onQosCallbackUnregistered(final int qosCallbackId) {
+ mQosCallbackExecutor.execute(() -> mQosCallbackTracker.removeFilter(qosCallbackId));
+ }
+
+ void updateQosBearerSessions(final List<QosBearerSession> qosBearerSessions) {
+ mQosCallbackExecutor.execute(() -> mQosCallbackTracker.updateSessions(qosBearerSessions));
+ }
+
+ public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId,
+ @NonNull final QosSessionAttributes attributes) {
+ super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
+ }
+
+ public void notifyQosSessionLost(final int qosCallbackId,
+ final int sessionId, final int qosSessionType) {
+ super.sendQosSessionLost(qosCallbackId, sessionId, qosSessionType);
+ }
+
+ @Override
public String toString() {
- return "DcNetworkAgent:"
+ return "DcNetworkAgent-"
+ + mId
+ " mDataConnection="
+ ((mDataConnection != null) ? mDataConnection.getName() : null)
+ " mTransportType="
+ AccessNetworkConstants.transportTypeToString(mTransportType)
+ + " " + ((mDataConnection != null) ? mDataConnection.getLinkProperties() : null)
+ " mNetworkCapabilities=" + mNetworkCapabilities;
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 7b1560ffb7..3d83934b74 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -17,11 +17,13 @@
package com.android.internal.telephony.dataconnection;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
import static android.telephony.TelephonyManager.NETWORK_TYPE_NR;
-import static android.telephony.data.ApnSetting.PROTOCOL_IPV4V6;
-import static android.telephony.data.ApnSetting.TYPE_DEFAULT;
-import static android.telephony.data.ApnSetting.TYPE_IA;
+import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_DO_FALLBACK;
+import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY;
+import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL;
import static com.android.internal.telephony.RILConstants.DATA_PROFILE_DEFAULT;
import static com.android.internal.telephony.RILConstants.DATA_PROFILE_INVALID;
@@ -30,6 +32,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
@@ -49,7 +53,6 @@ import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
-import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.AsyncResult;
@@ -61,6 +64,7 @@ import android.os.PersistableBundle;
import android.os.RegistrantList;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -70,6 +74,7 @@ import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.Annotation.NetworkType;
+import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
import android.telephony.DataFailCause;
@@ -86,15 +91,19 @@ import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.SimState;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataCallResponse.HandoverFailureMode;
import android.telephony.data.DataProfile;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.LocalLog;
+import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.EventLogTags;
@@ -105,13 +114,16 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.PhoneSwitcher;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.SettingsObserver;
import com.android.internal.telephony.SubscriptionInfoUpdater;
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
+import com.android.internal.telephony.metrics.DataStallRecoveryStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.util.ArrayUtils;
+import com.android.internal.telephony.util.NotificationChannelController;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.AsyncChannel;
import com.android.telephony.Rlog;
@@ -123,12 +135,13 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Comparator;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -142,6 +155,7 @@ public class DcTracker extends Handler {
private static final boolean VDBG = false; // STOPSHIP if true
private static final boolean VDBG_STALL = false; // STOPSHIP if true
private static final boolean RADIO_TESTS = false;
+ private static final String NOTIFICATION_TAG = DcTracker.class.getSimpleName();
@IntDef(value = {
REQUEST_TYPE_NORMAL,
@@ -196,10 +210,9 @@ public class DcTracker extends Handler {
*/
public static final int RELEASE_TYPE_HANDOVER = 3;
- /** The extras for request network completion message */
+ /** The extras for handover completion message */
static final String DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST = "extra_network_request";
static final String DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE = "extra_transport_type";
- static final String DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE = "extra_request_type";
static final String DATA_COMPLETE_MSG_EXTRA_SUCCESS = "extra_success";
/**
* The flag indicates whether after handover failure, the data connection should remain on the
@@ -209,7 +222,6 @@ public class DcTracker extends Handler {
"extra_handover_failure_fallback";
private final String mLogTag;
- private final String mLogTagSuffix;
public AtomicBoolean isCleanupRequired = new AtomicBoolean(false);
@@ -249,6 +261,9 @@ public class DcTracker extends Handler {
private static final String INTENT_DATA_STALL_ALARM_EXTRA_TRANSPORT_TYPE =
"data_stall_alarm_extra_transport_type";
+ // Unique id for no data notification on setup data permanently failed.
+ private static final int NO_DATA_NOTIFICATION = 1001;
+
/** The higher index has higher priority. */
private static final DctConstants.State[] DATA_CONNECTION_STATE_PRIORITIES = {
DctConstants.State.IDLE,
@@ -290,6 +305,16 @@ public class DcTracker extends Handler {
/* Indicating data service is bound or not */
private boolean mDataServiceBound = false;
+ /* Intent to hide/show the sign-in error notification for provisioning */
+ private static final String INTENT_PROVISION = "com.android.internal.telephony.PROVISION";
+
+ /**
+ * Extra containing the phone ID for INTENT_PROVISION
+ * Must be kept consistent with NetworkNotificationManager#setProvNotificationVisible.
+ * TODO: refactor the deprecated API to prevent hardcoding values.
+ */
+ private static final String EXTRA_PROVISION_PHONE_ID = "provision.phone.id";
+
/* Intent for the provisioning apn alarm */
private static final String INTENT_PROVISIONING_APN_ALARM =
"com.android.internal.telephony.provisioning_apn_alarm";
@@ -325,10 +350,23 @@ public class DcTracker extends Handler {
private boolean mNrSaAllUnmetered = false;
private boolean mNrSaMmwaveUnmetered = false;
private boolean mNrSaSub6Unmetered = false;
- private boolean mRoamingUnmetered = false;
+ private boolean mNrNsaRoamingUnmetered = false;
+
+ // stats per data call recovery event
+ private DataStallRecoveryStats mDataStallRecoveryStats;
- /* List of SubscriptionPlans, updated on SubscriptionManager.setSubscriptionPlans */
+ /* List of SubscriptionPlans, updated when initialized and when plans are changed. */
private List<SubscriptionPlan> mSubscriptionPlans = null;
+ /* List of network types an unmetered override applies to, set by onSubscriptionOverride
+ * and cleared when the device is rebooted or the override expires. */
+ private List<Integer> mUnmeteredNetworkTypes = null;
+ /* List of network types a congested override applies to, set by onSubscriptionOverride
+ * and cleared when the device is rebooted or the override expires. */
+ private List<Integer> mCongestedNetworkTypes = null;
+ /* Whether an unmetered override is currently active. */
+ private boolean mUnmeteredOverride = false;
+ /* Whether a congested override is currently active. */
+ private boolean mCongestedOverride = false;
@SimState
private int mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
@@ -409,11 +447,26 @@ public class DcTracker extends Handler {
private final NetworkPolicyManager.SubscriptionCallback mSubscriptionCallback =
new NetworkPolicyManager.SubscriptionCallback() {
@Override
- public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue) {
+ public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue,
+ int[] networkTypes) {
if (mPhone == null || mPhone.getSubId() != subId) return;
- for (DataConnection dataConnection : mDataConnections.values()) {
- dataConnection.onSubscriptionOverride(overrideMask, overrideValue);
+ List<Integer> tempList = new ArrayList<>();
+ for (int networkType : networkTypes) {
+ tempList.add(networkType);
+ }
+
+ log("Subscription override: overrideMask=" + overrideMask
+ + ", overrideValue=" + overrideValue + ", networkTypes=" + tempList);
+
+ if (overrideMask == SUBSCRIPTION_OVERRIDE_UNMETERED) {
+ mUnmeteredNetworkTypes = tempList;
+ mUnmeteredOverride = overrideValue != 0;
+ reevaluateUnmeteredConnections();
+ } else if (overrideMask == SUBSCRIPTION_OVERRIDE_CONGESTED) {
+ mCongestedNetworkTypes = tempList;
+ mCongestedOverride = overrideValue != 0;
+ reevaluateCongestedConnections();
}
}
@@ -422,6 +475,7 @@ public class DcTracker extends Handler {
if (mPhone == null || mPhone.getSubId() != subId) return;
mSubscriptionPlans = plans == null ? null : Arrays.asList(plans);
+ if (DBG) log("SubscriptionPlans changed: " + mSubscriptionPlans);
reevaluateUnmeteredConnections();
}
};
@@ -486,7 +540,8 @@ public class DcTracker extends Handler {
}
}
- private void onDataReconnect(ApnContext apnContextforRetry, int subId) {
+ private void onDataReconnect(ApnContext apnContextforRetry, int subId,
+ @RequestNetworkType int requestType) {
int phoneSubId = mPhone.getSubId();
String apnType = apnContextforRetry.getApnType();
String reason = apnContextforRetry.getReason();
@@ -519,7 +574,8 @@ public class DcTracker extends Handler {
if (DBG) log("onDataReconnect: keep associated");
}
// TODO: IF already associated should we send the EVENT_TRY_SETUP_DATA???
- sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
+ sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, requestType,
+ 0, apnContext));
}
}
@@ -603,8 +659,6 @@ public class DcTracker extends Handler {
private SparseArray<ApnContext> mApnContextsByType = new SparseArray<ApnContext>();
- private int mDisconnectPendingCount = 0;
-
private ArrayList<DataProfile> mLastDataProfileList = new ArrayList<>();
/** RAT name ===> (downstream, upstream) bandwidth values from carrier config. */
@@ -647,23 +701,25 @@ public class DcTracker extends Handler {
/** Watches for changes to the APN db. */
private ApnChangeObserver mApnObserver;
- private final String mProvisionActionName;
private BroadcastReceiver mProvisionBroadcastReceiver;
private ProgressDialog mProvisioningSpinner;
private final DataServiceManager mDataServiceManager;
+ @AccessNetworkConstants.TransportType
private final int mTransportType;
private DataStallRecoveryHandler mDsRecoveryHandler;
private HandlerThread mHandlerThread;
+ private final DataThrottler mDataThrottler;
+
/**
* Request network completion message map. Key is the APN type, value is the list of completion
* messages to be sent. Using a list because there might be multiple network requests for
* the same APN type.
*/
- private final Map<Integer, List<Message>> mRequestNetworkCompletionMsgs = new HashMap<>();
+ private final Map<Integer, List<Message>> mHandoverCompletionMsgs = new HashMap<>();
//***** Constructor
public DcTracker(Phone phone, @TransportType int transportType) {
@@ -674,12 +730,14 @@ public class DcTracker extends Handler {
.createForSubscriptionId(phone.getSubId());
// The 'C' in tag indicates cellular, and 'I' indicates IWLAN. This is to distinguish
// between two DcTrackers, one for each.
- mLogTagSuffix = "-" + ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
- ? "C" : "I") + "-" + mPhone.getPhoneId();
- mLogTag = "DCT" + mLogTagSuffix;
+ String tagSuffix = "-" + ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ ? "C" : "I");
+ tagSuffix += "-" + mPhone.getPhoneId();
+ mLogTag = "DCT" + tagSuffix;
mTransportType = transportType;
- mDataServiceManager = new DataServiceManager(phone, transportType, mLogTagSuffix);
+ mDataServiceManager = new DataServiceManager(phone, transportType, tagSuffix);
+ mDataThrottler = new DataThrottler(mPhone, transportType);
mResolver = mPhone.getContext().getContentResolver();
mAlarmManager =
@@ -715,7 +773,8 @@ public class DcTracker extends Handler {
mHandlerThread = new HandlerThread("DcHandlerThread");
mHandlerThread.start();
Handler dcHandler = new Handler(mHandlerThread.getLooper());
- mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler, mLogTagSuffix);
+ mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager, dcHandler.getLooper(),
+ tagSuffix);
mDcTesterFailBringUpAll = new DcTesterFailBringUpAll(mPhone, dcHandler);
mDataConnectionTracker = this;
@@ -726,10 +785,7 @@ public class DcTracker extends Handler {
initApnContexts();
- initEmergencyApnSetting();
- addEmergencyApnSetting();
-
- mProvisionActionName = "com.android.internal.telephony.PROVISION" + phone.getPhoneId();
+ addDefaultApnSettingsAsNeeded();
mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
registerSettingsObserver();
@@ -738,16 +794,15 @@ public class DcTracker extends Handler {
@VisibleForTesting
public DcTracker() {
mLogTag = "DCT";
- mLogTagSuffix = null;
mTelephonyManager = null;
mAlarmManager = null;
mPhone = null;
mDataConnectionTracker = null;
- mProvisionActionName = null;
mSettingsObserver = new SettingsObserver(null, this);
mDataEnabledSettings = null;
mTransportType = 0;
mDataServiceManager = null;
+ mDataThrottler = null;
}
public void registerServiceStateTrackerEvents() {
@@ -775,6 +830,7 @@ public class DcTracker extends Handler {
mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
mPhone.getServiceStateTracker().unregisterForDataRegStateOrRatChanged(mTransportType, this);
+ mPhone.getServiceStateTracker().unregisterForAirplaneModeChanged(this);
}
private void registerForAllEvents() {
@@ -799,6 +855,7 @@ public class DcTracker extends Handler {
registerServiceStateTrackerEvents();
mDataServiceManager.registerForServiceBindingChanged(this,
DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED, null);
+ mDataServiceManager.registerForApnUnthrottled(this, DctConstants.EVENT_APN_UNTHROTTLED);
}
public void dispose() {
@@ -820,7 +877,6 @@ public class DcTracker extends Handler {
mSettingsObserver.unobserve();
mNetworkPolicyManager.unregisterSubscriptionCallback(mSubscriptionCallback);
- mDcc.dispose();
mDcTesterFailBringUpAll.dispose();
mPhone.getContext().getContentResolver().unregisterContentObserver(mApnObserver);
@@ -857,6 +913,7 @@ public class DcTracker extends Handler {
mDataServiceManager.unregisterForServiceBindingChanged(this);
mDataEnabledSettings.unregisterForDataEnabledChanged(this);
mDataEnabledSettings.unregisterForDataEnabledOverrideChanged(this);
+ mDataServiceManager.unregisterForApnUnthrottled(this);
}
/**
@@ -891,12 +948,24 @@ public class DcTracker extends Handler {
mPhone.notifyDataActivity();
}
+ /**
+ * Request a network
+ *
+ * @param networkRequest Network request from clients
+ * @param type The request type
+ * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
+ * handover is completed. For normal request, this should be null.
+ */
public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
- Message onCompleteMsg) {
+ Message onHandoverCompleteMsg) {
+ if (type != REQUEST_TYPE_HANDOVER && onHandoverCompleteMsg != null) {
+ throw new RuntimeException("request network with normal type request type but passing "
+ + "handover complete message.");
+ }
final int apnType = ApnContext.getApnTypeFromNetworkRequest(networkRequest);
final ApnContext apnContext = mApnContextsByType.get(apnType);
if (apnContext != null) {
- apnContext.requestNetwork(networkRequest, type, onCompleteMsg);
+ apnContext.requestNetwork(networkRequest, type, onHandoverCompleteMsg);
}
}
@@ -950,6 +1019,10 @@ public class DcTracker extends Handler {
@Override
public void onReceive(Context context, Intent intent) {
+ if (mPhone.getPhoneId() != intent.getIntExtra(EXTRA_PROVISION_PHONE_ID,
+ SubscriptionManager.INVALID_PHONE_INDEX)) {
+ return;
+ }
// Turning back on the radio can take time on the order of a minute, so show user a
// spinner so they know something is going on.
log("onReceive : ProvisionNotificationBroadcastReceiver");
@@ -1008,11 +1081,21 @@ public class DcTracker extends Handler {
for (ApnConfigType apnConfigType : types) {
ApnContext apnContext = new ApnContext(mPhone, apnConfigType.getType(), mLogTag, this,
apnConfigType.getPriority());
+ int bitmask = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType());
mPrioritySortedApnContexts.add(apnContext);
mApnContexts.put(apnContext.getApnType(), apnContext);
- mApnContextsByType.put(ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType()),
- apnContext);
-
+ mApnContextsByType.put(bitmask, apnContext);
+ // Notify listeners that all data is disconnected when DCT is initialized.
+ // Once connections are established, DC will then notify that data is connected.
+ // This is to prevent the case where the phone process crashed but we don't notify
+ // listeners that data was disconnected, so they may be stuck in a connected state.
+ mPhone.notifyDataConnection(new PreciseDataConnectionState.Builder()
+ .setTransportType(mTransportType)
+ .setState(TelephonyManager.DATA_DISCONNECTED)
+ .setApnSetting(new ApnSetting.Builder()
+ .setApnTypeBitmask(bitmask).buildWithoutCheck())
+ .setNetworkType(getDataRat())
+ .build());
log("initApnContexts: apnContext=" + ApnSetting.getApnTypeString(
apnConfigType.getType()));
}
@@ -1096,6 +1179,18 @@ public class DcTracker extends Handler {
return result.toArray(new String[0]);
}
+ /**
+ * Get ApnTypes with connected data connections. This is different than getActiveApnTypes()
+ * which returns apn types that with active apn contexts.
+ * @return apn types
+ */
+ public String[] getConnectedApnTypes() {
+ return mApnContexts.values().stream()
+ .filter(ac -> ac.getState() == DctConstants.State.CONNECTED)
+ .map(ApnContext::getApnType)
+ .toArray(String[]::new);
+ }
+
@VisibleForTesting
public Collection<ApnContext> getApnContexts() {
return mPrioritySortedApnContexts;
@@ -1122,6 +1217,8 @@ public class DcTracker extends Handler {
* <p>
* Assumes there is less than one {@link ApnSetting} can support the given apn type.
*/
+ // TODO: for enterprise this always returns IDLE, which is ok for now since it is never called
+ // for enterprise
public DctConstants.State getState(String apnType) {
DctConstants.State state = DctConstants.State.IDLE;
final int apnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
@@ -1142,50 +1239,6 @@ public class DcTracker extends Handler {
return state;
}
- /** Convert the internal DctConstants enum state to the TelephonyManager DATA_* state.
- * @param state the DctConstants.State
- * @return a corresponding TelephonyManager.DataState
- */
- @TelephonyManager.DataState
- public static int convertDctStateToTelephonyDataState(DctConstants.State state) {
- switch(state) {
- case CONNECTING: // fall through
- case RETRYING:
- return TelephonyManager.DATA_CONNECTING;
- case CONNECTED:
- return TelephonyManager.DATA_CONNECTED;
- case DISCONNECTING:
- return TelephonyManager.DATA_DISCONNECTING;
- case IDLE: // fall through
- case FAILED: // fall through
- default:
- return TelephonyManager.DATA_DISCONNECTED;
- }
- }
-
- /** Return the Precise Data Connection State information */
- public @NonNull PreciseDataConnectionState getPreciseDataConnectionState(
- String apnType, boolean isSuspended, int networkType) {
-
- int telState = convertDctStateToTelephonyDataState(getState(apnType));
- // Since suspended isn't actually reported by the DCT, do a fixup based on current
- // voice call state and device + rat capability
- if ((telState == TelephonyManager.DATA_CONNECTED
- || telState == TelephonyManager.DATA_DISCONNECTING)
- && isSuspended) {
- telState = TelephonyManager.DATA_SUSPENDED;
- }
-
- ApnSetting apnSetting = getActiveApnSetting(apnType);
- int apnTypesBitmask = ApnSetting.getApnTypesBitmaskFromString(apnType);
-
- // TODO: should the data fail cause be populated?
- return new PreciseDataConnectionState(
- telState, networkType, apnTypesBitmask, apnType,
- getLinkProperties(apnType),
- DataFailCause.NONE, apnSetting);
- }
-
/**
* Return a better connection state between {@code stateA} and {@code stateB}. Check
* {@link #DATA_CONNECTION_STATE_PRIORITIES} for the details.
@@ -1207,52 +1260,6 @@ public class DcTracker extends Handler {
return false;
}
- // Return state of overall
- public DctConstants.State getOverallState() {
- boolean isConnecting = false;
- boolean isFailed = true; // All enabled Apns should be FAILED.
- boolean isAnyEnabled = false;
-
- for (ApnContext apnContext : mApnContexts.values()) {
- if (apnContext.isEnabled()) {
- isAnyEnabled = true;
- switch (apnContext.getState()) {
- case CONNECTED:
- case DISCONNECTING:
- if (VDBG) log("overall state is CONNECTED");
- return DctConstants.State.CONNECTED;
- case CONNECTING:
- isConnecting = true;
- isFailed = false;
- break;
- case IDLE:
- case RETRYING:
- isFailed = false;
- break;
- default:
- isAnyEnabled = true;
- break;
- }
- }
- }
-
- if (!isAnyEnabled) { // Nothing enabled. return IDLE.
- if (VDBG) log( "overall state is IDLE");
- return DctConstants.State.IDLE;
- }
-
- if (isConnecting) {
- if (VDBG) log( "overall state is CONNECTING");
- return DctConstants.State.CONNECTING;
- } else if (!isFailed) {
- if (VDBG) log( "overall state is IDLE");
- return DctConstants.State.IDLE;
- } else {
- if (VDBG) log( "overall state is FAILED");
- return DctConstants.State.FAILED;
- }
- }
-
//****** Called from ServiceStateTracker
/**
* Invoked when ServiceStateTracker observes a transition from GPRS
@@ -1266,18 +1273,16 @@ public class DcTracker extends Handler {
if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
stopNetStatPoll();
stopDataStallAlarm();
- mPhone.notifyAllActiveDataConnections();
mAttached.set(false);
}
private void onDataConnectionAttached() {
if (DBG) log("onDataConnectionAttached");
mAttached.set(true);
- if (getOverallState() == DctConstants.State.CONNECTED) {
+ if (isAnyDataConnected()) {
if (DBG) log("onDataConnectionAttached: start polling notify attached");
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- mPhone.notifyAllActiveDataConnections();
}
if (mAutoAttachOnCreationConfig) {
mAutoAttachEnabled.set(true);
@@ -1368,18 +1373,34 @@ public class DcTracker extends Handler {
// Step 3. Build disallowed reasons.
if (apnContext != null && !apnContext.isConnectable()) {
- reasons.add(DataDisallowedReasonType.APN_NOT_CONNECTABLE);
+ DctConstants.State state = apnContext.getState();
+ if (state == DctConstants.State.CONNECTED) {
+ reasons.add(DataDisallowedReasonType.DATA_ALREADY_CONNECTED);
+ } else if (state == DctConstants.State.DISCONNECTING) {
+ reasons.add(DataDisallowedReasonType.DATA_IS_DISCONNECTING);
+ } else if (state == DctConstants.State.CONNECTING) {
+ reasons.add(DataDisallowedReasonType.DATA_IS_CONNECTING);
+ } else {
+ reasons.add(DataDisallowedReasonType.APN_NOT_CONNECTABLE);
+ }
}
// In legacy mode, if RAT is IWLAN then don't allow default/IA PDP at all.
// Rest of APN types can be evaluated for remaining conditions.
- if ((apnContext != null && requestApnType == TYPE_DEFAULT
- || requestApnType == TYPE_IA)
+ if ((apnContext != null && requestApnType == ApnSetting.TYPE_DEFAULT
+ || requestApnType == ApnSetting.TYPE_ENTERPRISE
+ || requestApnType == ApnSetting.TYPE_IA)
&& mPhone.getTransportManager().isInLegacyMode()
&& dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
reasons.add(DataDisallowedReasonType.ON_IWLAN);
}
+ // If device is not on NR, don't allow enterprise
+ if (apnContext != null && requestApnType == ApnSetting.TYPE_ENTERPRISE
+ && dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_NR) {
+ reasons.add(DataDisallowedReasonType.NOT_ON_NR);
+ }
+
if (shouldRestrictDataForEcbm() || mPhone.isInEmergencyCall()) {
reasons.add(DataDisallowedReasonType.IN_ECBM);
}
@@ -1413,8 +1434,24 @@ public class DcTracker extends Handler {
if (!radioStateFromCarrier) {
reasons.add(DataDisallowedReasonType.RADIO_DISABLED_BY_CARRIER);
}
+ if (!mDataServiceBound) {
+ reasons.add(DataDisallowedReasonType.DATA_SERVICE_NOT_READY);
+ }
if (apnContext != null) {
+ if (mPhone.getTransportManager().getPreferredTransport(
+ apnContext.getApnTypeBitmask())
+ == AccessNetworkConstants.TRANSPORT_TYPE_INVALID) {
+ // If QNS explicitly specified this APN type is not allowed on either cellular or
+ // IWLAN, we should not allow data setup.
+ reasons.add(DataDisallowedReasonType.DISABLED_BY_QNS);
+ } else if (mTransportType != mPhone.getTransportManager().getPreferredTransport(
+ apnContext.getApnTypeBitmask())) {
+ // If the latest preference has already switched to other transport, we should not
+ // allow data setup.
+ reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT);
+ }
+
// If the transport has been already switched to the other transport, we should not
// allow the data setup. The only exception is the handover case, where we setup
// handover data connection before switching the transport.
@@ -1422,6 +1459,12 @@ public class DcTracker extends Handler {
apnContext.getApnTypeBitmask()) && requestType != REQUEST_TYPE_HANDOVER) {
reasons.add(DataDisallowedReasonType.ON_OTHER_TRANSPORT);
}
+
+ // Check if the device is under data throttling.
+ long retryTime = mDataThrottler.getRetryTime(apnContext.getApnTypeBitmask());
+ if (retryTime > SystemClock.elapsedRealtime()) {
+ reasons.add(DataDisallowedReasonType.DATA_THROTTLED);
+ }
}
boolean isDataEnabled = apnContext == null ? mDataEnabledSettings.isDataEnabled()
@@ -1450,14 +1493,18 @@ public class DcTracker extends Handler {
// Or if the data is on cellular, and the APN type is determined unmetered by the
// configuration.
} else if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
- && !isMeteredApnType && requestApnType != TYPE_DEFAULT) {
+ && !isMeteredApnType && requestApnType != ApnSetting.TYPE_DEFAULT
+ && requestApnType != ApnSetting.TYPE_ENTERPRISE) {
reasons.add(DataAllowedReasonType.UNMETERED_APN);
}
// If the request is restricted and there are only soft disallowed reasons (e.g. data
- // disabled, data roaming disabled) existing, we should allow the data.
+ // disabled, data roaming disabled) existing, we should allow the data. ENTERPRISE is
+ // an exception and should not be treated as restricted for this purpose; it should be
+ // treated same as DEFAULT.
if (apnContext != null
&& apnContext.hasRestrictedRequests(true)
+ && !apnContext.getApnType().equals(ApnSetting.TYPE_ENTERPRISE_STRING)
&& !reasons.allowed()) {
reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
}
@@ -1522,7 +1569,7 @@ public class DcTracker extends Handler {
if (apnContext.isConnectable()) {
log("isConnectable() call trySetupData");
apnContext.setReason(reason);
- trySetupData(apnContext, REQUEST_TYPE_NORMAL);
+ trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
}
}
@@ -1533,16 +1580,21 @@ public class DcTracker extends Handler {
return isInEcm && !isInImsEcm;
}
- private boolean trySetupData(ApnContext apnContext, @RequestNetworkType int requestType) {
+ private boolean isHandoverPending(@ApnType int apnType) {
+ List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
+ return messageList != null && messageList.size() > 0;
+ }
+
+ private void trySetupData(ApnContext apnContext, @RequestNetworkType int requestType,
+ @Nullable Message onHandoverCompleteMsg) {
+ if (onHandoverCompleteMsg != null) {
+ addHandoverCompleteMsg(onHandoverCompleteMsg, apnContext.getApnTypeBitmask());
+ }
if (mPhone.getSimulatedRadioControl() != null) {
// Assume data is connected on the simulator
- // FIXME this can be improved
- apnContext.setState(DctConstants.State.CONNECTED);
- mPhone.notifyDataConnection(apnContext.getApnType());
-
log("trySetupData: X We're on the simulator; assuming connected retValue=true");
- return true;
+ return;
}
DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
@@ -1550,49 +1602,18 @@ public class DcTracker extends Handler {
String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
+ apnContext.getReason() + ", requestType=" + requestTypeToString(requestType)
+ ". " + dataConnectionReasons.toString();
+ if (dataConnectionReasons.contains(DataDisallowedReasonType.DISABLED_BY_QNS)
+ || dataConnectionReasons.contains(DataDisallowedReasonType.ON_OTHER_TRANSPORT)) {
+ logStr += ", current transport=" + AccessNetworkConstants.transportTypeToString(
+ mPhone.getTransportManager().getCurrentTransport(
+ apnContext.getApnTypeBitmask()));
+ logStr += ", preferred transport=" + AccessNetworkConstants.transportTypeToString(
+ mPhone.getTransportManager().getPreferredTransport(
+ apnContext.getApnTypeBitmask()));
+ }
if (DBG) log(logStr);
apnContext.requestLog(logStr);
- if (isDataAllowed) {
- if (apnContext.getState() == DctConstants.State.FAILED) {
- String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
- if (DBG) log(str);
- apnContext.requestLog(str);
- apnContext.setState(DctConstants.State.IDLE);
- }
- int radioTech = getDataRat();
- if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && mPhone.getServiceState()
- .getState() == ServiceState.STATE_IN_SERVICE) {
- radioTech = getVoiceRat();
- }
- log("service state=" + mPhone.getServiceState());
- apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
- .isConcurrentVoiceAndDataAllowed());
- if (apnContext.getState() == DctConstants.State.IDLE) {
- ArrayList<ApnSetting> waitingApns =
- buildWaitingApns(apnContext.getApnType(), radioTech);
- if (waitingApns.isEmpty()) {
- ApnSetting apn = apnContext != null ? apnContext.getApnSetting() : null;
- mPhone.notifyDataConnectionFailed(apnContext.getApnType(),
- apn != null ? apn.getApnName() : null,
- DataFailCause.MISSING_UNKNOWN_APN);
- String str = "trySetupData: X No APN found retValue=false";
- if (DBG) log(str);
- apnContext.requestLog(str);
- return false;
- } else {
- apnContext.setWaitingApns(waitingApns);
- if (DBG) {
- log ("trySetupData: Create from mAllApnSettings : "
- + apnListToString(mAllApnSettings));
- }
- }
- }
-
- boolean retValue = setupData(apnContext, radioTech, requestType);
-
- if (DBG) log("trySetupData: X retValue=" + retValue);
- return retValue;
- } else {
+ if (!isDataAllowed) {
StringBuilder str = new StringBuilder();
str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
@@ -1604,6 +1625,18 @@ public class DcTracker extends Handler {
str.append("isDataEnabled() = false. " + mDataEnabledSettings);
}
+ // Check if it fails because of the existing data is still disconnecting.
+ if (dataConnectionReasons.contains(DataDisallowedReasonType.DATA_IS_DISCONNECTING)
+ && isHandoverPending(apnContext.getApnTypeBitmask())) {
+ // Normally we don't retry when isDataAllow() returns false, because that's consider
+ // pre-condition not met, for example, data not enabled by the user, or airplane
+ // mode is on. If we retry in those cases, there will be significant power impact.
+ // DATA_IS_DISCONNECTING is a special case we want to retry, and for the handover
+ // case only.
+ log("Data is disconnecting. Will retry handover later.");
+ return;
+ }
+
// If this is a data retry, we should set the APN state to FAILED so it won't stay
// in RETRYING forever.
if (apnContext.getState() == DctConstants.State.RETRYING) {
@@ -1613,7 +1646,55 @@ public class DcTracker extends Handler {
if (DBG) log(str.toString());
apnContext.requestLog(str.toString());
- return false;
+ if (requestType == REQUEST_TYPE_HANDOVER) {
+ // If fails due to latest preference already changed back to source transport, then
+ // just fallback (will not attempt handover anymore, and will not tear down the
+ // data connection on source transport.
+ boolean fallback = dataConnectionReasons.contains(
+ DataDisallowedReasonType.ON_OTHER_TRANSPORT);
+ sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, fallback);
+ }
+ return;
+ }
+
+ if (apnContext.getState() == DctConstants.State.FAILED) {
+ String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
+ if (DBG) log(str);
+ apnContext.requestLog(str);
+ apnContext.setState(DctConstants.State.IDLE);
+ }
+ int radioTech = getDataRat();
+ if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && mPhone.getServiceState()
+ .getState() == ServiceState.STATE_IN_SERVICE) {
+ radioTech = getVoiceRat();
+ }
+ log("service state=" + mPhone.getServiceState());
+ apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
+ .isConcurrentVoiceAndDataAllowed());
+ if (apnContext.getState() == DctConstants.State.IDLE) {
+ ArrayList<ApnSetting> waitingApns =
+ buildWaitingApns(apnContext.getApnType(), radioTech);
+ if (waitingApns.isEmpty()) {
+ String str = "trySetupData: X No APN found retValue=false";
+ if (DBG) log(str);
+ apnContext.requestLog(str);
+ if (requestType == REQUEST_TYPE_HANDOVER) {
+ sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
+ false);
+ }
+ return;
+ } else {
+ apnContext.setWaitingApns(waitingApns);
+ if (DBG) {
+ log("trySetupData: Create from mAllApnSettings : "
+ + apnListToString(mAllApnSettings));
+ }
+ }
+ }
+
+ if (!setupData(apnContext, radioTech, requestType)
+ && requestType == REQUEST_TYPE_HANDOVER) {
+ sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
}
}
@@ -1659,7 +1740,7 @@ public class DcTracker extends Handler {
for (ApnContext apnContext : mApnContexts.values()) {
// Exclude the IMS APN from single data connection case.
if (reason.equals(Phone.REASON_SINGLE_PDN_ARBITRATION)
- && apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
+ && apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
continue;
}
@@ -1681,9 +1762,7 @@ public class DcTracker extends Handler {
// TODO: Do we need mRequestedApnType?
mRequestedApnType = ApnSetting.TYPE_DEFAULT;
- log("cleanUpAllConnectionsInternal: mDisconnectPendingCount = "
- + mDisconnectPendingCount);
- if (detach && mDisconnectPendingCount == 0) {
+ if (areAllDataDisconnected()) {
notifyAllDataDisconnected();
}
@@ -1759,7 +1838,7 @@ public class DcTracker extends Handler {
if (dataConnection != null) {
if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
boolean disconnectAll = false;
- if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())
+ if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType())
&& ServiceState.isCdma(getDataRat())) {
if (DBG) {
log("cleanUpConnectionInternal: disconnectAll DUN connection");
@@ -1783,24 +1862,24 @@ public class DcTracker extends Handler {
}
apnContext.setState(DctConstants.State.DISCONNECTING);
- mDisconnectPendingCount++;
}
} else {
// apn is connected but no reference to the data connection.
// Should not be happen, but reset the state in case.
apnContext.setState(DctConstants.State.IDLE);
apnContext.requestLog("cleanUpConnectionInternal: connected, bug no dc");
- mPhone.notifyDataConnection(apnContext.getApnType());
}
}
} else {
// force clean up the data connection.
if (dataConnection != null) dataConnection.reset();
apnContext.setState(DctConstants.State.IDLE);
- mPhone.notifyDataConnection(apnContext.getApnType());
apnContext.setDataConnection(null);
}
+ // If there is any outstanding handover request, we need to respond it.
+ sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false, false);
+
// Make sure reconnection alarm is cleaned up if there is no ApnContext
// associated to the connection.
if (dataConnection != null) {
@@ -1811,12 +1890,105 @@ public class DcTracker extends Handler {
if (DBG) log(str + " apnContext=" + apnContext + " dc=" + apnContext.getDataConnection());
}
+ private Cursor getPreferredApnCursor(int subId) {
+ Cursor cursor = null;
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ cursor = mPhone.getContext().getContentResolver().query(
+ Uri.withAppendedPath(PREFERAPN_NO_UPDATE_URI_USING_SUBID,
+ String.valueOf(subId)), null, null, null,
+ Telephony.Carriers.DEFAULT_SORT_ORDER);
+ }
+ return cursor;
+ }
+
+ private ApnSetting getPreferredApnFromDB() {
+ ApnSetting preferredApn = null;
+ Cursor cursor = getPreferredApnCursor(mPhone.getSubId());
+ if (cursor != null) {
+ if (cursor.getCount() > 0) {
+ cursor.moveToFirst();
+ preferredApn = ApnSetting.makeApnSetting(cursor);
+ }
+ cursor.close();
+ }
+ if (VDBG) log("getPreferredApnFromDB: preferredApn=" + preferredApn);
+ return preferredApn;
+ }
+
+ private void setDefaultPreferredApnIfNeeded() {
+ ApnSetting defaultPreferredApn = null;
+ PersistableBundle bundle = getCarrierConfig();
+ String defaultPreferredApnName = bundle.getString(CarrierConfigManager
+ .KEY_DEFAULT_PREFERRED_APN_NAME_STRING);
+
+ if (TextUtils.isEmpty(defaultPreferredApnName) || getPreferredApnFromDB() != null) {
+ return;
+ }
+
+ String selection = Telephony.Carriers.APN + " = \"" + defaultPreferredApnName + "\" AND "
+ + Telephony.Carriers.EDITED_STATUS + " = " + Telephony.Carriers.UNEDITED;
+ Cursor cursor = mPhone.getContext().getContentResolver().query(
+ Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI,
+ "filtered/subId/" + mPhone.getSubId()),
+ null, selection, null, Telephony.Carriers._ID);
+
+ if (cursor != null) {
+ if (cursor.getCount() > 0) {
+ if (cursor.moveToFirst()) {
+ defaultPreferredApn = ApnSetting.makeApnSetting(cursor);
+ }
+ }
+ cursor.close();
+ }
+
+ if (defaultPreferredApn != null
+ && defaultPreferredApn.canHandleType(mRequestedApnType)) {
+ log("setDefaultPreferredApnIfNeeded: For APN type "
+ + ApnSetting.getApnTypeString(mRequestedApnType)
+ + " found default apnSetting "
+ + defaultPreferredApn);
+
+ setPreferredApn(defaultPreferredApn.getId(), true);
+ }
+
+ return;
+ }
+
+ /**
+ * Check if preferred apn is allowed to edit by user.
+ * @return {@code true} if it is allowed to edit.
+ */
+ @VisibleForTesting
+ public boolean isPreferredApnUserEdited() {
+ boolean isUserEdited = false;
+ Cursor cursor = getPreferredApnCursor(mPhone.getSubId());
+ if (cursor != null) {
+ if (cursor.getCount() > 0) {
+ if (cursor.moveToFirst()) {
+ isUserEdited = cursor.getInt(
+ cursor.getColumnIndexOrThrow(Telephony.Carriers.EDITED_STATUS))
+ == Telephony.Carriers.USER_EDITED;
+ }
+ }
+ cursor.close();
+ }
+ if (VDBG) log("isPreferredApnUserEdited: isUserEdited=" + isUserEdited);
+ return isUserEdited;
+ }
+
/**
* Fetch the DUN apns
* @return a list of DUN ApnSetting objects
*/
@VisibleForTesting
public @NonNull ArrayList<ApnSetting> fetchDunApns() {
+ if (mPhone.getServiceState().getRoaming() && !isPreferredApnUserEdited()
+ && getCarrierConfig().getBoolean(CarrierConfigManager
+ .KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL)) {
+ if (VDBG) log("fetchDunApns: Dun apn is not used in roaming network");
+ return new ArrayList<ApnSetting>(0);
+ }
+
int bearer = getDataRat();
ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
ArrayList<ApnSetting> retDunSettings = new ArrayList<ApnSetting>();
@@ -1840,12 +2012,21 @@ public class DcTracker extends Handler {
}
}
+ int preferredApnSetId = getPreferredApnSetId();
+ ApnSetting preferredApn = getPreferredApnFromDB();
for (ApnSetting dunSetting : dunCandidates) {
- if (!dunSetting.canSupportNetworkType(
+ if (dunSetting.canSupportNetworkType(
ServiceState.rilRadioTechnologyToNetworkType(bearer))) {
- continue;
+ if (preferredApnSetId == dunSetting.getApnSetId()) {
+ if (preferredApn != null && preferredApn.equals(dunSetting)) {
+ // If there is a preferred APN can handled DUN type, prepend it to list to
+ // use it preferred.
+ retDunSettings.add(0, dunSetting);
+ } else {
+ retDunSettings.add(dunSetting);
+ }
+ }
}
- retDunSettings.add(dunSetting);
}
if (VDBG) log("fetchDunApns: dunSettings=" + retDunSettings);
@@ -1853,6 +2034,9 @@ public class DcTracker extends Handler {
}
private int getPreferredApnSetId() {
+ // preferapnset uri returns all APNs for the current carrier which have an apn_set_id
+ // equal to the preferred APN (if no preferred APN, or if the preferred APN has no set id,
+ // the query will return null)
Cursor c = mPhone.getContext().getContentResolver()
.query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI,
"preferapnset/subId/" + mPhone.getSubId()),
@@ -1903,7 +2087,15 @@ public class DcTracker extends Handler {
return null;
}
- boolean isPermanentFailure(@DataFailureCause int dcFailCause) {
+ /**
+ * Check if the data fail cause is a permanent failure (i.e. Frameworks will not retry data
+ * setup).
+ *
+ * @param dcFailCause The data fail cause
+ * @return {@code true} if the data fail cause is a permanent failure.
+ */
+ @VisibleForTesting
+ public boolean isPermanentFailure(@DataFailureCause int dcFailCause) {
return (DataFailCause.isPermanentFailure(mPhone.getContext(), dcFailCause,
mPhone.getSubId())
&& (mAttached.get() == false || dcFailCause != DataFailCause.SIGNAL_LOST));
@@ -1967,9 +2159,11 @@ public class DcTracker extends Handler {
// a dun-profiled connection so we can't share an existing one
// On GSM/LTE we can share existing apn connections provided they support
// this type.
- if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DUN)
- || ServiceState.isGsm(getDataRat())) {
- dataConnection = checkForCompatibleDataConnection(apnContext);
+ // If asking for ENTERPRISE, there are no compatible data connections, so skip this check
+ if ((apnContext.getApnTypeBitmask() != ApnSetting.TYPE_DUN
+ || ServiceState.isGsm(getDataRat()))
+ && apnContext.getApnTypeBitmask() != ApnSetting.TYPE_ENTERPRISE) {
+ dataConnection = checkForCompatibleDataConnection(apnContext, apnSetting);
if (dataConnection != null) {
// Get the apn setting used by the data connection
ApnSetting dataConnectionApnSetting = dataConnection.getApnSetting();
@@ -1990,7 +2184,7 @@ public class DcTracker extends Handler {
// Should not start cleanUp if the setupData is for IMS APN
// or retry of same APN(State==RETRYING).
- if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)
+ if (!apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)
&& (apnContext.getState() != DctConstants.State.RETRYING)) {
// Only lower priority calls left. Disconnect them all in this single PDP case
// so that we can bring up the requested higher priority call (once we receive
@@ -2029,7 +2223,6 @@ public class DcTracker extends Handler {
apnContext.setDataConnection(dataConnection);
apnContext.setApnSetting(apnSetting);
apnContext.setState(DctConstants.State.CONNECTING);
- mPhone.notifyDataConnection(apnContext.getApnType());
Message msg = obtainMessage();
msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
@@ -2054,88 +2247,67 @@ public class DcTracker extends Handler {
return true;
}
- protected void setInitialAttachApn() {
- ApnSetting iaApnSetting = null;
- ApnSetting defaultApnSetting = null;
- ApnSetting firstNonEmergencyApnSetting = null;
-
- log("setInitialApn: E mPreferredApn=" + mPreferredApn);
-
- if (mPreferredApn != null && mPreferredApn.canHandleType(ApnSetting.TYPE_IA)) {
- iaApnSetting = mPreferredApn;
- } else if (!mAllApnSettings.isEmpty()) {
- // Search for Initial APN setting and the first apn that can handle default
- for (ApnSetting apn : mAllApnSettings) {
- if (firstNonEmergencyApnSetting == null
- && !apn.isEmergencyApn()) {
- firstNonEmergencyApnSetting = apn;
- log("setInitialApn: firstNonEmergencyApnSetting="
- + firstNonEmergencyApnSetting);
- }
- if (apn.canHandleType(ApnSetting.TYPE_IA)) {
- // The Initial Attach APN is highest priority so use it if there is one
- log("setInitialApn: iaApnSetting=" + apn);
- iaApnSetting = apn;
- break;
- } else if ((defaultApnSetting == null)
- && (apn.canHandleType(ApnSetting.TYPE_DEFAULT))) {
- // Use the first default apn if no better choice
- log("setInitialApn: defaultApnSetting=" + apn);
- defaultApnSetting = apn;
- }
+ // Get the allowed APN types for initial attach. The order in the returned list represent
+ // the order of APN types that should be used for initial attach.
+ private @NonNull @ApnType List<Integer> getAllowedInitialAttachApnTypes() {
+ PersistableBundle bundle = getCarrierConfig();
+ if (bundle != null) {
+ String[] apnTypesArray = bundle.getStringArray(
+ CarrierConfigManager.KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY);
+ if (apnTypesArray != null) {
+ return Arrays.stream(apnTypesArray)
+ .map(ApnSetting::getApnTypesBitmaskFromString)
+ .collect(Collectors.toList());
}
}
- if ((iaApnSetting == null) && (defaultApnSetting == null) &&
- !allowInitialAttachForOperator()) {
- log("Abort Initial attach");
- return;
+ return Collections.emptyList();
+ }
+
+ protected void setInitialAttachApn() {
+ ApnSetting apnSetting = null;
+ int preferredApnSetId = getPreferredApnSetId();
+ ArrayList<ApnSetting> allApnSettings = new ArrayList<>();
+ if (mPreferredApn != null) {
+ // Put the preferred apn at the beginning of the list. It's okay to have a duplicate
+ // when later on mAllApnSettings get added. That would not change the selection result.
+ allApnSettings.add(mPreferredApn);
+ }
+ allApnSettings.addAll(mAllApnSettings);
+
+ // Get the allowed APN types for initial attach. Note that if none of the APNs has the
+ // allowed APN types, then the initial attach will not be performed.
+ List<Integer> allowedApnTypes = getAllowedInitialAttachApnTypes();
+ for (int allowedApnType : allowedApnTypes) {
+ apnSetting = allApnSettings.stream()
+ .filter(apn -> apn.canHandleType(allowedApnType))
+ .filter(apn -> (apn.getApnSetId() == preferredApnSetId
+ || apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID))
+ .findFirst()
+ .orElse(null);
+ if (apnSetting != null) break;
}
- // The priority of apn candidates from highest to lowest is:
- // 1) APN_TYPE_IA (Initial Attach)
- // 2) mPreferredApn, i.e. the current preferred apn
- // 3) The first apn that than handle APN_TYPE_DEFAULT
- // 4) The first APN we can find.
-
- ApnSetting initialAttachApnSetting = null;
- if (iaApnSetting != null) {
- if (DBG) log("setInitialAttachApn: using iaApnSetting");
- initialAttachApnSetting = iaApnSetting;
- } else if (mPreferredApn != null) {
- if (DBG) log("setInitialAttachApn: using mPreferredApn");
- initialAttachApnSetting = mPreferredApn;
- } else if (defaultApnSetting != null) {
- if (DBG) log("setInitialAttachApn: using defaultApnSetting");
- initialAttachApnSetting = defaultApnSetting;
- } else if (firstNonEmergencyApnSetting != null) {
- if (DBG) log("setInitialAttachApn: using firstNonEmergencyApnSetting");
- initialAttachApnSetting = firstNonEmergencyApnSetting;
- }
-
- if (initialAttachApnSetting == null) {
- if (DBG) log("setInitialAttachApn: X There in no available apn");
- } else {
- if (DBG) log("setInitialAttachApn: X selected Apn=" + initialAttachApnSetting);
+ if (DBG) {
+ log("setInitialAttachApn: Allowed APN types=" + allowedApnTypes.stream()
+ .map(ApnSetting::getApnTypeString)
+ .collect(Collectors.joining(",")));
+ }
- mDataServiceManager.setInitialAttachApn(createDataProfile(initialAttachApnSetting,
- initialAttachApnSetting.equals(getPreferredApn())),
+ if (apnSetting == null) {
+ if (DBG) log("setInitialAttachApn: X There in no available apn.");
+ } else {
+ if (DBG) log("setInitialAttachApn: X selected APN=" + apnSetting);
+ mDataServiceManager.setInitialAttachApn(createDataProfile(apnSetting,
+ apnSetting.equals(getPreferredApn())),
mPhone.getServiceState().getDataRoamingFromRegistration(), null);
}
}
- protected boolean allowInitialAttachForOperator() {
- return true;
- }
-
/**
* Handles changes to the APN database.
*/
private void onApnChanged() {
- DctConstants.State overallState = getOverallState();
- boolean isDisconnected = (overallState == DctConstants.State.IDLE ||
- overallState == DctConstants.State.FAILED);
-
if (mPhone instanceof GsmCdmaPhone) {
// The "current" may no longer be valid. MMS depends on this to send properly. TBD
((GsmCdmaPhone)mPhone).updateCurrentCarrierInProvider();
@@ -2144,10 +2316,11 @@ public class DcTracker extends Handler {
// TODO: It'd be nice to only do this if the changed entrie(s)
// match the current operator.
if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
+ setDefaultPreferredApnIfNeeded();
createAllApnList();
setDataProfilesAsNeeded();
setInitialAttachApn();
- cleanUpConnectionsOnUpdatedApns(!isDisconnected, Phone.REASON_APN_CHANGED);
+ cleanUpConnectionsOnUpdatedApns(isAnyDataConnected(), Phone.REASON_APN_CHANGED);
// FIXME: See bug 17426028 maybe no conditional is needed.
if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
@@ -2161,12 +2334,12 @@ public class DcTracker extends Handler {
* @return true if higher priority active apn found
*/
private boolean isHigherPriorityApnContextActive(ApnContext apnContext) {
- if (apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
+ if (apnContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
return false;
}
for (ApnContext otherContext : mPrioritySortedApnContexts) {
- if (otherContext.getApnType().equals(PhoneConstants.APN_TYPE_IMS)) {
+ if (otherContext.getApnType().equals(ApnSetting.TYPE_IMS_STRING)) {
continue;
}
if (apnContext.getApnType().equalsIgnoreCase(otherContext.getApnType())) return false;
@@ -2250,16 +2423,22 @@ public class DcTracker extends Handler {
return retry;
}
- protected void startReconnect(long delay, ApnContext apnContext) {
+ protected void startReconnect(long delay, ApnContext apnContext,
+ @RequestNetworkType int requestType) {
+ apnContext.setState(DctConstants.State.RETRYING);
Message msg = obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
- mPhone.getSubId(), mTransportType, apnContext);
+ mPhone.getSubId(), requestType, apnContext);
cancelReconnect(apnContext);
+
+ // Wait a bit before trying the next APN, so that
+ // we're not tying up the RIL command channel
sendMessageDelayed(msg, delay);
if (DBG) {
- log("startReconnect: delay=" + delay + " apn="
- + apnContext + "reason: " + apnContext.getReason()
- + " subId: " + mPhone.getSubId());
+ log("startReconnect: delay=" + delay + ", apn="
+ + apnContext + ", reason=" + apnContext.getReason()
+ + ", subId=" + mPhone.getSubId() + ", request type="
+ + requestTypeToString(requestType));
}
}
@@ -2289,9 +2468,16 @@ public class DcTracker extends Handler {
}
mAutoAttachEnabled.set(false);
- setDefaultDataRoamingEnabled();
+ setDefaultPreferredApnIfNeeded();
read5GConfiguration();
registerSettingsObserver();
+ SubscriptionPlan[] plans = mNetworkPolicyManager.getSubscriptionPlans(
+ mPhone.getSubId(), mPhone.getContext().getOpPackageName());
+ if (plans != null) {
+ mSubscriptionPlans = Arrays.asList(plans);
+ if (DBG) log("SubscriptionPlans initialized: " + mSubscriptionPlans);
+ reevaluateUnmeteredConnections();
+ }
mConfigReady = true;
}
@@ -2321,6 +2507,7 @@ public class DcTracker extends Handler {
readConfiguration();
if (mSimState == TelephonyManager.SIM_STATE_LOADED) {
+ setDefaultDataRoamingEnabled();
createAllApnList();
setDataProfilesAsNeeded();
setInitialAttachApn();
@@ -2367,12 +2554,34 @@ public class DcTracker extends Handler {
}
}
- private DataConnection checkForCompatibleDataConnection(ApnContext apnContext) {
+ private void onApnUnthrottled(String apn) {
+ if (apn != null) {
+ ApnSetting apnSetting = mAllApnSettings.stream()
+ .filter(as -> apn.equals(as.getApnName()))
+ .findFirst()
+ .orElse(null);
+ if (apnSetting != null) {
+ @ApnType int apnTypes = apnSetting.getApnTypeBitmask();
+ mDataThrottler.setRetryTime(apnTypes, RetryManager.NO_SUGGESTED_RETRY_DELAY,
+ REQUEST_TYPE_NORMAL);
+ // After data unthrottled, we should see if it's possible to bring up the data
+ // again.
+ setupDataOnAllConnectableApns(Phone.REASON_DATA_UNTHROTTLED, RetryFailures.ALWAYS);
+ } else {
+ loge("EVENT_APN_UNTHROTTLED: Invalid APN passed: " + apn);
+ }
+ } else {
+ loge("EVENT_APN_UNTHROTTLED: apn is null");
+ }
+ }
+
+ private DataConnection checkForCompatibleDataConnection(ApnContext apnContext,
+ ApnSetting nextApn) {
int apnType = apnContext.getApnTypeBitmask();
ArrayList<ApnSetting> dunSettings = null;
if (ApnSetting.TYPE_DUN == apnType) {
- dunSettings = sortApnListByPreferred(fetchDunApns());
+ dunSettings = fetchDunApns();
}
if (DBG) {
log("checkForCompatibleDataConnection: apnContext=" + apnContext);
@@ -2385,7 +2594,9 @@ public class DcTracker extends Handler {
log("apnSetting: " + apnSetting);
if (dunSettings != null && dunSettings.size() > 0) {
for (ApnSetting dunSetting : dunSettings) {
- if (dunSetting.equals(apnSetting)) {
+ //This ignore network type as a check which is ok because that's checked
+ //when calculating dun candidates.
+ if (areCompatible(dunSetting, apnSetting)) {
if (curDc.isActive()) {
if (DBG) {
log("checkForCompatibleDataConnection:"
@@ -2397,14 +2608,15 @@ public class DcTracker extends Handler {
}
}
}
- } else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
+ } else if (isApnSettingCompatible(curDc, apnType)) {
if (curDc.isActive()) {
if (DBG) {
log("checkForCompatibleDataConnection:"
+ " found canHandle conn=" + curDc);
}
return curDc;
- } else if (curDc.isActivating()) {
+ } else if (curDc.isActivating()
+ || (apnSetting != null && apnSetting.equals(nextApn))) {
potentialDc = curDc;
}
}
@@ -2417,47 +2629,109 @@ public class DcTracker extends Handler {
return potentialDc;
}
- private void addRequestNetworkCompleteMsg(Message onCompleteMsg,
- @ApnType int apnType) {
+ private boolean isApnSettingCompatible(DataConnection dc, int apnType) {
+ ApnSetting apnSetting = dc.getApnSetting();
+ if (apnSetting == null) return false;
+
+ // Nothing can be compatible with type ENTERPRISE
+ for (ApnContext apnContext : dc.getApnContexts()) {
+ if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
+ return false;
+ }
+ }
+
+ return apnSetting.canHandleType(apnType);
+ }
+
+ private void addHandoverCompleteMsg(Message onCompleteMsg,
+ @ApnType int apnType) {
if (onCompleteMsg != null) {
- List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
+ List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
if (messageList == null) messageList = new ArrayList<>();
messageList.add(onCompleteMsg);
- mRequestNetworkCompletionMsgs.put(apnType, messageList);
+ mHandoverCompletionMsgs.put(apnType, messageList);
}
}
- private void sendRequestNetworkCompleteMsg(Message message, boolean success,
- @TransportType int transport,
- @RequestNetworkType int requestType,
- @DataFailureCause int cause) {
+ private void sendHandoverCompleteMessages(@ApnType int apnType, boolean success,
+ boolean fallbackOnFailedHandover) {
+ List<Message> messageList = mHandoverCompletionMsgs.get(apnType);
+ if (messageList != null) {
+ for (Message msg : messageList) {
+ sendHandoverCompleteMsg(msg, success, mTransportType, fallbackOnFailedHandover);
+ }
+ messageList.clear();
+ }
+ }
+
+ private void sendHandoverCompleteMsg(Message message, boolean success,
+ @TransportType int transport, boolean doFallbackOnFailedHandover) {
if (message == null) return;
Bundle b = message.getData();
b.putBoolean(DATA_COMPLETE_MSG_EXTRA_SUCCESS, success);
- b.putInt(DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE, requestType);
b.putInt(DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE, transport);
- // TODO: For now this is the only fail cause that we know modem keeps data connection on
- // original transport. Might add more complicated logic or mapping in the future.
- b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK,
- (requestType == REQUEST_TYPE_HANDOVER
- && cause == DataFailCause.HANDOFF_PREFERENCE_CHANGED));
+ b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK, doFallbackOnFailedHandover);
message.sendToTarget();
}
+ private static boolean shouldFallbackOnFailedHandover(
+ @HandoverFailureMode int handoverFailureMode,
+ @RequestNetworkType int requestType,
+ @DataFailureCause int cause) {
+ if (requestType != REQUEST_TYPE_HANDOVER) {
+ //The fallback is only relevant if the request is a handover
+ return false;
+ } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_DO_FALLBACK) {
+ return true;
+ } else if (handoverFailureMode == HANDOVER_FAILURE_MODE_LEGACY) {
+ return cause == DataFailCause.HANDOFF_PREFERENCE_CHANGED;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Calculates the new request type that will be used the next time a data connection retries
+ * after a failed data call attempt.
+ */
+ @RequestNetworkType
+ public static int calculateNewRetryRequestType(@HandoverFailureMode int handoverFailureMode,
+ @RequestNetworkType int requestType,
+ @DataFailureCause int cause) {
+ boolean fallbackOnFailedHandover =
+ shouldFallbackOnFailedHandover(handoverFailureMode, requestType, cause);
+ if (requestType != REQUEST_TYPE_HANDOVER) {
+ //The fallback is only relevant if the request is a handover
+ return requestType;
+ }
+
+ if (fallbackOnFailedHandover) {
+ // Since fallback is happening, the request type is really "NONE".
+ return REQUEST_TYPE_NORMAL;
+ }
+
+ if (handoverFailureMode == HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL) {
+ return REQUEST_TYPE_NORMAL;
+ }
+
+ return REQUEST_TYPE_HANDOVER;
+ }
+
public void enableApn(@ApnType int apnType, @RequestNetworkType int requestType,
- Message onCompleteMsg) {
+ Message onHandoverCompleteMsg) {
sendMessage(obtainMessage(DctConstants.EVENT_ENABLE_APN, apnType, requestType,
- onCompleteMsg));
+ onHandoverCompleteMsg));
}
private void onEnableApn(@ApnType int apnType, @RequestNetworkType int requestType,
- Message onCompleteMsg) {
+ Message onHandoverCompleteMsg) {
ApnContext apnContext = mApnContextsByType.get(apnType);
if (apnContext == null) {
loge("onEnableApn(" + apnType + "): NO ApnContext");
- sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
- DataFailCause.NONE);
+ if (onHandoverCompleteMsg != null) {
+ sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
+ }
return;
}
@@ -2472,8 +2746,9 @@ public class DcTracker extends Handler {
str = "onEnableApn: dependency is not met.";
if (DBG) log(str);
apnContext.requestLog(str);
- sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
- DataFailCause.NONE);
+ if (onHandoverCompleteMsg != null) {
+ sendHandoverCompleteMsg(onHandoverCompleteMsg, false, mTransportType, false);
+ }
return;
}
@@ -2481,24 +2756,29 @@ public class DcTracker extends Handler {
DctConstants.State state = apnContext.getState();
switch(state) {
case CONNECTING:
- if (DBG) log("onEnableApn: 'CONNECTING' so return");
- apnContext.requestLog("onEnableApn state=CONNECTING, so return");
- addRequestNetworkCompleteMsg(onCompleteMsg, apnType);
+ if (onHandoverCompleteMsg != null) {
+ if (DBG) {
+ log("onEnableApn: already in CONNECTING state. Handover request "
+ + "will be responded after connected.");
+ }
+ addHandoverCompleteMsg(onHandoverCompleteMsg, apnType);
+ } else {
+ if (DBG) log("onEnableApn: in CONNECTING state. Exit now.");
+ }
return;
case CONNECTED:
- if (DBG) log("onEnableApn: 'CONNECTED' so return");
- // Don't add to local log since this is so common
- sendRequestNetworkCompleteMsg(onCompleteMsg, true, mTransportType,
- requestType, DataFailCause.NONE);
- return;
- case DISCONNECTING:
- if (DBG) log("onEnableApn: 'DISCONNECTING' so return");
- apnContext.requestLog("onEnableApn state=DISCONNECTING, so return");
- sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
- requestType, DataFailCause.NONE);
+ if (onHandoverCompleteMsg != null) {
+ sendHandoverCompleteMsg(onHandoverCompleteMsg, true, mTransportType,
+ false);
+ if (DBG) {
+ log("onEnableApn: already in CONNECTED state. Consider as handover "
+ + "succeeded");
+ }
+ } else {
+ if (DBG) log("onEnableApn: APN in CONNECTED state. Exit now.");
+ }
return;
case IDLE:
- // fall through: this is unexpected but if it happens cleanup and try setup
case FAILED:
case RETRYING:
// We're "READY" but not active so disconnect (cleanup = true) and
@@ -2520,12 +2800,7 @@ public class DcTracker extends Handler {
apnContext.resetErrorCodeRetries();
if (mConfigReady || apnContext.getApnTypeBitmask() == ApnSetting.TYPE_EMERGENCY) {
- if (trySetupData(apnContext, requestType)) {
- addRequestNetworkCompleteMsg(onCompleteMsg, apnType);
- } else {
- sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
- requestType, DataFailCause.NONE);
- }
+ trySetupData(apnContext, requestType, onHandoverCompleteMsg);
} else {
log("onEnableApn: config not ready yet.");
}
@@ -2561,7 +2836,7 @@ public class DcTracker extends Handler {
// This doesn't apply to DUN. When the user disable tethering, we would like to
// detach the APN context from the data connection so the data connection can be
// torn down if no other APN context attached to it.
- if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())
+ if (ApnSetting.TYPE_DUN_STRING.equals(apnContext.getApnType())
|| apnContext.getState() != DctConstants.State.CONNECTED) {
str = "Clean up the connection. Apn type = " + apnContext.getApnType()
+ ", state = " + apnContext.getState();
@@ -2648,8 +2923,7 @@ public class DcTracker extends Handler {
// since we don't want to unset user preference from system update, pass true as the default
// value if shared pref does not exist and set shared pref to false explicitly from factory
// reset.
- if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)
- && Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+ if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)) {
sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
}
return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true);
@@ -2679,8 +2953,6 @@ public class DcTracker extends Handler {
// non-roaming, we should try to reestablish the data connection.
setupDataOnAllConnectableApns(Phone.REASON_ROAMING_OFF, RetryFailures.ALWAYS);
- } else {
- mPhone.notifyAllActiveDataConnections();
}
}
@@ -2711,7 +2983,6 @@ public class DcTracker extends Handler {
if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming");
setupDataOnAllConnectableApns(Phone.REASON_ROAMING_ON, RetryFailures.ALWAYS);
- mPhone.notifyAllActiveDataConnections();
} else {
// If the user does not turn on data roaming, when we transit from non-roaming to
// roaming, we need to tear down the data connection otherwise the user might be
@@ -2739,16 +3010,7 @@ public class DcTracker extends Handler {
private void onRadioAvailable() {
if (DBG) log("onRadioAvailable");
- if (mPhone.getSimulatedRadioControl() != null) {
- // Assume data is connected on the simulator
- // FIXME this can be improved
- // setState(DctConstants.State.CONNECTED);
- mPhone.notifyAllActiveDataConnections();
-
- log("onRadioAvailable: We're on the simulator; assuming data is connected");
- }
-
- if (getOverallState() != DctConstants.State.IDLE) {
+ if (!areAllDataDisconnected()) {
cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, null);
}
}
@@ -2799,25 +3061,39 @@ public class DcTracker extends Handler {
mProvisioningSpinner));
}
- mPhone.notifyDataConnection(apnContext.getApnType());
-
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
+
+ PersistableBundle b = getCarrierConfig();
+ if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
+ && b.getBoolean(CarrierConfigManager
+ .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
+ NotificationManager notificationManager = (NotificationManager)
+ mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(Integer.toString(mPhone.getSubId()),
+ NO_DATA_NOTIFICATION);
+ }
}
/**
* A SETUP (aka bringUp) has completed, possibly with an error. If
* there is an error this method will call {@link #onDataSetupCompleteError}.
*/
- protected void onDataSetupComplete(ApnContext apnContext, boolean success, int cause,
- @RequestNetworkType int requestType) {
- int apnType = ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType());
- List<Message> messageList = mRequestNetworkCompletionMsgs.get(apnType);
- if (messageList != null) {
- for (Message msg : messageList) {
- sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType, cause);
- }
- messageList.clear();
+ protected void onDataSetupComplete(ApnContext apnContext, boolean success,
+ @DataFailureCause int cause, @RequestNetworkType int requestType,
+ @HandoverFailureMode int handoverFailureMode) {
+ boolean fallbackOnFailedHandover = shouldFallbackOnFailedHandover(
+ handoverFailureMode, requestType, cause);
+
+ if (success && (handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN
+ && handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY)) {
+ Log.wtf(mLogTag, "bad failure mode: "
+ + DataCallResponse.failureModeToString(handoverFailureMode));
+ } else if (handoverFailureMode
+ != DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER
+ && cause != DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE) {
+ sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), success,
+ fallbackOnFailedHandover);
}
if (success) {
@@ -2843,30 +3119,16 @@ public class DcTracker extends Handler {
}
if (dataConnection == null) {
log("onDataSetupComplete: no connection to DC, handle as error");
- onDataSetupCompleteError(apnContext, requestType);
+ onDataSetupCompleteError(apnContext, requestType, false);
} else {
ApnSetting apn = apnContext.getApnSetting();
if (DBG) {
log("onDataSetupComplete: success apn=" + (apn == null ? "unknown"
: apn.getApnName()));
}
- if (apn != null && !TextUtils.isEmpty(apn.getProxyAddressAsString())) {
- try {
- int port = apn.getProxyPort();
- if (port == -1) {
- port = 8080;
- }
- ProxyInfo proxy = ProxyInfo.buildDirectProxy(
- apn.getProxyAddressAsString(), port);
- dataConnection.setLinkPropertiesHttpProxy(proxy);
- } catch (NumberFormatException e) {
- loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("
- + apn.getProxyPort() + "): " + e);
- }
- }
// everything is setup
- if (TextUtils.equals(apnContext.getApnType(), PhoneConstants.APN_TYPE_DEFAULT)
+ if (TextUtils.equals(apnContext.getApnType(), ApnSetting.TYPE_DEFAULT_STRING)
&& mCanSetPreferApn && mPreferredApn == null) {
if (DBG) log("onDataSetupComplete: PREFERRED APN is null");
mPreferredApn = apn;
@@ -2887,10 +3149,13 @@ public class DcTracker extends Handler {
mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
mProvisionBroadcastReceiver = null;
}
+
if ((!isProvApn) || mIsProvisioning) {
- // Hide any provisioning notification.
- cm.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_MOBILE,
- mProvisionActionName);
+ if (mIsProvisioning) {
+ // Hide any notification that was showing previously
+ hideProvisioningNotification();
+ }
+
// Complete the connection normally notifying the world we're connected.
// We do this if this isn't a special provisioning apn or if we've been
// told its time to provision.
@@ -2910,13 +3175,14 @@ public class DcTracker extends Handler {
// While radio is up, grab provisioning URL. The URL contains ICCID which
// disappears when radio is off.
mProvisionBroadcastReceiver = new ProvisionNotificationBroadcastReceiver(
- cm.getMobileProvisioningUrl(),
+ mPhone.getMobileProvisioningUrl(),
mTelephonyManager.getNetworkOperatorName());
mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver,
- new IntentFilter(mProvisionActionName));
+ new IntentFilter(INTENT_PROVISION));
+
// Put up user notification that sign-in is required.
- cm.setProvisioningNotificationVisible(true, ConnectivityManager.TYPE_MOBILE,
- mProvisionActionName);
+ showProvisioningNotification();
+
// Turn off radio to save battery and avoid wasting carrier resources.
// The network isn't usable and network validation will just fail anyhow.
setRadio(false);
@@ -2934,10 +3200,9 @@ public class DcTracker extends Handler {
value[0] = (byte) pcoVal;
final Intent intent =
new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE);
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, "default");
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE_INT, TYPE_DEFAULT);
- intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL, "IPV4V6");
- intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL_INT, PROTOCOL_IPV4V6);
+ intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, ApnSetting.TYPE_DEFAULT);
+ intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL,
+ ApnSetting.PROTOCOL_IPV4V6);
intent.putExtra(TelephonyManager.EXTRA_PCO_ID, 0xFF00);
intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, value);
mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
@@ -2947,8 +3212,9 @@ public class DcTracker extends Handler {
} else {
if (DBG) {
ApnSetting apn = apnContext.getApnSetting();
- log("onDataSetupComplete: error apn=" + apn.getApnName() + ", cause=" + cause
- + ", requestType=" + requestTypeToString(requestType));
+ log("onDataSetupComplete: error apn=" + apn.getApnName() + ", cause="
+ + DataFailCause.toString(cause) + ", requestType="
+ + requestTypeToString(requestType));
}
if (DataFailCause.isEventLoggable(cause)) {
// Log this failure to the Event Logs.
@@ -2957,15 +3223,12 @@ public class DcTracker extends Handler {
cause, cid, mTelephonyManager.getNetworkType());
}
ApnSetting apn = apnContext.getApnSetting();
- mPhone.notifyDataConnectionFailed(apnContext.getApnType(),
- apn != null ? apn.getApnName() : null, cause);
// Compose broadcast intent send to the specific carrier signaling receivers
Intent intent = new Intent(TelephonyManager
.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
- intent.putExtra(TelephonyManager.EXTRA_ERROR_CODE, cause);
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, apnContext.getApnType());
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE_INT,
+ intent.putExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE, cause);
+ intent.putExtra(TelephonyManager.EXTRA_APN_TYPE,
ApnSetting.getApnTypesBitmaskFromString(apnContext.getApnType()));
mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
@@ -2978,40 +3241,74 @@ public class DcTracker extends Handler {
// If the data call failure cause is a permanent failure, we mark the APN as permanent
// failed.
if (isPermanentFailure(cause)) {
- log("cause = " + cause + ", mark apn as permanent failed. apn = " + apn);
+ log("cause=" + DataFailCause.toString(cause)
+ + ", mark apn as permanent failed. apn = " + apn);
apnContext.markApnPermanentFailed(apn);
+
+ PersistableBundle b = getCarrierConfig();
+ if (apnContext.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT
+ && b.getBoolean(CarrierConfigManager
+ .KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL)) {
+ NotificationManager notificationManager = (NotificationManager)
+ mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+
+ CharSequence title = mPhone.getContext().getText(
+ com.android.internal.R.string.RestrictedOnDataTitle);
+ CharSequence details = mPhone.getContext().getText(
+ com.android.internal.R.string.RestrictedStateContent);
+
+ Notification notification = new Notification.Builder(mPhone.getContext(),
+ NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
+ .setWhen(System.currentTimeMillis())
+ .setAutoCancel(true)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+ .setTicker(title)
+ .setColor(mPhone.getContext().getResources().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setStyle(new Notification.BigTextStyle().bigText(details))
+ .setContentText(details)
+ .build();
+ notificationManager.notify(Integer.toString(mPhone.getSubId()),
+ NO_DATA_NOTIFICATION, notification);
+ }
}
- onDataSetupCompleteError(apnContext, requestType);
+
+ int newRequestType = calculateNewRetryRequestType(handoverFailureMode, requestType,
+ cause);
+ onDataSetupCompleteError(apnContext, newRequestType, fallbackOnFailedHandover);
}
}
+
+
/**
* Error has occurred during the SETUP {aka bringUP} request and the DCT
* should either try the next waiting APN or start over from the
* beginning if the list is empty. Between each SETUP request there will
- * be a delay defined by {@link #getApnDelay()}.
+ * be a delay defined by {@link ApnContext#getDelayForNextApn(boolean)}.
*/
protected void onDataSetupCompleteError(ApnContext apnContext,
- @RequestNetworkType int requestType) {
+ @RequestNetworkType int requestType, boolean fallbackOnFailedHandover) {
long delay = apnContext.getDelayForNextApn(mFailFast);
-
// Check if we need to retry or not.
- // TODO: We should support handover retry in the future.
- if (delay >= 0) {
- if (DBG) log("onDataSetupCompleteError: Try next APN. delay = " + delay);
- apnContext.setState(DctConstants.State.RETRYING);
- // Wait a bit before trying the next APN, so that
- // we're not tying up the RIL command channel
-
- startReconnect(delay, apnContext);
+ if (delay >= 0 && delay != RetryManager.NO_RETRY && !fallbackOnFailedHandover) {
+ if (DBG) {
+ log("onDataSetupCompleteError: APN type=" + apnContext.getApnType()
+ + ". Request type=" + requestTypeToString(requestType) + ", Retry in "
+ + delay + "ms.");
+ }
+ startReconnect(delay, apnContext, requestType);
} else {
// If we are not going to retry any APN, set this APN context to failed state.
// This would be the final state of a data connection.
apnContext.setState(DctConstants.State.FAILED);
- mPhone.notifyDataConnection(apnContext.getApnType());
apnContext.setDataConnection(null);
log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay
+ ", requestType=" + requestTypeToString(requestType));
+ //send request network complete messages as needed
+ sendHandoverCompleteMessages(apnContext.getApnTypeBitmask(), false,
+ fallbackOnFailedHandover);
}
}
@@ -3053,37 +3350,18 @@ public class DcTracker extends Handler {
private void onDisconnectDone(ApnContext apnContext) {
if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
apnContext.setState(DctConstants.State.IDLE);
- final DataConnection dc = apnContext.getDataConnection();
- // when data connection is gone and not for handover, notify all apn types which
- // this data connection can handle. Note, this might not work if one apn type served for
- // multiple data connection.
- if (dc != null && dc.isInactive() && !dc.hasBeenTransferred()) {
- String[] types = ApnSetting.getApnTypesStringFromBitmask(
- apnContext.getApnSetting().getApnTypeBitmask()).split(",");
- for (String type : types) {
- mPhone.notifyDataConnection(type);
- }
- }
- // if all data connection are gone, check whether Airplane mode request was
- // pending.
- if (isDisconnected()) {
- if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
- if (DBG) log("onDisconnectDone: radio will be turned off, no retries");
- // Radio will be turned off. No need to retry data setup
- apnContext.setApnSetting(null);
- apnContext.setDataConnection(null);
-
- // Need to notify disconnect as well, in the case of switching Airplane mode.
- // Otherwise, it would cause 30s delayed to turn on Airplane mode.
- if (mDisconnectPendingCount > 0) {
- mDisconnectPendingCount--;
- }
+ // If all data connection are gone, check whether Airplane mode request was pending.
+ if (areAllDataDisconnected()
+ && mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
+ if (DBG) log("onDisconnectDone: radio will be turned off, no retries");
+ // Radio will be turned off. No need to retry data setup
+ apnContext.setApnSetting(null);
+ apnContext.setDataConnection(null);
- if (mDisconnectPendingCount == 0) {
- notifyAllDataDisconnected();
- }
- return;
- }
+ // Need to notify disconnect as well, in the case of switching Airplane mode.
+ // Otherwise, it would cause 30s delayed to turn on Airplane mode.
+ notifyAllDataDisconnected();
+ return;
}
// If APN is still enabled, try to bring it back up automatically
if (mAttached.get() && apnContext.isReady() && retryAfterDisconnected(apnContext)) {
@@ -3091,11 +3369,19 @@ public class DcTracker extends Handler {
// we're not tying up the RIL command channel.
// This also helps in any external dependency to turn off the context.
if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
- long delay = apnContext.getRetryAfterDisconnectDelay();
- if (delay > 0) {
- // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
- // the waiting APN list, which will also reset/reconfigure the retry manager.
- startReconnect(delay, apnContext);
+
+ // See if there are still handover request pending that we need to retry handover
+ // after previous data gets disconnected.
+ if (isHandoverPending(apnContext.getApnTypeBitmask())) {
+ if (DBG) log("Handover request pending. Retry handover immediately.");
+ startReconnect(0, apnContext, REQUEST_TYPE_HANDOVER);
+ } else {
+ long delay = apnContext.getRetryAfterDisconnectDelay();
+ if (delay > 0) {
+ // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
+ // the waiting APN list, which will also reset/reconfigure the retry manager.
+ startReconnect(delay, apnContext, REQUEST_TYPE_NORMAL);
+ }
}
} else {
boolean restartRadioAfterProvisioning = mPhone.getContext().getResources().getBoolean(
@@ -3116,10 +3402,7 @@ public class DcTracker extends Handler {
}
}
- if (mDisconnectPendingCount > 0)
- mDisconnectPendingCount--;
-
- if (mDisconnectPendingCount == 0) {
+ if (areAllDataDisconnected()) {
apnContext.setConcurrentVoiceAndDataAllowed(
mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed());
notifyAllDataDisconnected();
@@ -3130,22 +3413,21 @@ public class DcTracker extends Handler {
private void onVoiceCallStarted() {
if (DBG) log("onVoiceCallStarted");
mInVoiceCall = true;
- if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
+ if (isAnyDataConnected()
+ && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
if (DBG) log("onVoiceCallStarted stop polling");
stopNetStatPoll();
stopDataStallAlarm();
- mPhone.notifyAllActiveDataConnections();
}
}
protected void onVoiceCallEnded() {
if (DBG) log("onVoiceCallEnded");
mInVoiceCall = false;
- if (isConnected()) {
+ if (isAnyDataConnected()) {
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- mPhone.notifyAllActiveDataConnections();
} else {
// clean slate after call end.
resetPollStats();
@@ -3154,26 +3436,29 @@ public class DcTracker extends Handler {
// reset reconnect timer
setupDataOnAllConnectableApns(Phone.REASON_VOICE_CALL_ENDED, RetryFailures.ALWAYS);
}
-
- protected boolean isConnected() {
- for (ApnContext apnContext : mApnContexts.values()) {
- if (apnContext.getState() == DctConstants.State.CONNECTED) {
- // At least one context is connected, return true
+ /**
+ * @return {@code true} if there is any data in connected state.
+ */
+ @VisibleForTesting
+ public boolean isAnyDataConnected() {
+ for (DataConnection dc : mDataConnections.values()) {
+ if (dc.isActive()) {
return true;
}
}
- // There are not any contexts connected, return false
return false;
}
- public boolean isDisconnected() {
- for (ApnContext apnContext : mApnContexts.values()) {
- if (!apnContext.isDisconnected()) {
- // At least one context was not disconnected return false
+ /**
+ * @return {@code true} if all data connections are in disconnected state.
+ */
+ public boolean areAllDataDisconnected() {
+ for (DataConnection dc : mDataConnections.values()) {
+ if (!dc.isInactive()) {
+ if (DBG) log("areAllDataDisconnected false due to DC: " + dc.getName());
return false;
}
}
- // All contexts were disconnected so return true
return true;
}
@@ -3182,10 +3467,19 @@ public class DcTracker extends Handler {
ArrayList<DataProfile> dataProfileList = new ArrayList<>();
+ int preferredApnSetId = getPreferredApnSetId();
for (ApnSetting apn : mAllApnSettings) {
- DataProfile dp = createDataProfile(apn, apn.equals(getPreferredApn()));
- if (!dataProfileList.contains(dp)) {
- dataProfileList.add(dp);
+ if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID
+ || preferredApnSetId == apn.getApnSetId()) {
+ DataProfile dp = createDataProfile(apn, apn.equals(getPreferredApn()));
+ if (!dataProfileList.contains(dp)) {
+ dataProfileList.add(dp);
+ }
+ } else {
+ if (VDBG) {
+ log("setDataProfilesAsNeeded: APN set id " + apn.getApnSetId()
+ + " does not match the preferred set id " + preferredApnSetId);
+ }
}
}
@@ -3228,8 +3522,6 @@ public class DcTracker extends Handler {
+ operator);
}
- addEmergencyApnSetting();
-
dedupeApnSettings();
if (mAllApnSettings.isEmpty()) {
@@ -3237,8 +3529,6 @@ public class DcTracker extends Handler {
mApnSettingsInitializationLog.log("no APN found for carrier, operator: "
+ operator);
mPreferredApn = null;
- // Notify that there are no APN Settings,
- mPhone.notifyDataConnectionFailed(null, null, DataFailCause.MISSING_UNKNOWN_APN);
} else {
mPreferredApn = getPreferredApn();
if (mPreferredApn != null && !mPreferredApn.getOperatorNumeric().equals(operator)) {
@@ -3247,6 +3537,8 @@ public class DcTracker extends Handler {
}
if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
}
+
+ addDefaultApnSettingsAsNeeded();
if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);
}
@@ -3310,8 +3602,10 @@ public class DcTracker extends Handler {
if (DBG) log("createDataConnection E");
int id = mUniqueIdGenerator.getAndIncrement();
+ boolean doAllocatePduSessionId =
+ mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
DataConnection dataConnection = DataConnection.makeDataConnection(mPhone, id, this,
- mDataServiceManager, mDcTesterFailBringUpAll, mDcc);
+ mDataServiceManager, mDcTesterFailBringUpAll, mDcc, doAllocatePduSessionId);
mDataConnections.put(id, dataConnection);
if (DBG) log("createDataConnection() X id=" + id + " dc=" + dataConnection);
return dataConnection;
@@ -3333,11 +3627,15 @@ public class DcTracker extends Handler {
* @return waitingApns list to be used to create PDP
* error when waitingApns.isEmpty()
*/
- private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, int radioTech) {
+ private @NonNull ArrayList<ApnSetting> buildWaitingApns(String requestedApnType,
+ int radioTech) {
if (DBG) log("buildWaitingApns: E requestedApnType=" + requestedApnType);
ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
int requestedApnTypeBitmask = ApnSetting.getApnTypesBitmaskFromString(requestedApnType);
+ if (requestedApnTypeBitmask == ApnSetting.TYPE_ENTERPRISE) {
+ requestedApnTypeBitmask = ApnSetting.TYPE_DEFAULT;
+ }
if (requestedApnTypeBitmask == ApnSetting.TYPE_DUN) {
ArrayList<ApnSetting> dunApns = fetchDunApns();
if (dunApns.size() > 0) {
@@ -3345,7 +3643,7 @@ public class DcTracker extends Handler {
apnList.add(dun);
if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
}
- return sortApnListByPreferred(apnList);
+ return apnList;
}
}
@@ -3383,8 +3681,15 @@ public class DcTracker extends Handler {
if (mPreferredApn.getOperatorNumeric().equals(operator)) {
if (mPreferredApn.canSupportNetworkType(
ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
- apnList.add(mPreferredApn);
- apnList = sortApnListByPreferred(apnList);
+ // Create a new instance of ApnSetting for ENTERPRISE because each
+ // DataConnection should have its own ApnSetting. ENTERPRISE uses the same
+ // APN as DEFAULT but is a separate DataConnection
+ if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType)
+ == ApnSetting.TYPE_ENTERPRISE) {
+ apnList.add(ApnSetting.makeApnSetting(mPreferredApn));
+ } else {
+ apnList.add(mPreferredApn);
+ }
if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
return apnList;
}
@@ -3395,12 +3700,27 @@ public class DcTracker extends Handler {
}
if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
+ int preferredApnSetId = getPreferredApnSetId();
for (ApnSetting apn : mAllApnSettings) {
if (apn.canHandleType(requestedApnTypeBitmask)) {
if (apn.canSupportNetworkType(
ServiceState.rilRadioTechnologyToNetworkType(radioTech))) {
- if (VDBG) log("buildWaitingApns: adding apn=" + apn);
- apnList.add(apn);
+ if (apn.getApnSetId() == Telephony.Carriers.MATCH_ALL_APN_SET_ID
+ || preferredApnSetId == apn.getApnSetId()) {
+ if (VDBG) log("buildWaitingApns: adding apn=" + apn);
+ // Create a new instance of ApnSetting for ENTERPRISE because each
+ // DataConnection should have its own ApnSetting. ENTERPRISE uses the same
+ // APN as DEFAULT but is a separate DataConnection
+ if (ApnSetting.getApnTypesBitmaskFromString(requestedApnType)
+ == ApnSetting.TYPE_ENTERPRISE) {
+ apnList.add(ApnSetting.makeApnSetting(apn));
+ } else {
+ apnList.add(apn);
+ }
+ } else {
+ log("buildWaitingApns: APN set id " + apn.getApnSetId()
+ + " does not match the preferred set id " + preferredApnSetId);
+ }
} else {
if (DBG) {
log("buildWaitingApns: networkTypeBitmask:"
@@ -3415,44 +3735,10 @@ public class DcTracker extends Handler {
}
}
- apnList = sortApnListByPreferred(apnList);
if (DBG) log("buildWaitingApns: " + apnList.size() + " APNs in the list: " + apnList);
return apnList;
}
- /**
- * Sort a list of ApnSetting objects, with the preferred APNs at the front of the list
- *
- * e.g. if the preferred APN set = 2 and we have
- * 1. APN with apn_set_id = 0 = Carriers.NO_SET_SET (no set is set)
- * 2. APN with apn_set_id = 1 (not preferred set)
- * 3. APN with apn_set_id = 2 (preferred set)
- * Then the return order should be (3, 1, 2) or (3, 2, 1)
- *
- * e.g. if the preferred APN set = Carriers.NO_SET_SET (no preferred set) then the
- * return order can be anything
- */
- @VisibleForTesting
- public ArrayList<ApnSetting> sortApnListByPreferred(ArrayList<ApnSetting> list) {
- if (list == null || list.size() <= 1) return list;
- int preferredApnSetId = getPreferredApnSetId();
- if (preferredApnSetId != Telephony.Carriers.NO_APN_SET_ID) {
- list.sort(new Comparator<ApnSetting>() {
- @Override
- public int compare(ApnSetting apn1, ApnSetting apn2) {
- if (apn1.getApnSetId() == preferredApnSetId) {
- return -1;
- }
- if (apn2.getApnSetId() == preferredApnSetId) {
- return 1;
- }
- return 0;
- }
- });
- }
- return list;
- }
-
private String apnListToString (ArrayList<ApnSetting> apns) {
StringBuilder result = new StringBuilder();
for (int i = 0, size = apns.size(); i < size; i++) {
@@ -3464,7 +3750,11 @@ public class DcTracker extends Handler {
}
private void setPreferredApn(int pos) {
- if (!mCanSetPreferApn) {
+ setPreferredApn(pos, false);
+ }
+
+ private void setPreferredApn(int pos, boolean force) {
+ if (!force && !mCanSetPreferApn) {
log("setPreferredApn: X !canSEtPreferApn");
return;
}
@@ -3515,8 +3805,8 @@ public class DcTracker extends Handler {
for(ApnSetting p : mAllApnSettings) {
if (p.getId() == pos && p.canHandleType(mRequestedApnType)) {
log("getPreferredApn: For APN type "
- + ApnSetting.getApnTypeString(mRequestedApnType) + " found apnSetting "
- + p);
+ + ApnSetting.getApnTypeString(mRequestedApnType)
+ + " found apnSetting " + p);
cursor.close();
return p;
}
@@ -3540,6 +3830,7 @@ public class DcTracker extends Handler {
ApnContext apnContext;
int generation;
int requestType;
+ int handoverFailureMode;
switch (msg.what) {
case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
onDataConnectionDetached();
@@ -3577,7 +3868,7 @@ public class DcTracker extends Handler {
*/
if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
mIsPsRestricted = false;
- if (isConnected()) {
+ if (isAnyDataConnected()) {
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
} else {
@@ -3589,7 +3880,7 @@ public class DcTracker extends Handler {
apnContext = mApnContextsByType.get(ApnSetting.TYPE_DEFAULT);
if (apnContext != null) {
apnContext.setReason(Phone.REASON_PS_RESTRICT_ENABLED);
- trySetupData(apnContext, REQUEST_TYPE_NORMAL);
+ trySetupData(apnContext, REQUEST_TYPE_NORMAL, null);
} else {
loge("**** Default ApnContext not found ****");
if (TelephonyUtils.IS_DEBUGGABLE) {
@@ -3600,9 +3891,10 @@ public class DcTracker extends Handler {
break;
case DctConstants.EVENT_TRY_SETUP_DATA:
- trySetupData((ApnContext) msg.obj, REQUEST_TYPE_NORMAL);
+ apnContext = (ApnContext) msg.obj;
+ requestType = msg.arg1;
+ trySetupData(apnContext, requestType, null);
break;
-
case DctConstants.EVENT_CLEAN_UP_CONNECTION:
if (DBG) log("EVENT_CLEAN_UP_CONNECTION");
cleanUpConnectionInternal(true, RELEASE_TYPE_DETACH, (ApnContext) msg.obj);
@@ -3688,7 +3980,8 @@ public class DcTracker extends Handler {
pair = (Pair<ApnContext, Integer>) ar.userObj;
apnContext = pair.first;
generation = pair.second;
- requestType = msg.arg2;
+ requestType = msg.arg1;
+ handoverFailureMode = msg.arg2;
if (apnContext.getConnectionGeneration() == generation) {
boolean success = true;
int cause = DataFailCause.UNKNOWN;
@@ -3696,7 +3989,8 @@ public class DcTracker extends Handler {
success = false;
cause = (int) ar.result;
}
- onDataSetupComplete(apnContext, success, cause, requestType);
+ onDataSetupComplete(apnContext, success, cause, requestType,
+ handoverFailureMode);
} else {
loge("EVENT_DATA_SETUP_COMPLETE: Dropped the event because generation "
+ "did not match.");
@@ -3708,9 +4002,9 @@ public class DcTracker extends Handler {
pair = (Pair<ApnContext, Integer>) ar.userObj;
apnContext = pair.first;
generation = pair.second;
- requestType = msg.arg2;
+ handoverFailureMode = msg.arg2;
if (apnContext.getConnectionGeneration() == generation) {
- onDataSetupCompleteError(apnContext, requestType);
+ onDataSetupCompleteError(apnContext, handoverFailureMode, false);
} else {
loge("EVENT_DATA_SETUP_COMPLETE_ERROR: Dropped the event because generation "
+ "did not match.");
@@ -3760,7 +4054,7 @@ public class DcTracker extends Handler {
mDataStallNoRxEnabled = !enabled;
if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled()
- && (getOverallState() == DctConstants.State.CONNECTED)
+ && isAnyDataConnected()
&& (!mInVoiceCall ||
mPhone.getServiceStateTracker()
.isConcurrentVoiceAndDataAllowed())) {
@@ -3859,8 +4153,11 @@ public class DcTracker extends Handler {
break;
}
case DctConstants.EVENT_DATA_RECONNECT:
- if (DBG) log("EVENT_DATA_RECONNECT: subId=" + msg.arg1);
- onDataReconnect((ApnContext) msg.obj, msg.arg1);
+ if (DBG) {
+ log("EVENT_DATA_RECONNECT: subId=" + msg.arg1 + ", type="
+ + requestTypeToString(msg.arg2));
+ }
+ onDataReconnect((ApnContext) msg.obj, msg.arg1, msg.arg2);
break;
case DctConstants.EVENT_DATA_SERVICE_BINDING_CHANGED:
onDataServiceBindingChanged((Boolean) ((AsyncResult) msg.obj).result);
@@ -3882,6 +4179,7 @@ public class DcTracker extends Handler {
reevaluateUnmeteredConnections();
break;
case DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED:
+ reevaluateCongestedConnections();
reevaluateUnmeteredConnections();
break;
case DctConstants.EVENT_CARRIER_CONFIG_CHANGED:
@@ -3891,6 +4189,11 @@ public class DcTracker extends Handler {
int simState = msg.arg1;
onSimStateUpdated(simState);
break;
+ case DctConstants.EVENT_APN_UNTHROTTLED:
+ ar = (AsyncResult) msg.obj;
+ String apn = (String) ar.result;
+ onApnUnthrottled(apn);
+ break;
default:
Rlog.e("DcTracker", "Unhandled event=" + msg);
break;
@@ -3899,15 +4202,15 @@ public class DcTracker extends Handler {
}
private int getApnProfileID(String apnType) {
- if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
+ if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) {
return RILConstants.DATA_PROFILE_IMS;
- } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_FOTA)) {
+ } else if (TextUtils.equals(apnType, ApnSetting.TYPE_FOTA_STRING)) {
return RILConstants.DATA_PROFILE_FOTA;
- } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_CBS)) {
+ } else if (TextUtils.equals(apnType, ApnSetting.TYPE_CBS_STRING)) {
return RILConstants.DATA_PROFILE_CBS;
- } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IA)) {
+ } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IA_STRING)) {
return RILConstants.DATA_PROFILE_DEFAULT; // DEFAULT for now
- } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_DUN)) {
+ } else if (TextUtils.equals(apnType, ApnSetting.TYPE_DUN_STRING)) {
return RILConstants.DATA_PROFILE_TETHERED;
} else {
return RILConstants.DATA_PROFILE_DEFAULT;
@@ -3916,7 +4219,7 @@ public class DcTracker extends Handler {
private int getCellLocationId() {
int cid = -1;
- CellLocation loc = mPhone.getCellIdentity().asCellLocation();
+ CellLocation loc = mPhone.getCurrentCellIdentity().asCellLocation();
if (loc != null) {
if (loc instanceof GsmCellLocation) {
@@ -4005,14 +4308,14 @@ public class DcTracker extends Handler {
private void notifyAllDataDisconnected() {
sEnableFailFastRefCounter = 0;
mFailFast = false;
+ log("notify all data disconnected");
mAllDataDisconnectedRegistrants.notifyRegistrants();
}
public void registerForAllDataDisconnected(Handler h, int what) {
mAllDataDisconnectedRegistrants.addUnique(h, what, null);
- if (isDisconnected()) {
- log("notify All Data Disconnected");
+ if (areAllDataDisconnected()) {
notifyAllDataDisconnected();
}
}
@@ -4053,17 +4356,22 @@ public class DcTracker extends Handler {
}
}
+ private void reevaluateCongestedConnections() {
+ log("reevaluateCongestedConnections");
+ int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
+ // congested override and either network is specified or unknown and all networks specified
+ boolean isCongested = mCongestedOverride && (mCongestedNetworkTypes.contains(rat)
+ || mUnmeteredNetworkTypes.containsAll(Arrays.stream(
+ TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet())));
+ for (DataConnection dataConnection : mDataConnections.values()) {
+ dataConnection.onCongestednessChanged(isCongested);
+ }
+ }
+
private void reevaluateUnmeteredConnections() {
log("reevaluateUnmeteredConnections");
int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
- int override = mPhone.getDisplayInfoController().getTelephonyDisplayInfo()
- .getOverrideNetworkType();
- boolean nrPlanUnmetered = isNetworkTypeUnmetered(NETWORK_TYPE_NR) && (rat == NETWORK_TYPE_NR
- || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
- || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE);
- if ((nrPlanUnmetered || isNrNsaFrequencyRangeUnmetered() || isNrSaFrequencyRangeUnmetered())
- && !mPhone.getServiceState().getRoaming() || mRoamingUnmetered) {
- if (DBG) log("NR is unmetered");
+ if (isNrUnmetered() && (!mPhone.getServiceState().getRoaming() || mNrNsaRoamingUnmetered)) {
setDataConnectionUnmetered(true);
if (!mWatchdog) {
startWatchdogAlarm();
@@ -4075,12 +4383,51 @@ public class DcTracker extends Handler {
}
private void setDataConnectionUnmetered(boolean isUnmetered) {
- for (DataConnection dataConnection : mDataConnections.values()) {
- dataConnection.onMeterednessChanged(isUnmetered);
+ // TODO: Remove this after b/176119724 is fixed. This is just a workaround to prevent
+ // NET_CAPABILITY_TEMPORARILY_NOT_METERED incorrectly set on devices that are not supposed
+ // to use 5G unmetered network. Currently TEMPORARILY_NOT_METERED can only happen on few
+ // devices and carriers.
+ if (!isUnmetered || (isUnmetered && tempNotMeteredPossible())) {
+ for (DataConnection dataConnection : mDataConnections.values()) {
+ dataConnection.onMeterednessChanged(isUnmetered);
+ }
+ } else {
+ // isUnmetered=true but TEMP_NOT_METERED is not possible
+ String message = "Unexpected temp not metered detected. carrier supported="
+ + isTempNotMeteredSupportedByCarrier() + ", device 5G capable="
+ + isDevice5GCapable() + ", camped on 5G=" + isCampedOn5G()
+ + ", timer active=" + mPhone.getDisplayInfoController().is5GHysteresisActive()
+ + ", display info="
+ + mPhone.getDisplayInfoController().getTelephonyDisplayInfo()
+ + ", subscription plans=" + mSubscriptionPlans
+ + ", Service state=" + mPhone.getServiceState();
+ loge(message);
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("9151f0fc-01df-4afb-b744-9c4529055250"), message);
}
}
private boolean isNetworkTypeUnmetered(@NetworkType int networkType) {
+ boolean isUnmetered;
+ if (mUnmeteredNetworkTypes == null || !mUnmeteredOverride) {
+ // check SubscriptionPlans if override is not defined
+ isUnmetered = isNetworkTypeUnmeteredViaSubscriptionPlan(networkType);
+ log("isNetworkTypeUnmeteredViaSubscriptionPlan: networkType=" + networkType
+ + ", isUnmetered=" + isUnmetered);
+ return isUnmetered;
+ }
+ // unmetered override and either network is specified or unknown and all networks specified
+ isUnmetered = mUnmeteredNetworkTypes.contains(networkType)
+ || mUnmeteredNetworkTypes.containsAll(Arrays.stream(
+ TelephonyManager.getAllNetworkTypes()).boxed().collect(Collectors.toSet()));
+ if (DBG) {
+ log("isNetworkTypeUnmetered: networkType=" + networkType
+ + ", isUnmetered=" + isUnmetered);
+ }
+ return isUnmetered;
+ }
+
+ private boolean isNetworkTypeUnmeteredViaSubscriptionPlan(@NetworkType int networkType) {
if (mSubscriptionPlans == null || mSubscriptionPlans.size() == 0) {
// safe return false if unable to get subscription plans or plans don't exist
return false;
@@ -4112,39 +4459,128 @@ public class DcTracker extends Handler {
}
private boolean isPlanUnmetered(SubscriptionPlan plan) {
- return plan.getDataLimitBytes() == SubscriptionPlan.BYTES_UNLIMITED
- && (plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN
- || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED);
+ return plan.getDataLimitBytes() == SubscriptionPlan.BYTES_UNLIMITED;
}
- private boolean isNrNsaFrequencyRangeUnmetered() {
+ private boolean isNrUnmetered() {
+ int rat = mPhone.getDisplayInfoController().getTelephonyDisplayInfo().getNetworkType();
int override = mPhone.getDisplayInfoController().getTelephonyDisplayInfo()
.getOverrideNetworkType();
- if (mNrNsaMmwaveUnmetered || mNrNsaSub6Unmetered) {
- return (mNrNsaMmwaveUnmetered
- && override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE)
- || (mNrNsaSub6Unmetered
- && override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA);
- } else {
- return mNrNsaAllUnmetered
- && (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE
- || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA);
+
+ if (isNetworkTypeUnmetered(NETWORK_TYPE_NR)) {
+ if (mNrNsaMmwaveUnmetered) {
+ if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
+ if (DBG) log("NR unmetered for mmwave only");
+ return true;
+ }
+ return false;
+ } else if (mNrNsaSub6Unmetered) {
+ if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
+ if (DBG) log("NR unmetered for sub6 only");
+ return true;
+ }
+ return false;
+ }
+ if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
+ || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
+ || rat == NETWORK_TYPE_NR) {
+ if (DBG) log("NR unmetered for all frequencies");
+ return true;
+ }
+ return false;
+ }
+
+ if (mNrNsaAllUnmetered) {
+ if (mNrNsaMmwaveUnmetered) {
+ if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED) {
+ if (DBG) log("NR NSA unmetered for mmwave only via carrier configs");
+ return true;
+ }
+ return false;
+ } else if (mNrNsaSub6Unmetered) {
+ if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
+ if (DBG) log("NR NSA unmetered for sub6 only via carrier configs");
+ return true;
+ }
+ return false;
+ }
+ if (override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED
+ || override == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA) {
+ if (DBG) log("NR NSA unmetered for all frequencies via carrier configs");
+ return true;
+ }
+ return false;
}
- }
- private boolean isNrSaFrequencyRangeUnmetered() {
- if (ServiceState.rilRadioTechnologyToNetworkType(getDataRat()) != NETWORK_TYPE_NR) {
+ if (mNrSaAllUnmetered) {
+ // TODO: add logic for mNrSaMmwaveUnmetered and mNrSaSub6Unmetered once it's defined
+ // in TelephonyDisplayInfo
+ if (rat == NETWORK_TYPE_NR) {
+ if (DBG) log("NR SA unmetered for all frequencies via carrier configs");
+ return true;
+ }
return false;
}
- if (mNrSaMmwaveUnmetered || mNrSaSub6Unmetered) {
- int frequencyRange = mPhone.getServiceState().getNrFrequencyRange();
- boolean mmwave = frequencyRange == ServiceState.FREQUENCY_RANGE_MMWAVE;
- // frequency range LOW, MID, or HIGH
- boolean sub6 = frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN && !mmwave;
- return mNrSaMmwaveUnmetered && mmwave || mNrSaSub6Unmetered && sub6;
- } else {
- return mNrSaAllUnmetered;
+
+ return false;
+ }
+
+ // TODO: Remove this after b/176119724 is fixed. This is just a workaround to prevent
+ // NET_CAPABILITY_TEMPORARILY_NOT_METERED incorrectly set on devices that are not supposed
+ // to use 5G unmetered network. Currently TEMPORARILY_NOT_METERED can only happen on few devices
+ // and carriers.
+ private boolean isDevice5GCapable() {
+ return (mPhone.getRadioAccessFamily() & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0;
+ }
+
+ // TODO: Remove this after b/176119724 is fixed. This is just a workaround to prevent
+ // NET_CAPABILITY_TEMPORARILY_NOT_METERED incorrectly set on devices that are not supposed
+ // to use 5G unmetered network. Currently TEMPORARILY_NOT_METERED can only happen on few devices
+ // and carriers.
+ private boolean isTempNotMeteredSupportedByCarrier() {
+ CarrierConfigManager configManager =
+ mPhone.getContext().getSystemService(CarrierConfigManager.class);
+ if (configManager != null) {
+ PersistableBundle bundle = configManager.getConfigForSubId(mPhone.getSubId());
+ if (bundle != null) {
+ return bundle.getBoolean(
+ CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL);
+ }
}
+
+ return false;
+ }
+
+ // TODO: Remove this after b/176119724 is fixed. This is just a workaround to prevent
+ // NET_CAPABILITY_TEMPORARILY_NOT_METERED incorrectly set on devices that are not supposed
+ // to use 5G unmetered network. Currently TEMPORARILY_NOT_METERED can only happen on few devices
+ // and carriers.
+ private boolean isCampedOn5G() {
+ TelephonyDisplayInfo displayInfo = mPhone.getDisplayInfoController()
+ .getTelephonyDisplayInfo();
+ int overrideNetworkType = displayInfo.getOverrideNetworkType();
+ NetworkRegistrationInfo nri = mPhone.getServiceState().getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ int networkType = nri == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN
+ : nri.getAccessNetworkTechnology();
+
+ boolean isNrSa = networkType == TelephonyManager.NETWORK_TYPE_NR;
+ boolean isNrNsa = (networkType == TelephonyManager.NETWORK_TYPE_LTE
+ || networkType == TelephonyManager.NETWORK_TYPE_LTE_CA)
+ && (overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA
+ || overrideNetworkType == TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED);
+ boolean is5GHysteresisActive = mPhone.getDisplayInfoController().is5GHysteresisActive();
+
+ // True if device is on NR SA or NR NSA, or neither but 5G hysteresis is active
+ return isNrSa || isNrNsa || is5GHysteresisActive;
+ }
+
+ // TODO: Remove this after b/176119724 is fixed. This is just a workaround to prevent
+ // NET_CAPABILITY_TEMPORARILY_NOT_METERED incorrectly set on devices that are not supposed
+ // to use 5G unmetered network. Currently TEMPORARILY_NOT_METERED can only happen on few devices
+ // and carriers.
+ private boolean tempNotMeteredPossible() {
+ return isDevice5GCapable() && isTempNotMeteredSupportedByCarrier() && isCampedOn5G();
}
protected void log(String s) {
@@ -4268,7 +4704,7 @@ public class DcTracker extends Handler {
pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
pw.println(" canSetPreferApn=" + mCanSetPreferApn);
pw.println(" mApnObserver=" + mApnObserver);
- pw.println(" getOverallState=" + getOverallState());
+ pw.println(" isAnyDataConnected=" + isAnyDataConnected());
pw.println(" mAttached=" + mAttached.get());
mDataEnabledSettings.dump(fd, pw, args);
pw.flush();
@@ -4283,9 +4719,9 @@ public class DcTracker extends Handler {
return null;
}
- if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_EMERGENCY)) {
+ if (TextUtils.equals(apnType, ApnSetting.TYPE_EMERGENCY_STRING)) {
apnContext = mApnContextsByType.get(ApnSetting.TYPE_EMERGENCY);
- } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
+ } else if (TextUtils.equals(apnType, ApnSetting.TYPE_IMS_STRING)) {
apnContext = mApnContextsByType.get(ApnSetting.TYPE_IMS);
} else {
log("apnType is invalid, return null");
@@ -4314,78 +4750,54 @@ public class DcTracker extends Handler {
}
/**
- * Read APN configuration from Telephony.db for Emergency APN
- * All operators recognize the connection request for EPDN based on APN type
- * PLMN name,APN name are not mandatory parameters
+ * Create default apn settings for the apn type like emergency, and ims
*/
- private void initEmergencyApnSetting() {
- // Operator Numeric is not available when SIM is not ready.
- // Query Telephony.db with APN type as EPDN request does not
- // require APN name, plmn and all operators support same APN config.
- // DB will contain only one entry for Emergency APN
- String selection = "type=\"emergency\"";
- Cursor cursor = mPhone.getContext().getContentResolver().query(
- Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "filtered"),
- null, selection, null, null);
-
- if (cursor != null) {
- if (cursor.getCount() > 0) {
- if (cursor.moveToFirst()) {
- mEmergencyApn = ApnSetting.makeApnSetting(cursor);
- }
- }
- cursor.close();
- }
- if (mEmergencyApn != null) return;
-
- // If no emergency APN setting has been found, make one using reasonable defaults
- mEmergencyApn = new ApnSetting.Builder()
- .setEntryName("Emergency")
+ private ApnSetting buildDefaultApnSetting(@NonNull String entry,
+ @NonNull String apn, @ApnType int apnTypeBitmask) {
+ return new ApnSetting.Builder()
+ .setEntryName(entry)
.setProtocol(ApnSetting.PROTOCOL_IPV4V6)
.setRoamingProtocol(ApnSetting.PROTOCOL_IPV4V6)
- .setNetworkTypeBitmask((int)(TelephonyManager.NETWORK_TYPE_BITMASK_LTE
- | TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN))
- .setApnName("sos")
- .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY)
+ .setApnName(apn)
+ .setApnTypeBitmask(apnTypeBitmask)
+ .setCarrierEnabled(true)
+ .setApnSetId(Telephony.Carriers.MATCH_ALL_APN_SET_ID)
.build();
}
/**
- * Add the Emergency APN settings to APN settings list
+ * Add default APN settings to APN settings list as needed
*/
- private void addEmergencyApnSetting() {
- if(mEmergencyApn != null) {
- for (ApnSetting apn : mAllApnSettings) {
- if (apn.canHandleType(ApnSetting.TYPE_EMERGENCY)) {
- log("addEmergencyApnSetting - E-APN setting is already present");
- return;
- }
- }
+ private void addDefaultApnSettingsAsNeeded() {
+ boolean isEmergencyApnConfigured = false;
+ boolean isImsApnConfigured = false;
- // If all of the APN settings cannot handle emergency, we add the emergency APN to the
- // list explicitly.
- if (!mAllApnSettings.contains(mEmergencyApn)) {
- mAllApnSettings.add(mEmergencyApn);
- log("Adding emergency APN : " + mEmergencyApn);
+ for (ApnSetting apn : mAllApnSettings) {
+ if (apn.canHandleType(ApnSetting.TYPE_EMERGENCY)) {
+ isEmergencyApnConfigured = true;
+ }
+ if (apn.canHandleType(ApnSetting.TYPE_IMS)) {
+ isImsApnConfigured = true;
+ }
+ if (isEmergencyApnConfigured && isImsApnConfigured) {
+ log("Both emergency and ims apn setting are already present");
return;
}
}
- }
- private boolean containsAllApns(List<ApnSetting> oldApnList, List<ApnSetting> newApnList) {
- for (ApnSetting newApnSetting : newApnList) {
- boolean canHandle = false;
- for (ApnSetting oldApnSetting : oldApnList) {
- // Make sure at least one of the APN from old list can cover the new APN
- if (oldApnSetting.equals(newApnSetting,
- mPhone.getServiceState().getDataRoamingFromRegistration())) {
- canHandle = true;
- break;
- }
- }
- if (!canHandle) return false;
+ // Add default apn setting for emergency service if it is not present
+ if (!isEmergencyApnConfigured) {
+ mAllApnSettings.add(buildDefaultApnSetting(
+ "DEFAULT EIMS", "sos", ApnSetting.TYPE_EMERGENCY));
+ log("default emergency apn is created");
+ }
+
+ // Only add default apn setting for ims when it is not present and sim is loaded
+ if (!isImsApnConfigured && mSimState == TelephonyManager.SIM_STATE_LOADED) {
+ mAllApnSettings.add(buildDefaultApnSetting(
+ "DEFAULT IMS", "ims", ApnSetting.TYPE_IMS));
+ log("default ims apn is created");
}
- return true;
}
private void cleanUpConnectionsOnUpdatedApns(boolean detach, String reason) {
@@ -4404,10 +4816,12 @@ public class DcTracker extends Handler {
if (!apnContext.isDisconnected()) {
ArrayList<ApnSetting> waitingApns = buildWaitingApns(
apnContext.getApnType(), getDataRat());
- apnContext.setWaitingApns(waitingApns);
+ if (apnContext.getWaitingApns().size() != waitingApns.size()
+ || !apnContext.getWaitingApns().containsAll(waitingApns)) {
+ apnContext.setWaitingApns(waitingApns);
+ }
for (ApnSetting apnSetting : waitingApns) {
- if (apnSetting.equals(apnContext.getApnSetting(),
- mPhone.getServiceState().getDataRoamingFromRegistration())) {
+ if (areCompatible(apnSetting, apnContext.getApnSetting())) {
cleanupRequired = false;
break;
}
@@ -4427,15 +4841,14 @@ public class DcTracker extends Handler {
}
}
- if (!isConnected()) {
+ if (!isAnyDataConnected()) {
stopNetStatPoll();
stopDataStallAlarm();
}
mRequestedApnType = ApnSetting.TYPE_DEFAULT;
- if (DBG) log("mDisconnectPendingCount = " + mDisconnectPendingCount);
- if (detach && mDisconnectPendingCount == 0) {
+ if (areAllDataDisconnected()) {
notifyAllDataDisconnected();
}
}
@@ -4450,8 +4863,7 @@ public class DcTracker extends Handler {
}
protected void startNetStatPoll() {
- if (getOverallState() == DctConstants.State.CONNECTED
- && mNetStatPollEnabled == false) {
+ if (isAnyDataConnected() && !mNetStatPollEnabled) {
if (DBG) {
log("startNetStatPoll");
}
@@ -4610,11 +5022,9 @@ public class DcTracker extends Handler {
String apnType = apnContext.getApnType();
final Intent intent = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE);
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE, apnType);
- intent.putExtra(TelephonyManager.EXTRA_APN_TYPE_INT,
+ intent.putExtra(TelephonyManager.EXTRA_APN_TYPE,
ApnSetting.getApnTypesBitmaskFromString(apnType));
- intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL, pcoData.bearerProto);
- intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL_INT,
+ intent.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL,
ApnSetting.getProtocolIntFromString(pcoData.bearerProto));
intent.putExtra(TelephonyManager.EXTRA_PCO_ID, pcoData.pcoId);
intent.putExtra(TelephonyManager.EXTRA_PCO_VALUE, pcoData.contents);
@@ -4636,7 +5046,7 @@ public class DcTracker extends Handler {
RECOVERY_ACTION_RADIO_RESTART
})
@Retention(RetentionPolicy.SOURCE)
- private @interface RecoveryAction {};
+ public @interface RecoveryAction {};
private static final int RECOVERY_ACTION_GET_DATA_CALL_LIST = 0;
private static final int RECOVERY_ACTION_CLEANUP = 1;
private static final int RECOVERY_ACTION_REREGISTER = 2;
@@ -4652,6 +5062,14 @@ public class DcTracker extends Handler {
private long mTimeLastRecoveryStartMs;
// Whether current network good or not
private boolean mIsValidNetwork;
+ // Whether data stall happened or not.
+ private boolean mWasDataStall;
+ // Whether the result of last action(RADIO_RESTART) reported.
+ private boolean mLastActionReported;
+ // The real time for data stall start.
+ private long mDataStallStartMs;
+ // Last data stall action.
+ private @RecoveryAction int mLastAction;
public DataStallRecoveryHandler() {
reset();
@@ -4662,6 +5080,36 @@ public class DcTracker extends Handler {
putRecoveryAction(RECOVERY_ACTION_GET_DATA_CALL_LIST);
}
+ private void setNetworkValidationState(boolean isValid) {
+ // Validation status is true and was not data stall.
+ if (isValid && !mWasDataStall) {
+ return;
+ }
+
+ if (!mWasDataStall) {
+ mWasDataStall = true;
+ mDataStallStartMs = SystemClock.elapsedRealtime();
+ if (DBG) log("data stall: start time = " + mDataStallStartMs);
+ return;
+ }
+
+ if (!mLastActionReported) {
+ int timeDuration = (int) (SystemClock.elapsedRealtime() - mDataStallStartMs);
+ if (DBG) {
+ log("data stall: lastaction = " + mLastAction + ", isRecovered = "
+ + isValid + ", mTimeDuration = " + timeDuration);
+ }
+ DataStallRecoveryStats.onDataStallEvent(mLastAction, mPhone, isValid,
+ timeDuration);
+ mLastActionReported = true;
+ }
+
+ if (isValid) {
+ mLastActionReported = false;
+ mWasDataStall = false;
+ }
+ }
+
public boolean isAggressiveRecovery() {
@RecoveryAction int action = getRecoveryAction();
@@ -4712,7 +5160,8 @@ public class DcTracker extends Handler {
}
// Skip recovery if it can cause a call to drop
- if (mInVoiceCall && getRecoveryAction() > RECOVERY_ACTION_CLEANUP) {
+ if (mPhone.getState() != PhoneConstants.State.IDLE
+ && getRecoveryAction() > RECOVERY_ACTION_CLEANUP) {
if (VDBG_STALL) log("skip data stall recovery as there is an active call");
return false;
}
@@ -4729,7 +5178,7 @@ public class DcTracker extends Handler {
}
public void doRecovery() {
- if (getOverallState() == DctConstants.State.CONNECTED) {
+ if (isAnyDataConnected()) {
// Go through a series of recovery steps, each action transitions to the next action
@RecoveryAction final int recoveryAction = getRecoveryAction();
final int signalStrength = mPhone.getSignalStrength().getLevel();
@@ -4737,6 +5186,8 @@ public class DcTracker extends Handler {
mPhone.getPhoneId(), signalStrength);
TelephonyMetrics.getInstance().writeDataStallEvent(
mPhone.getPhoneId(), recoveryAction);
+ mLastAction = recoveryAction;
+ mLastActionReported = false;
broadcastDataStallDetected(recoveryAction);
switch (recoveryAction) {
@@ -4753,6 +5204,8 @@ public class DcTracker extends Handler {
if (DBG) log("doRecovery() cleanup all connections");
cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString(
ApnSetting.TYPE_DEFAULT)));
+ cleanUpConnection(mApnContexts.get(ApnSetting.getApnTypeString(
+ ApnSetting.TYPE_ENTERPRISE)));
putRecoveryAction(RECOVERY_ACTION_REREGISTER);
break;
case RECOVERY_ACTION_REREGISTER:
@@ -4778,6 +5231,7 @@ public class DcTracker extends Handler {
}
public void processNetworkStatusChanged(boolean isValid) {
+ setNetworkValidationState(isValid);
if (isValid) {
mIsValidNetwork = true;
reset();
@@ -4893,8 +5347,7 @@ public class DcTracker extends Handler {
protected void startDataStallAlarm(boolean suspectedStall) {
int delayInMs;
- if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled()
- && getOverallState() == DctConstants.State.CONNECTED) {
+ if (mDsRecoveryHandler.isNoRxDataStallDetectionEnabled() && isAnyDataConnected()) {
// If screen is on or data stall is currently suspected, set the alarm
// with an aggressive timeout.
if (mIsScreenOn || suspectedStall || mDsRecoveryHandler.isAggressiveRecovery()) {
@@ -4940,7 +5393,7 @@ public class DcTracker extends Handler {
}
private void restartDataStallAlarm() {
- if (isConnected() == false) return;
+ if (!isAnyDataConnected()) return;
// To be called on screen status change.
// Do not cancel the alarm if it is set with aggressive timeout.
if (mDsRecoveryHandler.isAggressiveRecovery()) {
@@ -5028,10 +5481,14 @@ public class DcTracker extends Handler {
if (networkTypeBitmask == 0) {
profileType = DataProfile.TYPE_COMMON;
- } else if (ServiceState.bearerBitmapHasCdma(networkTypeBitmask)) {
+ } else if ((networkTypeBitmask & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2)
+ == networkTypeBitmask) {
profileType = DataProfile.TYPE_3GPP2;
- } else {
+ } else if ((networkTypeBitmask & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP)
+ == networkTypeBitmask) {
profileType = DataProfile.TYPE_3GPP;
+ } else {
+ profileType = DataProfile.TYPE_COMMON;
}
return new DataProfile.Builder()
@@ -5056,17 +5513,18 @@ public class DcTracker extends Handler {
}
private void onDataServiceBindingChanged(boolean bound) {
- if (bound) {
- if (mDcc == null) {
- mDcc = DcController.makeDcc(mPhone, this, mDataServiceManager,
- new Handler(mHandlerThread.getLooper()), mLogTagSuffix);
+ if (!bound) {
+ if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
+ boolean connPersistenceOnRestart = mPhone.getContext().getResources()
+ .getBoolean(com.android
+ .internal.R.bool.config_wlan_data_service_conn_persistence_on_restart);
+ if (!connPersistenceOnRestart) {
+ cleanUpAllConnectionsInternal(false, Phone.REASON_IWLAN_DATA_SERVICE_DIED);
+ }
}
- mDcc.start();
} else {
- mDcc.dispose();
- // dispose sets the associated Handler object (StateMachine#mSmHandler) to null, so mDcc
- // needs to be created again (simply calling start() on it after dispose will not work)
- mDcc = null;
+ //reset throttling after binding to data service
+ mDataThrottler.reset();
}
mDataServiceBound = bound;
}
@@ -5136,7 +5594,7 @@ public class DcTracker extends Handler {
CarrierConfigManager.KEY_UNMETERED_NR_SA_MMWAVE_BOOL);
mNrSaSub6Unmetered = b.getBoolean(
CarrierConfigManager.KEY_UNMETERED_NR_SA_SUB6_BOOL);
- mRoamingUnmetered = b.getBoolean(
+ mNrNsaRoamingUnmetered = b.getBoolean(
CarrierConfigManager.KEY_UNMETERED_NR_NSA_WHEN_ROAMING_BOOL);
}
}
@@ -5161,4 +5619,88 @@ public class DcTracker extends Handler {
public void unregisterForPhysicalLinkStateChanged(Handler h) {
mDcc.unregisterForPhysicalLinkStateChanged(h);
}
+
+ // We use a specialized equals function in Apn setting when checking if an active
+ // data connection is still legitimate to use against a different apn setting.
+ // This method is extracted to a function to ensure that any future changes to this check will
+ // be applied to both cleanUpConnectionsOnUpdatedApns and checkForCompatibleDataConnection.
+ // Fix for b/158908392.
+ private boolean areCompatible(ApnSetting apnSetting1, ApnSetting apnSetting2) {
+ return apnSetting1.equals(apnSetting2,
+ mPhone.getServiceState().getDataRoamingFromRegistration());
+ }
+
+ @NonNull
+ private PersistableBundle getCarrierConfig() {
+ CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ // If an invalid subId is used, this bundle will contain default values.
+ PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+ if (config != null) {
+ return config;
+ }
+ }
+ // Return static default defined in CarrierConfigManager.
+ return CarrierConfigManager.getDefaultConfig();
+ }
+
+ /**
+ * @return The data service manager.
+ */
+ public @NonNull DataServiceManager getDataServiceManager() {
+ return mDataServiceManager;
+ }
+
+ /**
+ * @return The data throttler
+ */
+ public @NonNull DataThrottler getDataThrottler() {
+ return mDataThrottler;
+ }
+
+ private void showProvisioningNotification() {
+ final Intent intent = new Intent(DcTracker.INTENT_PROVISION);
+ intent.putExtra(DcTracker.EXTRA_PROVISION_PHONE_ID, mPhone.getPhoneId());
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mPhone.getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+
+ final Resources r = mPhone.getContext().getResources();
+ final String title = r.getString(R.string.network_available_sign_in, 0);
+ final String details = mTelephonyManager.getNetworkOperator(mPhone.getSubId());
+ final Notification.Builder builder = new Notification.Builder(mPhone.getContext())
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.drawable.stat_notify_rssi_in_range)
+ .setChannelId(NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS)
+ .setAutoCancel(true)
+ .setTicker(title)
+ .setColor(mPhone.getContext().getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(details)
+ .setContentIntent(pendingIntent)
+ .setLocalOnly(true)
+ .setOnlyAlertOnce(true);
+
+ final Notification notification = builder.build();
+ try {
+ getNotificationManager().notify(NOTIFICATION_TAG, mPhone.getPhoneId(), notification);
+ } catch (final NullPointerException npe) {
+ Log.e(mLogTag, "showProvisioningNotification: error showing notification", npe);
+ }
+ }
+
+ private void hideProvisioningNotification() {
+ try {
+ getNotificationManager().cancel(NOTIFICATION_TAG, mPhone.getPhoneId());
+ } catch (final NullPointerException npe) {
+ Log.e(mLogTag, "hideProvisioningNotification: error hiding notification", npe);
+ }
+ }
+
+ private NotificationManager getNotificationManager() {
+ return (NotificationManager) mPhone.getContext()
+ .createContextAsUser(UserHandle.ALL, 0 /* flags */)
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ }
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/LinkBandwidthEstimator.java b/src/java/com/android/internal/telephony/dataconnection/LinkBandwidthEstimator.java
new file mode 100644
index 0000000000..833cf86986
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/LinkBandwidthEstimator.java
@@ -0,0 +1,1169 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.display.DisplayManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Message;
+import android.os.OutcomeReceiver;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.preference.PreferenceManager;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityNr;
+import android.telephony.CellIdentityTdscdma;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.ModemActivityInfo;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.LocalLog;
+import android.util.Pair;
+import android.view.Display;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.DctConstants;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyFacade;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.nano.TelephonyProto.NrMode;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Link Bandwidth Estimator based on the byte counts in TrafficStats and the time reported in modem
+ * activity.
+ */
+public class LinkBandwidthEstimator extends Handler {
+ private static final String TAG = LinkBandwidthEstimator.class.getSimpleName();
+ private static final boolean DBG = false;
+ @VisibleForTesting
+ static final int MSG_SCREEN_STATE_CHANGED = 1;
+ @VisibleForTesting
+ static final int MSG_TRAFFIC_STATS_POLL = 2;
+ @VisibleForTesting
+ static final int MSG_MODEM_ACTIVITY_RETURNED = 3;
+ @VisibleForTesting
+ static final int MSG_DEFAULT_NETWORK_CHANGED = 4;
+ @VisibleForTesting
+ static final int MSG_SIGNAL_STRENGTH_CHANGED = 5;
+ @VisibleForTesting
+ static final int MSG_NR_FREQUENCY_CHANGED = 6;
+ @VisibleForTesting
+ static final int MSG_NR_STATE_CHANGED = 7;
+ @VisibleForTesting
+ static final int MSG_ACTIVE_PHONE_CHANGED = 8;
+ @VisibleForTesting
+ static final int MSG_DATA_REG_STATE_OR_RAT_CHANGED = 9;
+
+ // TODO: move the following parameters to xml file
+ private static final int TRAFFIC_STATS_POLL_INTERVAL_MS = 1_000;
+ private static final int MODEM_POLL_MIN_INTERVAL_MS = 5_000;
+ private static final int TRAFFIC_MODEM_POLL_BYTE_RATIO = 8;
+ private static final int TRAFFIC_POLL_BYTE_THRESHOLD_MAX = 20_000;
+ private static final int BYTE_DELTA_ACC_THRESHOLD_MAX_KB = 8_000;
+ private static final int MODEM_POLL_TIME_DELTA_MAX_MS = 10_000;
+ private static final int FILTER_UPDATE_MAX_INTERVAL_MS = 5_100;
+ // BW samples with Tx or Rx time below the following value is ignored.
+ private static final int TX_RX_TIME_MIN_MS = 200;
+ // The large time constant used in BW filter
+ private static final int TIME_CONSTANT_LARGE_SEC = 6;
+ // The small time constant used in BW filter
+ private static final int TIME_CONSTANT_SMALL_SEC = 6;
+ // If RSSI changes by more than the below value, update BW filter with small time constant
+ private static final int RSSI_DELTA_THRESHOLD_DB = 6;
+ // The up-scaling factor of filter coefficient.
+ private static final int FILTER_SCALE = 128;
+ // Force weight to 0 if the elapsed time is above LARGE_TIME_DECAY_RATIO * time constant
+ private static final int LARGE_TIME_DECAY_RATIO = 4;
+ // Modem Tx time may contain Rx time as defined in HAL. To work around the issue, if Tx time
+ // over Rx time ratio is above the following value, use Tx time + Rx time as Rx time.
+ private static final int TX_OVER_RX_TIME_RATIO_THRESHOLD_NUM = 3;
+ private static final int TX_OVER_RX_TIME_RATIO_THRESHOLD_DEN = 2;
+ // Default Link bandwidth value if the RAT entry is not found in static BW table.
+ private static final int DEFAULT_LINK_BAND_WIDTH_KBPS = 14;
+ // If Tx or Rx link bandwidth change is above the following value, send the BW update
+ private static final int BW_UPDATE_THRESHOLD_PERCENT = 15;
+
+ // To be used in link bandwidth estimation, each TrafficStats poll sample needs to be above
+ // a predefine threshold.
+ // For RAT with static BW above HIGH_BANDWIDTH_THRESHOLD_KBPS, it uses the following table.
+ // For others RATs, the thresholds are derived from the static BW values.
+ // The following table is defined per signal level, int [NUM_SIGNAL_LEVEL].
+ private static final int HIGH_BANDWIDTH_THRESHOLD_KBPS = 5000;
+ //Array dimension : int [NUM_LINK_DIRECTION][NUM_SIGNAL_LEVEL]
+ private static final int[][] BYTE_DELTA_THRESHOLD_KB =
+ {{200, 300, 400, 600, 1000}, {400, 600, 800, 1000, 1000}};
+ // Used to derive byte count threshold from avg BW
+ private static final int LOW_BW_TO_AVG_BW_RATIO_NUM = 3;
+ private static final int LOW_BW_TO_AVG_BW_RATIO_DEN = 8;
+ private static final int BYTE_DELTA_THRESHOLD_MIN_KB = 10;
+ private static final int MAX_ERROR_PERCENT = 100 * 100;
+ private static final String[] AVG_BW_PER_RAT = {
+ "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA:14,14",
+ "CDMA - 1xRTT:30,30", "CDMA - EvDo rev. 0:750,48", "CDMA - EvDo rev. A:950,550",
+ "HSDPA:4300,620", "HSUPA:4300,1800", "HSPA:4300,1800", "CDMA - EvDo rev. B:1500,550",
+ "CDMA - eHRPD:750,48", "HSPA+:13000,3400", "TD_SCDMA:115,115",
+ "LTE:30000,15000", "NR_NSA:47000,18000",
+ "NR_NSA_MMWAVE:145000,60000", "NR:145000,60000", "NR_MMWAVE:145000,60000"};
+ private static final Map<String, Pair<Integer, Integer>> AVG_BW_PER_RAT_MAP = new ArrayMap<>();
+ private static final String UNKNOWN_PLMN = "";
+
+ // To be used in the long term avg, each count needs to be above the following value
+ public static final int BW_STATS_COUNT_THRESHOLD = 5;
+ public static final int NUM_SIGNAL_LEVEL = 5;
+ public static final int LINK_TX = 0;
+ public static final int LINK_RX = 1;
+ public static final int NUM_LINK_DIRECTION = 2;
+
+ // One common timestamp for all sim to avoid frequent modem polling
+ private final Phone mPhone;
+ private final TelephonyFacade mTelephonyFacade;
+ private final TelephonyManager mTelephonyManager;
+ private final ConnectivityManager mConnectivityManager;
+ private final LocalLog mLocalLog = new LocalLog(512);
+ private boolean mScreenOn = false;
+ private boolean mIsOnDefaultRoute = false;
+ private boolean mIsOnActiveData = false;
+ private long mLastModemPollTimeMs;
+ private boolean mLastTrafficValid = true;
+ private long mLastMobileTxBytes;
+ private long mLastMobileRxBytes;
+ private long mTxBytesDeltaAcc;
+ private long mRxBytesDeltaAcc;
+
+ private ModemActivityInfo mLastModemActivityInfo = null;
+ private final TelephonyCallback mTelephonyCallback = new TelephonyCallbackImpl();
+ private int mSignalStrengthDbm;
+ private int mSignalLevel;
+ private int mDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private int mTac;
+ private String mPlmn = UNKNOWN_PLMN;
+ private NetworkCapabilities mNetworkCapabilities;
+ private NetworkBandwidth mPlaceholderNetwork;
+ private long mFilterUpdateTimeMs;
+
+ private int mBandwidthUpdateSignalDbm = -1;
+ private int mBandwidthUpdateSignalLevel = -1;
+ private int mBandwidthUpdateDataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private String mBandwidthUpdatePlmn = UNKNOWN_PLMN;
+ private BandwidthState mTxState = new BandwidthState(LINK_TX);
+ private BandwidthState mRxState = new BandwidthState(LINK_RX);
+ private RegistrantList mBandwidthChangedRegistrants = new RegistrantList();
+ private long mLastPlmnOrRatChangeTimeMs;
+ private long mLastDrsOrRatChangeTimeMs;
+
+ private static void initAvgBwPerRatTable() {
+ for (String config : AVG_BW_PER_RAT) {
+ int rxKbps = 14;
+ int txKbps = 14;
+ String[] kv = config.split(":");
+ if (kv.length == 2) {
+ String[] split = kv[1].split(",");
+ if (split.length == 2) {
+ try {
+ rxKbps = Integer.parseInt(split[0]);
+ txKbps = Integer.parseInt(split[1]);
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ AVG_BW_PER_RAT_MAP.put(kv[0], new Pair<>(rxKbps, txKbps));
+ }
+ }
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ obtainMessage(MSG_SCREEN_STATE_CHANGED, isScreenOn()).sendToTarget();
+ }
+ };
+
+ private final OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ mOutcomeReceiver =
+ new OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, result).sendToTarget();
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Rlog.e(TAG, "error reading modem stats:" + e);
+ obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, null).sendToTarget();
+ }
+ };
+
+ private final ConnectivityManager.NetworkCallback mDefaultNetworkCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
+ obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, networkCapabilities).sendToTarget();
+ }
+
+ public void onLost(@NonNull Network network) {
+ obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, null).sendToTarget();
+ }
+ };
+
+ public LinkBandwidthEstimator(Phone phone, TelephonyFacade telephonyFacade) {
+ mPhone = phone;
+ mTelephonyFacade = telephonyFacade;
+ mTelephonyManager = phone.getContext()
+ .getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(phone.getSubId());
+ mConnectivityManager = phone.getContext().getSystemService(ConnectivityManager.class);
+ DisplayManager dm = (DisplayManager) phone.getContext().getSystemService(
+ Context.DISPLAY_SERVICE);
+ dm.registerDisplayListener(mDisplayListener, null);
+ handleScreenStateChanged(isScreenOn());
+ mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, this);
+ mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(this),
+ mTelephonyCallback);
+ mPlaceholderNetwork = new NetworkBandwidth(UNKNOWN_PLMN);
+ initAvgBwPerRatTable();
+ registerNrStateFrequencyChange();
+ mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(AccessNetworkConstants
+ .TRANSPORT_TYPE_WWAN, this, MSG_DATA_REG_STATE_OR_RAT_CHANGED, null);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SCREEN_STATE_CHANGED:
+ handleScreenStateChanged((boolean) msg.obj);
+ break;
+ case MSG_TRAFFIC_STATS_POLL:
+ handleTrafficStatsPoll();
+ break;
+ case MSG_MODEM_ACTIVITY_RETURNED:
+ handleModemActivityReturned((ModemActivityInfo) msg.obj);
+ break;
+ case MSG_DEFAULT_NETWORK_CHANGED:
+ handleDefaultNetworkChanged((NetworkCapabilities) msg.obj);
+ break;
+ case MSG_SIGNAL_STRENGTH_CHANGED:
+ handleSignalStrengthChanged((SignalStrength) msg.obj);
+ break;
+ case MSG_NR_FREQUENCY_CHANGED:
+ // fall through
+ case MSG_NR_STATE_CHANGED:
+ updateStaticBwValueResetFilter();
+ break;
+ case MSG_ACTIVE_PHONE_CHANGED:
+ handleActivePhoneChanged((int) msg.obj);
+ break;
+ case MSG_DATA_REG_STATE_OR_RAT_CHANGED:
+ handleDrsOrRatChanged((AsyncResult) msg.obj);
+ break;
+ default:
+ Rlog.e(TAG, "invalid message " + msg.what);
+ break;
+ }
+ }
+
+ /**
+ * Registers for bandwidth estimation change. The bandwidth will be returned
+ * * {@link AsyncResult#result} as a {@link Pair} Object.
+ * * The {@link AsyncResult} will be in the notification {@link Message#obj}.
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForBandwidthChanged(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ mBandwidthChangedRegistrants.add(r);
+ }
+
+ /**
+ * Unregisters for bandwidth estimation change.
+ * @param h handler to notify
+ */
+ public void unregisterForBandwidthChanged(Handler h) {
+ mBandwidthChangedRegistrants.remove(h);
+ }
+ /**
+ * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display etc...)
+ * is on.
+ */
+ private boolean isScreenOn() {
+ // Note that we don't listen to Intent.SCREEN_ON and Intent.SCREEN_OFF because they are no
+ // longer adequate for monitoring the screen state since they are not sent in cases where
+ // the screen is turned off transiently such as due to the proximity sensor.
+ final DisplayManager dm = (DisplayManager) mPhone.getContext().getSystemService(
+ Context.DISPLAY_SERVICE);
+ Display[] displays = dm.getDisplays();
+
+ if (displays != null) {
+ for (Display display : displays) {
+ // Anything other than STATE_ON is treated as screen off, such as STATE_DOZE,
+ // STATE_DOZE_SUSPEND, etc...
+ if (display.getState() == Display.STATE_ON) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return false;
+ }
+
+ private void handleScreenStateChanged(boolean screenOn) {
+ if (mScreenOn == screenOn) {
+ return;
+ }
+ mScreenOn = screenOn;
+ handleTrafficStatsPollConditionChanged();
+ }
+
+ private void handleDefaultNetworkChanged(NetworkCapabilities networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ boolean isOnDefaultRoute;
+ if (networkCapabilities == null) {
+ isOnDefaultRoute = false;
+ } else {
+ isOnDefaultRoute = networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
+ }
+ if (mIsOnDefaultRoute == isOnDefaultRoute) {
+ return;
+ }
+ mIsOnDefaultRoute = isOnDefaultRoute;
+ handleTrafficStatsPollConditionChanged();
+ }
+
+ private void handleActivePhoneChanged(int activeDataSubId) {
+ boolean isOnActiveData = activeDataSubId == mPhone.getSubId();
+ if (mIsOnActiveData == isOnActiveData) {
+ return;
+ }
+ mIsOnActiveData = isOnActiveData;
+ logd("mIsOnActiveData " + mIsOnActiveData + " activeDataSubId " + activeDataSubId);
+ handleTrafficStatsPollConditionChanged();
+ }
+
+ private void handleDrsOrRatChanged(AsyncResult ar) {
+ Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>) ar.result;
+ logd("DrsOrRatChanged dataRegState " + drsRatPair.first + " rilRat " + drsRatPair.second);
+ mLastDrsOrRatChangeTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+ }
+
+ private void handleTrafficStatsPollConditionChanged() {
+ removeMessages(MSG_TRAFFIC_STATS_POLL);
+ if (mScreenOn && mIsOnDefaultRoute && mIsOnActiveData) {
+ updateDataRatCellIdentityBandwidth();
+ handleTrafficStatsPoll();
+ }
+ }
+
+ private void handleTrafficStatsPoll() {
+ invalidateTxRxSamples();
+ long mobileTxBytes = mTelephonyFacade.getMobileTxBytes();
+ long mobileRxBytes = mTelephonyFacade.getMobileRxBytes();
+ long txBytesDelta = mobileTxBytes - mLastMobileTxBytes;
+ long rxBytesDelta = mobileRxBytes - mLastMobileRxBytes;
+
+ // Schedule the next traffic stats poll
+ sendEmptyMessageDelayed(MSG_TRAFFIC_STATS_POLL, TRAFFIC_STATS_POLL_INTERVAL_MS);
+
+ mLastMobileTxBytes = mobileTxBytes;
+ mLastMobileRxBytes = mobileRxBytes;
+ // Sometimes TrafficStats byte counts return invalid values
+ // Ignore two polls if it happens
+ boolean trafficValid = txBytesDelta >= 0 && rxBytesDelta >= 0;
+ if (!mLastTrafficValid || !trafficValid) {
+ mLastTrafficValid = trafficValid;
+ Rlog.e(TAG, " run into invalid traffic count");
+ return;
+ }
+
+ mTxBytesDeltaAcc += txBytesDelta;
+ mRxBytesDeltaAcc += rxBytesDelta;
+
+ boolean doModemPoll = true;
+ // Check if it meets the requirement to request modem activity
+ long txByteDeltaThr = Math.min(mTxState.mByteDeltaAccThr / TRAFFIC_MODEM_POLL_BYTE_RATIO,
+ TRAFFIC_POLL_BYTE_THRESHOLD_MAX);
+ long rxByteDeltaThr = Math.min(mRxState.mByteDeltaAccThr / TRAFFIC_MODEM_POLL_BYTE_RATIO,
+ TRAFFIC_POLL_BYTE_THRESHOLD_MAX);
+ if (txBytesDelta < txByteDeltaThr && rxBytesDelta < rxByteDeltaThr
+ && mTxBytesDeltaAcc < mTxState.mByteDeltaAccThr
+ && mRxBytesDeltaAcc < mRxState.mByteDeltaAccThr) {
+ doModemPoll = false;
+ }
+
+ long currTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+ long timeSinceLastModemPollMs = currTimeMs - mLastModemPollTimeMs;
+ if (timeSinceLastModemPollMs < MODEM_POLL_MIN_INTERVAL_MS) {
+ doModemPoll = false;
+ }
+
+ if (doModemPoll) {
+ StringBuilder sb = new StringBuilder();
+ logd(sb.append("txByteDelta ").append(txBytesDelta)
+ .append(" rxByteDelta ").append(rxBytesDelta)
+ .append(" txByteDeltaAcc ").append(mTxBytesDeltaAcc)
+ .append(" rxByteDeltaAcc ").append(mRxBytesDeltaAcc)
+ .append(" trigger modem activity request").toString());
+ updateDataRatCellIdentityBandwidth();
+ // Filter update will happen after the request
+ makeRequestModemActivity();
+ return;
+ }
+
+ long timeSinceLastFilterUpdateMs = currTimeMs - mFilterUpdateTimeMs;
+ // Update filter
+ if (timeSinceLastFilterUpdateMs >= FILTER_UPDATE_MAX_INTERVAL_MS) {
+ if (!updateDataRatCellIdentityBandwidth()) {
+ updateTxRxBandwidthFilterSendToDataConnection();
+ }
+ }
+ }
+
+ private void makeRequestModemActivity() {
+ mLastModemPollTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+ // TODO: add CountDown in case that onResult/OnError() never happen
+ mTelephonyManager.requestModemActivityInfo(Runnable::run, mOutcomeReceiver);
+ }
+
+ private void handleModemActivityReturned(ModemActivityInfo result) {
+ updateBandwidthTxRxSamples(result);
+ updateTxRxBandwidthFilterSendToDataConnection();
+ mLastModemActivityInfo = result;
+ // Update for next poll
+ resetByteDeltaAcc();
+ }
+
+ private void resetByteDeltaAcc() {
+ mTxBytesDeltaAcc = 0;
+ mRxBytesDeltaAcc = 0;
+ }
+
+ private void invalidateTxRxSamples() {
+ mTxState.mBwSampleValid = false;
+ mRxState.mBwSampleValid = false;
+ }
+
+ private void updateBandwidthTxRxSamples(ModemActivityInfo modemActivityInfo) {
+ if (mLastModemActivityInfo == null || modemActivityInfo == null
+ || mNetworkCapabilities == null || hasRecentDataRegStatePlmnOrRatChange()) {
+ return;
+ }
+
+ long lastTimeMs = mLastModemActivityInfo.getTimestampMillis();
+ long currTimeMs = modemActivityInfo.getTimestampMillis();
+ long timeDeltaMs = currTimeMs - lastTimeMs;
+
+ if (timeDeltaMs > MODEM_POLL_TIME_DELTA_MAX_MS || timeDeltaMs <= 0) {
+ return;
+ }
+ ModemActivityInfo deltaInfo = mLastModemActivityInfo.getDelta(modemActivityInfo);
+ long txTimeDeltaMs = getModemTxTimeMs(deltaInfo);
+ long rxTimeDeltaMs = deltaInfo.getReceiveTimeMillis();
+
+ // Check if txTimeDeltaMs / rxTimeDeltaMs > TX_OVER_RX_TIME_RATIO_THRESHOLD
+ boolean isTxTimeOverRxTimeRatioLarge = (txTimeDeltaMs * TX_OVER_RX_TIME_RATIO_THRESHOLD_DEN
+ > rxTimeDeltaMs * TX_OVER_RX_TIME_RATIO_THRESHOLD_NUM);
+ long rxTimeBwEstMs = isTxTimeOverRxTimeRatioLarge
+ ? (txTimeDeltaMs + rxTimeDeltaMs) : rxTimeDeltaMs;
+
+ mTxState.updateBandwidthSample(mTxBytesDeltaAcc, txTimeDeltaMs);
+ mRxState.updateBandwidthSample(mRxBytesDeltaAcc, rxTimeBwEstMs);
+
+ int reportedTxTputKbps = mNetworkCapabilities.getLinkUpstreamBandwidthKbps();
+ int reportedRxTputKbps = mNetworkCapabilities.getLinkDownstreamBandwidthKbps();
+
+ StringBuilder sb = new StringBuilder();
+ logd(sb.append("UpdateBwSample")
+ .append(" dBm ").append(mSignalStrengthDbm)
+ .append(" level ").append(mSignalLevel)
+ .append(" rat ").append(getDataRatName(mDataRat))
+ .append(" plmn ").append(mPlmn)
+ .append(" tac ").append(mTac)
+ .append(" reportedTxKbps ").append(reportedTxTputKbps)
+ .append(" reportedRxKbps ").append(reportedRxTputKbps)
+ .append(" txMs ").append(txTimeDeltaMs)
+ .append(" rxMs ").append(rxTimeDeltaMs)
+ .append(" txKB ").append(mTxBytesDeltaAcc / 1024)
+ .append(" rxKB ").append(mRxBytesDeltaAcc / 1024)
+ .append(" txKBThr ").append(mTxState.mByteDeltaAccThr / 1024)
+ .append(" rxKBThr ").append(mRxState.mByteDeltaAccThr / 1024)
+ .toString());
+ }
+
+ private boolean hasRecentDataRegStatePlmnOrRatChange() {
+ if (mLastModemActivityInfo == null) {
+ return false;
+ }
+ return (mLastDrsOrRatChangeTimeMs > mLastModemActivityInfo.getTimestampMillis()
+ || mLastPlmnOrRatChangeTimeMs > mLastModemActivityInfo.getTimestampMillis());
+ }
+
+ private long getModemTxTimeMs(ModemActivityInfo modemActivity) {
+ long txTimeMs = 0;
+ for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
+ txTimeMs += modemActivity.getTransmitDurationMillisAtPowerLevel(lvl);
+ }
+ return txTimeMs;
+ }
+
+ private void updateTxRxBandwidthFilterSendToDataConnection() {
+ mFilterUpdateTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+ mTxState.updateBandwidthFilter();
+ mRxState.updateBandwidthFilter();
+
+ boolean isNetworkChanged = mTxState.hasLargeBwChange()
+ || mRxState.hasLargeBwChange()
+ || mBandwidthUpdateDataRat != mDataRat
+ || mBandwidthUpdateSignalLevel != mSignalLevel
+ || !mBandwidthUpdatePlmn.equals(mPlmn);
+ if (isValidNetwork() && isNetworkChanged) {
+ mTxState.mLastReportedBwKbps = mTxState.mAvgUsedKbps < 0 ? -1 : mTxState.mFilterKbps;
+ mRxState.mLastReportedBwKbps = mRxState.mAvgUsedKbps < 0 ? -1 : mRxState.mFilterKbps;
+ sendLinkBandwidthToDataConnection(
+ mTxState.mLastReportedBwKbps,
+ mRxState.mLastReportedBwKbps);
+ }
+ mBandwidthUpdateSignalDbm = mSignalStrengthDbm;
+ mBandwidthUpdateSignalLevel = mSignalLevel;
+ mBandwidthUpdateDataRat = mDataRat;
+ mBandwidthUpdatePlmn = mPlmn;
+
+ mTxState.calculateError();
+ mRxState.calculateError();
+ }
+
+ private boolean isValidNetwork() {
+ return !mPlmn.equals(UNKNOWN_PLMN) && mDataRat != TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private class BandwidthState {
+ private final int mLink;
+ int mFilterKbps;
+ int mByteDeltaAccThr = BYTE_DELTA_THRESHOLD_KB[0][0];
+ int mAvgUsedKbps;
+ int mBwSampleKbps;
+ boolean mBwSampleValid;
+ long mBwSampleValidTimeMs;
+ int mStaticBwKbps;
+ int mLastReportedBwKbps;
+
+ BandwidthState(int link) {
+ mLink = link;
+ }
+
+ private void updateBandwidthSample(long bytesDelta, long timeDeltaMs) {
+ updateByteCountThr();
+ if (bytesDelta < mByteDeltaAccThr) {
+ return;
+ }
+ if (timeDeltaMs < TX_RX_TIME_MIN_MS) {
+ return;
+ }
+ long linkBandwidthLongKbps = bytesDelta * 8 / timeDeltaMs * 1000 / 1024;
+ if (linkBandwidthLongKbps > Integer.MAX_VALUE || linkBandwidthLongKbps < 0) {
+ return;
+ }
+ int linkBandwidthKbps = (int) linkBandwidthLongKbps;
+ mBwSampleValid = true;
+ mBwSampleKbps = linkBandwidthKbps;
+
+ String dataRatName = getDataRatName(mDataRat);
+ NetworkBandwidth network = lookupNetwork(mPlmn, dataRatName);
+ // Update per RAT stats of all TAC
+ network.update(linkBandwidthKbps, mLink, mSignalLevel);
+
+ // Update per TAC stats
+ network = lookupNetwork(mPlmn, mTac, dataRatName);
+ network.update(linkBandwidthKbps, mLink, mSignalLevel);
+ }
+
+ private void updateBandwidthFilter() {
+ int avgKbps = getAvgLinkBandwidthKbps();
+ // Feed the filter with the long term avg if there is no valid BW sample so that filter
+ // will gradually converge the long term avg.
+ int filterInKbps = mBwSampleValid ? mBwSampleKbps : avgKbps;
+
+ long currTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+ int timeDeltaSec = (int) (currTimeMs - mBwSampleValidTimeMs) / 1000;
+
+ // If the operation condition changes significantly since the last update
+ // or the sample has higher BW, use a faster filter. Otherwise, use a slow filter
+ int timeConstantSec;
+ if (Math.abs(mBandwidthUpdateSignalDbm - mSignalStrengthDbm) > RSSI_DELTA_THRESHOLD_DB
+ || !mBandwidthUpdatePlmn.equals(mPlmn)
+ || mBandwidthUpdateDataRat != mDataRat
+ || (mBwSampleValid && mBwSampleKbps > avgKbps)) {
+ timeConstantSec = TIME_CONSTANT_SMALL_SEC;
+ } else {
+ timeConstantSec = TIME_CONSTANT_LARGE_SEC;
+ }
+ // Update timestamp for next iteration
+ if (mBwSampleValid) {
+ mBwSampleValidTimeMs = currTimeMs;
+ }
+
+ if (filterInKbps == mFilterKbps) {
+ return;
+ }
+
+ int alpha = timeDeltaSec > LARGE_TIME_DECAY_RATIO * timeConstantSec ? 0
+ : (int) (FILTER_SCALE * Math.exp(-1.0 * timeDeltaSec / timeConstantSec));
+ if (alpha == 0) {
+ mFilterKbps = filterInKbps;
+ return;
+ }
+ long filterOutKbps = (long) mFilterKbps * alpha
+ + filterInKbps * FILTER_SCALE - filterInKbps * alpha;
+ filterOutKbps = filterOutKbps / FILTER_SCALE;
+ mFilterKbps = (int) Math.min(filterOutKbps, Integer.MAX_VALUE);
+
+ StringBuilder sb = new StringBuilder();
+ logv(sb.append(mLink)
+ .append(" lastSampleWeight=").append(alpha)
+ .append("/").append(FILTER_SCALE)
+ .append(" filterInKbps=").append(filterInKbps)
+ .append(" avgKbps=").append(avgKbps)
+ .append(" filterOutKbps=").append(mFilterKbps)
+ .toString());
+ }
+
+ private int getAvgUsedLinkBandwidthKbps() {
+ // Check if current TAC/RAT/level has enough stats
+ String dataRatName = getDataRatName(mDataRat);
+ NetworkBandwidth network = lookupNetwork(mPlmn, mTac, dataRatName);
+ int count = network.getCount(mLink, mSignalLevel);
+ if (count >= BW_STATS_COUNT_THRESHOLD) {
+ return (int) (network.getValue(mLink, mSignalLevel) / count);
+ }
+
+ // Check if current RAT/level has enough stats
+ network = lookupNetwork(mPlmn, dataRatName);
+ count = network.getCount(mLink, mSignalLevel);
+ if (count >= BW_STATS_COUNT_THRESHOLD) {
+ return (int) (network.getValue(mLink, mSignalLevel) / count);
+ }
+ return -1;
+ }
+
+ private int getCurrentCount() {
+ String dataRatName = getDataRatName(mDataRat);
+ NetworkBandwidth network = lookupNetwork(mPlmn, dataRatName);
+ return network.getCount(mLink, mSignalLevel);
+ }
+
+ /** get a long term avg value (PLMN/RAT/TAC/level dependent) or static value */
+ private int getAvgLinkBandwidthKbps() {
+ mAvgUsedKbps = getAvgUsedLinkBandwidthKbps();
+ if (mAvgUsedKbps > 0) {
+ return mAvgUsedKbps;
+ }
+ // Fall back to static value
+ return mStaticBwKbps;
+ }
+
+ private void resetBandwidthFilter() {
+ mBwSampleValid = false;
+ mFilterKbps = getAvgLinkBandwidthKbps();
+ }
+
+ private void updateByteCountThr() {
+ // For high BW RAT cases, use predefined value + threshold derived from avg usage BW
+ if (mStaticBwKbps > HIGH_BANDWIDTH_THRESHOLD_KBPS) {
+ int lowBytes = calculateByteCountThreshold(getAvgUsedLinkBandwidthKbps(),
+ MODEM_POLL_MIN_INTERVAL_MS);
+ // Start with a predefined value
+ mByteDeltaAccThr = BYTE_DELTA_THRESHOLD_KB[mLink][mSignalLevel] * 1024;
+ if (lowBytes > 0) {
+ // Raise the threshold if the avg usage BW is high
+ mByteDeltaAccThr = Math.max(lowBytes, mByteDeltaAccThr);
+ mByteDeltaAccThr = Math.min(mByteDeltaAccThr,
+ BYTE_DELTA_ACC_THRESHOLD_MAX_KB * 1024);
+ }
+ return;
+ }
+ // For low BW RAT cases, derive the threshold from avg BW values
+ mByteDeltaAccThr = calculateByteCountThreshold(mStaticBwKbps,
+ MODEM_POLL_MIN_INTERVAL_MS);
+
+ mByteDeltaAccThr = Math.max(mByteDeltaAccThr, BYTE_DELTA_THRESHOLD_MIN_KB * 1024);
+ // Low BW RAT threshold value should be no more than high BW one.
+ mByteDeltaAccThr = Math.min(mByteDeltaAccThr, BYTE_DELTA_THRESHOLD_KB[mLink][0] * 1024);
+ }
+
+ // Calculate a byte count threshold for the given avg BW and observation window size
+ private int calculateByteCountThreshold(int avgBwKbps, int durationMs) {
+ long avgBytes = (long) avgBwKbps / 8 * durationMs;
+ long result = avgBytes * LOW_BW_TO_AVG_BW_RATIO_NUM / LOW_BW_TO_AVG_BW_RATIO_DEN;
+ return (int) Math.min(result, Integer.MAX_VALUE);
+ }
+
+ public boolean hasLargeBwChange() {
+ int deltaKbps = Math.abs(mLastReportedBwKbps - mFilterKbps);
+ return mAvgUsedKbps > 0
+ && deltaKbps * 100 > BW_UPDATE_THRESHOLD_PERCENT * mLastReportedBwKbps;
+ }
+
+ public void calculateError() {
+ if (!mBwSampleValid || getCurrentCount() <= BW_STATS_COUNT_THRESHOLD + 1
+ || mAvgUsedKbps <= 0) {
+ return;
+ }
+ int bwEstExtErrPercent = calculateErrorPercent(mLastReportedBwKbps, mBwSampleKbps);
+ int bwEstAvgErrPercent = calculateErrorPercent(mAvgUsedKbps, mBwSampleKbps);
+ int bwEstIntErrPercent = calculateErrorPercent(mFilterKbps, mBwSampleKbps);
+ int coldStartErrPercent = calculateErrorPercent(mStaticBwKbps, mBwSampleKbps);
+
+ TelephonyMetrics.getInstance().writeBandwidthStats(mLink, mDataRat, getNrMode(mDataRat),
+ mSignalLevel, bwEstExtErrPercent, coldStartErrPercent, mBwSampleKbps);
+
+ StringBuilder sb = new StringBuilder();
+ logd(sb.append(mLink)
+ .append(" sampKbps ").append(mBwSampleKbps)
+ .append(" filtKbps ").append(mFilterKbps)
+ .append(" reportKbps ").append(mLastReportedBwKbps)
+ .append(" avgUsedKbps ").append(mAvgUsedKbps)
+ .append(" csKbps ").append(mStaticBwKbps)
+ .append(" intErrPercent ").append(bwEstIntErrPercent)
+ .append(" avgErrPercent ").append(bwEstAvgErrPercent)
+ .append(" extErrPercent ").append(bwEstExtErrPercent)
+ .append(" csErrPercent ").append(coldStartErrPercent)
+ .toString());
+ }
+
+ private int calculateErrorPercent(int inKbps, int bwSampleKbps) {
+ long errorPercent = 100L * (inKbps - bwSampleKbps) / bwSampleKbps;
+ return (int) Math.max(-MAX_ERROR_PERCENT, Math.min(errorPercent, MAX_ERROR_PERCENT));
+ }
+ }
+
+ /**
+ * Update the byte count threshold.
+ * It should be called whenever the RAT or signal level is changed.
+ * For the RAT with high BW (4G and beyond), use BYTE_DELTA_THRESHOLD_KB table.
+ * For other RATs, derive the threshold based on the static BW values.
+ */
+ private void updateByteCountThr() {
+ mTxState.updateByteCountThr();
+ mRxState.updateByteCountThr();
+ }
+
+ // Reset BW filter to a long term avg value (PLMN/RAT/TAC dependent) or static BW value.
+ // It should be called whenever PLMN/RAT or static BW value is changed;
+ private void resetBandwidthFilter() {
+ mTxState.resetBandwidthFilter();
+ mRxState.resetBandwidthFilter();
+ }
+
+ private void sendLinkBandwidthToDataConnection(int linkBandwidthTxKps, int linkBandwidthRxKps) {
+ logv("send to DC tx " + linkBandwidthTxKps + " rx " + linkBandwidthRxKps);
+ Pair<Integer, Integer> bandwidthInfo =
+ new Pair<Integer, Integer>(linkBandwidthTxKps, linkBandwidthRxKps);
+ mBandwidthChangedRegistrants.notifyRegistrants(new AsyncResult(null, bandwidthInfo, null));
+ }
+
+ private void handleSignalStrengthChanged(SignalStrength signalStrength) {
+ if (signalStrength == null) {
+ return;
+ }
+
+ mSignalStrengthDbm = signalStrength.getDbm();
+ mSignalLevel = signalStrength.getLevel();
+ updateByteCountThr();
+ if (updateDataRatCellIdentityBandwidth()) {
+ return;
+ }
+
+ if (Math.abs(mBandwidthUpdateSignalDbm - mSignalStrengthDbm) > RSSI_DELTA_THRESHOLD_DB) {
+ updateTxRxBandwidthFilterSendToDataConnection();
+ }
+ }
+
+ private void registerNrStateFrequencyChange() {
+ mPhone.getServiceStateTracker().registerForNrStateChanged(this,
+ MSG_NR_STATE_CHANGED, null);
+ mPhone.getServiceStateTracker().registerForNrFrequencyChanged(this,
+ MSG_NR_FREQUENCY_CHANGED, null);
+ }
+
+ /**
+ * Get a string based on current RAT
+ */
+ public String getDataRatName(int rat) {
+ return getDataRatName(rat, getNrMode(rat));
+ }
+
+ private int getNrMode(int rat) {
+ if (rat == TelephonyManager.NETWORK_TYPE_LTE && isNrNsaConnected()) {
+ return mPhone.getServiceState().getNrFrequencyRange()
+ == ServiceState.FREQUENCY_RANGE_MMWAVE
+ ? NrMode.NR_NSA_MMWAVE : NrMode.NR_NSA;
+ } else if (rat == TelephonyManager.NETWORK_TYPE_NR) {
+ return mPhone.getServiceState().getNrFrequencyRange()
+ == ServiceState.FREQUENCY_RANGE_MMWAVE
+ ? NrMode.NR_SA_MMWAVE : NrMode.NR_SA;
+ }
+ return NrMode.NR_NONE;
+ }
+
+ /**
+ * Get a string based on current RAT and NR operation mode.
+ */
+ public static String getDataRatName(int rat, int nrMode) {
+ if (rat == TelephonyManager.NETWORK_TYPE_LTE
+ && (nrMode == NrMode.NR_NSA || nrMode == NrMode.NR_NSA_MMWAVE)) {
+ return nrMode == NrMode.NR_NSA
+ ? DctConstants.RAT_NAME_NR_NSA : DctConstants.RAT_NAME_NR_NSA_MMWAVE;
+ } else if (rat == TelephonyManager.NETWORK_TYPE_NR) {
+ return nrMode == NrMode.NR_SA
+ ? TelephonyManager.getNetworkTypeName(rat) : DctConstants.RAT_NAME_NR_SA_MMWAVE;
+ }
+ return TelephonyManager.getNetworkTypeName(rat);
+ }
+
+ /**
+ * Check if the device is connected to NR 5G Non-Standalone network
+ */
+ private boolean isNrNsaConnected() {
+ return mPhone.getServiceState().getNrState()
+ == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+ }
+
+ // Update avg BW values.
+ // It should be called whenever the RAT could be changed.
+ // return true if avg value is changed;
+ private boolean updateStaticBwValue(int dataRat) {
+ Pair<Integer, Integer> values = getStaticAvgBw(dataRat);
+ if (values == null) {
+ mTxState.mStaticBwKbps = DEFAULT_LINK_BAND_WIDTH_KBPS;
+ mRxState.mStaticBwKbps = DEFAULT_LINK_BAND_WIDTH_KBPS;
+ return true;
+ }
+ if (mTxState.mStaticBwKbps != values.second
+ || mRxState.mStaticBwKbps != values.first) {
+ mTxState.mStaticBwKbps = values.second;
+ mRxState.mStaticBwKbps = values.first;
+ return true;
+ }
+ return false;
+ }
+
+ /** get per-RAT static bandwidth value */
+ public Pair<Integer, Integer> getStaticAvgBw(int dataRat) {
+ String dataRatName = getDataRatName(dataRat);
+ Pair<Integer, Integer> values = AVG_BW_PER_RAT_MAP.get(dataRatName);
+ if (values == null) {
+ Rlog.e(TAG, dataRatName + " is not found in Avg BW table");
+ }
+ return values;
+ }
+
+ private void updateStaticBwValueResetFilter() {
+ if (updateStaticBwValue(mDataRat)) {
+ updateByteCountThr();
+ resetBandwidthFilter();
+ updateTxRxBandwidthFilterSendToDataConnection();
+ }
+ }
+
+ private NetworkRegistrationInfo getDataNri() {
+ return mPhone.getServiceState().getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ }
+
+ private boolean updateDataRatCellIdentityBandwidth() {
+ boolean updatedPlmn = false;
+ CellIdentity cellIdentity = mPhone.getCurrentCellIdentity();
+ mTac = getTac(cellIdentity);
+ String plmn;
+
+ if (mPhone.getServiceState().getOperatorNumeric() != null) {
+ plmn = mPhone.getServiceState().getOperatorNumeric();
+ } else {
+ if (cellIdentity.getPlmn() != null) {
+ plmn = cellIdentity.getPlmn();
+ } else {
+ plmn = UNKNOWN_PLMN;
+ }
+ }
+ if (mPlmn == null || !plmn.equals(mPlmn)) {
+ updatedPlmn = true;
+ mPlmn = plmn;
+ }
+
+ boolean updatedRat = false;
+ NetworkRegistrationInfo nri = getDataNri();
+ if (nri != null) {
+ int dataRat = nri.getAccessNetworkTechnology();
+ if (dataRat != mDataRat) {
+ updatedRat = true;
+ mDataRat = dataRat;
+ updateStaticBwValue(mDataRat);
+ updateByteCountThr();
+ }
+ }
+
+ boolean updatedPlmnOrRat = updatedPlmn || updatedRat;
+ if (updatedPlmnOrRat) {
+ resetBandwidthFilter();
+ updateTxRxBandwidthFilterSendToDataConnection();
+ mLastPlmnOrRatChangeTimeMs = mTelephonyFacade.getElapsedSinceBootMillis();
+ }
+ return updatedPlmnOrRat;
+ }
+
+ private int getTac(@NonNull CellIdentity cellIdentity) {
+ if (cellIdentity instanceof CellIdentityLte) {
+ return ((CellIdentityLte) cellIdentity).getTac();
+ }
+ if (cellIdentity instanceof CellIdentityNr) {
+ return ((CellIdentityNr) cellIdentity).getTac();
+ }
+ if (cellIdentity instanceof CellIdentityWcdma) {
+ return ((CellIdentityWcdma) cellIdentity).getLac();
+ }
+ if (cellIdentity instanceof CellIdentityTdscdma) {
+ return ((CellIdentityTdscdma) cellIdentity).getLac();
+ }
+ if (cellIdentity instanceof CellIdentityGsm) {
+ return ((CellIdentityGsm) cellIdentity).getLac();
+ }
+ return 0;
+ }
+
+ private class TelephonyCallbackImpl extends TelephonyCallback implements
+ TelephonyCallback.SignalStrengthsListener,
+ TelephonyCallback.ActiveDataSubscriptionIdListener {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, signalStrength).sendToTarget();
+ }
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ obtainMessage(MSG_ACTIVE_PHONE_CHANGED, subId).sendToTarget();
+ }
+ }
+
+ void logv(String msg) {
+ if (DBG) Rlog.v(TAG, msg);
+ }
+
+ void logd(String msg) {
+ if (DBG) Rlog.d(TAG, msg);
+ mLocalLog.log(msg);
+ }
+
+ @VisibleForTesting
+ static final int UNKNOWN_TAC = -1;
+ // Map with NetworkKey as the key and NetworkBandwidth as the value.
+ // NetworkKey is specified by the PLMN, data RAT and TAC of network.
+ // NetworkBandwidth represents the bandwidth related stats of each network.
+ private final Map<NetworkKey, NetworkBandwidth> mNetworkMap = new ArrayMap<>();
+ private static class NetworkKey {
+ private final String mPlmn;
+ private final String mDataRat;
+ private final int mTac;
+ NetworkKey(String plmn, int tac, String dataRat) {
+ mPlmn = plmn;
+ mTac = tac;
+ mDataRat = dataRat;
+ }
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == null || !(o instanceof NetworkKey) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ NetworkKey that = (NetworkKey) o;
+ return mPlmn.equals(that.mPlmn)
+ && mTac == that.mTac
+ && mDataRat.equals(that.mDataRat);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPlmn, mDataRat, mTac);
+ }
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Plmn").append(mPlmn)
+ .append("Rat").append(mDataRat)
+ .append("Tac").append(mTac)
+ .toString();
+ return sb.toString();
+ }
+ }
+
+ @NonNull
+ private NetworkBandwidth lookupNetwork(String plmn, String dataRat) {
+ return lookupNetwork(plmn, UNKNOWN_TAC, dataRat);
+ }
+
+ /** Look up NetworkBandwidth and create a new one if it doesn't exist */
+ @VisibleForTesting
+ @NonNull
+ public NetworkBandwidth lookupNetwork(String plmn, int tac, String dataRat) {
+ if (plmn == null || dataRat.equals(
+ TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_UNKNOWN))) {
+ return mPlaceholderNetwork;
+ }
+ NetworkKey key = new NetworkKey(plmn, tac, dataRat);
+ NetworkBandwidth ans = mNetworkMap.get(key);
+ if (ans == null) {
+ ans = new NetworkBandwidth(key.toString());
+ mNetworkMap.put(key, ans);
+ }
+ return ans;
+ }
+
+ /** A class holding link bandwidth related stats */
+ @VisibleForTesting
+ public class NetworkBandwidth {
+ private final String mKey;
+ NetworkBandwidth(String key) {
+ mKey = key;
+ }
+
+ /** Update link bandwidth stats */
+ public void update(long value, int link, int level) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ mPhone.getContext());
+ String valueKey = getValueKey(link, level);
+ String countKey = getCountKey(link, level);
+ SharedPreferences.Editor editor = sp.edit();
+ long currValue = sp.getLong(valueKey, 0);
+ int currCount = sp.getInt(countKey, 0);
+ editor.putLong(valueKey, currValue + value);
+ editor.putInt(countKey, currCount + 1);
+ editor.apply();
+ }
+
+ private String getValueKey(int link, int level) {
+ return getDataKey(link, level) + "Data";
+ }
+
+ private String getCountKey(int link, int level) {
+ return getDataKey(link, level) + "Count";
+ }
+
+ private String getDataKey(int link, int level) {
+ StringBuilder sb = new StringBuilder();
+ return sb.append(mKey)
+ .append("Link").append(link)
+ .append("Level").append(level)
+ .toString();
+ }
+
+ /** Get the accumulated bandwidth value */
+ public long getValue(int link, int level) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ mPhone.getContext());
+ String valueKey = getValueKey(link, level);
+ return sp.getLong(valueKey, 0);
+ }
+
+ /** Get the accumulated bandwidth count */
+ public int getCount(int link, int level) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ mPhone.getContext());
+ String countKey = getCountKey(link, level);
+ return sp.getInt(countKey, 0);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(mKey);
+ sb.append("\n");
+ for (int link = 0; link < NUM_LINK_DIRECTION; link++) {
+ sb.append((link == 0 ? "tx" : "rx"));
+ sb.append("\n avgKbps");
+ for (int level = 0; level < NUM_SIGNAL_LEVEL; level++) {
+ int count = getCount(link, level);
+ int avgKbps = count == 0 ? 0 : (int) (getValue(link, level) / count);
+ sb.append(" ").append(avgKbps);
+ }
+ sb.append("\n count");
+ for (int level = 0; level < NUM_SIGNAL_LEVEL; level++) {
+ int count = getCount(link, level);
+ sb.append(" ").append(count);
+ }
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Dump the internal state and local logs
+ */
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.increaseIndent();
+ pw.println("current PLMN " + mPlmn + " TAC " + mTac + " RAT " + getDataRatName(mDataRat));
+ pw.println("all networks visited since device boot");
+ for (NetworkBandwidth network : mNetworkMap.values()) {
+ pw.println(network.toString());
+ }
+
+ try {
+ mLocalLog.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ pw.decreaseIndent();
+ pw.println();
+ pw.flush();
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java b/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java
new file mode 100644
index 0000000000..795ed147b4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LinkAddress;
+import android.net.QosSession;
+import android.telephony.data.EpsQos;
+import android.telephony.data.NrQos;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.NrQosSessionAttributes;
+import android.telephony.data.QosBearerFilter;
+import android.telephony.data.QosBearerSession;
+
+import com.android.telephony.Rlog;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Matches filters with qos sessions and send corresponding available and lost events.
+ *
+ * Note: This class is <b>NOT</b> thread-safe
+ *
+ * {@hide}
+ */
+public class QosCallbackTracker {
+ @NonNull private final String mTag;
+ @NonNull private final DcNetworkAgent mDcNetworkAgent;
+ @NonNull private final Map<Integer, QosBearerSession> mQosBearerSessions;
+
+ // We perform an exact match on the address
+ @NonNull private final Map<Integer, IFilter> mCallbacksToFilter;
+
+ /**
+ * Construct a new tracker
+ * @param dcNetworkAgent the network agent to send events to
+ */
+ public QosCallbackTracker(@NonNull final DcNetworkAgent dcNetworkAgent) {
+ mQosBearerSessions = new HashMap<>();
+ mCallbacksToFilter = new HashMap<>();
+ mDcNetworkAgent = dcNetworkAgent;
+ mTag = "QosCallbackTracker" + "-" + mDcNetworkAgent.getNetwork().getNetId();
+ }
+
+ /**
+ * Add new filter that is to receive events
+ *
+ * @param callbackId the associated callback id
+ * @param filter provides the matching logic
+ */
+ public void addFilter(final int callbackId, final IFilter filter) {
+ logd("addFilter: callbackId=" + callbackId);
+ // Called from mDcNetworkAgent
+ mCallbacksToFilter.put(callbackId, filter);
+
+ //On first change. Check all sessions and send.
+ for (final QosBearerSession session : mQosBearerSessions.values()) {
+ if (doFiltersMatch(session, filter)) {
+ sendSessionAvailable(callbackId, session, filter);
+ }
+ }
+ }
+
+ /**
+ * Remove the filter with the associated callback id
+ *
+ * @param callbackId the qos callback id
+ */
+ public void removeFilter(final int callbackId) {
+ logd("removeFilter: callbackId=" + callbackId);
+ mCallbacksToFilter.remove(callbackId);
+ }
+
+ /**
+ * Update the list of qos sessions and send out corresponding events
+ *
+ * @param sessions the new list of qos sessions
+ */
+ public void updateSessions(@NonNull final List<QosBearerSession> sessions) {
+ logd("updateSessions: sessions size=" + sessions.size());
+ final List<QosBearerSession> sessionsToAdd = new ArrayList<>();
+ final Map<Integer, QosBearerSession> incomingSessions = new HashMap<>();
+ for (final QosBearerSession incomingSession : sessions) {
+ incomingSessions.put(incomingSession.getQosBearerSessionId(), incomingSession);
+
+ final QosBearerSession existingSession = mQosBearerSessions.get(
+ incomingSession.getQosBearerSessionId());
+ for (final int callbackId : mCallbacksToFilter.keySet()) {
+ final IFilter filter = mCallbacksToFilter.get(callbackId);
+
+ final boolean incomingSessionMatch = doFiltersMatch(incomingSession, filter);
+ final boolean existingSessionMatch =
+ existingSession != null && doFiltersMatch(existingSession, filter);
+
+ if (!existingSessionMatch && incomingSessionMatch) {
+ // The filter matches now and didn't match earlier
+ sendSessionAvailable(callbackId, incomingSession, filter);
+ }
+
+ if (existingSessionMatch && incomingSessionMatch) {
+ // The same sessions matches the same filter, but if the qos changed,
+ // the callback still needs to be notified
+ if (!incomingSession.getQos().equals(existingSession.getQos())) {
+ sendSessionAvailable(callbackId, incomingSession, filter);
+ }
+ }
+ }
+ sessionsToAdd.add(incomingSession);
+ }
+
+ final List<Integer> sessionsToRemove = new ArrayList<>();
+ // Find sessions that no longer exist
+ for (final QosBearerSession existingSession : mQosBearerSessions.values()) {
+ if (!incomingSessions.containsKey(existingSession.getQosBearerSessionId())) {
+ for (final int callbackId : mCallbacksToFilter.keySet()) {
+ final IFilter filter = mCallbacksToFilter.get(callbackId);
+ // The filter matches which means it was previously available, and now is lost
+ if (doFiltersMatch(existingSession, filter)) {
+ sendSessionLost(callbackId, existingSession);
+ }
+ }
+ sessionsToRemove.add(existingSession.getQosBearerSessionId());
+ }
+ }
+
+ // Add in the new or existing sessions with updated information
+ for (final QosBearerSession sessionToAdd : sessionsToAdd) {
+ mQosBearerSessions.put(sessionToAdd.getQosBearerSessionId(), sessionToAdd);
+ }
+
+ // Remove any old sessions
+ for (final int sessionToRemove : sessionsToRemove) {
+ mQosBearerSessions.remove(sessionToRemove);
+ }
+ }
+
+ private boolean doFiltersMatch(
+ final QosBearerSession qosBearerSession, final IFilter filter) {
+ return getMatchingQosBearerFilter(qosBearerSession, filter) != null;
+ }
+
+ private boolean matchesByLocalAddress(
+ QosBearerFilter sessionFilter, final IFilter filter) {
+ for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) {
+ return filter.matchesLocalAddress(qosAddress.getAddress(),
+ sessionFilter.getLocalPortRange().getStart(),
+ sessionFilter.getLocalPortRange().getEnd());
+ }
+ return false;
+ }
+
+ private boolean matchesByRemoteAddress(
+ QosBearerFilter sessionFilter, final IFilter filter) {
+ for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) {
+ return filter.matchesRemoteAddress(qosAddress.getAddress(),
+ sessionFilter.getRemotePortRange().getStart(),
+ sessionFilter.getRemotePortRange().getEnd());
+ }
+ return false;
+ }
+
+ private boolean matchesByRemoteAndLocalAddress(
+ QosBearerFilter sessionFilter, final IFilter filter) {
+ for (final LinkAddress remoteAddress : sessionFilter.getRemoteAddresses()) {
+ for (final LinkAddress localAddress : sessionFilter.getLocalAddresses()) {
+ return filter.matchesRemoteAddress(remoteAddress.getAddress(),
+ sessionFilter.getRemotePortRange().getStart(),
+ sessionFilter.getRemotePortRange().getEnd())
+ && filter.matchesLocalAddress(localAddress.getAddress(),
+ sessionFilter.getLocalPortRange().getStart(),
+ sessionFilter.getLocalPortRange().getEnd());
+ }
+ }
+ return false;
+ }
+
+ private QosBearerFilter getFilterByPrecedence(
+ QosBearerFilter qosFilter, QosBearerFilter sessionFilter) {
+ // Find for the highest precedence filter, lower the value is the higher the precedence
+ return qosFilter == null || sessionFilter.getPrecedence() < qosFilter.getPrecedence()
+ ? sessionFilter : qosFilter;
+ }
+
+ private QosBearerFilter getMatchingQosBearerFilter(
+ final QosBearerSession qosBearerSession, final IFilter filter) {
+ QosBearerFilter qosFilter = null;
+
+ for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
+ if (!sessionFilter.getLocalAddresses().isEmpty()
+ && !sessionFilter.getRemoteAddresses().isEmpty()
+ && sessionFilter.getLocalPortRange().isValid()
+ && sessionFilter.getRemotePortRange().isValid()) {
+ if (matchesByRemoteAndLocalAddress(sessionFilter, filter)) {
+ qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
+ }
+ } else if (!sessionFilter.getRemoteAddresses().isEmpty()
+ && sessionFilter.getRemotePortRange().isValid()) {
+ if (matchesByRemoteAddress(sessionFilter, filter)) {
+ qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
+ }
+ } else if (!sessionFilter.getLocalAddresses().isEmpty()
+ && sessionFilter.getLocalPortRange().isValid()) {
+ if (matchesByLocalAddress(sessionFilter, filter)) {
+ qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
+ }
+ }
+ }
+ return qosFilter;
+ }
+
+ private void sendSessionAvailable(final int callbackId,
+ @NonNull final QosBearerSession session, @NonNull IFilter filter) {
+ QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter);
+ List<InetSocketAddress> remoteAddresses = new ArrayList<>();
+ if(qosBearerFilter.getRemoteAddresses().size() > 0) {
+ remoteAddresses.add(
+ new InetSocketAddress(qosBearerFilter.getRemoteAddresses().get(0).getAddress(),
+ qosBearerFilter.getRemotePortRange().getStart()));
+ }
+
+ if (session.getQos() instanceof EpsQos) {
+ EpsQos qos = (EpsQos) session.getQos();
+ EpsBearerQosSessionAttributes epsBearerAttr =
+ new EpsBearerQosSessionAttributes(qos.getQci(),
+ qos.getUplinkBandwidth().getMaxBitrateKbps(),
+ qos.getDownlinkBandwidth().getMaxBitrateKbps(),
+ qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(),
+ qos.getUplinkBandwidth().getGuaranteedBitrateKbps(),
+ remoteAddresses);
+ mDcNetworkAgent.notifyQosSessionAvailable(
+ callbackId, session.getQosBearerSessionId(), epsBearerAttr);
+ } else {
+ NrQos qos = (NrQos) session.getQos();
+ NrQosSessionAttributes nrQosAttr =
+ new NrQosSessionAttributes(qos.get5Qi(), qos.getQfi(),
+ qos.getUplinkBandwidth().getMaxBitrateKbps(),
+ qos.getDownlinkBandwidth().getMaxBitrateKbps(),
+ qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(),
+ qos.getUplinkBandwidth().getGuaranteedBitrateKbps(),
+ qos.getAveragingWindow(), remoteAddresses);
+ mDcNetworkAgent.notifyQosSessionAvailable(
+ callbackId, session.getQosBearerSessionId(), nrQosAttr);
+ }
+
+ logd("sendSessionAvailable, callbackId=" + callbackId);
+ }
+
+ private void sendSessionLost(final int callbackId, @NonNull final QosBearerSession session) {
+ mDcNetworkAgent.notifyQosSessionLost(callbackId, session.getQosBearerSessionId(),
+ session.getQos() instanceof EpsQos ?
+ QosSession.TYPE_EPS_BEARER : QosSession.TYPE_NR_BEARER);
+ logd("sendSessionLost, callbackId=" + callbackId);
+ }
+
+ public interface IFilter {
+ public boolean matchesLocalAddress(InetAddress address, int startPort, int endPort);
+ public boolean matchesRemoteAddress(InetAddress address, int startPort, int endPort);
+ }
+
+ /**
+ * Log with debug level
+ *
+ * @param s is string log
+ */
+ private void logd(String s) {
+ Rlog.d(mTag, s);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
index eff43408f7..2889e7aa6c 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
@@ -40,6 +40,7 @@ import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams;
+import com.android.internal.telephony.metrics.NetworkRequestsStats;
import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
@@ -130,24 +131,34 @@ public class TelephonyNetworkFactory extends NetworkFactory {
return makeNetworkFilter(subscriptionId);
}
- private NetworkCapabilities makeNetworkFilter(int subscriptionId) {
- NetworkCapabilities nc = new NetworkCapabilities();
- nc.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- nc.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
+ /**
+ * Build the network request filter used by this factory.
+ * @param subscriptionId the subscription ID to listen to
+ * @return the filter to send to the system server
+ */
+ // This is used by the test to simulate the behavior of the system server, which is to
+ // send requests that match the network filter.
+ @VisibleForTesting
+ public NetworkCapabilities makeNetworkFilter(int subscriptionId) {
+ final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_IA)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
.setSubscriptionId(subscriptionId).build());
- return nc;
+ return builder.build();
}
private class InternalHandler extends Handler {
@@ -183,23 +194,20 @@ public class TelephonyNetworkFactory extends NetworkFactory {
}
case EVENT_DATA_HANDOVER_COMPLETED: {
Bundle bundle = msg.getData();
- int requestType = bundle.getInt(DcTracker.DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE);
- if (requestType == DcTracker.REQUEST_TYPE_HANDOVER) {
- NetworkRequest nr = bundle.getParcelable(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST);
- boolean success = bundle.getBoolean(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS);
- int transport = bundle.getInt(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE);
- boolean fallback = bundle.getBoolean(
- DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK);
- HandoverParams handoverParams = mPendingHandovers.remove(msg);
- if (handoverParams != null) {
- onDataHandoverSetupCompleted(nr, success, transport, fallback,
- handoverParams);
- } else {
- logl("Handover completed but cannot find handover entry!");
- }
+ NetworkRequest nr = bundle.getParcelable(
+ DcTracker.DATA_COMPLETE_MSG_EXTRA_NETWORK_REQUEST);
+ boolean success = bundle.getBoolean(
+ DcTracker.DATA_COMPLETE_MSG_EXTRA_SUCCESS);
+ int transport = bundle.getInt(
+ DcTracker.DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE);
+ boolean fallback = bundle.getBoolean(
+ DcTracker.DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK);
+ HandoverParams handoverParams = mPendingHandovers.remove(msg);
+ if (handoverParams != null) {
+ onDataHandoverSetupCompleted(nr, success, transport, fallback,
+ handoverParams);
+ } else {
+ logl("Handover completed but cannot find handover entry!");
}
break;
}
@@ -212,18 +220,28 @@ public class TelephonyNetworkFactory extends NetworkFactory {
return mTransportManager.getCurrentTransport(apnType);
}
+ /**
+ * Request network
+ *
+ * @param networkRequest Network request from clients
+ * @param requestType The request type
+ * @param transport Transport type
+ * @param onHandoverCompleteMsg When request type is handover, this message will be sent when
+ * handover is completed. For normal request, this should be null.
+ */
private void requestNetworkInternal(NetworkRequest networkRequest,
- @RequestNetworkType int requestType,
- int transport, Message onCompleteMsg) {
+ @RequestNetworkType int requestType, int transport, Message onHandoverCompleteMsg) {
+ NetworkRequestsStats.addNetworkRequest(networkRequest, mSubscriptionId);
if (mPhone.getDcTracker(transport) != null) {
mPhone.getDcTracker(transport).requestNetwork(networkRequest, requestType,
- onCompleteMsg);
+ onHandoverCompleteMsg);
}
}
private void releaseNetworkInternal(NetworkRequest networkRequest,
@ReleaseNetworkType int releaseType,
int transport) {
+ NetworkRequestsStats.addNetworkRelease(networkRequest, mSubscriptionId);
if (mPhone.getDcTracker(transport) != null) {
mPhone.getDcTracker(transport).releaseNetwork(networkRequest, releaseType);
}
@@ -280,7 +298,7 @@ public class TelephonyNetworkFactory extends NetworkFactory {
}
@Override
- public void needNetworkFor(NetworkRequest networkRequest, int score) {
+ public void needNetworkFor(NetworkRequest networkRequest) {
Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST);
msg.obj = networkRequest;
msg.sendToTarget();
@@ -356,8 +374,10 @@ public class TelephonyNetworkFactory extends NetworkFactory {
mPendingHandovers.put(onCompleteMsg, handoverParams);
requestNetworkInternal(networkRequest, DcTracker.REQUEST_TYPE_HANDOVER,
targetTransport, onCompleteMsg);
- log("Requested handover " + ApnSetting.getApnTypeString(apnType) + " to "
- + AccessNetworkConstants.transportTypeToString(targetTransport));
+ log("Requested handover " + ApnSetting.getApnTypeString(apnType)
+ + " to "
+ + AccessNetworkConstants.transportTypeToString(targetTransport)
+ + ". " + networkRequest);
handoverPending = true;
} else {
// Request is there, but no actual data connection. In this case, just move
@@ -413,7 +433,29 @@ public class TelephonyNetworkFactory extends NetworkFactory {
// connection can be re-established on the other transport.
: DcTracker.RELEASE_TYPE_DETACH;
releaseNetworkInternal(networkRequest, releaseType, originTransport);
- mNetworkRequests.put(networkRequest, targetTransport);
+
+ // Before updating the network request with the target transport, make sure the request
+ // is still there because it's possible that connectivity service has already released
+ // the network while handover is ongoing. If connectivity service already released
+ // the network request, we need to tear down the just-handovered data connection on the
+ // target transport.
+ if (mNetworkRequests.containsKey(networkRequest)) {
+ // Update it with the target transport.
+ mNetworkRequests.put(networkRequest, targetTransport);
+ } else {
+ log("Network request was released before handover is completed. Now"
+ + " we need to release this network request. "
+ + networkRequest);
+ releaseNetworkInternal(networkRequest, DcTracker.RELEASE_TYPE_NORMAL,
+ targetTransport);
+ }
+ } else {
+ // If handover fails and requires to fallback, the context of target transport needs to
+ // be released
+ if (!success) {
+ releaseNetworkInternal(networkRequest,
+ DcTracker.RELEASE_TYPE_NORMAL, targetTransport);
+ }
}
handoverParams.callback.onCompleted(success, fallback);
diff --git a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
index f674e68a3d..805ca6609c 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
@@ -26,9 +26,11 @@ import android.os.RegistrantList;
import android.os.SystemProperties;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.data.ApnSetting;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -38,16 +40,16 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.dataconnection.AccessNetworksManager.QualifiedNetworks;
import com.android.internal.telephony.util.ArrayUtils;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.LinkedList;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -94,7 +96,7 @@ import java.util.stream.Collectors;
* The carrier config takes precedence over the resource overlay if both exist.
*/
public class TransportManager extends Handler {
- private static final String TAG = TransportManager.class.getSimpleName();
+ private final String mLogTag;
// Key is the access network, value is the transport.
private static final Map<Integer, Integer> ACCESS_NETWORK_TRANSPORT_TYPE_MAP;
@@ -168,7 +170,7 @@ public class TransportManager extends Handler {
/**
* The queued available networks list.
*/
- private final LinkedList<List<QualifiedNetworks>> mAvailableNetworksList;
+ private final ArrayDeque<List<QualifiedNetworks>> mQueuedNetworksList;
/**
* The current transport of the APN type. The key is the APN type, and the value is the
@@ -225,7 +227,8 @@ public class TransportManager extends Handler {
mCurrentTransports = new ConcurrentHashMap<>();
mPendingHandoverApns = new SparseIntArray();
mHandoverNeededEventRegistrants = new RegistrantList();
- mAvailableNetworksList = new LinkedList<>();
+ mQueuedNetworksList = new ArrayDeque<>();
+ mLogTag = TransportManager.class.getSimpleName() + "-" + mPhone.getPhoneId();
if (isInLegacyMode()) {
log("operates in legacy mode.");
@@ -248,7 +251,7 @@ public class TransportManager extends Handler {
case EVENT_QUALIFIED_NETWORKS_CHANGED:
AsyncResult ar = (AsyncResult) msg.obj;
List<QualifiedNetworks> networks = (List<QualifiedNetworks>) ar.result;
- mAvailableNetworksList.add(networks);
+ mQueuedNetworksList.add(networks);
sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
break;
case EVENT_UPDATE_AVAILABLE_NETWORKS:
@@ -317,9 +320,11 @@ public class TransportManager extends Handler {
* @param transport The transport. Must be WWAN or WLAN.
*/
private synchronized void setCurrentTransport(@ApnType int apnType, int transport) {
- mCurrentTransports.put(apnType, transport);
- logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
- + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
+ Integer previousTransport = mCurrentTransports.put(apnType, transport);
+ if (previousTransport == null || previousTransport != transport) {
+ logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType)
+ + ", transport=" + AccessNetworkConstants.transportTypeToString(transport));
+ }
}
private boolean isHandoverPending() {
@@ -332,12 +337,12 @@ public class TransportManager extends Handler {
return;
}
- if (mAvailableNetworksList.size() == 0) {
+ if (mQueuedNetworksList.size() == 0) {
log("Nothing in the available network list queue.");
return;
}
- List<QualifiedNetworks> networksList = mAvailableNetworksList.remove();
+ List<QualifiedNetworks> networksList = mQueuedNetworksList.remove();
logl("updateAvailableNetworks: " + networksList);
for (QualifiedNetworks networks : networksList) {
if (areNetworksValid(networks)) {
@@ -349,7 +354,8 @@ public class TransportManager extends Handler {
int targetTransport = ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(
networks.qualifiedNetworks[0]);
logl("Handover needed for APN type: "
- + ApnSetting.getApnTypeString(networks.apnType) + ", target transport: "
+ + ApnSetting.getApnTypeString(networks.apnType)
+ + ", target transport: "
+ AccessNetworkConstants.transportTypeToString(targetTransport));
mPendingHandoverApns.put(networks.apnType, targetTransport);
mHandoverNeededEventRegistrants.notifyResult(
@@ -376,7 +382,7 @@ public class TransportManager extends Handler {
// If there are still pending available network changes, we
// need to process the rest.
- if (mAvailableNetworksList.size() > 0) {
+ if (mQueuedNetworksList.size() > 0) {
sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
}
}));
@@ -388,7 +394,7 @@ public class TransportManager extends Handler {
}
// If there are still pending available network changes, we need to process the rest.
- if (mAvailableNetworksList.size() > 0) {
+ if (mQueuedNetworksList.size() > 0) {
sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS);
}
}
@@ -407,14 +413,18 @@ public class TransportManager extends Handler {
* @return {@code true} if the device operates in legacy mode, otherwise {@code false}.
*/
public boolean isInLegacyMode() {
- // Get IWLAN operation mode from the system property. If the system property is missing or
- // mis-configured the default behavior is tied to the IRadio version. For 1.4 or above, it's
- // AP-assisted mode, for 1.3 or below, it's legacy mode. For IRadio 1.3 or below, no matter
- // what the configuration is, it will always be legacy mode.
+ // Get IWLAN operation mode from the system property. If the system property is configured
+ // to default or not configured, the mode is tied to IRadio version. For 1.4 or above, it's
+ // AP-assisted mode, for 1.3 or below, it's legacy mode.
String mode = SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE);
- return mode.equals(IWLAN_OPERATION_MODE_LEGACY)
- || mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
+ if (mode.equals(IWLAN_OPERATION_MODE_AP_ASSISTED)) {
+ return false;
+ } else if (mode.equals(IWLAN_OPERATION_MODE_LEGACY)) {
+ return true;
+ }
+
+ return mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
}
/**
@@ -471,6 +481,60 @@ public class TransportManager extends Handler {
}
/**
+ * Registers the data throttler with DcTracker.
+ */
+ public void registerDataThrottler(DataThrottler dataThrottler) {
+ if (mAccessNetworksManager != null) {
+ mAccessNetworksManager.registerDataThrottler(dataThrottler);
+ }
+ }
+
+ /**
+ * Get the latest preferred transport. Note that the current transport only changed after
+ * handover is completed, and there might be queued update network requests not processed yet.
+ * This method is used to get the latest preference sent from qualified networks service.
+ *
+ * @param apnType APN type
+ * @return The preferred transport. {@link AccessNetworkConstants#TRANSPORT_TYPE_INVALID} if
+ * unknown, unavailable, or QNS explicitly specifies data connection of the APN type should not
+ * be brought up on either cellular or IWLAN.
+ */
+ public @TransportType int getPreferredTransport(@ApnType int apnType) {
+ // Since the latest updates from QNS is stored at the end of the queue, so if we want to
+ // check what's the latest, we should iterate the queue reversely.
+ Iterator<List<QualifiedNetworks>> it = mQueuedNetworksList.descendingIterator();
+ while (it.hasNext()) {
+ List<QualifiedNetworks> networksList = it.next();
+ for (QualifiedNetworks networks : networksList) {
+ if (networks.apnType == apnType) {
+ if (networks.qualifiedNetworks.length > 0) {
+ return ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(networks.qualifiedNetworks[0]);
+ }
+ // This is the case that QNS explicitly specifies no data allowed on neither
+ // cellular nor IWLAN.
+ return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+ }
+ }
+ }
+
+ // if not found in the queue, see if it's in the current available networks.
+ int[] currentNetworkList = mCurrentAvailableNetworks.get(apnType);
+ if (currentNetworkList != null) {
+ if (currentNetworkList.length > 0) {
+ return ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(currentNetworkList[0]);
+ }
+ // This is the case that QNS explicitly specifies no data allowed on neither
+ // cellular nor IWLAN.
+ return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+ }
+
+ // If no input from QNS, for example in legacy mode, the default preferred transport should
+ // be cellular.
+ return AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+ }
+
+
+ /**
* Dump the state of transport manager
*
* @param fd File descriptor
@@ -479,15 +543,30 @@ public class TransportManager extends Handler {
*/
public void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printwriter, " ");
- pw.println("TransportManager:");
+ pw.println(mLogTag);
pw.increaseIndent();
pw.println("mAvailableTransports=[" + Arrays.stream(mAvailableTransports)
- .mapToObj(type -> AccessNetworkConstants.transportTypeToString(type))
+ .mapToObj(AccessNetworkConstants::transportTypeToString)
.collect(Collectors.joining(",")) + "]");
- pw.println("mCurrentAvailableNetworks=" + mCurrentAvailableNetworks);
- pw.println("mAvailableNetworksList=" + mAvailableNetworksList);
+ pw.println("mCurrentAvailableNetworks=");
+ pw.increaseIndent();
+ for (int i = 0; i < mCurrentAvailableNetworks.size(); i++) {
+ pw.println("APN type "
+ + ApnSetting.getApnTypeString(mCurrentAvailableNetworks.keyAt(i))
+ + ": [" + Arrays.stream(mCurrentAvailableNetworks.valueAt(i))
+ .mapToObj(AccessNetworkType::toString)
+ .collect(Collectors.joining(",")) + "]");
+ }
+ pw.decreaseIndent();
+ pw.println("mQueuedNetworksList=" + mQueuedNetworksList);
pw.println("mPendingHandoverApns=" + mPendingHandoverApns);
- pw.println("mCurrentTransports=" + mCurrentTransports);
+ pw.println("mCurrentTransports=");
+ pw.increaseIndent();
+ for (Map.Entry<Integer, Integer> entry : mCurrentTransports.entrySet()) {
+ pw.println("APN type " + ApnSetting.getApnTypeString(entry.getKey())
+ + ": " + AccessNetworkConstants.transportTypeToString(entry.getValue()));
+ }
+ pw.decreaseIndent();
pw.println("isInLegacy=" + isInLegacyMode());
pw.println("IWLAN operation mode="
+ SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE));
@@ -508,10 +587,10 @@ public class TransportManager extends Handler {
}
private void log(String s) {
- Rlog.d(TAG, s);
+ Rlog.d(mLogTag, s);
}
private void loge(String s) {
- Rlog.e(TAG, s);
+ Rlog.e(mLogTag, s);
}
}
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 8cfde1f953..0226b072c5 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -59,7 +59,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -80,7 +79,9 @@ public class EmergencyNumberTracker extends Handler {
private static final String EMERGENCY_NUMBER_DB_OTA_FILE_NAME = "emergency_number_db";
private static final String EMERGENCY_NUMBER_DB_OTA_FILE_PATH =
"misc/emergencynumberdb/" + EMERGENCY_NUMBER_DB_OTA_FILE_NAME;
- private FileInputStream mEmergencyNumberDbOtaFileInputStream = null;
+
+ /** Used for storing overrided (non-default) OTA database file path */
+ private ParcelFileDescriptor mOverridedOtaDbParcelFileDescriptor = null;
/** @hide */
public static boolean DBG = false;
@@ -94,6 +95,7 @@ public class EmergencyNumberTracker extends Handler {
private final CommandsInterface mCi;
private final Phone mPhone;
private String mCountryIso;
+ private String mLastKnownEmergencyCountryIso = "";
private int mCurrentDatabaseVersion = INVALID_DATABASE_VERSION;
/**
* Indicates if the country iso is set by another subscription.
@@ -128,9 +130,11 @@ public class EmergencyNumberTracker extends Handler {
/** Event indicating the update for the emergency number prefix from carrier config. */
private static final int EVENT_UPDATE_EMERGENCY_NUMBER_PREFIX = 4;
/** Event indicating the update for the OTA emergency number database. */
- private static final int EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB = 5;
+ @VisibleForTesting
+ public static final int EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB = 5;
/** Event indicating the override for the test OTA emergency number database. */
- private static final int EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH = 6;
+ @VisibleForTesting
+ public static final int EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH = 6;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -148,16 +152,6 @@ public class EmergencyNumberTracker extends Handler {
logd("ACTION_NETWORK_COUNTRY_CHANGED: PhoneId: " + phoneId + " CountryIso: "
+ countryIso);
- // Sometimes the country is updated as an empty string when the network signal
- // is lost; though we may not call emergency when there is no signal, we want
- // to keep the old country iso to provide country-related emergency numbers,
- // because they think they are still in that country. We don't need to update
- // country change in this case. We will still need to update the empty string
- // if device is in APM.
- if (TextUtils.isEmpty(countryIso) && !isAirplaneModeEnabled()) {
- return;
- }
-
// Update country iso change for available Phones
updateEmergencyCountryIsoAllPhones(countryIso == null ? "" : countryIso);
}
@@ -170,13 +164,6 @@ public class EmergencyNumberTracker extends Handler {
mPhone = phone;
mCi = ci;
- try {
- mEmergencyNumberDbOtaFileInputStream = new FileInputStream(
- new File(Environment.getDataDirectory(), EMERGENCY_NUMBER_DB_OTA_FILE_PATH));
- } catch (FileNotFoundException ex) {
- loge("Initialize ota emergency database file input failure: " + ex);
- }
-
if (mPhone != null) {
CarrierConfigManager configMgr = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -309,11 +296,14 @@ public class EmergencyNumberTracker extends Handler {
EmergencyNumberTracker emergencyNumberTracker;
if (phone != null && phone.getEmergencyNumberTracker() != null) {
emergencyNumberTracker = phone.getEmergencyNumberTracker();
- if (TextUtils.isEmpty(emergencyNumberTracker.getEmergencyCountryIso())
- || emergencyNumberTracker.mIsCountrySetByAnotherSub) {
- emergencyNumberTracker.mIsCountrySetByAnotherSub = true;
- emergencyNumberTracker.updateEmergencyNumberDatabaseCountryChange(
+ // If signal is lost, do not update the empty country iso for other slots.
+ if (!TextUtils.isEmpty(countryIso)) {
+ if (TextUtils.isEmpty(emergencyNumberTracker.getEmergencyCountryIso())
+ || emergencyNumberTracker.mIsCountrySetByAnotherSub) {
+ emergencyNumberTracker.mIsCountrySetByAnotherSub = true;
+ emergencyNumberTracker.updateEmergencyNumberDatabaseCountryChange(
countryIso);
+ }
}
}
}
@@ -406,22 +396,32 @@ public class EmergencyNumberTracker extends Handler {
for (int typeData : eccInfo.types) {
switch (typeData) {
case EccInfo.Type.POLICE:
- emergencyServiceCategoryBitmask = emergencyServiceCategoryBitmask == 0
- ? EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
- : emergencyServiceCategoryBitmask
- | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
+ emergencyServiceCategoryBitmask |=
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
break;
case EccInfo.Type.AMBULANCE:
- emergencyServiceCategoryBitmask = emergencyServiceCategoryBitmask == 0
- ? EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
- : emergencyServiceCategoryBitmask
- | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE;
+ emergencyServiceCategoryBitmask |=
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE;
break;
case EccInfo.Type.FIRE:
- emergencyServiceCategoryBitmask = emergencyServiceCategoryBitmask == 0
- ? EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
- : emergencyServiceCategoryBitmask
- | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE;
+ emergencyServiceCategoryBitmask |=
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE;
+ break;
+ case EccInfo.Type.MARINE_GUARD:
+ emergencyServiceCategoryBitmask |=
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD;
+ break;
+ case EccInfo.Type.MOUNTAIN_RESCUE:
+ emergencyServiceCategoryBitmask |=
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE;
+ break;
+ case EccInfo.Type.MIEC:
+ emergencyServiceCategoryBitmask |=
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC;
+ break;
+ case EccInfo.Type.AIEC:
+ emergencyServiceCategoryBitmask |=
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC;
break;
default:
// Ignores unknown types.
@@ -488,6 +488,7 @@ public class EmergencyNumberTracker extends Handler {
}
private int cacheOtaEmergencyNumberDatabase() {
+ FileInputStream fileInputStream = null;
BufferedInputStream inputStream = null;
ProtobufEccData.AllInfo allEccMessages = null;
int otaDatabaseVersion = INVALID_DATABASE_VERSION;
@@ -496,20 +497,26 @@ public class EmergencyNumberTracker extends Handler {
List<EmergencyNumber> updatedOtaEmergencyNumberList = new ArrayList<>();
try {
// If OTA File partition is not available, try to reload the default one.
- if (mEmergencyNumberDbOtaFileInputStream == null) {
- mEmergencyNumberDbOtaFileInputStream = new FileInputStream(
- new File(Environment.getDataDirectory(), EMERGENCY_NUMBER_DB_OTA_FILE_PATH));
+ if (mOverridedOtaDbParcelFileDescriptor == null) {
+ fileInputStream = new FileInputStream(
+ new File(Environment.getDataDirectory(),
+ EMERGENCY_NUMBER_DB_OTA_FILE_PATH));
+ } else {
+ File file = ParcelFileDescriptor
+ .getFile(mOverridedOtaDbParcelFileDescriptor.getFileDescriptor());
+ fileInputStream = new FileInputStream(new File(file.getAbsolutePath()));
}
- inputStream = new BufferedInputStream(mEmergencyNumberDbOtaFileInputStream);
+ inputStream = new BufferedInputStream(fileInputStream);
allEccMessages = ProtobufEccData.AllInfo.parseFrom(readInputStreamToByteArray(
new GZIPInputStream(inputStream)));
- logd(mCountryIso + " ota emergency database is loaded. Ver: " + otaDatabaseVersion);
+ String countryIso = getLastKnownEmergencyCountryIso();
+ logd(countryIso + " ota emergency database is loaded. Ver: " + otaDatabaseVersion);
otaDatabaseVersion = allEccMessages.revision;
for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) {
- if (countryEccInfo.isoCode.equals(mCountryIso.toUpperCase())) {
+ if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) {
for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) {
updatedOtaEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
- eccInfo, mCountryIso));
+ eccInfo, countryIso));
}
}
}
@@ -517,7 +524,7 @@ public class EmergencyNumberTracker extends Handler {
} catch (IOException ex) {
loge("Cache ota emergency database IOException: " + ex);
} finally {
- // close quietly by catching non-runtime exceptions.
+ // Close quietly by catching non-runtime exceptions.
if (inputStream != null) {
try {
inputStream.close();
@@ -526,6 +533,14 @@ public class EmergencyNumberTracker extends Handler {
} catch (Exception ignored) {
}
}
+ if (fileInputStream != null) {
+ try {
+ fileInputStream.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
}
// Use a valid database that has higher version.
@@ -606,17 +621,7 @@ public class EmergencyNumberTracker extends Handler {
private void overrideOtaEmergencyNumberDbFilePath(
ParcelFileDescriptor otaParcelableFileDescriptor) {
logd("overrideOtaEmergencyNumberDbFilePath:" + otaParcelableFileDescriptor);
- try {
- if (otaParcelableFileDescriptor == null) {
- mEmergencyNumberDbOtaFileInputStream = new FileInputStream(
- new File(Environment.getDataDirectory(), EMERGENCY_NUMBER_DB_OTA_FILE_PATH));
- } else {
- mEmergencyNumberDbOtaFileInputStream = new FileInputStream(
- otaParcelableFileDescriptor.getFileDescriptor());
- }
- } catch (FileNotFoundException ex) {
- loge("Override ota emergency database failure: " + ex);
- }
+ mOverridedOtaDbParcelFileDescriptor = otaParcelableFileDescriptor;
}
private void updateOtaEmergencyNumberListDatabaseAndNotify() {
@@ -720,8 +725,9 @@ public class EmergencyNumberTracker extends Handler {
// According to com.android.i18n.phonenumbers.ShortNumberInfo, in
// these countries, if extra digits are added to an emergency number,
// it no longer connects to the emergency service.
- if (mCountryIso.equals("br") || mCountryIso.equals("cl")
- || mCountryIso.equals("ni")) {
+ String countryIso = getLastKnownEmergencyCountryIso();
+ if (countryIso.equals("br") || countryIso.equals("cl")
+ || countryIso.equals("ni")) {
exactMatch = true;
} else {
exactMatch = false || exactMatch;
@@ -804,6 +810,10 @@ public class EmergencyNumberTracker extends Handler {
return mCountryIso;
}
+ public String getLastKnownEmergencyCountryIso() {
+ return mLastKnownEmergencyCountryIso;
+ }
+
private String getCountryIsoForCachingDatabase() {
ServiceStateTracker sst = mPhone.getServiceStateTracker();
if (sst != null) {
@@ -821,6 +831,10 @@ public class EmergencyNumberTracker extends Handler {
private synchronized void updateEmergencyCountryIso(String countryIso) {
mCountryIso = countryIso;
+ if (!TextUtils.isEmpty(mCountryIso)) {
+ mLastKnownEmergencyCountryIso = mCountryIso;
+ }
+ mCurrentDatabaseVersion = INVALID_DATABASE_VERSION;
}
/**
@@ -911,8 +925,8 @@ public class EmergencyNumberTracker extends Handler {
number = PhoneNumberUtils.stripSeparators(number);
for (EmergencyNumber num : mEmergencyNumberListFromDatabase) {
if (num.getNumber().equals(number)) {
- return new EmergencyNumber(number, mCountryIso.toLowerCase(), "",
- num.getEmergencyServiceCategoryBitmask(),
+ return new EmergencyNumber(number, getLastKnownEmergencyCountryIso().toLowerCase(),
+ "", num.getEmergencyServiceCategoryBitmask(),
new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
}
@@ -955,7 +969,8 @@ public class EmergencyNumberTracker extends Handler {
emergencyNumbers = SystemProperties.get(ecclist, "");
- logd("slotId:" + slotId + " country:" + mCountryIso + " emergencyNumbers: "
+ String countryIso = getLastKnownEmergencyCountryIso();
+ logd("slotId:" + slotId + " country:" + countryIso + " emergencyNumbers: "
+ emergencyNumbers);
if (TextUtils.isEmpty(emergencyNumbers)) {
@@ -970,8 +985,8 @@ public class EmergencyNumberTracker extends Handler {
// According to com.android.i18n.phonenumbers.ShortNumberInfo, in
// these countries, if extra digits are added to an emergency number,
// it no longer connects to the emergency service.
- if (useExactMatch || mCountryIso.equals("br") || mCountryIso.equals("cl")
- || mCountryIso.equals("ni")) {
+ if (useExactMatch || countryIso.equals("br") || countryIso.equals("cl")
+ || countryIso.equals("ni")) {
if (number.equals(emergencyNum)) {
return true;
} else {
@@ -1030,26 +1045,26 @@ public class EmergencyNumberTracker extends Handler {
}
// No ecclist system property, so use our own list.
- if (mCountryIso != null) {
+ if (countryIso != null) {
ShortNumberInfo info = ShortNumberInfo.getInstance();
if (useExactMatch) {
- if (info.isEmergencyNumber(number, mCountryIso.toUpperCase())) {
+ if (info.isEmergencyNumber(number, countryIso.toUpperCase())) {
return true;
} else {
for (String prefix : mEmergencyNumberPrefix) {
- if (info.isEmergencyNumber(prefix + number, mCountryIso.toUpperCase())) {
+ if (info.isEmergencyNumber(prefix + number, countryIso.toUpperCase())) {
return true;
}
}
}
return false;
} else {
- if (info.connectsToEmergencyNumber(number, mCountryIso.toUpperCase())) {
+ if (info.connectsToEmergencyNumber(number, countryIso.toUpperCase())) {
return true;
} else {
for (String prefix : mEmergencyNumberPrefix) {
if (info.connectsToEmergencyNumber(prefix + number,
- mCountryIso.toUpperCase())) {
+ countryIso.toUpperCase())) {
return true;
}
}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index f135e62c85..f143552c19 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -57,7 +57,6 @@ import android.service.euicc.IOtaStatusChangedCallback;
import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
-import android.telephony.PackageChangeReceiver;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
@@ -70,6 +69,7 @@ import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PackageChangeReceiver;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.IState;
import com.android.internal.util.State;
@@ -404,6 +404,8 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
start();
+ // All app package changes could trigger the package monitor receiver. It is not limited to
+ // apps extended from EuiccService.
mPackageMonitor.register(mContext, null /* thread */, null /* user */);
mContext.registerReceiver(
mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
@@ -555,10 +557,10 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
mSelectedComponent = findBestComponent();
if (mSelectedComponent != null) {
transitionTo(mAvailableState);
+ updateSubscriptionInfoListForAllAccessibleEuiccs();
} else if (getCurrentState() != mUnavailableState) {
transitionTo(mUnavailableState);
}
- updateSubscriptionInfoListForAllAccessibleEuiccs();
return HANDLED;
} else if (isEuiccCommand(message.what)) {
BaseEuiccCommandCallback callback = getCallback(message);
@@ -651,11 +653,16 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
if (bestComponent == null) {
isSameComponent = mSelectedComponent != null;
} else {
+ // Checks whether the bound component is the same as the best component. If it
+ // is not, set isSameComponent to false and the connector will bind the best
+ // component instead.
isSameComponent = mSelectedComponent == null
|| Objects.equals(new ComponentName(bestComponent.packageName,
bestComponent.name),
new ComponentName(mSelectedComponent.packageName, mSelectedComponent.name));
}
+ // Checks whether the bound component is impacted by the package changes. If it is,
+ // change the forceRebind to true so the connector will re-bind the component.
boolean forceRebind = bestComponent != null
&& Objects.equals(bestComponent.packageName, affectedPackage);
if (!isSameComponent || forceRebind) {
@@ -666,8 +673,8 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
} else {
transitionTo(mBindingState);
}
+ updateSubscriptionInfoListForAllAccessibleEuiccs();
}
- updateSubscriptionInfoListForAllAccessibleEuiccs();
return HANDLED;
} else if (message.what == CMD_CONNECT_TIMEOUT) {
transitionTo(mAvailableState);
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 400c326713..4ac317602a 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -343,10 +343,10 @@ public class EuiccController extends IEuiccController.Stub {
"Must have WRITE_EMBEDDED_SUBSCRIPTIONS to check if the country is supported");
}
if (mSupportedCountries == null || mSupportedCountries.isEmpty()) {
- Log.i(TAG, "Using blacklist unsupportedCountries=" + mUnsupportedCountries);
+ Log.i(TAG, "Using deny list unsupportedCountries=" + mUnsupportedCountries);
return !isEsimUnsupportedCountry(countryIso);
} else {
- Log.i(TAG, "Using whitelist supportedCountries=" + mSupportedCountries);
+ Log.i(TAG, "Using allow list supportedCountries=" + mSupportedCountries);
return isEsimSupportedCountry(countryIso);
}
}
@@ -1293,7 +1293,10 @@ public class EuiccController extends IEuiccController.Stub {
confirmationCodeRetried);
intent.putExtra(EXTRA_OPERATION, op);
PendingIntent resolutionIntent = PendingIntent.getActivity(
- mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_ONE_SHOT);
+ mContext,
+ 0 /* requestCode */,
+ intent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
extrasIntent.putExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_INTENT, resolutionIntent);
}
@@ -1535,8 +1538,13 @@ public class EuiccController extends IEuiccController.Stub {
// There is no active subscription on the target SIM, checks whether the caller can
// manage any active subscription on any other SIM.
- return mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
} else {
for (SubscriptionInfo subInfo : subInfoList) {
if (subInfo.isEmbedded()
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index 47f75ca32f..86e646302e 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Message;
import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
@@ -38,8 +39,7 @@ import com.android.internal.telephony.VisualVoicemailSmsFilter;
import com.android.internal.telephony.uicc.UsimServiceTable;
/**
- * This class broadcasts incoming SMS messages to interested apps after storing them in
- * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
+ * Subclass of {@link InboundSmsHandler} for 3GPP type messages.
*/
public class GsmInboundSmsHandler extends InboundSmsHandler {
@@ -105,12 +105,6 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
return;
}
- // Return early if phone_id is explicilty included and does not match mPhone.
- // If phone_id extra is not included, continue.
- int phoneId = mPhone.getPhoneId();
- if (intent.getIntExtra("phone_id", phoneId) != phoneId) {
- return;
- }
Message m = Message.obtain();
AsyncResult.forMessage(m, smsPdu, null);
mCellBroadcastServiceManager.sendGsmMessageToHandler(m);
@@ -153,11 +147,12 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
* are handled by {@link #dispatchNormalMessage} in parent class.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return a result code from {@link android.provider.Telephony.Sms.Intents},
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
@Override
- protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) {
+ protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) {
SmsMessage sms = (SmsMessage) smsb;
if (sms.isTypeZero()) {
@@ -174,14 +169,14 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
// As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
// Displayed/Stored/Notified. They should only be acknowledged.
log("Received short message type 0, Don't display or store it. Send Ack");
- addSmsTypeZeroToMetrics();
+ addSmsTypeZeroToMetrics(smsSource);
return Intents.RESULT_SMS_HANDLED;
}
// Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
if (sms.isUsimDataDownload()) {
UsimServiceTable ust = mPhone.getUsimServiceTable();
- return mDataDownloadHandler.handleUsimDataDownload(ust, sms);
+ return mDataDownloadHandler.handleUsimDataDownload(ust, sms, smsSource);
}
boolean handled = false;
@@ -195,7 +190,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
if (DBG) log("Received voice mail indicator clear SMS shouldStore=" + !handled);
}
if (handled) {
- addVoicemailSmsToMetrics();
+ addVoicemailSmsToMetrics(smsSource);
return Intents.RESULT_SMS_HANDLED;
}
@@ -206,7 +201,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
return Intents.RESULT_SMS_OUT_OF_MEMORY;
}
- return dispatchNormalMessage(smsb);
+ return dispatchNormalMessage(smsb, smsSource);
}
private void updateMessageWaitingIndicator(int voicemailCount) {
@@ -229,7 +224,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
* @param result result code indicating any error
* @param response callback message sent when operation completes.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
mPhone.mCi.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response);
@@ -258,16 +253,18 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
/**
* Add SMS of type 0 to metrics.
*/
- private void addSmsTypeZeroToMetrics() {
+ private void addSmsTypeZeroToMetrics(@SmsSource int smsSource) {
mMetrics.writeIncomingSmsTypeZero(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP);
+ mPhone.getSmsStats().onIncomingSmsTypeZero(smsSource);
}
/**
* Add voicemail indication SMS 0 to metrics.
*/
- private void addVoicemailSmsToMetrics() {
+ private void addVoicemailSmsToMetrics(@SmsSource int smsSource) {
mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP);
+ mPhone.getSmsStats().onIncomingSmsVoicemail(false /* is3gpp2 */, smsSource);
}
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 9714de07ce..dec246836c 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.gsm;
+import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
+
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
@@ -31,17 +33,20 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandException;
@@ -137,25 +142,25 @@ public final class GsmMmiCode extends Handler implements MmiCode {
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
GsmCdmaPhone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Context mContext;
UiccCardApplication mUiccApplication;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
IccRecords mIccRecords;
String mAction; // One of ACTION_*
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mSc; // Service Code
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mSia; // Service Info a
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mSib; // Service Info b
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mSic; // Service Info c
String mPoundString; // Entire MMI string up to and including #
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String mDialingNumber;
String mPwd; // For password registration
@@ -220,7 +225,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
*
* Please see flow chart in TS 22.030 6.5.3.2
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static GsmMmiCode newFromDialString(String dialString, GsmCdmaPhone phone,
UiccCardApplication app) {
return newFromDialString(dialString, phone, app, null);
@@ -231,10 +236,14 @@ public final class GsmMmiCode extends Handler implements MmiCode {
Matcher m;
GsmMmiCode ret = null;
- if (phone.getServiceState().getVoiceRoaming()
- && phone.supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) {
+ if ((phone.getServiceState().getVoiceRoaming()
+ && phone.supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming())
+ || (isEmergencyNumber(phone, dialString)
+ && isCarrierSupportCallerIdVerticalServiceCodes(phone))) {
/* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString
- so that it can be processed by the matcher and code below
+ so that it can be processed by the matcher and code below. This can be triggered if
+ the dialing string is an emergency number and carrier supports caller ID vertical
+ service codes *67, *82.
*/
dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString);
}
@@ -616,7 +625,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
//***** Constructor
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public GsmMmiCode(GsmCdmaPhone phone, UiccCardApplication app) {
// The telephony unit-test cases may create GsmMmiCode's
// in secondary threads
@@ -775,7 +784,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
return false;
}
- if (PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), dialString)) {
+ if (isEmergencyNumber(phone, dialString)) {
return false;
} else {
return isShortCodeUSSD(dialString, phone);
@@ -827,10 +836,42 @@ public final class GsmMmiCode extends Handler implements MmiCode {
* " # 31 # [called number] SEND "
*/
@UnsupportedAppUsage
- public boolean
- isTemporaryModeCLIR() {
- return mSc != null && mSc.equals(SC_CLIR) && mDialingNumber != null
- && (isActivate() || isDeactivate());
+ public boolean isTemporaryModeCLIR() {
+ return mSc != null && mSc.equals(SC_CLIR)
+ && mDialingNumber != null && (isActivate() || isDeactivate());
+ }
+
+ /**
+ * Checks if the dialing string is an emergency number.
+ */
+ @VisibleForTesting
+ public static boolean isEmergencyNumber(Phone phone, String dialString) {
+ try {
+ TelephonyManager tm = phone.getContext().getSystemService(TelephonyManager.class);
+ return tm.isEmergencyNumber(dialString);
+ } catch (RuntimeException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if carrier supports caller id vertical service codes by checking with
+ * {@link CarrierConfigManager#KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL}.
+ */
+ @VisibleForTesting
+ public static boolean isCarrierSupportCallerIdVerticalServiceCodes(Phone phone) {
+ CarrierConfigManager configManager = phone.getContext().getSystemService(
+ CarrierConfigManager.class);
+ PersistableBundle b = null;
+ if (configManager != null) {
+ // If an invalid subId is used, this bundle will contain default values.
+ b = configManager.getConfigForSubId(phone.getSubId());
+ }
+ if (b != null) {
+ return b == null ? false : b.getBoolean(CarrierConfigManager
+ .KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL);
+ }
+ return false;
}
/**
@@ -1116,7 +1157,18 @@ public final class GsmMmiCode extends Handler implements MmiCode {
throw new RuntimeException ("Ivalid register/action=" + mAction);
}
} else if (mPoundString != null) {
- sendUssd(mPoundString);
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_allow_ussd_over_ims)) {
+ int ussd_method = getIntCarrierConfig(
+ CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT);
+ if (ussd_method != USSD_OVER_IMS_ONLY) {
+ sendUssd(mPoundString);
+ } else {
+ throw new RuntimeException("The USSD request is not allowed over CS");
+ }
+ } else {
+ sendUssd(mPoundString);
+ }
} else {
Rlog.d(LOG_TAG, "processCode: Invalid or Unsupported MMI Code");
throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -1176,7 +1228,9 @@ public final class GsmMmiCode extends Handler implements MmiCode {
onUssdFinishedError() {
if (mState == State.PENDING) {
mState = State.FAILED;
- mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+ if (TextUtils.isEmpty(mMessage)) {
+ mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+ }
Rlog.d(LOG_TAG, "onUssdFinishedError");
mPhone.onMMIDone(this);
}
@@ -1748,6 +1802,27 @@ public final class GsmMmiCode extends Handler implements MmiCode {
return sb;
}
+ /**
+ * Get the int config from carrier config manager.
+ *
+ * @param key config key defined in CarrierConfigManager
+ * @return integer value of corresponding key.
+ */
+ private int getIntCarrierConfig(String key) {
+ CarrierConfigManager ConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ PersistableBundle b = null;
+ if (ConfigManager != null) {
+ // If an invalid subId is used, this bundle will contain default values.
+ b = ConfigManager.getConfigForSubId(mPhone.getSubId());
+ }
+ if (b != null) {
+ return b.getInt(key);
+ } else {
+ // Return static default defined in CarrierConfigManager.
+ return CarrierConfigManager.getDefaultConfig().getInt(key);
+ }
+ }
+
public ResultReceiver getUssdCallbackReceiver() {
return this.mCallbackReceiver;
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 8a80b4570c..b3a8038833 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -20,16 +20,16 @@ import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Message;
-import android.provider.Telephony.Sms.Intents;
import android.telephony.ServiceState;
-import android.util.Pair;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsController;
import com.android.internal.telephony.SmsDispatchersController;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
@@ -49,7 +49,7 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
private AtomicReference<UiccCardApplication> mUiccApplication =
new AtomicReference<UiccCardApplication>();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private GsmInboundSmsHandler mGsmInboundSmsHandler;
/** Status report received */
@@ -72,7 +72,7 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
mUiccController.unregisterForIccChanged(this);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected String getFormat() {
return SmsConstants.FORMAT_3GPP;
@@ -132,40 +132,16 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
}
/**
- * Called when a status report is received. This should correspond to
- * a previously successful SEND.
+ * Called when a status report is received. This should correspond to a previously successful
+ * SEND.
*
- * @param ar AsyncResult passed into the message handler. ar.result should
- * be a String representing the status report PDU, as ASCII hex.
+ * @param ar AsyncResult passed into the message handler. ar.result should be a byte array for
+ * the status report PDU.
*/
private void handleStatusReport(AsyncResult ar) {
byte[] pdu = (byte[]) ar.result;
- SmsMessage sms = SmsMessage.newFromCDS(pdu);
- boolean handled = false;
-
- if (sms != null) {
- int messageRef = sms.mMessageRef;
- for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
- SmsTracker tracker = deliveryPendingList.get(i);
- if (tracker.mMessageRef == messageRef) {
- Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
- tracker,
- getFormat(),
- pdu);
- if (result.second) {
- deliveryPendingList.remove(i);
- }
- handled = true;
- break; // Only expect to see one tracker matching this messageref
- }
- }
- if (!handled) {
- // Try to find the sent SMS from the map in ImsSmsDispatcher.
- mSmsDispatchersController.handleSentOverImsStatusReport(
- messageRef, getFormat(), pdu);
- }
- }
- mCi.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
+ mSmsDispatchersController.handleSmsStatusReport(SmsConstants.FORMAT_3GPP, pdu);
+ mCi.acknowledgeLastIncomingGsmSms(true, 0 /* cause */, null);
}
/** {@inheritDoc} */
@@ -181,7 +157,7 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
+ " mMessageRef=" + tracker.mMessageRef
+ " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
+ " SS=" + ss
- + " id=" + tracker.mMessageId);
+ + " " + SmsController.formatCrossStackMessageId(tracker.mMessageId));
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
diff --git a/src/java/com/android/internal/telephony/gsm/SimTlv.java b/src/java/com/android/internal/telephony/gsm/SimTlv.java
index ee0d3ffbba..f377605968 100644
--- a/src/java/com/android/internal/telephony/gsm/SimTlv.java
+++ b/src/java/com/android/internal/telephony/gsm/SimTlv.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.gsm;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
* SIM Tag-Length-Value record
@@ -35,7 +36,7 @@ public class SimTlv
int mCurOffset;
int mCurDataOffset;
int mCurDataLength;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean mHasValidTlvObject;
@UnsupportedAppUsage
diff --git a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
index 5d088460ce..ed819c1adc 100644
--- a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
@@ -25,6 +25,8 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.cat.ComprehensionTlvTag;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -73,9 +75,11 @@ public class UsimDataDownloadHandler extends Handler {
*
* @param ust the UsimServiceTable, to check if data download is enabled
* @param smsMessage the SMS message to process
+ * @param smsSource the source of the SMS message
* @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
*/
- int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage) {
+ int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage,
+ @InboundSmsHandler.SmsSource int smsSource) {
// If we receive an SMS-PP message before the UsimServiceTable has been loaded,
// assume that the data download service is not present. This is very unlikely to
// happen because the IMS connection will not be established until after the ISIM
@@ -83,7 +87,7 @@ public class UsimDataDownloadHandler extends Handler {
if (ust != null && ust.isAvailable(
UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) {
Rlog.d(TAG, "Received SMS-PP data download, sending to UICC.");
- return startDataDownload(smsMessage);
+ return startDataDownload(smsMessage, smsSource);
} else {
Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC.");
String smsc = IccUtils.bytesToHexString(
@@ -92,7 +96,7 @@ public class UsimDataDownloadHandler extends Handler {
mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc,
IccUtils.bytesToHexString(smsMessage.getPdu()),
obtainMessage(EVENT_WRITE_SMS_COMPLETE));
- addUsimDataDownloadToMetrics(false);
+ addUsimDataDownloadToMetrics(false, smsSource);
return Activity.RESULT_OK; // acknowledge after response from write to USIM
}
@@ -103,10 +107,13 @@ public class UsimDataDownloadHandler extends Handler {
* thread than this Handler is running on.
*
* @param smsMessage the message to process
+ * @param smsSource the source of the SMS message
* @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
*/
- public int startDataDownload(SmsMessage smsMessage) {
- if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD, smsMessage))) {
+ public int startDataDownload(SmsMessage smsMessage,
+ @InboundSmsHandler.SmsSource int smsSource) {
+ if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD,
+ smsSource, 0 /* unused */, smsMessage))) {
return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response
} else {
Rlog.e(TAG, "startDataDownload failed to send message to start data download.");
@@ -114,7 +121,8 @@ public class UsimDataDownloadHandler extends Handler {
}
}
- private void handleDataDownload(SmsMessage smsMessage) {
+ private void handleDataDownload(SmsMessage smsMessage,
+ @InboundSmsHandler.SmsSource int smsSource) {
int dcs = smsMessage.getDataCodingScheme();
int pid = smsMessage.getProtocolIdentifier();
byte[] pdu = smsMessage.getPdu(); // includes SC address
@@ -166,7 +174,7 @@ public class UsimDataDownloadHandler extends Handler {
if (index != envelope.length) {
Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting.");
acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR);
- addUsimDataDownloadToMetrics(false);
+ addUsimDataDownloadToMetrics(false, smsSource);
return;
}
@@ -174,7 +182,7 @@ public class UsimDataDownloadHandler extends Handler {
mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage(
EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid }));
- addUsimDataDownloadToMetrics(true);
+ addUsimDataDownloadToMetrics(true, smsSource);
}
/**
@@ -284,9 +292,11 @@ public class UsimDataDownloadHandler extends Handler {
* to the USIM. The metrics does not cover the case where the SMS-PP might be rejected
* by the USIM itself.
*/
- private void addUsimDataDownloadToMetrics(boolean result) {
+ private void addUsimDataDownloadToMetrics(boolean result,
+ @InboundSmsHandler.SmsSource int smsSource) {
TelephonyMetrics metrics = TelephonyMetrics.getInstance();
metrics.writeIncomingSMSPP(mPhoneId, android.telephony.SmsMessage.FORMAT_3GPP, result);
+ PhoneFactory.getPhone(mPhoneId).getSmsStats().onIncomingSmsPP(smsSource, result);
}
/**
@@ -300,7 +310,7 @@ public class UsimDataDownloadHandler extends Handler {
switch (msg.what) {
case EVENT_START_DATA_DOWNLOAD:
- handleDataDownload((SmsMessage) msg.obj);
+ handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */);
break;
case EVENT_SEND_ENVELOPE_RESPONSE:
diff --git a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index f6fc0690ed..e594ab6c1f 100755
--- a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.gsm;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.SparseArray;
@@ -43,7 +44,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
private static final boolean DBG = true;
private ArrayList<PbrRecord> mPbrRecords;
private Boolean mIsPbrPresent;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IccFileHandler mFh;
private AdnRecordCache mAdnCache;
@UnsupportedAppUsage
@@ -287,12 +288,12 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
private void buildType1EmailList(int recId) {
/**
* If this is type 1, the number of records in EF_EMAIL would be same as the record number
- * in the master/reference file.
+ * in the main/reference file.
*/
if (mPbrRecords.get(recId) == null)
return;
- int numRecs = mPbrRecords.get(recId).mMasterFileRecordNum;
+ int numRecs = mPbrRecords.get(recId).mMainFileRecordNum;
log("Building type 1 email list. recId = "
+ recId + ", numRecs = " + numRecs);
@@ -359,15 +360,15 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
if (mPbrRecords.get(recId) == null)
return false;
- int numRecs = mPbrRecords.get(recId).mMasterFileRecordNum;
+ int numRecs = mPbrRecords.get(recId).mMainFileRecordNum;
log("Building type 2 email list. recId = "
+ recId + ", numRecs = " + numRecs);
/**
* 3GPP TS 31.102 4.4.2.1 EF_PBR (Phone Book Reference file) table 4.1
- * The number of records in the IAP file is same as the number of records in the master
- * file (e.g EF_ADN). The order of the pointers in an EF_IAP shall be the same as the
+ * The number of records in the IAP file is same as the number of records in the EF_ADN
+ * file. The order of the pointers in an EF_IAP shall be the same as the
* order of file IDs that appear in the TLV object indicated by Tag 'A9' in the
* reference file record (e.g value of mEmailTagNumberInIap)
*/
@@ -492,7 +493,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
* The recent added ADN record # would be the reference record size
* for the rest of EFs associated within this PBR.
*/
- mPbrRecords.get(recId).mMasterFileRecordNum = mPhoneBookRecords.size() - previousSize;
+ mPbrRecords.get(recId).mMainFileRecordNum = mPhoneBookRecords.size() - previousSize;
}
// Create the phonebook reference file based on EF_PBR
@@ -581,10 +582,10 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
/**
* 3GPP TS 31.102 4.4.2.1 EF_PBR (Phone Book Reference file)
* If this is type 1 files, files that contain as many records as the
- * reference/master file (EF_ADN, EF_ADN1) and are linked on record number
- * bases (Rec1 -> Rec1). The master file record number is the reference.
+ * reference/main file (EF_ADN, EF_ADN1) and are linked on record number
+ * bases (Rec1 -> Rec1). The EF_ADN/EF_ADN1 file record number is the reference.
*/
- private int mMasterFileRecordNum;
+ private int mMainFileRecordNum;
PbrRecord(byte[] record) {
mFileIds = new SparseArray<File>();
diff --git a/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java b/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java
index 5a51fd7c29..a41ed7ff46 100644
--- a/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java
+++ b/src/java/com/android/internal/telephony/ims/ImsRegistrationCompatAdapter.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.ims;
import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_NR;
import android.net.Uri;
import android.os.RemoteException;
@@ -34,6 +35,7 @@ public class ImsRegistrationCompatAdapter extends ImsRegistrationImplBase {
// Maps "RAT" based radio technologies to ImsRegistrationImplBase definitions.
private static final Map<Integer, Integer> RADIO_TECH_MAPPER = new ArrayMap<>(2);
static {
+ RADIO_TECH_MAPPER.put(RIL_RADIO_TECHNOLOGY_NR, REGISTRATION_TECH_NR);
RADIO_TECH_MAPPER.put(RIL_RADIO_TECHNOLOGY_LTE, REGISTRATION_TECH_LTE);
RADIO_TECH_MAPPER.put(RIL_RADIO_TECHNOLOGY_IWLAN, REGISTRATION_TECH_IWLAN);
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 2d293a1455..188e69582c 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.AsyncResult;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
@@ -40,8 +41,6 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsService;
import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
@@ -52,6 +51,8 @@ import android.util.LocalLog;
import android.util.Log;
import android.util.SparseArray;
+import com.android.ims.ImsFeatureBinderRepository;
+import com.android.ims.ImsFeatureContainer;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
@@ -68,6 +69,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -83,7 +85,7 @@ import java.util.stream.Collectors;
* 2. Device overlay default value (including no SIM case).
*
* ImsManager can then retrieve the binding to the correct ImsService using
- * {@link #getImsServiceControllerAndListen} on a per-slot and per feature basis.
+ * {@link #listenForFeature} on a per-slot and per feature basis.
*/
public class ImsResolver implements ImsServiceController.ImsServiceControllerCallbacks {
@@ -98,7 +100,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
@VisibleForTesting
public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
- // Overrides the sanity permission check of android.permission.BIND_IMS_SERVICE for any
+ // Overrides the correctness permission check of android.permission.BIND_IMS_SERVICE for any
// ImsService that is connecting to the platform.
// This should ONLY be used for testing and should not be used in production ImsServices.
private static final String METADATA_OVERRIDE_PERM_CHECK = "override_bind_check";
@@ -121,10 +123,33 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// Sent when the number of slots has dynamically changed on the device. We will need to
// resize available ImsServiceController slots and perform dynamic queries again.
private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 7;
+ // clear any carrier ImsService test overrides.
+ private static final int HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG = 8;
// Delay between dynamic ImsService queries.
private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
+ private static ImsResolver sInstance;
+
+ /**
+ * Create the ImsResolver Service singleton instance.
+ */
+ public static void make(Context context, String defaultMmTelPackageName,
+ String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo) {
+ if (sInstance == null) {
+ sInstance = new ImsResolver(context, defaultMmTelPackageName, defaultRcsPackageName,
+ numSlots, repo);
+ }
+ }
+
+ /**
+ * @return The ImsResolver Service instance. May be {@code null} if no ImsResolver was created
+ * due to IMS not being supported.
+ */
+ public static @Nullable ImsResolver getInstance() {
+ return sInstance;
+ }
+
private static class OverrideConfig {
public final int slotId;
public final boolean isCarrierService;
@@ -209,7 +234,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// Receives broadcasts from the system involving changes to the installed applications. If
// an ImsService that we are configured to use is installed, we must bind to it.
- private BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
@@ -233,7 +258,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// Receives the broadcast that a new Carrier Config has been loaded in order to possibly
// unbind from one service and bind to another.
- private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -249,15 +274,18 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
int slotSimState = mTelephonyManagerProxy.getSimState(mContext, slotId);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
- && slotSimState != TelephonyManager.SIM_STATE_ABSENT) {
+ && (slotSimState != TelephonyManager.SIM_STATE_ABSENT
+ && slotSimState != TelephonyManager.SIM_STATE_NOT_READY)) {
// We only care about carrier config updates that happen when a slot is known to be
- // absent or populated and the carrier config has been loaded.
+ // absent, the subscription is disabled (not ready), or populated and the carrier
+ // config has been loaded.
Log.i(TAG, "Received CCC for slot " + slotId + " and sim state "
+ slotSimState + ", ignoring.");
return;
}
- Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId);
+ Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId
+ + ", sim state: " + slotSimState);
mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, slotId).sendToTarget();
}
@@ -265,7 +293,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// Receives the broadcast that the device has finished booting (and the device is no longer
// encrypted).
- private BroadcastReceiver mBootCompleted = new BroadcastReceiver() {
+ private final BroadcastReceiver mBootCompleted = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received BOOT_COMPLETED");
@@ -342,7 +370,8 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
* @return the ImsServiceController created using the context and componentName supplied.
*/
ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks);
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository repo);
}
private ImsServiceControllerFactory mImsServiceControllerFactory =
@@ -355,8 +384,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
@Override
public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- return new ImsServiceController(context, componentName, callbacks);
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository repo) {
+ return new ImsServiceController(context, componentName, callbacks, repo);
}
};
@@ -369,7 +399,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
ImsServiceFeatureQueryManager.Listener listener);
}
- private ImsServiceControllerFactory mImsServiceControllerFactoryCompat =
+ private final ImsServiceControllerFactory mImsServiceControllerFactoryCompat =
new ImsServiceControllerFactory() {
@Override
public String getServiceInterface() {
@@ -378,8 +408,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
@Override
public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- return new ImsServiceControllerCompat(context, componentName, callbacks);
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository repo) {
+ return new ImsServiceControllerCompat(context, componentName, callbacks, repo);
}
};
@@ -393,24 +424,26 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// registered through, so we must retain the Context as long as we need the receiver to be
// active.
private final Context mReceiverContext;
+ private final ImsFeatureBinderRepository mRepo;
// Locks mBoundImsServicesByFeature only. Be careful to avoid deadlocks from
// ImsServiceController callbacks.
private final Object mBoundServicesLock = new Object();
private int mNumSlots;
// Array index corresponds to slot, per slot there is a feature->package name mapping.
// should only be accessed from handler
- private SparseArray<Map<Integer, String>> mCarrierServices;
+ private final SparseArray<Map<Integer, String>> mCarrierServices;
// Package name of the default device services, Maps ImsFeature -> packageName.
- // should only be accessed from handler
- private Map<Integer, String> mDeviceServices;
+ // Must synchronize on this object to access.
+ private final Map<Integer, String> mDeviceServices = new ArrayMap<>();
// Persistent Logging
private final LocalLog mEventLog = new LocalLog(50);
private boolean mBootCompletedHandlerRan = false;
+ private boolean mCarrierConfigReceived = false;
// Synchronize all events on a handler to ensure that the cache includes the most recent
// version of the installed ImsServices.
- private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
switch (msg.what) {
case HANDLER_ADD_PACKAGE: {
String packageName = (String) msg.obj;
@@ -426,8 +459,16 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
if (!mBootCompletedHandlerRan) {
mBootCompletedHandlerRan = true;
mEventLog.log("handling BOOT_COMPLETE");
- // Re-evaluate bound services for all slots after requerying packagemanager
- maybeAddedImsService(null /*packageName*/);
+ if (mCarrierConfigReceived) {
+ mEventLog.log("boot complete - reeval");
+ // Re-evaluate bound services for all slots after requerying packagemanager
+ maybeAddedImsService(null /*packageName*/);
+ } else {
+ mEventLog.log("boot complete - update cache");
+ // Do not bind any ImsServices yet, just update the cache to include new
+ // services. All will be re-evaluated after first carrier config changed.
+ updateInstalledServicesCache();
+ }
}
break;
}
@@ -439,6 +480,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
Log.w(TAG, "HANDLER_CONFIG_CHANGED for invalid slotid=" + slotId);
break;
}
+ mCarrierConfigReceived = true;
carrierConfigChanged(slotId);
break;
}
@@ -471,14 +513,20 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
handleMsimConfigChange((Integer) result.result);
break;
}
+ case HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG: {
+ clearCarrierServiceOverrides(msg.arg1);
+ break;
+ }
default:
return false;
}
return true;
});
+ private final HandlerExecutor mRunnableExecutor = new HandlerExecutor(mHandler);
+
// Results from dynamic queries to ImsService regarding the features they support.
- private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener =
+ private final ImsServiceFeatureQueryManager.Listener mDynamicQueryListener =
new ImsServiceFeatureQueryManager.Listener() {
@Override
@@ -507,28 +555,28 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// Used during testing, overrides the carrier services while non-empty.
// Array index corresponds to slot, per slot there is a feature->package name mapping.
// should only be accessed from handler
- private SparseArray<SparseArray<String>> mOverrideServices;
+ private final SparseArray<SparseArray<String>> mOverrideServices;
// Outer array index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
// Locked on mBoundServicesLock
- private SparseArray<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
+ private final SparseArray<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
// not locked, only accessed on a handler thread.
// Tracks list of all installed ImsServices
- private Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>();
+ private final Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>();
// not locked, only accessed on a handler thread.
// Active ImsServiceControllers, which are bound to ImsServices.
- private Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>();
+ private final Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>();
private ImsServiceFeatureQueryManager mFeatureQueryManager;
public ImsResolver(Context context, String defaultMmTelPackageName,
- String defaultRcsPackageName, int numSlots) {
+ String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo) {
Log.i(TAG, "device MMTEL package: " + defaultMmTelPackageName + ", device RCS package:"
+ defaultRcsPackageName);
mContext = context;
mNumSlots = numSlots;
+ mRepo = repo;
mReceiverContext = context.createContextAsUser(UserHandle.ALL, 0 /*flags*/);
mCarrierServices = new SparseArray<>(mNumSlots);
- mDeviceServices = new ArrayMap<>();
setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_EMERGENCY_MMTEL);
setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_MMTEL);
setDeviceConfiguration(defaultRcsPackageName, ImsFeature.FEATURE_RCS);
@@ -536,26 +584,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
Context.CARRIER_CONFIG_SERVICE);
mOverrideServices = new SparseArray<>(0 /*initial size*/);
mBoundImsServicesByFeature = new SparseArray<>(mNumSlots);
-
- IntentFilter appChangedFilter = new IntentFilter();
- appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- appChangedFilter.addDataScheme("package");
- mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
- mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
-
- UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (userManager.isUserUnlocked()) {
- mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
- } else {
- mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter(
- Intent.ACTION_BOOT_COMPLETED));
- if (userManager.isUserUnlocked()) {
- mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
- }
- }
}
@VisibleForTesting
@@ -593,13 +621,28 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
HANDLER_MSIM_CONFIGURATION_CHANGE, null);
mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener);
- // This will get all services with the correct intent filter from PackageManager
- List<ImsServiceInfo> infos = getImsServiceInfo(null);
- for (ImsServiceInfo info : infos) {
- if (!mInstalledServicesCache.containsKey(info.name)) {
- mInstalledServicesCache.put(info.name, info);
+ updateInstalledServicesCache();
+
+ IntentFilter appChangedFilter = new IntentFilter();
+ appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ appChangedFilter.addDataScheme("package");
+ mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
+ mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
+ CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ if (userManager.isUserUnlocked()) {
+ mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
+ } else {
+ mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter(
+ Intent.ACTION_BOOT_COMPLETED));
+ if (userManager.isUserUnlocked()) {
+ mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
}
}
+
// Update the package names of the carrier ImsServices if they do not exist already and
// possibly bind if carrier configs exist. Otherwise wait for CarrierConfigChanged
// indication.
@@ -607,6 +650,19 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
/**
+ * Query the system for all registered ImsServices and add them to the cache if there are any
+ * new ones that are not tracked.
+ */
+ private void updateInstalledServicesCache() {
+ // This will get all services with the correct intent filter from PackageManager
+ for (ImsServiceInfo info : getImsServiceInfo(null)) {
+ if (!mInstalledServicesCache.containsKey(info.name)) {
+ mInstalledServicesCache.put(info.name, info);
+ }
+ }
+ }
+
+ /**
* Destroys this ImsResolver. Used for tearing down static resources during testing.
*/
@VisibleForTesting
@@ -626,6 +682,8 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
if (!TextUtils.isEmpty(newPackageName)) {
mEventLog.log("bindCarrierServicesIfAvailable - carrier package found: "
+ newPackageName + " on slot " + slotId);
+ // Carrier configs are already available, so mark received.
+ mCarrierConfigReceived = true;
setCarrierConfiguredPackageName(newPackageName, slotId, f);
ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
// We do not want to trigger feature configuration changes unless there is
@@ -671,75 +729,19 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
/**
- * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id or {@link null} if
- * the service is not available. If an IImsMMTelFeature is available, the
- * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
- * @param slotId The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * the feature.
- * @return {@link IImsMmTelFeature} interface or {@link null} if it is unavailable.
- */
- public IImsMmTelFeature getMmTelFeatureAndListen(int slotId,
- IImsServiceFeatureCallback callback) {
- ImsServiceController controller = getImsServiceControllerAndListen(slotId,
- ImsFeature.FEATURE_MMTEL, callback);
- return (controller != null) ? controller.getMmTelFeature(slotId) : null;
- }
-
- /**
- * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for emergency
- * calling or {@link null} if the service is not available. If an IImsMMTelFeature is
- * available, the {@link IImsServiceFeatureCallback} callback is registered as a listener for
- * feature updates.
- * @param slotId The SIM slot that we are requesting the {@link IImsRcsFeature} for.
- * @param callback listener that will send updates to ImsManager when there are updates to
- * the feature.
- * @return {@link IImsRcsFeature} interface or {@link null} if it is unavailable.
- */
- public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) {
- ImsServiceController controller = getImsServiceControllerAndListen(slotId,
- ImsFeature.FEATURE_RCS, callback);
- return (controller != null) ? controller.getRcsFeature(slotId) : null;
- }
-
- /**
* Returns the ImsRegistration structure associated with the slotId and feature specified.
*/
- public @Nullable IImsRegistration getImsRegistration(int slotId, int feature)
- throws RemoteException {
- ImsServiceController controller = getImsServiceController(slotId, feature);
- if (controller != null) {
- return controller.getRegistration(slotId);
- }
- return null;
+ public @Nullable IImsRegistration getImsRegistration(int slotId, int feature) {
+ ImsFeatureContainer fc = mRepo.getIfExists(slotId, feature).orElse(null);
+ return (fc != null) ? fc.imsRegistration : null;
}
/**
* Returns the ImsConfig structure associated with the slotId and feature specified.
*/
- public @Nullable IImsConfig getImsConfig(int slotId, int feature)
- throws RemoteException {
- ImsServiceController controller = getImsServiceController(slotId, feature);
- if (controller != null) {
- return controller.getConfig(slotId);
- }
- return null;
- }
-
- @VisibleForTesting
- public ImsServiceController getImsServiceController(int slotId, int feature) {
- if (slotId < 0 || slotId >= mNumSlots) {
- return null;
- }
- ImsServiceController controller;
- synchronized (mBoundServicesLock) {
- SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
- if (services == null) {
- return null;
- }
- controller = services.get(feature);
- }
- return controller;
+ public @Nullable IImsConfig getImsConfig(int slotId, int feature) {
+ ImsFeatureContainer fc = mRepo.getIfExists(slotId, feature).orElse(null);
+ return (fc != null) ? fc.imsConfig : null;
}
private SparseArray<ImsServiceController> getImsServiceControllers(int slotId) {
@@ -755,32 +757,33 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
}
- @VisibleForTesting
- public ImsServiceController getImsServiceControllerAndListen(int slotId, int feature,
- IImsServiceFeatureCallback callback) {
- ImsServiceController controller = getImsServiceController(slotId, feature);
-
- if (controller != null) {
- controller.addImsServiceFeatureCallback(callback);
- return controller;
- }
- return null;
+ /**
+ * Register a new listener for the feature type and slot specified. ImsServiceController will
+ * update the connections as they become available.
+ */
+ public void listenForFeature(int slotId, int feature, IImsServiceFeatureCallback callback) {
+ mRepo.registerForConnectionUpdates(slotId, feature, callback, mRunnableExecutor);
}
/**
* Unregister a previously registered IImsServiceFeatureCallback through
- * {@link #getImsServiceControllerAndListen(int, int, IImsServiceFeatureCallback)} .
- * @param slotId The slot id associated with the ImsFeature.
- * @param feature The {@link ImsFeature.FeatureType}
+ * {@link #listenForFeature(int, int, IImsServiceFeatureCallback)}.
* @param callback The callback to be unregistered.
*/
- public void unregisterImsFeatureCallback(int slotId, int feature,
- IImsServiceFeatureCallback callback) {
- ImsServiceController controller = getImsServiceController(slotId, feature);
+ public void unregisterImsFeatureCallback(IImsServiceFeatureCallback callback) {
+ mRepo.unregisterForConnectionUpdates(callback);
+ }
- if (controller != null) {
- controller.removeImsServiceFeatureCallback(callback);
+ // Used for testing only.
+ public boolean clearCarrierImsServiceConfiguration(int slotId) {
+ if (slotId < 0 || slotId >= mNumSlots) {
+ Log.w(TAG, "clearCarrierImsServiceConfiguration: invalid slotId!");
+ return false;
}
+
+ Message.obtain(mHandler, HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG, slotId, 0 /*arg2*/)
+ .sendToTarget();
+ return true;
}
// Used for testing only.
@@ -797,14 +800,16 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
return true;
}
- // not synchronized, access through handler ONLY.
private String getDeviceConfiguration(@ImsFeature.FeatureType int featureType) {
- return mDeviceServices.getOrDefault(featureType, "");
+ synchronized (mDeviceServices) {
+ return mDeviceServices.getOrDefault(featureType, "");
+ }
}
- // not synchronized, access in handler ONLY.
private void setDeviceConfiguration(String name, @ImsFeature.FeatureType int featureType) {
- mDeviceServices.put(featureType, name);
+ synchronized (mDeviceServices) {
+ mDeviceServices.put(featureType, name);
+ }
}
// not synchronized, access in handler ONLY.
@@ -830,6 +835,13 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
// not synchronized, access in handler ONLY.
+ private void removeOverridePackageName(int slotId) {
+ for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
+ getOverridePackageName(slotId).remove(f);
+ }
+ }
+
+ // not synchronized, access in handler ONLY.
private void setOverridePackageName(@Nullable String packageName, int slotId,
@ImsFeature.FeatureType int featureType) {
getOverridePackageName(slotId).put(featureType, packageName);
@@ -864,7 +876,18 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
return false;
}
// Config exists, but the carrier ImsService also needs to support this feature
- ImsServiceInfo info = getImsServiceInfoFromCache(carrierPackage);
+ return doesCachedImsServiceExist(carrierPackage, slotId, featureType);
+ }
+
+ /**
+ * Check the cached ImsServices that exist on this device to determine if there is a ImsService
+ * with the same package name that matches the provided configuration.
+ */
+ // not synchronized, access in handler ONLY.
+ private boolean doesCachedImsServiceExist(String packageName, int slotId,
+ @ImsFeature.FeatureType int featureType) {
+ // Config exists, but the carrier ImsService also needs to support this feature
+ ImsServiceInfo info = getImsServiceInfoFromCache(packageName);
return info != null && info.getSupportedFeatures().stream().anyMatch(
feature -> feature.slotId == slotId && feature.featureType == featureType);
}
@@ -893,6 +916,117 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
}
+ /**
+ * Determines if there is a valid ImsService configured for the specified ImsFeature.
+ * @param slotId The slot ID to check for.
+ * @param featureType The ImsFeature featureType to check for.
+ * @return true if there is an ImsService configured for the specified ImsFeature type, false
+ * if there is not.
+ */
+ public boolean isImsServiceConfiguredForFeature(int slotId,
+ @ImsFeature.FeatureType int featureType) {
+ if (!TextUtils.isEmpty(getDeviceConfiguration(featureType))) {
+ // Shortcut a little bit here - instead of dynamically looking up the configured
+ // package name, which can be a long operation depending on the state, just return true
+ // if there is a configured device ImsService for the requested feature because that
+ // means there will always be at least a device configured ImsService.
+ return true;
+ }
+ return !TextUtils.isEmpty(getConfiguredImsServicePackageName(slotId, featureType));
+ }
+
+ /**
+ * Resolves the PackageName of the ImsService that is configured to be bound for the slotId and
+ * FeatureType specified and returns it.
+ * <p>
+ * If there is a PackageName that is configured, but there is no application on the device that
+ * fulfills that configuration, this method will also return {@code null} as the ImsService will
+ * not be bound.
+ *
+ * @param slotId The slot ID that the request is for.
+ * @param featureType The ImsService feature type that the request is for.
+ * @return The package name of the ImsService that will be bound from telephony for the provided
+ * slot id and featureType.
+ */
+ public String getConfiguredImsServicePackageName(int slotId,
+ @ImsFeature.FeatureType int featureType) {
+ if (slotId < 0 || slotId >= mNumSlots || featureType <= ImsFeature.FEATURE_INVALID
+ || featureType >= ImsFeature.FEATURE_MAX) {
+ Log.w(TAG, "getResolvedImsServicePackageName received invalid parameters - slot: "
+ + slotId + ", feature: " + featureType);
+ return null;
+ }
+ CompletableFuture<String> packageNameFuture = new CompletableFuture<>();
+ final long startTimeMs = System.currentTimeMillis();
+ if (mHandler.getLooper().isCurrentThread()) {
+ // If we are on the same thread as the Handler's looper, run the internal method
+ // directly.
+ packageNameFuture.complete(getConfiguredImsServicePackageNameInternal(slotId,
+ featureType));
+ } else {
+ mHandler.post(() -> {
+ try {
+ packageNameFuture.complete(getConfiguredImsServicePackageNameInternal(slotId,
+ featureType));
+ } catch (Exception e) {
+ // Catch all Exceptions to ensure we do not block indefinitely in the case of an
+ // unexpected error.
+ packageNameFuture.completeExceptionally(e);
+ }
+ });
+ }
+ try {
+ String packageName = packageNameFuture.get();
+ long timeDiff = System.currentTimeMillis() - startTimeMs;
+ if (timeDiff > 50) {
+ // Took an unusually long amount of time (> 50 ms), so log it.
+ mEventLog.log("getResolvedImsServicePackageName - [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType)
+ + "], async query complete, took " + timeDiff + " ms with package name: "
+ + packageName);
+ Log.w(TAG, "getResolvedImsServicePackageName: [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType)
+ + "], async query complete, took " + timeDiff + " ms with package name: "
+ + packageName);
+ }
+ return packageName;
+ } catch (Exception e) {
+ mEventLog.log("getResolvedImsServicePackageName - [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "] -> Exception: " + e);
+ Log.w(TAG, "getResolvedImsServicePackageName: [" + slotId + ", "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "] returned Exception: " + e);
+ return null;
+ }
+ }
+
+ /**
+ * @return the package name for the configured carrier ImsService if it exists on the device and
+ * supports the supplied slotId and featureType. If no such configuration exists, fall back to
+ * the device ImsService. If neither exist, then return {@code null};
+ */
+ // Not synchronized, access on Handler ONLY!
+ private String getConfiguredImsServicePackageNameInternal(int slotId,
+ @ImsFeature.FeatureType int featureType) {
+ // If a carrier ImsService is configured to be used for the provided slotId and
+ // featureType, then return that one.
+ String carrierPackage = getCarrierConfiguredPackageName(slotId, featureType);
+ if (!TextUtils.isEmpty(carrierPackage)
+ && doesCachedImsServiceExist(carrierPackage, slotId, featureType)) {
+ return carrierPackage;
+ }
+ // If there is no carrier ImsService configured for that configuration, then
+ // return the device's default ImsService for the provided slotId and
+ // featureType.
+ String devicePackage = getDeviceConfiguration(featureType);
+ if (!TextUtils.isEmpty(devicePackage)
+ && doesCachedImsServiceExist(devicePackage, slotId, featureType)) {
+ return devicePackage;
+ }
+ // There is no ImsService configuration that exists for the slotId and
+ // featureType.
+ return null;
+ }
+
private void putImsController(int slotId, int feature, ImsServiceController controller) {
if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
|| feature >= ImsFeature.FEATURE_MAX) {
@@ -1006,7 +1140,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
private boolean isDeviceService(ImsServiceInfo info) {
if (info == null) return false;
- return mDeviceServices.containsValue(info.name.getPackageName());
+ synchronized (mDeviceServices) {
+ return mDeviceServices.containsValue(info.name.getPackageName());
+ }
}
private List<Integer> getSlotsForActiveCarrierService(ImsServiceInfo info) {
@@ -1057,7 +1193,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
Log.w(TAG, "bindImsService: error=" + e.getMessage());
}
} else {
- controller = info.controllerFactory.create(mContext, info.name, this);
+ controller = info.controllerFactory.create(mContext, info.name, this, mRepo);
Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
+ " with features: " + features);
controller.bind(features);
@@ -1124,6 +1260,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
* {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureCreated}, which
* removes the ImsServiceController from the mBoundImsServicesByFeature structure.
*/
+ @Override
public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) {
putImsController(slotId, feature, controller);
}
@@ -1133,6 +1270,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
* {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureRemoved}, which
* removes the ImsServiceController from the mBoundImsServicesByFeature structure.
*/
+ @Override
public void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller) {
removeImsController(slotId, feature);
}
@@ -1192,6 +1330,15 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// Possibly rebind to another ImsService for testing carrier ImsServices.
// Called from the handler ONLY
+ private void clearCarrierServiceOverrides(int slotId) {
+ Log.i(TAG, "clearing carrier ImsService overrides");
+ mEventLog.log("clearing carrier ImsService overrides");
+ removeOverridePackageName(slotId);
+ carrierConfigChanged(slotId);
+ }
+
+ // Possibly rebind to another ImsService for testing carrier ImsServices.
+ // Called from the handler ONLY
private void overrideDeviceService(Map<Integer, String> featureMap) {
boolean requiresRecalc = false;
for (Integer featureType : featureMap.keySet()) {
@@ -1271,6 +1418,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
// Carrier config may have not changed, but we still want to kick off a recalculation
// in case there has been a change to the supported device features.
ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
+ Log.i(TAG, "updateBoundServices - carrier package changed: "
+ + oldPackageName + " -> " + newPackageName + " on slot " + slotId
+ + ", hasConfigChanged=" + hasConfigChanged);
mEventLog.log("updateBoundServices - carrier package changed: "
+ oldPackageName + " -> " + newPackageName + " on slot " + slotId
+ ", hasConfigChanged=" + hasConfigChanged);
@@ -1444,9 +1594,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
+ name);
return;
}
+ sanitizeFeatureConfig(features);
mEventLog.log("dynamicQueryComplete: for package " + name + ", features: "
+ printFeatures(service.getSupportedFeatures()) + " -> " + printFeatures(features));
- sanitizeFeatureConfig(features);
// Add features to service
service.replaceFeatures(features);
// Wait until all queries have completed before changing the configuration to reduce churn.
@@ -1460,9 +1610,17 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
/**
- * Ensure the feature includes MMTEL when it supports EMERGENCY_MMTEL, if not, remove.
+ * Sanitize feature configurations from the ImsService.
+ * <ul>
+ * <li> Strip out feature configs for inactive slots.</li>
+ * <li> Ensure the feature includes MMTEL when it supports EMERGENCY_MMTEL, if not, remove.
+ * </li>
+ * </ul>
*/
private void sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ // remove configs for slots that are mot active.
+ features.removeIf(f -> f.slotId >= mNumSlots);
+ // Ensure that if EMERGENCY_MMTEL is defined for a slot, MMTEL is also defined.
Set<ImsFeatureConfiguration.FeatureSlotPair> emergencyMmtelFeatures = features.stream()
.filter(feature -> feature.featureType == ImsFeature.FEATURE_EMERGENCY_MMTEL)
.collect(Collectors.toSet());
@@ -1542,7 +1700,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
serviceIntent,
PackageManager.GET_META_DATA,
- UserHandle.getUserHandleForUid(UserHandle.myUserId()))) {
+ UserHandle.of(UserHandle.myUserId()))) {
ServiceInfo serviceInfo = entry.serviceInfo;
if (serviceInfo != null) {
@@ -1610,8 +1768,10 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
pw.increaseIndent();
pw.println("Device:");
pw.increaseIndent();
- for (Integer i : mDeviceServices.keySet()) {
- pw.println(ImsFeature.FEATURE_LOG_MAP.get(i) + " -> " + mDeviceServices.get(i));
+ synchronized (mDeviceServices) {
+ for (Integer i : mDeviceServices.keySet()) {
+ pw.println(ImsFeature.FEATURE_LOG_MAP.get(i) + " -> " + mDeviceServices.get(i));
+ }
}
pw.decreaseIndent();
pw.println("Carrier: ");
@@ -1631,22 +1791,6 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
pw.decreaseIndent();
pw.decreaseIndent();
- pw.println("Bound Features:");
- pw.increaseIndent();
- for (int i = 0; i < mNumSlots; i++) {
- for (int j = 0; j < MmTelFeature.FEATURE_MAX; j++) {
- pw.print("slot=");
- pw.print(i);
- pw.print(", feature=");
- pw.print(ImsFeature.FEATURE_LOG_MAP.getOrDefault(j, "?"));
- pw.println(": ");
- pw.increaseIndent();
- ImsServiceController c = getImsServiceController(i, j);
- pw.println(c == null ? "none" : c);
- pw.decreaseIndent();
- }
- }
- pw.decreaseIndent();
pw.println("Cached ImsServices:");
pw.increaseIndent();
for (ImsServiceInfo i : mInstalledServicesCache.values()) {
@@ -1662,6 +1806,10 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
pw.decreaseIndent();
}
pw.decreaseIndent();
+ pw.println("Connection Repository Log:");
+ pw.increaseIndent();
+ mRepo.dump(pw);
+ pw.decreaseIndent();
pw.println("Event Log:");
pw.increaseIndent();
mEventLog.dump(pw);
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index bc5ddbeac5..c47d5b05c6 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -20,35 +20,38 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.ChangedPackages;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
+import android.telephony.AnomalyReporter;
import android.telephony.ims.ImsService;
import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.util.LocalLog;
import android.util.Log;
+import com.android.ims.ImsFeatureBinderRepository;
+import com.android.ims.ImsFeatureContainer;
import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.ExponentialBackoff;
import com.android.internal.telephony.util.TelephonyUtils;
import java.io.PrintWriter;
import java.util.HashSet;
-import java.util.Iterator;
+import java.util.List;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
@@ -67,13 +70,19 @@ import java.util.stream.Collectors;
* called for each feature that is created/removed.
*/
public class ImsServiceController {
-
+ private final UUID mAnomalyUUID = UUID.fromString("e93b05e4-6d0a-4755-a6da-a2d2dbfb10d6");
+ private int mLastSequenceNumber = 0;
+ private ChangedPackages mChangedPackages;
+ private PackageManager mPackageManager;
class ImsServiceConnection implements ServiceConnection {
+ // Track the status of whether or not the Service has died in case we need to permanently
+ // unbind (see onNullBinding below).
+ private boolean mIsServiceConnectionDead = false;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- mBackoff.stop();
synchronized (mLock) {
+ mBackoff.stop();
mIsBound = true;
mIsBinding = false;
try {
@@ -82,15 +91,20 @@ public class ImsServiceController {
+ service);
setServiceController(service);
notifyImsServiceReady();
+ retrieveStaticImsServiceCapabilities();
// create all associated features in the ImsService
for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
- addImsServiceFeature(i);
+ long caps = modifyCapabiltiesForSlot(mImsFeatures, i.slotId,
+ mServiceCapabilities);
+ addImsServiceFeature(i, caps);
}
} catch (RemoteException e) {
mIsBound = false;
mIsBinding = false;
- // Remote exception means that the binder already died.
+ // RemoteException means that the process holding the binder died or something
+ // unexpected happened... try a full rebind.
cleanupConnection();
+ unbindService();
startDelayedRebindToService();
mLocalLog.log("onConnected exception=" + e.getMessage() + ", retry in "
+ mBackoff.getCurrentDelay() + " mS");
@@ -104,48 +118,56 @@ public class ImsServiceController {
public void onServiceDisconnected(ComponentName name) {
synchronized (mLock) {
mIsBinding = false;
+ cleanupConnection();
}
- cleanupConnection();
mLocalLog.log("onServiceDisconnected");
Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting...");
// Service disconnected, but we are still technically bound. Waiting for reconnect.
+ checkAndReportAnomaly(name);
}
@Override
public void onBindingDied(ComponentName name) {
+ mIsServiceConnectionDead = true;
synchronized (mLock) {
mIsBinding = false;
mIsBound = false;
- }
- if (mImsServiceConnection != null) {
// according to the docs, we should fully unbind before rebinding again.
- mContext.unbindService(mImsServiceConnection);
+ cleanupConnection();
+ unbindService();
+ startDelayedRebindToService();
}
- cleanupConnection();
Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind...");
- startDelayedRebindToService();
mLocalLog.log("onBindingDied, retrying in " + mBackoff.getCurrentDelay() + " mS");
}
@Override
public void onNullBinding(ComponentName name) {
- Log.w(LOG_TAG, "ImsService(" + name + "): onNullBinding. Removing.");
- mLocalLog.log("onNullBinding");
+ Log.w(LOG_TAG, "ImsService(" + name + "): onNullBinding. Is service dead = "
+ + mIsServiceConnectionDead);
+ mLocalLog.log("onNullBinding, is service dead = " + mIsServiceConnectionDead);
+ // onNullBinding will happen after onBindingDied. In this case, we should not
+ // permanently unbind and instead let the automatic rebind occur.
+ if (mIsServiceConnectionDead) return;
synchronized (mLock) {
mIsBinding = false;
- mIsBound = false;
+ // Service connection exists, so we are bound but the binder is null. Wait for
+ // ImsResolver to trigger the unbind here.
+ mIsBound = true;
+ cleanupConnection();
}
- cleanupConnection();
if (mCallbacks != null) {
// Will trigger an unbind.
mCallbacks.imsServiceBindPermanentError(getComponentName());
}
}
- // Does not clear features, just removes all active features.
+ // Does not clear feature configuration, just cleans up the active callbacks and
+ // invalidates remote FeatureConnections.
+ // This should only be called when locked
private void cleanupConnection() {
cleanupAllFeatures();
- cleanUpService();
+ setServiceController(null);
}
}
@@ -199,9 +221,13 @@ public class ImsServiceController {
private static final int REBIND_START_DELAY_MS = 2 * 1000; // 2 seconds
private static final int REBIND_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
private static final long CHANGE_PERMISSION_TIMEOUT_MS = 15 * 1000; // 15 seconds
+ // Enforce ImsService has both MMTEL and RCS supported in order to enable SIP transport API.
+ // Enable ImsServiceControllerTest and SipDelegateManagerTest cases if this is re-enabled.
+ private static final boolean ENFORCE_SINGLE_SERVICE_FOR_SIP_TRANSPORT = false;
private final ComponentName mComponentName;
private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
- private final PermissionManager mPermissionManager;
+ private final LegacyPermissionManager mPermissionManager;
+ private ImsFeatureBinderRepository mRepo;
private ImsServiceControllerCallbacks mCallbacks;
private ExponentialBackoff mBackoff;
@@ -209,11 +235,10 @@ public class ImsServiceController {
private boolean mIsBinding = false;
// Set of a pair of slotId->feature
private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
- // Binder interfaces to the features set in mImsFeatures;
- private HashSet<ImsFeatureContainer> mImsFeatureBinders = new HashSet<>();
private IImsServiceController mIImsServiceController;
+ // The Capabilities bitmask of the connected ImsService (see ImsService#ImsServiceCapability).
+ private long mServiceCapabilities;
private ImsServiceConnection mImsServiceConnection;
- private Set<IImsServiceFeatureCallback> mImsStatusCallbacks = ConcurrentHashMap.newKeySet();
// Only added or removed, never accessed on purpose.
private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>();
private final LocalLog mLocalLog = new LocalLog(10);
@@ -232,43 +257,6 @@ public class ImsServiceController {
}
};
- private class ImsFeatureContainer {
- public int slotId;
- public int featureType;
- private IInterface mBinder;
-
- ImsFeatureContainer(int slotId, int featureType, IInterface binder) {
- this.slotId = slotId;
- this.featureType = featureType;
- this.mBinder = binder;
- }
-
- // Casts the IInterface into the binder class we are looking for.
- public <T extends IInterface> T resolve(Class<T> className) {
- return className.cast(mBinder);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- ImsFeatureContainer that = (ImsFeatureContainer) o;
-
- if (slotId != that.slotId) return false;
- if (featureType != that.featureType) return false;
- return mBinder != null ? mBinder.equals(that.mBinder) : that.mBinder == null;
- }
-
- @Override
- public int hashCode() {
- int result = slotId;
- result = 31 * result + featureType;
- result = 31 * result + (mBinder != null ? mBinder.hashCode() : 0);
- return result;
- }
- }
-
/**
* Container class for the IImsFeatureStatusCallback callback implementation. This class is
* never used directly, but we need to keep track of the IImsFeatureStatusCallback
@@ -285,7 +273,7 @@ public class ImsServiceController {
Log.i(LOG_TAG, "notifyImsFeatureStatus: slot=" + mSlotId + ", feature="
+ ImsFeature.FEATURE_LOG_MAP.get(mFeatureType) + ", status="
+ ImsFeature.STATE_LOG_MAP.get(featureStatus));
- sendImsFeatureStatusChanged(mSlotId, mFeatureType, featureStatus);
+ mRepo.notifyFeatureStateChanged(mSlotId, mFeatureType, featureStatus);
}
};
@@ -325,7 +313,7 @@ public class ImsServiceController {
};
public ImsServiceController(Context context, ComponentName componentName,
- ImsServiceControllerCallbacks callbacks) {
+ ImsServiceControllerCallbacks callbacks, ImsFeatureBinderRepository repo) {
mContext = context;
mComponentName = componentName;
mCallbacks = callbacks;
@@ -336,15 +324,25 @@ public class ImsServiceController {
2, /* multiplier */
mHandlerThread.getLooper(),
mRestartImsServiceRunnable);
- mPermissionManager =
- (PermissionManager) mContext.getSystemService(Context.PERMISSION_SERVICE);
+ mPermissionManager = (LegacyPermissionManager) mContext.getSystemService(
+ Context.LEGACY_PERMISSION_SERVICE);
+ mRepo = repo;
+
+ mPackageManager = mContext.getPackageManager();
+ if (mPackageManager != null) {
+ mChangedPackages = mPackageManager.getChangedPackages(mLastSequenceNumber);
+ if (mChangedPackages != null) {
+ mLastSequenceNumber = mChangedPackages.getSequenceNumber();
+ }
+ }
}
@VisibleForTesting
// Creating a new HandlerThread and background handler for each test causes a segfault, so for
// testing, use a handler supplied by the testing system.
public ImsServiceController(Context context, ComponentName componentName,
- ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry) {
+ ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry,
+ ImsFeatureBinderRepository repo) {
mContext = context;
mComponentName = componentName;
mCallbacks = callbacks;
@@ -355,6 +353,7 @@ public class ImsServiceController {
handler,
mRestartImsServiceRunnable);
mPermissionManager = null;
+ mRepo = repo;
}
/**
@@ -427,18 +426,12 @@ public class ImsServiceController {
public void unbind() throws RemoteException {
synchronized (mLock) {
mBackoff.stop();
- if (mImsServiceConnection == null) {
- return;
- }
// Clean up all features
changeImsServiceFeatures(new HashSet<>());
- removeImsServiceFeatureCallbacks();
- Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
- mLocalLog.log("unbinding");
- mContext.unbindService(mImsServiceConnection);
mIsBound = false;
mIsBinding = false;
- cleanUpService();
+ setServiceController(null);
+ unbindService();
}
}
@@ -467,7 +460,9 @@ public class ImsServiceController {
new HashSet<>(mImsFeatures);
newFeatures.removeAll(oldImsFeatures);
for (ImsFeatureConfiguration.FeatureSlotPair i : newFeatures) {
- addImsServiceFeature(i);
+ long caps = modifyCapabiltiesForSlot(mImsFeatures, i.slotId,
+ mServiceCapabilities);
+ addImsServiceFeature(i, caps);
}
// remove old features
HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldFeatures =
@@ -476,6 +471,16 @@ public class ImsServiceController {
for (ImsFeatureConfiguration.FeatureSlotPair i : oldFeatures) {
removeImsServiceFeature(i);
}
+ // ensure the capabilities have been updated for unchanged features.
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> unchangedFeatures =
+ new HashSet<>(mImsFeatures);
+ unchangedFeatures.removeAll(oldFeatures);
+ unchangedFeatures.removeAll(newFeatures);
+ for (ImsFeatureConfiguration.FeatureSlotPair p : unchangedFeatures) {
+ long caps = modifyCapabiltiesForSlot(mImsFeatures, p.slotId,
+ mServiceCapabilities);
+ mRepo.notifyFeatureCapabilitiesChanged(p.slotId, p.featureType, caps);
+ }
}
}
}
@@ -499,35 +504,6 @@ public class ImsServiceController {
return mComponentName;
}
- /**
- * Add a callback to ImsManager that signals a new feature that the ImsServiceProxy can handle.
- */
- public void addImsServiceFeatureCallback(IImsServiceFeatureCallback callback) {
- mImsStatusCallbacks.add(callback);
- Set<ImsFeatureConfiguration.FeatureSlotPair> features;
- synchronized (mLock) {
- if (mImsFeatures == null || mImsFeatures.isEmpty()) {
- return;
- }
- features = new HashSet<>(mImsFeatures);
- }
- // notify the new status callback of the features that are available.
- try {
- for (ImsFeatureConfiguration.FeatureSlotPair i : features) {
- callback.imsFeatureCreated(i.slotId, i.featureType);
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "addImsServiceFeatureCallback: exception notifying callback");
- }
- }
-
- /**
- * Removes a previously registered callback if it was associated with this feature.
- */
- public void removeImsServiceFeatureCallback(IImsServiceFeatureCallback callback) {
- mImsStatusCallbacks.remove(callback);
- }
-
public void enableIms(int slotId) {
try {
synchronized (mLock) {
@@ -553,50 +529,38 @@ public class ImsServiceController {
}
/**
- * Return the {@Link MMTelFeature} binder on the slot associated with the slotId.
- * Used for normal calling.
+ * @return the IImsRegistration that corresponds to the slot id specified.
*/
- public IImsMmTelFeature getMmTelFeature(int slotId) {
+ public IImsRegistration getRegistration(int slotId) throws RemoteException {
synchronized (mLock) {
- ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_MMTEL);
- if (f == null) {
- Log.w(LOG_TAG, "Requested null MMTelFeature on slot " + slotId);
- return null;
- }
- return f.resolve(IImsMmTelFeature.class);
+ return isServiceControllerAvailable()
+ ? mIImsServiceController.getRegistration(slotId) : null;
}
}
/**
- * Return the {@Link RcsFeature} binder on the slot associated with the slotId.
+ * @return the IImsConfig that corresponds to the slot id specified.
*/
- public IImsRcsFeature getRcsFeature(int slotId) {
+ public IImsConfig getConfig(int slotId) throws RemoteException {
synchronized (mLock) {
- ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_RCS);
- if (f == null) {
- Log.w(LOG_TAG, "Requested null RcsFeature on slot " + slotId);
- return null;
- }
- return f.resolve(IImsRcsFeature.class);
+ return isServiceControllerAvailable() ? mIImsServiceController.getConfig(slotId) : null;
}
}
/**
- * @return the IImsRegistration that corresponds to the slot id specified.
+ * @return the ISipTransport instance associated with the requested slot ID.
*/
- public IImsRegistration getRegistration(int slotId) throws RemoteException {
+ public ISipTransport getSipTransport(int slotId) throws RemoteException {
synchronized (mLock) {
return isServiceControllerAvailable()
- ? mIImsServiceController.getRegistration(slotId) : null;
+ ? mIImsServiceController.getSipTransport(slotId) : null;
}
}
- /**
- * @return the IImsConfig that corresponds to the slot id specified.
- */
- public IImsConfig getConfig(int slotId) throws RemoteException {
+ protected long getStaticServiceCapabilities() throws RemoteException {
synchronized (mLock) {
- return isServiceControllerAvailable() ? mIImsServiceController.getConfig(slotId) : null;
+ return isServiceControllerAvailable()
+ ? mIImsServiceController.getImsServiceCapabilities() : 0L;
}
}
@@ -613,6 +577,17 @@ public class ImsServiceController {
}
}
+ private void retrieveStaticImsServiceCapabilities() throws RemoteException {
+ long caps = getStaticServiceCapabilities();
+ Log.i(LOG_TAG, "retrieveStaticImsServiceCapabilities: "
+ + ImsService.getCapabilitiesString(caps));
+ mLocalLog.log("retrieveStaticImsServiceCapabilities: "
+ + ImsService.getCapabilitiesString(caps));
+ synchronized (mLock) {
+ mServiceCapabilities = caps;
+ }
+ }
+
protected String getServiceInterface() {
return ImsService.SERVICE_INTERFACE;
}
@@ -626,15 +601,6 @@ public class ImsServiceController {
}
/**
- * @return true if the controller is currently bound.
- */
- public boolean isBound() {
- synchronized (mLock) {
- return mIsBound;
- }
- }
-
- /**
* Check to see if the service controller is available, overridden for compat versions,
* @return true if available, false otherwise;
*/
@@ -642,16 +608,57 @@ public class ImsServiceController {
return mIImsServiceController != null;
}
- @VisibleForTesting
- public void removeImsServiceFeatureCallbacks() {
- mImsStatusCallbacks.clear();
- }
-
// Only add a new rebind if there are no pending rebinds waiting.
private void startDelayedRebindToService() {
mBackoff.start();
}
+ private void unbindService() {
+ synchronized (mLock) {
+ if (mImsServiceConnection != null) {
+ Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
+ mLocalLog.log("unbinding: " + mComponentName);
+ mContext.unbindService(mImsServiceConnection);
+ mImsServiceConnection = null;
+ } else {
+ Log.i(LOG_TAG, "unbindService called on already unbound ImsService: "
+ + mComponentName);
+ mLocalLog.log("Note: unbindService called with no ServiceConnection on "
+ + mComponentName);
+ }
+ }
+ }
+
+ /**
+ * Modify the capabilities returned by the ImsService based on the state of this controller:
+ * - CAPABILITY_EMERGENCY_OVER_MMTEL should only be set if features contains
+ * FEATURE_EMERGENCY_MMTEL (This is not set by the ImsService itself).
+ * - CAPABILITY_SIP_DELEGATE_CREATION should only be set in the case that this ImsService is
+ * handling both MMTEL and RCS features for this slot.
+ */
+ private long modifyCapabiltiesForSlot(
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features, int slotId, long serviceCaps) {
+ long caps = serviceCaps;
+ List<Integer> featureTypes = getFeaturesForSlot(slotId, features);
+ if (featureTypes.contains(ImsFeature.FEATURE_EMERGENCY_MMTEL)) {
+ // We only consider MMTEL_EMERGENCY as a capability here, so set the capability if
+ // the ImsService has declared it.
+ caps |= ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL;
+ }
+
+ if (ENFORCE_SINGLE_SERVICE_FOR_SIP_TRANSPORT) {
+ if (!featureTypes.contains(ImsFeature.FEATURE_MMTEL)
+ || !featureTypes.contains(ImsFeature.FEATURE_RCS)) {
+ // Only allow SipDelegate creation if this ImsService is providing both MMTEL and
+ // RCS features.
+ caps &= ~(ImsService.CAPABILITY_SIP_DELEGATE_CREATION);
+ }
+ } else {
+ Log.i(LOG_TAG, "skipping single service enforce check...");
+ }
+ return caps;
+ }
+
// Grant runtime permissions to ImsService. PermissionManager ensures that the ImsService is
// system/signed before granting permissions.
private void grantPermissionsToService() {
@@ -677,74 +684,24 @@ public class ImsServiceController {
}
}
- private void sendImsFeatureCreatedCallback(int slot, int feature) {
- for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureCallback callbacks = i.next();
- try {
- callbacks.imsFeatureCreated(slot, feature);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureCreatedCallback: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
- }
- }
-
- private void sendImsFeatureRemovedCallback(int slot, int feature) {
- for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureCallback callbacks = i.next();
- try {
- callbacks.imsFeatureRemoved(slot, feature);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureRemovedCallback: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
- }
- }
-
- private void sendImsFeatureStatusChanged(int slot, int feature, int status) {
- for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator();
- i.hasNext(); ) {
- IImsServiceFeatureCallback callbacks = i.next();
- try {
- callbacks.imsStatusChanged(slot, feature, status);
- } catch (RemoteException e) {
- // binder died, remove callback.
- Log.w(LOG_TAG, "sendImsFeatureStatusChanged: Binder died, removing "
- + "callback. Exception:" + e.getMessage());
- i.remove();
- }
- }
- }
-
// This method should only be called when synchronized on mLock
- private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair)
+ private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair,
+ long capabilities)
throws RemoteException {
if (!isServiceControllerAvailable() || mCallbacks == null) {
Log.w(LOG_TAG, "addImsServiceFeature called with null values.");
return;
}
if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
- ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.slotId,
- featurePair.featureType);
- mFeatureStatusCallbacks.add(c);
- IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType,
- c.getCallback());
- addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f);
+ IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType);
+ addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f, capabilities);
+ addImsFeatureStatusCallback(featurePair.slotId, featurePair.featureType);
} else {
// Don't update ImsService for emergency MMTEL feature.
Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
}
// Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
- // Send callback to ImsServiceProxy to change supported ImsFeatures including emergency
- // MMTEL state.
- sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType);
}
// This method should only be called when synchronized on mLock
@@ -756,17 +713,10 @@ public class ImsServiceController {
// Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this);
if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
- ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
- c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType)
- .findFirst().orElse(null);
- // Remove status callbacks from list.
- if (callbackToRemove != null) {
- mFeatureStatusCallbacks.remove(callbackToRemove);
- }
+ removeImsFeatureStatusCallback(featurePair.slotId, featurePair.featureType);
removeImsFeatureBinder(featurePair.slotId, featurePair.featureType);
try {
- removeImsFeature(featurePair.slotId, featurePair.featureType,
- (callbackToRemove != null ? callbackToRemove.getCallback() : null));
+ removeImsFeature(featurePair.slotId, featurePair.featureType);
} catch (RemoteException e) {
// The connection to this ImsService doesn't exist. This may happen if the service
// has died and we are removing features.
@@ -778,54 +728,105 @@ public class ImsServiceController {
// Don't update ImsService for emergency MMTEL feature.
Log.i(LOG_TAG, "doesn't support emergency calling on slot " + featurePair.slotId);
}
- // Send callback to ImsServiceProxy to change supported ImsFeatures
- // Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an
- // ImsManager requests the ImsService while it is being removed in ImsResolver, this
- // callback will clean it up after.
- sendImsFeatureRemovedCallback(featurePair.slotId, featurePair.featureType);
}
// This method should only be called when already synchronized on mLock.
// overridden by compat layer to create features
- protected IInterface createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ protected IInterface createImsFeature(int slotId, int featureType)
throws RemoteException {
switch (featureType) {
case ImsFeature.FEATURE_MMTEL: {
- return mIImsServiceController.createMmTelFeature(slotId, c);
+ return mIImsServiceController.createMmTelFeature(slotId);
}
case ImsFeature.FEATURE_RCS: {
- return mIImsServiceController.createRcsFeature(slotId, c);
+ return mIImsServiceController.createRcsFeature(slotId);
}
default:
return null;
}
}
+ // This method should only be called when already synchronized on mLock.
+ private void addImsFeatureStatusCallback(int slotId, int featureType) throws RemoteException {
+ ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(slotId, featureType);
+ mFeatureStatusCallbacks.add(c);
+ registerImsFeatureStatusCallback(slotId, featureType, c.getCallback());
+ }
+
+ // This method should only be called when already synchronized on mLock.
+ private void removeImsFeatureStatusCallback(int slotId, int featureType) {
+ ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
+ c.mSlotId == slotId && c.mFeatureType == featureType).findFirst().orElse(null);
+ // Remove status callbacks from list.
+ if (callbackToRemove != null) {
+ mFeatureStatusCallbacks.remove(callbackToRemove);
+ unregisterImsFeatureStatusCallback(slotId, featureType, callbackToRemove.getCallback());
+ }
+ }
+
+ // overridden by compat layer to register feature status callbacks
+ protected void registerImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) throws RemoteException {
+ mIImsServiceController.addFeatureStatusCallback(slotId, featureType, c);
+ }
+
+ // overridden by compat layer to deregister feature status callbacks
+ protected void unregisterImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ try {
+ mIImsServiceController.removeFeatureStatusCallback(slotId, featureType, c);
+ } catch (RemoteException e) {
+ mLocalLog.log("unregisterImsFeatureStatusCallback - couldn't remove " + c);
+ }
+ }
+
+
// overridden by compat layer to remove features
- protected void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ protected void removeImsFeature(int slotId, int featureType)
throws RemoteException {
- mIImsServiceController.removeImsFeature(slotId, featureType, c);
+ mIImsServiceController.removeImsFeature(slotId, featureType);
}
- // This method should only be called when synchronized on mLock
- private void addImsFeatureBinder(int slotId, int featureType, IInterface b) {
- mImsFeatureBinders.add(new ImsFeatureContainer(slotId, featureType, b));
+ private void addImsFeatureBinder(int slotId, int featureType, IInterface b, long capabilities)
+ throws RemoteException {
+ if (b == null) {
+
+ Log.w(LOG_TAG, "addImsFeatureBinder: null IInterface reported for "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType));
+ mLocalLog.log("addImsFeatureBinder: null IInterface reported for "
+ + ImsFeature.FEATURE_LOG_MAP.get(featureType));
+ return;
+ }
+ ImsFeatureContainer fc = createFeatureContainer(slotId, b.asBinder(), capabilities);
+ mRepo.addConnection(slotId, featureType, fc);
}
- // This method should only be called when synchronized on mLock
private void removeImsFeatureBinder(int slotId, int featureType) {
- ImsFeatureContainer container = mImsFeatureBinders.stream()
- .filter(f-> (f.slotId == slotId && f.featureType == featureType))
- .findFirst().orElse(null);
- if (container != null) {
- mImsFeatureBinders.remove(container);
+ mRepo.removeConnection(slotId, featureType);
+ }
+
+ private ImsFeatureContainer createFeatureContainer(int slotId, IBinder b, long capabilities)
+ throws RemoteException {
+ IImsConfig config = getConfig(slotId);
+ IImsRegistration reg = getRegistration(slotId);
+ // When either is null, this is an unexpected condition. Do not report the ImsService as
+ // being available.
+ if (config == null || reg == null) {
+ Log.w(LOG_TAG, "createFeatureContainer: invalid state. Reporting as not "
+ + "available. componentName= " + getComponentName());
+ mLocalLog.log("createFeatureContainer: invalid state. Reporting as not "
+ + "available.");
+ return null;
}
+ // SipTransport AIDL may be null for older devices, this is expected.
+ ISipTransport transport = getSipTransport(slotId);
+ return new ImsFeatureContainer(b, config, reg, transport, capabilities);
}
- private ImsFeatureContainer getImsFeatureContainer(int slotId, int featureType) {
- return mImsFeatureBinders.stream()
- .filter(f-> (f.slotId == slotId && f.featureType == featureType))
- .findFirst().orElse(null);
+ private List<Integer> getFeaturesForSlot(int slotId,
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+ return features.stream().filter(f -> f.slotId == slotId).map(f -> f.featureType)
+ .collect(Collectors.toList());
}
private void cleanupAllFeatures() {
@@ -834,17 +835,26 @@ public class ImsServiceController {
for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
removeImsServiceFeature(i);
}
- // remove all MmTelFeatureConnection callbacks, since we have already sent removed
- // callback.
- removeImsServiceFeatureCallbacks();
}
}
- private void cleanUpService() {
- synchronized (mLock) {
- mImsServiceConnection = null;
- setServiceController(null);
+ private void checkAndReportAnomaly(ComponentName name) {
+ if (mPackageManager == null) {
+ Log.w(LOG_TAG, "mPackageManager null");
+ return;
+ }
+ ChangedPackages curChangedPackages =
+ mPackageManager.getChangedPackages(mLastSequenceNumber);
+ if (curChangedPackages != null) {
+ mLastSequenceNumber = curChangedPackages.getSequenceNumber();
+ List<String> packagesNames = curChangedPackages.getPackageNames();
+ if (packagesNames.contains(name.getPackageName())) {
+ Log.d(LOG_TAG, "Ignore due to updated, package: " + name.getPackageName());
+ return;
+ }
}
+ String message = "IMS Service Crashed";
+ AnomalyReporter.reportAnomaly(mAnomalyUUID, message);
}
@Override
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
index 835d7807d0..8ba390b898 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
@@ -25,12 +25,14 @@ import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.compat.ImsService;
import android.telephony.ims.compat.feature.ImsFeature;
import android.telephony.ims.compat.feature.MMTelFeature;
import android.util.Log;
import android.util.SparseArray;
+import com.android.ims.ImsFeatureBinderRepository;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsServiceController;
@@ -56,8 +58,9 @@ public class ImsServiceControllerCompat extends ImsServiceController {
new SparseArray<>();
public ImsServiceControllerCompat(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
- super(context, componentName, callbacks);
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository repo) {
+ super(context, componentName, callbacks, repo);
}
@Override
@@ -126,6 +129,21 @@ public class ImsServiceControllerCompat extends ImsServiceController {
return adapter.getIImsConfig();
}
+ /**
+ * Return the SIP transport interface, which is not supported on the compat version of
+ * ImsService, so return null.
+ */
+ @Override
+ public ISipTransport getSipTransport(int slotId) {
+ return null;
+ }
+
+ @Override
+ protected long getStaticServiceCapabilities() {
+ // Older implementations do not support optional static capabilities
+ return 0L;
+ }
+
@Override
protected final void notifyImsServiceReady() {
Log.d(TAG, "notifyImsServiceReady");
@@ -133,15 +151,15 @@ public class ImsServiceControllerCompat extends ImsServiceController {
}
@Override
- protected final IInterface createImsFeature(int slotId, int featureType,
- IImsFeatureStatusCallback c)
+ protected final IInterface createImsFeature(int slotId, int featureType)
throws RemoteException {
switch (featureType) {
case ImsFeature.MMTEL: {
- return createMMTelCompat(slotId, c);
+ return createMMTelCompat(slotId);
}
case ImsFeature.RCS: {
- return createRcsFeature(slotId, c);
+
+ return createRcsFeature(slotId);
}
default:
return null;
@@ -149,7 +167,23 @@ public class ImsServiceControllerCompat extends ImsServiceController {
}
@Override
- protected final void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+ protected void registerImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) throws RemoteException {
+ mServiceController.addFeatureStatusCallback(slotId, featureType, c);
+ }
+
+ @Override
+ protected void unregisterImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ try {
+ mServiceController.removeFeatureStatusCallback(slotId, featureType, c);
+ } catch (RemoteException e) {
+ Log.w(TAG, "compat - unregisterImsFeatureStatusCallback - couldn't remove " + c);
+ }
+ }
+
+ @Override
+ protected final void removeImsFeature(int slotId, int featureType)
throws RemoteException {
if (featureType == ImsFeature.MMTEL) {
mMmTelCompatAdapters.remove(slotId);
@@ -157,7 +191,7 @@ public class ImsServiceControllerCompat extends ImsServiceController {
mConfigCompatAdapters.remove(slotId);
}
if (mServiceController != null) {
- mServiceController.removeImsFeature(slotId, featureType, c);
+ mServiceController.removeImsFeature(slotId, featureType);
}
}
@@ -171,9 +205,9 @@ public class ImsServiceControllerCompat extends ImsServiceController {
return mServiceController != null;
}
- private MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c)
+ private MmTelInterfaceAdapter getInterface(int slotId)
throws RemoteException {
- IImsMMTelFeature feature = mServiceController.createMMTelFeature(slotId, c);
+ IImsMMTelFeature feature = mServiceController.createMMTelFeature(slotId);
if (feature == null) {
Log.w(TAG, "createMMTelCompat: createMMTelFeature returned null.");
return null;
@@ -181,9 +215,9 @@ public class ImsServiceControllerCompat extends ImsServiceController {
return new MmTelInterfaceAdapter(slotId, feature.asBinder());
}
- private IImsMmTelFeature createMMTelCompat(int slotId, IImsFeatureStatusCallback c)
+ private IImsMmTelFeature createMMTelCompat(int slotId)
throws RemoteException {
- MmTelInterfaceAdapter interfaceAdapter = getInterface(slotId, c);
+ MmTelInterfaceAdapter interfaceAdapter = getInterface(slotId);
MmTelFeatureCompatAdapter mmTelAdapter = new MmTelFeatureCompatAdapter(mContext, slotId,
interfaceAdapter);
mMmTelCompatAdapters.put(slotId, mmTelAdapter);
@@ -195,7 +229,7 @@ public class ImsServiceControllerCompat extends ImsServiceController {
return mmTelAdapter.getBinder();
}
- private IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+ private IImsRcsFeature createRcsFeature(int slotId) {
// Return non-null if there is a custom RCS implementation that needs a compatability layer.
return null;
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
index 638b76f71e..564cdcc8dd 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
@@ -43,6 +43,10 @@ public class ImsServiceFeatureQueryManager {
private final ComponentName mName;
private final String mIntentFilter;
+ // Track the status of whether or not the Service has died in case we need to permanently
+ // unbind (see onNullBinding below).
+ private boolean mIsServiceConnectionDead = false;
+
ImsServiceFeatureQuery(ComponentName name, String intentFilter) {
mName = name;
@@ -85,6 +89,7 @@ public class ImsServiceFeatureQueryManager {
@Override
public void onBindingDied(ComponentName name) {
+ mIsServiceConnectionDead = true;
Log.w(LOG_TAG, "onBindingDied: " + name);
cleanup();
// retry again!
@@ -94,6 +99,9 @@ public class ImsServiceFeatureQueryManager {
@Override
public void onNullBinding(ComponentName name) {
Log.w(LOG_TAG, "onNullBinding: " + name);
+ // onNullBinding will happen after onBindingDied. In this case, we should not
+ // permanently unbind and instead let the automatic rebind occur.
+ if (mIsServiceConnectionDead) return;
cleanup();
mListener.onPermanentError(name);
}
diff --git a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
index 7b0619bb9d..d9dfd79549 100644
--- a/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
+++ b/src/java/com/android/internal/telephony/ims/MmTelFeatureCompatAdapter.java
@@ -61,10 +61,14 @@ public class MmTelFeatureCompatAdapter extends MmTelFeature {
private static final Map<Integer, Integer> REG_TECH_TO_NET_TYPE = new HashMap<>(2);
static {
+ REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_NR,
+ TelephonyManager.NETWORK_TYPE_NR);
REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
TelephonyManager.NETWORK_TYPE_LTE);
REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
TelephonyManager.NETWORK_TYPE_IWLAN);
+ REG_TECH_TO_NET_TYPE.put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
+ TelephonyManager.NETWORK_TYPE_IWLAN);
}
// Feature Type for compatibility with old "feature" updates
@@ -516,7 +520,8 @@ public class MmTelFeatureCompatAdapter extends MmTelFeature {
Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
intent.setPackage(TelephonyManager.PHONE_PROCESS_NAME);
return PendingIntent.getBroadcast(mContext, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ // Mutable because information associated with the call is passed back here.
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
}
private int convertCapability(int capability, int radioTech) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
index 8b5b4a22a9..57a7674168 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalCall.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.imsphone;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.telephony.ims.ImsExternalCallState;
import com.android.internal.telephony.Call;
@@ -31,7 +32,7 @@ public class ImsExternalCall extends Call {
private Phone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsExternalCall(Phone phone, ImsExternalConnection connection) {
mPhone = phone;
addConnection(connection);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
index 414cbf83f7..59ac1f61d1 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsExternalConnection.java
@@ -19,6 +19,7 @@ package com.android.internal.telephony.imsphone;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.net.Uri;
+import android.os.Build;
import android.telecom.PhoneAccount;
import android.telephony.PhoneNumberUtils;
import android.telephony.ims.ImsExternalCallState;
@@ -201,7 +202,7 @@ public class ImsExternalConnection extends Connection {
/**
* Sets this external call as active.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setActive() {
if (mCall == null) {
return;
@@ -282,7 +283,7 @@ public class ImsExternalConnection extends Connection {
/**
* Rebuilds the connection capabilities.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void rebuildCapabilities() {
int capabilities = Capability.IS_EXTERNAL_CONNECTION;
if (mIsPullable) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index b7eb8af960..028f3c2500 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -49,8 +49,10 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -61,6 +63,7 @@ import android.os.Registrant;
import android.os.RegistrantList;
import android.os.ResultReceiver;
import android.os.UserHandle;
+import android.preference.PreferenceManager;
import android.sysprop.TelephonyProperties;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
@@ -70,6 +73,7 @@ import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UssdResponse;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallForwardInfo;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsReasonInfo;
@@ -104,8 +108,8 @@ import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
-import com.android.internal.telephony.gsm.GsmMmiCode;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.metrics.ImsStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
@@ -139,6 +143,7 @@ public class ImsPhone extends ImsPhoneBase {
public static final int EVENT_SERVICE_STATE_CHANGED = EVENT_LAST + 8;
private static final int EVENT_VOICE_CALL_ENDED = EVENT_LAST + 9;
private static final int EVENT_INITIATE_VOLTE_SILENT_REDIAL = EVENT_LAST + 10;
+ private static final int EVENT_GET_CLIP_DONE = EVENT_LAST + 11;
static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
@@ -146,29 +151,47 @@ public class ImsPhone extends ImsPhoneBase {
// Default Emergency Callback Mode exit timer
private static final long DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
+ // String to Call Composer Option Prefix set by user
+ private static final String PREF_USER_SET_CALL_COMPOSER_PREFIX = "userset_callcomposer_prefix";
+
+ /**
+ * Used to create ImsManager instances, which may be injected during testing.
+ */
+ @VisibleForTesting
+ public interface ImsManagerFactory {
+ /**
+ * Create a new instance of ImsManager for the specified phoneId.
+ */
+ ImsManager create(Context context, int phoneId);
+ }
+
public static class ImsDialArgs extends DialArgs {
public static class Builder extends DialArgs.Builder<ImsDialArgs.Builder> {
private android.telecom.Connection.RttTextStream mRttTextStream;
- private int mClirMode = CommandsInterface.CLIR_DEFAULT;
private int mRetryCallFailCause = ImsReasonInfo.CODE_UNSPECIFIED;
private int mRetryCallFailNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private boolean mIsWpsCall = false;
public static ImsDialArgs.Builder from(DialArgs dialArgs) {
+ if (dialArgs instanceof ImsDialArgs) {
+ return new ImsDialArgs.Builder()
+ .setUusInfo(dialArgs.uusInfo)
+ .setIsEmergency(dialArgs.isEmergency)
+ .setVideoState(dialArgs.videoState)
+ .setIntentExtras(dialArgs.intentExtras)
+ .setRttTextStream(((ImsDialArgs)dialArgs).rttTextStream)
+ .setClirMode(dialArgs.clirMode)
+ .setRetryCallFailCause(((ImsDialArgs)dialArgs).retryCallFailCause)
+ .setRetryCallFailNetworkType(
+ ((ImsDialArgs)dialArgs).retryCallFailNetworkType)
+ .setIsWpsCall(((ImsDialArgs)dialArgs).isWpsCall);
+ }
return new ImsDialArgs.Builder()
.setUusInfo(dialArgs.uusInfo)
+ .setIsEmergency(dialArgs.isEmergency)
.setVideoState(dialArgs.videoState)
- .setIntentExtras(dialArgs.intentExtras);
- }
-
- public static ImsDialArgs.Builder from(ImsDialArgs dialArgs) {
- return new ImsDialArgs.Builder()
- .setUusInfo(dialArgs.uusInfo)
- .setVideoState(dialArgs.videoState)
- .setIntentExtras(dialArgs.intentExtras)
- .setRttTextStream(dialArgs.rttTextStream)
.setClirMode(dialArgs.clirMode)
- .setRetryCallFailCause(dialArgs.retryCallFailCause)
- .setRetryCallFailNetworkType(dialArgs.retryCallFailNetworkType);
+ .setIntentExtras(dialArgs.intentExtras);
}
public ImsDialArgs.Builder setRttTextStream(
@@ -177,11 +200,6 @@ public class ImsPhone extends ImsPhoneBase {
return this;
}
- public ImsDialArgs.Builder setClirMode(int clirMode) {
- this.mClirMode = clirMode;
- return this;
- }
-
public ImsDialArgs.Builder setRetryCallFailCause(int retryCallFailCause) {
this.mRetryCallFailCause = retryCallFailCause;
return this;
@@ -192,6 +210,11 @@ public class ImsPhone extends ImsPhoneBase {
return this;
}
+ public ImsDialArgs.Builder setIsWpsCall(boolean isWpsCall) {
+ this.mIsWpsCall = isWpsCall;
+ return this;
+ }
+
public ImsDialArgs build() {
return new ImsDialArgs(this);
}
@@ -203,30 +226,35 @@ public class ImsPhone extends ImsPhoneBase {
*/
public final android.telecom.Connection.RttTextStream rttTextStream;
- /** The CLIR mode to use */
- public final int clirMode;
public final int retryCallFailCause;
public final int retryCallFailNetworkType;
+ /** Indicates the call is Wireless Priority Service call */
+ public final boolean isWpsCall;
+
private ImsDialArgs(ImsDialArgs.Builder b) {
super(b);
this.rttTextStream = b.mRttTextStream;
- this.clirMode = b.mClirMode;
this.retryCallFailCause = b.mRetryCallFailCause;
this.retryCallFailNetworkType = b.mRetryCallFailNetworkType;
+ this.isWpsCall = b.mIsWpsCall;
}
}
// Instance Variables
Phone mDefaultPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ImsPhoneCallTracker mCT;
ImsExternalCallTracker mExternalCallTracker;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ServiceState mSS = new ServiceState();
+ private final ImsManagerFactory mImsManagerFactory;
+
+ private SharedPreferences mImsPhoneSharedPreferences;
+
// To redial silently through GSM or CDMA when dialing through IMS fails
private String mLastDialString;
@@ -244,13 +272,16 @@ public class ImsPhone extends ImsPhoneBase {
// The helper class to receive and store the MmTel registration status updated.
private ImsRegistrationCallbackHelper mImsMmTelRegistrationHelper;
- private boolean mRoaming = false;
+ // The roaming state if currently in service, or the last roaming state when was in service.
+ private boolean mLastKnownRoamingState = false;
private boolean mIsInImsEcm = false;
// List of Registrants to send supplementary service notifications to.
private RegistrantList mSsnRegistrants = new RegistrantList();
+ private ImsStats mImsStats;
+
// A runnable which is used to automatically exit from Ecm after a period of time.
private Runnable mExitEcmRunnable = new Runnable() {
@Override
@@ -270,6 +301,18 @@ public class ImsPhone extends ImsPhoneBase {
return mCurrentSubscriberUris;
}
+ /** Set call composer status from users for the current subscription */
+ public void setCallComposerStatus(int status) {
+ mImsPhoneSharedPreferences.edit().putInt(
+ PREF_USER_SET_CALL_COMPOSER_PREFIX + getSubId(), status).commit();
+ }
+
+ /** Get call composer status from users for the current subscription */
+ public int getCallComposerStatus() {
+ return mImsPhoneSharedPreferences.getInt(PREF_USER_SET_CALL_COMPOSER_PREFIX + getSubId(),
+ TelephonyManager.CALL_COMPOSER_STATUS_OFF);
+ }
+
@Override
public int getEmergencyNumberDbVersion() {
return getEmergencyNumberTracker().getEmergencyNumberDbVersion();
@@ -294,7 +337,7 @@ public class ImsPhone extends ImsPhoneBase {
final Message mOnComplete;
final boolean mIsCfu;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Cf(String cfNumber, boolean isCfu, Message onComplete) {
mSetCfNumber = cfNumber;
mIsCfu = isCfu;
@@ -302,17 +345,99 @@ public class ImsPhone extends ImsPhoneBase {
}
}
+ // Create SS (Supplementary Service) so that save SS params &
+ // mOnComplete (Message object passed by client) can be packed
+ // given as a single SS object as user data to UtInterface.
+ @VisibleForTesting
+ public static class SS {
+ int mCfAction;
+ int mCfReason;
+ String mDialingNumber;
+ int mTimerSeconds;
+ boolean mEnable;
+ int mClirMode;
+ String mFacility;
+ boolean mLockState;
+ String mPassword;
+ int mServiceClass;
+ @VisibleForTesting
+ public Message mOnComplete;
+
+ // Default // Query CW, CLIR, CLIP
+ SS(Message onComplete) {
+ mOnComplete = onComplete;
+ }
+
+ // Update CLIP
+ SS(boolean enable, Message onComplete) {
+ mEnable = enable;
+ mOnComplete = onComplete;
+ }
+
+ // Update CLIR
+ SS(int clirMode, Message onComplete) {
+ mClirMode = clirMode;
+ mOnComplete = onComplete;
+ }
+
+ // Update CW
+ SS(boolean enable, int serviceClass, Message onComplete) {
+ mEnable = enable;
+ mServiceClass = serviceClass;
+ mOnComplete = onComplete;
+ }
+
+ // Query CF
+ SS(int cfReason, int serviceClass, Message onComplete) {
+ mCfReason = cfReason;
+ mServiceClass = serviceClass;
+ mOnComplete = onComplete;
+ }
+
+ // Update CF
+ SS(int cfAction, int cfReason, String dialingNumber,
+ int serviceClass, int timerSeconds, Message onComplete) {
+ mCfAction = cfAction;
+ mCfReason = cfReason;
+ mDialingNumber = dialingNumber;
+ mServiceClass = serviceClass;
+ mTimerSeconds = timerSeconds;
+ mOnComplete = onComplete;
+ }
+
+ // Query CB
+ SS(String facility, String password, int serviceClass, Message onComplete) {
+ mFacility = facility;
+ mPassword = password;
+ mServiceClass = serviceClass;
+ mOnComplete = onComplete;
+ }
+
+ // Update CB
+ SS(String facility, boolean lockState, String password,
+ int serviceClass, Message onComplete) {
+ mFacility = facility;
+ mLockState = lockState;
+ mPassword = password;
+ mServiceClass = serviceClass;
+ mOnComplete = onComplete;
+ }
+ }
+
// Constructors
public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) {
- this(context, notifier, defaultPhone, false);
+ this(context, notifier, defaultPhone, ImsManager::getInstance, false);
}
@VisibleForTesting
public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone,
- boolean unitTestMode) {
+ ImsManagerFactory imsManagerFactory, boolean unitTestMode) {
super("ImsPhone", context, notifier, unitTestMode);
mDefaultPhone = defaultPhone;
+ mImsManagerFactory = imsManagerFactory;
+ mImsPhoneSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ mImsStats = new ImsStats(this);
// The ImsExternalCallTracker needs to be defined before the ImsPhoneCallTracker, as the
// ImsPhoneCallTracker uses a thread to spool up the ImsManager. Part of this involves
// setting the multiendpoint listener on the external call tracker. So we need to ensure
@@ -385,13 +510,13 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public ServiceState getServiceState() {
return mSS;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public void setServiceState(int state) {
boolean isVoiceRegStateChanged = false;
@@ -476,21 +601,21 @@ public class ImsPhone extends ImsPhoneBase {
mCT.explicitCallTransfer();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public ImsPhoneCall
getForegroundCall() {
return mCT.mForegroundCall;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public ImsPhoneCall
getBackgroundCall() {
return mCT.mBackgroundCall;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public ImsPhoneCall
getRingingCall() {
@@ -696,7 +821,7 @@ public class ImsPhone extends ImsPhoneBase {
mSsnRegistrants.notifyRegistrants(ar);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean handleInCallMmiCommands(String dialString) {
if (!isInCall()) {
@@ -767,7 +892,7 @@ public class ImsPhone extends ImsPhoneBase {
mDefaultPhone.notifyNewRingingConnectionP(c);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void notifyUnknownConnection(Connection c) {
mDefaultPhone.notifyUnknownConnectionP(c);
}
@@ -789,11 +914,7 @@ public class ImsPhone extends ImsPhoneBase {
public Connection startConference(String[] participantsToDial, DialArgs dialArgs)
throws CallStateException {
ImsDialArgs.Builder imsDialArgsBuilder;
- if (!(dialArgs instanceof ImsDialArgs)) {
- imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
- } else {
- imsDialArgsBuilder = ImsDialArgs.Builder.from((ImsDialArgs) dialArgs);
- }
+ imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
return mCT.startConference(participantsToDial, imsDialArgsBuilder.build());
}
@@ -817,12 +938,8 @@ public class ImsPhone extends ImsPhoneBase {
}
ImsDialArgs.Builder imsDialArgsBuilder;
+ imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
// Get the CLIR info if needed
- if (!(dialArgs instanceof ImsDialArgs)) {
- imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
- } else {
- imsDialArgsBuilder = ImsDialArgs.Builder.from((ImsDialArgs) dialArgs);
- }
imsDialArgsBuilder.setClirMode(mCT.getClirMode());
if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
@@ -922,13 +1039,13 @@ public class ImsPhone extends ImsPhoneBase {
return mCT.getMute();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public PhoneConstants.State getState() {
return mCT.getState();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
switch (commandInterfaceCFReason) {
case CF_REASON_UNCONDITIONAL:
@@ -943,7 +1060,7 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
switch (commandInterfaceCFAction) {
case CF_ACTION_DISABLE:
@@ -956,12 +1073,12 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isCfEnable(int action) {
return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int getConditionFromCFReason(int reason) {
switch(reason) {
case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL;
@@ -992,7 +1109,7 @@ public class ImsPhone extends ImsPhoneBase {
return CF_REASON_NOT_REACHABLE;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int getActionFromCFAction(int action) {
switch(action) {
case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
@@ -1010,7 +1127,8 @@ public class ImsPhone extends ImsPhoneBase {
public void getOutgoingCallerIdDisplay(Message onComplete) {
if (DBG) logd("getCLIR");
Message resp;
- resp = obtainMessage(EVENT_GET_CLIR_DONE, onComplete);
+ SS ss = new SS(onComplete);
+ resp = obtainMessage(EVENT_GET_CLIR_DONE, ss);
try {
ImsUtInterface ut = mCT.getUtInterface();
@@ -1027,7 +1145,8 @@ public class ImsPhone extends ImsPhoneBase {
// Packing CLIR value in the message. This will be required for
// SharedPreference caching, if the message comes back as part of
// a success response.
- resp = obtainMessage(EVENT_SET_CLIR_DONE, clirMode, 0, onComplete);
+ SS ss = new SS(clirMode, onComplete);
+ resp = obtainMessage(EVENT_SET_CLIR_DONE, ss);
try {
ImsUtInterface ut = mCT.getUtInterface();
ut.updateCLIR(clirMode, resp);
@@ -1036,7 +1155,22 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @Override
+ public void queryCLIP(Message onComplete) {
+ Message resp;
+ SS ss = new SS(onComplete);
+ resp = obtainMessage(EVENT_GET_CLIP_DONE, ss);
+
+ try {
+ Rlog.d(LOG_TAG, "ut.queryCLIP");
+ ImsUtInterface ut = mCT.getUtInterface();
+ ut.queryCLIP(resp);
+ } catch (ImsException e) {
+ sendErrorResponse(onComplete, e);
+ }
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void getCallForwardingOption(int commandInterfaceCFReason,
Message onComplete) {
@@ -1051,7 +1185,8 @@ public class ImsPhone extends ImsPhoneBase {
if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
if (DBG) logd("requesting call forwarding query.");
Message resp;
- resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
+ SS ss = new SS(commandInterfaceCFReason, serviceClass, onComplete);
+ resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, ss);
try {
ImsUtInterface ut = mCT.getUtInterface();
@@ -1074,7 +1209,7 @@ public class ImsPhone extends ImsPhoneBase {
CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setCallForwardingOption(int commandInterfaceCFAction,
int commandInterfaceCFReason,
@@ -1089,10 +1224,9 @@ public class ImsPhone extends ImsPhoneBase {
if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
(isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
Message resp;
- Cf cf = new Cf(dialingNumber, GsmMmiCode.isVoiceUnconditionalForwarding(
- commandInterfaceCFReason, serviceClass), onComplete);
- resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
- isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, cf);
+ SS ss = new SS(commandInterfaceCFAction, commandInterfaceCFReason,
+ dialingNumber, serviceClass, timerSeconds, onComplete);
+ resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, ss);
try {
ImsUtInterface ut = mCT.getUtInterface();
@@ -1110,12 +1244,13 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void getCallWaiting(Message onComplete) {
if (DBG) logd("getCallWaiting");
Message resp;
- resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, onComplete);
+ SS ss = new SS(onComplete);
+ resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, ss);
try {
ImsUtInterface ut = mCT.getUtInterface();
@@ -1125,7 +1260,7 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setCallWaiting(boolean enable, Message onComplete) {
int serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
@@ -1142,7 +1277,8 @@ public class ImsPhone extends ImsPhoneBase {
public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
if (DBG) logd("setCallWaiting enable=" + enable);
Message resp;
- resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
+ SS ss = new SS(enable, serviceClass, onComplete);
+ resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, ss);
try {
ImsUtInterface ut = mCT.getUtInterface();
@@ -1189,7 +1325,8 @@ public class ImsPhone extends ImsPhoneBase {
int serviceClass) {
if (DBG) logd("getCallBarring facility=" + facility + ", serviceClass = " + serviceClass);
Message resp;
- resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, onComplete);
+ SS ss = new SS(facility, password, serviceClass, onComplete);
+ resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, ss);
try {
ImsUtInterface ut = mCT.getUtInterface();
@@ -1214,7 +1351,8 @@ public class ImsPhone extends ImsPhoneBase {
+ ", lockState=" + lockState + ", serviceClass = " + serviceClass);
}
Message resp;
- resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, onComplete);
+ SS ss = new SS(facility, lockState, password, serviceClass, onComplete);
+ resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, ss);
int action;
if (lockState) {
@@ -1253,7 +1391,7 @@ public class ImsPhone extends ImsPhoneBase {
mCT.cancelUSSD(msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendErrorResponse(Message onComplete) {
logd("sendErrorResponse");
if (onComplete != null) {
@@ -1263,7 +1401,7 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public void sendErrorResponse(Message onComplete, Throwable e) {
logd("sendErrorResponse");
@@ -1368,6 +1506,12 @@ public class ImsPhone extends ImsPhoneBase {
isUssdRequest,
this);
onNetworkInitiatedUssd(mmi);
+ } else if (isUssdError) {
+ ImsPhoneMmiCode mmi;
+ mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
+ true,
+ this);
+ mmi.onUssdFinishedError();
}
}
@@ -1376,7 +1520,7 @@ public class ImsPhone extends ImsPhoneBase {
* registrants that it is complete.
* @param mmi MMI that is done
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void onMMIDone(ImsPhoneMmiCode mmi) {
/* Only notify complete if it's on the pending list.
* Otherwise, it's already been handled (eg, previously canceled).
@@ -1422,8 +1566,19 @@ public class ImsPhone extends ImsPhoneBase {
/* package */ void
initiateSilentRedial() {
- String result = mLastDialString;
- AsyncResult ar = new AsyncResult(null, result, null);
+ initiateSilentRedial(false, EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+ }
+
+ /* package */ void
+ initiateSilentRedial(boolean isEmergency, int eccCategory) {
+ DialArgs dialArgs = new DialArgs.Builder()
+ .setIsEmergency(isEmergency)
+ .setEccCategory(eccCategory)
+ .build();
+ int cause = CallFailCause.LOCAL_CALL_CS_RETRY_REQUIRED;
+ AsyncResult ar = new AsyncResult(null,
+ new SilentRedialParam(mLastDialString, cause, dialArgs),
+ null);
if (ar != null) {
mSilentRedialRegistrants.notifyRegistrants(ar);
}
@@ -1554,18 +1709,99 @@ public class ImsPhone extends ImsPhoneBase {
}
}
+ boolean isCsRetryException(Throwable e) {
+ if ((e != null) && (e instanceof ImsException)
+ && (((ImsException)e).getCode()
+ == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED)) {
+ return true;
+ }
+ return false;
+ }
+
+ private Bundle setCsfbBundle(boolean isCsRetry) {
+ Bundle b = new Bundle();
+ b.putBoolean(CS_FALLBACK_SS, isCsRetry);
+ return b;
+ }
+
+ private void sendResponseOrRetryOnCsfbSs(SS ss, int what, Throwable e, Object obj) {
+ if (!isCsRetryException(e)) {
+ sendResponse(ss.mOnComplete, obj, e);
+ return;
+ }
+
+ Rlog.d(LOG_TAG, "Try CSFB: " + what);
+ ss.mOnComplete.setData(setCsfbBundle(true));
+
+ switch (what) {
+ case EVENT_GET_CALL_FORWARD_DONE:
+ mDefaultPhone.getCallForwardingOption(ss.mCfReason,
+ ss.mServiceClass,
+ ss.mOnComplete);
+ break;
+ case EVENT_SET_CALL_FORWARD_DONE:
+ mDefaultPhone.setCallForwardingOption(ss.mCfAction,
+ ss.mCfReason,
+ ss.mDialingNumber,
+ ss.mServiceClass,
+ ss.mTimerSeconds,
+ ss.mOnComplete);
+ break;
+ case EVENT_GET_CALL_BARRING_DONE:
+ mDefaultPhone.getCallBarring(ss.mFacility,
+ ss.mPassword,
+ ss.mOnComplete,
+ ss.mServiceClass);
+ break;
+ case EVENT_SET_CALL_BARRING_DONE:
+ mDefaultPhone.setCallBarring(ss.mFacility,
+ ss.mLockState,
+ ss.mPassword,
+ ss.mOnComplete,
+ ss.mServiceClass);
+ break;
+ case EVENT_GET_CALL_WAITING_DONE:
+ mDefaultPhone.getCallWaiting(ss.mOnComplete);
+ break;
+ case EVENT_SET_CALL_WAITING_DONE:
+ mDefaultPhone.setCallWaiting(ss.mEnable,
+ ss.mServiceClass,
+ ss.mOnComplete);
+ break;
+ case EVENT_GET_CLIR_DONE:
+ mDefaultPhone.getOutgoingCallerIdDisplay(ss.mOnComplete);
+ break;
+ case EVENT_SET_CLIR_DONE:
+ mDefaultPhone.setOutgoingCallerIdDisplay(ss.mClirMode, ss.mOnComplete);
+ break;
+ case EVENT_GET_CLIP_DONE:
+ mDefaultPhone.queryCLIP(ss.mOnComplete);
+ break;
+ default:
+ break;
+ }
+ }
+
@Override
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
+ Message onComplete;
+ SS ss = null;
+ if (ar != null && ar.userObj instanceof SS) {
+ ss = (SS) ar.userObj;
+ }
if (DBG) logd("handleMessage what=" + msg.what);
switch (msg.what) {
case EVENT_SET_CALL_FORWARD_DONE:
- Cf cf = (Cf) ar.userObj;
- if (cf.mIsCfu && ar.exception == null) {
- setVoiceCallForwardingFlag(getIccRecords(), 1, msg.arg1 == 1, cf.mSetCfNumber);
+ if (ar.exception == null && ss != null &&
+ (ss.mCfReason == CF_REASON_UNCONDITIONAL)) {
+ setVoiceCallForwardingFlag(getIccRecords(), 1, isCfEnable(ss.mCfAction),
+ ss.mDialingNumber);
+ }
+ if (ss != null) {
+ sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null);
}
- sendResponse(cf.mOnComplete, null, ar.exception);
break;
case EVENT_GET_CALL_FORWARD_DONE:
@@ -1573,7 +1809,9 @@ public class ImsPhone extends ImsPhoneBase {
if (ar.exception == null) {
cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
}
- sendResponse((Message) ar.userObj, cfInfos, ar.exception);
+ if (ss != null) {
+ sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, cfInfos);
+ }
break;
case EVENT_GET_CALL_BARRING_DONE:
@@ -1586,7 +1824,9 @@ public class ImsPhone extends ImsPhoneBase {
ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
}
}
- sendResponse((Message) ar.userObj, ssInfos, ar.exception);
+ if (ss != null) {
+ sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfos);
+ }
break;
case EVENT_GET_CLIR_DONE:
@@ -1597,17 +1837,33 @@ public class ImsPhone extends ImsPhoneBase {
// that for compatibility
clirInfo = ssInfo.getCompatArray(ImsSsData.SS_CLIR);
}
- sendResponse((Message) ar.userObj, clirInfo, ar.exception);
+ if (ss != null) {
+ sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, clirInfo);
+ }
+ break;
+
+ case EVENT_GET_CLIP_DONE:
+ ImsSsInfo ssInfoResp = null;
+ if (ar.exception == null && ar.result instanceof ImsSsInfo) {
+ ssInfoResp = (ImsSsInfo) ar.result;
+ }
+ if (ss != null) {
+ sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfoResp);
+ }
break;
case EVENT_SET_CLIR_DONE:
if (ar.exception == null) {
- saveClirSetting(msg.arg1);
+ if (ss != null) {
+ saveClirSetting(ss.mClirMode);
+ }
}
// (Intentional fallthrough)
case EVENT_SET_CALL_BARRING_DONE:
case EVENT_SET_CALL_WAITING_DONE:
- sendResponse((Message) ar.userObj, null, ar.exception);
+ if (ss != null) {
+ sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null);
+ }
break;
case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED:
@@ -1722,7 +1978,7 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void handleEnterEmergencyCallbackMode() {
if (DBG) logd("handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " + isInEcm());
// if phone is not in Ecm mode, and it's changed to Ecm mode
@@ -1742,7 +1998,7 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void handleExitEmergencyCallbackMode() {
if (DBG) logd("handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " + isInEcm());
@@ -1792,7 +2048,7 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
mEcmExitRespRegistrant = new Registrant(h, what, obj);
@@ -1812,10 +2068,15 @@ public class ImsPhone extends ImsPhoneBase {
return mCT.isImsCapabilityAvailable(capability, regTech);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean isVolteEnabled() {
- return mCT.isVolteEnabled();
+ return isVoiceOverCellularImsEnabled();
+ }
+
+ @Override
+ public boolean isVoiceOverCellularImsEnabled() {
+ return mCT.isVoiceOverCellularImsEnabled();
}
@Override
@@ -1854,18 +2115,13 @@ public class ImsPhone extends ImsPhoneBase {
}
// Not used, but not removed due to UnsupportedAppUsage tag.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setImsRegistered(boolean isRegistered) {
mImsMmTelRegistrationHelper.updateRegistrationState(
isRegistered ? RegistrationManager.REGISTRATION_STATE_REGISTERED :
RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
}
- public void setImsRegistrationState(@RegistrationManager.ImsRegistrationState int value) {
- if (DBG) logd("setImsRegistrationState: " + value);
- mImsMmTelRegistrationHelper.updateRegistrationState(value);
- }
-
@Override
public void callEndCleanupHandOverCallIfAny() {
mCT.callEndCleanupHandOverCallIfAny();
@@ -1900,9 +2156,7 @@ public class ImsPhone extends ImsPhoneBase {
mContext,
0,
resultIntent,
- // Note: Since resultIntent above specifies an explicit class name
- // we do not need to specify PendingIntent.FLAG_IMMUTABLE here.
- PendingIntent.FLAG_UPDATE_CURRENT
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
final Notification notification = new Notification.Builder(mContext)
@@ -1934,7 +2188,7 @@ public class ImsPhone extends ImsPhoneBase {
if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
&& imsReasonInfo.mExtraMessage != null) {
// Suppress WFC Registration notifications if WFC is not enabled by the user.
- if (ImsManager.getInstance(mContext, mPhoneId).isWfcEnabledByUser()) {
+ if (mImsManagerFactory.create(mContext, mPhoneId).isWfcEnabledByUser()) {
processWfcDisconnectForNotification(imsReasonInfo);
}
}
@@ -2035,7 +2289,7 @@ public class ImsPhone extends ImsPhoneBase {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public boolean isUtEnabled() {
return mCT.isUtEnabled();
@@ -2069,15 +2323,15 @@ public class ImsPhone extends ImsPhoneBase {
}
boolean newRoamingState = ss.getRoaming();
// Do not recalculate if there is no change to state.
- if (mRoaming == newRoamingState) {
+ if (mLastKnownRoamingState == newRoamingState) {
return;
}
boolean isInService = (ss.getState() == ServiceState.STATE_IN_SERVICE
|| ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE);
// If we are not IN_SERVICE for voice or data, ignore change roaming state, as we always
// move to home in this case.
- if (!isInService) {
- logi("updateRoamingState: we are OUT_OF_SERVICE, ignoring roaming change.");
+ if (!isInService || !mDefaultPhone.isRadioOn()) {
+ logi("updateRoamingState: we are not IN_SERVICE, ignoring roaming change.");
return;
}
// We ignore roaming changes when moving to IWLAN because it always sets the roaming
@@ -2091,14 +2345,14 @@ public class ImsPhone extends ImsPhoneBase {
}
if (mCT.getState() == PhoneConstants.State.IDLE) {
if (DBG) logd("updateRoamingState now: " + newRoamingState);
- mRoaming = newRoamingState;
+ mLastKnownRoamingState = newRoamingState;
CarrierConfigManager configManager = (CarrierConfigManager)
getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
// Don't set wfc mode if carrierconfig has not loaded. It will be set by GsmCdmaPhone
// when receives ACTION_CARRIER_CONFIG_CHANGED broadcast.
if (configManager != null && CarrierConfigManager.isConfigForIdentifiedCarrier(
configManager.getConfigForSubId(getSubId()))) {
- ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
+ ImsManager imsManager = mImsManagerFactory.create(mContext, mPhoneId);
imsManager.setWfcMode(imsManager.getWfcMode(newRoamingState), newRoamingState);
}
} else {
@@ -2148,36 +2402,46 @@ public class ImsPhone extends ImsPhoneBase {
@Override
public void handleImsRegistered(int imsRadioTech) {
if (DBG) {
- logd("onImsMmTelConnected imsRadioTech="
+ logd("handleImsRegistered: onImsMmTelConnected imsRadioTech="
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
}
- mRegLocalLog.log("onImsMmTelConnected imsRadioTech="
+ mRegLocalLog.log("handleImsRegistered: onImsMmTelConnected imsRadioTech="
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
setServiceState(ServiceState.STATE_IN_SERVICE);
+ getDefaultPhone().setImsRegistrationState(true);
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null);
+ mImsStats.onImsRegistered(imsRadioTech);
}
@Override
public void handleImsRegistering(int imsRadioTech) {
if (DBG) {
- logd("onImsMmTelProgressing imsRadioTech="
+ logd("handleImsRegistering: onImsMmTelProgressing imsRadioTech="
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
}
- mRegLocalLog.log("onImsMmTelProgressing imsRadioTech="
+ mRegLocalLog.log("handleImsRegistering: onImsMmTelProgressing imsRadioTech="
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+ getDefaultPhone().setImsRegistrationState(false);
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING,
null);
+ mImsStats.onImsRegistering(imsRadioTech);
}
@Override
public void handleImsUnregistered(ImsReasonInfo imsReasonInfo) {
- if (DBG) logd("onImsMmTelDisconnected imsReasonInfo=" + imsReasonInfo);
- mRegLocalLog.log("onImsMmTelDisconnected imsRadioTech=" + imsReasonInfo);
+ if (DBG) {
+ logd("handleImsUnregistered: onImsMmTelDisconnected imsReasonInfo="
+ + imsReasonInfo);
+ }
+ mRegLocalLog.log("handleImsUnregistered: onImsMmTelDisconnected imsRadioTech="
+ + imsReasonInfo);
setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
processDisconnectReason(imsReasonInfo);
+ getDefaultPhone().setImsRegistrationState(false);
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED,
imsReasonInfo);
+ mImsStats.onImsUnregistered(imsReasonInfo);
}
@Override
@@ -2194,13 +2458,8 @@ public class ImsPhone extends ImsPhoneBase {
public DialArgs updateDialArgsForVolteSilentRedial(DialArgs dialArgs, int causeCode) {
if (dialArgs != null) {
ImsPhone.ImsDialArgs.Builder imsDialArgsBuilder;
- if (dialArgs instanceof ImsPhone.ImsDialArgs) {
- imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder
- .from((ImsPhone.ImsDialArgs) dialArgs);
- } else {
- imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder
- .from(dialArgs);
- }
+ imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder.from(dialArgs);
+
Bundle extras = new Bundle(dialArgs.intentExtras);
if (causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI && isWifiCallingEnabled()) {
extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE,
@@ -2220,11 +2479,26 @@ public class ImsPhone extends ImsPhoneBase {
return mDefaultPhone.getVoiceCallSessionStats();
}
+ /** Returns the {@link ImsStats} for this IMS phone. */
+ public ImsStats getImsStats() {
+ return mImsStats;
+ }
+
+ /** Sets the {@link ImsStats} mock for this IMS phone during unit testing. */
+ @VisibleForTesting
+ public void setImsStats(ImsStats imsStats) {
+ mImsStats = imsStats;
+ }
+
public boolean hasAliveCall() {
return (getForegroundCall().getState() != Call.State.IDLE ||
getBackgroundCall().getState() != Call.State.IDLE);
}
+ public boolean getLastKnownRoamingState() {
+ return mLastKnownRoamingState;
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
@@ -2243,7 +2517,7 @@ public class ImsPhone extends ImsPhoneBase {
pw.println(" mSilentRedialRegistrants = " + mSilentRedialRegistrants);
pw.println(" mImsMmTelRegistrationState = "
+ mImsMmTelRegistrationHelper.getImsRegistrationState());
- pw.println(" mRoaming = " + mRoaming);
+ pw.println(" mLastKnownRoamingState = " + mLastKnownRoamingState);
pw.println(" mSsnRegistrants = " + mSsnRegistrants);
pw.println(" Registration Log:");
pw.increaseIndent();
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index 9df7215e94..98cc441861 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.imsphone;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.telephony.DisconnectCause;
import android.telephony.ims.ImsStreamMediaProfile;
import android.util.Log;
@@ -89,7 +90,7 @@ public class ImsPhoneCall extends Call {
/************************** Overridden from Call *************************/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public ArrayList<Connection> getConnections() {
return super.getConnections();
@@ -115,7 +116,7 @@ public class ImsPhoneCall extends Call {
/** Please note: if this is the foreground call and a
* background call exists, the background call will be resumed.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void
hangup() throws CallStateException {
@@ -173,7 +174,7 @@ public class ImsPhoneCall extends Call {
mOwner.logState();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void attach(Connection conn, State state) {
if (VDBG) {
Rlog.v(LOG_TAG, "attach : " + mCallContext + " state = " +
@@ -183,7 +184,7 @@ public class ImsPhoneCall extends Call {
mState = state;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void attachFake(Connection conn, State state) {
attach(conn, state);
}
@@ -243,7 +244,7 @@ public class ImsPhoneCall extends Call {
/**
* Called when this Call is being hung up locally (eg, user pressed "end")
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@VisibleForTesting
public void onHangupLocal() {
ArrayList<Connection> connections = getConnections();
@@ -286,7 +287,7 @@ public class ImsPhoneCall extends Call {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
/* package */ void
merge(ImsPhoneCall that, State state) {
// This call is the conference host and the "that" call is the one being merged in.
@@ -320,7 +321,7 @@ public class ImsPhoneCall extends Call {
* @return The {@link ImsCall}.
*/
@VisibleForTesting
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsCall getImsCall() {
ImsPhoneConnection connection = getFirstConnection();
return (connection == null) ? null : connection.getImsCall();
@@ -333,44 +334,70 @@ public class ImsPhoneCall extends Call {
}
ImsStreamMediaProfile mediaProfile = imsCall.getCallProfile().mMediaProfile;
-
- return (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE)
- ? true : false;
+ boolean shouldPlayRingback =
+ (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE)
+ ? true : false;
+ Rlog.i(LOG_TAG, "isLocalTone: audioDirection=" + mediaProfile.mAudioDirection
+ + ", playRingback=" + shouldPlayRingback);
+ return shouldPlayRingback;
}
- public boolean update (ImsPhoneConnection conn, ImsCall imsCall, State state) {
+ public boolean update(ImsPhoneConnection conn, ImsCall imsCall, State state) {
boolean changed = false;
State oldState = mState;
+ // We will try to start or stop ringback whenever the call has major call state changes.
+ maybeChangeRingbackState(imsCall, state);
+
+ if ((state != mState) && (state != State.DISCONNECTED)) {
+ mState = state;
+ changed = true;
+ } else if (state == State.DISCONNECTED) {
+ changed = true;
+ }
+
+ if (VDBG) {
+ Rlog.v(LOG_TAG, "update : " + mCallContext + " state: " + oldState + " --> " + mState);
+ }
+
+ return changed;
+ }
+
+ /**
+ * Determines whether to change the ringback state for a call.
+ * @param imsCall The call.
+ */
+ public void maybeChangeRingbackState(ImsCall imsCall) {
+ maybeChangeRingbackState(imsCall, mState);
+ }
+
+ /**
+ * Determines whether local ringback should be playing for the call. We will play local
+ * ringback when a call is in an ALERTING state and the audio direction is DIRECTION_INACTIVE.
+ * @param imsCall The call the change pertains to.
+ * @param state The current state of the call.
+ */
+ private void maybeChangeRingbackState(ImsCall imsCall, State state) {
//ImsCall.Listener.onCallProgressing can be invoked several times
//and ringback tone mode can be changed during the call setup procedure
+ Rlog.i(LOG_TAG, "maybeChangeRingbackState: state=" + state);
if (state == State.ALERTING) {
if (mIsRingbackTonePlaying && !isLocalTone(imsCall)) {
+ Rlog.i(LOG_TAG, "maybeChangeRingbackState: stop ringback");
getPhone().stopRingbackTone();
mIsRingbackTonePlaying = false;
} else if (!mIsRingbackTonePlaying && isLocalTone(imsCall)) {
+ Rlog.i(LOG_TAG, "maybeChangeRingbackState: start ringback");
getPhone().startRingbackTone();
mIsRingbackTonePlaying = true;
}
} else {
if (mIsRingbackTonePlaying) {
+ Rlog.i(LOG_TAG, "maybeChangeRingbackState: stop ringback");
getPhone().stopRingbackTone();
mIsRingbackTonePlaying = false;
}
}
-
- if ((state != mState) && (state != State.DISCONNECTED)) {
- mState = state;
- changed = true;
- } else if (state == State.DISCONNECTED) {
- changed = true;
- }
-
- if (VDBG) {
- Rlog.v(LOG_TAG, "update : " + mCallContext + " state: " + oldState + " --> " + mState);
- }
-
- return changed;
}
/* package */ ImsPhoneConnection
@@ -401,6 +428,10 @@ public class ImsPhoneCall extends Call {
}
}
+ public boolean isRingbackTonePlaying() {
+ return mIsRingbackTonePlaying;
+ }
+
private void takeOver(ImsPhoneCall that) {
copyConnectionFrom(that);
mState = that.mState;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index c162b7a933..7b1f020169 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -16,6 +16,9 @@
package com.android.internal.telephony.imsphone;
+import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED;
+import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.internal.telephony.Phone.CS_FALLBACK;
@@ -36,9 +39,11 @@ import android.net.NetworkRequest;
import android.net.NetworkStats;
import android.net.netstats.provider.NetworkStatsProvider;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
@@ -57,6 +62,7 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyLocalConnection;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsCallProfile;
@@ -66,11 +72,14 @@ import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.ImsSuppServiceNotification;
import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
@@ -79,11 +88,9 @@ import android.util.SparseIntArray;
import com.android.ims.FeatureConnector;
import com.android.ims.ImsCall;
import com.android.ims.ImsConfig;
-import com.android.ims.ImsConfigListener;
import com.android.ims.ImsEcbm;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
-import com.android.ims.ImsMultiEndpoint;
import com.android.ims.ImsUtInterface;
import com.android.ims.internal.ConferenceParticipant;
import com.android.ims.internal.IImsCallSession;
@@ -99,11 +106,13 @@ import com.android.internal.telephony.CallTracker;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.IccCardConstants;
import com.android.internal.telephony.LocaleTracker;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.d2d.RtpTransport;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
@@ -123,6 +132,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
@@ -138,6 +148,20 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
static final String LOG_TAG = "ImsPhoneCallTracker";
static final String VERBOSE_STATE_TAG = "IPCTState";
+ /**
+ * Class which contains configuration items obtained from the config.xml in
+ * packages/services/Telephony which are injected in the ImsPhoneCallTracker at phone creation
+ * time.
+ */
+ public static class Config {
+ /**
+ * The value for config.xml/config_use_device_to_device_communication.
+ * When {@code true}, the device supports device to device communication using both DTMF
+ * and RTP header extensions.
+ */
+ public boolean isD2DCommunicationSupported;
+ }
+
public interface PhoneStateListener {
void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState);
}
@@ -146,10 +170,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
SharedPreferences getDefaultSharedPreferences(Context context);
}
- public interface PhoneNumberUtilsProxy {
- boolean isEmergencyNumber(String number);
- }
-
private static final boolean DBG = true;
// When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
@@ -158,6 +178,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
private static final boolean VERBOSE_STATE_LOGGING = FORCE_VERBOSE_STATE_LOGGING ||
Rlog.isLoggable(VERBOSE_STATE_TAG, Log.VERBOSE);
+ private static final int CONNECTOR_RETRY_DELAY_MS = 5000; // 5 seconds.
private MmTelFeature.MmTelCapabilities mMmTelCapabilities =
new MmTelFeature.MmTelCapabilities();
@@ -331,11 +352,11 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (subId == mPhone.getSubId()) {
- cacheCarrierConfiguration(subId);
+ updateCarrierConfiguration(subId);
log("onReceive : Updating mAllowEmergencyVideoCalls = " +
mAllowEmergencyVideoCalls);
}
- } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) {
+ } else if (TelecomManager.ACTION_DEFAULT_DIALER_CHANGED.equals(intent.getAction())) {
mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
}
@@ -355,6 +376,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private boolean mIsConferenceEventPackageEnabled = true;
/**
+ * The Telephony config.xml values pertinent to ImsPhoneCallTracker.
+ */
+ private Config mConfig = null;
+
+ /**
+ * Whether D2D has been force enabled via the d2d telephony command.
+ */
+ private boolean mDeviceToDeviceForceEnabled = false;
+
+ /**
* Network callback used to schedule the handover check when a wireless network connects.
*/
private ConnectivityManager.NetworkCallback mNetworkCallback =
@@ -416,20 +447,20 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>();
private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsPhoneCall mForegroundCall = new ImsPhoneCall(this,
ImsPhoneCall.CONTEXT_FOREGROUND);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this,
ImsPhoneCall.CONTEXT_BACKGROUND);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER);
// Hold aggregated video call data usage for each video call since boot.
@@ -463,29 +494,29 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private final AtomicInteger mDefaultDialerUid = new AtomicInteger(NetworkStats.UID_ALL);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsPhoneConnection mPendingMO;
private int mClirMode = CommandsInterface.CLIR_DEFAULT;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Object mSyncHold = new Object();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsCall mUssdSession = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Message mPendingUssd = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ImsPhone mPhone;
private boolean mDesiredMute = false; // false = mute off
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mOnHoldToneStarted = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mOnHoldToneId = -1;
private PhoneConstants.State mState = PhoneConstants.State.IDLE;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsManager mImsManager;
private ImsUtInterface mUtInterface;
@@ -498,17 +529,19 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private int mPendingCallVideoState;
private Bundle mPendingIntentExtras;
private boolean pendingCallInEcm = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mSwitchingFgAndBgCalls = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsCall mCallExpectedToResume = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mAllowEmergencyVideoCalls = false;
private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
private boolean mIsViLteDataMetered = false;
private boolean mAlwaysPlayRemoteHoldTone = false;
private boolean mAutoRetryFailedWifiEmergencyCall = false;
private boolean mSupportCepOnPeer = true;
+ private boolean mSupportD2DUsingRtp = false;
+ private boolean mSupportSdpForRtpHeaderExtensions = false;
// Tracks the state of our background/foreground calls while a call hold/swap operation is
// in progress. Values listed above.
private HoldSwapState mHoldSwitchingState = HoldSwapState.INACTIVE;
@@ -831,8 +864,14 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
/**
+ * Carrier configuration option which specifies how the carrier handles USSD request.
+ * See {@link CarrierConfigManager#KEY_CARRIER_USSD_METHOD_INT} for more information.
+ */
+ private int mUssdMethod = USSD_OVER_CS_PREFERRED;
+
+ /**
* TODO: Remove this code; it is a workaround.
- * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(boolean)} to
+ * When {@code true}, forces {@link ImsManager#updateImsServiceConfig} to
* be called when an ongoing video call is disconnected. In some cases, where video pause is
* supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data
* has been disabled we will pause the video rather than disconnecting the call. When this
@@ -841,6 +880,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
*/
private boolean mShouldUpdateImsConfigOnDisconnect = false;
+ private Pair<Boolean, Integer> mPendingSilentRedialInfo = null;
+
/**
* Default implementation for retrieving shared preferences; uses the actual PreferencesManager.
*/
@@ -848,14 +889,26 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return PreferenceManager.getDefaultSharedPreferences(context);
};
- /**
- * Default implementation for determining if a number is an emergency number. Uses the real
- * PhoneNumberUtils.
- */
- private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = (String string) -> {
- return PhoneNumberUtils.isEmergencyNumber(string);
+ private Runnable mConnectorRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mImsManagerConnector.connect();
+ }
};
+ /**
+ * Allows the FeatureConnector used to be swapped for easier testing.
+ */
+ @VisibleForTesting
+ public interface ConnectorFactory {
+ /**
+ * Create a FeatureConnector for this class to use to connect to an ImsManager.
+ */
+ FeatureConnector<ImsManager> create(Context context, int phoneId,
+ String logPrefix, FeatureConnector.Listener<ImsManager> listener,
+ Executor executor);
+ }
+ private final ConnectorFactory mConnectorFactory;
private final FeatureConnector<ImsManager> mImsManagerConnector;
// Used exclusively for IMS Registration related events for logging.
@@ -867,21 +920,22 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
//***** Constructors
- public ImsPhoneCallTracker(ImsPhone phone) {
- this(phone, phone.getContext().getMainExecutor());
+ public ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory) {
+ this(phone, factory, phone.getContext().getMainExecutor());
}
@VisibleForTesting
- public ImsPhoneCallTracker(ImsPhone phone, Executor executor) {
+ public ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory, Executor executor) {
this.mPhone = phone;
+ mConnectorFactory = factory;
mMetrics = TelephonyMetrics.getInstance();
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- intentfilter.addAction(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
+ intentfilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
mPhone.getContext().registerReceiver(mReceiver, intentfilter);
- cacheCarrierConfiguration(mPhone.getSubId());
+ updateCarrierConfiguration(mPhone.getSubId());
mPhone.getDefaultPhone().getDataEnabledSettings().registerForDataEnabledChanged(
this, EVENT_DATA_ENABLED_CHANGED, null);
@@ -899,27 +953,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
Context.NETWORK_STATS_SERVICE);
statsManager.registerNetworkStatsProvider(LOG_TAG, mVtDataUsageProvider);
- // Allow the executor to be specified for testing.
- mImsManagerConnector = new FeatureConnector<>(
- phone.getContext(), phone.getPhoneId(),
- new FeatureConnector.Listener<ImsManager>() {
- @Override
- public ImsManager getFeatureManager() {
- return ImsManager.getInstance(phone.getContext(), phone.getPhoneId());
- }
-
- @Override
+ mImsManagerConnector = mConnectorFactory.create(mPhone.getContext(), mPhone.getPhoneId(),
+ LOG_TAG, new FeatureConnector.Listener<ImsManager>() {
public void connectionReady(ImsManager manager) throws ImsException {
mImsManager = manager;
startListeningForCalls();
}
@Override
- public void connectionUnavailable() {
+ public void connectionUnavailable(int reason) {
+ logi("connectionUnavailable: " + reason);
+ if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) {
+ postDelayed(mConnectorRunnable, CONNECTOR_RETRY_DELAY_MS);
+ }
stopListeningForCalls();
}
- }, executor, "ImsPhoneCallTracker");
- mImsManagerConnector.connect();
+ }, executor);
+ // It can take some time for ITelephony to get published, so defer connecting.
+ post(mConnectorRunnable);
}
/**
@@ -932,23 +983,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mSharedPreferenceProxy = sharedPreferenceProxy;
}
- /**
- * Test-only method used to mock out access to the phone number utils class.
- * @param phoneNumberUtilsProxy
- */
- @VisibleForTesting
- public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy phoneNumberUtilsProxy) {
- mPhoneNumberUtilsProxy = phoneNumberUtilsProxy;
- }
-
- /**
- * Test-only method used to set the ImsService retry timeout.
- */
- @VisibleForTesting
- public void setRetryTimeout(FeatureConnector.RetryTimeout retryTimeout) {
- mImsManagerConnector.mRetryTimeout = retryTimeout;
- }
-
private int getPackageUid(Context context, String pkg) {
if (pkg == null) {
return NetworkStats.UID_ALL;
@@ -965,19 +999,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return uid;
}
- private void startListeningForCalls() throws ImsException {
+ @VisibleForTesting
+ public void startListeningForCalls() throws ImsException {
log("startListeningForCalls");
mOperationLocalLog.log("startListeningForCalls - Connecting to ImsService");
- mImsManager.open(mMmTelFeatureListener);
- mImsManager.addRegistrationCallback(mPhone.getImsMmTelRegistrationCallback());
- mImsManager.addCapabilitiesCallback(mImsCapabilityCallback);
+ ImsExternalCallTracker externalCallTracker = mPhone.getExternalCallTracker();
+ ImsExternalCallTracker.ExternalCallStateListener externalCallStateListener =
+ externalCallTracker != null
+ ? externalCallTracker.getExternalCallStateListener() : null;
- mImsManager.setConfigListener(mImsConfigListener);
+ mImsManager.open(mMmTelFeatureListener, mPhone.getImsEcbmStateListener(),
+ externalCallStateListener);
+ mImsManager.addRegistrationCallback(mPhone.getImsMmTelRegistrationCallback(), this::post);
+ mImsManager.addCapabilitiesCallback(mImsCapabilityCallback, this::post);
+
+ ImsManager.setImsStatsCallback(mPhone.getPhoneId(), mImsStatsCallback);
mImsManager.getConfigInterface().addConfigCallback(mConfigCallback);
- // Get the ECBM interface and set IMSPhone's listener object for notifications
- getEcbmInterface().setEcbmStateListener(mPhone.getImsEcbmStateListener());
if (mPhone.isInEcm()) {
// Call exit ECBM which will invoke onECBMExited
mPhone.exitEmergencyCallbackMode();
@@ -988,43 +1027,122 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
Phone.TTY_MODE_OFF);
mImsManager.setUiTTYMode(mPhone.getContext(), mPreferredTtyMode, null);
- ImsMultiEndpoint multiEndpoint = getMultiEndpointInterface();
- if (multiEndpoint != null) {
- multiEndpoint.setExternalCallStateListener(
- mPhone.getExternalCallTracker().getExternalCallStateListener());
- }
-
- //Set UT interface listener to receive UT indications.
+ // Set UT interface listener to receive UT indications & keep track of the interface so the
+ // handler reference can be cleared.
mUtInterface = getUtInterface();
if (mUtInterface != null) {
- mUtInterface.registerForSuppServiceIndication(this,
- EVENT_SUPP_SERVICE_INDICATION, null);
+ mUtInterface.registerForSuppServiceIndication(this, EVENT_SUPP_SERVICE_INDICATION,
+ null);
}
- if (mCarrierConfigLoaded) {
- mImsManager.updateImsServiceConfig(true);
- }
+ maybeConfigureRtpHeaderExtensions();
+ updateImsServiceConfig();
// For compatibility with apps that still use deprecated intent
sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_UP);
}
+ /**
+ * Configures RTP header extension types used during SDP negotiation.
+ */
+ private void maybeConfigureRtpHeaderExtensions() {
+ // Where device to device communication is available, ensure that the
+ // supported RTP header extension types defined in {@link RtpTransport} are
+ // set as the offered RTP header extensions for this device.
+ if (mDeviceToDeviceForceEnabled
+ || (mConfig != null && mConfig.isD2DCommunicationSupported
+ && mSupportD2DUsingRtp)) {
+ ArraySet<RtpHeaderExtensionType> types = new ArraySet<>();
+ if (mSupportSdpForRtpHeaderExtensions) {
+ types.add(RtpTransport.CALL_STATE_RTP_HEADER_EXTENSION_TYPE);
+ types.add(RtpTransport.DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE);
+ logi("maybeConfigureRtpHeaderExtensions: set offered RTP header extension types");
+
+ } else {
+ logi("maybeConfigureRtpHeaderExtensions: SDP negotiation not supported; not "
+ + "setting offered RTP header extension types");
+ }
+ try {
+ mImsManager.setOfferedRtpHeaderExtensionTypes(types);
+ } catch (ImsException e) {
+ loge("maybeConfigureRtpHeaderExtensions: failed to set extensions; " + e);
+ }
+ }
+ }
+
+ /**
+ * Used via the telephony shell command to force D2D to be enabled.
+ * @param isEnabled {@code true} if D2D is force enabled.
+ */
+ public void setDeviceToDeviceForceEnabled(boolean isEnabled) {
+ mDeviceToDeviceForceEnabled = isEnabled;
+ maybeConfigureRtpHeaderExtensions();
+ }
+
private void stopListeningForCalls() {
log("stopListeningForCalls");
mOperationLocalLog.log("stopListeningForCalls - Disconnecting from ImsService");
- resetImsCapabilities();
// Only close on valid session.
if (mImsManager != null) {
+ mImsManager.removeRegistrationListener(mPhone.getImsMmTelRegistrationCallback());
+ mImsManager.removeCapabilitiesCallback(mImsCapabilityCallback);
try {
+ ImsManager.setImsStatsCallback(mPhone.getPhoneId(), null);
mImsManager.getConfigInterface().removeConfigCallback(mConfigCallback.getBinder());
} catch (ImsException e) {
Log.w(LOG_TAG, "stopListeningForCalls: unable to remove config callback.");
}
+ // Will release other listeners for MMTEL/ECBM/UT/MultiEndpoint Indications set in #open
mImsManager.close();
}
+ if (mUtInterface != null) {
+ mUtInterface.unregisterForSuppServiceIndication(this);
+ mUtInterface = null;
+ }
+
+ resetImsCapabilities();
+ hangupAllOrphanedConnections(DisconnectCause.LOST_SIGNAL);
// For compatibility with apps that still use deprecated intent
sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_DOWN);
}
+ /**
+ * Hang up all ongoing connections in the case that the ImsService has been disconnected and the
+ * existing calls have been orphaned. This method assumes that there is no connection to the
+ * ImsService and DOES NOT try to terminate the connections on the service side before
+ * disconnecting here, as it assumes they have already been disconnected when we lost the
+ * connection to the ImsService.
+ */
+ @VisibleForTesting
+ public void hangupAllOrphanedConnections(int disconnectCause) {
+ Log.w(LOG_TAG, "hangupAllOngoingConnections called for cause " + disconnectCause);
+
+ // Move connections to disconnected and notify the reason why.
+ for (ImsPhoneConnection connection : mConnections) {
+ connection.update(connection.getImsCall(), ImsPhoneCall.State.DISCONNECTED);
+ connection.onDisconnect(disconnectCause);
+ connection.getCall().detach(connection);
+ }
+ mConnections.clear();
+ // Pending MO was added to mConnections previously, so it has already been disconnected
+ // above. Remove all references to it.
+ mPendingMO = null;
+ updatePhoneState();
+ }
+
+ /**
+ * Requests modem to hang up all connections.
+ */
+ public void hangupAllConnections() {
+ getConnections().stream().forEach(c -> {
+ logi("Disconnecting callId = " + c.getTelecomCallId());
+ try {
+ c.hangup();
+ } catch (CallStateException e) {
+ loge("Failed to disconnet call...");
+ }
+ });
+ }
+
private void sendImsServiceStateIntent(String intentAction) {
Intent intent = new Intent(intentAction);
intent.putExtra(ImsManager.EXTRA_PHONE_ID, mPhone.getPhoneId());
@@ -1041,9 +1159,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mHandoverCall.dispose();
clearDisconnected();
- if (mUtInterface != null) {
- mUtInterface.unregisterForSuppServiceIndication(this);
- }
mPhone.getContext().unregisterReceiver(mReceiver);
mPhone.getDefaultPhone().getDataEnabledSettings().unregisterForDataEnabledChanged(this);
mImsManagerConnector.disconnect();
@@ -1186,7 +1301,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return pendingConnection;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
CallStateException {
ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder()
@@ -1200,7 +1315,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
public synchronized Connection dial(String dialString, ImsPhone.ImsDialArgs dialArgs)
throws CallStateException {
boolean isPhoneInEcmMode = isPhoneInEcbMode();
- boolean isEmergencyNumber = mPhoneNumberUtilsProxy.isEmergencyNumber(dialString);
+ boolean isEmergencyNumber = dialArgs.isEmergency;
+ boolean isWpsCall = dialArgs.isWpsCall;
if (!shouldNumberBePlacedOnIms(isEmergencyNumber, dialString)) {
Rlog.i(LOG_TAG, "dial: shouldNumberBePlacedOnIms = false");
@@ -1243,7 +1359,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mLastDialString = dialString;
mLastDialArgs = dialArgs;
mPendingMO = new ImsPhoneConnection(mPhone, dialString, this, mForegroundCall,
- isEmergencyNumber);
+ isEmergencyNumber, isWpsCall);
if (isEmergencyNumber && dialArgs != null && dialArgs.intentExtras != null) {
Rlog.i(LOG_TAG, "dial ims emergency dialer: " + dialArgs.intentExtras.getBoolean(
TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL));
@@ -1329,11 +1445,13 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
/**
- * Caches frequently used carrier configuration items locally.
+ * Caches frequently used carrier configuration items locally and notifies ImsService of new
+ * configuration if the subId is valid (there is an active sub ID loaded).
*
- * @param subId The sub id.
+ * @param subId The sub id to use to update configuration, may be invalid if a SIM has been
+ * removed.
*/
- private void cacheCarrierConfiguration(int subId) {
+ private void updateCarrierConfiguration(int subId) {
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (carrierConfigManager == null
@@ -1344,6 +1462,20 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return;
}
+ Phone defaultPhone = getPhone().getDefaultPhone();
+ if (defaultPhone != null && defaultPhone.getIccCard() != null) {
+ IccCardConstants.State state = defaultPhone.getIccCard().getState();
+ // Bypass until PIN/PUK lock is removed as to ensure that we do not push a config down
+ // when the device is still locked. A CARRIER_CONFIG_CHANGED indication will be sent
+ // once the device moves to ready.
+ if (state != null && (!state.iccCardExist() || state.isPinLocked())) {
+ loge("cacheCarrierConfiguration: card state is not ready, skipping. State= "
+ + state);
+ mCarrierConfigLoaded = false;
+ return;
+ }
+ }
+
PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
if (carrierConfig == null) {
loge("cacheCarrierConfiguration: Empty carrier config.");
@@ -1353,6 +1485,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mCarrierConfigLoaded = true;
updateCarrierConfigCache(carrierConfig);
+ updateImsServiceConfig();
+ // Check for changes due to carrier config.
+ maybeConfigureRtpHeaderExtensions();
}
/**
@@ -1396,6 +1531,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
CarrierConfigManager.KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL);
mSupportCepOnPeer = carrierConfig.getBoolean(
CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL);
+ mSupportD2DUsingRtp = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
+ mSupportSdpForRtpHeaderExtensions = carrierConfig.getBoolean(
+ CarrierConfigManager
+ .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
+
+ if (mPhone.getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_allow_ussd_over_ims)) {
+ mUssdMethod = carrierConfig.getInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT);
+ }
String[] mappings = carrierConfig
.getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
@@ -1429,10 +1574,13 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
} else {
log("No carrier ImsReasonInfo mappings defined.");
+ if (!mImsReasonCodeMap.isEmpty()) {
+ mImsReasonCodeMap.clear();
+ }
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void handleEcmTimer(int action) {
mPhone.handleTimerInEmergencyCallbackMode(action);
}
@@ -1461,7 +1609,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
// Always unmute when initiating a new call
setMute(false);
- boolean isEmergencyCall = mPhoneNumberUtilsProxy.isEmergencyNumber(conn.getAddress());
+ boolean isEmergencyCall = conn.isEmergency();
int serviceType = isEmergencyCall
? ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL;
int callType = ImsCallProfile.getCallTypeFromVideoState(videoState);
@@ -1473,6 +1621,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
ImsCallProfile profile = mImsManager.createCallProfile(serviceType, callType);
if (conn.isAdhocConference()) {
profile.setCallExtraBoolean(ImsCallProfile.EXTRA_CONFERENCE, true);
+ // Also set value for EXTRA_CONFERENCE_DEPRECATED in case receivers are using old
+ // values.
+ profile.setCallExtraBoolean(ImsCallProfile.EXTRA_CONFERENCE_DEPRECATED, true);
}
profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
profile.setCallExtraInt(ImsCallProfile.EXTRA_RETRY_CALL_FAIL_REASON,
@@ -1493,6 +1644,27 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
cleanseInstantLetteringMessage(intentExtras.getString(
android.telecom.TelecomManager.EXTRA_CALL_SUBJECT))
);
+ profile.setCallExtra(ImsCallProfile.EXTRA_CALL_SUBJECT,
+ intentExtras.getString(TelecomManager.EXTRA_CALL_SUBJECT));
+ }
+
+ if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_PRIORITY)) {
+ profile.setCallExtraInt(ImsCallProfile.EXTRA_PRIORITY, intentExtras.getInt(
+ android.telecom.TelecomManager.EXTRA_PRIORITY));
+ }
+
+ if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_LOCATION)) {
+ profile.setCallExtraParcelable(ImsCallProfile.EXTRA_LOCATION,
+ intentExtras.getParcelable(
+ android.telecom.TelecomManager.EXTRA_LOCATION));
+ }
+
+ if (intentExtras.containsKey(
+ android.telecom.TelecomManager.EXTRA_OUTGOING_PICTURE)) {
+ String url = TelephonyLocalConnection.getCallComposerServerUrlForHandle(
+ mPhone.getSubId(), ((ParcelUuid) intentExtras.getParcelable(
+ TelecomManager.EXTRA_OUTGOING_PICTURE)).getUuid());
+ profile.setCallExtra(ImsCallProfile.EXTRA_PICTURE_URL, url);
}
if (conn.hasRttTextStream()) {
@@ -1534,7 +1706,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mOperationLocalLog.log("dialInternal exception: " + e);
conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
- retryGetImsService();
} catch (RemoteException e) {
}
}
@@ -1626,7 +1797,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void switchAfterConferenceSuccess() {
if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() +
", bg = " + mBackgroundCall.getState());
@@ -1705,6 +1876,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
ImsCall callToHold = mForegroundCall.getImsCall();
HoldSwapState oldHoldState = mHoldSwitchingState;
mHoldSwitchingState = HoldSwapState.HOLDING_TO_ANSWER_INCOMING;
+ ImsCall callExpectedToResume = mCallExpectedToResume;
+ mCallExpectedToResume = mRingingCall.getImsCall();
mForegroundCall.switchWith(mBackgroundCall);
logHoldSwapState("holdActiveCallForWaitingCall");
try {
@@ -1714,6 +1887,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
} catch (ImsException e) {
mForegroundCall.switchWith(mBackgroundCall);
mHoldSwitchingState = oldHoldState;
+ mCallExpectedToResume = callExpectedToResume;
logHoldSwapState("holdActiveCallForWaitingCall - fail");
throw new CallStateException(e.getMessage());
}
@@ -1911,6 +2085,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
fgImsCall.merge(bgImsCall);
} catch (ImsException e) {
log("conference " + e.getMessage());
+ handleConferenceFailed(foregroundConnection, backgroundConnection);
}
}
@@ -1927,13 +2102,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
try {
- fgImsCall.consultativeTransfer(bgImsCall);
+ // Per 3GPP TS 24.629 - A.2, the signalling for a consultative transfer should send the
+ // REFER on the background held call with the foreground call specified as the
+ // destination.
+ bgImsCall.consultativeTransfer(fgImsCall);
} catch (ImsException e) {
throw new CallStateException(e.getMessage());
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void
clearDisconnected() {
if (DBG) log("clearDisconnected");
@@ -2012,7 +2190,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mHandoverCall.clearDisconnected();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void
updatePhoneState() {
PhoneConstants.State oldState = mState;
@@ -2102,7 +2280,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mImsManager.setTtyMode(ttyMode);
} catch (ImsException e) {
loge("setTtyMode : " + e);
- retryGetImsService();
}
}
@@ -2121,7 +2298,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
} catch (ImsException e) {
loge("setUITTYMode : " + e);
mPhone.sendErrorResponse(onComplete, e);
- retryGetImsService();
}
}
@@ -2309,7 +2485,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
} catch (ImsException e) {
loge("sendUSSD : " + e);
mPhone.sendErrorResponse(response, e);
- retryGetImsService();
}
}
@@ -2324,7 +2499,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) {
for (ImsPhoneConnection conn : mConnections) {
if (conn.getImsCall() == imsCall) {
@@ -2334,7 +2509,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return null;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private synchronized void removeConnection(ImsPhoneConnection conn) {
mConnections.remove(conn);
// If not emergency call is remaining, notify emergency call registrants
@@ -2358,7 +2533,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private synchronized void addConnection(ImsPhoneConnection conn) {
mConnections.add(conn);
if (conn.isEmergency()) {
@@ -2367,7 +2542,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause);
// This method is called on onCallUpdate() where there is not necessarily a call state
@@ -2377,7 +2552,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
processCallStateChange(imsCall, state, cause, false /* do not ignore state update */);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause,
boolean ignoreState) {
if (DBG) {
@@ -2404,6 +2579,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (ignoreState) {
conn.updateAddressDisplay(imsCall);
conn.updateExtras(imsCall);
+ // Some devices will change the audio direction between major call state changes, so we
+ // need to check whether to start or stop ringback
+ conn.maybeChangeRingbackState();
maybeSetVideoCallProvider(conn, imsCall);
return;
@@ -2415,6 +2593,19 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
//detach the disconnected connections
conn.getCall().detach(conn);
removeConnection(conn);
+
+ // remove conference participants from the cached list when call is disconnected
+ List<ConferenceParticipant> cpList = imsCall.getConferenceParticipants();
+ if (cpList != null) {
+ for (ConferenceParticipant cp : cpList) {
+ String number = ConferenceParticipant.getParticipantAddress(cp.getHandle(),
+ getCountryIso()).getSchemeSpecificPart();
+ if (!TextUtils.isEmpty(number)) {
+ String formattedNumber = getFormattedPhoneNumber(number);
+ mPhoneNumAndConnTime.remove(formattedNumber);
+ }
+ }
+ }
}
if (changed) {
@@ -2570,6 +2761,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return DisconnectCause.TIMED_OUT;
case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
+ case ImsReasonInfo.CODE_RADIO_OFF:
return DisconnectCause.POWER_OFF;
case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
@@ -2693,7 +2885,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
* Before dialing pending MO request, check for the Emergency Callback mode.
* If device is in Emergency callback mode, then exit the mode before dialing pending MO.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void dialPendingMO() {
boolean isPhoneInEcmMode = isPhoneInEcbMode();
boolean isEmergencyNumber = mPendingMO.isEmergency();
@@ -2707,9 +2899,18 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
/**
* Listen to the IMS call state change
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsCall.Listener mImsCallListener = new ImsCall.Listener() {
@Override
+ public void onCallInitiating(ImsCall imsCall) {
+ if (DBG) log("onCallInitiating");
+ mPendingMO = null;
+ processCallStateChange(imsCall, ImsPhoneCall.State.DIALING,
+ DisconnectCause.NOT_DISCONNECTED, true);
+ mMetrics.writeOnImsCallInitiating(mPhone.getPhoneId(), imsCall.getCallSession());
+ }
+
+ @Override
public void onCallProgressing(ImsCall imsCall) {
if (DBG) log("onCallProgressing");
@@ -2781,6 +2982,11 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
+ int eccCategory = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+ if (imsCall != null && imsCall.getCallProfile() != null) {
+ eccCategory = imsCall.getCallProfile().getEmergencyServiceCategories();
+ }
+
if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
// If we put a call on hold to answer an incoming call, we should reset the
// variables that keep track of the switch here.
@@ -2795,19 +3001,45 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (mPendingMO != null) {
// To initiate dialing circuit-switched call
if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
- && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE
- && mRingingCall.getState() == ImsPhoneCall.State.IDLE) {
+ && mRingingCall.getState() == ImsPhoneCall.State.IDLE
+ && isForegroundHigherPriority()) {
mForegroundCall.detach(mPendingMO);
removeConnection(mPendingMO);
mPendingMO.finalize();
mPendingMO = null;
- mPhone.initiateSilentRedial();
+ // if we need to perform CSFB of call, hang up any background call
+ // before redialing if it is a lower priority.
+ if (mBackgroundCall.getState().isAlive()) {
+ try {
+ hangup(mBackgroundCall);
+ mPendingSilentRedialInfo = new Pair<>(reasonInfo.getExtraCode() ==
+ ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY, eccCategory);
+ } catch (CallStateException ex) {
+ mPendingSilentRedialInfo = null;
+ }
+ } else {
+ updatePhoneState();
+ mPhone.initiateSilentRedial(reasonInfo.getExtraCode() ==
+ ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY, eccCategory);
+ }
return;
} else {
sendCallStartFailedDisconnect(imsCall, reasonInfo);
}
mMetrics.writeOnImsCallStartFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
reasonInfo);
+ } else if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+ && mForegroundCall.getState() == ImsPhoneCall.State.ALERTING) {
+ if (DBG) log("onCallStartFailed: Initiated call by silent redial"
+ + " under ALERTING state.");
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ mForegroundCall.detach(conn);
+ removeConnection(conn);
+ }
+ updatePhoneState();
+ mPhone.initiateSilentRedial(reasonInfo.getExtraCode() ==
+ ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY, eccCategory);
}
}
@@ -2882,9 +3114,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
String callId = imsCall.getSession().getCallId();
- EmergencyNumberTracker emergencyNumberTracker = conn.getEmergencyNumberTracker();
+ EmergencyNumberTracker emergencyNumberTracker = null;
+ EmergencyNumber num = null;
+
+ if (conn != null) {
+ emergencyNumberTracker = conn.getEmergencyNumberTracker();
+ num = conn.getEmergencyNumberInfo();
+ }
+
mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
- reasonInfo, mCallQualityMetrics.get(callId), conn.getEmergencyNumberInfo(),
+ reasonInfo, mCallQualityMetrics.get(callId), num,
getNetworkCountryIso(), emergencyNumberTracker != null
? emergencyNumberTracker.getEmergencyNumberDbVersion()
: TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION);
@@ -2899,6 +3138,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (conn != null) {
conn.setPreciseDisconnectCause(getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
+ conn.setImsReasonInfo(reasonInfo);
}
if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
@@ -2927,10 +3167,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
}
- if (conn != null) {
- conn.onRemoteDisconnect(reasonInfo.getExtraMessage());
- }
-
if (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
if (DBG) {
log("onCallTerminated: Call terminated in the midst of Switching " +
@@ -2989,11 +3225,15 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (mShouldUpdateImsConfigOnDisconnect) {
// Ensure we update the IMS config when the call is disconnected; we delayed this
// because a video call was paused.
- if (mImsManager != null) {
- mImsManager.updateImsServiceConfig(true);
- }
+ updateImsServiceConfig();
mShouldUpdateImsConfigOnDisconnect = false;
}
+
+ if (mPendingSilentRedialInfo != null) {
+ mPhone.initiateSilentRedial(mPendingSilentRedialInfo.first,
+ mPendingSilentRedialInfo.second);
+ mPendingSilentRedialInfo = null;
+ }
}
@Override
@@ -3392,12 +3632,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
- ImsPhoneCall imsPhoneCall = conn.getCall();
- if (imsPhoneCall != null) {
- // We might be playing ringback on the handover connection; we should stop
- // playing it at this point (otherwise it could play indefinitely).
- imsPhoneCall.maybeStopRingback();
- }
if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
if (isHandoverToWifi) {
removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
@@ -3540,6 +3774,15 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mPhone.notifySuppServiceFailed(Phone.SuppService.TRANSFER);
}
+ @Override
+ public void onCallSessionDtmfReceived(ImsCall imsCall, char digit) {
+ log("onCallSessionDtmfReceived digit=" + digit);
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ conn.receivedDtmfDigit(digit);
+ }
+ }
+
/**
* Handles a change to the multiparty state for an {@code ImsCall}. Notifies the associated
* {@link ImsPhoneConnection} of the change.
@@ -3555,6 +3798,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
conn.updateMultipartyState(isMultiParty);
+ mPhone.getVoiceCallSessionStats().onMultipartyChange(conn, isMultiParty);
}
}
@@ -3573,6 +3817,31 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
cqm.saveCallQuality(callQuality);
mCallQualityMetrics.put(callId, cqm);
+
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ Bundle report = new Bundle();
+ report.putParcelable(android.telecom.Connection.EXTRA_CALL_QUALITY_REPORT,
+ callQuality);
+ conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_QUALITY_REPORT,
+ report);
+ }
+ }
+
+ /**
+ * Handles reception of RTP header extension data from the network.
+ * @param imsCall The ImsCall the data was received on.
+ * @param rtpHeaderExtensionData The RTP extension data received.
+ */
+ @Override
+ public void onCallSessionRtpHeaderExtensionsReceived(ImsCall imsCall,
+ @NonNull Set<RtpHeaderExtension> rtpHeaderExtensionData) {
+ log("onCallSessionRtpHeaderExtensionsReceived numExtensions="
+ + rtpHeaderExtensionData.size());
+ ImsPhoneConnection conn = findConnection(imsCall);
+ if (conn != null) {
+ conn.receivedRtpHeaderExtensions(rtpHeaderExtensionData);
+ }
}
};
@@ -3600,7 +3869,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (mUssdSession != null) {
if (DBG) log("mUssdSession is not null");
// To initiate sending Ussd under circuit-switched call
- if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED) {
+ if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
+ && mUssdMethod != USSD_OVER_IMS_ONLY) {
mUssdSession = null;
mPhone.getPendingMmiCodes().clear();
mPhone.initiateSilentRedial();
@@ -3670,21 +3940,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
};
- private ImsConfigListener.Stub mImsConfigListener = new ImsConfigListener.Stub() {
+ private final ImsManager.ImsStatsCallback mImsStatsCallback =
+ new ImsManager.ImsStatsCallback() {
@Override
- public void onGetFeatureResponse(int feature, int network, int value, int status) {}
-
- @Override
- public void onSetFeatureResponse(int feature, int network, int value, int status) {
- mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), feature, network, value);
+ public void onEnabledMmTelCapabilitiesChanged(int capability, int regTech,
+ boolean isEnabled) {
+ int enabledVal = isEnabled ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
+ : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
+ mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), capability, regTech, enabledVal);
+ mPhone.getImsStats().onSetFeatureResponse(capability, regTech, enabledVal);
}
-
- @Override
- public void onGetVideoQuality(int status, int quality) {}
-
- @Override
- public void onSetVideoQuality(int status) {}
-
};
private final ProvisioningManager.Callback mConfigCallback =
@@ -3698,7 +3963,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
|| item == ImsConfig.ConfigConstants.LVC_SETTING_ENABLED)) {
// Update Ims Service state to make sure updated provisioning values take effect
// immediately.
- mImsManager.updateImsServiceConfig(true);
+ updateImsServiceConfig();
}
}
@@ -3734,9 +3999,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
callState = Call.State.DIALING;
}
int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
- if (conn != null) {
- conn.onRemoteDisconnect(reasonInfo.getExtraMessage());
- }
processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
@@ -3748,13 +4010,13 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mPhone.notifyImsReason(reasonInfo);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsUtInterface getUtInterface() throws ImsException {
if (mImsManager == null) {
throw getImsManagerIsNullException();
}
- ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration();
+ ImsUtInterface ut = mImsManager.createOrGetSupplementaryServiceConfiguration();
return ut;
}
@@ -3804,6 +4066,10 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mSrvccState = state;
if (mSrvccState == Call.SrvccState.COMPLETED) {
+ // If the dialing call had ringback, ensure it stops now, otherwise it'll keep playing
+ // afer the SRVCC completes.
+ mForegroundCall.maybeStopRingback();
+
resetState();
transferHandoverConnections(mForegroundCall);
transferHandoverConnections(mBackgroundCall);
@@ -3815,6 +4081,12 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private void resetState() {
mIsInEmergencyCall = false;
mPhone.setEcmCanceledForEmergency(false);
+ mHoldSwitchingState = HoldSwapState.INACTIVE;
+ }
+
+ @VisibleForTesting
+ public boolean isHoldOrSwapInProgress() {
+ return mHoldSwitchingState != HoldSwapState.INACTIVE;
}
//****** Overridden from Handler
@@ -4046,7 +4318,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
// the only thing we can do here is splitting the usage into half rx and half tx.
// Uid -1 indicates this is for the overall device data usage.
vtDataUsageSnapshot.combineValues(new NetworkStats.Entry(
- NetworkStats.IFACE_VT, -1, NetworkStats.SET_FOREGROUND,
+ getVtInterface(), -1, NetworkStats.SET_FOREGROUND,
NetworkStats.TAG_NONE, NetworkStats.METERED_YES, isRoaming,
NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0));
mVtDataUsageSnapshot = vtDataUsageSnapshot;
@@ -4069,23 +4341,32 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
// Since the modem only reports the total vt data usage rather than rx/tx separately,
// the only thing we can do here is splitting the usage into half rx and half tx.
vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry(
- NetworkStats.IFACE_VT, mDefaultDialerUid.get(),
+ getVtInterface(), mDefaultDialerUid.get(),
NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, NetworkStats.METERED_YES,
isRoaming, NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0));
mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot;
}
- @UnsupportedAppUsage
+ @VisibleForTesting(visibility = PRIVATE)
+ public String getVtInterface() {
+ return NetworkStats.IFACE_VT + mPhone.getSubId();
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void log(String msg) {
Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void loge(String msg) {
Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
}
+ void logw(String msg) {
+ Rlog.w(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
+ }
+
void logi(String msg) {
Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
}
@@ -4170,6 +4451,15 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
pw.println(" mCallQualityMetricsHistory=" + mCallQualityMetricsHistory);
pw.println(" mIsConferenceEventPackageHandlingEnabled=" + mIsConferenceEventPackageEnabled);
pw.println(" mSupportCepOnPeer=" + mSupportCepOnPeer);
+ if (mConfig != null) {
+ pw.print(" isDeviceToDeviceCommsSupported= " + mConfig.isD2DCommunicationSupported);
+ pw.println("(forceEnabled=" + mDeviceToDeviceForceEnabled + ")");
+ if (mConfig.isD2DCommunicationSupported) {
+ pw.println(" mSupportD2DUsingRtp= " + mSupportD2DUsingRtp);
+ pw.println(" mSupportSdpForRtpHeaderExtensions= "
+ + mSupportSdpForRtpHeaderExtensions);
+ }
+ }
pw.println(" Event Log:");
pw.increaseIndent();
mOperationLocalLog.dump(pw);
@@ -4197,7 +4487,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
protected void handlePollCalls(AsyncResult ar) {
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
/* package */
ImsEcbm getEcbmInterface() throws ImsException {
if (mImsManager == null) {
@@ -4208,24 +4498,6 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return ecbm;
}
- /* package */
- ImsMultiEndpoint getMultiEndpointInterface() throws ImsException {
- if (mImsManager == null) {
- throw getImsManagerIsNullException();
- }
-
- try {
- return mImsManager.getMultiEndpointInterface();
- } catch (ImsException e) {
- if (e.getCode() == ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED) {
- return null;
- } else {
- throw e;
- }
-
- }
- }
-
public boolean isInEmergencyCall() {
return mIsInEmergencyCall;
}
@@ -4243,14 +4515,23 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
}
- public boolean isVolteEnabled() {
+ /**
+ * @return {@code true} if voice over cellular is enabled.
+ */
+ public boolean isVoiceOverCellularImsEnabled() {
return isImsCapabilityInCacheAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
- ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE)
+ || isImsCapabilityInCacheAvailable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_NR);
}
public boolean isVowifiEnabled() {
return isImsCapabilityInCacheAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
- ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
+ || isImsCapabilityInCacheAvailable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
}
public boolean isVideoCallEnabled() {
@@ -4285,16 +4566,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
}
- private void retryGetImsService() {
- // The binder connection is already up. Do not try to get it again.
- if (mImsManager.isServiceAvailable()) {
- return;
- }
-
- mImsManagerConnector.connect();
- }
-
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
throws RemoteException {
IImsVideoCallProvider imsVideoCallProvider =
@@ -4543,9 +4815,17 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
&& reason != DataEnabledSettings.REASON_REGISTERED && mCarrierConfigLoaded) {
// This will call into updateVideoCallFeatureValue and eventually all clients will be
// asynchronously notified that the availability of VT over LTE has changed.
- if (mImsManager != null) {
- mImsManager.updateImsServiceConfig(true);
- }
+ updateImsServiceConfig();
+ }
+ }
+
+ /**
+ * If the ImsService is currently connected and we have loaded the carrier config, proceed to
+ * trigger the update of the configuration sent to the ImsService.
+ */
+ private void updateImsServiceConfig() {
+ if (mImsManager != null && mCarrierConfigLoaded) {
+ mImsManager.updateImsServiceConfig();
}
}
@@ -4780,7 +5060,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (DBG) log(sb.toString());
- String logMessage = "handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
+ String logMessage = "handleFeatureCapabilityChanged: isVolteEnabled="
+ + isVoiceOverCellularImsEnabled()
+ ", isVideoCallEnabled=" + isVideoCallEnabled()
+ ", isVowifiEnabled=" + isVowifiEnabled()
+ ", isUtEnabled=" + isUtEnabled();
@@ -4791,8 +5072,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mPhone.onFeatureCapabilityChanged();
- mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), getImsRegistrationTech(),
- mMmTelCapabilities);
+ int regTech = getImsRegistrationTech();
+ mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), regTech, mMmTelCapabilities);
+ mPhone.getImsStats().onImsCapabilitiesChanged(regTech, mMmTelCapabilities);
}
@VisibleForTesting
@@ -4896,4 +5178,39 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
public ArrayList<ImsPhoneConnection> getConnections() {
return mConnections;
}
+
+ /**
+ * Set up static configuration from package/services/Telephony's config.xml.
+ * @param config the config.
+ */
+ public void setConfig(@NonNull Config config) {
+ mConfig = config;
+ }
+
+ private void handleConferenceFailed(ImsPhoneConnection fgConnection,
+ ImsPhoneConnection bgConnection) {
+ if (fgConnection != null) {
+ fgConnection.handleMergeComplete();
+ }
+ if (bgConnection != null) {
+ bgConnection.handleMergeComplete();
+ }
+ mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
+ }
+
+ /**
+ * Calculate whether CSFB or not with fg call type and bg call type.
+ * @return {@code true} if bg call is not alive or fg call has higher score than bg call.
+ */
+ private boolean isForegroundHigherPriority() {
+ if (!mBackgroundCall.getState().isAlive()) {
+ return true;
+ }
+ ImsPhoneConnection fgConnection = mForegroundCall.getFirstConnection();
+ ImsPhoneConnection bgConnection = mBackgroundCall.getFirstConnection();
+ if (fgConnection.getCallPriority() > bgConnection.getCallPriority()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 9dcb06f6c1..1371c92db9 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -24,16 +24,19 @@ import android.os.Message;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
import android.telephony.SignalThresholdInfo;
+import android.telephony.TelephonyManager;
import android.telephony.data.DataProfile;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.RadioCapability;
import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
/**
* Volte doesn't need CommandsInterface. The class does nothing but made to work
@@ -282,8 +285,9 @@ class ImsPhoneCommandInterface extends BaseCommands implements CommandsInterface
@Override
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- Message result) {
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, Message result) {
}
@Override
@@ -455,6 +459,15 @@ class ImsPhoneCommandInterface extends BaseCommands implements CommandsInterface
}
@Override
+ public void setAllowedNetworkTypesBitmap(
+ @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message response) {
+ }
+
+ @Override
+ public void getAllowedNetworkTypesBitmap(Message response) {
+ }
+
+ @Override
public void setLocationUpdates(boolean enable, Message response) {
}
@@ -663,4 +676,20 @@ class ImsPhoneCommandInterface extends BaseCommands implements CommandsInterface
@Override
public void stopNattKeepalive(int sessionHandle, Message result) {
}
+
+ @Override
+ public void allocatePduSessionId(Message result) {
+ }
+
+ @Override
+ public void releasePduSessionId(Message result, int pduSessionId) {
+ }
+
+ @Override
+ public void startHandover(Message result, int callId) {
+ }
+
+ @Override
+ public void cancelHandover(Message result, int callId) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 7d4a36f0ae..8c4e2e018c 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -16,10 +16,13 @@
package com.android.internal.telephony.imsphone;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -35,13 +38,18 @@ import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.ims.AudioCodecAttributes;
import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.RtpHeaderExtensionType;
import android.text.TextUtils;
import com.android.ims.ImsCall;
import com.android.ims.ImsException;
import com.android.ims.internal.ImsVideoCallProviderWrapper;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
@@ -51,7 +59,9 @@ import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.telephony.Rlog;
+import java.util.Collections;
import java.util.Objects;
+import java.util.Set;
/**
* {@hide}
@@ -64,16 +74,16 @@ public class ImsPhoneConnection extends Connection implements
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsPhoneCallTracker mOwner;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsPhoneCall mParent;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsCall mImsCall;
private Bundle mExtras = new Bundle();
private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mDisconnected;
/*
@@ -101,6 +111,8 @@ public class ImsPhoneConnection extends Connection implements
private boolean mIsEmergency = false;
+ private boolean mIsWpsCall = false;
+
/**
* Used to indicate that video state changes detected by
* {@link #updateMediaCapabilities(ImsCall)} should be ignored. When a video state change from
@@ -124,8 +136,6 @@ public class ImsPhoneConnection extends Connection implements
*/
private boolean mIsMergeInProcess = false;
- private String mVendorCause;
-
/**
* Used as an override to determine whether video is locally available for this call.
* This allows video availability to be overridden in the case that the modem says video is
@@ -134,6 +144,12 @@ public class ImsPhoneConnection extends Connection implements
*/
private boolean mIsLocalVideoCapable = true;
+ /**
+ * When the call is in a disconnected, state, will be set to the {@link ImsReasonInfo}
+ * associated with the disconnection, if known.
+ */
+ private ImsReasonInfo mImsReasonInfo;
+
//***** Event Constants
private static final int EVENT_DTMF_DONE = 1;
private static final int EVENT_PAUSE_DONE = 2;
@@ -226,7 +242,7 @@ public class ImsPhoneConnection extends Connection implements
/** This is an MO call, created when dialing */
public ImsPhoneConnection(Phone phone, String dialString, ImsPhoneCallTracker ct,
- ImsPhoneCall parent, boolean isEmergency) {
+ ImsPhoneCall parent, boolean isEmergency, boolean isWpsCall) {
super(PhoneConstants.PHONE_TYPE_IMS);
createWakeLock(phone.getContext());
acquireWakeLock();
@@ -256,6 +272,8 @@ public class ImsPhoneConnection extends Connection implements
setEmergencyCallInfo(mOwner);
}
+ mIsWpsCall = isWpsCall;
+
fetchDtmfToneDelay(phone);
if (phone.getContext().getResources().getBoolean(
@@ -294,6 +312,10 @@ public class ImsPhoneConnection extends Connection implements
}
}
+ @VisibleForTesting
+ public void setTelephonyMetrics(TelephonyMetrics tm) {
+ mMetrics = tm;
+ }
public void dispose() {
}
@@ -349,7 +371,7 @@ public class ImsPhoneConnection extends Connection implements
return mDialString;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public ImsPhoneCall getCall() {
return mParent;
@@ -392,10 +414,10 @@ public class ImsPhoneConnection extends Connection implements
@Override
public String getVendorDisconnectCause() {
- return mVendorCause;
+ return null;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ImsPhoneCallTracker getOwner () {
return mOwner;
}
@@ -515,7 +537,6 @@ public class ImsPhoneConnection extends Connection implements
void
onHangupLocal() {
mCause = DisconnectCause.LOCAL;
- mVendorCause = null;
}
/** Called when the connection has been disconnected */
@@ -528,7 +549,7 @@ public class ImsPhoneConnection extends Connection implements
return onDisconnect();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean onDisconnect() {
boolean changed = false;
@@ -562,11 +583,6 @@ public class ImsPhoneConnection extends Connection implements
return changed;
}
- void
- onRemoteDisconnect(String vendorCause) {
- this.mVendorCause = vendorCause;
- }
-
/**
* An incoming or outgoing call has connected
*/
@@ -711,14 +727,14 @@ public class ImsPhoneConnection extends Connection implements
notifyPostDialListeners();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void
createWakeLock(Context context) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void
acquireWakeLock() {
Rlog.d(LOG_TAG, "acquireWakeLock");
@@ -761,7 +777,7 @@ public class ImsPhoneConnection extends Connection implements
return null;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public synchronized boolean isMultiparty() {
return mImsCall != null && mImsCall.isMultiparty();
@@ -801,7 +817,7 @@ public class ImsPhoneConnection extends Connection implements
* @return {@code true} if the {@link ImsPhoneConnection} or its media capabilities have been
* changed, and {@code false} otherwise.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean update(ImsCall imsCall, ImsPhoneCall.State state) {
if (state == ImsPhoneCall.State.ACTIVE) {
// If the state of the call is active, but there is a pending request to the RIL to hold
@@ -838,6 +854,14 @@ public class ImsPhoneConnection extends Connection implements
return updateParent || updateAddressDisplay || updateMediaCapabilities || updateExtras;
}
+ /**
+ * Re-evaluate whether ringback should be playing.
+ */
+ public void maybeChangeRingbackState() {
+ Rlog.i(LOG_TAG, "maybeChangeRingbackState");
+ mParent.maybeChangeRingbackState(mImsCall);
+ }
+
@Override
public int getPreciseDisconnectCause() {
return mPreciseDisconnectCause;
@@ -1068,12 +1092,43 @@ public class ImsPhoneConnection extends Connection implements
}
}
+ boolean mediaAttributesChanged = false;
+
// Metrics for audio codec
if (localCallProfile != null
&& localCallProfile.mMediaProfile.mAudioQuality != mAudioCodec) {
mAudioCodec = localCallProfile.mMediaProfile.mAudioQuality;
mMetrics.writeAudioCodecIms(mOwner.mPhone.getPhoneId(), imsCall.getCallSession());
mOwner.getPhone().getVoiceCallSessionStats().onAudioCodecChanged(this, mAudioCodec);
+ changed = true;
+ mediaAttributesChanged = true;
+ }
+
+ if (localCallProfile != null
+ && localCallProfile.mMediaProfile.getAudioCodecAttributes() != null) {
+ AudioCodecAttributes audioCodecAttributes =
+ localCallProfile.mMediaProfile.getAudioCodecAttributes();
+
+ if (Math.abs(mAudioCodecBitrateKbps
+ - audioCodecAttributes.getBitrateRangeKbps().getUpper()) > THRESHOLD) {
+ mAudioCodecBitrateKbps = audioCodecAttributes.getBitrateRangeKbps().getUpper();
+ changed = true;
+ mediaAttributesChanged = true;
+ }
+ if (Math.abs(mAudioCodecBandwidthKhz
+ - audioCodecAttributes.getBandwidthRangeKhz().getUpper()) > THRESHOLD) {
+ mAudioCodecBandwidthKhz =
+ audioCodecAttributes.getBandwidthRangeKhz().getUpper();
+ changed = true;
+ mediaAttributesChanged = true;
+ }
+ }
+
+ if (mediaAttributesChanged) {
+ Rlog.i(LOG_TAG, "updateMediaCapabilities: mediate attributes changed: codec = "
+ + mAudioCodec + ", bitRate=" + mAudioCodecBitrateKbps + ", bandwidth="
+ + mAudioCodecBandwidthKhz);
+ notifyMediaAttributesChanged();
}
int newAudioQuality =
@@ -1094,6 +1149,7 @@ public class ImsPhoneConnection extends Connection implements
mImsVideoCallProviderWrapper.onVideoStateChanged(newVideoState);
}
setVideoState(newVideoState);
+ mOwner.getPhone().getVoiceCallSessionStats().onVideoStateChange(this, newVideoState);
}
@@ -1224,6 +1280,9 @@ public class ImsPhoneConnection extends Connection implements
* @param extras The ImsCallProfile extras.
*/
private void updateImsCallRatFromExtras(Bundle extras) {
+ if (extras == null) {
+ return;
+ }
if (extras.containsKey(ImsCallProfile.EXTRA_CALL_NETWORK_TYPE)
|| extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE)
|| extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE_ALT)) {
@@ -1240,11 +1299,23 @@ public class ImsPhoneConnection extends Connection implements
}
private void updateEmergencyCallFromExtras(Bundle extras) {
+ if (extras == null) {
+ return;
+ }
if (extras.getBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL)) {
setIsNetworkIdentifiedEmergencyCall(true);
}
}
+ private void updateForwardedNumberFromExtras(Bundle extras) {
+ if (extras == null) {
+ return;
+ }
+ if (extras.containsKey(ImsCallProfile.EXTRA_FORWARDED_NUMBER)) {
+ mForwardedNumber = extras.getStringArrayList(ImsCallProfile.EXTRA_FORWARDED_NUMBER);
+ }
+ }
+
/**
* Check for a change in call extras of {@link ImsCall}, and
* update the {@link ImsPhoneConnection} accordingly.
@@ -1267,8 +1338,11 @@ public class ImsPhoneConnection extends Connection implements
if (changed) {
updateImsCallRatFromExtras(extras);
updateEmergencyCallFromExtras(extras);
+ updateForwardedNumberFromExtras(extras);
mExtras.clear();
- mExtras.putAll(extras);
+ if (extras != null) {
+ mExtras.putAll(extras);
+ }
setConnectionExtras(mExtras);
}
return changed;
@@ -1374,6 +1448,18 @@ public class ImsPhoneConnection extends Connection implements
return mIsEmergency;
}
+ protected boolean isWpsCall() {
+ return mIsWpsCall;
+ }
+
+ /**
+ * Indicates whether current phone connection is cross sim calling or not
+ * @return boolean: true if cross sim calling, false otherwise
+ */
+ public boolean isCrossSimCall() {
+ return mImsCall != null && mImsCall.isCrossSimCall();
+ }
+
/**
* Handles notifications from the {@link ImsVideoCallProviderWrapper} of session modification
* responses received.
@@ -1500,6 +1586,46 @@ public class ImsPhoneConnection extends Connection implements
}
/**
+ * Sends RTP header extension data.
+ * @param rtpHeaderExtensions the RTP header extension data to send.
+ */
+ public void sendRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
+ if (mImsCall == null) {
+ Rlog.w(LOG_TAG, "sendRtpHeaderExtensions: Not an IMS call");
+ return;
+ }
+ Rlog.i(LOG_TAG, "sendRtpHeaderExtensions: numExtensions = " + rtpHeaderExtensions.size());
+ mImsCall.sendRtpHeaderExtensions(rtpHeaderExtensions);
+ }
+
+ /**
+ * @return the RTP header extensions accepted for this call.
+ */
+ public Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions() {
+ if (mImsCall == null || mImsCall.getCallProfile() == null) {
+ return Collections.EMPTY_SET;
+ }
+ return mImsCall.getCallProfile().getAcceptedRtpHeaderExtensionTypes();
+ }
+
+ /**
+ * For a connection being disconnected, sets the {@link ImsReasonInfo} which describes the
+ * reason for the disconnection.
+ * @param imsReasonInfo The IMS reason info.
+ */
+ public void setImsReasonInfo(@Nullable ImsReasonInfo imsReasonInfo) {
+ mImsReasonInfo = imsReasonInfo;
+ }
+
+ /**
+ * @return the {@link ImsReasonInfo} describing why this connection disconnected, or
+ * {@code null} otherwise.
+ */
+ public @Nullable ImsReasonInfo getImsReasonInfo() {
+ return mImsReasonInfo;
+ }
+
+ /**
* Converts an {@link ImsCallProfile} verification status to a
* {@link android.telecom.Connection} verification status.
* @param verificationStatus The {@link ImsCallProfile} verification status.
@@ -1518,4 +1644,16 @@ public class ImsPhoneConnection extends Connection implements
return android.telecom.Connection.VERIFICATION_STATUS_NOT_VERIFIED;
}
}
+
+ /**
+ * The priority of the call to the user. A higher number means higher priority.
+ */
+ protected int getCallPriority() {
+ if (isEmergency()) {
+ return 2;
+ } else if (isWpsCall()) {
+ return 1;
+ }
+ return 0;
+ }
}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index e2b1534351..359079d560 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -16,6 +16,10 @@
package com.android.internal.telephony.imsphone;
+import static android.telephony.CarrierConfigManager.USSD_OVER_CS_ONLY;
+import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED;
+import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
+import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_PREFERRED;
import static android.telephony.ServiceState.STATE_IN_SERVICE;
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
@@ -33,10 +37,14 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallForwardInfo;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsSsData;
@@ -47,6 +55,7 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import com.android.ims.ImsException;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandException;
@@ -175,9 +184,9 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private ImsPhone mPhone;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Context mContext;
private IccRecords mIccRecords;
@@ -250,8 +259,9 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
* Please see flow chart in TS 22.030 6.5.3.2
*/
- @UnsupportedAppUsage
- static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone) {
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @VisibleForTesting
+ public static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone) {
return newFromDialString(dialString, phone, null);
}
@@ -260,10 +270,14 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
Matcher m;
ImsPhoneMmiCode ret = null;
- if (phone.getDefaultPhone().getServiceState().getVoiceRoaming()
- && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) {
+ if ((phone.getDefaultPhone().getServiceState().getVoiceRoaming()
+ && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming())
+ || (isEmergencyNumber(phone, dialString)
+ && isCarrierSupportCallerIdVerticalServiceCodes(phone))) {
/* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString
- so that it can be processed by the matcher and code below
+ so that it can be processed by the matcher and code below. This can be triggered if
+ the dialing string is an emergency number and carrier supports caller ID vertical
+ service codes *67, *82.
*/
dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString);
}
@@ -309,13 +323,13 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
ret = new ImsPhoneMmiCode(phone);
ret.mDialingNumber = dialString;
}
-
return ret;
}
private static String convertCdmaMmiCodesTo3gppMmiCodes(String dialString) {
Matcher m;
m = sPatternCdmaMmiCodeWhileRoaming.matcher(dialString);
+
if (m.matches()) {
String serviceCode = makeEmptyNull(m.group(MATCH_GROUP_CDMA_MMI_CODE_SERVICE_CODE));
String prefix = m.group(MATCH_GROUP_CDMA_MMI_CODE_NUMBER_PREFIX);
@@ -332,8 +346,8 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
return dialString;
}
- static ImsPhoneMmiCode
- newNetworkInitiatedUssd(String ussdMessage, boolean isUssdRequest, ImsPhone phone) {
+ public static ImsPhoneMmiCode newNetworkInitiatedUssd(String ussdMessage,
+ boolean isUssdRequest, ImsPhone phone) {
ImsPhoneMmiCode ret;
ret = new ImsPhoneMmiCode(phone);
@@ -390,7 +404,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
}
/** returns true of the string is empty or null */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static boolean
isEmptyOrNull(CharSequence s) {
return s == null || (s.length() == 0);
@@ -622,7 +636,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
//***** Instance Methods
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String getDialingNumber() {
return mDialingNumber;
}
@@ -664,8 +678,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
if (dialString.length() == 0) {
return false;
}
-
- if (PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), dialString)) {
+ if (isEmergencyNumber(phone, dialString)) {
return false;
} else {
return isShortCodeUSSD(dialString, phone);
@@ -715,18 +728,51 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
* In temporary mode, to invoke CLIR for a single call enter:
* " # 31 # [called number] SEND "
*/
- @UnsupportedAppUsage
- boolean
- isTemporaryModeCLIR() {
- return mSc != null && mSc.equals(SC_CLIR) && mDialingNumber != null
- && (isActivate() || isDeactivate());
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @VisibleForTesting
+ public boolean isTemporaryModeCLIR() {
+ return mSc != null && mSc.equals(SC_CLIR)
+ && mDialingNumber != null && (isActivate() || isDeactivate());
+ }
+
+ /**
+ * Checks if the dialing string is an emergency number.
+ */
+ @VisibleForTesting
+ public static boolean isEmergencyNumber(Phone phone, String dialString) {
+ try {
+ TelephonyManager tm = phone.getContext().getSystemService(TelephonyManager.class);
+ return tm.isEmergencyNumber(dialString);
+ } catch (RuntimeException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if carrier supports caller id vertical service codes by checking with
+ * {@link CarrierConfigManager#KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL}.
+ */
+ @VisibleForTesting
+ public static boolean isCarrierSupportCallerIdVerticalServiceCodes(Phone phone) {
+ CarrierConfigManager configManager = phone.getContext().getSystemService(
+ CarrierConfigManager.class);
+ PersistableBundle b = null;
+ if (configManager != null) {
+ // If an invalid subId is used, this bundle will contain default values.
+ b = configManager.getConfigForSubId(phone.getSubId());
+ }
+ if (b != null) {
+ return b == null ? false : b.getBoolean(CarrierConfigManager
+ .KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL);
+ }
+ return false;
}
/**
* returns CommandsInterface.CLIR_*
* See also isTemporaryModeCLIR()
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int
getCLIRMode() {
if (mSc != null && mSc.equals(SC_CLIR)) {
@@ -740,12 +786,12 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
return CommandsInterface.CLIR_DEFAULT;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean isActivate() {
return mAction != null && mAction.equals(ACTION_ACTIVATE);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean isDeactivate() {
return mAction != null && mAction.equals(ACTION_DEACTIVATE);
}
@@ -754,12 +800,12 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
return mAction != null && mAction.equals(ACTION_INTERROGATE);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean isRegister() {
return mAction != null && mAction.equals(ACTION_REGISTER);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean isErasure() {
return mAction != null && mAction.equals(ACTION_ERASURE);
}
@@ -777,7 +823,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
return mIsUssdRequest;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
boolean
isSupportedOverImsPhone() {
if (isShortCode()) return true;
@@ -837,7 +883,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
}
/** Process a MMI code or short code...anything that isn't a dialing number */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void
processCode () throws CallStateException {
try {
@@ -924,24 +970,23 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
if (isActivate()
&& !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) {
try {
- mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_INVOCATION,
+ mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_INVOCATION,
obtainMessage(EVENT_SET_COMPLETE, this));
- } catch (ImsException e) {
+ } catch (Exception e) {
Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
}
} else if (isDeactivate()
&& !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) {
try {
- mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_SUPPRESSION,
+ mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION,
obtainMessage(EVENT_SET_COMPLETE, this));
- } catch (ImsException e) {
+ } catch (Exception e) {
Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
}
} else if (isInterrogate()) {
try {
- mPhone.mCT.getUtInterface()
- .queryCLIR(obtainMessage(EVENT_GET_CLIR_COMPLETE, this));
- } catch (ImsException e) {
+ mPhone.getOutgoingCallerIdDisplay(obtainMessage(EVENT_GET_CLIR_COMPLETE, this));
+ } catch (Exception e) {
Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIR.");
}
} else {
@@ -951,9 +996,8 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
// NOTE: Refer to the note above.
if (isInterrogate()) {
try {
- mPhone.mCT.getUtInterface()
- .queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
- } catch (ImsException e) {
+ mPhone.queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
+ } catch (Exception e) {
Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIP.");
}
} else if (isActivate() || isDeactivate()) {
@@ -1062,18 +1106,41 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
} else if (mPoundString != null) {
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allow_ussd_over_ims)) {
- // We'll normally send USSD over the CS pipe, but if it happens that
- // the CS phone is out of service, we'll just try over IMS instead.
- if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState()
- == STATE_IN_SERVICE) {
- Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
- + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe "
- + "(allowed over ims).");
- throw new CallStateException(Phone.CS_FALLBACK);
- } else {
- Rlog.i(LOG_TAG, "processCode: CS is out of service, sending ussd string '"
- + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
- sendUssd(mPoundString);
+ int ussd_method = getIntCarrierConfig(
+ CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT);
+
+ switch (ussd_method) {
+ case USSD_OVER_CS_PREFERRED:
+ // We'll normally send USSD over the CS pipe, but if it happens that
+ // the CS phone is out of service, we'll just try over IMS instead.
+ if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState()
+ == STATE_IN_SERVICE) {
+ Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
+ + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe "
+ + "(allowed over ims).");
+ throw new CallStateException(Phone.CS_FALLBACK);
+ } else {
+ Rlog.i(LOG_TAG, "processCode: CS is out of service, "
+ + "sending ussd string '"
+ + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
+ sendUssd(mPoundString);
+ }
+ break;
+ case USSD_OVER_IMS_PREFERRED:
+ case USSD_OVER_IMS_ONLY:
+ Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
+ + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
+ sendUssd(mPoundString);
+ break;
+ case USSD_OVER_CS_ONLY:
+ Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
+ + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe.");
+ throw new CallStateException(Phone.CS_FALLBACK);
+ default:
+ Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
+ + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe."
+ + "(unsupported method)");
+ throw new CallStateException(Phone.CS_FALLBACK);
}
} else {
// USSD codes are not supported over IMS due to modem limitations; send over
@@ -1127,12 +1194,12 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
*
* The radio has reset, and this is still pending
*/
-
- void
- onUssdFinishedError() {
+ public void onUssdFinishedError() {
if (mState == State.PENDING) {
mState = State.FAILED;
- mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+ if (TextUtils.isEmpty(mMessage)) {
+ mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+ }
Rlog.d(LOG_TAG, "onUssdFinishedError: mmi=" + this);
mPhone.onMMIDone(this);
}
@@ -1256,7 +1323,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CharSequence getErrorMessage(AsyncResult ar) {
CharSequence errorMessage;
return ((errorMessage = getMmiErrorMessage(ar)) != null) ? errorMessage :
@@ -1291,12 +1358,14 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss);
} else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO) {
return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video);
+ } else if (err.getCommandError() == CommandException.Error.INTERNAL_ERR) {
+ return mContext.getText(com.android.internal.R.string.mmiError);
}
}
return null;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CharSequence getScString() {
if (mSc != null) {
if (isServiceCodeCallBarring(mSc)) {
@@ -1349,6 +1418,10 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
} else if (ar.exception instanceof ImsException) {
sb.append(getImsErrorMessage(ar));
}
+ } else if (ar.result != null && ar.result instanceof Integer
+ && (int) ar.result == CommandsInterface.SS_STATUS_UNKNOWN) {
+ mState = State.FAILED;
+ sb = null;
} else if (isActivate()) {
mState = State.COMPLETE;
if (mIsCallFwdReg) {
@@ -1395,7 +1468,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
* Returns null if unrecognized
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CharSequence
serviceClassToCFString (int serviceClass) {
switch (serviceClass) {
@@ -1492,6 +1565,11 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
else {
sb.append(getErrorMessage(ar));
}
+ } else if (ar.result instanceof CallForwardInfo[] &&
+ ((CallForwardInfo[]) ar.result)[0].status
+ == CommandsInterface.SS_STATUS_UNKNOWN) {
+ sb = null;
+ mState = State.FAILED;
} else {
CallForwardInfo infos[];
@@ -1574,15 +1652,20 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
}
} else {
- Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received Call Barring Response.");
- // Response for Call Barring queries.
- int[] cbInfos = (int[]) ar.result;
- if (cbInfos[0] == 1) {
- sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
- mState = State.COMPLETE;
+ Rlog.d(LOG_TAG,
+ "onSuppSvcQueryComplete: Received Call Barring/CSFB CLIP Response.");
+ // Response for Call Barring and CSFB CLIP queries.
+ int[] infos = (int[]) ar.result;
+ if (infos == null || infos.length == 0) {
+ sb.append(mContext.getText(com.android.internal.R.string.mmiError));
} else {
- sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
- mState = State.COMPLETE;
+ if (infos[0] != 0) {
+ sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
+ mState = State.COMPLETE;
+ } else {
+ sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
+ mState = State.COMPLETE;
+ }
}
}
}
@@ -1645,16 +1728,18 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
if (ar.exception != null) {
if (ar.exception instanceof ImsException) {
sb.append(getImsErrorMessage(ar));
+ } else {
+ sb.append(getErrorMessage(ar));
}
} else {
- ImsSsInfo ssInfo = (ImsSsInfo) ar.result;
+ int[] clirInfo = (int[]) ar.result;
// ssInfo.getClirOutgoingState() = The 'n' parameter from TS 27.007 7.7
// ssInfo.getClirInterrogationStatus() = The 'm' parameter from TS 27.007 7.7
- Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + ssInfo.getClirOutgoingState()
- + " m=" + ssInfo.getClirInterrogationStatus());
+ Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + clirInfo[0]
+ + " m=" + clirInfo[1]);
// 'm' parameter.
- switch (ssInfo.getClirInterrogationStatus()) {
+ switch (clirInfo[1]) {
case ImsSsInfo.CLIR_STATUS_NOT_PROVISIONED:
sb.append(mContext.getText(
com.android.internal.R.string.serviceNotProvisioned));
@@ -1667,7 +1752,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
break;
case ImsSsInfo.CLIR_STATUS_TEMPORARILY_RESTRICTED:
// 'n' parameter.
- switch (ssInfo.getClirOutgoingState()) {
+ switch (clirInfo[0]) {
case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
sb.append(mContext.getText(
com.android.internal.R.string.CLIRDefaultOnNextCallOn));
@@ -1691,7 +1776,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
break;
case ImsSsInfo.CLIR_STATUS_TEMPORARILY_ALLOWED:
// 'n' parameter.
- switch (ssInfo.getClirOutgoingState()) {
+ switch (clirInfo[0]) {
case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
sb.append(mContext.getText(
com.android.internal.R.string.CLIRDefaultOffNextCallOff));
@@ -1737,11 +1822,13 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
} else {
sb.append(getErrorMessage(ar));
}
-
+ } else if ((ar.result instanceof int[]) &&
+ ((int[])ar.result)[0] == CommandsInterface.SS_STATUS_UNKNOWN) {
+ sb = null;
} else {
int[] ints = (int[])ar.result;
- if (ints.length != 0) {
+ if (ints != null && ints.length != 0) {
if (ints[0] == 0) {
sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
mState = State.COMPLETE;
@@ -1795,6 +1882,28 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
}
}
+ /**
+ * Get the int config from carrier config manager.
+ *
+ * @param key config key defined in CarrierConfigManager
+ * @return integer value of corresponding key.
+ */
+ private int getIntCarrierConfig(String key) {
+ CarrierConfigManager ConfigManager =
+ (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle b = null;
+ if (ConfigManager != null) {
+ // If an invalid subId is used, this bundle will contain default values.
+ b = ConfigManager.getConfigForSubId(mPhone.getSubId());
+ }
+ if (b != null) {
+ return b.getInt(key);
+ } else {
+ // Return static default defined in CarrierConfigManager.
+ return CarrierConfigManager.getDefaultConfig().getInt(key);
+ }
+ }
+
@Override
public ResultReceiver getUssdCallbackReceiver() {
return this.mCallbackReceiver;
@@ -1850,8 +1959,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
case ImsSsData.SS_INTERROGATION:
if (ssData.isTypeClir()) {
Rlog.d(LOG_TAG, "CLIR INTERROGATION");
- ImsSsInfo clirInfo = ssData.getSuppServiceInfo().get(0);
- onQueryClirComplete(new AsyncResult(null, clirInfo, ex));
+ onQueryClirComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex));
} else if (ssData.isTypeCF()) {
Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
// Have to translate to an array, since the modem still returns it in the
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java b/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java
index 33c16e2705..115f6fe43b 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsRegistrationCallbackHelper.java
@@ -109,8 +109,9 @@ public class ImsRegistrationCallbackHelper {
public synchronized void updateRegistrationState(
@RegistrationManager.ImsRegistrationState int newState) {
synchronized (mLock) {
- Log.d(TAG, "updateRegistrationState: registration state from " + mRegistrationState
- + " to " + newState);
+ Log.d(TAG, "updateRegistrationState: registration state from "
+ + RegistrationManager.registrationStateToString(mRegistrationState)
+ + " to " + RegistrationManager.registrationStateToString(newState));
mRegistrationState = newState;
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java b/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
new file mode 100644
index 0000000000..1da3928245
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
+import static com.android.internal.telephony.TelephonyStatsLog.AIRPLANE_MODE;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.telephony.Rlog;
+
+/** Metrics for the usage of airplane mode. */
+public class AirplaneModeStats extends ContentObserver {
+ private static final String TAG = AirplaneModeStats.class.getSimpleName();
+
+ /** Ignore airplane mode events occurring in the first 30 seconds. */
+ private static final long GRACE_PERIOD_MILLIS = 30000L;
+
+ /** An airplane mode toggle is considered short if under 10 seconds. */
+ private static final long SHORT_TOGGLE_MILLIS = 10000L;
+
+ private long mLastActivationTime = 0L;
+
+ private final Context mContext;
+ private final Uri mAirplaneModeSettingUri;
+
+ public AirplaneModeStats(Context context) {
+ super(new Handler(Looper.getMainLooper()));
+
+ mContext = context;
+ mAirplaneModeSettingUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
+
+ context.getContentResolver().registerContentObserver(mAirplaneModeSettingUri, false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri.equals(mAirplaneModeSettingUri)) {
+ onAirplaneModeChanged(isAirplaneModeOn());
+ }
+ }
+
+ private boolean isAirplaneModeOn() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+ }
+
+ /** Generate metrics when airplane mode is enabled or disabled. */
+ private void onAirplaneModeChanged(boolean isAirplaneModeOn) {
+ Rlog.d(TAG, "Airplane mode change. Value: " + isAirplaneModeOn);
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime < GRACE_PERIOD_MILLIS) {
+ return;
+ }
+
+ boolean isShortToggle = calculateShortToggle(currentTime, isAirplaneModeOn);
+ int carrierId = getCarrierId();
+
+ Rlog.d(TAG, "Airplane mode: " + isAirplaneModeOn + ", short=" + isShortToggle
+ + ", carrierId=" + carrierId);
+ TelephonyStatsLog.write(AIRPLANE_MODE, isAirplaneModeOn, isShortToggle, carrierId);
+ }
+
+
+ /* Keep tracks of time and returns if it was a short toggle. */
+ private boolean calculateShortToggle(long currentTime, boolean isAirplaneModeOn) {
+ boolean isShortToggle = false;
+ if (isAirplaneModeOn) {
+ // When airplane mode is enabled, track the time.
+ if (mLastActivationTime == 0L) {
+ mLastActivationTime = currentTime;
+ }
+ return false;
+ } else {
+ // When airplane mode is disabled, reset the time and check if it was a short toggle.
+ long duration = currentTime - mLastActivationTime;
+ mLastActivationTime = 0L;
+ return duration > 0 && duration < SHORT_TOGGLE_MILLIS;
+ }
+ }
+
+ /**
+ * Returns the carrier ID of the active data subscription. If this is not available,
+ * it returns the carrier ID of the first phone.
+ */
+ private static int getCarrierId() {
+ int dataSubId = SubscriptionManager.getActiveDataSubscriptionId();
+ int phoneId = dataSubId != INVALID_SUBSCRIPTION_ID
+ ? SubscriptionManager.getPhoneId(dataSubId) : 0;
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ return phone != null ? phone.getCarrierId() : UNKNOWN_CARRIER_ID;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java b/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
new file mode 100644
index 0000000000..4d0397cf04
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_MISMATCH_REPORTED;
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_UPDATED;
+
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch;
+import com.android.telephony.Rlog;
+
+/** Metrics for the carrier id matching. */
+public class CarrierIdMatchStats {
+ private static final String TAG = CarrierIdMatchStats.class.getSimpleName();
+
+ private CarrierIdMatchStats() { }
+
+ /** Generate metrics when carrier ID mismatch occurs. */
+ public static void onCarrierIdMismatch(
+ int cid, String mccMnc, String gid1, String spn, String pnn) {
+ PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ CarrierIdMismatch carrierIdMismatch = new CarrierIdMismatch();
+ carrierIdMismatch.mccMnc = nullToEmpty(mccMnc);
+ carrierIdMismatch.gid1 = nullToEmpty(gid1);
+ carrierIdMismatch.spn = nullToEmpty(spn);
+ carrierIdMismatch.pnn = carrierIdMismatch.spn.isEmpty() ? nullToEmpty(pnn) : "";
+
+ // Add to storage and generate atom only if it was added (new SIM card).
+ boolean isAdded = storage.addCarrierIdMismatch(carrierIdMismatch);
+ if (isAdded) {
+ Rlog.d(TAG, "New carrier ID mismatch event: " + carrierIdMismatch.toString());
+ TelephonyStatsLog.write(CARRIER_ID_MISMATCH_REPORTED, cid, mccMnc, gid1, spn, pnn);
+ }
+ }
+
+ /** Generate metrics for the carrier ID table version. */
+ public static void sendCarrierIdTableVersion(int carrierIdTableVersion) {
+ PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ if (storage.setCarrierIdTableVersion(carrierIdTableVersion)) {
+ Rlog.d(TAG, "New carrier ID table version: " + carrierIdTableVersion);
+ TelephonyStatsLog.write(CARRIER_ID_TABLE_UPDATED, carrierIdTableVersion);
+ }
+ }
+
+ private static String nullToEmpty(String string) {
+ return string != null ? string : "";
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
new file mode 100644
index 0000000000..da350c391a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.DataFailCause;
+import android.telephony.ServiceState;
+import android.telephony.ServiceState.RilRadioTechnology;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting.ProtocolType;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataService;
+import android.telephony.data.DataService.DeactivateDataReason;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.telephony.Rlog;
+
+import java.util.Random;
+
+/** Collects data call change events per DataConnection for the pulled atom. */
+public class DataCallSessionStats {
+ private static final String TAG = DataCallSessionStats.class.getSimpleName();
+
+ private final Phone mPhone;
+ private long mStartTime;
+ @Nullable private DataCallSession mDataCallSession;
+
+ private final PersistAtomsStorage mAtomsStorage =
+ PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ private static final Random RANDOM = new Random();
+
+ public DataCallSessionStats(Phone phone) {
+ mPhone = phone;
+ }
+
+ /** Creates a new ongoing atom when data call is set up. */
+ public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) {
+ mDataCallSession = getDefaultProto(apnTypeBitMask);
+ mStartTime = getTimeMillis();
+ }
+
+ /**
+ * Updates the ongoing dataCall's atom for data call response event.
+ *
+ * @param response setup Data call response
+ * @param radioTechnology The data call RAT
+ * @param apnTypeBitmask APN type bitmask
+ * @param protocol Data connection protocol
+ * @param failureCause failure cause as per android.telephony.DataFailCause
+ */
+ public synchronized void onSetupDataCallResponse(
+ @Nullable DataCallResponse response,
+ @RilRadioTechnology int radioTechnology,
+ @ApnType int apnTypeBitmask,
+ @ProtocolType int protocol,
+ @DataFailureCause int failureCause) {
+ // there should've been a call to onSetupDataCall to initiate the atom,
+ // so this method is being called out of order -> no metric will be logged
+ if (mDataCallSession == null) {
+ loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
+ return;
+ }
+ mDataCallSession.ratAtEnd = ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
+
+ // only set if apn hasn't been set during setup
+ if (mDataCallSession.apnTypeBitmask == 0) {
+ mDataCallSession.apnTypeBitmask = apnTypeBitmask;
+ }
+
+ mDataCallSession.ipType = protocol;
+ mDataCallSession.failureCause = failureCause;
+ if (response != null) {
+ mDataCallSession.suggestedRetryMillis =
+ (int) Math.min(response.getRetryDurationMillis(), Integer.MAX_VALUE);
+ // If setup has failed, then store the atom
+ if (failureCause != DataFailCause.NONE) {
+ mDataCallSession.failureCause = failureCause;
+ mDataCallSession.setupFailed = true;
+ mDataCallSession.ongoing = false;
+ mAtomsStorage.addDataCallSession(mDataCallSession);
+ mDataCallSession = null;
+ }
+ }
+ }
+
+ /**
+ * Updates the dataCall atom when data call is deactivated.
+ *
+ * @param reason Deactivate reason
+ */
+ public synchronized void setDeactivateDataCallReason(@DeactivateDataReason int reason) {
+ // there should've been another call to initiate the atom,
+ // so this method is being called out of order -> no metric will be logged
+ if (mDataCallSession == null) {
+ loge("setDeactivateDataCallReason: no DataCallSession atom has been initiated.");
+ return;
+ }
+ switch (reason) {
+ case DataService.REQUEST_REASON_NORMAL:
+ mDataCallSession.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
+ break;
+ case DataService.REQUEST_REASON_SHUTDOWN:
+ mDataCallSession.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
+ break;
+ case DataService.REQUEST_REASON_HANDOVER:
+ mDataCallSession.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
+ break;
+ default:
+ mDataCallSession.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
+ break;
+ }
+ }
+
+ /** Stores the atom when DataConnection reaches DISCONNECTED state.
+ * @param failureCause failure cause as per android.telephony.DataFailCause
+ **/
+ public synchronized void onDataCallDisconnected(@DataFailureCause int failureCause) {
+ // there should've been another call to initiate the atom,
+ // so this method is being called out of order -> no atom will be saved
+ if (mDataCallSession == null) {
+ loge("onDataCallDisconnected: no DataCallSession atom has been initiated.");
+ return;
+ }
+ mDataCallSession.failureCause = failureCause;
+ mDataCallSession.oosAtEnd = getIsOos();
+ mDataCallSession.ongoing = false;
+ mDataCallSession.durationMinutes = convertMillisToMinutes(getTimeMillis() - mStartTime);
+ // store for the data call list event, after DataCall is disconnected and entered into
+ // inactive mode
+ mAtomsStorage.addDataCallSession(mDataCallSession);
+ mDataCallSession = null;
+ }
+
+ /**
+ * Updates the atom when data registration state or RAT changes.
+ *
+ * <p>NOTE: in {@link ServiceStateTracker}, change of channel number will trigger data
+ * registration state change.
+ */
+ public synchronized void onDrsOrRatChanged(@RilRadioTechnology int radioTechnology) {
+ @NetworkType int currentRat =
+ ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
+ if (mDataCallSession != null
+ && currentRat != TelephonyManager.NETWORK_TYPE_UNKNOWN
+ && mDataCallSession.ratAtEnd != currentRat) {
+ mDataCallSession.ratSwitchCount++;
+ mDataCallSession.ratAtEnd = currentRat;
+ mDataCallSession.bandAtEnd = ServiceStateStats.getBand(mPhone, currentRat);
+ }
+ }
+
+ private static long convertMillisToMinutes(long millis) {
+ return Math.round(millis / 60000.0);
+ }
+
+ /** Creates a proto for a normal {@code DataCallSession} with default values. */
+ private DataCallSession getDefaultProto(@ApnType int apnTypeBitmask) {
+ DataCallSession proto = new DataCallSession();
+ proto.dimension = RANDOM.nextInt();
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(mPhone.getPhoneId());
+ proto.apnTypeBitmask = apnTypeBitmask;
+ proto.carrierId = mPhone.getCarrierId();
+ proto.isRoaming = getIsRoaming();
+ proto.oosAtEnd = false;
+ proto.ratSwitchCount = 0L;
+ proto.isOpportunistic = getIsOpportunistic();
+ proto.ipType = DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
+ proto.setupFailed = false;
+ proto.failureCause = DataFailCause.NONE;
+ proto.suggestedRetryMillis = 0;
+ proto.deactivateReason = DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
+ proto.durationMinutes = 0;
+ proto.ongoing = true;
+ return proto;
+ }
+
+ private boolean getIsRoaming() {
+ ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
+ ServiceState serviceState =
+ serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ return serviceState != null ? serviceState.getRoaming() : false;
+ }
+
+ private boolean getIsOpportunistic() {
+ SubscriptionController subController = SubscriptionController.getInstance();
+ return subController != null ? subController.isOpportunistic(mPhone.getSubId()) : false;
+ }
+
+ private boolean getIsOos() {
+ ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
+ ServiceState serviceState =
+ serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ return serviceState != null
+ ? serviceState.getDataRegistrationState() == ServiceState.STATE_OUT_OF_SERVICE
+ : false;
+ }
+
+ private void loge(String format, Object... args) {
+ Rlog.e(TAG, "[" + mPhone.getPhoneId() + "]" + String.format(format, args));
+ }
+
+ @VisibleForTesting
+ protected long getTimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
new file mode 100644
index 0000000000..410afcf421
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import android.telephony.Annotation.NetworkType;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.dataconnection.DcTracker;
+
+/** Generates metrics related to data stall recovery events per phone ID for the pushed atom. */
+public class DataStallRecoveryStats {
+ /**
+ * Create and push new atom when there is a data stall recovery event
+ *
+ * @param recoveryAction Data stall recovery action
+ * @param phone
+ */
+ public static void onDataStallEvent(@DcTracker.RecoveryAction int recoveryAction,
+ Phone phone, boolean isRecovered, int durationMillis) {
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = phone.getDefaultPhone();
+ }
+
+ int carrierId = phone.getCarrierId();
+ int rat = getRat(phone);
+ int band = ServiceStateStats.getBand(phone, rat);
+ // the number returned here matches the SignalStrength enum we have
+ int signalStrength = phone.getSignalStrength().getLevel();
+ boolean isOpportunistic = getIsOpportunistic(phone);
+ boolean isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
+
+ TelephonyStatsLog.write(TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED, carrierId, rat,
+ signalStrength, recoveryAction, isOpportunistic, isMultiSim, band, isRecovered,
+ durationMillis);
+ }
+
+ /** Returns the RAT used for data (including IWLAN). */
+ private static @NetworkType int getRat(Phone phone) {
+ ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
+ ServiceState serviceState =
+ serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ return serviceState != null ? serviceState.getDataNetworkType()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private static boolean getIsOpportunistic(Phone phone) {
+ SubscriptionController subController = SubscriptionController.getInstance();
+ return subController != null ? subController.isOpportunistic(phone.getSubId()) : false;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/ImsStats.java b/src/java/com/android/internal/telephony/metrics/ImsStats.java
new file mode 100644
index 0000000000..fe00a4accb
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/ImsStats.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERING;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NR;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+import static android.util.Patterns.EMAIL_ADDRESS;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RegistrationManager.ImsRegistrationState;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability;
+import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.telephony.Rlog;
+
+import java.util.regex.Pattern;
+
+/** Tracks IMS registration metrics for each phone. */
+public class ImsStats {
+ private static final String TAG = ImsStats.class.getSimpleName();
+
+ /**
+ * Minimal duration of the registration state.
+ *
+ * <p>Registration state (including changes in capable/available features) with duration shorter
+ * than this will be ignored as they are considered transient states.
+ */
+ private static final long MIN_REGISTRATION_DURATION_MILLIS = 1L * SECOND_IN_MILLIS;
+
+ /**
+ * Maximum length of the extra message in the termination reason.
+ *
+ * <p>If the extra message is longer than this length, it will be truncated.
+ */
+ private static final int MAX_EXTRA_MESSAGE_LENGTH = 128;
+
+ /** Pattern used to match UUIDs in IMS extra messages for filtering. */
+ private static final Pattern PATTERN_UUID =
+ Pattern.compile(
+ "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}");
+
+ /** Replacement for UUIDs. */
+ private static final String REPLACEMENT_UUID = "<UUID_REDACTED>";
+
+ /**
+ * Pattern used to match URI (e.g. sip, tel) in IMS extra messages for filtering.
+ *
+ * <p>NOTE: this simple pattern aims to catch the most common URI schemes. It is not meant to be
+ * RFC-complaint.
+ */
+ private static final Pattern PATTERN_URI =
+ Pattern.compile("([a-zA-Z]{2,}:)" + EMAIL_ADDRESS.pattern());
+
+ /** Replacement for URI. */
+ private static final String REPLACEMENT_URI = "$1<REDACTED>";
+
+ /**
+ * Pattern used to match IPv4 addresses in IMS extra messages for filtering.
+ *
+ * <p>This is a copy of {@code android.util.Patterns.IP_ADDRESS}, which is deprecated and might
+ * be removed in the future.
+ */
+ private static final Pattern PATTERN_IPV4 =
+ Pattern.compile(
+ "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
+ + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
+ + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+ + "|[1-9][0-9]|[0-9]))");
+
+ /** Replacement for IPv4 addresses. */
+ private static final String REPLACEMENT_IPV4 = "<IPV4_REDACTED>";
+
+ /**
+ * Pattern used to match IPv6 addresses in IMS extra messages for filtering.
+ *
+ * <p>NOTE: this pattern aims to catch the most common IPv6 addresses. It is not meant to be
+ * RFC-complaint or free of false positives.
+ */
+ private static final Pattern PATTERN_IPV6 =
+ Pattern.compile(
+ // Full address
+ "([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}"
+ // Abbreviated address, e.g. 2001:4860:4860::8888
+ + "|([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1,6}"
+ // Abbreviated network address, e.g. 2607:F8B0::
+ + "|([0-9a-fA-F]{1,4}:){1,7}:"
+ // Abbreviated address, e.g. ::1
+ + "|:(:[0-9a-fA-F]{1,4}){1,7}");
+
+ /** Replacement for IPv6 addresses. */
+ private static final String REPLACEMENT_IPV6 = "<IPV6_REDACTED>";
+
+ /**
+ * Pattern used to match potential IMEI values in IMS extra messages for filtering.
+ *
+ * <p>This includes segmented IMEI or IMEI/SV, as well as unsegmented IMEI/SV.
+ */
+ private static final Pattern PATTERN_IMEI =
+ Pattern.compile(
+ "(^|[^0-9])(?:"
+ // IMEI, AABBBBBB-CCCCCC-D format; IMEI/SV, AABBBBBB-CCCCCC-EE format
+ + "[0-9]{8}-[0-9]{6}-[0-9][0-9]?"
+ // IMEI, AA-BBBBBB-CCCCCC-D format; IMEI/SV, AA-BBBBBB-CCCCCC-EE format
+ + "|[0-9]{2}-[0-9]{6}-[0-9]{6}-[0-9][0-9]?"
+ // IMEI/SV, unsegmented
+ + "|[0-9]{16}"
+ + ")($|[^0-9])");
+
+ /** Replacement for IMEI. */
+ private static final String REPLACEMENT_IMEI = "$1<IMEI_REDACTED>$2";
+
+ /**
+ * Pattern used to match potential unsegmented IMEI/IMSI values in IMS extra messages for
+ * filtering.
+ */
+ private static final Pattern PATTERN_UNSEGMENTED_IMEI_IMSI =
+ Pattern.compile("(^|[^0-9])[0-9]{15}($|[^0-9])");
+
+ /** Replacement for unsegmented IMEI/IMSI. */
+ private static final String REPLACEMENT_UNSEGMENTED_IMEI_IMSI = "$1<IMEI_IMSI_REDACTED>$2";
+
+ /**
+ * Pattern used to match hostnames in IMS extra messages for filtering.
+ *
+ * <p>This pattern differs from {@link android.util.Patterns.DOMAIN_NAME} in a few ways: it
+ * requires the name to have at least 3 segments (shorter names are nearly always public or
+ * typos, i.e. missing space after period), does not check the validity of TLDs, and does not
+ * support punycodes in TLDs.
+ */
+ private static final Pattern PATTERN_HOSTNAME =
+ Pattern.compile("([0-9a-zA-Z][0-9a-zA-Z_\\-]{0,61}[0-9a-zA-Z]\\.){2,}[a-zA-Z]{2,}");
+
+ /** Replacement for hostnames. */
+ private static final String REPLACEMENT_HOSTNAME = "<HOSTNAME_REDACTED>";
+
+ /**
+ * Pattern used to match potential IDs in IMS extra messages for filtering.
+ *
+ * <p>This pattern target numbers that are potential IDs in unknown formats. It should be
+ * replaced after all other replacements are done to ensure complete and correct filtering.
+ *
+ * <p>Specifically, this pattern looks for any number (including hex) that is separated by dots
+ * or dashes has at least 6 significant digits, and any unsegmented numbers that has at least 5
+ * significant digits.
+ */
+ private static final Pattern PATTERN_UNKNOWN_ID =
+ Pattern.compile(
+ "(^|[^0-9a-fA-F])(([-\\.]?0)*[1-9a-fA-F]([-\\.]?[0-9a-fA-F]){5,}"
+ + "|0*[1-9a-fA-F]([0-9a-fA-F]){4,})");
+
+ /** Replacement for potential IDs. */
+ private static final String REPLACEMENT_UNKNOWN_ID = "$1<ID_REDACTED>";
+
+ private final ImsPhone mPhone;
+ private final PersistAtomsStorage mStorage;
+
+ @ImsRegistrationState private int mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
+
+ private long mLastTimestamp;
+ @Nullable private ImsRegistrationStats mLastRegistrationStats;
+
+ // Available features are those reported by ImsService to be available for use.
+ private MmTelCapabilities mLastAvailableFeatures = new MmTelCapabilities();
+
+ // Capable features (enabled by device/carrier). Theses are available before IMS is registered
+ // and not necessarily updated when RAT changes.
+ private final MmTelCapabilities mLastWwanCapableFeatures = new MmTelCapabilities();
+ private final MmTelCapabilities mLastWlanCapableFeatures = new MmTelCapabilities();
+
+ public ImsStats(ImsPhone phone) {
+ mPhone = phone;
+ mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ }
+
+ /**
+ * Finalizes the durations of the current IMS registration stats segment.
+ *
+ * <p>This method is also invoked whenever the registration state, feature capability, or
+ * feature availability changes.
+ */
+ public synchronized void conclude() {
+ long now = getTimeMillis();
+
+ // Currently not tracking time spent on registering.
+ if (mLastRegistrationState == REGISTRATION_STATE_REGISTERED) {
+ ImsRegistrationStats stats = copyOf(mLastRegistrationStats);
+ long duration = now - mLastTimestamp;
+
+ if (duration < MIN_REGISTRATION_DURATION_MILLIS) {
+ logw("conclude: discarding transient stats, duration=%d", duration);
+ } else {
+ stats.registeredMillis = duration;
+
+ stats.voiceAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0;
+ stats.videoAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0;
+ stats.utAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0;
+ stats.smsAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0;
+
+ MmTelCapabilities lastCapableFeatures =
+ stats.rat == TelephonyManager.NETWORK_TYPE_IWLAN
+ ? mLastWlanCapableFeatures
+ : mLastWwanCapableFeatures;
+ stats.voiceCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0;
+ stats.videoCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0;
+ stats.utCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0;
+ stats.smsCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0;
+
+ mStorage.addImsRegistrationStats(stats);
+ }
+ }
+
+ mLastTimestamp = now;
+ }
+
+ /** Updates the stats when registered features changed. */
+ public synchronized void onImsCapabilitiesChanged(
+ @ImsRegistrationTech int radioTech, MmTelCapabilities capabilities) {
+ conclude();
+
+ if (mLastRegistrationStats != null) {
+ mLastRegistrationStats.rat = convertRegistrationTechToNetworkType(radioTech);
+ }
+ mLastAvailableFeatures = capabilities;
+ }
+
+ /** Updates the stats when capable features changed. */
+ public synchronized void onSetFeatureResponse(
+ @MmTelCapability int feature, @ImsRegistrationTech int network, int value) {
+ MmTelCapabilities lastCapableFeatures = getLastCapableFeaturesForTech(network);
+ if (lastCapableFeatures != null) {
+ conclude();
+ if (value == ProvisioningManager.PROVISIONING_VALUE_ENABLED) {
+ lastCapableFeatures.addCapabilities(feature);
+ } else {
+ lastCapableFeatures.removeCapabilities(feature);
+ }
+ }
+ }
+
+ /** Updates the stats when IMS registration is progressing. */
+ public synchronized void onImsRegistering(@TransportType int imsRadioTech) {
+ conclude();
+
+ mLastRegistrationStats = getDefaultImsRegistrationStats();
+ mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech);
+ mLastRegistrationState = REGISTRATION_STATE_REGISTERING;
+ }
+
+ /** Updates the stats when IMS registration succeeds. */
+ public synchronized void onImsRegistered(@TransportType int imsRadioTech) {
+ conclude();
+
+ // NOTE: mLastRegistrationStats can be null (no registering phase).
+ if (mLastRegistrationStats == null) {
+ mLastRegistrationStats = getDefaultImsRegistrationStats();
+ }
+ mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech);
+ mLastRegistrationState = REGISTRATION_STATE_REGISTERED;
+ }
+
+ /** Updates the stats and generates a termination atom when IMS registration fails/ends. */
+ public synchronized void onImsUnregistered(ImsReasonInfo reasonInfo) {
+ conclude();
+
+ // Generate end reason atom.
+ // NOTE: mLastRegistrationStats can be null (no registering phase).
+ ImsRegistrationTermination termination = new ImsRegistrationTermination();
+ if (mLastRegistrationStats != null) {
+ termination.carrierId = mLastRegistrationStats.carrierId;
+ termination.ratAtEnd = getRatAtEnd(mLastRegistrationStats.rat);
+ } else {
+ termination.carrierId = mPhone.getDefaultPhone().getCarrierId();
+ // We cannot tell whether the registration was intended for WWAN or WLAN
+ termination.ratAtEnd = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ termination.isMultiSim = SimSlotState.isMultiSim();
+ termination.setupFailed = (mLastRegistrationState != REGISTRATION_STATE_REGISTERED);
+ termination.reasonCode = reasonInfo.getCode();
+ termination.extraCode = reasonInfo.getExtraCode();
+ termination.extraMessage = filterExtraMessage(reasonInfo.getExtraMessage());
+ termination.count = 1;
+ mStorage.addImsRegistrationTermination(termination);
+
+ // Reset state to unregistered.
+ mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
+ mLastRegistrationStats = null;
+ mLastAvailableFeatures = new MmTelCapabilities();
+ }
+
+ @NetworkType
+ private int getRatAtEnd(@NetworkType int lastStateRat) {
+ return lastStateRat == TelephonyManager.NETWORK_TYPE_IWLAN ? lastStateRat : getWwanPsRat();
+ }
+
+ @NetworkType
+ private int convertTransportTypeToNetworkType(@TransportType int transportType) {
+ switch (transportType) {
+ case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
+ return getWwanPsRat();
+ case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ default:
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ @NetworkType
+ private int getWwanPsRat() {
+ ServiceState state = mPhone.getServiceStateTracker().getServiceState();
+ final NetworkRegistrationInfo wwanRegInfo =
+ state.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return wwanRegInfo != null
+ ? wwanRegInfo.getAccessNetworkTechnology()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private ImsRegistrationStats getDefaultImsRegistrationStats() {
+ Phone phone = mPhone.getDefaultPhone();
+ ImsRegistrationStats stats = new ImsRegistrationStats();
+ stats.carrierId = phone.getCarrierId();
+ stats.simSlotIndex = phone.getPhoneId();
+ return stats;
+ }
+
+ @Nullable
+ private MmTelCapabilities getLastCapableFeaturesForTech(@ImsRegistrationTech int radioTech) {
+ switch (radioTech) {
+ case REGISTRATION_TECH_NONE:
+ return null;
+ case REGISTRATION_TECH_IWLAN:
+ return mLastWlanCapableFeatures;
+ default:
+ return mLastWwanCapableFeatures;
+ }
+ }
+
+ @NetworkType
+ private int convertRegistrationTechToNetworkType(@ImsRegistrationTech int radioTech) {
+ switch (radioTech) {
+ case REGISTRATION_TECH_NONE:
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ case REGISTRATION_TECH_LTE:
+ return TelephonyManager.NETWORK_TYPE_LTE;
+ case REGISTRATION_TECH_IWLAN:
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ case REGISTRATION_TECH_NR:
+ return TelephonyManager.NETWORK_TYPE_NR;
+ default:
+ loge("convertRegistrationTechToNetworkType: unknown radio tech %d", radioTech);
+ return getWwanPsRat();
+ }
+ }
+
+ private static ImsRegistrationStats copyOf(ImsRegistrationStats source) {
+ ImsRegistrationStats dest = new ImsRegistrationStats();
+
+ dest.carrierId = source.carrierId;
+ dest.simSlotIndex = source.simSlotIndex;
+ dest.rat = source.rat;
+ dest.registeredMillis = source.registeredMillis;
+ dest.voiceCapableMillis = source.voiceCapableMillis;
+ dest.voiceAvailableMillis = source.voiceAvailableMillis;
+ dest.smsCapableMillis = source.smsCapableMillis;
+ dest.smsAvailableMillis = source.smsAvailableMillis;
+ dest.videoCapableMillis = source.videoCapableMillis;
+ dest.videoAvailableMillis = source.videoAvailableMillis;
+ dest.utCapableMillis = source.utCapableMillis;
+ dest.utAvailableMillis = source.utAvailableMillis;
+
+ return dest;
+ }
+
+ @VisibleForTesting
+ protected long getTimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ /** Filters IMS extra messages to ensure length limit and remove IDs. */
+ public static String filterExtraMessage(@Nullable String str) {
+ if (str == null) {
+ return "";
+ }
+
+ str = PATTERN_UUID.matcher(str).replaceAll(REPLACEMENT_UUID);
+ str = PATTERN_URI.matcher(str).replaceAll(REPLACEMENT_URI);
+ str = PATTERN_HOSTNAME.matcher(str).replaceAll(REPLACEMENT_HOSTNAME);
+ str = PATTERN_IPV4.matcher(str).replaceAll(REPLACEMENT_IPV4);
+ str = PATTERN_IPV6.matcher(str).replaceAll(REPLACEMENT_IPV6);
+ str = PATTERN_IMEI.matcher(str).replaceAll(REPLACEMENT_IMEI);
+ str = PATTERN_UNSEGMENTED_IMEI_IMSI.matcher(str)
+ .replaceAll(REPLACEMENT_UNSEGMENTED_IMEI_IMSI);
+ str = PATTERN_UNKNOWN_ID.matcher(str).replaceAll(REPLACEMENT_UNKNOWN_ID);
+
+ return str.length() > MAX_EXTRA_MESSAGE_LENGTH
+ ? str.substring(0, MAX_EXTRA_MESSAGE_LENGTH)
+ : str;
+ }
+
+ private void logw(String format, Object... args) {
+ Rlog.w(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args));
+ }
+
+ private void loge(String format, Object... args) {
+ Rlog.e(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index af71c547a3..ae9e30e675 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -20,8 +20,17 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_VERSION;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_TERMINATION;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
+import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_NETWORK_REQUESTS;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION;
@@ -33,7 +42,17 @@ import android.util.StatsEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequests;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.util.ConcurrentUtils;
import com.android.telephony.Rlog;
@@ -82,20 +101,33 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
private PersistAtomsStorage mStorage;
private final StatsManager mStatsManager;
+ private final AirplaneModeStats mAirplaneModeStats;
private static final Random sRandom = new Random();
public MetricsCollector(Context context) {
mStorage = new PersistAtomsStorage(context);
mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
if (mStatsManager != null) {
+ registerAtom(CELLULAR_DATA_SERVICE_SWITCH, POLICY_PULL_DAILY);
+ registerAtom(CELLULAR_SERVICE_STATE, POLICY_PULL_DAILY);
registerAtom(SIM_SLOT_STATE, null);
registerAtom(SUPPORTED_RADIO_ACCESS_FAMILY, null);
registerAtom(VOICE_CALL_RAT_USAGE, POLICY_PULL_DAILY);
registerAtom(VOICE_CALL_SESSION, POLICY_PULL_DAILY);
+ registerAtom(INCOMING_SMS, POLICY_PULL_DAILY);
+ registerAtom(OUTGOING_SMS, POLICY_PULL_DAILY);
+ registerAtom(CARRIER_ID_TABLE_VERSION, null);
+ registerAtom(DATA_CALL_SESSION, POLICY_PULL_DAILY);
+ registerAtom(IMS_REGISTRATION_STATS, POLICY_PULL_DAILY);
+ registerAtom(IMS_REGISTRATION_TERMINATION, POLICY_PULL_DAILY);
+ registerAtom(TELEPHONY_NETWORK_REQUESTS, POLICY_PULL_DAILY);
+
Rlog.d(TAG, "registered");
} else {
Rlog.e(TAG, "could not get StatsManager, atoms not registered");
}
+
+ mAirplaneModeStats = new AirplaneModeStats(context);
}
/** Replaces the {@link PersistAtomsStorage} backing the puller. Used during unit tests. */
@@ -114,6 +146,10 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
switch (atomTag) {
+ case CELLULAR_DATA_SERVICE_SWITCH:
+ return pullCellularDataServiceSwitch(data);
+ case CELLULAR_SERVICE_STATE:
+ return pullCellularServiceState(data);
case SIM_SLOT_STATE:
return pullSimSlotState(data);
case SUPPORTED_RADIO_ACCESS_FAMILY:
@@ -122,6 +158,20 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
return pullVoiceCallRatUsages(data);
case VOICE_CALL_SESSION:
return pullVoiceCallSessions(data);
+ case INCOMING_SMS:
+ return pullIncomingSms(data);
+ case OUTGOING_SMS:
+ return pullOutgoingSms(data);
+ case CARRIER_ID_TABLE_VERSION:
+ return pullCarrierIdTableVersion(data);
+ case DATA_CALL_SESSION:
+ return pullDataCallSession(data);
+ case IMS_REGISTRATION_STATS:
+ return pullImsRegistrationStats(data);
+ case IMS_REGISTRATION_TERMINATION:
+ return pullImsRegistrationTermination(data);
+ case TELEPHONY_NETWORK_REQUESTS:
+ return pullTelephonyNetworkRequests(data);
default:
Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
return StatsManager.PULL_SKIP;
@@ -142,40 +192,46 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
return StatsManager.PULL_SKIP;
}
- StatsEvent e =
- StatsEvent.newBuilder()
- .setAtomId(SIM_SLOT_STATE)
- .writeInt(state.numActiveSlots)
- .writeInt(state.numActiveSims)
- .writeInt(state.numActiveEsims)
- .build();
- data.add(e);
+ data.add(
+ TelephonyStatsLog.buildStatsEvent(
+ SIM_SLOT_STATE,
+ state.numActiveSlots,
+ state.numActiveSims,
+ state.numActiveEsims));
return StatsManager.PULL_SUCCESS;
}
private static int pullSupportedRadioAccessFamily(List<StatsEvent> data) {
- long rafSupported = 0L;
- try {
- // The bitmask is defined in android.telephony.TelephonyManager.NetworkTypeBitMask
- for (Phone phone : PhoneFactory.getPhones()) {
- rafSupported |= phone.getRadioAccessFamily();
- }
- } catch (IllegalStateException e) {
- // Phones have not been made yet
+ Phone[] phones = getPhonesIfAny();
+ if (phones.length == 0) {
return StatsManager.PULL_SKIP;
}
- StatsEvent e =
- StatsEvent.newBuilder()
- .setAtomId(SUPPORTED_RADIO_ACCESS_FAMILY)
- .writeLong(rafSupported)
- .build();
- data.add(e);
+ // The bitmask is defined in android.telephony.TelephonyManager.NetworkTypeBitMask
+ long rafSupported = 0L;
+ for (Phone phone : PhoneFactory.getPhones()) {
+ rafSupported |= phone.getRadioAccessFamily();
+ }
+
+ data.add(TelephonyStatsLog.buildStatsEvent(SUPPORTED_RADIO_ACCESS_FAMILY, rafSupported));
return StatsManager.PULL_SUCCESS;
}
+ private static int pullCarrierIdTableVersion(List<StatsEvent> data) {
+ Phone[] phones = getPhonesIfAny();
+ if (phones.length == 0) {
+ return StatsManager.PULL_SKIP;
+ } else {
+ // All phones should have the same version of the carrier ID table, so only query the
+ // first one.
+ int version = phones[0].getCarrierIdListVersion();
+ data.add(TelephonyStatsLog.buildStatsEvent(CARRIER_ID_TABLE_VERSION, version));
+ return StatsManager.PULL_SUCCESS;
+ }
+ }
+
private int pullVoiceCallRatUsages(List<StatsEvent> data) {
- RawVoiceCallRatUsage[] usages = mStorage.getVoiceCallRatUsages(MIN_COOLDOWN_MILLIS);
+ VoiceCallRatUsage[] usages = mStorage.getVoiceCallRatUsages(MIN_COOLDOWN_MILLIS);
if (usages != null) {
// sort by carrier/RAT and remove buckets with insufficient number of calls
Arrays.stream(usages)
@@ -199,7 +255,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
private int pullVoiceCallSessions(List<StatsEvent> data) {
VoiceCallSession[] calls = mStorage.getVoiceCallSessions(MIN_COOLDOWN_MILLIS);
if (calls != null) {
- // call session list is already shuffled when calls inserted
+ // call session list is already shuffled when calls were inserted
Arrays.stream(calls).forEach(call -> data.add(buildStatsEvent(call)));
return StatsManager.PULL_SUCCESS;
} else {
@@ -208,56 +264,315 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
}
}
+ private int pullIncomingSms(List<StatsEvent> data) {
+ IncomingSms[] smsList = mStorage.getIncomingSms(MIN_COOLDOWN_MILLIS);
+ if (smsList != null) {
+ // SMS list is already shuffled when SMS were inserted
+ Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "INCOMING_SMS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullOutgoingSms(List<StatsEvent> data) {
+ OutgoingSms[] smsList = mStorage.getOutgoingSms(MIN_COOLDOWN_MILLIS);
+ if (smsList != null) {
+ // SMS list is already shuffled when SMS were inserted
+ Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "OUTGOING_SMS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullDataCallSession(List<StatsEvent> data) {
+ DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS);
+ if (dataCallSessions != null) {
+ Arrays.stream(dataCallSessions)
+ .forEach(dataCall -> data.add(buildStatsEvent(dataCall)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "DATA_CALL_SESSION pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullCellularDataServiceSwitch(List<StatsEvent> data) {
+ CellularDataServiceSwitch[] persistAtoms =
+ mStorage.getCellularDataServiceSwitches(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "CELLULAR_DATA_SERVICE_SWITCH pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullCellularServiceState(List<StatsEvent> data) {
+ // Include the latest durations
+ for (Phone phone : getPhonesIfAny()) {
+ phone.getServiceStateTracker().getServiceStateStats().conclude();
+ }
+
+ CellularServiceState[] persistAtoms =
+ mStorage.getCellularServiceStates(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "CELLULAR_SERVICE_STATE pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullImsRegistrationStats(List<StatsEvent> data) {
+ // Include the latest durations
+ for (Phone phone : getPhonesIfAny()) {
+ ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+ if (imsPhone != null) {
+ imsPhone.getImsStats().conclude();
+ }
+ }
+
+ ImsRegistrationStats[] persistAtoms = mStorage.getImsRegistrationStats(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_REGISTRATION_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullImsRegistrationTermination(List<StatsEvent> data) {
+ ImsRegistrationTermination[] persistAtoms =
+ mStorage.getImsRegistrationTerminations(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_REGISTRATION_TERMINATION pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullTelephonyNetworkRequests(List<StatsEvent> data) {
+ NetworkRequests[] persistAtoms = mStorage.getNetworkRequests(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "TELEPHONY_NETWORK_REQUESTS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
/** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */
private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) {
mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this);
}
- private static StatsEvent buildStatsEvent(RawVoiceCallRatUsage usage) {
- return StatsEvent.newBuilder()
- .setAtomId(VOICE_CALL_RAT_USAGE)
- .writeInt(usage.carrierId)
- .writeInt(usage.rat)
- .writeLong(
- round(usage.totalDurationMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)
- .writeLong(usage.callCount)
- .build();
+ private static StatsEvent buildStatsEvent(CellularDataServiceSwitch serviceSwitch) {
+ return TelephonyStatsLog.buildStatsEvent(
+ CELLULAR_DATA_SERVICE_SWITCH,
+ serviceSwitch.ratFrom,
+ serviceSwitch.ratTo,
+ serviceSwitch.simSlotIndex,
+ serviceSwitch.isMultiSim,
+ serviceSwitch.carrierId,
+ serviceSwitch.switchCount);
+ }
+
+ private static StatsEvent buildStatsEvent(CellularServiceState state) {
+ return TelephonyStatsLog.buildStatsEvent(
+ CELLULAR_SERVICE_STATE,
+ state.voiceRat,
+ state.dataRat,
+ state.voiceRoamingType,
+ state.dataRoamingType,
+ state.isEndc,
+ state.simSlotIndex,
+ state.isMultiSim,
+ state.carrierId,
+ (int) (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ }
+
+ private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) {
+ return TelephonyStatsLog.buildStatsEvent(
+ VOICE_CALL_RAT_USAGE,
+ usage.carrierId,
+ usage.rat,
+ round(usage.totalDurationMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS,
+ usage.callCount);
}
private static StatsEvent buildStatsEvent(VoiceCallSession session) {
- return StatsEvent.newBuilder()
- .setAtomId(VOICE_CALL_SESSION)
- .writeInt(session.bearerAtStart)
- .writeInt(session.bearerAtEnd)
- .writeInt(session.direction)
- .writeInt(session.setupDuration)
- .writeBoolean(session.setupFailed)
- .writeInt(session.disconnectReasonCode)
- .writeInt(session.disconnectExtraCode)
- .writeString(session.disconnectExtraMessage)
- .writeInt(session.ratAtStart)
- .writeInt(session.ratAtEnd)
- .writeLong(session.ratSwitchCount)
- .writeLong(session.codecBitmask)
- .writeInt(session.concurrentCallCountAtStart)
- .writeInt(session.concurrentCallCountAtEnd)
- .writeInt(session.simSlotIndex)
- .writeBoolean(session.isMultiSim)
- .writeBoolean(session.isEsim)
- .writeInt(session.carrierId)
- .writeBoolean(session.srvccCompleted)
- .writeLong(session.srvccFailureCount)
- .writeLong(session.srvccCancellationCount)
- .writeBoolean(session.rttEnabled)
- .writeBoolean(session.isEmergency)
- .writeBoolean(session.isRoaming)
+ return TelephonyStatsLog.buildStatsEvent(
+ VOICE_CALL_SESSION,
+ session.bearerAtStart,
+ session.bearerAtEnd,
+ session.direction,
+ session.setupDuration,
+ session.setupFailed,
+ session.disconnectReasonCode,
+ session.disconnectExtraCode,
+ session.disconnectExtraMessage,
+ session.ratAtStart,
+ session.ratAtEnd,
+ session.ratSwitchCount,
+ session.codecBitmask,
+ session.concurrentCallCountAtStart,
+ session.concurrentCallCountAtEnd,
+ session.simSlotIndex,
+ session.isMultiSim,
+ session.isEsim,
+ session.carrierId,
+ session.srvccCompleted,
+ session.srvccFailureCount,
+ session.srvccCancellationCount,
+ session.rttEnabled,
+ session.isEmergency,
+ session.isRoaming,
// workaround: dimension required for keeping multiple pulled atoms
- .writeInt(sRandom.nextInt())
- .build();
+ sRandom.nextInt(),
+ // New fields introduced in Android S
+ session.signalStrengthAtEnd,
+ session.bandAtEnd,
+ session.setupDurationMillis,
+ session.mainCodecQuality,
+ session.videoEnabled,
+ session.ratAtConnected,
+ session.isMultiparty);
+ }
+
+ private static StatsEvent buildStatsEvent(IncomingSms sms) {
+ return TelephonyStatsLog.buildStatsEvent(
+ INCOMING_SMS,
+ sms.smsFormat,
+ sms.smsTech,
+ sms.rat,
+ sms.smsType,
+ sms.totalParts,
+ sms.receivedParts,
+ sms.blocked,
+ sms.error,
+ sms.isRoaming,
+ sms.simSlotIndex,
+ sms.isMultiSim,
+ sms.isEsim,
+ sms.carrierId,
+ sms.messageId);
+ }
+
+ private static StatsEvent buildStatsEvent(OutgoingSms sms) {
+ return TelephonyStatsLog.buildStatsEvent(
+ OUTGOING_SMS,
+ sms.smsFormat,
+ sms.smsTech,
+ sms.rat,
+ sms.sendResult,
+ sms.errorCode,
+ sms.isRoaming,
+ sms.isFromDefaultApp,
+ sms.simSlotIndex,
+ sms.isMultiSim,
+ sms.isEsim,
+ sms.carrierId,
+ sms.messageId,
+ sms.retryId);
+ }
+
+ private static StatsEvent buildStatsEvent(DataCallSession dataCallSession) {
+ return TelephonyStatsLog.buildStatsEvent(
+ DATA_CALL_SESSION,
+ dataCallSession.dimension,
+ dataCallSession.isMultiSim,
+ dataCallSession.isEsim,
+ 0, // profile is deprecated, so we default to 0
+ dataCallSession.apnTypeBitmask,
+ dataCallSession.carrierId,
+ dataCallSession.isRoaming,
+ dataCallSession.ratAtEnd,
+ dataCallSession.oosAtEnd,
+ dataCallSession.ratSwitchCount,
+ dataCallSession.isOpportunistic,
+ dataCallSession.ipType,
+ dataCallSession.setupFailed,
+ dataCallSession.failureCause,
+ dataCallSession.suggestedRetryMillis,
+ dataCallSession.deactivateReason,
+ round(dataCallSession.durationMinutes, DURATION_BUCKET_MILLIS / MINUTE_IN_MILLIS),
+ dataCallSession.ongoing,
+ dataCallSession.bandAtEnd);
+ }
+
+ private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ IMS_REGISTRATION_STATS,
+ stats.carrierId,
+ stats.simSlotIndex,
+ stats.rat,
+ (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ (int) (round(stats.voiceCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ (int)
+ (round(stats.voiceAvailableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS),
+ (int) (round(stats.smsCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ (int) (round(stats.smsAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ (int) (round(stats.videoCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ (int)
+ (round(stats.videoAvailableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS),
+ (int) (round(stats.utCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ (int) (round(stats.utAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ }
+
+ private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) {
+ return TelephonyStatsLog.buildStatsEvent(
+ IMS_REGISTRATION_TERMINATION,
+ termination.carrierId,
+ termination.isMultiSim,
+ termination.ratAtEnd,
+ termination.setupFailed,
+ termination.reasonCode,
+ termination.extraCode,
+ termination.extraMessage,
+ termination.count);
+ }
+
+ private static StatsEvent buildStatsEvent(NetworkRequests networkRequests) {
+ return TelephonyStatsLog.buildStatsEvent(
+ TELEPHONY_NETWORK_REQUESTS,
+ networkRequests.carrierId,
+ networkRequests.enterpriseRequestCount,
+ networkRequests.enterpriseReleaseCount);
+ }
+
+ /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
+ private static Phone[] getPhonesIfAny() {
+ try {
+ return PhoneFactory.getPhones();
+ } catch (IllegalStateException e) {
+ // Phones have not been made yet
+ return new Phone[0];
+ }
}
/** Returns the value rounded to the bucket. */
private static long round(long value, long bucket) {
- return ((value + bucket / 2) / bucket) * bucket;
+ return bucket == 0 ? value : ((value + bucket / 2) / bucket) * bucket;
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java b/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
index 67816c9ef9..ad27acb3c7 100644
--- a/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/ModemPowerMetrics.java
@@ -71,7 +71,7 @@ public class ModemPowerMetrics {
m.rxTimeMs = stats.getRxTimeMillis();
List<Long> txTimeMillis = new ArrayList<>();
- for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+ for (int i = 0; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
long t = stats.getTxTimeMillis(i);
if (t >= 0) {
txTimeMillis.add(t);
diff --git a/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java b/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
new file mode 100644
index 0000000000..343bd4230d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.internal.telephony.TelephonyStatsLog.MODEM_RESTART;
+
+import android.os.Build;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.telephony.Rlog;
+
+/** Metrics for the modem restarts. */
+public class ModemRestartStats {
+ private static final String TAG = ModemRestartStats.class.getSimpleName();
+
+ /* Maximum length of the baseband version. */
+ private static final int MAX_BASEBAND_LEN = 100;
+
+ /* Maximum length of the modem restart reason. */
+ private static final int MAX_REASON_LEN = 100;
+
+ private ModemRestartStats() { }
+
+ /** Generate metrics when modem restart occurs. */
+ public static void onModemRestart(String reason) {
+ reason = truncateString(reason, MAX_REASON_LEN);
+ String basebandVersion = truncateString(Build.getRadioVersion(), MAX_BASEBAND_LEN);
+ int carrierId = getCarrierId();
+
+ Rlog.d(TAG, "Modem restart (carrier=" + carrierId + "): " + reason);
+ TelephonyStatsLog.write(MODEM_RESTART, basebandVersion, reason, carrierId);
+ }
+
+ private static String truncateString(String string, int maxLen) {
+ string = nullToEmpty(string);
+ if (string.length() > maxLen) {
+ string = string.substring(0, maxLen);
+ }
+ return string;
+ }
+
+ private static String nullToEmpty(String string) {
+ return string != null ? string : "";
+ }
+
+ /** Returns the carrier ID of the first SIM card for which carrier ID is available. */
+ private static int getCarrierId() {
+ int carrierId = INVALID_SUBSCRIPTION_ID;
+ try {
+ for (Phone phone : PhoneFactory.getPhones()) {
+ carrierId = phone.getCarrierId();
+ if (carrierId != INVALID_SUBSCRIPTION_ID) {
+ break;
+ }
+ }
+ } catch (IllegalStateException e) {
+ // Nothing to do here.
+ }
+ return carrierId;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java b/src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java
new file mode 100644
index 0000000000..2975e727b4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/NetworkRequestsStats.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequests;
+
+
+/** Metrics for the network requests. */
+public class NetworkRequestsStats {
+ private NetworkRequestsStats() { }
+
+ /** Generate metrics when network request occurs. */
+ public static void addNetworkRequest(NetworkRequest networkRequest, int subId) {
+ if (!networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) {
+ // Currently only handle enterprise
+ return;
+ }
+ NetworkRequests networkRequests = new NetworkRequests();
+ networkRequests.carrierId = getCarrierId(subId);
+ networkRequests.enterpriseRequestCount = 1;
+
+ PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ storage.addNetworkRequests(networkRequests);
+ }
+
+ /** Generate metrics when network release occurs. */
+ public static void addNetworkRelease(NetworkRequest networkRequest, int subId) {
+ if (!networkRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)) {
+ // Currently only handle enterprise
+ return;
+ }
+ NetworkRequests networkRequests = new NetworkRequests();
+ networkRequests.carrierId = getCarrierId(subId);
+ networkRequests.enterpriseReleaseCount = 1;
+
+ PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ storage.addNetworkRequests(networkRequests);
+ }
+
+ /** Returns the carrier ID of the given subscription id. */
+ private static int getCarrierId(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ return phone != null ? phone.getCarrierId() : TelephonyManager.UNKNOWN_CARRIER_ID;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index f208369bfd..7265f845e2 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -18,18 +18,34 @@ package com.android.internal.telephony.metrics;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.telephony.TelephonyManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequests;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
+import com.android.internal.util.ArrayUtils;
import com.android.telephony.Rlog;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.security.SecureRandom;
import java.util.Arrays;
+import java.util.stream.IntStream;
/**
* Stores and aggregates metrics that should not be pulled at arbitrary frequency.
@@ -43,49 +59,259 @@ public class PersistAtomsStorage {
/** Name of the file where cached statistics are saved to. */
private static final String FILENAME = "persist_atoms.pb";
- /** Maximum number of call sessions to store during pulls. */
+ /** Delay to store atoms to persistent storage to bundle multiple operations together. */
+ private static final int SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS = 30000;
+
+ /**
+ * Delay to store atoms to persistent storage during pulls to avoid unnecessary operations.
+ *
+ * <p>This delay should be short to avoid duplicating atoms or losing pull timestamp in case of
+ * crash or power loss.
+ */
+ private static final int SAVE_TO_FILE_DELAY_FOR_GET_MILLIS = 500;
+
+ /** Maximum number of call sessions to store between pulls. */
private static final int MAX_NUM_CALL_SESSIONS = 50;
+ /**
+ * Maximum number of SMS to store between pulls. Incoming messages and outgoing messages are
+ * counted separately.
+ */
+ private static final int MAX_NUM_SMS = 25;
+
+ /**
+ * Maximum number of carrier ID mismatch events stored on the device to avoid sending duplicated
+ * metrics.
+ */
+ private static final int MAX_CARRIER_ID_MISMATCH = 40;
+
+ /** Maximum number of data call sessions to store during pulls. */
+ private static final int MAX_NUM_DATA_CALL_SESSIONS = 15;
+
+ /** Maximum number of service states to store between pulls. */
+ private static final int MAX_NUM_CELLULAR_SERVICE_STATES = 50;
+
+ /** Maximum number of data service switches to store between pulls. */
+ private static final int MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES = 50;
+
+ /** Maximum number of IMS registration stats to store between pulls. */
+ private static final int MAX_NUM_IMS_REGISTRATION_STATS = 10;
+
+ /** Maximum number of IMS registration terminations to store between pulls. */
+ private static final int MAX_NUM_IMS_REGISTRATION_TERMINATIONS = 10;
+
/** Stores persist atoms and persist states of the puller. */
@VisibleForTesting protected final PersistAtoms mAtoms;
/** Aggregates RAT duration and call count. */
private final VoiceCallRatTracker mVoiceCallRatTracker;
+ /** Whether atoms should be saved immediately, skipping the delay. */
+ @VisibleForTesting protected boolean mSaveImmediately;
+
private final Context mContext;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
private static final SecureRandom sRandom = new SecureRandom();
+ private Runnable mSaveRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ saveAtomsToFileNow();
+ }
+ };
+
public PersistAtomsStorage(Context context) {
mContext = context;
mAtoms = loadAtomsFromFile();
- mVoiceCallRatTracker = VoiceCallRatTracker.fromProto(mAtoms.rawVoiceCallRatUsage);
+ mVoiceCallRatTracker = VoiceCallRatTracker.fromProto(mAtoms.voiceCallRatUsage);
+
+ mHandlerThread = new HandlerThread("PersistAtomsThread");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mSaveImmediately = false;
}
/** Adds a call to the storage. */
public synchronized void addVoiceCallSession(VoiceCallSession call) {
- int newLength = mAtoms.voiceCallSession.length + 1;
- if (newLength > MAX_NUM_CALL_SESSIONS) {
- // will evict one previous call randomly instead of making the array larger
- newLength = MAX_NUM_CALL_SESSIONS;
- } else {
- mAtoms.voiceCallSession = Arrays.copyOf(mAtoms.voiceCallSession, newLength);
- }
- int insertAt = 0;
- if (newLength > 1) {
- // shuffle when each call is added, or randomly replace a previous call instead if
- // MAX_NUM_CALL_SESSIONS is reached (call at the last index is evicted).
- insertAt = sRandom.nextInt(newLength);
- mAtoms.voiceCallSession[newLength - 1] = mAtoms.voiceCallSession[insertAt];
- }
- mAtoms.voiceCallSession[insertAt] = call;
- saveAtomsToFile();
+ mAtoms.voiceCallSession =
+ insertAtRandomPlace(mAtoms.voiceCallSession, call, MAX_NUM_CALL_SESSIONS);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+
+ Rlog.d(TAG, "Add new voice call session: " + call.toString());
}
/** Adds RAT usages to the storage when a call session ends. */
public synchronized void addVoiceCallRatUsage(VoiceCallRatTracker ratUsages) {
mVoiceCallRatTracker.mergeWith(ratUsages);
- mAtoms.rawVoiceCallRatUsage = mVoiceCallRatTracker.toProto();
- saveAtomsToFile();
+ mAtoms.voiceCallRatUsage = mVoiceCallRatTracker.toProto();
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds an incoming SMS to the storage. */
+ public synchronized void addIncomingSms(IncomingSms sms) {
+ mAtoms.incomingSms = insertAtRandomPlace(mAtoms.incomingSms, sms, MAX_NUM_SMS);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+
+ // To be removed
+ Rlog.d(TAG, "Add new incoming SMS atom: " + sms.toString());
+ }
+
+ /** Adds an outgoing SMS to the storage. */
+ public synchronized void addOutgoingSms(OutgoingSms sms) {
+ // Update the retry id, if needed, so that it's unique and larger than all
+ // previous ones. (this algorithm ignores the fact that some SMS atoms might
+ // be dropped due to limit in size of the array).
+ for (OutgoingSms storedSms : mAtoms.outgoingSms) {
+ if (storedSms.messageId == sms.messageId && storedSms.retryId >= sms.retryId) {
+ sms.retryId = storedSms.retryId + 1;
+ }
+ }
+
+ mAtoms.outgoingSms = insertAtRandomPlace(mAtoms.outgoingSms, sms, MAX_NUM_SMS);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+
+ // To be removed
+ Rlog.d(TAG, "Add new outgoing SMS atom: " + sms.toString());
+ }
+
+ /** Adds a service state to the storage, together with data service switch if any. */
+ public synchronized void addCellularServiceStateAndCellularDataServiceSwitch(
+ CellularServiceState state, @Nullable CellularDataServiceSwitch serviceSwitch) {
+ CellularServiceState existingState = find(state);
+ if (existingState != null) {
+ existingState.totalTimeMillis += state.totalTimeMillis;
+ existingState.lastUsedMillis = getWallTimeMillis();
+ } else {
+ state.lastUsedMillis = getWallTimeMillis();
+ mAtoms.cellularServiceState =
+ insertAtRandomPlace(
+ mAtoms.cellularServiceState, state, MAX_NUM_CELLULAR_SERVICE_STATES);
+ }
+
+ if (serviceSwitch != null) {
+ CellularDataServiceSwitch existingSwitch = find(serviceSwitch);
+ if (existingSwitch != null) {
+ existingSwitch.switchCount += serviceSwitch.switchCount;
+ existingSwitch.lastUsedMillis = getWallTimeMillis();
+ } else {
+ serviceSwitch.lastUsedMillis = getWallTimeMillis();
+ mAtoms.cellularDataServiceSwitch =
+ insertAtRandomPlace(
+ mAtoms.cellularDataServiceSwitch,
+ serviceSwitch,
+ MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES);
+ }
+ }
+
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a data call session to the storage. */
+ public synchronized void addDataCallSession(DataCallSession dataCall) {
+ mAtoms.dataCallSession =
+ insertAtRandomPlace(mAtoms.dataCallSession, dataCall, MAX_NUM_DATA_CALL_SESSIONS);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /**
+ * Adds a new carrier ID mismatch event to the storage.
+ *
+ * @return true if the item was not present and was added to the persistent storage, false
+ * otherwise.
+ */
+ public synchronized boolean addCarrierIdMismatch(CarrierIdMismatch carrierIdMismatch) {
+ // Check if the details of the SIM cards are already present and in case return.
+ if (find(carrierIdMismatch) != null) {
+ return false;
+ }
+ // Add the new CarrierIdMismatch at the end of the array, so that the same atom will not be
+ // sent again in future.
+ if (mAtoms.carrierIdMismatch.length == MAX_CARRIER_ID_MISMATCH) {
+ System.arraycopy(
+ mAtoms.carrierIdMismatch,
+ 1,
+ mAtoms.carrierIdMismatch,
+ 0,
+ MAX_CARRIER_ID_MISMATCH - 1);
+ mAtoms.carrierIdMismatch[MAX_CARRIER_ID_MISMATCH - 1] = carrierIdMismatch;
+ } else {
+ int newLength = mAtoms.carrierIdMismatch.length + 1;
+ mAtoms.carrierIdMismatch = Arrays.copyOf(mAtoms.carrierIdMismatch, newLength);
+ mAtoms.carrierIdMismatch[newLength - 1] = carrierIdMismatch;
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ return true;
+ }
+
+ /** Adds IMS registration stats to the storage. */
+ public synchronized void addImsRegistrationStats(ImsRegistrationStats stats) {
+ ImsRegistrationStats existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.registeredMillis += stats.registeredMillis;
+ existingStats.voiceCapableMillis += stats.voiceCapableMillis;
+ existingStats.voiceAvailableMillis += stats.voiceAvailableMillis;
+ existingStats.smsCapableMillis += stats.smsCapableMillis;
+ existingStats.smsAvailableMillis += stats.smsAvailableMillis;
+ existingStats.videoCapableMillis += stats.videoCapableMillis;
+ existingStats.videoAvailableMillis += stats.videoAvailableMillis;
+ existingStats.utCapableMillis += stats.utCapableMillis;
+ existingStats.utAvailableMillis += stats.utAvailableMillis;
+ existingStats.lastUsedMillis = getWallTimeMillis();
+ } else {
+ stats.lastUsedMillis = getWallTimeMillis();
+ mAtoms.imsRegistrationStats =
+ insertAtRandomPlace(
+ mAtoms.imsRegistrationStats, stats, MAX_NUM_IMS_REGISTRATION_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds IMS registration termination to the storage. */
+ public synchronized void addImsRegistrationTermination(ImsRegistrationTermination termination) {
+ ImsRegistrationTermination existingTermination = find(termination);
+ if (existingTermination != null) {
+ existingTermination.count += termination.count;
+ existingTermination.lastUsedMillis = getWallTimeMillis();
+ } else {
+ termination.lastUsedMillis = getWallTimeMillis();
+ mAtoms.imsRegistrationTermination =
+ insertAtRandomPlace(
+ mAtoms.imsRegistrationTermination,
+ termination,
+ MAX_NUM_IMS_REGISTRATION_TERMINATIONS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /**
+ * Stores the version of the carrier ID matching table.
+ *
+ * @return true if the version is newer than last available version, false otherwise.
+ */
+ public synchronized boolean setCarrierIdTableVersion(int carrierIdTableVersion) {
+ if (mAtoms.carrierIdTableVersion < carrierIdTableVersion) {
+ mAtoms.carrierIdTableVersion = carrierIdTableVersion;
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /** Adds a new {@link NetworkRequests} to the storage. */
+ public synchronized void addNetworkRequests(NetworkRequests networkRequests) {
+ NetworkRequests existingMetrics = find(networkRequests);
+ if (existingMetrics != null) {
+ existingMetrics.enterpriseRequestCount += networkRequests.enterpriseRequestCount;
+ existingMetrics.enterpriseReleaseCount += networkRequests.enterpriseReleaseCount;
+ } else {
+ int newLength = mAtoms.networkRequests.length + 1;
+ mAtoms.networkRequests = Arrays.copyOf(mAtoms.networkRequests, newLength);
+ mAtoms.networkRequests[newLength - 1] = networkRequests;
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
}
/**
@@ -98,7 +324,7 @@ public class PersistAtomsStorage {
mAtoms.voiceCallSessionPullTimestampMillis = getWallTimeMillis();
VoiceCallSession[] previousCalls = mAtoms.voiceCallSession;
mAtoms.voiceCallSession = new VoiceCallSession[0];
- saveAtomsToFile();
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
return previousCalls;
} else {
return null;
@@ -110,53 +336,267 @@ public class PersistAtomsStorage {
* minIntervalMillis} ago, otherwise returns {@code null}.
*/
@Nullable
- public synchronized RawVoiceCallRatUsage[] getVoiceCallRatUsages(long minIntervalMillis) {
- if (getWallTimeMillis() - mAtoms.rawVoiceCallRatUsagePullTimestampMillis
- > minIntervalMillis) {
- mAtoms.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
- RawVoiceCallRatUsage[] previousUsages = mAtoms.rawVoiceCallRatUsage;
+ public synchronized VoiceCallRatUsage[] getVoiceCallRatUsages(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.voiceCallRatUsagePullTimestampMillis > minIntervalMillis) {
+ mAtoms.voiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
+ VoiceCallRatUsage[] previousUsages = mAtoms.voiceCallRatUsage;
mVoiceCallRatTracker.clear();
- mAtoms.rawVoiceCallRatUsage = new RawVoiceCallRatUsage[0];
- saveAtomsToFile();
+ mAtoms.voiceCallRatUsage = new VoiceCallRatUsage[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
return previousUsages;
} else {
return null;
}
}
+ /**
+ * Returns and clears the incoming SMS if last pulled longer than {@code minIntervalMillis} ago,
+ * otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized IncomingSms[] getIncomingSms(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.incomingSmsPullTimestampMillis > minIntervalMillis) {
+ mAtoms.incomingSmsPullTimestampMillis = getWallTimeMillis();
+ IncomingSms[] previousIncomingSms = mAtoms.incomingSms;
+ mAtoms.incomingSms = new IncomingSms[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousIncomingSms;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the outgoing SMS if last pulled longer than {@code minIntervalMillis} ago,
+ * otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized OutgoingSms[] getOutgoingSms(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.outgoingSmsPullTimestampMillis > minIntervalMillis) {
+ mAtoms.outgoingSmsPullTimestampMillis = getWallTimeMillis();
+ OutgoingSms[] previousOutgoingSms = mAtoms.outgoingSms;
+ mAtoms.outgoingSms = new OutgoingSms[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousOutgoingSms;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the data call session if last pulled longer than {@code minIntervalMillis}
+ * ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized DataCallSession[] getDataCallSessions(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.dataCallSessionPullTimestampMillis > minIntervalMillis) {
+ mAtoms.dataCallSessionPullTimestampMillis = getWallTimeMillis();
+ DataCallSession[] previousDataCallSession = mAtoms.dataCallSession;
+ mAtoms.dataCallSession = new DataCallSession[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousDataCallSession;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the service state durations if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized CellularServiceState[] getCellularServiceStates(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.cellularServiceStatePullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.cellularServiceStatePullTimestampMillis = getWallTimeMillis();
+ CellularServiceState[] previousStates = mAtoms.cellularServiceState;
+ Arrays.stream(previousStates).forEach(state -> state.lastUsedMillis = 0L);
+ mAtoms.cellularServiceState = new CellularServiceState[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStates;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the service state durations if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized CellularDataServiceSwitch[] getCellularDataServiceSwitches(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.cellularDataServiceSwitchPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.cellularDataServiceSwitchPullTimestampMillis = getWallTimeMillis();
+ CellularDataServiceSwitch[] previousSwitches = mAtoms.cellularDataServiceSwitch;
+ Arrays.stream(previousSwitches)
+ .forEach(serviceSwitch -> serviceSwitch.lastUsedMillis = 0L);
+ mAtoms.cellularDataServiceSwitch = new CellularDataServiceSwitch[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousSwitches;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the IMS registration statistics if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsRegistrationStats[] getImsRegistrationStats(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsRegistrationStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsRegistrationStatsPullTimestampMillis = getWallTimeMillis();
+ ImsRegistrationStats[] previousStats = mAtoms.imsRegistrationStats;
+ Arrays.stream(previousStats).forEach(stats -> stats.lastUsedMillis = 0L);
+ mAtoms.imsRegistrationStats = new ImsRegistrationStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the IMS registration terminations if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsRegistrationTermination[] getImsRegistrationTerminations(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsRegistrationTerminationPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsRegistrationTerminationPullTimestampMillis = getWallTimeMillis();
+ ImsRegistrationTermination[] previousTerminations = mAtoms.imsRegistrationTermination;
+ Arrays.stream(previousTerminations)
+ .forEach(termination -> termination.lastUsedMillis = 0L);
+ mAtoms.imsRegistrationTermination = new ImsRegistrationTermination[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousTerminations;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the network requests if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized NetworkRequests[] getNetworkRequests(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.networkRequestsPullTimestampMillis > minIntervalMillis) {
+ mAtoms.networkRequestsPullTimestampMillis = getWallTimeMillis();
+ NetworkRequests[] previousNetworkRequests = mAtoms.networkRequests;
+ mAtoms.networkRequests = new NetworkRequests[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousNetworkRequests;
+ } else {
+ return null;
+ }
+ }
+
/** Loads {@link PersistAtoms} from a file in private storage. */
private PersistAtoms loadAtomsFromFile() {
try {
- PersistAtoms atomsFromFile =
+ PersistAtoms atoms =
PersistAtoms.parseFrom(
Files.readAllBytes(mContext.getFileStreamPath(FILENAME).toPath()));
- // check all the fields in case of situations such as OTA or crash during saving
- if (atomsFromFile.rawVoiceCallRatUsage == null) {
- atomsFromFile.rawVoiceCallRatUsage = new RawVoiceCallRatUsage[0];
- }
- if (atomsFromFile.voiceCallSession == null) {
- atomsFromFile.voiceCallSession = new VoiceCallSession[0];
- }
- if (atomsFromFile.voiceCallSession.length > MAX_NUM_CALL_SESSIONS) {
- atomsFromFile.voiceCallSession =
- Arrays.copyOf(atomsFromFile.voiceCallSession, MAX_NUM_CALL_SESSIONS);
+ // Start from scratch if build changes, since mixing atoms from different builds could
+ // produce strange results
+ if (!Build.FINGERPRINT.equals(atoms.buildFingerprint)) {
+ Rlog.d(TAG, "Build changed");
+ return makeNewPersistAtoms();
}
- // out of caution, set timestamps to now if they are missing
- if (atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis == 0L) {
- atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
- }
- if (atomsFromFile.voiceCallSessionPullTimestampMillis == 0L) {
- atomsFromFile.voiceCallSessionPullTimestampMillis = getWallTimeMillis();
- }
- return atomsFromFile;
+ // check all the fields in case of situations such as OTA or crash during saving
+ atoms.voiceCallRatUsage =
+ sanitizeAtoms(atoms.voiceCallRatUsage, VoiceCallRatUsage.class);
+ atoms.voiceCallSession =
+ sanitizeAtoms(
+ atoms.voiceCallSession, VoiceCallSession.class, MAX_NUM_CALL_SESSIONS);
+ atoms.incomingSms = sanitizeAtoms(atoms.incomingSms, IncomingSms.class, MAX_NUM_SMS);
+ atoms.outgoingSms = sanitizeAtoms(atoms.outgoingSms, OutgoingSms.class, MAX_NUM_SMS);
+ atoms.carrierIdMismatch =
+ sanitizeAtoms(
+ atoms.carrierIdMismatch,
+ CarrierIdMismatch.class,
+ MAX_CARRIER_ID_MISMATCH);
+ atoms.dataCallSession =
+ sanitizeAtoms(
+ atoms.dataCallSession,
+ DataCallSession.class,
+ MAX_NUM_DATA_CALL_SESSIONS);
+ atoms.cellularServiceState =
+ sanitizeAtoms(
+ atoms.cellularServiceState,
+ CellularServiceState.class,
+ MAX_NUM_CELLULAR_SERVICE_STATES);
+ atoms.cellularDataServiceSwitch =
+ sanitizeAtoms(
+ atoms.cellularDataServiceSwitch,
+ CellularDataServiceSwitch.class,
+ MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES);
+ atoms.imsRegistrationStats =
+ sanitizeAtoms(
+ atoms.imsRegistrationStats,
+ ImsRegistrationStats.class,
+ MAX_NUM_IMS_REGISTRATION_STATS);
+ atoms.imsRegistrationTermination =
+ sanitizeAtoms(
+ atoms.imsRegistrationTermination,
+ ImsRegistrationTermination.class,
+ MAX_NUM_IMS_REGISTRATION_TERMINATIONS);
+ atoms.networkRequests = sanitizeAtoms(atoms.networkRequests, NetworkRequests.class);
+ // out of caution, sanitize also the timestamps
+ atoms.voiceCallRatUsagePullTimestampMillis =
+ sanitizeTimestamp(atoms.voiceCallRatUsagePullTimestampMillis);
+ atoms.voiceCallSessionPullTimestampMillis =
+ sanitizeTimestamp(atoms.voiceCallSessionPullTimestampMillis);
+ atoms.incomingSmsPullTimestampMillis =
+ sanitizeTimestamp(atoms.incomingSmsPullTimestampMillis);
+ atoms.outgoingSmsPullTimestampMillis =
+ sanitizeTimestamp(atoms.outgoingSmsPullTimestampMillis);
+ atoms.dataCallSessionPullTimestampMillis =
+ sanitizeTimestamp(atoms.dataCallSessionPullTimestampMillis);
+ atoms.cellularServiceStatePullTimestampMillis =
+ sanitizeTimestamp(atoms.cellularServiceStatePullTimestampMillis);
+ atoms.cellularDataServiceSwitchPullTimestampMillis =
+ sanitizeTimestamp(atoms.cellularDataServiceSwitchPullTimestampMillis);
+ atoms.imsRegistrationStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsRegistrationStatsPullTimestampMillis);
+ atoms.imsRegistrationTerminationPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsRegistrationTerminationPullTimestampMillis);
+ atoms.networkRequestsPullTimestampMillis =
+ sanitizeTimestamp(atoms.networkRequestsPullTimestampMillis);
+ return atoms;
+ } catch (NoSuchFileException e) {
+ Rlog.d(TAG, "PersistAtoms file not found");
} catch (IOException | NullPointerException e) {
Rlog.e(TAG, "cannot load/parse PersistAtoms", e);
- return makeNewPersistAtoms();
}
+ return makeNewPersistAtoms();
+ }
+
+ /**
+ * Posts message to save a copy of {@link PersistAtoms} to a file after a delay.
+ *
+ * <p>The delay is introduced to avoid too frequent operations to disk, which would negatively
+ * impact the power consumption.
+ */
+ private void saveAtomsToFile(int delayMillis) {
+ if (delayMillis > 0 && !mSaveImmediately) {
+ mHandler.removeCallbacks(mSaveRunnable);
+ if (mHandler.postDelayed(mSaveRunnable, delayMillis)) {
+ return;
+ }
+ }
+ // In case of error posting the event or if delay is 0, save immediately
+ saveAtomsToFileNow();
}
/** Saves a copy of {@link PersistAtoms} to a file in private storage. */
- private void saveAtomsToFile() {
+ private synchronized void saveAtomsToFileNow() {
try (FileOutputStream stream = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE)) {
stream.write(PersistAtoms.toByteArray(mAtoms));
} catch (IOException e) {
@@ -164,19 +604,203 @@ public class PersistAtomsStorage {
}
}
+ /**
+ * Returns the service state that has the same dimension values with the given one, or {@code
+ * null} if it does not exist.
+ */
+ private @Nullable CellularServiceState find(CellularServiceState key) {
+ for (CellularServiceState state : mAtoms.cellularServiceState) {
+ if (state.voiceRat == key.voiceRat
+ && state.dataRat == key.dataRat
+ && state.voiceRoamingType == key.voiceRoamingType
+ && state.dataRoamingType == key.dataRoamingType
+ && state.isEndc == key.isEndc
+ && state.simSlotIndex == key.simSlotIndex
+ && state.isMultiSim == key.isMultiSim
+ && state.carrierId == key.carrierId) {
+ return state;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the data service switch that has the same dimension values with the given one, or
+ * {@code null} if it does not exist.
+ */
+ private @Nullable CellularDataServiceSwitch find(CellularDataServiceSwitch key) {
+ for (CellularDataServiceSwitch serviceSwitch : mAtoms.cellularDataServiceSwitch) {
+ if (serviceSwitch.ratFrom == key.ratFrom
+ && serviceSwitch.ratTo == key.ratTo
+ && serviceSwitch.simSlotIndex == key.simSlotIndex
+ && serviceSwitch.isMultiSim == key.isMultiSim
+ && serviceSwitch.carrierId == key.carrierId) {
+ return serviceSwitch;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the carrier ID mismatch event that has the same dimension values with the given one,
+ * or {@code null} if it does not exist.
+ */
+ private @Nullable CarrierIdMismatch find(CarrierIdMismatch key) {
+ for (CarrierIdMismatch mismatch : mAtoms.carrierIdMismatch) {
+ if (mismatch.mccMnc.equals(key.mccMnc)
+ && mismatch.gid1.equals(key.gid1)
+ && mismatch.spn.equals(key.spn)
+ && mismatch.pnn.equals(key.pnn)) {
+ return mismatch;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the IMS registration stats that has the same dimension values with the given one, or
+ * {@code null} if it does not exist.
+ */
+ private @Nullable ImsRegistrationStats find(ImsRegistrationStats key) {
+ for (ImsRegistrationStats stats : mAtoms.imsRegistrationStats) {
+ if (stats.carrierId == key.carrierId
+ && stats.simSlotIndex == key.simSlotIndex
+ && stats.rat == key.rat) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the IMS registration termination that has the same dimension values with the given
+ * one, or {@code null} if it does not exist.
+ */
+ private @Nullable ImsRegistrationTermination find(ImsRegistrationTermination key) {
+ for (ImsRegistrationTermination termination : mAtoms.imsRegistrationTermination) {
+ if (termination.carrierId == key.carrierId
+ && termination.isMultiSim == key.isMultiSim
+ && termination.ratAtEnd == key.ratAtEnd
+ && termination.setupFailed == key.setupFailed
+ && termination.reasonCode == key.reasonCode
+ && termination.extraCode == key.extraCode
+ && termination.extraMessage.equals(key.extraMessage)) {
+ return termination;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the network requests event that has the same carrier id as the given one,
+ * or {@code null} if it does not exist.
+ */
+ private @Nullable NetworkRequests find(NetworkRequests key) {
+ for (NetworkRequests item : mAtoms.networkRequests) {
+ if (item.carrierId == key.carrierId) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Inserts a new element in a random position in an array with a maximum size, replacing the
+ * least recent item if possible.
+ */
+ private static <T> T[] insertAtRandomPlace(T[] storage, T instance, int maxLength) {
+ final int newLength = storage.length + 1;
+ final boolean arrayFull = (newLength > maxLength);
+ T[] result = Arrays.copyOf(storage, arrayFull ? maxLength : newLength);
+ if (newLength == 1) {
+ result[0] = instance;
+ } else if (arrayFull) {
+ result[findItemToEvict(storage)] = instance;
+ } else {
+ // insert at random place (by moving the item at the random place to the end)
+ int insertAt = sRandom.nextInt(newLength);
+ result[newLength - 1] = result[insertAt];
+ result[insertAt] = instance;
+ }
+ return result;
+ }
+
+ /** Returns index of the item suitable for eviction when the array is full. */
+ private static <T> int findItemToEvict(T[] array) {
+ if (array instanceof CellularServiceState[]) {
+ CellularServiceState[] arr = (CellularServiceState[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ if (array instanceof CellularDataServiceSwitch[]) {
+ CellularDataServiceSwitch[] arr = (CellularDataServiceSwitch[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ if (array instanceof ImsRegistrationStats[]) {
+ ImsRegistrationStats[] arr = (ImsRegistrationStats[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ if (array instanceof ImsRegistrationTermination[]) {
+ ImsRegistrationTermination[] arr = (ImsRegistrationTermination[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ return sRandom.nextInt(array.length);
+ }
+
+ /** Sanitizes the loaded array of atoms to avoid null values. */
+ private <T> T[] sanitizeAtoms(T[] array, Class<T> cl) {
+ return ArrayUtils.emptyIfNull(array, cl);
+ }
+
+ /** Sanitizes the loaded array of atoms loaded to avoid null values and enforce max length. */
+ private <T> T[] sanitizeAtoms(T[] array, Class<T> cl, int maxLength) {
+ array = sanitizeAtoms(array, cl);
+ if (array.length > maxLength) {
+ return Arrays.copyOf(array, maxLength);
+ }
+ return array;
+ }
+
+ /** Sanitizes the timestamp of the last pull loaded from persistent storage. */
+ private long sanitizeTimestamp(long timestamp) {
+ return timestamp <= 0L ? getWallTimeMillis() : timestamp;
+ }
+
/** Returns an empty PersistAtoms with pull timestamp set to current time. */
private PersistAtoms makeNewPersistAtoms() {
PersistAtoms atoms = new PersistAtoms();
// allow pulling only after some time so data are sufficiently aggregated
- atoms.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
- atoms.voiceCallSessionPullTimestampMillis = getWallTimeMillis();
+ long currentTime = getWallTimeMillis();
+ atoms.buildFingerprint = Build.FINGERPRINT;
+ atoms.voiceCallRatUsagePullTimestampMillis = currentTime;
+ atoms.voiceCallSessionPullTimestampMillis = currentTime;
+ atoms.incomingSmsPullTimestampMillis = currentTime;
+ atoms.outgoingSmsPullTimestampMillis = currentTime;
+ atoms.carrierIdTableVersion = TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION;
+ atoms.dataCallSessionPullTimestampMillis = currentTime;
+ atoms.cellularServiceStatePullTimestampMillis = currentTime;
+ atoms.cellularDataServiceSwitchPullTimestampMillis = currentTime;
+ atoms.imsRegistrationStatsPullTimestampMillis = currentTime;
+ atoms.imsRegistrationTerminationPullTimestampMillis = currentTime;
+ atoms.networkRequestsPullTimestampMillis = currentTime;
Rlog.d(TAG, "created new PersistAtoms");
return atoms;
}
@VisibleForTesting
protected long getWallTimeMillis() {
- // epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
+ // Epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
return System.currentTimeMillis();
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
new file mode 100644
index 0000000000..a45e43fcec
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkUtils;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.telephony.Rlog;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/** Tracks service state duration and switch metrics for each phone. */
+public class ServiceStateStats {
+ private static final String TAG = ServiceStateStats.class.getSimpleName();
+
+ private final AtomicReference<TimestampedServiceState> mLastState =
+ new AtomicReference<>(new TimestampedServiceState(null, 0L));
+ private final Phone mPhone;
+ private final PersistAtomsStorage mStorage;
+
+ public ServiceStateStats(Phone phone) {
+ mPhone = phone;
+ mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ }
+
+ /** Finalizes the durations of the current service state segment. */
+ public void conclude() {
+ final long now = getTimeMillis();
+ TimestampedServiceState lastState =
+ mLastState.getAndUpdate(
+ state -> new TimestampedServiceState(state.mServiceState, now));
+ addServiceState(lastState, now);
+ }
+
+ /** Updates the current service state. */
+ public void onServiceStateChanged(ServiceState serviceState) {
+ final long now = getTimeMillis();
+ if (isModemOff(serviceState)) {
+ // Finish the duration of last service state and mark modem off
+ addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now);
+ } else {
+ CellularServiceState newState = new CellularServiceState();
+ newState.voiceRat = getVoiceRat(mPhone, serviceState);
+ newState.dataRat = getDataRat(serviceState);
+ newState.voiceRoamingType = serviceState.getVoiceRoamingType();
+ newState.dataRoamingType = serviceState.getDataRoamingType();
+ newState.isEndc = isEndc(serviceState);
+ newState.simSlotIndex = mPhone.getPhoneId();
+ newState.isMultiSim = SimSlotState.isMultiSim();
+ newState.carrierId = mPhone.getCarrierId();
+
+ TimestampedServiceState prevState =
+ mLastState.getAndSet(new TimestampedServiceState(newState, now));
+ addServiceStateAndSwitch(
+ prevState, now, getDataServiceSwitch(prevState.mServiceState, newState));
+ }
+ }
+
+ private void addServiceState(TimestampedServiceState prevState, long now) {
+ addServiceStateAndSwitch(prevState, now, null);
+ }
+
+ private void addServiceStateAndSwitch(
+ TimestampedServiceState prevState,
+ long now,
+ @Nullable CellularDataServiceSwitch serviceSwitch) {
+ if (prevState.mServiceState == null) {
+ // Skip duration when modem is off
+ return;
+ }
+ if (now >= prevState.mTimestamp) {
+ CellularServiceState state = copyOf(prevState.mServiceState);
+ state.totalTimeMillis = now - prevState.mTimestamp;
+ mStorage.addCellularServiceStateAndCellularDataServiceSwitch(state, serviceSwitch);
+ } else {
+ Rlog.e(TAG, "addServiceState: durationMillis<0");
+ }
+ }
+
+ @Nullable
+ private CellularDataServiceSwitch getDataServiceSwitch(
+ @Nullable CellularServiceState prevState, CellularServiceState nextState) {
+ // Record switch only if multi-SIM state and carrier ID are the same and data RAT differs.
+ if (prevState != null
+ && prevState.isMultiSim == nextState.isMultiSim
+ && prevState.carrierId == nextState.carrierId
+ && prevState.dataRat != nextState.dataRat) {
+ CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
+ serviceSwitch.ratFrom = prevState.dataRat;
+ serviceSwitch.ratTo = nextState.dataRat;
+ serviceSwitch.isMultiSim = nextState.isMultiSim;
+ serviceSwitch.simSlotIndex = nextState.simSlotIndex;
+ serviceSwitch.carrierId = nextState.carrierId;
+ serviceSwitch.switchCount = 1;
+ return serviceSwitch;
+ } else {
+ return null;
+ }
+ }
+
+ /** Returns the service state for the given phone, or {@code null} if it cannot be obtained. */
+ @Nullable
+ private static ServiceState getServiceStateForPhone(Phone phone) {
+ ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
+ return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ }
+
+ /**
+ * Returns the band used from the given phone and RAT, or {@code 0} if it is invalid or cannot
+ * be determined.
+ */
+ static int getBand(Phone phone, @NetworkType int rat) {
+ ServiceState serviceState = getServiceStateForPhone(phone);
+ return getBand(serviceState, rat);
+ }
+
+ /**
+ * Returns the band used from the given service state and RAT, or {@code 0} if it is invalid or
+ * cannot be determined.
+ */
+ static int getBand(@Nullable ServiceState serviceState, @NetworkType int rat) {
+ if (serviceState == null) {
+ return 0; // Band unknown
+ }
+ int chNumber = serviceState.getChannelNumber();
+ int band;
+ switch (rat) {
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ band = AccessNetworkUtils.getOperatingBandForArfcn(chNumber);
+ break;
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ band = AccessNetworkUtils.getOperatingBandForUarfcn(chNumber);
+ break;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber);
+ break;
+ default:
+ band = 0;
+ break;
+ }
+ return band == AccessNetworkUtils.INVALID_BAND ? 0 : band;
+ }
+
+ private static CellularServiceState copyOf(CellularServiceState state) {
+ // MessageNano does not support clone, have to copy manually
+ CellularServiceState copy = new CellularServiceState();
+ copy.voiceRat = state.voiceRat;
+ copy.dataRat = state.dataRat;
+ copy.voiceRoamingType = state.voiceRoamingType;
+ copy.dataRoamingType = state.dataRoamingType;
+ copy.isEndc = state.isEndc;
+ copy.simSlotIndex = state.simSlotIndex;
+ copy.isMultiSim = state.isMultiSim;
+ copy.carrierId = state.carrierId;
+ copy.totalTimeMillis = state.totalTimeMillis;
+ return copy;
+ }
+
+ /**
+ * Returns {@code true} if modem radio is turned off (e.g. airplane mode).
+ *
+ * <p>Currently this is approximated by voice service state being {@code STATE_POWER_OFF}.
+ */
+ private static boolean isModemOff(ServiceState state) {
+ // TODO(b/189335473): we should get this info from phone's radio power state, which is
+ // updated separately
+ return state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
+ }
+
+ private static @NetworkType int getVoiceRat(Phone phone, ServiceState state) {
+ boolean isWifiCall =
+ phone.getImsPhone() != null
+ && phone.getImsPhone().isWifiCallingEnabled()
+ && state.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN;
+ return isWifiCall ? TelephonyManager.NETWORK_TYPE_IWLAN : state.getVoiceNetworkType();
+ }
+
+ private static @NetworkType int getDataRat(ServiceState state) {
+ final NetworkRegistrationInfo wwanRegInfo =
+ state.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return wwanRegInfo != null
+ ? wwanRegInfo.getAccessNetworkTechnology()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private static boolean isEndc(ServiceState state) {
+ if (getDataRat(state) != TelephonyManager.NETWORK_TYPE_LTE) {
+ return false;
+ }
+ int nrState = state.getNrState();
+ return nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED
+ || nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
+ }
+
+ @VisibleForTesting
+ protected long getTimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ private static final class TimestampedServiceState {
+ private final CellularServiceState mServiceState;
+ private final long mTimestamp; // Start time of the service state segment
+
+ TimestampedServiceState(CellularServiceState serviceState, long timestamp) {
+ mServiceState = serviceState;
+ mTimestamp = timestamp;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/SimSlotState.java b/src/java/com/android/internal/telephony/metrics/SimSlotState.java
index 3c3ae62c9f..95fa0427d2 100644
--- a/src/java/com/android/internal/telephony/metrics/SimSlotState.java
+++ b/src/java/com/android/internal/telephony/metrics/SimSlotState.java
@@ -20,9 +20,12 @@ import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccSlot;
+import com.android.telephony.Rlog;
/** Snapshots and stores the current SIM state. */
public class SimSlotState {
+ private static final String TAG = SimSlotState.class.getSimpleName();
+
public final int numActiveSlots;
public final int numActiveSims;
public final int numActiveEsims;
@@ -62,4 +65,21 @@ public class SimSlotState {
this.numActiveSims = numActiveSims;
this.numActiveEsims = numActiveEsims;
}
+
+ /** Returns whether the given phone is using a eSIM. */
+ public static boolean isEsim(int phoneId) {
+ UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
+ if (slot != null) {
+ return slot.isEuicc();
+ } else {
+ // should not happen, but assume we are not using eSIM
+ Rlog.e(TAG, "isEsim: slot=null for phone " + phoneId);
+ return false;
+ }
+ }
+
+ /** Returns whether the device has multiple active SIM profiles. */
+ public static boolean isMultiSim() {
+ return (getCurrentState().numActiveSims > 1);
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/SmsStats.java b/src/java/com/android/internal/telephony/metrics/SmsStats.java
new file mode 100644
index 0000000000..5268aba944
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/SmsStats.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_NOT_INJECTED;
+import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_SUCCESS;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP2;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP2;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_IMS;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_NORMAL;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_SMS_PP;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_VOICEMAIL_INDICATION;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_WAP_PUSH;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_ZERO;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_UNKNOWN;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.ServiceState;
+import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
+
+import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
+import com.android.telephony.Rlog;
+
+import java.util.Random;
+
+/** Collects sms events per phone ID for the pulled atom. */
+public class SmsStats {
+ private static final String TAG = SmsStats.class.getSimpleName();
+
+ /** 3GPP error for out of service: "no network service" in TS 27.005 cl 3.2.5 */
+ private static final int NO_NETWORK_ERROR_3GPP = 331;
+
+ /** 3GPP2 error for out of service: "Other radio interface problem" in N.S0005 Table 171 */
+ private static final int NO_NETWORK_ERROR_3GPP2 = 66;
+
+ private final Phone mPhone;
+
+ private final PersistAtomsStorage mAtomsStorage =
+ PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ private static final Random RANDOM = new Random();
+
+ public SmsStats(Phone phone) {
+ mPhone = phone;
+ }
+
+ /** Create a new atom when multi-part incoming SMS is dropped due to missing parts. */
+ public void onDroppedIncomingMultipartSms(boolean is3gpp2, int receivedCount, int totalCount) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, SOURCE_NOT_INJECTED);
+ // Keep SMS tech as unknown because it's possible that it changed overtime and is not
+ // necessarily the current one. Similarly mark the RAT as unknown.
+ proto.smsTech = INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN;
+ proto.rat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ proto.error = INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+ proto.totalParts = totalCount;
+ proto.receivedParts = receivedCount;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS for the voicemail indicator is received. */
+ public void onIncomingSmsVoicemail(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_VOICEMAIL_INDICATION;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS of type zero is received. */
+ public void onIncomingSmsTypeZero(@InboundSmsHandler.SmsSource int smsSource) {
+ IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_ZERO;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS-PP for the SIM card is received. */
+ public void onIncomingSmsPP(@InboundSmsHandler.SmsSource int smsSource, boolean success) {
+ IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_SMS_PP;
+ proto.error = getIncomingSmsError(success);
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS is received successfully. */
+ public void onIncomingSmsSuccess(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource, int messageCount,
+ boolean blocked, long messageId) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+ proto.totalParts = messageCount;
+ proto.receivedParts = messageCount;
+ proto.blocked = blocked;
+ proto.messageId = messageId;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an incoming SMS has an error. */
+ public void onIncomingSmsError(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource, int result) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+ proto.error = getIncomingSmsError(result);
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an incoming WAP_PUSH SMS is received. */
+ public void onIncomingSmsWapPush(@InboundSmsHandler.SmsSource int smsSource,
+ int messageCount, int result, long messageId) {
+ IncomingSms proto = getIncomingDefaultProto(false, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_WAP_PUSH;
+ proto.totalParts = messageCount;
+ proto.receivedParts = messageCount;
+ proto.error = getIncomingSmsError(result);
+ proto.messageId = messageId;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an outgoing SMS is sent. */
+ public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
+ @SmsManager.Result int errorCode, long messageId, boolean isFromDefaultApp) {
+ onOutgoingSms(isOverIms, is3gpp2, fallbackToCs, errorCode, NO_ERROR_CODE,
+ messageId, isFromDefaultApp);
+ }
+
+ /** Create a new atom when an outgoing SMS is sent. */
+ public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
+ @SmsManager.Result int errorCode, int radioSpecificErrorCode, long messageId,
+ boolean isFromDefaultApp) {
+ OutgoingSms proto =
+ getOutgoingDefaultProto(is3gpp2, isOverIms, messageId, isFromDefaultApp);
+
+ if (isOverIms) {
+ // Populate error code and result for IMS case
+ proto.errorCode = errorCode;
+ if (fallbackToCs) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
+ } else if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+ } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+ }
+ } else {
+ // Populate error code and result for CS case
+ if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+ } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+ }
+ proto.errorCode = radioSpecificErrorCode;
+ if (errorCode == SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE
+ && radioSpecificErrorCode == NO_ERROR_CODE) {
+ proto.errorCode = is3gpp2 ? NO_NETWORK_ERROR_3GPP2 : NO_NETWORK_ERROR_3GPP;
+ }
+ }
+ mAtomsStorage.addOutgoingSms(proto);
+ }
+
+ /** Creates a proto for a normal single-part {@code IncomingSms} with default values. */
+ private IncomingSms getIncomingDefaultProto(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource) {
+ IncomingSms proto = new IncomingSms();
+ proto.smsFormat = getSmsFormat(is3gpp2);
+ proto.smsTech = getSmsTech(smsSource, is3gpp2);
+ proto.rat = getRat(smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_NORMAL;
+ proto.totalParts = 1;
+ proto.receivedParts = 1;
+ proto.blocked = false;
+ proto.error = INCOMING_SMS__ERROR__SMS_SUCCESS;
+ proto.isRoaming = getIsRoaming();
+ proto.simSlotIndex = getPhoneId();
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(getPhoneId());
+ proto.carrierId = getCarrierId();
+ // Message ID is initialized with random number, as it is not available for all incoming
+ // SMS messages (e.g. those handled by OS or error cases).
+ proto.messageId = RANDOM.nextLong();
+ return proto;
+ }
+
+ /** Create a proto for a normal {@code OutgoingSms} with default values. */
+ private OutgoingSms getOutgoingDefaultProto(boolean is3gpp2, boolean isOverIms,
+ long messageId, boolean isFromDefaultApp) {
+ OutgoingSms proto = new OutgoingSms();
+ proto.smsFormat = getSmsFormat(is3gpp2);
+ proto.smsTech = getSmsTech(isOverIms, is3gpp2);
+ proto.rat = getRat(isOverIms);
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS;
+ proto.errorCode = isOverIms ? SmsManager.RESULT_ERROR_NONE : NO_ERROR_CODE;
+ proto.isRoaming = getIsRoaming();
+ proto.isFromDefaultApp = isFromDefaultApp;
+ proto.simSlotIndex = getPhoneId();
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(getPhoneId());
+ proto.carrierId = getCarrierId();
+ // If the message ID is invalid, generate a random value
+ proto.messageId = messageId != 0L ? messageId : RANDOM.nextLong();
+ // Setting the retry ID to zero. If needed, it will be incremented when the atom is added
+ // in the persistent storage.
+ proto.retryId = 0;
+ return proto;
+ }
+
+ private static int getSmsFormat(boolean is3gpp2) {
+ if (is3gpp2) {
+ return INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP2;
+ } else {
+ return INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP;
+ }
+ }
+
+ private int getSmsTech(@InboundSmsHandler.SmsSource int smsSource, boolean is3gpp2) {
+ if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN;
+ }
+ return getSmsTech(smsSource == SOURCE_INJECTED_FROM_IMS, is3gpp2);
+ }
+
+ private int getSmsTech(boolean isOverIms, boolean is3gpp2) {
+ if (isOverIms) {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_IMS;
+ } else if (is3gpp2) {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP2;
+ } else {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP;
+ }
+ }
+
+ private static int getIncomingSmsError(int result) {
+ switch (result) {
+ case Activity.RESULT_OK:
+ case Intents.RESULT_SMS_HANDLED:
+ return INCOMING_SMS__ERROR__SMS_SUCCESS;
+ case Intents.RESULT_SMS_OUT_OF_MEMORY:
+ return INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY;
+ case Intents.RESULT_SMS_UNSUPPORTED:
+ return INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED;
+ case Intents.RESULT_SMS_GENERIC_ERROR:
+ default:
+ return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+ }
+ }
+
+ private static int getIncomingSmsError(boolean success) {
+ if (success) {
+ return INCOMING_SMS__ERROR__SMS_SUCCESS;
+ } else {
+ return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+ }
+ }
+
+ private static int getOutgoingSmsError(@SendStatusResult int imsSendResult) {
+ switch (imsSendResult) {
+ case ImsSmsImplBase.SEND_STATUS_OK:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS;
+ case ImsSmsImplBase.SEND_STATUS_ERROR:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
+ default:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_UNKNOWN;
+ }
+ }
+
+ private int getPhoneId() {
+ Phone phone = mPhone;
+ if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = mPhone.getDefaultPhone();
+ }
+ return phone.getPhoneId();
+ }
+
+ @Nullable
+ private ServiceState getServiceState() {
+ Phone phone = mPhone;
+ if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = mPhone.getDefaultPhone();
+ }
+ ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
+ return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ }
+
+ private @NetworkType int getRat(@InboundSmsHandler.SmsSource int smsSource) {
+ if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) {
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ return getRat(smsSource == SOURCE_INJECTED_FROM_IMS);
+ }
+
+ private @NetworkType int getRat(boolean isOverIms) {
+ if (isOverIms) {
+ if (mPhone.getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ }
+ }
+ // TODO(b/168837897): Returns the RAT at the time the SMS was received..
+ ServiceState serviceState = getServiceState();
+ return serviceState != null
+ ? serviceState.getVoiceNetworkType() : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private boolean getIsRoaming() {
+ ServiceState serviceState = getServiceState();
+ return serviceState != null ? serviceState.getRoaming() : false;
+ }
+
+ private int getCarrierId() {
+ Phone phone = mPhone;
+ if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = mPhone.getDefaultPhone();
+ }
+ return phone.getCarrierId();
+ }
+
+ private void loge(String format, Object... args) {
+ Rlog.e(TAG, "[" + mPhone.getPhoneId() + "]" + String.format(format, args));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
index 8a423ef36e..45b9ab7d23 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
@@ -226,4 +226,11 @@ public class TelephonyEventBuilder {
mEvent.networkCapabilities = networkCapabilities;
return this;
}
+
+ /** Set radio state. */
+ public TelephonyEventBuilder setRadioState(int radioState) {
+ mEvent.type = TelephonyEvent.Type.RADIO_STATE_CHANGED;
+ mEvent.radioState = radioState;
+ return this;
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 367b3b2524..5e43876df8 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -18,6 +18,8 @@ package com.android.internal.telephony.metrics;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_NOT_INJECTED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
@@ -29,6 +31,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_S
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.NUM_SIGNAL_LEVEL;
import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IP;
import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV6;
@@ -45,6 +48,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.RadioPowerState;
import android.telephony.CallQuality;
import android.telephony.DisconnectCause;
import android.telephony.NetworkRegistrationInfo;
@@ -55,6 +59,7 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyHistogram;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.PrefNetworkMode;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataService;
import android.telephony.emergency.EmergencyNumber;
@@ -66,21 +71,26 @@ import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.SparseArray;
import com.android.internal.telephony.CarrierResolver;
import com.android.internal.telephony.DriverCall;
import com.android.internal.telephony.GsmCdmaConnection;
+import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.SmsController;
import com.android.internal.telephony.SmsResponse;
import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.nano.TelephonyProto;
import com.android.internal.telephony.nano.TelephonyProto.ActiveSubscriptionInfo;
+import com.android.internal.telephony.nano.TelephonyProto.BandwidthEstimatorStats;
import com.android.internal.telephony.nano.TelephonyProto.EmergencyNumberInfo;
import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
@@ -100,6 +110,7 @@ import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwi
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.NetworkCapabilitiesInfo;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RadioState;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall.DeactivateReason;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
@@ -122,6 +133,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -173,8 +185,7 @@ public class TelephonyMetrics {
private final SparseArray<TelephonyServiceState> mLastServiceState = new SparseArray<>();
/**
- * Last ims capabilities. This is for injecting the base of a new log or a new call/sms
- * session
+ * Last ims capabilities. This is for injecting the base of a new log or a new call/sms session
*/
private final SparseArray<ImsCapabilities> mLastImsCapabilities = new SparseArray<>();
@@ -184,19 +195,16 @@ public class TelephonyMetrics {
*/
private final SparseArray<ImsConnectionState> mLastImsConnectionState = new SparseArray<>();
- /**
- * Last settings state. This is for deduping same settings event logged.
- */
+ /** Last settings state. This is for deduping same settings event logged. */
private final SparseArray<TelephonySettings> mLastSettings = new SparseArray<>();
- /**
- * Last sim state, indexed by phone id.
- */
+ /** Last sim state, indexed by phone id. */
private final SparseArray<Integer> mLastSimState = new SparseArray<>();
- /**
- * Last active subscription information, indexed by phone id.
- */
+ /** Last radio state, indexed by phone id. */
+ private final SparseArray<Integer> mLastRadioState = new SparseArray<>();
+
+ /** Last active subscription information, indexed by phone id. */
private final SparseArray<ActiveSubscriptionInfo> mLastActiveSubscriptionInfos =
new SparseArray<>();
@@ -208,23 +216,21 @@ public class TelephonyMetrics {
*/
private int mLastEnabledModemBitmap = (1 << TelephonyManager.getDefault().getPhoneCount()) - 1;
- /**
- * Last carrier id matching.
- */
+ /** Last carrier id matching. */
private final SparseArray<CarrierIdMatching> mLastCarrierId = new SparseArray<>();
- /**
- * Last NetworkCapabilitiesInfo, indexed by phone id.
- */
+ /** Last NetworkCapabilitiesInfo, indexed by phone id. */
private final SparseArray<NetworkCapabilitiesInfo> mLastNetworkCapabilitiesInfos =
new SparseArray<>();
- /**
- * Last RilDataCall Events (indexed by cid), indexed by phone id
- */
+ /** Last RilDataCall Events (indexed by cid), indexed by phone id */
private final SparseArray<SparseArray<RilDataCall>> mLastRilDataCallEvents =
new SparseArray<>();
+ /** List of Tx and Rx Bandwidth estimation stats maps */
+ private final List<Map<String, BwEstimationStats>> mBwEstStatsMapList = new ArrayList<>(
+ Arrays.asList(new ArrayMap<>(), new ArrayMap<>()));
+
/** The start system time of the TelephonyLog in milliseconds*/
private long mStartSystemTimeMs;
@@ -271,7 +277,7 @@ public class TelephonyMetrics {
* @param pw Print writer
* @param args Arguments
*/
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (args != null && args.length > 0) {
boolean reset = true;
if (args.length > 1 && "--keep".equals(args[1])) {
@@ -484,10 +490,12 @@ public class TelephonyMetrics {
pw.print("Start time in minutes: " + callSession.startTimeMinutes);
pw.print(", phone: " + callSession.phoneId);
if (callSession.eventsDropped) {
- pw.println(" Events dropped: " + callSession.eventsDropped);
+ pw.println(", events dropped: " + callSession.eventsDropped);
+ } else {
+ pw.println("");
}
- pw.println(" Events: ");
+ pw.println("Events: ");
pw.increaseIndent();
for (TelephonyCallSession.Event event : callSession.events) {
pw.print(event.delay);
@@ -540,9 +548,16 @@ public class TelephonyMetrics {
for (SmsSession.Event event : smsSession.events) {
pw.print(event.delay);
pw.print(" T=");
- pw.println(smsSessionEventToString(event.type));
- // Only show more info for tx/rx sms
- if (event.type == SmsSession.Event.Type.SMS_RECEIVED) {
+ if (event.type == SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED) {
+ pw.println(smsSessionEventToString(event.type)
+ + "(" + "Data RAT " + event.serviceState.dataRat
+ + " Voice RAT " + event.serviceState.voiceRat
+ + " Channel Number " + event.serviceState.channelNumber
+ + " NR Frequency Range " + event.serviceState.nrFrequencyRange
+ + " NR State " + event.serviceState.nrState
+ + ")");
+ } else if (event.type == SmsSession.Event.Type.SMS_RECEIVED) {
+ pw.println(smsSessionEventToString(event.type));
pw.increaseIndent();
switch (event.smsType) {
case SmsSession.Event.SmsType.SMS_TYPE_SMS_PP:
@@ -566,6 +581,7 @@ public class TelephonyMetrics {
pw.decreaseIndent();
} else if (event.type == SmsSession.Event.Type.SMS_SEND
|| event.type == SmsSession.Event.Type.SMS_SEND_RESULT) {
+ pw.println(smsSessionEventToString(event.type));
pw.increaseIndent();
pw.println("ReqId=" + event.rilRequestId);
pw.println("E=" + event.errorCode);
@@ -573,6 +589,7 @@ public class TelephonyMetrics {
pw.println("ImsE=" + event.imsError);
pw.decreaseIndent();
} else if (event.type == SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED) {
+ pw.println(smsSessionEventToString(event.type));
pw.increaseIndent();
pw.println("Received: " + event.incompleteSms.receivedParts + "/"
+ event.incompleteSms.totalParts);
@@ -613,6 +630,20 @@ public class TelephonyMetrics {
+ new DecimalFormat("#.##").format(s.monitoredRailEnergyConsumedMah));
pw.decreaseIndent();
pw.println("Hardware Version: " + SystemProperties.get("ro.boot.revision", ""));
+
+ pw.decreaseIndent();
+ pw.println("LinkBandwidthEstimator stats:");
+ pw.increaseIndent();
+
+ pw.println("Tx");
+ for (BwEstimationStats stats : mBwEstStatsMapList.get(0).values()) {
+ pw.println(stats.toString());
+ }
+
+ pw.println("Rx");
+ for (BwEstimationStats stats : mBwEstStatsMapList.get(1).values()) {
+ pw.println(stats.toString());
+ }
}
/**
@@ -633,6 +664,8 @@ public class TelephonyMetrics {
mTelephonyEvents.clear();
mCompletedCallSessions.clear();
mCompletedSmsSessions.clear();
+ mBwEstStatsMapList.get(0).clear();
+ mBwEstStatsMapList.get(1).clear();
mTelephonyEventsDropped = false;
@@ -705,6 +738,13 @@ public class TelephonyMetrics {
.setDataCalls(dataCalls).build());
}
}
+
+ for (int i = 0; i < mLastRadioState.size(); i++) {
+ final int key = mLastRadioState.keyAt(i);
+ TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
+ .setRadioState(mLastRadioState.get(key)).build();
+ addTelephonyEvent(event);
+ }
}
/**
@@ -778,7 +818,7 @@ public class TelephonyMetrics {
}
}
log.lastActiveSubscriptionInfo = activeSubscriptionInfo;
-
+ log.bandwidthEstimatorStats = buildBandwidthEstimatorStats();
return log;
}
@@ -788,7 +828,8 @@ public class TelephonyMetrics {
Integer lastSimState = mLastSimState.get(phoneId);
if (lastSimState == null || !lastSimState.equals(state)) {
mLastSimState.put(phoneId, state);
- addTelephonyEvent(new TelephonyEventBuilder().setSimStateChange(mLastSimState).build());
+ addTelephonyEvent(
+ new TelephonyEventBuilder(phoneId).setSimStateChange(mLastSimState).build());
}
}
@@ -1077,7 +1118,7 @@ public class TelephonyMetrics {
TelephonyServiceState serviceState = mLastServiceState.get(phoneId);
if (serviceState != null) {
smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder(
- TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
+ SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
.setServiceState(serviceState));
}
@@ -1131,7 +1172,7 @@ public class TelephonyMetrics {
}
}
- private SmsSession finishSmsSession(InProgressSmsSession inProgressSmsSession) {
+ private synchronized SmsSession finishSmsSession(InProgressSmsSession inProgressSmsSession) {
SmsSession smsSession = new SmsSession();
smsSession.events = new SmsSession.Event[inProgressSmsSession.events.size()];
inProgressSmsSession.events.toArray(smsSession.events);
@@ -1212,6 +1253,20 @@ public class TelephonyMetrics {
.setSignalStrength(signalStrength).build());
}
+ private TelephonySettings cloneCurrentTelephonySettings(int phoneId) {
+ TelephonySettings newSettings = new TelephonySettings();
+ TelephonySettings lastSettings = mLastSettings.get(phoneId);
+ if (lastSettings != null) {
+ // No clone method available, so each relevant field is copied individually.
+ newSettings.preferredNetworkMode = lastSettings.preferredNetworkMode;
+ newSettings.isEnhanced4GLteModeEnabled = lastSettings.isEnhanced4GLteModeEnabled;
+ newSettings.isVtOverLteEnabled = lastSettings.isVtOverLteEnabled;
+ newSettings.isWifiCallingEnabled = lastSettings.isWifiCallingEnabled;
+ newSettings.isVtOverWifiEnabled = lastSettings.isVtOverWifiEnabled;
+ }
+ return newSettings;
+ }
+
/**
* Write IMS feature settings changed event
*
@@ -1220,8 +1275,9 @@ public class TelephonyMetrics {
* @param network The IMS network type
* @param value The settings. 0 indicates disabled, otherwise enabled.
*/
- public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value) {
- TelephonySettings s = new TelephonySettings();
+ public synchronized void writeImsSetFeatureValue(int phoneId, int feature, int network,
+ int value) {
+ TelephonySettings s = cloneCurrentTelephonySettings(phoneId);
if (network == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
switch (feature) {
case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
@@ -1242,7 +1298,6 @@ public class TelephonyMetrics {
}
}
-
// If the settings don't change, we don't log the event.
if (mLastSettings.get(phoneId) != null &&
Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
@@ -1269,8 +1324,9 @@ public class TelephonyMetrics {
* @param phoneId Phone id
* @param networkType The preferred network
*/
- public void writeSetPreferredNetworkType(int phoneId, int networkType) {
- TelephonySettings s = new TelephonySettings();
+ public synchronized void writeSetPreferredNetworkType(int phoneId,
+ @PrefNetworkMode int networkType) {
+ TelephonySettings s = cloneCurrentTelephonySettings(phoneId);
s.preferredNetworkMode = networkType + 1;
// If the settings don't change, we don't log the event.
@@ -1772,7 +1828,7 @@ public class TelephonyMetrics {
dataCall.type = response.getProtocolType() + 1;
if (!TextUtils.isEmpty(response.getInterfaceName())) {
- dataCall.iframe = response.getInterfaceName();
+ dataCall.ifname = response.getInterfaceName();
}
}
setupDataCallResponse.call = dataCall;
@@ -1813,15 +1869,15 @@ public class TelephonyMetrics {
*/
private synchronized void writeOnSmsSolicitedResponse(int phoneId, int rilSerial, int rilError,
SmsResponse response) {
-
InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
if (smsSession == null) {
Rlog.e(TAG, "SMS session is missing");
} else {
-
int errorCode = SmsResponse.NO_ERROR_CODE;
+ long messageId = 0L;
if (response != null) {
errorCode = response.mErrorCode;
+ messageId = response.mMessageId;
}
smsSession.addEvent(new SmsSessionEventBuilder(
@@ -1829,6 +1885,7 @@ public class TelephonyMetrics {
.setErrorCode(errorCode)
.setRilErrno(rilError + 1)
.setRilRequestId(rilSerial)
+ .setMessageId(messageId)
);
smsSession.decreaseExpectedResponse();
@@ -1856,7 +1913,7 @@ public class TelephonyMetrics {
SmsSession.Event.Type.SMS_SEND_RESULT)
.setImsServiceErrno(resultCode)
.setErrorCode(errorReason)
- .setMessageId((messageId))
+ .setMessageId(messageId)
);
smsSession.decreaseExpectedResponse();
@@ -2236,15 +2293,19 @@ public class TelephonyMetrics {
* @param phoneId Phone id
* @param rilSerial RIL request serial number
* @param tech SMS RAT
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param messageId Unique id for this message.
*/
- public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
+ public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format,
+ long messageId) {
InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
.setTech(tech)
.setRilRequestId(rilSerial)
.setFormat(format)
+ .setMessageId(messageId)
);
smsSession.increaseExpectedResponse();
@@ -2259,14 +2320,16 @@ public class TelephonyMetrics {
* {@link SmsMessage#FORMAT_3GPP2}.
* @param resultCode The result of sending the new SMS to the vendor layer to be sent to the
* carrier network.
+ * @param messageId Unique id for this message.
*/
public synchronized void writeImsServiceSendSms(int phoneId, String format,
- @ImsSmsImplBase.SendStatusResult int resultCode) {
+ @ImsSmsImplBase.SendStatusResult int resultCode, long messageId) {
InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
.setTech(SmsSession.Event.Tech.SMS_IMS)
.setImsServiceErrno(resultCode)
.setFormat(convertSmsFormat(format))
+ .setMessageId(messageId)
);
smsSession.increaseExpectedResponse();
@@ -2317,7 +2380,8 @@ public class TelephonyMetrics {
* Write an incoming multi-part SMS that was discarded because some parts were missing
*
* @param phoneId Phone id
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
* @param receivedCount Number of received parts.
* @param totalCount Total number of parts of the SMS.
*/
@@ -2344,7 +2408,8 @@ public class TelephonyMetrics {
*
* @param phoneId Phone id
* @param type Type of the SMS.
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
* @param success Indicates if the SMS-PP was successfully delivered to the USIM.
*/
private void writeIncomingSmsWithType(int phoneId, int type, String format, boolean success) {
@@ -2361,7 +2426,8 @@ public class TelephonyMetrics {
* Write an incoming SMS-PP for the USIM
*
* @param phoneId Phone id
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
* @param success Indicates if the SMS-PP was successfully delivered to the USIM.
*/
public void writeIncomingSMSPP(int phoneId, String format, boolean success) {
@@ -2374,7 +2440,8 @@ public class TelephonyMetrics {
* Write an incoming SMS to update voicemail indicator
*
* @param phoneId Phone id
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
*/
public void writeIncomingVoiceMailSms(int phoneId, String format) {
logv("Logged VoiceMail message.");
@@ -2386,7 +2453,8 @@ public class TelephonyMetrics {
* Write an incoming SMS of type 0
*
* @param phoneId Phone id
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
*/
public void writeIncomingSmsTypeZero(int phoneId, String format) {
logv("Logged Type-0 SMS message.");
@@ -2399,35 +2467,42 @@ public class TelephonyMetrics {
*
* @param phoneId Phone id
* @param type Type of the SMS.
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param smsSource the source of the SMS message
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
* @param timestamps array with timestamps of each incoming SMS part. It contains a single
* @param blocked indicates if the message was blocked or not.
* @param success Indicates if the SMS-PP was successfully delivered to the USIM.
* @param messageId Unique id for this message.
*/
- private void writeIncomingSmsSessionWithType(int phoneId, int type, boolean smsOverIms,
- String format, long[] timestamps, boolean blocked, boolean success,
- long messageId) {
+ private void writeIncomingSmsSessionWithType(int phoneId, int type,
+ @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps,
+ boolean blocked, boolean success, long messageId) {
logv("Logged SMS session consisting of " + timestamps.length
- + " parts, over IMS = " + smsOverIms
+ + " parts, source = " + smsSource
+ " blocked = " + blocked
+ " type = " + type
- + " messageId = " + messageId);
+ + " " + SmsController.formatCrossStackMessageId(messageId));
+
+ int smsFormat = convertSmsFormat(format);
+ int smsError =
+ success ? SmsManager.RESULT_ERROR_NONE : SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+ int smsTech = getSmsTech(smsSource, smsFormat == SmsSession.Event.Format.SMS_FORMAT_3GPP2);
InProgressSmsSession smsSession = startNewSmsSession(phoneId);
- for (long time : timestamps) {
+
+ long startElapsedTimeMillis = SystemClock.elapsedRealtime();
+ for (int i = 0; i < timestamps.length; i++) {
SmsSessionEventBuilder eventBuilder =
new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
- .setFormat(convertSmsFormat(format))
- .setTech(smsOverIms ? SmsSession.Event.Tech.SMS_IMS :
- SmsSession.Event.Tech.SMS_GSM)
- .setErrorCode(success ? SmsManager.RESULT_ERROR_NONE :
- SmsManager.RESULT_ERROR_GENERIC_FAILURE)
+ .setFormat(smsFormat)
+ .setTech(smsTech)
+ .setErrorCode(smsError)
.setSmsType(type)
.setBlocked(blocked)
.setMessageId(messageId);
- smsSession.addEvent(time, eventBuilder);
+ long interval = (i > 0) ? timestamps[i] - timestamps[i - 1] : 0;
+ smsSession.addEvent(startElapsedTimeMillis + interval, eventBuilder);
}
finishSmsSession(smsSession);
}
@@ -2436,42 +2511,46 @@ public class TelephonyMetrics {
* Write an incoming WAP-PUSH message.
*
* @param phoneId Phone id
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param smsSource the source of the SMS message
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
* @param timestamps array with timestamps of each incoming SMS part. It contains a single
* @param success Indicates if the SMS-PP was successfully delivered to the USIM.
* @param messageId Unique id for this message.
*/
- public void writeIncomingWapPush(int phoneId, boolean smsOverIms, String format,
- long[] timestamps, boolean success, long messageId) {
+ public void writeIncomingWapPush(int phoneId, @InboundSmsHandler.SmsSource int smsSource,
+ String format, long[] timestamps, boolean success, long messageId) {
writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH,
- smsOverIms, format, timestamps, false, success, messageId);
+ smsSource, format, timestamps, false, success, messageId);
}
/**
* Write a successful incoming SMS session
*
* @param phoneId Phone id
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
- * @param format SMS format. Either 3GPP or 3GPP2.
+ * @param smsSource the source of the SMS message
+ * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
+ * {@link SmsMessage#FORMAT_3GPP2}.
* @param timestamps array with timestamps of each incoming SMS part. It contains a single
* @param blocked indicates if the message was blocked or not.
* @param messageId Unique id for this message.
*/
- public void writeIncomingSmsSession(int phoneId, boolean smsOverIms, String format,
- long[] timestamps, boolean blocked, long messageId) {
+ public void writeIncomingSmsSession(int phoneId, @InboundSmsHandler.SmsSource int smsSource,
+ String format, long[] timestamps, boolean blocked, long messageId) {
writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_NORMAL,
- smsOverIms, format, timestamps, blocked, true, messageId);
+ smsSource, format, timestamps, blocked, true, messageId);
}
/**
* Write an error incoming SMS
*
* @param phoneId Phone id
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
+ * @param is3gpp2 true for 3GPP2 format, false for 3GPP format.
+ * @param smsSource the source of the SMS message
* @param result Indicates the reason of the failure.
*/
- public void writeIncomingSmsError(int phoneId, boolean smsOverIms, int result) {
+ public void writeIncomingSmsError(int phoneId, boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource, int result) {
logv("Incoming SMS error = " + result);
int smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE;
@@ -2495,9 +2574,11 @@ public class TelephonyMetrics {
SmsSessionEventBuilder eventBuilder =
new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
- .setErrorCode(smsError)
- .setTech(smsOverIms ? SmsSession.Event.Tech.SMS_IMS :
- SmsSession.Event.Tech.SMS_GSM);
+ .setFormat(is3gpp2
+ ? SmsSession.Event.Format.SMS_FORMAT_3GPP2
+ : SmsSession.Event.Format.SMS_FORMAT_3GPP)
+ .setTech(getSmsTech(smsSource, is3gpp2))
+ .setErrorCode(smsError);
smsSession.addEvent(eventBuilder);
finishSmsSession(smsSession);
}
@@ -2625,6 +2706,27 @@ public class TelephonyMetrics {
addTelephonyEvent(event);
}
+ /** Write radio state changed event */
+ public void writeRadioState(int phoneId, @RadioPowerState int state) {
+ int radioState = convertRadioState(state);
+ TelephonyEvent event = new TelephonyEventBuilder(phoneId).setRadioState(radioState).build();
+ mLastRadioState.put(phoneId, radioState);
+ addTelephonyEvent(event);
+ }
+
+ private static int convertRadioState(@RadioPowerState int state) {
+ switch (state) {
+ case TelephonyManager.RADIO_POWER_OFF:
+ return RadioState.RADIO_STATE_OFF;
+ case TelephonyManager.RADIO_POWER_ON:
+ return RadioState.RADIO_STATE_ON;
+ case TelephonyManager.RADIO_POWER_UNAVAILABLE:
+ return RadioState.RADIO_STATE_UNAVAILABLE;
+ default:
+ return RadioState.RADIO_STATE_UNKNOWN;
+ }
+ }
+
/**
* Convert SMS format
*/
@@ -2644,6 +2746,19 @@ public class TelephonyMetrics {
}
/**
+ * Get SMS technology
+ */
+ private int getSmsTech(@InboundSmsHandler.SmsSource int smsSource, boolean is3gpp2) {
+ if (smsSource == SOURCE_INJECTED_FROM_IMS) {
+ return SmsSession.Event.Tech.SMS_IMS;
+ } else if (smsSource == SOURCE_NOT_INJECTED) {
+ return is3gpp2 ? SmsSession.Event.Tech.SMS_CDMA : SmsSession.Event.Tech.SMS_GSM;
+ } else { // SOURCE_INJECTED_FROM_UNKNOWN
+ return SmsSession.Event.Tech.SMS_UNKNOWN;
+ }
+ }
+
+ /**
* Convert IMS audio codec into proto defined value
*
* @param c IMS codec value
@@ -2774,6 +2889,7 @@ public class TelephonyMetrics {
}
//TODO: Expand the proto in the future
+ public void writeOnImsCallInitiating(int phoneId, ImsCallSession session) {}
public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {}
public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {}
public void writeOnImsCallStartFailed(int phoneId, ImsCallSession session,
@@ -2830,4 +2946,121 @@ public class TelephonyMetrics {
return SimState.SIM_STATE_UNKNOWN;
}
}
+
+ /**
+ * Write bandwidth estimator stats
+ */
+ public synchronized void writeBandwidthStats(int link, int rat, int nrMode,
+ int signalLevel, int bwEstExtErrPercent, int coldStartErrPercent, int bwKbps) {
+ BwEstimationStats stats = lookupEstimationStats(link, rat, nrMode);
+ stats.mBwEstErrorAcc[signalLevel] += Math.abs(bwEstExtErrPercent);
+ stats.mStaticBwErrorAcc[signalLevel] += Math.abs(coldStartErrPercent);
+ stats.mBwAccKbps[signalLevel] += bwKbps;
+ stats.mCount[signalLevel]++;
+ }
+
+ private BwEstimationStats lookupEstimationStats(int linkIndex, int dataRat, int nrMode) {
+ String dataRatName = LinkBandwidthEstimator.getDataRatName(dataRat, nrMode);
+ BwEstimationStats ans = mBwEstStatsMapList.get(linkIndex).get(dataRatName);
+ if (ans == null) {
+ ans = new BwEstimationStats(dataRat, nrMode);
+ mBwEstStatsMapList.get(linkIndex).put(dataRatName, ans);
+ }
+ return ans;
+ }
+
+ private BandwidthEstimatorStats buildBandwidthEstimatorStats() {
+ BandwidthEstimatorStats stats = new BandwidthEstimatorStats();
+ List<BandwidthEstimatorStats.PerRat> ratList;
+ ratList = writeBandwidthEstimatorStatsRatList(mBwEstStatsMapList.get(0));
+ stats.perRatTx = ratList.toArray(new BandwidthEstimatorStats.PerRat[0]);
+ ratList = writeBandwidthEstimatorStatsRatList(mBwEstStatsMapList.get(1));
+ stats.perRatRx = ratList.toArray(new BandwidthEstimatorStats.PerRat[0]);
+ return stats;
+ }
+
+ private List<BandwidthEstimatorStats.PerRat> writeBandwidthEstimatorStatsRatList(
+ Map<String, BwEstimationStats> bwEstStatsMap) {
+ List<BandwidthEstimatorStats.PerRat> ratList = new ArrayList<>();
+ for (BwEstimationStats perRat : bwEstStatsMap.values()) {
+ ratList.add(perRat.writeBandwidthStats());
+ }
+ return ratList;
+ }
+
+ private static class BwEstimationStats {
+ final int mRadioTechnology;
+ final int mNrMode;
+ final long[] mBwEstErrorAcc = new long[NUM_SIGNAL_LEVEL];
+ final long[] mStaticBwErrorAcc = new long[NUM_SIGNAL_LEVEL];
+ final long[] mBwAccKbps = new long[NUM_SIGNAL_LEVEL];
+ final int[] mCount = new int[NUM_SIGNAL_LEVEL];
+
+ BwEstimationStats(int radioTechnology, int nrMode) {
+ mRadioTechnology = radioTechnology;
+ mNrMode = nrMode;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ return sb.append(LinkBandwidthEstimator.getDataRatName(mRadioTechnology, mNrMode))
+ .append("\n Count\n").append(printValues(mCount))
+ .append("\n AvgKbps\n").append(printAvgValues(mBwAccKbps, mCount))
+ .append("\n BwEst Error\n").append(printAvgValues(mBwEstErrorAcc, mCount))
+ .append("\n StaticBw Error\n").append(printAvgValues(mStaticBwErrorAcc, mCount))
+ .toString();
+ }
+
+ private String printValues(int[] values) {
+ StringBuilder sb = new StringBuilder();
+ for (int k = 0; k < NUM_SIGNAL_LEVEL; k++) {
+ sb.append(" " + values[k]);
+ }
+ return sb.toString();
+ }
+
+ private String printAvgValues(long[] stats, int[] count) {
+ StringBuilder sb = new StringBuilder();
+ for (int k = 0; k < NUM_SIGNAL_LEVEL; k++) {
+ int avgStat = calculateAvg(stats[k], count[k]);
+ sb.append(" " + avgStat);
+ }
+ return sb.toString();
+ }
+
+ private BandwidthEstimatorStats.PerRat writeBandwidthStats() {
+ BandwidthEstimatorStats.PerRat stats = new BandwidthEstimatorStats.PerRat();
+ List<BandwidthEstimatorStats.PerLevel> levelList = new ArrayList<>();
+ for (int level = 0; level < NUM_SIGNAL_LEVEL; level++) {
+ BandwidthEstimatorStats.PerLevel currStats = writeBandwidthStatsPerLevel(level);
+ if (currStats != null) {
+ levelList.add(currStats);
+ }
+ }
+ stats.rat = mRadioTechnology;
+ stats.perLevel = levelList.toArray(new BandwidthEstimatorStats.PerLevel[0]);
+ stats.nrMode = mNrMode;
+ return stats;
+ }
+
+ private BandwidthEstimatorStats.PerLevel writeBandwidthStatsPerLevel(int level) {
+ int count = mCount[level];
+ if (count > 0) {
+ BandwidthEstimatorStats.PerLevel stats = new BandwidthEstimatorStats.PerLevel();
+ stats.signalLevel = level;
+ stats.count = count;
+ stats.avgBwKbps = calculateAvg(mBwAccKbps[level], count);
+ stats.staticBwErrorPercent = calculateAvg(mStaticBwErrorAcc[level], count);
+ stats.bwEstErrorPercent = calculateAvg(mBwEstErrorAcc[level], count);
+ return stats;
+ }
+ return null;
+ }
+
+ private int calculateAvg(long acc, int count) {
+ return (count > 0) ? (int) (acc / count) : 0;
+ }
+ }
+
}
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java b/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java
index 7d7cd0e126..5183ea4734 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony.metrics;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.telephony.Rlog;
import java.util.Arrays;
@@ -52,7 +52,7 @@ public class VoiceCallRatTracker {
}
/** Creates an RAT tracker from saved atoms at startup. */
- public static VoiceCallRatTracker fromProto(RawVoiceCallRatUsage[] usages) {
+ public static VoiceCallRatTracker fromProto(VoiceCallRatUsage[] usages) {
VoiceCallRatTracker tracker = new VoiceCallRatTracker();
if (usages == null) {
Rlog.e(TAG, "fromProto: usages=null");
@@ -63,10 +63,10 @@ public class VoiceCallRatTracker {
}
/** Append the map to javanano persist atoms. */
- public RawVoiceCallRatUsage[] toProto() {
+ public VoiceCallRatUsage[] toProto() {
return mRatUsageMap.entrySet().stream()
.map(VoiceCallRatTracker::entryToProto)
- .toArray(RawVoiceCallRatUsage[]::new);
+ .toArray(VoiceCallRatUsage[]::new);
}
/** Resets the tracker. */
@@ -140,14 +140,14 @@ public class VoiceCallRatTracker {
}
}
- private void addProto(RawVoiceCallRatUsage usage) {
+ private void addProto(VoiceCallRatUsage usage) {
mRatUsageMap.put(Key.fromProto(usage), Value.fromProto(usage));
}
- private static RawVoiceCallRatUsage entryToProto(Map.Entry<Key, Value> entry) {
+ private static VoiceCallRatUsage entryToProto(Map.Entry<Key, Value> entry) {
Key key = entry.getKey();
Value value = entry.getValue();
- RawVoiceCallRatUsage usage = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage = new VoiceCallRatUsage();
usage.carrierId = key.carrierId;
usage.rat = key.rat;
if (value.mConnectionIds != null) {
@@ -172,7 +172,7 @@ public class VoiceCallRatTracker {
this.rat = rat;
}
- static Key fromProto(RawVoiceCallRatUsage usage) {
+ static Key fromProto(VoiceCallRatUsage usage) {
return new Key(usage.carrierId, usage.rat);
}
@@ -226,7 +226,7 @@ public class VoiceCallRatTracker {
}
}
- static Value fromProto(RawVoiceCallRatUsage usage) {
+ static Value fromProto(VoiceCallRatUsage usage) {
Value value = new Value(usage.totalDurationMillis, usage.callCount);
return value;
}
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index ad6d52da02..9b988add1b 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -21,6 +21,11 @@ import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSIO
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST;
@@ -31,19 +36,25 @@ import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSIO
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
import android.annotation.Nullable;
+import android.content.Context;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.SystemClock;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.VideoState;
import android.telephony.Annotation.NetworkType;
import android.telephony.DisconnectCause;
import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
+import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
@@ -58,10 +69,10 @@ import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec;
import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccSlot;
import com.android.telephony.Rlog;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -71,23 +82,36 @@ import java.util.stream.Collectors;
public class VoiceCallSessionStats {
private static final String TAG = VoiceCallSessionStats.class.getSimpleName();
- /** Bitmask value of unknown audio codecs. */
- private static final long AUDIO_CODEC_UNKNOWN = 1L << AudioCodec.AUDIO_CODEC_UNKNOWN;
+ /** Upper bounds of each call setup duration category in milliseconds. */
+ private static final int CALL_SETUP_DURATION_UNKNOWN = 0;
+ private static final int CALL_SETUP_DURATION_EXTREMELY_FAST = 400;
+ private static final int CALL_SETUP_DURATION_ULTRA_FAST = 700;
+ private static final int CALL_SETUP_DURATION_VERY_FAST = 1000;
+ private static final int CALL_SETUP_DURATION_FAST = 1500;
+ private static final int CALL_SETUP_DURATION_NORMAL = 2500;
+ private static final int CALL_SETUP_DURATION_SLOW = 4000;
+ private static final int CALL_SETUP_DURATION_VERY_SLOW = 6000;
+ private static final int CALL_SETUP_DURATION_ULTRA_SLOW = 10000;
+ // CALL_SETUP_DURATION_EXTREMELY_SLOW has no upper bound (it includes everything above 10000)
+
+ /** Number of buckets for codec quality, from UNKNOWN to FULLBAND. */
+ private static final int CODEC_QUALITY_COUNT = 5;
/**
- * Value denoting the carrier ID being unknown.
+ * Threshold to calculate the main audio codec quality of the call.
*
- * <p>NOTE: 0 is unused in {@code carrier_list.textpb} (it starts from 1).
+ * The audio codec quality was equal to or greater than the main audio codec quality for
+ * at least 70% of the call.
*/
- private static final int CARRIER_ID_UNKNOWN = 0;
+ private static final int MAIN_CODEC_QUALITY_THRESHOLD = 70;
- /** Holds the audio codec bitmask value for CS calls. */
- private static final SparseLongArray CS_CODEC_MAP = buildGsmCdmaCodecMap();
+ /** Holds the audio codec value for CS calls. */
+ private static final SparseIntArray CS_CODEC_MAP = buildGsmCdmaCodecMap();
- /** Holds the audio codec bitmask value for IMS calls. */
- private static final SparseLongArray IMS_CODEC_MAP = buildImsCodecMap();
+ /** Holds the audio codec value for IMS calls. */
+ private static final SparseIntArray IMS_CODEC_MAP = buildImsCodecMap();
- /** Holds setup duration buckets with keys as their lower bounds in milliseconds. */
+ /** Holds setup duration buckets with values as their upper bounds in milliseconds. */
private static final SparseIntArray CALL_SETUP_DURATION_MAP = buildCallSetupDurationMap();
/**
@@ -97,6 +121,13 @@ public class VoiceCallSessionStats {
private final SparseArray<VoiceCallSession> mCallProtos = new SparseArray<>();
/**
+ * Tracks usage of codecs for each call. The outer array is used to map each connection id to
+ * the corresponding codec usage. The inner array is used to map timestamp (key) with the
+ * codec in use (value).
+ */
+ private final SparseArray<LongSparseArray<Integer>> mCodecUsage = new SparseArray<>();
+
+ /**
* Tracks call RAT usage.
*
* <p>RAT usage is mainly tied to phones rather than calls, since each phone can have multiple
@@ -106,7 +137,6 @@ public class VoiceCallSessionStats {
private final int mPhoneId;
private final Phone mPhone;
- private int mCarrierId = CARRIER_ID_UNKNOWN;
private final PersistAtomsStorage mAtomsStorage =
PhoneFactory.getMetricsCollector().getAtomsStorage();
@@ -219,26 +249,53 @@ public class VoiceCallSessionStats {
/* general & misc. */
- /** Updates internal states when carrier changes. */
- public synchronized void onActiveSubscriptionInfoChanged(List<SubscriptionInfo> subInfos) {
- int slotId = getSimSlotId();
- if (subInfos != null) {
- for (SubscriptionInfo subInfo : subInfos) {
- if (subInfo.getSimSlotIndex() == slotId) {
- mCarrierId = subInfo.getCarrierId();
- }
- }
- }
- }
-
/** Updates internal states when audio codec for a call is changed. */
public synchronized void onAudioCodecChanged(Connection conn, int audioQuality) {
- VoiceCallSession proto = mCallProtos.get(getConnectionId(conn));
+ int id = getConnectionId(conn);
+ VoiceCallSession proto = mCallProtos.get(id);
if (proto == null) {
loge("onAudioCodecChanged: untracked connection");
return;
}
- proto.codecBitmask |= audioQualityToCodecBitmask(proto.bearerAtEnd, audioQuality);
+ int codec = audioQualityToCodec(proto.bearerAtEnd, audioQuality);
+ proto.codecBitmask |= (1L << codec);
+
+ if (mCodecUsage.contains(id)) {
+ mCodecUsage.get(id).append(getTimeMillis(), codec);
+ } else {
+ LongSparseArray<Integer> arr = new LongSparseArray<>();
+ arr.append(getTimeMillis(), codec);
+ mCodecUsage.put(id, arr);
+ }
+ }
+
+ /** Updates internal states when video state changes. */
+ public synchronized void onVideoStateChange(
+ ImsPhoneConnection conn, @VideoState int videoState) {
+ int id = getConnectionId(conn);
+ VoiceCallSession proto = mCallProtos.get(id);
+ if (proto == null) {
+ loge("onVideoStateChange: untracked connection");
+ return;
+ }
+ logd("Video state = " + videoState);
+ if (videoState != VideoProfile.STATE_AUDIO_ONLY) {
+ proto.videoEnabled = true;
+ }
+ }
+
+ /** Updates internal states when multiparty state changes. */
+ public synchronized void onMultipartyChange(ImsPhoneConnection conn, boolean isMultiParty) {
+ int id = getConnectionId(conn);
+ VoiceCallSession proto = mCallProtos.get(id);
+ if (proto == null) {
+ loge("onMultipartyChange: untracked connection");
+ return;
+ }
+ logd("Multiparty = " + isMultiParty);
+ if (isMultiParty) {
+ proto.isMultiparty = true;
+ }
}
/**
@@ -325,7 +382,7 @@ public class VoiceCallSessionStats {
} else {
int bearer = getBearer(conn);
ServiceState serviceState = getServiceState();
- int rat = getRat(serviceState);
+ @NetworkType int rat = getRat(serviceState);
VoiceCallSession proto = new VoiceCallSession();
@@ -338,19 +395,21 @@ public class VoiceCallSessionStats {
proto.disconnectExtraCode = conn.getPreciseDisconnectCause();
proto.disconnectExtraMessage = conn.getVendorDisconnectCause();
proto.ratAtStart = rat;
+ proto.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
proto.ratAtEnd = rat;
proto.ratSwitchCount = 0L;
proto.codecBitmask = 0L;
- proto.simSlotIndex = getSimSlotId();
- proto.isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
- proto.isEsim = isEsim();
- proto.carrierId = mCarrierId;
+ proto.simSlotIndex = mPhoneId;
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(mPhoneId);
+ proto.carrierId = mPhone.getCarrierId();
proto.srvccCompleted = false;
proto.srvccFailureCount = 0L;
proto.srvccCancellationCount = 0L;
proto.rttEnabled = false;
proto.isEmergency = conn.isEmergencyCall();
proto.isRoaming = serviceState != null ? serviceState.getVoiceRoaming() : false;
+ proto.isMultiparty = conn.isMultiparty();
// internal fields for tracking
proto.setupBeginMillis = getTimeMillis();
@@ -373,6 +432,12 @@ public class VoiceCallSessionStats {
mCallProtos.delete(connectionId);
proto.concurrentCallCountAtEnd = mCallProtos.size();
+ // Calculate signal strength at the end of the call
+ proto.signalStrengthAtEnd = getSignalStrength(proto.ratAtEnd);
+
+ // Calculate main codec quality
+ proto.mainCodecQuality = finalizeMainCodecQuality(connectionId);
+
// ensure internal fields are cleared
proto.setupBeginMillis = 0L;
@@ -381,6 +446,11 @@ public class VoiceCallSessionStats {
proto.disconnectExtraMessage = "";
}
+ // Retry populating carrier ID if it was invalid
+ if (proto.carrierId <= 0) {
+ proto.carrierId = mPhone.getCarrierId();
+ }
+
mAtomsStorage.addVoiceCallSession(proto);
// merge RAT usages to PersistPullers when the call session ends (i.e. no more active calls)
@@ -430,24 +500,35 @@ public class VoiceCallSessionStats {
private void checkCallSetup(Connection conn, VoiceCallSession proto) {
if (proto.setupBeginMillis != 0L && isSetupFinished(conn.getCall())) {
- proto.setupDuration = classifySetupDuration(getTimeMillis() - proto.setupBeginMillis);
+ proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
+ proto.setupDuration = classifySetupDuration(proto.setupDurationMillis);
proto.setupBeginMillis = 0L;
}
- // clear setupFailed if call now active, but otherwise leave it unchanged
- if (conn.getState() == Call.State.ACTIVE) {
+ // Clear setupFailed if call now active, but otherwise leave it unchanged
+ // This block is executed only once, when call becomes active for the first time.
+ if (proto.setupFailed && conn.getState() == Call.State.ACTIVE) {
proto.setupFailed = false;
+ // Track RAT when voice call is connected.
+ ServiceState serviceState = getServiceState();
+ proto.ratAtConnected = getRat(serviceState);
+ // Reset list of codecs with the last codec at the present time. In this way, we
+ // track codec quality only after call is connected and not while ringing.
+ resetCodecList(conn);
}
}
private void updateRatTracker(ServiceState state) {
- int rat = getRat(state);
- mRatUsage.add(mCarrierId, rat, getTimeMillis(), getConnectionIds());
+ @NetworkType int rat = getRat(state);
+ int band = ServiceStateStats.getBand(state, rat);
+
+ mRatUsage.add(mPhone.getCarrierId(), rat, getTimeMillis(), getConnectionIds());
for (int i = 0; i < mCallProtos.size(); i++) {
VoiceCallSession proto = mCallProtos.valueAt(i);
if (proto.ratAtEnd != rat) {
proto.ratSwitchCount++;
proto.ratAtEnd = rat;
}
+ proto.bandAtEnd = band;
// assuming that SIM carrier ID does not change during the call
}
}
@@ -457,27 +538,10 @@ public class VoiceCallSessionStats {
proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
proto.disconnectReasonCode = reasonInfo.mCode;
proto.disconnectExtraCode = reasonInfo.mExtraCode;
- proto.disconnectExtraMessage = reasonInfo.mExtraMessage;
+ proto.disconnectExtraMessage = ImsStats.filterExtraMessage(reasonInfo.mExtraMessage);
finishCall(id);
}
- private boolean isEsim() {
- int slotId = getSimSlotId();
- UiccSlot slot = mUiccController.getUiccSlot(slotId);
- if (slot != null) {
- return slot.isEuicc();
- } else {
- // should not happen, but assume we are not using eSIM
- loge("isEsim: slot %d is null", slotId);
- return false;
- }
- }
-
- private int getSimSlotId() {
- // NOTE: UiccController's mapping hasn't be initialized when Phone was created
- return mUiccController.getSlotIdFromPhoneId(mPhoneId);
- }
-
private @Nullable ServiceState getServiceState() {
ServiceStateTracker tracker = mPhone.getServiceStateTracker();
return tracker != null ? tracker.getServiceState() : null;
@@ -509,13 +573,124 @@ public class VoiceCallSessionStats {
}
boolean isWifiCall =
mPhone.getImsPhone() != null
- && mPhone.getImsPhone().isWifiCallingEnabled()
- && state.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN;
+ && mPhone.getImsPhone().isWifiCallingEnabled()
+ && state.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN;
return isWifiCall ? TelephonyManager.NETWORK_TYPE_IWLAN : state.getVoiceNetworkType();
}
- // NOTE: when setup is finished for MO calls, it is not successful yet.
+ /** Returns the signal strength. */
+ private int getSignalStrength(@NetworkType int rat) {
+ if (rat == TelephonyManager.NETWORK_TYPE_IWLAN) {
+ return getSignalStrengthWifi();
+ } else {
+ return getSignalStrengthCellular();
+ }
+ }
+
+ /** Returns the signal strength of WiFi. */
+ private int getSignalStrengthWifi() {
+ WifiManager wifiManager =
+ (WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE);
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ int result = VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ if (wifiInfo != null) {
+ int level = wifiManager.calculateSignalLevel(wifiInfo.getRssi());
+ int max = wifiManager.getMaxSignalLevel();
+ // Scale result into 0 to 4 range.
+ result = VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT
+ * level / max;
+ logd("WiFi level: " + result + " (" + level + "/" + max + ")");
+ }
+ return result;
+ }
+
+ /** Returns the signal strength of cellular RAT. */
+ private int getSignalStrengthCellular() {
+ return mPhone.getSignalStrength().getLevel();
+ }
+
+ /** Resets the list of codecs used for the connection with only the codec currently in use. */
+ private void resetCodecList(Connection conn) {
+ int id = getConnectionId(conn);
+ LongSparseArray<Integer> codecUsage = mCodecUsage.get(id);
+ if (codecUsage != null) {
+ int lastCodec = codecUsage.valueAt(codecUsage.size() - 1);
+ LongSparseArray<Integer> arr = new LongSparseArray<>();
+ arr.append(getTimeMillis(), lastCodec);
+ mCodecUsage.put(id, arr);
+ }
+ }
+
+ /** Returns the main codec quality used during the call. */
+ private int finalizeMainCodecQuality(int connectionId) {
+ // Retrieve information about codec usage for this call and remove it from main array.
+ if (!mCodecUsage.contains(connectionId)) {
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+ }
+ LongSparseArray<Integer> codecUsage = mCodecUsage.get(connectionId);
+ mCodecUsage.delete(connectionId);
+
+ // Append fake entry at the end, to facilitate the calculation of time for each codec.
+ codecUsage.put(getTimeMillis(), AudioCodec.AUDIO_CODEC_UNKNOWN);
+
+ // Calculate array with time for each quality
+ int totalTime = 0;
+ long[] timePerQuality = new long[CODEC_QUALITY_COUNT];
+ for (int i = 0; i < codecUsage.size() - 1; i++) {
+ long time = codecUsage.keyAt(i + 1) - codecUsage.keyAt(i);
+ int quality = getCodecQuality(codecUsage.valueAt(i));
+ timePerQuality[quality] += time;
+ totalTime += time;
+ }
+ logd("Time per codec quality = " + Arrays.toString(timePerQuality));
+
+ // We calculate 70% duration of the call as the threshold for the main audio codec quality
+ // and iterate on all codec qualities. As soon as the sum of codec duration is greater than
+ // the threshold, we have identified the main codec quality.
+ long timeAtMinimumQuality = 0;
+ long timeThreshold = totalTime * MAIN_CODEC_QUALITY_THRESHOLD / 100;
+ for (int i = CODEC_QUALITY_COUNT - 1; i >= 0; i--) {
+ timeAtMinimumQuality += timePerQuality[i];
+ if (timeAtMinimumQuality >= timeThreshold) {
+ return i;
+ }
+ }
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+ }
+
+ private int getCodecQuality(int codec) {
+ switch(codec) {
+ case AudioCodec.AUDIO_CODEC_AMR:
+ case AudioCodec.AUDIO_CODEC_QCELP13K:
+ case AudioCodec.AUDIO_CODEC_EVRC:
+ case AudioCodec.AUDIO_CODEC_EVRC_B:
+ case AudioCodec.AUDIO_CODEC_EVRC_NW:
+ case AudioCodec.AUDIO_CODEC_GSM_EFR:
+ case AudioCodec.AUDIO_CODEC_GSM_FR:
+ case AudioCodec.AUDIO_CODEC_GSM_HR:
+ case AudioCodec.AUDIO_CODEC_G711U:
+ case AudioCodec.AUDIO_CODEC_G723:
+ case AudioCodec.AUDIO_CODEC_G711A:
+ case AudioCodec.AUDIO_CODEC_G722:
+ case AudioCodec.AUDIO_CODEC_G711AB:
+ case AudioCodec.AUDIO_CODEC_G729:
+ case AudioCodec.AUDIO_CODEC_EVS_NB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ case AudioCodec.AUDIO_CODEC_AMR_WB:
+ case AudioCodec.AUDIO_CODEC_EVS_WB:
+ case AudioCodec.AUDIO_CODEC_EVRC_WB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND;
+ case AudioCodec.AUDIO_CODEC_EVS_SWB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+ case AudioCodec.AUDIO_CODEC_EVS_FB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND;
+ default:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+ }
+ }
+
private static boolean isSetupFinished(@Nullable Call call) {
+ // NOTE: when setup is finished for MO calls, it is not successful yet.
if (call != null) {
switch (call.getState()) {
case ACTIVE: // MT setup: accepted to ACTIVE
@@ -527,19 +702,20 @@ public class VoiceCallSessionStats {
return false;
}
- private static long audioQualityToCodecBitmask(int bearer, int audioQuality) {
+ private static int audioQualityToCodec(int bearer, int audioQuality) {
switch (bearer) {
case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS:
- return CS_CODEC_MAP.get(audioQuality, AUDIO_CODEC_UNKNOWN);
+ return CS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN);
case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS:
- return IMS_CODEC_MAP.get(audioQuality, AUDIO_CODEC_UNKNOWN);
+ return IMS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN);
default:
- loge("audioQualityToCodecBitmask: unknown bearer %d", bearer);
- return AUDIO_CODEC_UNKNOWN;
+ loge("audioQualityToCodec: unknown bearer %d", bearer);
+ return AudioCodec.AUDIO_CODEC_UNKNOWN;
}
}
- private static int classifySetupDuration(long durationMillis) {
+ private static int classifySetupDuration(int durationMillis) {
+ // keys in CALL_SETUP_DURATION_MAP are upper bounds in ascending order
for (int i = 0; i < CALL_SETUP_DURATION_MAP.size(); i++) {
if (durationMillis < CALL_SETUP_DURATION_MAP.keyAt(i)) {
return CALL_SETUP_DURATION_MAP.valueAt(i);
@@ -574,63 +750,75 @@ public class VoiceCallSessionStats {
Rlog.e(TAG, String.format(format, args));
}
- private static SparseLongArray buildGsmCdmaCodecMap() {
- SparseLongArray map = new SparseLongArray();
-
- map.put(DriverCall.AUDIO_QUALITY_AMR, 1L << AudioCodec.AUDIO_CODEC_AMR);
- map.put(DriverCall.AUDIO_QUALITY_AMR_WB, 1L << AudioCodec.AUDIO_CODEC_AMR_WB);
- map.put(DriverCall.AUDIO_QUALITY_GSM_EFR, 1L << AudioCodec.AUDIO_CODEC_GSM_EFR);
- map.put(DriverCall.AUDIO_QUALITY_GSM_FR, 1L << AudioCodec.AUDIO_CODEC_GSM_FR);
- map.put(DriverCall.AUDIO_QUALITY_GSM_HR, 1L << AudioCodec.AUDIO_CODEC_GSM_HR);
- map.put(DriverCall.AUDIO_QUALITY_EVRC, 1L << AudioCodec.AUDIO_CODEC_EVRC);
- map.put(DriverCall.AUDIO_QUALITY_EVRC_B, 1L << AudioCodec.AUDIO_CODEC_EVRC_B);
- map.put(DriverCall.AUDIO_QUALITY_EVRC_WB, 1L << AudioCodec.AUDIO_CODEC_EVRC_WB);
- map.put(DriverCall.AUDIO_QUALITY_EVRC_NW, 1L << AudioCodec.AUDIO_CODEC_EVRC_NW);
-
+ private static SparseIntArray buildGsmCdmaCodecMap() {
+ SparseIntArray map = new SparseIntArray();
+ map.put(DriverCall.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR);
+ map.put(DriverCall.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB);
+ map.put(DriverCall.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR);
+ map.put(DriverCall.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR);
+ map.put(DriverCall.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW);
return map;
}
- private static SparseLongArray buildImsCodecMap() {
- SparseLongArray map = new SparseLongArray();
-
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 1L << AudioCodec.AUDIO_CODEC_AMR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB, 1L << AudioCodec.AUDIO_CODEC_AMR_WB);
- map.put(
- ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K,
- 1L << AudioCodec.AUDIO_CODEC_QCELP13K);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC, 1L << AudioCodec.AUDIO_CODEC_EVRC);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B, 1L << AudioCodec.AUDIO_CODEC_EVRC_B);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB, 1L << AudioCodec.AUDIO_CODEC_EVRC_WB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW, 1L << AudioCodec.AUDIO_CODEC_EVRC_NW);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR, 1L << AudioCodec.AUDIO_CODEC_GSM_EFR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR, 1L << AudioCodec.AUDIO_CODEC_GSM_FR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR, 1L << AudioCodec.AUDIO_CODEC_GSM_HR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711U, 1L << AudioCodec.AUDIO_CODEC_G711U);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G723, 1L << AudioCodec.AUDIO_CODEC_G723);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711A, 1L << AudioCodec.AUDIO_CODEC_G711A);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G722, 1L << AudioCodec.AUDIO_CODEC_G722);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711AB, 1L << AudioCodec.AUDIO_CODEC_G711AB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G729, 1L << AudioCodec.AUDIO_CODEC_G729);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB, 1L << AudioCodec.AUDIO_CODEC_EVS_NB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB, 1L << AudioCodec.AUDIO_CODEC_EVS_WB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB, 1L << AudioCodec.AUDIO_CODEC_EVS_SWB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB, 1L << AudioCodec.AUDIO_CODEC_EVS_FB);
-
+ private static SparseIntArray buildImsCodecMap() {
+ SparseIntArray map = new SparseIntArray();
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K, AudioCodec.AUDIO_CODEC_QCELP13K);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711U, AudioCodec.AUDIO_CODEC_G711U);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G723, AudioCodec.AUDIO_CODEC_G723);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711A, AudioCodec.AUDIO_CODEC_G711A);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G722, AudioCodec.AUDIO_CODEC_G722);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711AB, AudioCodec.AUDIO_CODEC_G711AB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G729, AudioCodec.AUDIO_CODEC_G729);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB, AudioCodec.AUDIO_CODEC_EVS_NB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB, AudioCodec.AUDIO_CODEC_EVS_WB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB, AudioCodec.AUDIO_CODEC_EVS_SWB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB, AudioCodec.AUDIO_CODEC_EVS_FB);
return map;
}
private static SparseIntArray buildCallSetupDurationMap() {
SparseIntArray map = new SparseIntArray();
- map.put(0, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN);
- map.put(60, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST);
- map.put(100, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST);
- map.put(300, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST);
- map.put(600, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST);
- map.put(1000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL);
- map.put(3000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW);
- map.put(6000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW);
- map.put(10000, VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW);
+ map.put(
+ CALL_SETUP_DURATION_UNKNOWN,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN);
+ map.put(
+ CALL_SETUP_DURATION_EXTREMELY_FAST,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST);
+ map.put(
+ CALL_SETUP_DURATION_ULTRA_FAST,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST);
+ map.put(
+ CALL_SETUP_DURATION_VERY_FAST,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST);
+ map.put(
+ CALL_SETUP_DURATION_FAST,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST);
+ map.put(
+ CALL_SETUP_DURATION_NORMAL,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_NORMAL);
+ map.put(
+ CALL_SETUP_DURATION_SLOW,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_SLOW);
+ map.put(
+ CALL_SETUP_DURATION_VERY_SLOW,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW);
+ map.put(
+ CALL_SETUP_DURATION_ULTRA_SLOW,
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_SLOW);
// anything above would be CALL_SETUP_DURATION_EXTREMELY_SLOW
return map;
diff --git a/src/java/com/android/internal/telephony/nitz/TimeZoneLookupHelper.java b/src/java/com/android/internal/telephony/nitz/TimeZoneLookupHelper.java
index fab45f7fac..dd147e58fa 100644
--- a/src/java/com/android/internal/telephony/nitz/TimeZoneLookupHelper.java
+++ b/src/java/com/android/internal/telephony/nitz/TimeZoneLookupHelper.java
@@ -259,7 +259,11 @@ public final class TimeZoneLookupHelper {
private static OffsetResult lookupByInstantOffsetDst(long timeMillis, int utcOffsetMillis,
@Nullable Boolean isDst) {
- String[] zones = TimeZone.getAvailableIDs();
+ // Use java.util.TimeZone and not android.icu.util.TimeZone to find candidate zone IDs: ICU
+ // references some non-standard zone IDs that can be rejected by java.util.TimeZone. There
+ // is a CTS test (in com.android.i18n.test.timezone.TimeZoneIntegrationTest) that confirms
+ // that ICU can interpret all IDs that are known to java.util.TimeZone.
+ String[] zones = java.util.TimeZone.getAvailableIDs();
TimeZone match = null;
boolean isOnlyMatch = true;
for (String zone : zones) {
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index e57c12f268..f3c1839e68 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -24,15 +24,18 @@ import android.os.Message;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.NetworkScanRequest;
import android.telephony.SignalThresholdInfo;
+import android.telephony.TelephonyManager;
import android.telephony.data.DataProfile;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
/**
* SIP doesn't need CommandsInterface. The class does nothing but made to work
@@ -283,8 +286,9 @@ class SipCommandInterface extends BaseCommands implements CommandsInterface {
@Override
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- Message result) {
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, Message result) {
}
@Override
@@ -456,6 +460,15 @@ class SipCommandInterface extends BaseCommands implements CommandsInterface {
}
@Override
+ public void setAllowedNetworkTypesBitmap(
+ @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message response) {
+ }
+
+ @Override
+ public void getAllowedNetworkTypesBitmap(Message response) {
+ }
+
+ @Override
public void setLocationUpdates(boolean enable, Message response) {
}
@@ -663,4 +676,20 @@ class SipCommandInterface extends BaseCommands implements CommandsInterface {
@Override
public void stopNattKeepalive(int sessionHandle, Message result) {
}
+
+ @Override
+ public void allocatePduSessionId(Message result) {
+ }
+
+ @Override
+ public void releasePduSessionId(Message result, int pduSessionId) {
+ }
+
+ @Override
+ public void startHandover(Message result, int callId) {
+ }
+
+ @Override
+ public void cancelHandover(Message result, int callId) {
+ }
}
diff --git a/src/java/com/android/internal/telephony/sip/SipPhone.java b/src/java/com/android/internal/telephony/sip/SipPhone.java
index adfcdce888..c88d01f9bb 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhone.java
@@ -27,6 +27,7 @@ import android.net.sip.SipManager;
import android.net.sip.SipProfile;
import android.net.sip.SipSession;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Message;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
@@ -59,9 +60,9 @@ public class SipPhone extends SipPhoneBase {
// A call that is ringing or (call) waiting
private SipCall mRingingCall = new SipCall();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private SipCall mForegroundCall = new SipCall();
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private SipCall mBackgroundCall = new SipCall();
private SipManager mSipManager;
@@ -439,7 +440,7 @@ public class SipPhone extends SipPhoneBase {
return false;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
@@ -448,7 +449,7 @@ public class SipPhone extends SipPhoneBase {
Rlog.d(LOG_TAG, s);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loge(String s) {
Rlog.e(LOG_TAG, s);
}
@@ -468,7 +469,7 @@ public class SipPhone extends SipPhoneBase {
setState(Call.State.IDLE);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void switchWith(SipCall that) {
if (SC_DBG) log("switchWith");
synchronized (SipPhone.class) {
@@ -606,7 +607,7 @@ public class SipPhone extends SipPhoneBase {
audioGroup.getMode()));
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void hold() throws CallStateException {
if (SC_DBG) log("hold:");
setState(State.HOLDING);
@@ -614,7 +615,7 @@ public class SipPhone extends SipPhoneBase {
setAudioGroupMode();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void unhold() throws CallStateException {
if (SC_DBG) log("unhold:");
setState(State.ACTIVE);
diff --git a/src/java/com/android/internal/telephony/uicc/AdnCapacity.aidl b/src/java/com/android/internal/telephony/uicc/AdnCapacity.aidl
new file mode 100644
index 0000000000..61ae73627f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/AdnCapacity.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+parcelable AdnCapacity;
diff --git a/src/java/com/android/internal/telephony/uicc/AdnCapacity.java b/src/java/com/android/internal/telephony/uicc/AdnCapacity.java
new file mode 100644
index 0000000000..300759a870
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/AdnCapacity.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.hardware.radio.V1_6.PhonebookCapacity;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used to present ADN capacity
+ *
+ * {@hide}
+ */
+public class AdnCapacity implements Parcelable {
+
+ private int mMaxAdnCount;
+ private int mUsedAdnCount;
+ private int mMaxEmailCount;
+ private int mUsedEmailCount;
+ private int mMaxAnrCount;
+ private int mUsedAnrCount;
+ private int mMaxNameLength;
+ private int mMaxNumberLength;
+ private int mMaxEmailLength;
+ private int mMaxAnrLength;
+
+ private int mHashCode = 0;
+
+ public AdnCapacity(int maxAdnCount, int usedAdnCount, int maxEmailCount,
+ int usedEmailCount, int maxAnrCount, int usedAnrCount, int maxNameLength,
+ int maxNumberLength, int maxEmailLength, int maxAnrLength) {
+ mMaxAdnCount = maxAdnCount;
+ mUsedAdnCount = usedAdnCount;
+ mMaxEmailCount = maxEmailCount;
+ mUsedEmailCount = usedEmailCount;
+ mMaxAnrCount = maxAnrCount;
+ mUsedAnrCount = usedAnrCount;
+ mMaxNameLength = maxNameLength;
+ mMaxNumberLength = maxNumberLength;
+ mMaxEmailLength = maxEmailLength;
+ mMaxAnrLength = maxAnrLength;
+ }
+
+ public AdnCapacity(PhonebookCapacity pbCap) {
+ if (pbCap != null) {
+ mMaxAdnCount = pbCap.maxAdnRecords;
+ mUsedAdnCount = pbCap.usedAdnRecords;
+ mMaxEmailCount = pbCap.maxEmailRecords;
+ mUsedEmailCount = pbCap.usedEmailRecords;
+ mMaxAnrCount = pbCap.maxAdditionalNumberRecords;
+ mUsedAnrCount = pbCap.usedAdditionalNumberRecords;
+ mMaxNameLength = pbCap.maxNameLen;
+ mMaxNumberLength = pbCap.maxNumberLen;
+ mMaxEmailLength = pbCap.maxEmailLen;
+ mMaxAnrLength = pbCap.maxAdditionalNumberLen;
+ }
+ }
+
+ public int getMaxAdnCount() {
+ return mMaxAdnCount;
+ }
+
+ public int getUsedAdnCount() {
+ return mUsedAdnCount;
+ }
+
+ public int getMaxEmailCount() {
+ return mMaxEmailCount;
+ }
+
+ public int getUsedEmailCount() {
+ return mUsedEmailCount;
+ }
+
+ public int getMaxAnrCount() {
+ return mMaxAnrCount;
+ }
+
+ public int getUsedAnrCount() {
+ return mUsedAnrCount;
+ }
+
+ public int getMaxNameLength() {
+ return mMaxNameLength;
+ }
+
+ public int getMaxNumberLength() {
+ return mMaxNumberLength;
+ }
+
+ public int getMaxEmailLength() {
+ return mMaxEmailLength;
+ }
+
+ public int getMaxAnrLength() {
+ return mMaxAnrLength;
+ }
+
+ public boolean isSimFull() {
+ return mMaxAdnCount == mUsedAdnCount;
+ }
+
+ public static final Parcelable.Creator<AdnCapacity> CREATOR
+ = new Parcelable.Creator<AdnCapacity>() {
+ @Override
+ public AdnCapacity createFromParcel(Parcel source) {
+ final int maxAdnCount = source.readInt();
+ final int usedAdnCount = source.readInt();
+ final int maxEmailCount = source.readInt();
+ final int usedEmailCount = source.readInt();
+ final int maxAnrCount = source.readInt();
+ final int usedAnrCount = source.readInt();
+ final int maxNameLength = source.readInt();
+ final int maxNumberLength = source.readInt();
+ final int maxEmailLength = source.readInt();
+ final int maxAnrLength = source.readInt();
+ return new AdnCapacity(maxAdnCount, usedAdnCount, maxEmailCount,
+ usedEmailCount, maxAnrCount, usedAnrCount, maxNameLength,
+ maxNumberLength, maxEmailLength, maxAnrLength);
+ }
+
+ @Override
+ public AdnCapacity[] newArray(int size) {
+ return new AdnCapacity[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMaxAdnCount);
+ dest.writeInt(mUsedAdnCount);
+ dest.writeInt(mMaxEmailCount);
+ dest.writeInt(mUsedEmailCount);
+ dest.writeInt(mMaxAnrCount);
+ dest.writeInt(mUsedAnrCount);
+ dest.writeInt(mMaxNameLength);
+ dest.writeInt(mMaxNumberLength);
+ dest.writeInt(mMaxEmailLength);
+ dest.writeInt(mMaxAnrLength);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof AdnCapacity) {
+ AdnCapacity capacity = (AdnCapacity)obj;
+ return capacity.getMaxAdnCount() == mMaxAdnCount
+ && capacity.getUsedAdnCount() == mUsedAdnCount
+ && capacity.getMaxEmailCount() == mMaxEmailCount
+ && capacity.getUsedEmailCount() == mUsedEmailCount
+ && capacity.getMaxAnrCount() == mMaxAnrCount
+ && capacity.getUsedAnrCount() == mUsedAnrCount
+ && capacity.getMaxNameLength() == mMaxNameLength
+ && capacity.getMaxNumberLength() == mMaxNumberLength
+ && capacity.getMaxEmailLength() == mMaxEmailLength
+ && capacity.getMaxAnrLength() == mMaxAnrLength;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (mHashCode == 0) {
+ mHashCode = mMaxAdnCount;
+ mHashCode = 31 * mHashCode + mUsedAdnCount;
+ mHashCode = 31 * mHashCode + mMaxEmailCount;
+ mHashCode = 31 * mHashCode + mUsedEmailCount;
+ mHashCode = 31 * mHashCode + mMaxAnrCount;
+ mHashCode = 31 * mHashCode + mUsedAnrCount;
+ mHashCode = 31 * mHashCode + mMaxNameLength;
+ mHashCode = 31 * mHashCode + mMaxNumberLength;
+ mHashCode = 31 * mHashCode + mMaxEmailLength;
+ mHashCode = 31 * mHashCode + mMaxAnrLength;
+ }
+ return mHashCode;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecord.java b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
index a0b64382da..0d23ce7751 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecord.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
@@ -16,16 +16,19 @@
package com.android.internal.telephony.uicc;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.util.ArrayUtils;
import com.android.telephony.Rlog;
import java.util.Arrays;
+import java.util.List;
/**
*
@@ -39,17 +42,18 @@ public class AdnRecord implements Parcelable {
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mAlphaTag = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String mNumber = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String[] mEmails;
- @UnsupportedAppUsage
+ String[] mAdditionalNumbers = null;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mExtRecord = 0xff;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mEfid; // or 0 if none
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mRecordNumber; // or 0 if none
@@ -77,7 +81,7 @@ public class AdnRecord implements Parcelable {
//***** Static Methods
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final Parcelable.Creator<AdnRecord> CREATOR
= new Parcelable.Creator<AdnRecord>() {
@Override
@@ -87,14 +91,16 @@ public class AdnRecord implements Parcelable {
String alphaTag;
String number;
String[] emails;
+ String[] additionalNumbers;
efid = source.readInt();
recordNumber = source.readInt();
alphaTag = source.readString();
number = source.readString();
emails = source.createStringArray();
+ additionalNumbers = source.createStringArray();
- return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
+ return new AdnRecord(efid, recordNumber, alphaTag, number, emails, additionalNumbers);
}
@Override
@@ -103,46 +109,104 @@ public class AdnRecord implements Parcelable {
}
};
+ /**
+ * Returns the maximum number of characters that supported by the alpha tag for a record with
+ * the specified maximum size.
+ */
+ public static int getMaxAlphaTagBytes(int maxRecordLength) {
+ return Math.max(0, maxRecordLength - FOOTER_SIZE_BYTES);
+ }
+
+ /**
+ * Encodes the alphaTag to a binary representation supported by the SIM.
+ *
+ * <p>This is the same representation as is used for this field in buildAdnString but there
+ * is no restriction on the length.
+ */
+ @NonNull
+ public static byte[] encodeAlphaTag(String alphaTag) {
+ if (TextUtils.isEmpty(alphaTag)) {
+ return new byte[0];
+ }
+ return IccUtils.stringToAdnStringField(alphaTag);
+ }
+
+ /**
+ * Decodes an encoded alphaTag from a record or encoded tag.
+ *
+ * <p>This is the same as is used to construct an AdnRecord from byte[]
+ */
+ public static String decodeAlphaTag(byte[] encodedTagOrRecord, int offset, int length) {
+ return IccUtils.adnStringFieldToString(encodedTagOrRecord, offset, length);
+ }
+
+ /**
+ * Returns the maximum number of digits (or other dialable characters) that can be stored in
+ * the phone number.
+ *
+ * <p>Additional length is supported via the ext1 entity file but the current implementation
+ * doesn't support writing of that file so it is not included in this calculation.
+ */
+ public static int getMaxPhoneNumberDigits() {
+ // Multiply by 2 because it is packed BCD encoded (2 digits per byte).
+ return (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2;
+ }
//***** Constructor
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AdnRecord (byte[] record) {
this(0, 0, record);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AdnRecord (int efid, int recordNumber, byte[] record) {
this.mEfid = efid;
this.mRecordNumber = recordNumber;
parseRecord(record);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AdnRecord (String alphaTag, String number) {
this(0, 0, alphaTag, number);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AdnRecord (String alphaTag, String number, String[] emails) {
this(0, 0, alphaTag, number, emails);
}
- @UnsupportedAppUsage
+ public AdnRecord(String alphaTag, String number, String[] emails, String[] additionalNumbers) {
+ this(0, 0, alphaTag, number, emails, additionalNumbers);
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
this.mEfid = efid;
this.mRecordNumber = recordNumber;
this.mAlphaTag = alphaTag;
this.mNumber = number;
this.mEmails = emails;
+ this.mAdditionalNumbers = null;
+ }
+
+ public AdnRecord(int efid, int recordNumber, String alphaTag, String number, String[] emails,
+ String[] additionalNumbers) {
+ this.mEfid = efid;
+ this.mRecordNumber = recordNumber;
+ this.mAlphaTag = alphaTag;
+ this.mNumber = number;
+ this.mEmails = emails;
+ this.mAdditionalNumbers = additionalNumbers;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
this.mEfid = efid;
this.mRecordNumber = recordNumber;
this.mAlphaTag = alphaTag;
this.mNumber = number;
this.mEmails = null;
+ this.mAdditionalNumbers = null;
}
//***** Instance Methods
@@ -159,7 +223,11 @@ public class AdnRecord implements Parcelable {
return mRecordNumber;
}
- @UnsupportedAppUsage
+ public void setRecId(int recordId) {
+ mRecordNumber = recordId;
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getNumber() {
return mNumber;
}
@@ -168,25 +236,35 @@ public class AdnRecord implements Parcelable {
mNumber = number;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String[] getEmails() {
return mEmails;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void setEmails(String[] emails) {
this.mEmails = emails;
}
+ public String[] getAdditionalNumbers() {
+ return mAdditionalNumbers;
+ }
+
+ public void setAdditionalNumbers(String[] additionalNumbers) {
+ mAdditionalNumbers = additionalNumbers;
+ }
+
@Override
public String toString() {
return "ADN Record '" + mAlphaTag + "' '" + Rlog.pii(LOG_TAG, mNumber) + " "
- + Rlog.pii(LOG_TAG, mEmails) + "'";
+ + Rlog.pii(LOG_TAG, mEmails) + " "
+ + Rlog.pii(LOG_TAG, mAdditionalNumbers) + "'";
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isEmpty() {
- return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber) && mEmails == null;
+ return TextUtils.isEmpty(mAlphaTag) && TextUtils.isEmpty(mNumber)
+ && mEmails == null && mAdditionalNumbers == null;
}
public boolean hasExtendedRecord() {
@@ -207,10 +285,30 @@ public class AdnRecord implements Parcelable {
return (s1.equals(s2));
}
+ /** Help function for ANR/EMAIL array compare. */
+ private static boolean arrayCompareNullEqualsEmpty(String s1[], String s2[]) {
+ if (s1 == s2) {
+ return true;
+ }
+
+ s1 = ArrayUtils.emptyIfNull(s1, String.class);
+ s2 = ArrayUtils.emptyIfNull(s2, String.class);
+
+ List<String> src = Arrays.asList(s1);
+ List<String> dest = Arrays.asList(s2);
+
+ if (src.size() != dest.size()) {
+ return false;
+ }
+
+ return src.containsAll(dest);
+ }
+
public boolean isEqual(AdnRecord adn) {
return ( stringCompareNullEqualsEmpty(mAlphaTag, adn.mAlphaTag) &&
stringCompareNullEqualsEmpty(mNumber, adn.mNumber) &&
- Arrays.equals(mEmails, adn.mEmails));
+ arrayCompareNullEqualsEmpty(mEmails, adn.mEmails) &&
+ arrayCompareNullEqualsEmpty(mAdditionalNumbers, adn.mAdditionalNumbers));
}
//***** Parcelable Implementation
@@ -226,6 +324,7 @@ public class AdnRecord implements Parcelable {
dest.writeString(mAlphaTag);
dest.writeString(mNumber);
dest.writeStringArray(mEmails);
+ dest.writeStringArray(mAdditionalNumbers);
}
/**
@@ -236,7 +335,7 @@ public class AdnRecord implements Parcelable {
* @return hex byte[recordSize] to be written to EF record
* return null for wrong format of dialing number or tag
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public byte[] buildAdnString(int recordSize) {
byte[] bcdNumber;
byte[] byteTag;
@@ -249,31 +348,32 @@ public class AdnRecord implements Parcelable {
adnString[i] = (byte) 0xFF;
}
- if (TextUtils.isEmpty(mNumber)) {
+ if (TextUtils.isEmpty(mNumber) && TextUtils.isEmpty(mAlphaTag)) {
Rlog.w(LOG_TAG, "[buildAdnString] Empty dialing number");
return adnString; // return the empty record (for delete)
- } else if (mNumber.length()
+ } else if (mNumber != null && mNumber.length()
> (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
Rlog.w(LOG_TAG,
"[buildAdnString] Max length of dialing number is 20");
return null;
}
- byteTag = !TextUtils.isEmpty(mAlphaTag) ? GsmAlphabet.stringToGsm8BitPacked(mAlphaTag)
- : new byte[0];
+ byteTag = encodeAlphaTag(mAlphaTag);
if (byteTag.length > footerOffset) {
Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
return null;
} else {
- bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
- mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
+ if (!TextUtils.isEmpty(mNumber)) {
+ bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+ mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
- System.arraycopy(bcdNumber, 0, adnString,
- footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
+ System.arraycopy(bcdNumber, 0, adnString,
+ footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
- adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
- = (byte) (bcdNumber.length);
+ adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
+ = (byte) (bcdNumber.length);
+ }
adnString[footerOffset + ADN_CAPABILITY_ID]
= (byte) 0xFF; // Capability Id
adnString[footerOffset + ADN_EXTENSION_ID]
@@ -328,7 +428,7 @@ public class AdnRecord implements Parcelable {
private void
parseRecord(byte[] record) {
try {
- mAlphaTag = IccUtils.adnStringFieldToString(
+ mAlphaTag = decodeAlphaTag(
record, 0, record.length - FOOTER_SIZE_BYTES);
int footerOffset = record.length - FOOTER_SIZE_BYTES;
@@ -358,12 +458,13 @@ public class AdnRecord implements Parcelable {
mExtRecord = 0xff & record[record.length - 1];
mEmails = null;
-
+ mAdditionalNumbers = null;
} catch (RuntimeException ex) {
Rlog.w(LOG_TAG, "Error parsing AdnRecord", ex);
mNumber = "";
mAlphaTag = "";
mEmails = null;
+ mAdditionalNumbers = null;
}
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
index 90d7a383a6..21a6e37981 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.SparseArray;
@@ -33,9 +34,9 @@ import java.util.Iterator;
public class AdnRecordCache extends Handler implements IccConstants {
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IccFileHandler mFh;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private UsimPhoneBookManager mUsimPhoneBookManager;
// Indexed by EF ID
@@ -43,12 +44,12 @@ public class AdnRecordCache extends Handler implements IccConstants {
= new SparseArray<ArrayList<AdnRecord>>();
// People waiting for ADN-like files to be loaded
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
SparseArray<ArrayList<Message>> mAdnLikeWaiters
= new SparseArray<ArrayList<Message>>();
// People waiting for adn record to be updated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
SparseArray<Message> mUserWriteResponse = new SparseArray<Message>();
//***** Event Constants
@@ -70,7 +71,7 @@ public class AdnRecordCache extends Handler implements IccConstants {
/**
* Called from SIMRecords.onRadioNotAvailable and SIMRecords.handleSimRefresh.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void reset() {
mAdnLikeFiles.clear();
mUsimPhoneBookManager.reset();
@@ -102,7 +103,7 @@ public class AdnRecordCache extends Handler implements IccConstants {
* @return List of AdnRecords for efid if we've already loaded them this
* radio session, or null if we haven't
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public ArrayList<AdnRecord>
getRecordsIfLoaded(int efid) {
return mAdnLikeFiles.get(efid);
@@ -114,7 +115,7 @@ public class AdnRecordCache extends Handler implements IccConstants {
*
* See 3GPP TS 51.011 for this mapping
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int extensionEfForEf(int efid) {
switch (efid) {
case EF_MBDN: return EF_EXT6;
@@ -123,11 +124,13 @@ public class AdnRecordCache extends Handler implements IccConstants {
case EF_FDN: return EF_EXT2;
case EF_MSISDN: return EF_EXT1;
case EF_PBR: return 0; // The EF PBR doesn't have an extension record
- default: return -1;
+ default:
+ // See TS 131.102 4.4.2.1 '4FXX' are entity files from EF PBR
+ return (0x4FFF & efid) == efid ? 0 : -1;
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void sendErrorResponse(Message response, String errString) {
if (response != null) {
Exception e = new RuntimeException(errString);
@@ -146,7 +149,7 @@ public class AdnRecordCache extends Handler implements IccConstants {
* @param response message to be posted when done
* response.exception hold the exception in error
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void updateAdnByIndex(int efid, AdnRecord adn, int recordIndex, String pin2,
Message response) {
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
index a23248ccb4..a688c6e5d7 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -32,7 +33,7 @@ public class AdnRecordLoader extends Handler {
//***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private IccFileHandler mFh;
int mEf;
int mExtensionEF;
@@ -60,7 +61,7 @@ public class AdnRecordLoader extends Handler {
//***** Constructor
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
AdnRecordLoader(IccFileHandler fh) {
// The telephony unit-test cases may create AdnRecords
// in secondary threads
@@ -68,7 +69,7 @@ public class AdnRecordLoader extends Handler {
mFh = fh;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String getEFPath(int efid) {
if (efid == IccConstants.EF_ADN) {
return IccConstants.MF_SIM + IccConstants.DF_TELECOM;
@@ -81,7 +82,7 @@ public class AdnRecordLoader extends Handler {
* Resulting AdnRecord is placed in response.obj.result
* or response.obj.exception is set
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void
loadFromEF(int ef, int extensionEF, int recordNumber,
Message response) {
@@ -128,7 +129,7 @@ public class AdnRecordLoader extends Handler {
* @param pin2 for CHV2 operations, must be null if pin2 is not needed
* @param response will be sent to its handler when completed
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void
updateEF(AdnRecord adn, int ef, int extensionEF, int recordNumber,
String pin2, Message response) {
diff --git a/src/java/com/android/internal/telephony/uicc/CsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/CsimFileHandler.java
index c5dd05c0d8..2190df6e90 100644
--- a/src/java/com/android/internal/telephony/uicc/CsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/CsimFileHandler.java
@@ -43,8 +43,12 @@ public final class CsimFileHandler extends IccFileHandler implements IccConstant
case EF_CSIM_IMSIM:
case EF_CSIM_CDMAHOME:
case EF_CSIM_EPRL:
+ case EF_CSIM_PRL:
case EF_CSIM_MIPUPP:
return MF_SIM + DF_ADF;
+ case EF_CSIM_MSPL:
+ case EF_CSIM_MLPL:
+ return MF_SIM + DF_TELECOM + DF_MMSS;
}
String path = getCommonIccEFPath(efid);
if (path == null) {
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
index d0ad3584d9..fb8b11128e 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.internal.telephony.uicc.IccCardStatus.PinState;
import com.android.telephony.Rlog;
@@ -144,7 +145,7 @@ public class IccCardApplicationStatus {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AppType app_type;
public AppState app_state;
// applicable only if app_state == RIL_APPSTATE_SUBSCRIPTION_PERSO
@@ -158,11 +159,11 @@ public class IccCardApplicationStatus {
public PinState pin1;
public PinState pin2;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IccCardApplicationStatus() {
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public AppType AppTypeFromRILInt(int type) {
AppType newType;
/* RIL_AppType ril.h */
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
index 765d1e1bc0..74f7a41954 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.telephony.SubscriptionInfo;
/**
@@ -36,7 +37,7 @@ public class IccCardStatus {
CARDSTATE_ERROR,
CARDSTATE_RESTRICTED;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isCardPresent() {
return this == CARDSTATE_PRESENT ||
this == CARDSTATE_RESTRICTED;
@@ -69,20 +70,20 @@ public class IccCardStatus {
@UnsupportedAppUsage
public CardState mCardState;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public PinState mUniversalPinState;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int mGsmUmtsSubscriptionAppIndex;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int mCdmaSubscriptionAppIndex;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int mImsSubscriptionAppIndex;
public int physicalSlotIndex = UiccController.INVALID_SLOT_ID;
public String atr;
public String iccid;
public String eid;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IccCardApplicationStatus[] mApplications;
public void setCardState(int state) {
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index 0f41f1ef16..5eae070eb4 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -72,6 +72,10 @@ public interface IccConstants {
static final int EF_CSIM_IMSIM = 0x6F22;
static final int EF_CSIM_CDMAHOME = 0x6F28;
static final int EF_CSIM_EPRL = 0x6F5A;
+ static final int EF_CSIM_PRL = 0x6F30;
+ // C.S0074-Av1.0 Section 4
+ static final int EF_CSIM_MLPL = 0x4F20;
+ static final int EF_CSIM_MSPL = 0x4F21;
static final int EF_CSIM_MIPUPP = 0x6F4D;
//ISIM access
@@ -103,6 +107,7 @@ public interface IccConstants {
static final String DF_GRAPHICS = "5F50";
static final String DF_GSM = "7F20";
static final String DF_CDMA = "7F25";
+ static final String DF_MMSS = "5F3C";
//UICC access
static final String DF_ADF = "7FFF";
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index ceeed34ce6..f3b1d0bbf7 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -100,27 +101,27 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
protected static final int EVENT_GET_EF_TRANSPARENT_SIZE_DONE = 12;
// member variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final CommandsInterface mCi;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final UiccCardApplication mParentApp;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final String mAid;
static class LoadLinearFixedContext {
int mEfid;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mRecordNum, mRecordSize, mCountRecords;
boolean mLoadAll;
String mPath;
Message mOnLoaded;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ArrayList<byte[]> results;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
LoadLinearFixedContext(int efid, int recordNum, Message onLoaded) {
mEfid = efid;
mRecordNum = recordNum;
@@ -179,7 +180,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* ((AsyncResult)(onLoaded.obj)).result is the byte[]
*
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void loadEFLinearFixed(int fileid, String path, int recordNum, Message onLoaded) {
String efPath = (path == null) ? getEFPath(fileid) : path;
Message response
@@ -200,7 +201,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* ((AsyncResult)(onLoaded.obj)).result is the byte[]
*
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) {
loadEFLinearFixed(fileid, getEFPath(fileid), recordNum, onLoaded);
}
@@ -235,7 +236,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* and recordSize[2] is the number of records in the EF file. So recordSize[0]
* * recordSize[2] = recordSize[1].
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void getEFLinearRecordSize(int fileid, String path, Message onLoaded) {
String efPath = (path == null) ? getEFPath(fileid) : path;
Message response
@@ -254,7 +255,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* and recordSize[2] is the number of records in the EF file. So recordSize[0]
* * recordSize[2] = recordSize[1].
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void getEFLinearRecordSize(int fileid, Message onLoaded) {
getEFLinearRecordSize(fileid, getEFPath(fileid), onLoaded);
}
@@ -301,7 +302,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
*
* ((AsyncResult)(onLoaded.obj)).result is an ArrayList<byte[]>
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void loadEFLinearFixedAll(int fileid, String path, Message onLoaded) {
String efPath = (path == null) ? getEFPath(fileid) : path;
Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
@@ -320,7 +321,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* ((AsyncResult)(onLoaded.obj)).result is an ArrayList<byte[]>
*
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void loadEFLinearFixedAll(int fileid, Message onLoaded) {
loadEFLinearFixedAll(fileid, getEFPath(fileid), onLoaded);
}
@@ -399,7 +400,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* @param onComplete onComplete.obj will be an AsyncResult
* onComplete.obj.userObj will be a IccIoResult on success
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void updateEFLinearFixed(int fileid, String path, int recordNum, byte[] data,
String pin2, Message onComplete) {
String efPath = (path == null) ? getEFPath(fileid) : path;
@@ -417,7 +418,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* @param onComplete onComplete.obj will be an AsyncResult
* onComplete.obj.userObj will be a IccIoResult on success
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void updateEFLinearFixed(int fileid, int recordNum, byte[] data,
String pin2, Message onComplete) {
mCi.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, getEFPath(fileid),
@@ -430,7 +431,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* @param fileid EF id
* @param data must be exactly as long as the EF
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void updateEFTransparent(int fileid, byte[] data, Message onComplete) {
mCi.iccIOForApp(COMMAND_UPDATE_BINARY, fileid, getEFPath(fileid),
0, 0, data.length,
@@ -675,7 +676,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
/**
* Returns the root path of the EF file.
- * i.e returns MasterFile + DFfile as a string.
+ * i.e returns MainFile + DFfile as a string.
* Ex: For EF_ADN on a SIM, it will return "3F007F10"
* This function handles only EFids that are common to
* RUIM, SIM, USIM and other types of Icc cards.
@@ -707,7 +708,7 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
return null;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected abstract String getEFPath(int efid);
protected abstract void logd(String s);
protected abstract void loge(String s);
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index b33e899b9f..4eee4cfa82 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.AsyncResult;
@@ -25,6 +26,7 @@ import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.SystemClock;
+import android.telephony.CellIdentity;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -54,6 +56,11 @@ public abstract class IccRecords extends Handler implements IccConstants {
protected static final boolean DBG = true;
protected static final boolean VDBG = false; // STOPSHIP if true
+ public static final int PLMN_MIN_LENGTH = CellIdentity.MCC_LENGTH
+ + CellIdentity.MNC_MIN_LENGTH;
+ public static final int PLMN_MAX_LENGTH = CellIdentity.MCC_LENGTH
+ + CellIdentity.MNC_MAX_LENGTH;
+
// Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
"302370", "302720", "310260",
@@ -150,6 +157,14 @@ public abstract class IccRecords extends Handler implements IccConstants {
// Reference: 3GPP TS 31.102 Section 4.2.66
protected String[] mSpdi;
+ // A list of PLMN Network Name (PNN).
+ // Reference: 3GPP TS 31.102 Section 4.2.58
+ protected PlmnNetworkName[] mPnns;
+
+ // Operator PLMN List (OPL).
+ // Reference: 3GPP TS 31.102 Section 4.2.59
+ protected OperatorPlmnInfo[] mOpl;
+
// Carrier name display condition bitmask
// Reference: 3GPP TS 131.102 section 4.2.12 EF_SPN Display Condition
@@ -699,6 +714,14 @@ public abstract class IccRecords extends Handler implements IccConstants {
return mPnnHomeName;
}
+ public PlmnNetworkName[] getPnns() {
+ return mPnns;
+ }
+
+ public OperatorPlmnInfo[] getOpl() {
+ return mOpl;
+ }
+
public void setMsisdnNumber(String alphaTag, String number,
Message onComplete) {
loge("setMsisdn() should not be invoked on base IccRecords");
@@ -1356,35 +1379,112 @@ public abstract class IccRecords extends Handler implements IccConstants {
}
/**
+ * Get network name in PNN for the provided PLMN and LAC/TAC.
+ *
+ * @param opls OPL.
+ * @param pnns PNN list.
+ * @param plmn PLMN.
+ * @param lacTac LAC/TAC
+ * @return network Name for the provided PLMN and LAC/TAC.
+ */
+ @Nullable public static String getNetworkNameForPlmnFromPnnOpl(PlmnNetworkName[] pnns,
+ OperatorPlmnInfo[] opls, @Nullable String plmn, int lacTac) {
+ if (opls == null || pnns == null || plmn == null || plmn.length() < PLMN_MIN_LENGTH
+ || plmn.length() > PLMN_MAX_LENGTH) {
+ return null;
+ }
+
+ for (OperatorPlmnInfo operatorPlmnInfo: opls) {
+ int pnnIdx = operatorPlmnInfo.getPnnIdx(plmn, lacTac);
+ if (pnnIdx >= 0) {
+ if (pnnIdx < pnns.length && pnns[pnnIdx] != null) {
+ return pnns[pnnIdx].getName();
+ } else {
+ Rlog.e("IccRecords", "Invalid PNN record for Record" + pnnIdx);
+ break;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* Operator PLMN information. This contains the location area information or tracking area
* that are used to associate a specific name contained in EF_PNN.
*
* Reference: 3GPP TS 31.102 section 4.2.59 EF_OPL
*/
public static final class OperatorPlmnInfo {
- // PLMN numeric that may contains wildcard character ".".
- // For example, the pattern "123..." could match all PLMN which mcc is 123.
+ // PLMN numeric that may contains wildcard character "D".
+ // A BCD value of 'D' in any of the MCC and/or MNC digits shall be used to indicate
+ // a "wild" value for that corresponding MCC/MNC digit.
+ // For example, the pattern "123DDD" could match all PLMN which mcc is 123.
public final String plmnNumericPattern;
-
public final int lacTacStart;
public final int lacTacEnd;
+ // Identifier of operator name in PNN to be displayed.
+ // 0 indicates that the name is to be taken from other sources, see 3GPP TS 22.101.
+ // pnnRecordId > 0 indicates record # (pnnRecordId - 1) in PNNs.
+ public final int pnnRecordId;
- public final int plmnNetworkNameIndex;
- public OperatorPlmnInfo(String plmnNumericPattern, int lacTacStart, int lacTacEnd,
- int plmnNetworkNameIndex) {
+ public OperatorPlmnInfo(@NonNull String plmnNumericPattern, int lacTacStart, int lacTacEnd,
+ int pnnRecordId) {
this.plmnNumericPattern = plmnNumericPattern;
this.lacTacStart = lacTacStart;
this.lacTacEnd = lacTacEnd;
- this.plmnNetworkNameIndex = plmnNetworkNameIndex;
+ this.pnnRecordId = pnnRecordId;
+ }
+
+ /**
+ * Check whether provided plmn and lacTac matches the stored OperatorPlmnInfo.
+ *
+ * @return -1 if not matching.
+ */
+ public int getPnnIdx(@Nullable String plmn, int lacTac) {
+ if (plmn == null || plmn.length() != plmnNumericPattern.length()) return -1;
+
+ // Check whether PLMN matches with the plmnNumericPattern
+ // Character-by-character check is for performance reasons.
+ for (int i = 0; i < plmn.length(); i++) {
+ if (plmn.charAt(i) != plmnNumericPattern.charAt(i)
+ && plmnNumericPattern.charAt(i) != 'D') {
+ return -1;
+ }
+ }
+ // As defiend in 3GPP TS 31.102 section 4.2.59 , lacTacStart = 0 and lacTacEnd = 0xFFFE
+ // are used to indicate the entire range of LACs/TACs for the given PLMN.
+ if (lacTacStart == 0 && lacTacEnd == 0xFFFE) {
+ return pnnRecordId - 1;
+ }
+ if (lacTac < lacTacStart || lacTac > lacTacEnd) return -1;
+ return pnnRecordId - 1;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(plmnNumericPattern, lacTacStart, lacTacEnd,
+ pnnRecordId);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (!(other instanceof OperatorPlmnInfo)) return false;
+
+ OperatorPlmnInfo opi = (OperatorPlmnInfo) other;
+ return TextUtils.equals(plmnNumericPattern, opi.plmnNumericPattern)
+ && lacTacStart == opi.lacTacStart
+ && lacTacEnd == opi.lacTacEnd
+ && pnnRecordId == opi.pnnRecordId;
}
@Override
public String toString() {
- return "{ plmnNumericPattern = " + plmnNumericPattern
- + "lacTacStart = " + lacTacStart
- + "lacTacEnd = " + lacTacEnd
- + "plmnNetworkNameIndex = " + plmnNetworkNameIndex
- + " }";
+ return "{plmnNumericPattern = " + plmnNumericPattern + ", "
+ + "lacTacStart = " + lacTacStart + ", "
+ + "lacTacEnd = " + lacTacEnd + ", "
+ + "pnnRecordId = " + pnnRecordId
+ + "}";
}
}
@@ -1400,9 +1500,36 @@ public abstract class IccRecords extends Handler implements IccConstants {
this.shortName = shortName;
}
+ /**
+ * Get the name stored in the PlmnNetworkName.
+ * @return the full name if it's available; otherwise, short Name.
+ */
+ @Nullable public String getName() {
+ if (!TextUtils.isEmpty(fullName)) {
+ return fullName;
+ } else {
+ return shortName;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(fullName, shortName);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) return true;
+ if (!(other instanceof PlmnNetworkName)) return false;
+
+ PlmnNetworkName pnn = (PlmnNetworkName) other;
+ return TextUtils.equals(fullName, pnn.fullName)
+ && TextUtils.equals(shortName, pnn.shortName);
+ }
+
@Override
public String toString() {
- return "{ fullName = " + fullName + " shortName = " + shortName + " }";
+ return "{fullName = " + fullName + ", shortName = " + shortName + "}";
}
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java b/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java
index 30fd36f8b0..11d2d82704 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
* See also RIL_SimRefresh in include/telephony/ril.h
@@ -32,14 +33,14 @@ public class IccRefreshResponse {
@UnsupportedAppUsage
public int refreshResult; /* Sim Refresh result */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int efId; /* EFID */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String aid; /* null terminated string, e.g.,
from 0xA0, 0x00 -> 0x41,
0x30, 0x30, 0x30 */
/* Example: a0000000871002f310ffff89080000ff */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IccRefreshResponse() {
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccServiceTable.java b/src/java/com/android/internal/telephony/uicc/IccServiceTable.java
index fe6e433000..ade3b52fea 100644
--- a/src/java/com/android/internal/telephony/uicc/IccServiceTable.java
+++ b/src/java/com/android/internal/telephony/uicc/IccServiceTable.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import com.android.telephony.Rlog;
@@ -24,7 +25,7 @@ import com.android.telephony.Rlog;
* Wrapper class for an ICC EF containing a bit field of enabled services.
*/
public abstract class IccServiceTable {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final byte[] mServiceTable;
protected IccServiceTable(byte[] table) {
@@ -32,7 +33,7 @@ public abstract class IccServiceTable {
}
// Get the class name to use for log strings
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected abstract String getTag();
// Get the array of enums to use for toString
diff --git a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
index 109f72e52a..412295dde8 100644
--- a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
+++ b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
@@ -125,7 +125,7 @@ public class InstallCarrierAppUtils {
context,
0,
ShowInstallAppNotificationReceiver.get(context, pkgName),
- 0);
+ PendingIntent.FLAG_IMMUTABLE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + delayMillis,
pendingIntent);
diff --git a/src/java/com/android/internal/telephony/uicc/IsimRecords.java b/src/java/com/android/internal/telephony/uicc/IsimRecords.java
index 3de7b3d663..51279d98d4 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimRecords.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
* {@hide}
@@ -28,7 +29,7 @@ public interface IsimRecords {
* Returns null if the IMPI hasn't been loaded or isn't present on the ISIM.
* @return the IMS private user identity string, or null if not available
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String getIsimImpi();
/**
@@ -36,7 +37,7 @@ public interface IsimRecords {
* Returns null if the IMS domain hasn't been loaded or isn't present on the ISIM.
* @return the IMS home network domain name, or null if not available
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String getIsimDomain();
/**
@@ -44,7 +45,7 @@ public interface IsimRecords {
* Returns null if the IMPU hasn't been loaded or isn't present on the ISIM.
* @return an array of IMS public user identity strings, or null if not available
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
String[] getIsimImpu();
/**
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index b44eda0582..1fe62232d9 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Message;
import android.telephony.SubscriptionManager;
@@ -45,26 +46,20 @@ public class IsimUiccRecords extends IccRecords implements IsimRecords {
// STOPSHIP if true
public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
- private static final int EVENT_APP_READY = 1;
- private static final int EVENT_ISIM_AUTHENTICATE_DONE = 91;
-
// ISIM EF records (see 3GPP TS 31.103)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mIsimImpi; // IMS private user identity
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mIsimDomain; // IMS home network domain name
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String[] mIsimImpu; // IMS public user identity(s)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mIsimIst; // IMS Service Table
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String[] mIsimPcscf; // IMS Proxy Call Session Control Function
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String auth_rsp;
- @UnsupportedAppUsage
- private final Object mLock = new Object();
-
private static final int TAG_ISIM_VALUE = 0x80; // From 3GPP TS 31.103
@Override
@@ -117,25 +112,6 @@ public class IsimUiccRecords extends IccRecords implements IsimRecords {
super.handleMessage(msg);
break;
- case EVENT_ISIM_AUTHENTICATE_DONE:
- ar = (AsyncResult)msg.obj;
- log("EVENT_ISIM_AUTHENTICATE_DONE");
- if (ar.exception != null) {
- log("Exception ISIM AKA: " + ar.exception);
- } else {
- try {
- auth_rsp = (String)ar.result;
- log("ISIM AKA: auth_rsp = " + auth_rsp);
- } catch (Exception e) {
- log("Failed to parse ISIM AKA contents: " + e);
- }
- }
- synchronized (mLock) {
- mLock.notifyAll();
- }
-
- break;
-
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
@@ -146,7 +122,7 @@ public class IsimUiccRecords extends IccRecords implements IsimRecords {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void fetchIsimRecords() {
mRecordsRequested = true;
@@ -259,7 +235,7 @@ public class IsimUiccRecords extends IccRecords implements IsimRecords {
* @param record the byte array containing the IMS data string
* @return the decoded String value, or null if the record can't be decoded
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static String isimTlvToString(byte[] record) {
SimTlv tlv = new SimTlv(record, 0, record.length);
do {
@@ -432,7 +408,7 @@ public class IsimUiccRecords extends IccRecords implements IsimRecords {
// Not applicable to Isim
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void log(String s) {
if (mParentApp != null) {
diff --git a/src/java/com/android/internal/telephony/uicc/PinStorage.java b/src/java/com/android/internal/telephony/uicc/PinStorage.java
new file mode 100644
index 0000000000..b348c61286
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/PinStorage.java
@@ -0,0 +1,1221 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import static android.security.keystore.KeyProperties.AUTH_DEVICE_CREDENTIAL;
+import static android.security.keystore.KeyProperties.BLOCK_MODE_GCM;
+import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_NONE;
+import static android.security.keystore.KeyProperties.KEY_ALGORITHM_AES;
+import static android.security.keystore.KeyProperties.PURPOSE_DECRYPT;
+import static android.security.keystore.KeyProperties.PURPOSE_ENCRYPT;
+
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__CACHED_PIN_DISCARDED;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_COUNT_NOT_MATCHING_AFTER_REBOOT;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_DECRYPTION_ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_ENCRYPTION_ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_REQUIRED_AFTER_REBOOT;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_STORED_FOR_VERIFICATION;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SKIPPED_SIM_CARD_MISMATCH;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS;
+import static com.android.internal.telephony.uicc.IccCardStatus.PinState.PINSTATE_ENABLED_NOT_VERIFIED;
+import static com.android.internal.telephony.uicc.IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
+
+import android.annotation.Nullable;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.provider.Settings;
+import android.security.keystore.KeyGenParameterSpec;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.SimState;
+import android.util.Base64;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SubscriptionInfoUpdater;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.nano.StoredPinProto.EncryptedPin;
+import com.android.internal.telephony.nano.StoredPinProto.StoredPin;
+import com.android.internal.telephony.nano.StoredPinProto.StoredPin.PinStatus;
+import com.android.internal.telephony.uicc.IccCardStatus.PinState;
+import com.android.internal.util.ArrayUtils;
+import com.android.telephony.Rlog;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.KeyStore;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+/**
+ * This class stores the SIM PIN for automatic verification after an unattended reboot.
+ */
+public class PinStorage extends Handler {
+ private static final String TAG = "PinStorage";
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ /**
+ * Time duration in milliseconds to allow automatic PIN verification after reboot. All unused
+ * PINs are discarded when the timer expires.
+ */
+ private static final int TIMER_VALUE_AFTER_OTA_MILLIS = 20_000;
+
+ /**
+ * Time duration in milliseconds to reboot the device after {@code prepareUnattendedReboot}
+ * is invoked. After the time expires, a new invocation of {@code prepareUnattendedReboot} is
+ * required to perform the automatic PIN verification after reboot.
+ */
+ private static final int TIMER_VALUE_BEFORE_OTA_MILLIS = 20_000;
+
+ /** Minimum valid length of the ICCID. */
+ private static final int MIN_ICCID_LENGTH = 12;
+ /** Minimum length of the SIM PIN, as per 3GPP TS 31.101. */
+ private static final int MIN_PIN_LENGTH = 4;
+ /** Maximum length of the SIM PIN, as per 3GPP TS 31.101. */
+ private static final int MAX_PIN_LENGTH = 8;
+
+ // Variables related to the encryption of the SIM PIN.
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+ private static final String CIPHER_TRANSFORMATION = "AES/GCM/NoPadding";
+ private static final int GCM_PARAMETER_TAG_BIT_LEN = 128;
+ private static final int SHORT_TERM_KEY_DURATION_MINUTES = 15;
+
+ /** Alias of the long-term key that does not require user authentication. */
+ private static final String KEYSTORE_ALIAS_LONG_TERM_ALWAYS = "PinStorage_longTerm_always_key";
+ /** Alias of the user authentication blound long-term key. */
+ private static final String KEYSTORE_ALIAS_LONG_TERM_USER_AUTH = "PinStorage_longTerm_ua_key";
+ /** Alias of the short-term key (30 minutes) used before and after an unattended reboot. */
+ private static final String KEYSTORE_ALIAS_SHORT_TERM = "PinStorage_shortTerm_key";
+
+ // Constants related to the storage of the encrypted SIM PIN to non-volatile memory.
+ // Data is stored in two separate files:
+ // - "available" is for the PIN(s) in AVAILABLE state and uses a key that does not expire
+ // - "reboot" is for the PIN(s) in other states and uses a short-term key (30 minutes)
+ private static final String SHARED_PREFS_NAME = "pinstorage_prefs";
+ private static final String SHARED_PREFS_AVAILABLE_PIN_BASE_KEY = "encrypted_pin_available_";
+ private static final String SHARED_PREFS_REBOOT_PIN_BASE_KEY = "encrypted_pin_reboot_";
+ private static final String SHARED_PREFS_STORED_PINS = "stored_pins";
+
+ // Events
+ private static final int ICC_CHANGED_EVENT = 1;
+ private static final int CARRIER_CONFIG_CHANGED_EVENT = 2;
+ private static final int TIMER_EXPIRATION_EVENT = 3;
+ private static final int USER_UNLOCKED_EVENT = 4;
+ private static final int SUPPLY_PIN_COMPLETE = 5;
+
+ private final Context mContext;
+ private final int mBootCount;
+ private final KeyStore mKeyStore;
+
+ private SecretKey mLongTermSecretKey;
+ private SecretKey mShortTermSecretKey;
+
+ private boolean mIsDeviceSecure;
+ private boolean mIsDeviceLocked;
+ private boolean mLastCommitResult = true;
+
+ /** Duration of the short-term key, in minutes. */
+ @VisibleForTesting
+ public int mShortTermSecretKeyDurationMinutes;
+
+ /** RAM storage is used on secure devices before the device is unlocked. */
+ private final SparseArray<byte[]> mRamStorage;
+
+ /** Receiver for the required intents. */
+ private final BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
+ int slotId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, -1);
+ sendMessage(obtainMessage(CARRIER_CONFIG_CHANGED_EVENT, slotId, 0));
+ } else if (TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals(action)
+ || TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals(action)) {
+ int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY, -1);
+ int state = intent.getIntExtra(
+ TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_UNKNOWN);
+ if (validateSlotId(slotId)) {
+ sendMessage(obtainMessage(ICC_CHANGED_EVENT, slotId, state));
+ }
+ } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+ sendMessage(obtainMessage(USER_UNLOCKED_EVENT));
+ }
+ }
+ };
+
+ public PinStorage(Context context) {
+ mContext = context;
+ mBootCount = getBootCount();
+ mKeyStore = initializeKeyStore();
+ mShortTermSecretKeyDurationMinutes = SHORT_TERM_KEY_DURATION_MINUTES;
+
+ mIsDeviceSecure = isDeviceSecure();
+ mIsDeviceLocked = mIsDeviceSecure ? isDeviceLocked() : false;
+
+ // Register for necessary intents.
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intentFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+ intentFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+ mContext.registerReceiver(mCarrierConfigChangedReceiver, intentFilter);
+
+ // Initialize the long term secret key. This needs to be present in all cases:
+ // - if the device is not secure or is locked: key does not require user authentication
+ // - if the device is secure and unlocked: key requires user authentication.
+ // The short term key is retrieved later when needed.
+ String alias = (!mIsDeviceSecure || mIsDeviceLocked)
+ ? KEYSTORE_ALIAS_LONG_TERM_ALWAYS : KEYSTORE_ALIAS_LONG_TERM_USER_AUTH;
+ mLongTermSecretKey = initializeSecretKey(alias, /*createIfAbsent=*/ true);
+
+ // If the device is not securee or is unlocked, we can start logic. Otherwise we need to
+ // wait for the device to be unlocked and store any temporary PIN in RAM.
+ if (!mIsDeviceSecure || !mIsDeviceLocked) {
+ mRamStorage = null;
+ onDeviceReady();
+ } else {
+ logd("Device is locked - Postponing initialization");
+ mRamStorage = new SparseArray<>();
+ }
+ }
+
+ /** Store the {@code pin} for the {@code slotId}. */
+ public synchronized void storePin(String pin, int slotId) {
+ String iccid = getIccid(slotId);
+
+ if (!validatePin(pin) || !validateIccid(iccid) || !validateSlotId(slotId)) {
+ // We are unable to store the PIN. At least clear the old one, if present.
+ loge("storePin[%d] - Invalid PIN, slotId or ICCID", slotId);
+ clearPin(slotId);
+ return;
+ }
+ if (!isCacheAllowed(slotId)) {
+ logd("storePin[%d]: caching it not allowed", slotId);
+ return;
+ }
+
+ logd("storePin[%d]", slotId);
+
+ StoredPin storedPin = new StoredPin();
+ storedPin.iccid = iccid;
+ storedPin.pin = pin;
+ storedPin.slotId = slotId;
+ storedPin.status = PinStatus.AVAILABLE;
+
+ savePinInformation(slotId, storedPin);
+ }
+
+ /** Clear the cached pin for the {@code slotId}. */
+ public synchronized void clearPin(int slotId) {
+ logd("clearPin[%d]", slotId);
+
+ if (!validateSlotId(slotId)) {
+ return;
+ }
+ savePinInformation(slotId, null);
+ }
+
+ /**
+ * Return the cached pin for the {@code slotId}, or an empty string if it is not available.
+ *
+ * The method returns the PIN only if the state is VERIFICATION_READY. If the PIN is found,
+ * its state changes to AVAILABLE, so that it cannot be retrieved a second time during the
+ * same boot cycle. If the PIN verification fails, it will be removed after the failed attempt.
+ */
+ public synchronized String getPin(int slotId) {
+ String iccid = getIccid(slotId);
+ if (!validateSlotId(slotId) || !validateIccid(iccid)) {
+ return "";
+ }
+
+ StoredPin storedPin = loadPinInformation(slotId);
+ if (storedPin != null) {
+ if (!storedPin.iccid.equals(iccid)) {
+ // The ICCID does not match: it's possible that the SIM card was changed.
+ // Delete the cached PIN.
+ savePinInformation(slotId, null);
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SKIPPED_SIM_CARD_MISMATCH,
+ /* number_of_pins= */ 1);
+ } else if (storedPin.status == PinStatus.VERIFICATION_READY) {
+ logd("getPin[%d] - Found PIN ready for verification", slotId);
+ // Move the state to AVAILABLE, so that it cannot be retrieved again.
+ storedPin.status = PinStatus.AVAILABLE;
+ savePinInformation(slotId, storedPin);
+ return storedPin.pin;
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Prepare for an unattended reboot.
+ *
+ * All PINs in AVAILABLE and VERIFICATION_READY state are moved to REBOOT_READY state. A
+ * timer is started to make sure that reboot occurs shortly after invoking this method.
+ *
+ * @return The result of the reboot preparation.
+ */
+ @TelephonyManager.PrepareUnattendedRebootResult
+ public synchronized int prepareUnattendedReboot() {
+ // Unattended reboot should never occur before the device is unlocked.
+ if (mIsDeviceLocked) {
+ loge("prepareUnattendedReboot - Device is locked");
+ return TelephonyManager.PREPARE_UNATTENDED_REBOOT_ERROR;
+ }
+
+ // Start timer to make sure that device is rebooted shortly after this is executed.
+ if (!startTimer(TIMER_VALUE_BEFORE_OTA_MILLIS)) {
+ return TelephonyManager.PREPARE_UNATTENDED_REBOOT_ERROR;
+ }
+
+ int numSlots = getSlotCount();
+ SparseArray<StoredPin> storedPins = loadPinInformation();
+
+ // Delete any previous short-term key, if present: a new one is created (if needed).
+ deleteSecretKey(KEYSTORE_ALIAS_SHORT_TERM);
+ mShortTermSecretKey = null;
+
+ // If any PIN is present, generate a new short-term key to save PIN(s) to
+ // non-volatile memory.
+ if (storedPins.size() > 0) {
+ mShortTermSecretKey =
+ initializeSecretKey(KEYSTORE_ALIAS_SHORT_TERM, /*createIfAbsent=*/ true);
+ }
+
+ @TelephonyManager.PrepareUnattendedRebootResult
+ int result = TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS;
+ int storedCount = 0;
+ int notAvailableCount = 0;
+
+ for (int slotId = 0; slotId < numSlots; slotId++) {
+ StoredPin storedPin = storedPins.get(slotId);
+ if (storedPin != null) {
+ storedPin.status = PinStatus.REBOOT_READY;
+ if (!savePinInformation(slotId, storedPin)) {
+ result = TelephonyManager.PREPARE_UNATTENDED_REBOOT_ERROR;
+ break;
+ }
+ storedCount++;
+ } else if (isPinState(slotId, PINSTATE_ENABLED_VERIFIED)) {
+ // If PIN is not available, check if PIN will be required after reboot (current PIN
+ // status is enabled and verified).
+ loge("Slot %d requires PIN and is not cached", slotId);
+ result = TelephonyManager.PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED;
+ notAvailableCount++;
+ }
+ }
+
+ // Generate metrics
+ if (result == TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS) {
+ logd("prepareUnattendedReboot - Stored %d PINs", storedCount);
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__PIN_STORED_FOR_VERIFICATION, storedCount);
+ } else if (result == TelephonyManager.PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED) {
+ logd("prepareUnattendedReboot - Required %d PINs after reboot", notAvailableCount);
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__PIN_REQUIRED_AFTER_REBOOT, notAvailableCount);
+ }
+
+ // Save number of PINs to generate metrics after reboot
+ saveNumberOfCachedPins(storedCount);
+
+ return result;
+ }
+
+ /**
+ * Execute logic when a secure device is unlocked.
+ *
+ * The temporary long-term key that does not require user verification is replaced by the long
+ * term key that requires user verification. The cached PIN temporarily stored in RAM are
+ * merged with those on disk from the previous boot.
+ */
+ private synchronized void onUserUnlocked() {
+ if (!mIsDeviceLocked) {
+ // This should never happen.
+ // Nothing to do because the device was already unlocked before
+ return;
+ }
+
+ logd("onUserUnlocked - Device is unlocked");
+
+ // It's possible that SIM PIN was already verified and stored temporarily in RAM. Load the
+ // data and erase the memory.
+ SparseArray<StoredPin> storedPinInRam = loadPinInformation();
+ cleanRamStorage();
+
+ // Mark the device as unlocked
+ mIsDeviceLocked = false;
+
+ // Replace the temporary long-term key without user authentication with a new long-term
+ // key that requires user authentication to save all PINs previously in RAM (all in
+ // AVAILABLE state) to disk.
+ mLongTermSecretKey =
+ initializeSecretKey(KEYSTORE_ALIAS_LONG_TERM_USER_AUTH, /*createIfAbsent=*/ true);
+
+ // Save the PINs previously in RAM to disk, overwriting any PIN that might already exists.
+ for (int i = 0; i < storedPinInRam.size(); i++) {
+ savePinInformation(storedPinInRam.keyAt(i), storedPinInRam.valueAt(i));
+ }
+
+ // At this point the module is fully initialized. Execute the start logic.
+ onDeviceReady();
+
+ // Verify any pending PIN for SIM cards that need it.
+ verifyPendingPins();
+ }
+
+ /**
+ * Executes logic when module is fully ready. This occurs immediately if the device is not
+ * secure or after the user unlocks the device.
+ *
+ * At this point, the short-term key is initialized (if present), the configuration is read
+ * and the status of each PIN is updated as needed.
+ */
+ private void onDeviceReady() {
+ logd("onDeviceReady");
+
+ // Try to initialize the short term key, if present, as this would be required to read
+ // stored PIN for verification.
+ mShortTermSecretKey =
+ initializeSecretKey(KEYSTORE_ALIAS_SHORT_TERM, /*createIfAbsent=*/ false);
+
+ int verificationReadyCount = 0;
+ int slotCount = getSlotCount();
+ for (int slotId = 0; slotId < slotCount; slotId++) {
+ // Read PIN information from storage
+ StoredPin storedPin = loadPinInformation(slotId);
+ if (storedPin == null) {
+ continue;
+ }
+
+ // For each PIN in AVAILABLE state, check the boot count.
+ // If the boot count matches, it means that module crashed and it's ok to preserve
+ // the PIN code. If the boot count does not match, then delete those PINs.
+ if (storedPin.status == PinStatus.AVAILABLE) {
+ if (storedPin.bootCount != mBootCount) {
+ logd("Boot count [%d] does not match - remove PIN", slotId);
+ savePinInformation(slotId, null);
+ continue;
+ }
+ logd("Boot count [%d] matches - keep stored PIN", slotId);
+ }
+
+ // If there is any PIN in REBOOT_READY state, move it to VERIFICATION_READY and start
+ // the timer. Don't change PINs that might be already in VERIFICATION_READY state
+ // (e.g. due to crash).
+ if (storedPin.status == PinStatus.REBOOT_READY) {
+ storedPin.status = PinStatus.VERIFICATION_READY;
+ savePinInformation(slotId, storedPin);
+ verificationReadyCount++;
+ }
+ }
+ if (verificationReadyCount > 0) {
+ startTimer(TIMER_VALUE_AFTER_OTA_MILLIS);
+ }
+
+ // Generate metrics for PINs that had been stored before reboot, but are not available
+ // after. This can happen if there is an excessive delay in unlocking the device (short
+ // term key expires), but also if a new SIM card without PIN is present.
+ int prevCachedPinCount = saveNumberOfCachedPins(0);
+ if (prevCachedPinCount > verificationReadyCount) {
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__PIN_COUNT_NOT_MATCHING_AFTER_REBOOT,
+ prevCachedPinCount - verificationReadyCount);
+ }
+ }
+
+ /**
+ * Executes logic at the expiration of the timer. This method is common for two cases:
+ * - timer started after unattended reeboot to verify the SIM PIN automatically
+ * - timer started after prepareUnattendedReboot() is invoked.
+ */
+ private synchronized void onTimerExpiration() {
+ logd("onTimerExpiration");
+
+ int discardedPin = 0;
+ int slotCount = getSlotCount();
+ for (int slotId = 0; slotId < slotCount; slotId++) {
+ // Read PIN information from storage
+ StoredPin storedPin = loadPinInformation(slotId);
+ if (storedPin == null) {
+ continue;
+ }
+
+ // Delete all PINs in VERIFICATION_READY state. This happens when reboot occurred after
+ // OTA, but the SIM card is not detected on the device.
+ if (storedPin.status == PinStatus.VERIFICATION_READY) {
+ logd("onTimerExpiration - Discarding PIN in slot %d", slotId);
+ savePinInformation(slotId, null);
+ discardedPin++;
+ continue;
+ }
+
+ // Move all PINs in REBOOT_READY to AVAILABLE. This happens when
+ // prepareUnattendedReboot() is invoked, but the reboot does not occur.
+ if (storedPin.status == PinStatus.REBOOT_READY) {
+ logd("onTimerExpiration - Moving PIN in slot %d back to AVAILABLE", slotId);
+ storedPin.status = PinStatus.AVAILABLE;
+ savePinInformation(slotId, storedPin);
+ continue;
+ }
+ }
+
+ // Delete short term key no matter the reason of the timer expiration.
+ // This is done after loading the PIN information, so that it's possible to change
+ // the status of the PIN as needed.
+ deleteSecretKey(KEYSTORE_ALIAS_SHORT_TERM);
+ mShortTermSecretKey = null;
+
+ // Reset number of stored PINs (applicable if timer expired before unattended reboot).
+ saveNumberOfCachedPins(0);
+
+ // Write metrics about number of discarded PINs
+ if (discardedPin > 0) {
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__CACHED_PIN_DISCARDED, discardedPin);
+ }
+ }
+
+ /** Handle the update of the {@code state} of the SIM card in {@code slotId}. */
+ private synchronized void onSimStatusChange(int slotId, @SimState int state) {
+ logd("SIM card/application changed[%d]: %s",
+ slotId, SubscriptionInfoUpdater.simStateString(state));
+ switch (state) {
+ case TelephonyManager.SIM_STATE_ABSENT:
+ case TelephonyManager.SIM_STATE_PIN_REQUIRED: {
+ // These states are likely to occur after a reboot, so we don't clear cached PINs
+ // in VERIFICATION_READY state, as they might be verified later, when the SIM is
+ // detected. On the other hand, we remove PINs in AVAILABLE state.
+ StoredPin storedPin = loadPinInformation(slotId);
+ if (storedPin != null && storedPin.status != PinStatus.VERIFICATION_READY) {
+ savePinInformation(slotId, null);
+ }
+ break;
+ }
+ case TelephonyManager.SIM_STATE_PUK_REQUIRED:
+ case TelephonyManager.SIM_STATE_PERM_DISABLED:
+ case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
+ // These states indicate that the SIM card will need a manual PIN verification.
+ // Delete the cached PIN regardless of its state.
+ clearPin(slotId);
+ break;
+ case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
+ case TelephonyManager.SIM_STATE_CARD_RESTRICTED:
+ case TelephonyManager.SIM_STATE_LOADED:
+ case TelephonyManager.SIM_STATE_READY: {
+ // These states can occur after successful PIN caching, so we don't clear cached
+ // PINs in AVAILABLE state, as they need to be retained. We clear any PIN in
+ // other states, as they are no longer needed for automatic verification.
+ StoredPin storedPin = loadPinInformation(slotId);
+ if (storedPin != null && storedPin.status != PinStatus.AVAILABLE) {
+ savePinInformation(slotId, null);
+ }
+ break;
+ }
+
+ case TelephonyManager.SIM_STATE_NOT_READY:
+ case TelephonyManager.SIM_STATE_PRESENT:
+ default:
+ break;
+ }
+ }
+
+ private void onCarrierConfigChanged(int slotId) {
+ logv("onCarrierConfigChanged[%d]", slotId);
+ if (!isCacheAllowed(slotId)) {
+ logd("onCarrierConfigChanged[%d] - PIN caching not allowed", slotId);
+ clearPin(slotId);
+ }
+ }
+
+ private void onSupplyPinComplete(int slotId, boolean success) {
+ logd("onSupplyPinComplete[%d] - success: %s", slotId, success);
+ if (!success) {
+ // In case of failure to verify the PIN, delete the stored value.
+ // Otherwise nothing to do.
+ clearPin(slotId);
+ }
+ // Update metrics:
+ TelephonyStatsLog.write(
+ PIN_STORAGE_EVENT,
+ success
+ ? PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS
+ : PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE,
+ /* number_of_pins= */ 1);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ICC_CHANGED_EVENT:
+ onSimStatusChange(/* slotId= */ msg.arg1, /* state= */ msg.arg2);
+ break;
+ case CARRIER_CONFIG_CHANGED_EVENT:
+ onCarrierConfigChanged(/* slotId= */ msg.arg1);
+ break;
+ case TIMER_EXPIRATION_EVENT:
+ onTimerExpiration();
+ break;
+ case USER_UNLOCKED_EVENT:
+ onUserUnlocked();
+ break;
+ case SUPPLY_PIN_COMPLETE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ boolean success = ar != null && ar.exception == null;
+ onSupplyPinComplete(/* slotId= */ msg.arg2, success);
+ break;
+ default:
+ // Nothing to do
+ break;
+ }
+ }
+
+ /** Return if the device is secure (device PIN is enabled). */
+ private boolean isDeviceSecure() {
+ KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
+ return keyguardManager != null ? keyguardManager.isDeviceSecure() : false;
+ }
+
+ /** Return if the device is locked (device PIN is enabled and not verified). */
+ private boolean isDeviceLocked() {
+ KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
+ return keyguardManager != null
+ ? keyguardManager.isDeviceSecure() && keyguardManager.isDeviceLocked()
+ : false;
+ }
+
+ /** Loads the stored PIN informations for all SIM slots. */
+ private SparseArray<StoredPin> loadPinInformation() {
+ SparseArray<StoredPin> result = new SparseArray<>();
+ int slotCount = getSlotCount();
+ for (int slotId = 0; slotId < slotCount; slotId++) {
+ StoredPin storedPin = loadPinInformation(slotId);
+ if (storedPin != null) {
+ result.put(slotId, storedPin);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Loads the stored PIN information for the {@code slotId}.
+ *
+ * The RAM storage is used if the device is locked, the disk storage is used otherwise.
+ * This method tries to use both the long-term key and the short-term key (if available)
+ * to retrieve the PIN information, regardless of its status.
+ *
+ * @return the stored {@code StoredPin}, or null if not present.
+ */
+ @Nullable
+ private StoredPin loadPinInformation(int slotId) {
+ if (!mLastCommitResult) {
+ // If the last commit failed, do not read from file, as we might retrieve stale data.
+ loge("Last commit failed - returning empty values");
+ return null;
+ }
+
+ StoredPin result = null;
+
+ if (mIsDeviceLocked) {
+ // If the device is still locked, retrieve data from RAM storage.
+ if (mRamStorage != null && mRamStorage.get(slotId) != null) {
+ result = decryptStoredPin(mRamStorage.get(slotId), mLongTermSecretKey);
+ }
+ } else {
+ // Load both the stored PIN in available state (with long-term key) and in other states
+ // (with short-term key). At most one of them should be present at any given time and
+ // we treat the case wheere both are present as an error.
+ StoredPin availableStoredPin = loadPinInformationFromDisk(
+ slotId, SHARED_PREFS_AVAILABLE_PIN_BASE_KEY, mLongTermSecretKey);
+ StoredPin rebootStoredPin = loadPinInformationFromDisk(
+ slotId, SHARED_PREFS_REBOOT_PIN_BASE_KEY, mShortTermSecretKey);
+ if (availableStoredPin != null && rebootStoredPin == null) {
+ result = availableStoredPin;
+ } else if (availableStoredPin == null && rebootStoredPin != null) {
+ result = rebootStoredPin;
+ }
+ }
+
+ // Validate the slot ID of the retrieved PIN information
+ if (result != null && result.slotId != slotId) {
+ loge("Load PIN: slot ID does not match (%d != %d)", result.slotId, slotId);
+ result = null;
+ }
+
+ if (result != null) {
+ logv("Load PIN: %s", result.toString());
+ } else {
+ logv("Load PIN for slot %d: null", slotId);
+ }
+ return result;
+ }
+
+ /**
+ * Load the PIN information from a specific file in non-volatile memory.
+ *
+ * @param key the key in the {@code SharedPreferences} to read
+ * @param secretKey the key used for encryption/decryption
+ * @return the {@code StoredPin} from non-volatile memory. It returns a default instance in
+ * case of error.
+ */
+ @Nullable
+ private StoredPin loadPinInformationFromDisk(
+ int slotId, String key, @Nullable SecretKey secretKey) {
+ String base64encryptedPin =
+ mContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
+ .getString(key + slotId, "");
+ if (!base64encryptedPin.isEmpty()) {
+ try {
+ byte[] blob = Base64.decode(base64encryptedPin, Base64.DEFAULT);
+ return decryptStoredPin(blob, secretKey);
+ } catch (Exception e) {
+ // Nothing to do
+ }
+ }
+ return null;
+ }
+
+ /** Load the PIN information from an encrypted binary blob.
+ *
+ * @param blob the encrypted binary blob
+ * @param secretKey the key used for encryption/decryption
+ * @return the decrypted {@code StoredPin}, or null in case of error.
+ */
+ @Nullable
+ private StoredPin decryptStoredPin(byte[] blob, @Nullable SecretKey secretKey) {
+ if (secretKey != null) {
+ try {
+ byte[] decryptedPin = decrypt(secretKey, blob);
+ if (decryptedPin.length > 0) {
+ return StoredPin.parseFrom(decryptedPin);
+ }
+ } catch (Exception e) {
+ loge("cannot decrypt/parse PIN information", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Stores the PIN information.
+ *
+ * If the device is locked, the PIN information is stored to RAM, othewrwise to disk.
+ * The PIN information is divided based on the PIN status and stored in two separate
+ * files in non-volatile memory, each encrypted with a different key.
+ *
+ * @param slotId the slot ID
+ * @param storedPin the PIN information to be stored
+ * @return true if the operation was successfully done, false otherwise.
+ */
+ private boolean savePinInformation(int slotId, @Nullable StoredPin storedPin) {
+ // Populate the boot count
+ if (storedPin != null) {
+ storedPin.bootCount = mBootCount;
+ }
+
+ // If the device is still locked, we can only save PINs in AVAILABLE state in RAM.
+ // NOTE: at this point, there should not be any PIN in any other state.
+ if (mIsDeviceLocked) {
+ return savePinInformationToRam(slotId, storedPin);
+ }
+
+ // Remove any prvious key related to this slot.
+ SharedPreferences.Editor editor =
+ mContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .remove(SHARED_PREFS_AVAILABLE_PIN_BASE_KEY + slotId)
+ .remove(SHARED_PREFS_REBOOT_PIN_BASE_KEY + slotId);
+
+ boolean result = true;
+ if (storedPin != null) {
+ // Available PINs are stored with a long-term key, while the PINs in other states
+ // are stored with a short-term key.
+ logd("Saving PIN for slot %d", slotId);
+ if (storedPin.status == PinStatus.AVAILABLE) {
+ result = savePinInformation(editor, slotId, storedPin,
+ SHARED_PREFS_AVAILABLE_PIN_BASE_KEY, mLongTermSecretKey);
+ } else {
+ result = savePinInformation(editor, slotId, storedPin,
+ SHARED_PREFS_REBOOT_PIN_BASE_KEY, mShortTermSecretKey);
+ }
+ } else {
+ logv("Deleting PIN for slot %d (if existed)", slotId);
+ }
+
+ mLastCommitResult = editor.commit() && result;
+ return mLastCommitResult;
+ }
+
+ /**
+ * Store the PIN information to a specific file in non-volatile memory.
+ *
+ * @param editor the {@code SharedPreferences.Editor} to use for storage
+ * @param slotId the slot ID
+ * @param storedPin the PIN information to store
+ * @param baseKey the base name of the key in the {@code SharedPreferences}. The full name is
+ * derived appending the value of {@code slotId}.
+ * @param secretKey the key used for encryption/decryption
+ * @return true if the operation was successful, false otherwise
+ */
+ private boolean savePinInformation(SharedPreferences.Editor editor, int slotId,
+ StoredPin storedPin, String baseKey, SecretKey secretKey) {
+ if (secretKey == null) {
+ // Secret key for encryption is missing
+ return false;
+ }
+ if (slotId != storedPin.slotId) {
+ loge("Save PIN: the slotId does not match (%d != %d)", slotId, storedPin.slotId);
+ return false;
+ }
+
+ logv("Save PIN: %s", storedPin.toString());
+
+ byte[] encryptedPin = encrypt(secretKey, StoredPin.toByteArray(storedPin));
+ if (encryptedPin.length > 0) {
+ editor.putString(
+ baseKey + slotId, Base64.encodeToString(encryptedPin, Base64.DEFAULT));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /** Stored PIN information for slot {@code slotId} in RAM. */
+ private boolean savePinInformationToRam(int slotId, @Nullable StoredPin storedPin) {
+ // Clear the RAM in all cases, to avoid leaking any previous PIN.
+ cleanRamStorage(slotId);
+
+ if (storedPin == null) {
+ return true;
+ }
+
+ if (storedPin.status == PinStatus.AVAILABLE) {
+ byte[] encryptedPin = encrypt(mLongTermSecretKey, StoredPin.toByteArray(storedPin));
+ if (encryptedPin != null && encryptedPin.length > 0) {
+ logd("Saving PIN for slot %d in RAM", slotId);
+ mRamStorage.put(slotId, encryptedPin);
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /** Erases all the PINs stored in RAM before a secure device is unlocked. */
+ private void cleanRamStorage() {
+ int slotCount = getSlotCount();
+ for (int slotId = 0; slotId < slotCount; slotId++) {
+ cleanRamStorage(slotId);
+ }
+ }
+
+ /** Erases the PIN of slot {@code slotId} stored in RAM before a secure device is unlocked. */
+ private void cleanRamStorage(int slotId) {
+ if (mRamStorage != null) {
+ byte[] data = mRamStorage.get(slotId);
+ if (data != null) {
+ Arrays.fill(data, (byte) 0);
+ }
+ mRamStorage.delete(slotId);
+ }
+ }
+
+ /**
+ * Verifies all pending PIN codes that are ready for verification.
+ *
+ * The PIN verificartion is done if the PIN state is VERIFICATION_READY and the SIM
+ * card has the PIN enabled and not verified.
+ */
+ private void verifyPendingPins() {
+ int slotCount = getSlotCount();
+ for (int slotId = 0; slotId < slotCount; slotId++) {
+ if (isPinState(slotId, PINSTATE_ENABLED_NOT_VERIFIED)) {
+ verifyPendingPin(slotId);
+ }
+ }
+ }
+
+ /** Verifies the PIN code for a given SIM card in slot {@code slotId}. */
+ private void verifyPendingPin(int slotId) {
+ // We intentionally invoke getPin() here, as it updates the status and makes sure that
+ // same PIN is not used more than once
+ String pin = getPin(slotId);
+ if (pin.isEmpty()) {
+ // PIN is not available for verification: return.
+ return;
+ }
+
+ logd("Perform automatic verification of PIN in slot %d", slotId);
+
+ UiccProfile profile = UiccController.getInstance().getUiccProfileForPhone(slotId);
+ if (profile != null) {
+ Message onComplete = obtainMessage(SUPPLY_PIN_COMPLETE);
+ onComplete.arg2 = slotId; // arg1 is the number of remaining attempts in the response
+ profile.supplyPin(pin, onComplete);
+ } else {
+ logd("Perform automatic verification of PIN in slot %d not possible", slotId);
+ }
+ }
+
+ /** Returns the boot count. */
+ private int getBootCount() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.BOOT_COUNT,
+ -1);
+ }
+
+ /** Returns the number of available SIM slots. */
+ private int getSlotCount() {
+ // Count the number of slots as the number of Phones.
+ // At power up, it is possible that number of phones is still unknown, so we query
+ // TelephonyManager for it.
+ try {
+ return PhoneFactory.getPhones().length;
+ } catch (Exception ex) {
+ return TelephonyManager.getDefault().getActiveModemCount();
+ }
+ }
+
+ /**
+ * Saves the number of cached PINs ready for verification after reboot and returns the
+ * previous value.
+ */
+ private int saveNumberOfCachedPins(int storedCount) {
+ SharedPreferences sharedPrefs =
+ mContext.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
+
+ int previousValue = sharedPrefs.getInt(SHARED_PREFS_STORED_PINS, 0);
+ sharedPrefs.edit().putInt(SHARED_PREFS_STORED_PINS, storedCount).commit();
+ return previousValue;
+ }
+
+ private boolean startTimer(int duration) {
+ removeMessages(TIMER_EXPIRATION_EVENT);
+ return duration > 0 ? sendEmptyMessageDelayed(TIMER_EXPIRATION_EVENT, duration) : true;
+ }
+
+ /** Returns the ICCID of the SIM card for the given {@code slotId}. */
+ private String getIccid(int slotId) {
+ Phone phone = PhoneFactory.getPhone(slotId);
+ return phone != null ? phone.getFullIccSerialNumber() : "";
+ }
+
+ private boolean validatePin(String pin) {
+ return pin != null && pin.length() >= MIN_PIN_LENGTH && pin.length() <= MAX_PIN_LENGTH;
+ }
+
+ private boolean validateIccid(String iccid) {
+ return iccid != null && iccid.length() >= MIN_ICCID_LENGTH;
+ }
+
+ private boolean validateSlotId(int slotId) {
+ return slotId >= 0 && slotId < getSlotCount();
+ }
+
+ /** Checks if the PIN status of the SIM in slot {@code slotId} is a given {@code PinState}. */
+ private boolean isPinState(int slotId, PinState pinState) {
+ UiccProfile profile = UiccController.getInstance().getUiccProfileForPhone(slotId);
+ if (profile != null) {
+ // Loop thru all possible app families to identify at least one that is available in
+ // order to check the PIN state.
+ int[] families = {
+ UiccController.APP_FAM_3GPP,
+ UiccController.APP_FAM_3GPP2,
+ UiccController.APP_FAM_IMS };
+ for (int i = 0; i < families.length; i++) {
+ UiccCardApplication app = profile.getApplication(i);
+ if (app != null) {
+ return app.getPin1State() == pinState;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Returns if the PIN cache is allowed for a given slot. */
+ private boolean isCacheAllowed(int slotId) {
+ return isCacheAllowedByDevice() && isCacheAllowedByCarrier(slotId);
+ }
+
+ /** Returns if the PIN cache is allowed by the device. */
+ private boolean isCacheAllowedByDevice() {
+ if (!mContext.getResources().getBoolean(
+ R.bool.config_allow_pin_storage_for_unattended_reboot)) {
+ logv("Pin caching disabled in resources");
+ return false;
+ }
+ return true;
+ }
+
+ /** Returns if the PIN cache is allowed by carrier for a given slot. */
+ private boolean isCacheAllowedByCarrier(int slotId) {
+ PersistableBundle config = null;
+ CarrierConfigManager configManager =
+ mContext.getSystemService(CarrierConfigManager.class);
+ if (configManager != null) {
+ Phone phone = PhoneFactory.getPhone(slotId);
+ if (phone != null) {
+ // If an invalid subId is used, this bundle will contain default values.
+ config = configManager.getConfigForSubId(phone.getSubId());
+ }
+ }
+ if (config == null) {
+ config = CarrierConfigManager.getDefaultConfig();
+ }
+
+ return config.getBoolean(
+ CarrierConfigManager.KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true);
+ }
+
+ /** Initializes KeyStore and returns the instance. */
+ @Nullable
+ private static KeyStore initializeKeyStore() {
+ KeyStore keyStore = null;
+ try {
+ keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ keyStore.load(/*param=*/ null);
+ } catch (Exception e) {
+ // Should never happen.
+ loge("Error loading KeyStore", e);
+ return null;
+ }
+ logv("KeyStore ready");
+ return keyStore;
+ }
+
+ /**
+ * Initializes a secret key and returns it.
+ *
+ * @param alias alias of the key in {@link KeyStore}.
+ * @param createIfAbsent indicates weather the key must be created if not already present.
+ * @return the {@link SecretKey}, or null if the key does not exist.
+ */
+ @Nullable
+ private SecretKey initializeSecretKey(String alias, boolean createIfAbsent) {
+ if (mKeyStore == null) {
+ return null;
+ }
+
+ SecretKey secretKey = getSecretKey(alias);
+ if (secretKey != null) {
+ logd("KeyStore: alias %s exists", alias);
+ return secretKey;
+ } else if (createIfAbsent) {
+ Date expiration =
+ KEYSTORE_ALIAS_SHORT_TERM.equals(alias) ? getShortLivedKeyValidityEnd() : null;
+ boolean isUserAuthRequired =
+ !KEYSTORE_ALIAS_LONG_TERM_ALWAYS.equals(alias) && isDeviceSecure();
+ logd("KeyStore: alias %s does not exist - Creating (exp=%s, auth=%s)",
+ alias, expiration != null ? expiration.toString() : "", isUserAuthRequired);
+ return createSecretKey(alias, expiration, isUserAuthRequired);
+ } else {
+ // Nothing to do
+ logd("KeyStore: alias %s does not exist - Nothing to do", alias);
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the secret key previously stored in {@link KeyStore}.
+ *
+ * @param alias alias of the key in {@link KeyStore}.
+ * @return the {@link SecretKey}, or null in case of error or if the key does not exist.
+ */
+ @Nullable
+ private SecretKey getSecretKey(String alias) {
+ try {
+ final KeyStore.SecretKeyEntry secretKeyEntry =
+ (KeyStore.SecretKeyEntry) mKeyStore.getEntry(alias, null);
+ if (secretKeyEntry != null) {
+ return secretKeyEntry.getSecretKey();
+ }
+ } catch (Exception e) {
+ // In case of exception, it means that key exists, but cannot be retrieved
+ // We delete the old key, so that a new key can be created.
+ loge("Exception with getting the key " + alias, e);
+ deleteSecretKey(alias);
+ }
+ return null;
+ }
+
+ /**
+ * Generates a new secret key in {@link KeyStore}.
+ *
+ * @param alias alias of the key in {@link KeyStore}.
+ * @param expiration expiration of the key, or null if the key does not expire.
+ * @param isUserAuthRequired indicates if user authentication is required to use the key
+ * @return the created {@link SecretKey}, or null in case of error
+ */
+ @Nullable
+ private SecretKey createSecretKey(String alias, Date expiration, boolean isUserAuthRequired) {
+ try {
+ final KeyGenerator keyGenerator =
+ KeyGenerator.getInstance(KEY_ALGORITHM_AES, ANDROID_KEY_STORE_PROVIDER);
+ KeyGenParameterSpec.Builder keyGenParameterSpec =
+ new KeyGenParameterSpec.Builder(alias, PURPOSE_ENCRYPT | PURPOSE_DECRYPT)
+ .setBlockModes(BLOCK_MODE_GCM)
+ .setEncryptionPaddings(ENCRYPTION_PADDING_NONE);
+ if (expiration != null) {
+ keyGenParameterSpec = keyGenParameterSpec
+ .setKeyValidityEnd(expiration);
+ }
+ if (isUserAuthRequired) {
+ keyGenParameterSpec = keyGenParameterSpec
+ .setUserAuthenticationRequired(true)
+ .setUserAuthenticationParameters(Integer.MAX_VALUE, AUTH_DEVICE_CREDENTIAL);
+ }
+ keyGenerator.init(keyGenParameterSpec.build());
+ return keyGenerator.generateKey();
+ } catch (Exception e) {
+ loge("Create key exception", e);
+ return null;
+ }
+ }
+
+ /** Returns the validity end of a new short-lived key, or null if key does not expire. */
+ @Nullable
+ private Date getShortLivedKeyValidityEnd() {
+ if (mShortTermSecretKeyDurationMinutes > 0) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(new Date());
+ calendar.add(Calendar.MINUTE, mShortTermSecretKeyDurationMinutes);
+ return calendar.getTime();
+ } else {
+ return null;
+ }
+ }
+
+ /** Deletes the short term key from KeyStore, if it exists. */
+ private void deleteSecretKey(String alias) {
+ if (mKeyStore != null) {
+ logd("Delete key: %s", alias);
+ try {
+ mKeyStore.deleteEntry(alias);
+ } catch (Exception e) {
+ // Nothing to do. Even if the key removal fails, it becomes unusable.
+ loge("Delete key exception");
+ }
+ }
+ }
+
+ /** Returns the encrypted version of {@code input}, or an empty array in case of error. */
+ private byte[] encrypt(SecretKey secretKey, byte[] input) {
+ if (secretKey == null) {
+ loge("Encrypt: Secret key is null");
+ return new byte[0];
+ }
+
+ try {
+ final Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+
+ EncryptedPin encryptedPin = new EncryptedPin();
+ encryptedPin.iv = cipher.getIV();
+ encryptedPin.encryptedStoredPin = cipher.doFinal(input);
+ return EncryptedPin.toByteArray(encryptedPin);
+ } catch (Exception e) {
+ loge("Encrypt exception", e);
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__PIN_ENCRYPTION_ERROR, 1);
+ }
+ return new byte[0];
+ }
+
+ /** Returns the decrypted version of {@code input}, or an empty array in case of error. */
+ private byte[] decrypt(SecretKey secretKey, byte[] input) {
+ if (secretKey == null) {
+ loge("Decrypt: Secret key is null");
+ return new byte[0];
+ }
+
+ try {
+ EncryptedPin encryptedPin = EncryptedPin.parseFrom(input);
+ if (!ArrayUtils.isEmpty(encryptedPin.encryptedStoredPin)
+ && !ArrayUtils.isEmpty(encryptedPin.iv)) {
+ final Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ final GCMParameterSpec spec =
+ new GCMParameterSpec(GCM_PARAMETER_TAG_BIT_LEN, encryptedPin.iv);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
+ return cipher.doFinal(encryptedPin.encryptedStoredPin);
+ }
+ } catch (Exception e) {
+ loge("Decrypt exception", e);
+ TelephonyStatsLog.write(PIN_STORAGE_EVENT,
+ PIN_STORAGE_EVENT__EVENT__PIN_DECRYPTION_ERROR, 1);
+ }
+ return new byte[0];
+ }
+
+ private static void logv(String format, Object... args) {
+ if (VDBG) {
+ Rlog.d(TAG, String.format(format, args));
+ }
+ }
+
+ private static void logd(String format, Object... args) {
+ Rlog.d(TAG, String.format(format, args));
+ }
+
+ private static void loge(String format, Object... args) {
+ Rlog.e(TAG, String.format(format, args));
+ }
+
+ private static void loge(String msg, Throwable tr) {
+ Rlog.e(TAG, msg, tr);
+ }
+
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("PinStorage:");
+ pw.println(" mIsDeviceSecure=" + mIsDeviceSecure);
+ pw.println(" mIsDeviceLocked=" + mIsDeviceLocked);
+ pw.println(" isLongTermSecretKey=" + (boolean) (mLongTermSecretKey != null));
+ pw.println(" isShortTermSecretKey=" + (boolean) (mShortTermSecretKey != null));
+ pw.println(" isCacheAllowedByDevice=" + isCacheAllowedByDevice());
+ int slotCount = getSlotCount();
+ for (int i = 0; i < slotCount; i++) {
+ pw.println(" isCacheAllowedByCarrier[" + i + "]=" + isCacheAllowedByCarrier(i));
+ }
+ if (VDBG) {
+ SparseArray<StoredPin> storedPins = loadPinInformation();
+ for (int i = 0; i < storedPins.size(); i++) {
+ pw.println(" pin=" + storedPins.valueAt(i).toString());
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/ReceivedPhonebookRecords.java b/src/java/com/android/internal/telephony/uicc/ReceivedPhonebookRecords.java
new file mode 100644
index 0000000000..16fb55b4de
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/ReceivedPhonebookRecords.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Represents the received ADN entries from the SIM.
+ *
+ * {@hide}
+ */
+public class ReceivedPhonebookRecords {
+ @PhonebookReceivedState
+ private int mPhonebookReceivedState;
+ private List<SimPhonebookRecord> mEntries;
+
+ @IntDef(value = {
+ RS_OK,
+ RS_ERROR,
+ RS_ABORT,
+ RS_FINAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PhonebookReceivedState {}
+
+ public static final int RS_OK = 1;
+ public static final int RS_ERROR = 2;
+ public static final int RS_ABORT = 3;
+ public static final int RS_FINAL = 4;
+
+ public ReceivedPhonebookRecords(@PhonebookReceivedState int state,
+ List<SimPhonebookRecord> entries) {
+ mPhonebookReceivedState = state;
+ mEntries = entries;
+ }
+
+ public boolean isCompleted() {
+ return mPhonebookReceivedState == RS_FINAL;
+ }
+
+ public boolean isRetryNeeded() {
+ return mPhonebookReceivedState == RS_ABORT;
+ }
+
+ public boolean isOk() {
+ return mPhonebookReceivedState == RS_OK;
+ }
+ public List<SimPhonebookRecord> getPhonebookRecords() {
+ return mEntries;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/RuimFileHandler.java b/src/java/com/android/internal/telephony/uicc/RuimFileHandler.java
index 2323b5a4bb..f24fed3364 100644
--- a/src/java/com/android/internal/telephony/uicc/RuimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimFileHandler.java
@@ -64,8 +64,12 @@ public final class RuimFileHandler extends IccFileHandler {
case EF_CSIM_IMSIM:
case EF_CSIM_CDMAHOME:
case EF_CSIM_EPRL:
+ case EF_CSIM_PRL:
case EF_CSIM_MIPUPP:
return MF_SIM + DF_CDMA;
+ case EF_CSIM_MSPL:
+ case EF_CSIM_MLPL:
+ return MF_SIM + DF_TELECOM + DF_MMSS;
}
return getCommonIccEFPath(efid);
}
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index 28ce2d2fb8..f905bdc6ec 100644..100755
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -16,10 +16,12 @@
package com.android.internal.telephony.uicc;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Message;
import android.sysprop.TelephonyProperties;
import android.telephony.SubscriptionInfo;
@@ -27,6 +29,7 @@ import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.MccTable;
@@ -47,6 +50,7 @@ import java.util.Locale;
*/
public class RuimRecords extends IccRecords {
static final String LOG_TAG = "RuimRecords";
+ private final static int IMSI_MIN_LENGTH = 10;
private boolean mOtaCommited=false;
@@ -57,17 +61,17 @@ public class RuimRecords extends IccRecords {
private String mPrlVersion;
// From CSIM application
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private byte[] mEFpl = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private byte[] mEFli = null;
boolean mCsimSpnDisplayCondition = false;
private String mMdn;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mMin;
private String mHomeSystemId;
private String mHomeNetworkId;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mNai;
@Override
@@ -87,7 +91,6 @@ public class RuimRecords extends IccRecords {
}
// ***** Event Constants
- private static final int EVENT_GET_IMSI_DONE = 3;
private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
private static final int EVENT_GET_ICCID_DONE = 5;
private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
@@ -154,7 +157,7 @@ public class RuimRecords extends IccRecords {
mLoaded.set(false);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getMdnNumber() {
return mMyMobileNumber;
}
@@ -198,21 +201,11 @@ public class RuimRecords extends IccRecords {
}
}
- @UnsupportedAppUsage
- private int adjstMinDigits (int digits) {
- // Per C.S0005 section 2.3.1.
- digits += 111;
- digits = (digits % 10 == 0)?(digits - 10):digits;
- digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
- digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
- return digits;
- }
-
/**
* Returns the 5 or 6 digit MCC/MNC of the operator that
* provided the RUIM card. Returns null of RUIM is not yet ready
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getRUIMOperatorNumeric() {
String imsi = getIMSI();
@@ -382,7 +375,11 @@ public class RuimRecords extends IccRecords {
}
}
- private class EfCsimImsimLoaded implements IccRecordLoaded {
+ /**
+ * Parses IMSI based on C.S0065 section 5.2.2 and C.S0005 section 2.3.1
+ */
+ @VisibleForTesting
+ public class EfCsimImsimLoaded implements IccRecordLoaded {
@Override
public String getEfName() {
return "EF_CSIM_IMSIM";
@@ -391,32 +388,75 @@ public class RuimRecords extends IccRecords {
@Override
public void onRecordLoaded(AsyncResult ar) {
byte[] data = (byte[]) ar.result;
- if (VDBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
+ if (data == null || data.length < IMSI_MIN_LENGTH) {
+ loge("Invalid IMSI from EF_CSIM_IMSIM");
+ return;
+ }
+ if (DBG) log("data=" + Rlog.pii(LOG_TAG, IccUtils.bytesToHexString(data)));
// C.S0065 section 5.2.2 for IMSI_M encoding
// C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
boolean provisioned = ((data[7] & 0x80) == 0x80);
if (provisioned) {
- int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
- int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
- int digit7 = 0x0F & (data[4] >> 2);
- if (digit7 > 0x09) digit7 = 0;
- int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
- first3digits = adjstMinDigits(first3digits);
- second3digits = adjstMinDigits(second3digits);
- last3digits = adjstMinDigits(last3digits);
-
- StringBuilder builder = new StringBuilder();
- builder.append(String.format(Locale.US, "%03d", first3digits));
- builder.append(String.format(Locale.US, "%03d", second3digits));
- builder.append(String.format(Locale.US, "%d", digit7));
- builder.append(String.format(Locale.US, "%03d", last3digits));
- mMin = builder.toString();
+ final String imsi = decodeImsi(data);
+ if (TextUtils.isEmpty(mImsi)) {
+ mImsi = imsi;
+ if (DBG) log("IMSI=" + Rlog.pii(LOG_TAG, mImsi));
+ }
+ mMin = imsi.substring(5, 15);
if (DBG) log("min present=" + Rlog.pii(LOG_TAG, mMin));
} else {
if (DBG) log("min not present");
}
}
+
+ private int decodeImsiDigits(int digits, int length) {
+ // Per C.S0005 section 2.3.1.
+ for (int i = 0, denominator = 1; i < length; i++) {
+ digits += denominator;
+ if ((digits / denominator) % 10 == 0) {
+ digits = digits - (10 * denominator);
+ }
+ denominator *= 10;
+ }
+ return digits;
+ }
+
+ /**
+ * Decode utility to decode IMSI from data read from EF_IMSIM
+ * Please refer to
+ * C.S0065 section 5.2.2 for IMSI_M encoding
+ * C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
+ */
+ @VisibleForTesting
+ @NonNull
+ public String decodeImsi(byte[] data) {
+ // Retrieve the MCC and digits 11 and 12
+ int mcc_data = ((0x03 & data[9]) << 8) | (0xFF & data[8]);
+ int mcc = decodeImsiDigits(mcc_data, 3);
+ int digits_11_12_data = data[6] & 0x7f;
+ int digits_11_12 = decodeImsiDigits(digits_11_12_data, 2);
+
+ // Retrieve 10 MIN digits
+ int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
+ int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
+ int digit7 = 0x0F & (data[4] >> 2);
+ if (digit7 > 0x09) digit7 = 0;
+ int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
+
+ first3digits = decodeImsiDigits(first3digits, 3);
+ second3digits = decodeImsiDigits(second3digits, 3);
+ last3digits = decodeImsiDigits(last3digits, 3);
+
+ StringBuilder builder = new StringBuilder();
+ builder.append(String.format(Locale.US, "%03d", mcc));
+ builder.append(String.format(Locale.US, "%02d", digits_11_12));
+ builder.append(String.format(Locale.US, "%03d", first3digits));
+ builder.append(String.format(Locale.US, "%03d", second3digits));
+ builder.append(String.format(Locale.US, "%d", digit7));
+ builder.append(String.format(Locale.US, "%03d", last3digits));
+ return builder.toString();
+ }
}
private class EfCsimCdmaHomeLoaded implements IccRecordLoaded {
@@ -464,7 +504,7 @@ public class RuimRecords extends IccRecords {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void onGetCSimEprlDone(AsyncResult ar) {
// C.S0065 section 5.2.57 for EFeprl encoding
// C.S0016 section 3.5.5 for PRL format.
@@ -623,43 +663,6 @@ public class RuimRecords extends IccRecords {
log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received");
break;
- /* IO events */
- case EVENT_GET_IMSI_DONE:
- isRecordLoadResponse = true;
-
- ar = (AsyncResult)msg.obj;
- if (ar.exception != null) {
- loge("Exception querying IMSI, Exception:" + ar.exception);
- break;
- }
-
- mImsi = (String) ar.result;
-
- // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
- // than 15 (and usually 15).
- if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
- loge("invalid IMSI " + mImsi);
- mImsi = null;
- }
-
- // FIXME: CSIM IMSI may not contain the MNC.
- if (false) {
- log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx");
-
- String operatorNumeric = getRUIMOperatorNumeric();
- if (operatorNumeric != null) {
- if (operatorNumeric.length() <= 6) {
- log("update mccmnc=" + operatorNumeric);
- MccTable.updateMccMncConfiguration(mContext, operatorNumeric);
- }
- }
- } else {
- String operatorNumeric = getRUIMOperatorNumeric();
- log("NO update mccmnc=" + operatorNumeric);
- }
-
- break;
-
case EVENT_GET_CDMA_SUBSCRIPTION_DONE:
ar = (AsyncResult)msg.obj;
String localTemp[] = (String[])ar.result;
@@ -731,7 +734,7 @@ public class RuimRecords extends IccRecords {
* NOTE: This array will have duplicates. If this method will be caused
* frequently or in a tight loop, it can be rewritten for efficiency.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static String[] getAssetLanguages(Context ctx) {
final String[] locales = ctx.getAssets().getLocales();
final String[] localeLangs = new String[locales.length];
@@ -843,15 +846,12 @@ public class RuimRecords extends IccRecords {
mRecordsToLoad++;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void fetchRuimRecords() {
mRecordsRequested = true;
if (DBG) log("fetchRuimRecords " + mRecordsToLoad);
- mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE));
- mRecordsToLoad++;
-
mFh.loadEFTransparent(EF_ICCID,
obtainMessage(EVENT_GET_ICCID_DONE));
mRecordsToLoad++;
@@ -941,7 +941,7 @@ public class RuimRecords extends IccRecords {
fetchRuimRecords();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getMdn() {
return mMdn;
}
@@ -958,11 +958,11 @@ public class RuimRecords extends IccRecords {
return mHomeNetworkId;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean getCsimSpnDisplayCondition() {
return mCsimSpnDisplayCondition;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void log(String s) {
if (mParentApp != null) {
@@ -972,7 +972,7 @@ public class RuimRecords extends IccRecords {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void loge(String s) {
if (mParentApp != null) {
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 1118045539..aed860bb22 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Message;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
@@ -58,7 +59,7 @@ public class SIMRecords extends IccRecords {
// ***** Instance Variables
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
VoiceMailConstants mVmConfig;
// ***** Cached SIM State; cleared on channel close
@@ -78,21 +79,21 @@ public class SIMRecords extends IccRecords {
private byte[] mCphsInfo = null;
boolean mCspPlmnEnabled = true;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
byte[] mEfMWIS = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
byte[] mEfCPHS_MWI =null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
byte[] mEfCff = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
byte[] mEfCfis = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
byte[] mEfLi = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
byte[] mEfPl = null;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
UsimServiceTable mUsimServiceTable;
@Override
@@ -122,6 +123,9 @@ public class SIMRecords extends IccRecords {
// Short Name IEI from TS 24.008
static final int TAG_SHORT_NETWORK_NAME = 0x45;
+ // PLMN Additional Information tag from from TS 24.008
+ static final int TAG_PLMN_ADDITIONAL_INFORMATION = 0x80;
+
// active CFF from CPHS 4.2 B.4.5
static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a;
static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05;
@@ -157,7 +161,8 @@ public class SIMRecords extends IccRecords {
private static final int EVENT_GET_SPN_DONE = 12 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_GET_SPDI_DONE = 13 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_UPDATE_DONE = 14 + SIM_RECORD_EVENT_BASE;
- private static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE;
+ protected static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE;
+ protected static final int EVENT_GET_OPL_DONE = 16 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_GET_ALL_SMS_DONE = 18 + SIM_RECORD_EVENT_BASE;
private static final int EVENT_MARK_SMS_READ_DONE = 19 + SIM_RECORD_EVENT_BASE;
@@ -228,6 +233,8 @@ public class SIMRecords extends IccRecords {
mEfCPHS_MWI = null;
mSpdi = null;
mPnnHomeName = null;
+ mPnns = null;
+ mOpl = null;
mGid1 = null;
mGid2 = null;
mPlmnActRecords = null;
@@ -254,7 +261,7 @@ public class SIMRecords extends IccRecords {
//***** Public Methods
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getMsisdnNumber() {
return mMsisdn;
@@ -265,7 +272,7 @@ public class SIMRecords extends IccRecords {
return mUsimServiceTable;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int getExtFromEf(int ef) {
int ext;
switch (ef) {
@@ -321,7 +328,7 @@ public class SIMRecords extends IccRecords {
return mMsisdnTag;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getVoiceMailNumber() {
return mVoiceMailNum;
@@ -354,6 +361,10 @@ public class SIMRecords extends IccRecords {
@Override
public void setVoiceMailNumber(String alphaTag, String voiceNumber,
Message onComplete) {
+ if (mDestroyed.get()) {
+ return;
+ }
+
if (mIsVoiceMailFixed) {
AsyncResult.forMessage((onComplete)).exception =
new IccVmFixedException("Voicemail number is fixed by operator");
@@ -501,7 +512,7 @@ public class SIMRecords extends IccRecords {
/**
* {@inheritDoc}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public void setVoiceCallForwardingFlag(int line, boolean enable, String dialNumber) {
@@ -584,7 +595,7 @@ public class SIMRecords extends IccRecords {
/**
* {@inheritDoc}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
public String getOperatorNumeric() {
String imsi = getIMSI();
@@ -902,22 +913,22 @@ public class SIMRecords extends IccRecords {
isRecordLoadResponse = true;
ar = (AsyncResult) msg.obj;
- data = (byte[]) ar.result;
-
if (ar.exception != null) {
break;
}
- SimTlv tlv = new SimTlv(data, 0, data.length);
+ parseEfPnn((ArrayList<byte[]>) ar.result);
+ break;
- for (; tlv.isValidObject(); tlv.nextObject()) {
- if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
- mPnnHomeName = IccUtils.networkNameToString(
- tlv.getData(), 0, tlv.getData().length);
- log("PNN: " + mPnnHomeName);
- break;
- }
+ case EVENT_GET_OPL_DONE:
+ isRecordLoadResponse = true;
+
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ break;
}
+
+ parseEfOpl((ArrayList<byte[]>) ar.result);
break;
case EVENT_GET_ALL_SMS_DONE:
@@ -928,7 +939,7 @@ public class SIMRecords extends IccRecords {
break;
}
- handleSmses((ArrayList<byte []>) ar.result);
+ handleSmses((ArrayList<byte[]>) ar.result);
break;
case EVENT_MARK_SMS_READ_DONE:
@@ -1570,7 +1581,7 @@ public class SIMRecords extends IccRecords {
mRecordsToLoad++;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void fetchSimRecords() {
mRecordsRequested = true;
@@ -1618,7 +1629,10 @@ public class SIMRecords extends IccRecords {
mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
mRecordsToLoad++;
- mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
+ mFh.loadEFLinearFixedAll(EF_PNN, obtainMessage(EVENT_GET_PNN_DONE));
+ mRecordsToLoad++;
+
+ mFh.loadEFLinearFixedAll(EF_OPL, obtainMessage(EVENT_GET_OPL_DONE));
mRecordsToLoad++;
mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
@@ -1717,7 +1731,7 @@ public class SIMRecords extends IccRecords {
* ar.exception holds exception in error
* ar.result is byte[] for data in success
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void getSpnFsm(boolean start, AsyncResult ar) {
byte[] data;
@@ -1878,14 +1892,96 @@ public class SIMRecords extends IccRecords {
for (int i = 0; i + 2 < plmnEntries.length; i += 3) {
String plmnCode = IccUtils.bcdPlmnToString(plmnEntries, i);
if (!TextUtils.isEmpty(plmnCode)) {
- log("EF_SPDI PLMN: " + plmnCode);
tmpSpdi.add(plmnCode);
}
}
+ log("parseEfSpdi: " + tmpSpdi);
+
mSpdi = tmpSpdi.toArray(new String[tmpSpdi.size()]);
}
/**
+ * Parse EF PLMN Network Name (PNN) record from SIM
+ * Reference: 3GPP TS 31.102 Section 4.2.58.
+ */
+ private void parseEfPnn(ArrayList<byte[]> dataArray) {
+ if (dataArray == null) return;
+
+ final int count = dataArray.size();
+ List<PlmnNetworkName> tmpPnns = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ byte[] data = dataArray.get(i);
+ SimTlv tlv = new SimTlv(data, 0, data.length);
+
+ String longName = null;
+ String shortName = null;
+ for (; tlv.isValidObject(); tlv.nextObject()) {
+ switch (tlv.getTag()) {
+ case TAG_FULL_NETWORK_NAME:
+ longName = IccUtils.networkNameToString(tlv.getData(), 0,
+ tlv.getData().length);
+ break;
+
+ case TAG_SHORT_NETWORK_NAME:
+ shortName = IccUtils.networkNameToString(tlv.getData(), 0,
+ tlv.getData().length);
+ break;
+
+ case TAG_PLMN_ADDITIONAL_INFORMATION:
+ // TODO(b/154300344): read PLMN Additional Information.
+ break;
+ }
+ }
+ // PNNs must maintain their original indices. They will be referred to by index in OPL.
+ tmpPnns.add(new PlmnNetworkName(longName, shortName));
+ }
+ log("parseEfPnn: " + tmpPnns);
+
+ mPnns = tmpPnns.toArray(new PlmnNetworkName[0]);
+
+ // For compatiblility with legacy code.
+ if (mPnns.length > 0) mPnnHomeName = mPnns[0].getName();
+ }
+
+ /**
+ * Parse EF Operator PLMN List (OPL) record from SIM
+ * Reference: 3GPP TS 31.102 Section 4.2.59.
+ */
+ private void parseEfOpl(ArrayList<byte[]> dataArray) {
+ if (dataArray == null) return;
+
+ final int count = dataArray.size();
+ List<OperatorPlmnInfo> tmpOpl = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ byte[] data = dataArray.get(i);
+ // data.length is 8 as defined in 3GPP TS 31.102 Section 4.2.59.
+ // Byte 0 to 2 are for PLMN.
+ // Byte 3 and 4 are for lacTacStart.
+ // Byte 5 and 6 are for lacTacEnd.
+ // Byte 7 is for PNN Record Identifier.
+ if (data.length != 8) {
+ loge("Invalid length for OPL record " + data);
+ continue;
+ }
+
+ // A BCD value of 'D' in any of the MCC and/or MNC digits shall be used to indicate
+ // a "wild" value for that corresponding MCC/MNC digit.
+ String plmn = IccUtils.bcdPlmnToString(data, 0);
+ if (plmn.length() < PLMN_MIN_LENGTH) {
+ loge("Invalid length for decoded PLMN " + plmn);
+ continue;
+ }
+ int lacTacStart = IccUtils.bytesToInt(data, 3, 2);
+ int lacTacEnd = IccUtils.bytesToInt(data, 5, 2);
+ int pnnRecordId = IccUtils.bytesToInt(data, 7, 1);
+
+ tmpOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnRecordId));
+ }
+ log("parseEfOpl: " + tmpOpl);
+ mOpl = tmpOpl.toArray(new OperatorPlmnInfo[0]);
+ }
+
+ /**
* convert a byte array of packed plmns to an array of strings
*/
private String[] parseBcdPlmnList(byte[] data, String description) {
@@ -1911,13 +2007,13 @@ public class SIMRecords extends IccRecords {
/**
* check to see if Mailbox Number is allocated and activated in CPHS SST
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean isCphsMailboxEnabled() {
if (mCphsInfo == null) return false;
return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void log(String s) {
if (mParentApp != null) {
@@ -1927,7 +2023,7 @@ public class SIMRecords extends IccRecords {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Override
protected void loge(String s) {
if (mParentApp != null) {
@@ -1945,7 +2041,7 @@ public class SIMRecords extends IccRecords {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected void logv(String s) {
if (mParentApp != null) {
Rlog.v(LOG_TAG, "[SIMRecords-" + mParentApp.getPhoneId() + "] " + s);
diff --git a/src/java/com/android/internal/telephony/uicc/SimPhonebookRecord.java b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecord.java
new file mode 100644
index 0000000000..c6c7d6d858
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecord.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.hardware.radio.V1_6.PhonebookRecordInfo;
+import android.text.TextUtils;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents a Phonebook entry from the SIM.
+ *
+ * {@hide}
+ */
+public class SimPhonebookRecord {
+ // Instance variables
+ private int mRecordIndex = 0;
+ private String mAlphaTag;
+ private String mNumber;
+ private String[] mEmails;
+ private String[] mAdditionalNumbers;
+
+ // Instance methods
+ public SimPhonebookRecord (int recordIndex, String alphaTag, String number,
+ String[] emails, String[] adNumbers) {
+ mRecordIndex = recordIndex;
+ mAlphaTag = alphaTag;
+ mNumber = convertRecordFormatToNumber(number);
+ mEmails = emails;
+ if (adNumbers != null) {
+ mAdditionalNumbers = new String[adNumbers.length];
+ for (int i = 0 ; i < adNumbers.length; i++) {
+ mAdditionalNumbers[i] =
+ convertRecordFormatToNumber(adNumbers[i]);
+ }
+ }
+ }
+
+ public SimPhonebookRecord(PhonebookRecordInfo recInfo) {
+ mRecordIndex = recInfo.recordId;
+ mAlphaTag = recInfo.name;
+ mNumber = recInfo.number;
+ mEmails = recInfo.emails == null ? null
+ : recInfo.emails.toArray(new String[recInfo.emails.size()]);
+ mAdditionalNumbers = recInfo.additionalNumbers == null ? null
+ : recInfo.additionalNumbers.toArray(
+ new String[recInfo.additionalNumbers.size()]);
+ }
+
+ public SimPhonebookRecord() {}
+
+ public PhonebookRecordInfo toPhonebookRecordInfo() {
+ PhonebookRecordInfo pbRecordInfo = new PhonebookRecordInfo();
+ pbRecordInfo.recordId = mRecordIndex;
+ pbRecordInfo.name = convertNullToEmptyString(mAlphaTag);
+ pbRecordInfo.number = convertNullToEmptyString(convertNumberToRecordFormat(mNumber));
+ if (mEmails != null) {
+ for (String email : mEmails) {
+ pbRecordInfo.emails.add(email);
+ }
+ }
+ if (mAdditionalNumbers != null) {
+ for (String addNum : mAdditionalNumbers) {
+ pbRecordInfo.additionalNumbers.add(convertNumberToRecordFormat(addNum));
+ }
+ }
+ return pbRecordInfo;
+ }
+ public int getRecordIndex() {
+ return mRecordIndex;
+ }
+
+ public String getAlphaTag() {
+ return mAlphaTag;
+ }
+
+ public String getNumber() {
+ return mNumber;
+ }
+
+ public String[] getEmails() {
+ return mEmails;
+ }
+
+ public String[] getAdditionalNumbers() {
+ return mAdditionalNumbers;
+ }
+
+ /**
+ * convert phone number in the SIM phonebook record format to GSM pause/wild/wait character
+ */
+ private static String convertRecordFormatToNumber(String input) {
+ return input == null ? null : input.replace( 'e', PhoneNumberUtils.WAIT )
+ .replace( 'T', PhoneNumberUtils.PAUSE )
+ .replace( '?', PhoneNumberUtils.WILD );
+ }
+
+ /**
+ * convert the GSM pause/wild/wait character to the phone number in the SIM pb record format
+ */
+ private static String convertNumberToRecordFormat(String input) {
+ return input == null ? null : input.replace(PhoneNumberUtils.WAIT, 'e')
+ .replace(PhoneNumberUtils.PAUSE, 'T')
+ .replace(PhoneNumberUtils.WILD, '?');
+ }
+
+ private static String convertNullToEmptyString(String string) {
+ return string != null ? string : "";
+ }
+
+ public boolean isEmpty() {
+ return TextUtils.isEmpty(mAlphaTag)
+ && TextUtils.isEmpty(mNumber)
+ && ArrayUtils.isEmpty(mEmails)
+ && ArrayUtils.isEmpty(mAdditionalNumbers);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("SimPhoneBookRecord{").append("index =")
+ .append(mRecordIndex).append(", name = ")
+ .append(mAlphaTag == null ? "null" : mAlphaTag)
+ .append(", number = ").append(mNumber == null ? "null" : mNumber)
+ .append(", email count = ").append(mEmails == null ? 0 : mEmails.length)
+ .append(", email = ").append(Arrays.toString(mEmails))
+ .append(", ad number count = ")
+ .append(mAdditionalNumbers == null ? 0 : mAdditionalNumbers.length)
+ .append(", ad number = ").append(Arrays.toString(mAdditionalNumbers))
+ .append("}");
+ return sb.toString();
+ }
+
+ public final static class Builder {
+ private int mRecordIndex = 0;
+ private String mAlphaTag = null;
+ private String mNumber = null;
+ private String[] mEmails;
+ private String[] mAdditionalNumbers;
+
+ public SimPhonebookRecord build() {
+ SimPhonebookRecord record = new SimPhonebookRecord();
+ record.mAlphaTag = mAlphaTag;
+ record.mRecordIndex = mRecordIndex;
+ record.mNumber = mNumber;
+ if (mEmails != null) {
+ record.mEmails = mEmails;
+ }
+ if (mAdditionalNumbers != null) {
+ record.mAdditionalNumbers = mAdditionalNumbers;
+ }
+ return record;
+ }
+
+ public Builder setRecordIndex(int index) {
+ mRecordIndex = index;
+ return this;
+ }
+
+ public Builder setAlphaTag(String tag) {
+ mAlphaTag = tag;
+ return this;
+ }
+
+ public Builder setNumber(String number) {
+ mNumber = number;
+ return this;
+ }
+
+ public Builder setEmails(String[] emails) {
+ mEmails = emails;
+ return this;
+ }
+
+ public Builder setAdditionalNumbers(String[] anrs) {
+ mAdditionalNumbers = anrs;
+ return this;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java
new file mode 100644
index 0000000000..ca0011216a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/SimPhonebookRecordCache.java
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.annotation.RequiresFeature;
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.RadioInterfaceCapabilityController;
+import com.android.internal.telephony.uicc.AdnCapacity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * Used to store SIM phonebook records.
+ * <p/>
+ * This will be {@link #INVALID} if either is the case:
+ * <ol>
+ * <li>The device does not support
+ * {@link android.telephony.TelephonyManager#CAPABILITY_SIM_PHONEBOOK_IN_MODEM}.</li>
+ * </ol>
+ * {@hide}
+ */
+@RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = "TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM")
+public class SimPhonebookRecordCache extends Handler {
+ // Instance Variables
+ private static final String LOG_TAG = "SimPhonebookRecordCache";
+ private static final boolean DBG = true;
+ // Event Constants
+ private static final int EVENT_PHONEBOOK_CHANGED = 1;
+ private static final int EVENT_PHONEBOOK_RECORDS_RECEIVED = 2;
+ private static final int EVENT_GET_PHONEBOOK_RECORDS_DONE = 3;
+ private static final int EVENT_GET_PHONEBOOK_CAPACITY_DONE = 4;
+ private static final int EVENT_UPDATE_PHONEBOOK_RECORD_DONE = 5;
+ private static final int EVENT_SIM_REFRESH = 6;
+ private static final int EVENT_GET_PHONEBOOK_RECORDS_RETRY = 7;
+
+ private static final int MAX_RETRY_COUNT = 3;
+ private static final int RETRY_INTERVAL = 3000; // 3S
+
+ // member variables
+ private final CommandsInterface mCi;
+ private int mPhoneId;
+ private Context mContext;
+
+ // Presenting ADN capacity, including ADN, EMAIL ANR, and so on.
+ private AtomicReference<AdnCapacity> mAdnCapacity = new AtomicReference<AdnCapacity>(null);
+ private Object mReadLock = new Object();
+ private List<AdnRecord> mSimPbRecords =
+ Collections.synchronizedList(new ArrayList<AdnRecord>());
+ private List<UpdateRequest> mUpdateRequests =
+ Collections.synchronizedList(new ArrayList<UpdateRequest>());
+ // If true, clear the records in the cache and re-query from modem
+ private AtomicBoolean mIsCacheInvalidated = new AtomicBoolean(false);
+ private AtomicBoolean mIsRecordLoading = new AtomicBoolean(false);
+ private AtomicBoolean mIsInRetry = new AtomicBoolean(false);
+ private AtomicBoolean mIsInitialized = new AtomicBoolean(false);
+
+ // People waiting for SIM phonebook records to be loaded
+ ArrayList<Message> mAdnLoadingWaiters = new ArrayList<Message>();
+ /**
+ * The manual update from upper layer will result in notifying SIM phonebook changed,
+ * leading to fetch the Adn capacity, then whether to need to reload phonebook records
+ * is a problem. the SIM phoneback changed shall follow by updating record done, so that
+ * uses this flag to avoid unnecessary loading.
+ */
+ boolean mIsUpdateDone = false;
+
+ public SimPhonebookRecordCache(Context context, int phoneId, CommandsInterface ci) {
+ mCi = ci;
+ mPhoneId = phoneId;
+ mContext = context;
+ mCi.registerForSimPhonebookChanged(this, EVENT_PHONEBOOK_CHANGED, null);
+ mCi.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
+ mCi.registerForSimPhonebookRecordsReceived(this, EVENT_PHONEBOOK_RECORDS_RECEIVED, null);
+ }
+
+ /**
+ * This is recommended to use in work thread like IccPhoneBookInterfaceManager
+ * because it can't block main thread.
+ * @return true if this feature is supported
+ */
+ public boolean isEnabled() {
+ boolean isEnabled = RadioInterfaceCapabilityController
+ .getInstance()
+ .getCapabilities()
+ .contains(TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM);
+ return mIsInitialized.get() || isEnabled;
+ }
+
+ public void dispose() {
+ reset();
+ mCi.unregisterForSimPhonebookChanged(this);
+ mCi.unregisterForIccRefresh(this);
+ mCi.unregisterForSimPhonebookRecordsReceived(this);
+ }
+
+ private void reset() {
+ mAdnCapacity.set(null);
+ mSimPbRecords.clear();
+ mIsCacheInvalidated.set(false);
+ mIsRecordLoading.set(false);
+ mIsInRetry.set(false);
+ mIsInitialized.set(false);
+ mIsUpdateDone = false;
+ }
+
+ private void sendErrorResponse(Message response, String errString) {
+ if (response != null) {
+ Exception e = new RuntimeException(errString);
+ AsyncResult.forMessage(response).exception = e;
+ response.sendToTarget();
+ }
+ }
+
+ private void notifyAndClearWaiters() {
+ synchronized (mReadLock) {
+ for (Message response : mAdnLoadingWaiters){
+ if (response != null) {
+ AsyncResult.forMessage(response).result = mSimPbRecords;
+ response.sendToTarget();
+ }
+ }
+ mAdnLoadingWaiters.clear();
+ }
+ }
+
+ private void sendResponsesToWaitersWithError() {
+ synchronized (mReadLock) {
+ mReadLock.notify();
+
+ for (Message response : mAdnLoadingWaiters) {
+ sendErrorResponse(response, "Query adn record failed");
+ }
+ mAdnLoadingWaiters.clear();
+ }
+ }
+
+ private void getSimPhonebookCapacity() {
+ logd("Start to getSimPhonebookCapacity");
+ mCi.getSimPhonebookCapacity(obtainMessage(EVENT_GET_PHONEBOOK_CAPACITY_DONE));
+ }
+
+ public AdnCapacity getAdnCapacity() {
+ return mAdnCapacity.get();
+ }
+
+ private void fillCache() {
+ synchronized (mReadLock) {
+ fillCacheWithoutWaiting();
+ try {
+ mReadLock.wait();
+ } catch (InterruptedException e) {
+ loge("Interrupted Exception in queryAdnRecord");
+ }
+ }
+ }
+
+ private void fillCacheWithoutWaiting() {
+ logd("Start to queryAdnRecord");
+ if (mIsRecordLoading.compareAndSet(false, true)) {
+ mCi.getSimPhonebookRecords(obtainMessage(EVENT_GET_PHONEBOOK_RECORDS_DONE));
+ } else {
+ logd("The loading is ongoing");
+ }
+ }
+
+ public void requestLoadAllPbRecords(Message response) {
+ if (response == null && !mIsInitialized.get()) {
+ logd("Try to enforce flushing cache");
+ fillCacheWithoutWaiting();
+ return;
+ }
+
+ synchronized (mReadLock) {
+ mAdnLoadingWaiters.add(response);
+ final int pendingSize = mAdnLoadingWaiters.size();
+ boolean isCapacityInvalid = getAdnCapacity() == null;
+ if (isCapacityInvalid) {
+ getSimPhonebookCapacity();
+ }
+ if (pendingSize > 1 || mIsInRetry.get()
+ || !mIsInitialized.get() || isCapacityInvalid) {
+ logd("Add to the pending list as pending size = "
+ + pendingSize + " is retrying = " + mIsInRetry.get()
+ + " IsInitialized = " + mIsInitialized.get());
+ return;
+ }
+ }
+ if (!mIsRecordLoading.get() && !mIsInRetry.get()) {
+ logd("ADN cache has already filled in");
+ if (!mIsCacheInvalidated.get()) {
+ notifyAndClearWaiters();
+ return;
+ }
+ }
+ fillCache();
+ }
+
+ @VisibleForTesting
+ public boolean isLoading() {
+ return mIsRecordLoading.get();
+ }
+
+ @VisibleForTesting
+ public List<AdnRecord> getAdnRecords() {
+ return mSimPbRecords;
+ }
+
+ private void notifyAdnLoadingWaiters() {
+ synchronized (mReadLock) {
+ mReadLock.notify();
+ }
+ notifyAndClearWaiters();
+ }
+
+ public void updateSimPbAdnByRecordId(int recordId, AdnRecord newAdn, Message response) {
+ if (newAdn == null) {
+ sendErrorResponse(response, "There is an invalid new Adn for update");
+ return;
+ }
+ boolean found = false;
+ int index = 0;
+ for (Iterator<AdnRecord> it = mSimPbRecords.iterator(); it.hasNext();) {
+ AdnRecord oldAdn = it.next();
+ ++index;
+ if (oldAdn.getRecId() == recordId) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ sendErrorResponse(response, "There is an invalid old Adn for update");
+ return;
+ }
+ updateSimPhonebookByNewAdn(index, newAdn, response);
+ }
+
+ public void updateSimPbAdnBySearch(AdnRecord oldAdn, AdnRecord newAdn, Message response) {
+ int index = -1;
+ if ((oldAdn == null || oldAdn.isEmpty()) && !newAdn.isEmpty()) {
+ // Add contact
+ index = 0;
+ } else {
+ int count = 1;
+ // Delete or update contact
+ for (Iterator<AdnRecord> it = mSimPbRecords.iterator(); it.hasNext();) {
+ if (oldAdn.isEqual(it.next())) {
+ index = count;
+ break;
+ }
+ count++;
+ }
+ }
+ if (index == -1) {
+ sendErrorResponse(response, "SIM Phonebook record don't exist for " + oldAdn);
+ return;
+ }
+
+ if (newAdn == null) {
+ sendErrorResponse(response, "There is an invalid new Adn for update");
+ return;
+ }
+
+ if (index == 0 && mAdnCapacity.get() != null && mAdnCapacity.get().isSimFull()) {
+ sendErrorResponse(response, "SIM Phonebook record is full");
+ return;
+ }
+
+ updateSimPhonebookByNewAdn(index, newAdn, response);
+ }
+
+ private void updateSimPhonebookByNewAdn(int index, AdnRecord newAdn, Message response) {
+ int recordIndex = (index == 0) ? newAdn.getRecId()
+ : mSimPbRecords.get(index - 1).getRecId();
+ SimPhonebookRecord updateAdn = new SimPhonebookRecord.Builder()
+ .setRecordIndex(recordIndex)
+ .setAlphaTag(newAdn.getAlphaTag())
+ .setNumber(newAdn.getNumber())
+ .setEmails(newAdn.getEmails())
+ .setAdditionalNumbers(newAdn.getAdditionalNumbers())
+ .build();
+ UpdateRequest updateRequest = new UpdateRequest(index, newAdn, updateAdn, response);
+ mUpdateRequests.add(updateRequest);
+ boolean isCapacityInvalid = getAdnCapacity() == null;
+ if (isCapacityInvalid) {
+ getSimPhonebookCapacity();
+ }
+ if (mIsRecordLoading.get() || mIsInRetry.get() || mUpdateRequests.size() > 1
+ || !mIsInitialized.get() || isCapacityInvalid) {
+ logd("It is pending on update as " + " mIsRecordLoading = " + mIsRecordLoading.get()
+ + " mIsInRetry = " + mIsInRetry.get() + " pending size = "
+ + mUpdateRequests.size() + " mIsInitialized = " + mIsInitialized.get());
+ return;
+ }
+
+ updateSimPhonebook(updateRequest);
+ }
+
+ private void updateSimPhonebook(UpdateRequest request) {
+ logd("update Sim phonebook");
+ mCi.updateSimPhonebookRecord(request.phonebookRecord,
+ obtainMessage(EVENT_UPDATE_PHONEBOOK_RECORD_DONE, request));
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch(msg.what) {
+ case EVENT_PHONEBOOK_CHANGED:
+ logd("EVENT_PHONEBOOK_CHANGED");
+ handlePhonebookChanged();
+ break;
+ case EVENT_GET_PHONEBOOK_RECORDS_DONE:
+ logd("EVENT_GET_PHONEBOOK_RECORDS_DONE");
+ ar = (AsyncResult)msg.obj;
+ if (ar != null && ar.exception != null) {
+ loge("Failed to gain phonebook records");
+ invalidateSimPbCache();
+ if (!mIsInRetry.get()) {
+ sendGettingPhonebookRecordsRetry(0);
+ }
+ }
+ break;
+ case EVENT_GET_PHONEBOOK_CAPACITY_DONE:
+ logd("EVENT_GET_PHONEBOOK_CAPACITY_DONE");
+ ar = (AsyncResult)msg.obj;
+ if (ar != null && ar.exception == null) {
+ AdnCapacity capacity = (AdnCapacity)ar.result;
+ handlePhonebookCapacityChanged(capacity);
+ }
+ break;
+ case EVENT_PHONEBOOK_RECORDS_RECEIVED:
+ logd("EVENT_PHONEBOOK_RECORDS_RECEIVED");
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception != null) {
+ loge("Unexpected exception happened");
+ ar.result = null;
+ }
+
+ handlePhonebookRecordReceived((ReceivedPhonebookRecords)(ar.result));
+ break;
+ case EVENT_UPDATE_PHONEBOOK_RECORD_DONE:
+ logd("EVENT_UPDATE_PHONEBOOK_RECORD_DONE");
+ ar = (AsyncResult)msg.obj;
+ handleUpdatePhonebookRecordDone(ar);
+ break;
+ case EVENT_SIM_REFRESH:
+ logd("EVENT_SIM_REFRESH");
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ handleSimRefresh((IccRefreshResponse)ar.result);
+ } else {
+ logd("SIM refresh Exception: " + ar.exception);
+ }
+ break;
+ case EVENT_GET_PHONEBOOK_RECORDS_RETRY:
+ int retryCount = msg.arg1;
+ logd("EVENT_GET_PHONEBOOK_RECORDS_RETRY cnt = " + retryCount);
+ if (retryCount < MAX_RETRY_COUNT) {
+ mIsRecordLoading.set(false);
+ fillCacheWithoutWaiting();
+ sendGettingPhonebookRecordsRetry(++retryCount);
+ } else {
+ responseToWaitersWithErrorOrSuccess(false);
+ }
+ break;
+ default:
+ loge("Unexpected event: " + msg.what);
+ }
+
+ }
+
+ private void responseToWaitersWithErrorOrSuccess(boolean success) {
+ logd("responseToWaitersWithErrorOrSuccess success = " + success);
+ mIsRecordLoading.set(false);
+ mIsInRetry.set(false);
+ if (success) {
+ notifyAdnLoadingWaiters();
+ } else {
+ sendResponsesToWaitersWithError();
+
+ }
+ tryFireUpdatePendingList();
+ }
+
+ private void handlePhonebookChanged() {
+ if (mUpdateRequests.isEmpty()) {
+ // If this event is received, means this feature is supported.
+ getSimPhonebookCapacity();
+ } else {
+ logd("Do nothing in the midst of multiple update");
+ }
+ }
+
+ private void handlePhonebookCapacityChanged(AdnCapacity newCapacity) {
+ AdnCapacity oldCapacity = mAdnCapacity.get();
+ mAdnCapacity.set(newCapacity);
+ if (oldCapacity == null && newCapacity != null) {
+ if (newCapacity.getMaxAdnCount() > 0){
+ mSimPbRecords.clear();
+ fillCacheWithoutWaiting();
+ } else {
+ notifyAdnLoadingWaiters();
+ }
+ mIsInitialized.set(true); // Let's say the whole process is ready
+ } else {
+ // There is nothing from PB, so notify waiters directly if any
+ if (newCapacity != null && newCapacity.getMaxAdnCount() == 0) {
+ notifyAdnLoadingWaiters();
+ } else if (!mIsUpdateDone) {
+ invalidateSimPbCache();
+ fillCacheWithoutWaiting();
+ }
+ mIsUpdateDone = false;
+ }
+ }
+
+ private void handlePhonebookRecordReceived(ReceivedPhonebookRecords records) {
+ if (records != null) {
+ if (records.isOk()) {
+ logd("Partial data is received");
+ populateAdnRecords(records.getPhonebookRecords());
+ } else if (records.isCompleted()) {
+ logd("The whole loading process is finished");
+ populateAdnRecords(records.getPhonebookRecords());
+ mIsRecordLoading.set(false);
+ mIsInRetry.set(false);
+ notifyAdnLoadingWaiters();
+ tryFireUpdatePendingList();
+ } else if (records.isRetryNeeded() && !mIsInRetry.get()) {
+ logd("Start to retry as aborted");
+ sendGettingPhonebookRecordsRetry(0);
+ } else {
+ loge("Error happened");
+ // Let's keep the stale data, in example of SIM getting removed during loading,
+ // expects to finish the whole process.
+ responseToWaitersWithErrorOrSuccess(true);
+ }
+ } else {
+ loge("No records there");
+ responseToWaitersWithErrorOrSuccess(true);
+ }
+ }
+
+ private void handleUpdatePhonebookRecordDone(AsyncResult ar) {
+ Exception e = null;
+ UpdateRequest updateRequest = (UpdateRequest)ar.userObj;
+ mIsUpdateDone = true;
+ if (ar.exception == null) {
+ int index = updateRequest.index;
+ AdnRecord adn = updateRequest.adnRecord;
+ int recordIndex = ((int[]) (ar.result))[0];
+
+ if (index == 0) {
+ // add contact
+ addSimPbRecord(adn, recordIndex);
+ } else if (adn.isEmpty()){
+ // delete contact
+ AdnRecord deletedRecord = mSimPbRecords.get(index - 1);
+ int adnRecordIndex = deletedRecord.getRecId();
+ logd("Record number for deleted ADN is " + adnRecordIndex);
+ if(recordIndex == adnRecordIndex) {
+ deleteSimPbRecord(index);
+ } else {
+ e = new RuntimeException(
+ "The index for deleted ADN record did not match");
+ }
+ } else {
+ // Change contact
+ if (mSimPbRecords.size() > index - 1) {
+ AdnRecord oldRecord = mSimPbRecords.get(index - 1);
+ int adnRecordIndex = oldRecord.getRecId();
+ logd("Record number for changed ADN is " + adnRecordIndex);
+ if(recordIndex == adnRecordIndex) {
+ updateSimPbRecord(adn, recordIndex, index);
+ } else {
+ e = new RuntimeException(
+ "The index for changed ADN record did not match");
+ }
+ } else {
+ e = new RuntimeException(
+ "The index for changed ADN record is out of the border");
+ }
+ }
+ } else {
+ e = new RuntimeException("Update adn record failed", ar.exception);
+ }
+
+ if (mUpdateRequests.contains(updateRequest)) {
+ mUpdateRequests.remove(updateRequest);
+ updateRequest.responseResult(e);
+ } else {
+ loge("this update request isn't found");
+ }
+ tryFireUpdatePendingList();
+ }
+
+ private void tryFireUpdatePendingList() {
+ if (!mUpdateRequests.isEmpty()) {
+ updateSimPhonebook(mUpdateRequests.get(0));
+ }
+ }
+
+ private void handleSimRefresh(IccRefreshResponse iccRefreshResponse) {
+ if (iccRefreshResponse != null) {
+ if (iccRefreshResponse.refreshResult == IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE
+ && (iccRefreshResponse.efId == IccConstants.EF_PBR ||
+ iccRefreshResponse.efId == IccConstants.EF_ADN) ||
+ iccRefreshResponse.refreshResult == IccRefreshResponse.REFRESH_RESULT_INIT) {
+ invalidateSimPbCache();
+ getSimPhonebookCapacity();
+ }
+ } else {
+ logd("IccRefreshResponse received is null");
+ }
+ }
+
+ private void populateAdnRecords(List<SimPhonebookRecord> records) {
+ if (records != null) {
+ List<AdnRecord> newRecords = records.stream().map(record -> {return
+ new AdnRecord(0, // PBR or ADN
+ record.getRecordIndex(),
+ record.getAlphaTag(),
+ record.getNumber(),
+ record.getEmails(),
+ record.getAdditionalNumbers());}).collect(Collectors.toList());
+ mSimPbRecords.addAll(newRecords);
+ }
+ }
+
+ private void sendGettingPhonebookRecordsRetry (int times) {
+ if (hasMessages(EVENT_GET_PHONEBOOK_RECORDS_RETRY)) {
+ removeMessages(EVENT_GET_PHONEBOOK_RECORDS_RETRY);
+ }
+ mIsInRetry.set(true);
+ Message message = obtainMessage(EVENT_GET_PHONEBOOK_RECORDS_RETRY, 1, 0);
+ sendMessageDelayed(message, RETRY_INTERVAL);
+ }
+
+ private void addSimPbRecord(AdnRecord addedRecord, int recordIndex) {
+ logd("Record number for the added ADN is " + recordIndex);
+ addedRecord.setRecId(recordIndex);
+ mSimPbRecords.add(addedRecord);
+ }
+
+
+ private void deleteSimPbRecord(int index) {
+ logd("Record number for the deleted ADN is " + index);
+ mSimPbRecords.remove(index - 1);
+ }
+
+ private void updateSimPbRecord(AdnRecord newRecord,
+ int recordIndex, int index) {
+ logd("Record number for the updated ADN is " + recordIndex);
+ newRecord.setRecId(recordIndex);
+ mSimPbRecords.set(index - 1, newRecord);
+ }
+
+ private void invalidateSimPbCache() {
+ logd("invalidateSimPbCache");
+ mIsCacheInvalidated.set(true);
+ mSimPbRecords.clear();
+ }
+
+ private void logd(String msg) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ }
+
+ private void loge(String msg) {
+ if (DBG) {
+ Rlog.e(LOG_TAG, msg);
+ }
+ }
+
+ private final static class UpdateRequest {
+ private int index;
+ private Message response;
+ private AdnRecord adnRecord;
+ private SimPhonebookRecord phonebookRecord;
+
+ UpdateRequest(int index, AdnRecord record, SimPhonebookRecord phonebookRecord,
+ Message response) {
+ this.index = index;
+ this.adnRecord = record;
+ this.phonebookRecord = phonebookRecord;
+ this.response = response;
+ }
+
+ void responseResult(Exception e) {
+ if (response != null) {
+ AsyncResult.forMessage(response, null, e);
+ response.sendToTarget();
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 73e26a9083..a840391137 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.telephony.TelephonyManager;
@@ -50,9 +51,9 @@ public class UiccCard {
// The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock
// between UiccSlot, UiccCard, EuiccCard, and UiccProfile for now.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
protected final Object mLock;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CardState mCardState;
private String mIccid;
protected String mCardId;
@@ -61,7 +62,7 @@ public class UiccCard {
private Context mContext;
@UnsupportedAppUsage
private CommandsInterface mCi;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final int mPhoneId;
public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId, Object lock) {
@@ -155,7 +156,7 @@ public class UiccCard {
/**
* @deprecated Please use {@link UiccProfile#isApplicationOnIcc(AppType)} instead.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
synchronized (mLock) {
@@ -226,7 +227,7 @@ public class UiccCard {
*
* @deprecated Please use {@link UiccProfile#getApplicationByType(int)} instead.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
public UiccCardApplication getApplicationByType(int type) {
synchronized (mLock) {
@@ -453,7 +454,7 @@ public class UiccCard {
* @deprecated Please use
* {@link UiccProfile#getCarrierPackageNamesForIntent(PackageManager, Intent)} instead.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
public List<String> getCarrierPackageNamesForIntent(
PackageManager packageManager, Intent intent) {
@@ -479,7 +480,7 @@ public class UiccCard {
/**
* @deprecated Please use {@link UiccProfile#getOperatorBrandOverride()} instead.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
public String getOperatorBrandOverride() {
if (mUiccProfile != null) {
@@ -489,7 +490,7 @@ public class UiccCard {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getIccId() {
if (mIccid != null) {
return mIccid;
@@ -514,12 +515,12 @@ public class UiccCard {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String msg) {
Rlog.d(LOG_TAG, msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loge(String msg) {
Rlog.e(LOG_TAG, msg);
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index 66565075ca..e876efdc50 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -19,11 +19,13 @@ package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
@@ -59,21 +61,21 @@ public class UiccCardApplication {
public static final int AUTH_CONTEXT_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA;
public static final int AUTH_CONTEXT_UNDEFINED = PhoneConstants.AUTH_CONTEXT_UNDEFINED;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final Object mLock = new Object();
private UiccProfile mUiccProfile; //parent
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private AppState mAppState;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private AppType mAppType;
private int mAuthContext;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private PersoSubState mPersoSubState;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mAid;
private String mAppLabel;
private boolean mPin1Replaced;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private PinState mPin1State;
private PinState mPin2State;
private boolean mIccFdnEnabled;
@@ -85,13 +87,13 @@ public class UiccCardApplication {
private boolean mIgnoreApp;
private boolean mIccFdnAvailable = true; // Default is enabled.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CommandsInterface mCi;
private Context mContext;
private IccRecords mIccRecords;
private IccFileHandler mIccFh;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private boolean mDestroyed;//set to true once this App is commanded to be disposed of.
private RegistrantList mReadyRegistrants = new RegistrantList();
@@ -128,7 +130,7 @@ public class UiccCardApplication {
mCi.registerForNotAvailable(mHandler, EVENT_RADIO_UNAVAILABLE, null);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void update (IccCardApplicationStatus as, Context c, CommandsInterface ci) {
synchronized (mLock) {
if (mDestroyed) {
@@ -183,7 +185,7 @@ public class UiccCardApplication {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void dispose() {
synchronized (mLock) {
if (DBG) log(mAppType + " being Disposed");
@@ -310,7 +312,7 @@ public class UiccCardApplication {
mIccLockEnabled = (ints[0] != 0);
- // Sanity check: we expect mPin1State to match mIccLockEnabled.
+ // Correctness check: we expect mPin1State to match mIccLockEnabled.
// When mPin1State is DISABLED mIccLockEanbled should be false.
// When mPin1State is ENABLED mIccLockEnabled should be true.
//
@@ -388,6 +390,18 @@ public class UiccCardApplication {
if (mDestroyed) {
loge("Received message " + msg + "[" + msg.what
+ "] while being destroyed. Ignoring.");
+ //When UiccCardApp dispose,unlock SIM PIN message and need return exception.
+ if (msg.what == EVENT_PIN1_PUK1_DONE) {
+ ar = (AsyncResult) msg.obj;
+ if (ar != null) {
+ ar.exception = new CommandException(CommandException.Error.ABORTED);
+ Message response = (Message) ar.userObj;
+ if (response != null) {
+ AsyncResult.forMessage(response).exception = ar.exception;
+ response.sendToTarget();
+ }
+ }
+ }
return;
}
@@ -431,7 +445,7 @@ public class UiccCardApplication {
}
};
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void registerForReady(Handler h, int what, Object obj) {
synchronized (mLock) {
Registrant r = new Registrant (h, what, obj);
@@ -509,7 +523,7 @@ public class UiccCardApplication {
mPin1State == PinState.PINSTATE_ENABLED_BLOCKED ||
mPin1State == PinState.PINSTATE_ENABLED_PERM_BLOCKED) {
loge("Sanity check failed! APPSTATE is ready while PIN1 is not verified!!!");
- // Don't notify if application is in insane state
+ // Don't notify if application is in an invalid state
return;
}
if (r == null) {
@@ -557,7 +571,7 @@ public class UiccCardApplication {
if (mPin1State == PinState.PINSTATE_ENABLED_VERIFIED ||
mPin1State == PinState.PINSTATE_DISABLED) {
loge("Sanity check failed! APPSTATE is locked while PIN1 is not!!!");
- //Don't notify if application is in insane state
+ //Don't notify if application is in an invalid state
return;
}
if (r == null) {
@@ -607,7 +621,7 @@ public class UiccCardApplication {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getAuthContext() {
synchronized (mLock) {
return mAuthContext;
@@ -641,7 +655,7 @@ public class UiccCardApplication {
return authContext;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public PersoSubState getPersoSubState() {
synchronized (mLock) {
return mPersoSubState;
@@ -659,7 +673,7 @@ public class UiccCardApplication {
return mAppLabel;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public PinState getPin1State() {
synchronized (mLock) {
if (mPin1Replaced) {
@@ -940,7 +954,7 @@ public class UiccCardApplication {
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getPhoneId() {
return mUiccProfile.getPhoneId();
}
@@ -957,12 +971,12 @@ public class UiccCardApplication {
return mUiccProfile;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String msg) {
Rlog.d(LOG_TAG, msg);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void loge(String msg) {
Rlog.e(LOG_TAG, msg);
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index e1bc0b7d51..4e7e3bd63b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -25,6 +25,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.telephony.TelephonyManager;
@@ -32,6 +33,7 @@ import android.telephony.UiccAccessRule;
import android.text.TextUtils;
import android.util.LocalLog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CommandException;
import com.android.telephony.Rlog;
@@ -111,9 +113,9 @@ public class UiccCarrierPrivilegeRules extends Handler {
private static final int STATE_LOADED = 1;
private static final int STATE_ERROR = 2;
- // Max number of retries for open logical channel, interval is 10s.
- private static final int MAX_RETRY = 1;
- private static final int RETRY_INTERVAL_MS = 10000;
+ // Max number of retries for open logical channel, interval is 5s.
+ private static final int MAX_RETRY = 2;
+ private static final int RETRY_INTERVAL_MS = 5000;
private static final int STATUS_CODE_CONDITION_NOT_SATISFIED = 0x6985;
private static final int STATUS_CODE_APPLET_SELECT_FAILED = 0x6999;
@@ -127,9 +129,9 @@ public class UiccCarrierPrivilegeRules extends Handler {
// Bytes for the length field, in ASCII HEX string form.
private String lengthBytes;
// Decoded length as integer.
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Integer length;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String value;
public TLV(String tag) {
@@ -187,11 +189,11 @@ public class UiccCarrierPrivilegeRules extends Handler {
private UiccProfile mUiccProfile; // Parent
private UiccPkcs15 mUiccPkcs15; // ARF fallback
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private AtomicInteger mState;
private List<UiccAccessRule> mAccessRules;
private String mRules;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private Message mLoadedCallback;
// LocalLog buffer to hold important status messages for debugging.
private LocalLog mStatusMessage = new LocalLog(100);
@@ -228,6 +230,14 @@ public class UiccCarrierPrivilegeRules extends Handler {
openChannel(mAIDInUse);
}
+ @VisibleForTesting
+ public UiccCarrierPrivilegeRules(List<UiccAccessRule> rules) {
+ mAccessRules = rules;
+ mState = new AtomicInteger(STATE_LOADED);
+ mRules = "";
+ mStatusMessage.log("Loaded from test rules.");
+ }
+
/**
* Returns true if the carrier privilege rules have finished loading.
*/
@@ -371,10 +381,12 @@ public class UiccCarrierPrivilegeRules extends Handler {
PackageManager packageManager, int uid) {
String[] packages = packageManager.getPackagesForUid(uid);
- for (String pkg : packages) {
- int accessStatus = getCarrierPrivilegeStatus(packageManager, pkg);
- if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
- return accessStatus;
+ if (packages != null) {
+ for (String pkg : packages) {
+ int accessStatus = getCarrierPrivilegeStatus(packageManager, pkg);
+ if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
+ return accessStatus;
+ }
}
}
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 767005dfa3..36cad5f442 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -28,6 +28,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
@@ -131,7 +132,7 @@ public class UiccController extends Handler {
// NOTE: any new EVENT_* values must be added to eventToString.
// this needs to be here, because on bootup we dont know which index maps to which UiccSlot
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private CommandsInterface[] mCis;
@VisibleForTesting
public UiccSlot[] mUiccSlots;
@@ -192,6 +193,9 @@ public class UiccController extends Handler {
private UiccStateChangedLauncher mLauncher;
private RadioConfig mRadioConfig;
+ /* The storage for the PIN codes. */
+ private final PinStorage mPinStorage;
+
// LocalLog buffer to hold important SIM related events for debugging
private static LocalLog sLocalLog = new LocalLog(TelephonyUtils.IS_DEBUGGABLE ? 250 : 100);
@@ -228,7 +232,7 @@ public class UiccController extends Handler {
mPhoneIdToSlotId = new int[mCis.length];
Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
if (VDBG) logPhoneIdToSlotIdMapping();
- mRadioConfig = RadioConfig.getInstance(mContext);
+ mRadioConfig = RadioConfig.getInstance();
mRadioConfig.registerForSimSlotStatusChanged(this, EVENT_SLOT_STATUS_CHANGED, null);
for (int i = 0; i < mCis.length; i++) {
mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);
@@ -253,6 +257,8 @@ public class UiccController extends Handler {
PhoneConfigurationManager.registerForMultiSimConfigChange(
this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
+
+ mPinStorage = new PinStorage(mContext);
}
/**
@@ -421,7 +427,7 @@ public class UiccController extends Handler {
}
// Easy to use API
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IccRecords getIccRecords(int phoneId, int family) {
synchronized (mLock) {
UiccCardApplication app = getUiccCardApplication(phoneId, family);
@@ -433,7 +439,7 @@ public class UiccController extends Handler {
}
// Easy to use API
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public IccFileHandler getIccFileHandler(int phoneId, int family) {
synchronized (mLock) {
UiccCardApplication app = getUiccCardApplication(phoneId, family);
@@ -612,7 +618,7 @@ public class UiccController extends Handler {
}
// Easy to use API
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public UiccCardApplication getUiccCardApplication(int phoneId, int family) {
synchronized (mLock) {
UiccCard uiccCard = getUiccCardForPhone(phoneId);
@@ -623,7 +629,13 @@ public class UiccController extends Handler {
}
}
- static String getIccStateIntentString(IccCardConstants.State state) {
+ /**
+ * Convert IccCardConstants.State enum values to corresponding IccCardConstants String
+ * constants
+ * @param state IccCardConstants.State enum value
+ * @return IccCardConstants String constant representing ICC state
+ */
+ public static String getIccStateIntentString(IccCardConstants.State state) {
switch (state) {
case ABSENT: return IccCardConstants.INTENT_VALUE_ICC_ABSENT;
case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
@@ -682,6 +694,13 @@ public class UiccController extends Handler {
Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
return;
}
+ if (isShuttingDown()) {
+ // Do not process the SIM/SLOT events during device shutdown,
+ // as it may unnecessarily modify the persistent information
+ // like, SubscriptionManager.UICC_APPLICATIONS_ENABLED.
+ log("onGetIccCardStatusDone: shudown in progress ignore event");
+ return;
+ }
IccCardStatus status = (IccCardStatus)ar.result;
@@ -873,6 +892,11 @@ public class UiccController extends Handler {
return mDefaultEuiccCardId;
}
+ /** Get the {@link PinStorage}. */
+ public PinStorage getPinStorage() {
+ return mPinStorage;
+ }
+
private ArrayList<String> loadCardStrings() {
String cardStrings =
PreferenceManager.getDefaultSharedPreferences(mContext).getString(CARD_STRINGS, "");
@@ -910,6 +934,13 @@ public class UiccController extends Handler {
}
return;
}
+ if (isShuttingDown()) {
+ // Do not process the SIM/SLOT events during device shutdown,
+ // as it may unnecessarily modify the persistent information
+ // like, SubscriptionManager.UICC_APPLICATIONS_ENABLED.
+ log("onGetSlotStatusDone: shudown in progress ignore event");
+ return;
+ }
ArrayList<IccSlotStatus> status = (ArrayList<IccSlotStatus>) ar.result;
@@ -939,7 +970,7 @@ public class UiccController extends Handler {
if (isActive) {
numActiveSlots++;
- // sanity check: logicalSlotIndex should be valid for an active slot
+ // Correctness check: logicalSlotIndex should be valid for an active slot
if (!isValidPhoneIndex(iss.logicalSlotIndex)) {
Rlog.e(LOG_TAG, "Skipping slot " + i + " as phone " + iss.logicalSlotIndex
+ " is not available to communicate with this slot");
@@ -1048,13 +1079,13 @@ public class UiccController extends Handler {
if (VDBG) logPhoneIdToSlotIdMapping();
- // sanity check: number of active slots should be valid
+ // Correctness check: number of active slots should be valid
if (numActiveSlots != mPhoneIdToSlotId.length) {
Rlog.e(LOG_TAG, "Number of active slots " + numActiveSlots
+ " does not match the number of Phones" + mPhoneIdToSlotId.length);
}
- // sanity check: slotIds should be unique in mPhoneIdToSlotId
+ // Correctness check: slotIds should be unique in mPhoneIdToSlotId
Set<Integer> slotIds = new HashSet<>();
for (int slotId : mPhoneIdToSlotId) {
if (slotIds.contains(slotId)) {
@@ -1137,12 +1168,6 @@ public class UiccController extends Handler {
CarrierConfigManager configManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
configManager.updateConfigForPhoneId(index, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
-
- boolean requirePowerOffOnSimRefreshReset = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
- if (requirePowerOffOnSimRefreshReset) {
- mCis[index].setRadioPower(false, null);
- }
}
// The card status could have changed. Get the latest state.
@@ -1226,7 +1251,17 @@ public class UiccController extends Handler {
return (index >= 0 && index < mUiccSlots.length);
}
- @UnsupportedAppUsage
+ private boolean isShuttingDown() {
+ for (int i = 0; i < TelephonyManager.getDefault().getActiveModemCount(); i++) {
+ if (PhoneFactory.getPhone(i) != null &&
+ PhoneFactory.getPhone(i).isShuttingDown()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void log(String string) {
Rlog.d(LOG_TAG, string);
}
@@ -1260,9 +1295,10 @@ public class UiccController extends Handler {
pw.println(" mIsCdmaSupported=" + isCdmaSupported(mContext));
pw.println(" mHasBuiltInEuicc=" + mHasBuiltInEuicc);
pw.println(" mHasActiveBuiltInEuicc=" + mHasActiveBuiltInEuicc);
- pw.println(" mUiccSlots: size=" + mUiccSlots.length);
pw.println(" mCardStrings=" + mCardStrings);
pw.println(" mDefaultEuiccCardId=" + mDefaultEuiccCardId);
+ pw.println(" mPhoneIdToSlotId=" + Arrays.toString(mPhoneIdToSlotId));
+ pw.println(" mUiccSlots: size=" + mUiccSlots.length);
for (int i = 0; i < mUiccSlots.length; i++) {
if (mUiccSlots[i] == null) {
pw.println(" mUiccSlots[" + i + "]=null");
@@ -1273,5 +1309,6 @@ public class UiccController extends Handler {
}
pw.println(" sLocalLog= ");
sLocalLog.dump(fd, pw, args);
+ mPinStorage.dump(fd, pw, args);
}
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
index 3513bb90ab..12540672ea 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
@@ -53,7 +53,7 @@ public class UiccPkcs15 extends Handler {
private class FileHandler extends Handler {
// EF path for PKCS15 root, eg. "3F007F50"
// null if logical channel is used for PKCS15 access.
- private final String mPkcs15Path;
+ final String mPkcs15Path;
// Message to send when file has been parsed.
private Message mCallback;
// File id to read data from, eg. "5031"
@@ -90,7 +90,7 @@ public class UiccPkcs15 extends Handler {
private void readBinary() {
if (mChannelId >=0 ) {
mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00,
- "", obtainMessage(EVENT_READ_BINARY_DONE));
+ mFileId, obtainMessage(EVENT_READ_BINARY_DONE));
} else {
log("EF based");
}
@@ -200,8 +200,8 @@ public class UiccPkcs15 extends Handler {
// ar.result is null if using logical channel,
// or string for pkcs15 path if using file access.
mFh = new FileHandler((String) ar.result);
- if (!mFh.loadFile(ID_ACRF, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
- cleanUp();
+ if (!mFh.loadFile(EFODF_PATH, obtainMessage(EVENT_LOAD_ODF_DONE))) {
+ startFromAcrf();
}
} else {
log("select pkcs15 failed: " + ar.exception);
@@ -210,6 +210,39 @@ public class UiccPkcs15 extends Handler {
}
break;
+ case EVENT_LOAD_ODF_DONE:
+ if (ar.exception == null && ar.result != null) {
+ String idDodf = parseOdf((String) ar.result);
+ if (!mFh.loadFile(idDodf, obtainMessage(EVENT_LOAD_DODF_DONE))) {
+ startFromAcrf();
+ }
+ } else {
+ startFromAcrf();
+ }
+ break;
+
+ case EVENT_LOAD_DODF_DONE:
+ if (ar.exception == null && ar.result != null) {
+ String idAcmf = parseDodf((String) ar.result);
+ if (!mFh.loadFile(idAcmf, obtainMessage(EVENT_LOAD_ACMF_DONE))) {
+ startFromAcrf();
+ }
+ } else {
+ startFromAcrf();
+ }
+ break;
+
+ case EVENT_LOAD_ACMF_DONE:
+ if (ar.exception == null && ar.result != null) {
+ String idAcrf = parseAcmf((String) ar.result);
+ if (!mFh.loadFile(idAcrf, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
+ startFromAcrf();
+ }
+ } else {
+ startFromAcrf();
+ }
+ break;
+
case EVENT_LOAD_ACRF_DONE:
if (ar.exception == null && ar.result != null) {
mRules = new ArrayList<String>();
@@ -238,6 +271,13 @@ public class UiccPkcs15 extends Handler {
}
}
+ private void startFromAcrf() {
+ log("Fallback to use ACRF_PATH");
+ if (!mFh.loadFile(ACRF_PATH, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
+ cleanUp();
+ }
+ }
+
private void cleanUp() {
log("cleanUp");
if (mChannelId >= 0) {
@@ -250,10 +290,125 @@ public class UiccPkcs15 extends Handler {
// Constants defined in specs, needed for parsing
private static final String CARRIER_RULE_AID = "FFFFFFFFFFFF"; // AID for carrier privilege rule
- private static final String ID_ACRF = "4300";
+ private static final String ACRF_PATH = "4300";
+ private static final String EFODF_PATH = "5031";
private static final String TAG_ASN_SEQUENCE = "30";
private static final String TAG_ASN_OCTET_STRING = "04";
+ private static final String TAG_ASN_OID = "06";
private static final String TAG_TARGET_AID = "A0";
+ private static final String TAG_ODF = "A7";
+ private static final String TAG_DODF = "A1";
+ private static final String REFRESH_TAG_LEN = "08";
+ // OID defined by Global Platform for the "Access Control". The hexstring here can be converted
+ // to OID string value 1.2.840.114283.200.1.1
+ public static final String AC_OID = "060A2A864886FC6B81480101";
+
+
+ // parse ODF file to get file id for DODF file
+ // data is hex string, return file id if parse success, null otherwise
+ private String parseOdf(String data) {
+ // Example:
+ // [A7] 06 [30] 04 [04] 02 52 07
+ try {
+ TLV tlvRule = new TLV(TAG_ODF); // A7
+ tlvRule.parse(data, false);
+ String ruleString = tlvRule.getValue();
+ TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
+ TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04
+ tlvAsnPath.parse(ruleString, true);
+ tlvPath.parse(tlvAsnPath.getValue(), true);
+ return tlvPath.getValue();
+ } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+ log("Error: " + ex);
+ return null;
+ }
+ }
+
+ // parse DODF file to get file id for ACMF file
+ // data is hex string, return file id if parse success, null otherwise
+ private String parseDodf(String data) {
+ // Example:
+ // [A1] 29 [30] 00 [30] 0F 0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C [A1] 14 [30] 12
+ // [06] 0A 2A 86 48 86 FC 6B 81 48 01 01 [30] 04 04 02 42 00
+ String ret = null;
+ String acRules = data;
+ while (!acRules.isEmpty()) {
+ TLV dodfTag = new TLV(TAG_DODF); // A1
+ try {
+ acRules = dodfTag.parse(acRules, false);
+ String ruleString = dodfTag.getValue();
+ // Skip the Common Object Attributes
+ TLV commonObjectAttributes = new TLV(TAG_ASN_SEQUENCE); // 30
+ ruleString = commonObjectAttributes.parse(ruleString, false);
+
+ // Skip the Common Data Object Attributes
+ TLV commonDataObjectAttributes = new TLV(TAG_ASN_SEQUENCE); // 30
+ ruleString = commonDataObjectAttributes.parse(ruleString, false);
+
+ if (ruleString.startsWith(TAG_TARGET_AID)) {
+ // Skip SubClassAttributes [Optional]
+ TLV subClassAttributes = new TLV(TAG_TARGET_AID); // A0
+ ruleString = subClassAttributes.parse(ruleString, false);
+ }
+
+ if (ruleString.startsWith(TAG_DODF)) {
+ TLV oidDoTag = new TLV(TAG_DODF); // A1
+ oidDoTag.parse(ruleString, true);
+ ruleString = oidDoTag.getValue();
+
+ TLV oidDo = new TLV(TAG_ASN_SEQUENCE); // 30
+ oidDo.parse(ruleString, true);
+ ruleString = oidDo.getValue();
+
+ TLV oidTag = new TLV(TAG_ASN_OID); // 06
+ oidTag.parse(ruleString, false);
+ // Example : [06] 0A 2A 86 48 86 FC 6B 81 48 01 01
+ String oid = oidTag.getValue();
+ if (oid.equals(AC_OID)) {
+ // Skip OID and get the AC to the ACCM
+ ruleString = oidTag.parse(ruleString, false);
+ TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
+ TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04
+ tlvAsnPath.parse(ruleString, true);
+ tlvPath.parse(tlvAsnPath.getValue(), true);
+ return tlvPath.getValue();
+ }
+ }
+ continue; // skip current rule as it doesn't have expected TAG
+ } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+ log("Error: " + ex);
+ break; // Bad data, ignore all remaining ACRules
+ }
+ }
+ return ret;
+ }
+
+ // parse ACMF file to get file id for ACRF file
+ // data is hex string, return file id if parse success, null otherwise
+ private String parseAcmf(String data) {
+ try {
+ // [30] 10 [04] 08 01 02 03 04 05 06 07 08 [30] 04 [04] 02 43 00
+ TLV acmfTag = new TLV(TAG_ASN_SEQUENCE); // 30
+ acmfTag.parse(data, false);
+ String ruleString = acmfTag.getValue();
+ TLV refreshTag = new TLV(TAG_ASN_OCTET_STRING); // 04
+ String refreshTagLength = refreshTag.parseLength(ruleString);
+ if (!refreshTagLength.equals(REFRESH_TAG_LEN)) {
+ log("Error: refresh tag in ACMF must be 8.");
+ return null;
+ }
+ ruleString = refreshTag.parse(ruleString, false);
+ TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
+ TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04
+ tlvAsnPath.parse(ruleString, true);
+ tlvPath.parse(tlvAsnPath.getValue(), true);
+ return tlvPath.getValue();
+ } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+ log("Error: " + ex);
+ return null;
+ }
+
+ }
// parse ACRF file to get file id for ACCF file
// data is hex string, return file id if parse success, null otherwise
@@ -262,14 +417,14 @@ public class UiccPkcs15 extends Handler {
String acRules = data;
while (!acRules.isEmpty()) {
+ // Example:
+ // [30] 10 [A0] 08 04 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10
+ // bytes in [] are tags for the data
TLV tlvRule = new TLV(TAG_ASN_SEQUENCE);
try {
acRules = tlvRule.parse(acRules, false);
String ruleString = tlvRule.getValue();
if (ruleString.startsWith(TAG_TARGET_AID)) {
- // rule string consists of target AID + path, example:
- // [A0] 08 [04] 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10
- // bytes in [] are tags for the data
TLV tlvTarget = new TLV(TAG_TARGET_AID); // A0
TLV tlvAid = new TLV(TAG_ASN_OCTET_STRING); // 04
TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index e53a1cba1d..04b41c629a 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -16,6 +16,12 @@
package com.android.internal.telephony.uicc;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE;
+import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -48,6 +54,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccCardConstants;
@@ -56,6 +63,7 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.cat.CatService;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
@@ -106,12 +114,14 @@ public class UiccProfile extends IccCard {
private final UiccCard mUiccCard; //parent
private CatService mCatService;
private UiccCarrierPrivilegeRules mCarrierPrivilegeRules;
+ private UiccCarrierPrivilegeRules mTestOverrideCarrierPrivilegeRules;
private boolean mDisposed = false;
private RegistrantList mCarrierPrivilegeRegistrants = new RegistrantList();
private RegistrantList mOperatorBrandOverrideRegistrants = new RegistrantList();
private final int mPhoneId;
+ private final PinStorage mPinStorage;
private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 1;
private static final int EVENT_ICC_LOCKED = 2;
@@ -128,13 +138,17 @@ public class UiccProfile extends IccCard {
private static final int EVENT_SIM_IO_DONE = 12;
private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 13;
private static final int EVENT_CARRIER_CONFIG_CHANGED = 14;
+ private static final int EVENT_CARRIER_PRIVILEGES_TEST_OVERRIDE_SET = 15;
+ private static final int EVENT_SUPPLY_ICC_PIN_DONE = 16;
// NOTE: any new EVENT_* values must be added to eventToString.
private TelephonyManager mTelephonyManager;
private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
- private int mCurrentAppType = UiccController.APP_FAM_3GPP; //default to 3gpp?
+ @VisibleForTesting
+ public int mCurrentAppType = UiccController.APP_FAM_3GPP; //default to 3gpp?
+ private int mRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
private UiccCardApplication mUiccApplication = null;
private IccRecords mIccRecords = null;
private IccCardConstants.State mExternalState = IccCardConstants.State.UNKNOWN;
@@ -197,8 +211,13 @@ public class UiccProfile extends IccCard {
logWithLocalLog("handleMessage: Received " + eventName + " for phoneId " + mPhoneId);
switch (msg.what) {
case EVENT_NETWORK_LOCKED:
- mNetworkLockedRegistrants.notifyRegistrants(new AsyncResult(
- null, mUiccApplication.getPersoSubState().ordinal(), null));
+ if (mUiccApplication != null) {
+ mNetworkLockedRegistrants.notifyRegistrants(new AsyncResult(
+ null, mUiccApplication.getPersoSubState().ordinal(), null));
+ } else {
+ log("EVENT_NETWORK_LOCKED: mUiccApplication is NULL, "
+ + "mNetworkLockedRegistrants not notified.");
+ }
// intentional fall through
case EVENT_RADIO_OFF_OR_UNAVAILABLE:
case EVENT_ICC_LOCKED:
@@ -235,7 +254,7 @@ public class UiccProfile extends IccCard {
case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE:
case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE:
- case EVENT_SIM_IO_DONE:
+ case EVENT_SIM_IO_DONE: {
AsyncResult ar = (AsyncResult) msg.obj;
if (ar.exception != null) {
logWithLocalLog("handleMessage: Error in SIM access with exception "
@@ -244,6 +263,39 @@ public class UiccProfile extends IccCard {
AsyncResult.forMessage((Message) ar.userObj, ar.result, ar.exception);
((Message) ar.userObj).sendToTarget();
break;
+ }
+
+ case EVENT_CARRIER_PRIVILEGES_TEST_OVERRIDE_SET:
+ if (msg.obj == null) {
+ mTestOverrideCarrierPrivilegeRules = null;
+ } else {
+ mTestOverrideCarrierPrivilegeRules =
+ new UiccCarrierPrivilegeRules((List<UiccAccessRule>) msg.obj);
+ }
+ refresh();
+ break;
+
+ case EVENT_SUPPLY_ICC_PIN_DONE: {
+ AsyncResult ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ // An error occurred during automatic PIN verification. At this point,
+ // clear the cache and propagate the state.
+ loge("An error occurred during internal PIN verification");
+ mPinStorage.clearPin(mPhoneId);
+ updateExternalState();
+ } else {
+ log("Internal PIN verification was successful!");
+ // Nothing to do.
+ }
+ // Update metrics:
+ TelephonyStatsLog.write(
+ PIN_STORAGE_EVENT,
+ ar.exception != null
+ ? PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE
+ : PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS,
+ /* number_of_pins= */ 1);
+ break;
+ }
default:
loge("handleMessage: Unhandled message with number: " + msg.what);
@@ -269,6 +321,7 @@ public class UiccProfile extends IccCard {
// for RadioConfig<1.2 eid is not known when the EuiccCard is constructed
((EuiccCard) mUiccCard).registerForEidReady(mHandler, EVENT_EID_READY, null);
}
+ mPinStorage = UiccController.getInstance().getPinStorage();
update(c, ci, ics);
ci.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
@@ -319,6 +372,7 @@ public class UiccProfile extends IccCard {
}
mCatService = null;
mUiccApplications = null;
+ mRadioTech = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
mCarrierPrivilegeRules = null;
mContext.getContentResolver().unregisterContentObserver(
mProvisionCompleteContentObserver);
@@ -335,6 +389,7 @@ public class UiccProfile extends IccCard {
if (DBG) {
log("Setting radio tech " + ServiceState.rilRadioTechnologyToString(radioTech));
}
+ mRadioTech = radioTech;
setCurrentAppType(ServiceState.isGsm(radioTech));
updateIccAvailability(false);
}
@@ -347,7 +402,7 @@ public class UiccProfile extends IccCard {
mCurrentAppType = UiccController.APP_FAM_3GPP;
} else {
UiccCardApplication newApp = getApplication(UiccController.APP_FAM_3GPP2);
- if(newApp != null) {
+ if (newApp != null || getApplication(UiccController.APP_FAM_3GPP) == null) {
mCurrentAppType = UiccController.APP_FAM_3GPP2;
} else {
mCurrentAppType = UiccController.APP_FAM_3GPP;
@@ -569,6 +624,17 @@ public class UiccProfile extends IccCard {
log("updateExternalState: card locked and records loaded; "
+ "setting state to locked");
}
+ // If the PIN code is required and an available cached PIN is available, intercept
+ // the update of external state and perform an internal PIN verification.
+ if (lockedState == IccCardConstants.State.PIN_REQUIRED) {
+ String pin = mPinStorage.getPin(mPhoneId);
+ if (!pin.isEmpty()) {
+ log("PIN_REQUIRED[" + mPhoneId + "] - Cache present");
+ mCi.supplyIccPin(pin, mHandler.obtainMessage(EVENT_SUPPLY_ICC_PIN_DONE));
+ return;
+ }
+ }
+
setExternalState(lockedState);
} else {
if (VDBG) {
@@ -771,8 +837,15 @@ public class UiccProfile extends IccCard {
mNetworkLockedRegistrants.add(r);
if (getState() == IccCardConstants.State.NETWORK_LOCKED) {
- r.notifyRegistrant(
- new AsyncResult(null, mUiccApplication.getPersoSubState().ordinal(), null));
+ if (mUiccApplication != null) {
+ r.notifyRegistrant(
+ new AsyncResult(null, mUiccApplication.getPersoSubState().ordinal(),
+ null));
+
+ } else {
+ log("registerForNetworkLocked: not notifying registrants, "
+ + "mUiccApplication == null");
+ }
}
}
}
@@ -1041,6 +1114,9 @@ public class UiccProfile extends IccCard {
}
sanitizeApplicationIndexesLocked();
+ if (mRadioTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+ setCurrentAppType(ServiceState.isGsm(mRadioTech));
+ }
updateIccAvailability(true);
}
}
@@ -1250,6 +1326,11 @@ public class UiccProfile extends IccCard {
}
private void onCarrierPrivilegesLoadedMessage() {
+ // Update set of enabled carrier apps now that the privilege rules may have changed.
+ ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ mTelephonyManager, am.getCurrentUser(), mContext);
+
UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(
Context.USAGE_STATS_SERVICE);
if (usm != null) {
@@ -1307,21 +1388,22 @@ public class UiccProfile extends IccCard {
}
private Set<String> getUninstalledCarrierPackages() {
- String whitelistSetting = Settings.Global.getString(
+ String allowListSetting = Settings.Global.getString(
mContext.getContentResolver(),
Settings.Global.CARRIER_APP_WHITELIST);
- if (TextUtils.isEmpty(whitelistSetting)) {
+ if (TextUtils.isEmpty(allowListSetting)) {
return Collections.emptySet();
}
- Map<String, String> certPackageMap = parseToCertificateToPackageMap(whitelistSetting);
+ Map<String, String> certPackageMap = parseToCertificateToPackageMap(allowListSetting);
if (certPackageMap.isEmpty()) {
return Collections.emptySet();
}
- if (mCarrierPrivilegeRules == null) {
+ UiccCarrierPrivilegeRules rules = getCarrierPrivilegeRules();
+ if (rules == null) {
return Collections.emptySet();
}
Set<String> uninstalledCarrierPackages = new ArraySet<>();
- List<UiccAccessRule> accessRules = mCarrierPrivilegeRules.getAccessRules();
+ List<UiccAccessRule> accessRules = rules.getAccessRules();
for (UiccAccessRule accessRule : accessRules) {
String certHexString = accessRule.getCertificateHexString().toUpperCase();
String pkgName = certPackageMap.get(certHexString);
@@ -1338,11 +1420,11 @@ public class UiccProfile extends IccCard {
* @hide
*/
@VisibleForTesting
- public static Map<String, String> parseToCertificateToPackageMap(String whitelistSetting) {
+ public static Map<String, String> parseToCertificateToPackageMap(String allowListSetting) {
final String pairDelim = "\\s*;\\s*";
final String keyValueDelim = "\\s*:\\s*";
- List<String> keyValuePairList = Arrays.asList(whitelistSetting.split(pairDelim));
+ List<String> keyValuePairList = Arrays.asList(allowListSetting.split(pairDelim));
if (keyValuePairList.isEmpty()) {
return Collections.emptyMap();
@@ -1355,7 +1437,7 @@ public class UiccProfile extends IccCard {
if (keyValue.length == 2) {
map.put(keyValue[0].toUpperCase(), keyValue[1]);
} else {
- loge("Incorrect length of key-value pair in carrier app whitelist map. "
+ loge("Incorrect length of key-value pair in carrier app allow list map. "
+ "Length should be exactly 2");
}
}
@@ -1450,20 +1532,32 @@ public class UiccProfile extends IccCard {
}
/**
- * Resets the application with the input AID. Returns true if any changes were made.
+ * Resets the application with the input AID.
*
* A null aid implies a card level reset - all applications must be reset.
*
* @param aid aid of the application which should be reset; null imples all applications
* @param reset true if reset is required. false for initialization.
- * @return boolean indicating if there was any change made as part of the reset
+ * @return boolean indicating if there was any change made as part of the reset which
+ * requires carrier config to be reset too (for e.g. if only ISIM app is refreshed carrier
+ * config should not be reset)
*/
+ @VisibleForTesting
public boolean resetAppWithAid(String aid, boolean reset) {
synchronized (mLock) {
boolean changed = false;
+ boolean isIsimRefresh = false;
for (int i = 0; i < mUiccApplications.length; i++) {
if (mUiccApplications[i] != null
&& (TextUtils.isEmpty(aid) || aid.equals(mUiccApplications[i].getAid()))) {
+ // Resetting only ISIM does not need to be treated as a change from caller
+ // perspective, as it does not affect SIM state now or even later when ISIM
+ // is re-loaded, hence return false.
+ if (!TextUtils.isEmpty(aid)
+ && mUiccApplications[i].getType() == AppType.APPTYPE_ISIM) {
+ isIsimRefresh = true;
+ }
+
// Delete removed applications
mUiccApplications[i].dispose();
mUiccApplications[i] = null;
@@ -1484,7 +1578,7 @@ public class UiccProfile extends IccCard {
changed = true;
}
}
- return changed;
+ return changed && !isIsimRefresh;
}
}
@@ -1545,13 +1639,7 @@ public class UiccProfile extends IccCard {
* Returns number of applications on this card
*/
public int getNumApplications() {
- int count = 0;
- for (UiccCardApplication a : mUiccApplications) {
- if (a != null) {
- count++;
- }
- }
- return count;
+ return mLastReportedNumOfUiccApplications;
}
/**
@@ -1647,6 +1735,15 @@ public class UiccProfile extends IccCard {
return certs.isEmpty() ? null : certs;
}
+ /** @return a list of {@link UiccAccessRule}s, or an empty list if none have been loaded yet. */
+ public List<UiccAccessRule> getCarrierPrivilegeAccessRules() {
+ UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+ if (carrierPrivilegeRules == null) {
+ return Collections.EMPTY_LIST;
+ }
+ return new ArrayList<>(carrierPrivilegeRules.getAccessRules());
+ }
+
/**
* Exposes {@link UiccCarrierPrivilegeRules#getCarrierPackageNamesForIntent}.
*/
@@ -1661,26 +1758,14 @@ public class UiccProfile extends IccCard {
/** Returns a reference to the current {@link UiccCarrierPrivilegeRules}. */
private UiccCarrierPrivilegeRules getCarrierPrivilegeRules() {
synchronized (mLock) {
+ if (mTestOverrideCarrierPrivilegeRules != null) {
+ return mTestOverrideCarrierPrivilegeRules;
+ }
return mCarrierPrivilegeRules;
}
}
/**
- * Make sure the iccid in SIM record matches the current active subId. If not, return false.
- * When SIM switching in eSIM is happening, there are rare cases that setOperatorBrandOverride
- * is called on old subId while new iccid is already loaded on SIM record. For those cases
- * setOperatorBrandOverride would apply to the wrong (new) iccid. This check is to avoid it.
- */
- private boolean checkSubIdAndIccIdMatch(String iccid) {
- if (TextUtils.isEmpty(iccid)) return false;
- SubscriptionInfo subInfo = SubscriptionController.getInstance()
- .getActiveSubscriptionInfoForSimSlotIndex(
- getPhoneId(), mContext.getOpPackageName(), null);
- return subInfo != null && IccUtils.stripTrailingFs(subInfo.getIccId()).equals(
- IccUtils.stripTrailingFs(iccid));
- }
-
- /**
* Sets the overridden operator brand.
*/
public boolean setOperatorBrandOverride(String brand) {
@@ -1691,7 +1776,7 @@ public class UiccProfile extends IccCard {
if (TextUtils.isEmpty(iccId)) {
return false;
}
- if (!checkSubIdAndIccIdMatch(iccId)) {
+ if (!SubscriptionController.getInstance().checkPhoneIdAndIccIdMatch(getPhoneId(), iccId)) {
loge("iccId doesn't match current active subId.");
return false;
}
@@ -1747,11 +1832,15 @@ public class UiccProfile extends IccCard {
case EVENT_ICC_RECORD_EVENTS: return "ICC_RECORD_EVENTS";
case EVENT_OPEN_LOGICAL_CHANNEL_DONE: return "OPEN_LOGICAL_CHANNEL_DONE";
case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: return "CLOSE_LOGICAL_CHANNEL_DONE";
- case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE: return "TRANSMIT_APDU_LOGICAL_CHANNEL_DONE";
+ case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE:
+ return "TRANSMIT_APDU_LOGICAL_CHANNEL_DONE";
case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE: return "TRANSMIT_APDU_BASIC_CHANNEL_DONE";
case EVENT_SIM_IO_DONE: return "SIM_IO_DONE";
case EVENT_CARRIER_PRIVILEGES_LOADED: return "CARRIER_PRIVILEGES_LOADED";
case EVENT_CARRIER_CONFIG_CHANGED: return "CARRIER_CONFIG_CHANGED";
+ case EVENT_CARRIER_PRIVILEGES_TEST_OVERRIDE_SET:
+ return "CARRIER_PRIVILEGES_TEST_OVERRIDE_SET";
+ case EVENT_SUPPLY_ICC_PIN_DONE: return "SUPPLY_ICC_PIN_DONE";
default: return "UNKNOWN(" + event + ")";
}
}
@@ -1779,6 +1868,19 @@ public class UiccProfile extends IccCard {
}
/**
+ * Set a test set of carrier privilege rules which will override the actual rules on the SIM.
+ *
+ * <p>May be null, in which case the rules on the SIM will be used and any previous overrides
+ * will be cleared.
+ *
+ * @see TelephonyManager#setCarrierTestOverride
+ */
+ public void setTestOverrideCarrierPrivilegeRules(@Nullable List<UiccAccessRule> rules) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_TEST_OVERRIDE_SET, rules));
+ }
+
+ /**
* Dump
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -1831,6 +1933,11 @@ public class UiccProfile extends IccCard {
pw.println(" mCarrierPrivilegeRules: " + mCarrierPrivilegeRules);
mCarrierPrivilegeRules.dump(fd, pw, args);
}
+ if (mTestOverrideCarrierPrivilegeRules != null) {
+ pw.println(" mTestOverrideCarrierPrivilegeRules: "
+ + mTestOverrideCarrierPrivilegeRules);
+ mTestOverrideCarrierPrivilegeRules.dump(fd, pw, args);
+ }
pw.println(" mCarrierPrivilegeRegistrants: size=" + mCarrierPrivilegeRegistrants.size());
for (int i = 0; i < mCarrierPrivilegeRegistrants.size(); i++) {
pw.println(" mCarrierPrivilegeRegistrants[" + i + "]="
diff --git a/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java b/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
index 281b5f2778..fc58d3cec3 100644
--- a/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimServiceTable.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
/**
@@ -142,7 +143,7 @@ public final class UsimServiceTable extends IccServiceTable {
super(table);
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isAvailable(UsimService service) {
return super.isAvailable(service.ordinal());
}
diff --git a/src/java/com/android/internal/telephony/uicc/VoiceMailConstants.java b/src/java/com/android/internal/telephony/uicc/VoiceMailConstants.java
index b10b8758ad..ee2ba0e119 100644
--- a/src/java/com/android/internal/telephony/uicc/VoiceMailConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/VoiceMailConstants.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.uicc;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
import android.os.Environment;
import android.util.Xml;
@@ -47,7 +48,7 @@ class VoiceMailConstants {
static final int TAG = 2;
static final int SIZE = 3;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
VoiceMailConstants () {
CarrierVmMap = new HashMap<String, String[]>();
loadVoiceMail();
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
index 0b5627382a..7ec5e604d7 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -1063,13 +1063,13 @@ public class EuiccCard extends UiccCard {
devCapBuilder.addChildAsBytes(Tags.TAG_CTX_7, versionBytes);
break;
case DEV_CAP_NREPC:
- devCapBuilder.addChildAsBytes(Tags.TAG_CTX_9, versionBytes);
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_8, versionBytes);
break;
case DEV_CAP_NR5GC:
- devCapBuilder.addChildAsBytes(Tags.TAG_CTX_10, versionBytes);
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_9, versionBytes);
break;
case DEV_CAP_EUTRAN5GC:
- devCapBuilder.addChildAsBytes(Tags.TAG_CTX_11, versionBytes);
+ devCapBuilder.addChildAsBytes(Tags.TAG_CTX_10, versionBytes);
break;
default:
loge("Invalid device capability name: " + devCap);
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/Tags.java b/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
index 26df42b196..1eab1998a7 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/Tags.java
@@ -57,7 +57,6 @@ class Tags {
static final int TAG_CTX_8 = 0x88;
static final int TAG_CTX_9 = 0x89;
static final int TAG_CTX_10 = 0x8A;
- static final int TAG_CTX_11 = 0x8B;
// Context tags for constructed (compound) types
static final int TAG_CTX_COMP_0 = 0xA0;
diff --git a/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java b/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java
deleted file mode 100644
index 0c92159ade..0000000000
--- a/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.vendor;
-
-import android.content.Context;
-import android.os.AsyncResult;
-import android.os.Message;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.PhoneNotifier;
-import com.android.internal.telephony.TelephonyComponentFactory;
-
-public class VendorGsmCdmaPhone extends GsmCdmaPhone {
- private static final String LOG_TAG = "VendorGsmCdmaPhone";
- private static final int PROP_EVENT_START = EVENT_LAST;
- private static final int DEFAULT_PHONE_INDEX = 0;
-
- private boolean mIsPhoneReadySent = false;
- private boolean mIsPhoneReadyPending = false;
- private static int READY = 1;
-
- public VendorGsmCdmaPhone(Context context,
- CommandsInterface ci, PhoneNotifier notifier, int phoneId,
- int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) {
- this(context, ci, notifier, false, phoneId, precisePhoneType,
- telephonyComponentFactory);
- }
-
- public VendorGsmCdmaPhone(Context context,
- CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode, int phoneId,
- int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) {
- super(context, ci, notifier, unitTestMode, phoneId, precisePhoneType,
- telephonyComponentFactory);
- Rlog.d(LOG_TAG, "Constructor");
- }
-
- @Override
- protected void phoneObjectUpdater(int newVoiceTech) {
- super.phoneObjectUpdater(newVoiceTech);
- }
-
- @Override
- public boolean getCallForwardingIndicator() {
- if (!isCurrentSubValid()) {
- return false;
- }
- return super.getCallForwardingIndicator();
- }
-
- private boolean isCurrentSubValid() {
- boolean isUiccApplicationEnabled = true;
- // FIXME get the SubscriptionManager.UICC_APPLICATIONS_ENABLED value and use it above
-
- SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
-
- Rlog.d(LOG_TAG, "ProvisionStatus: " + isUiccApplicationEnabled + " phone id:" + mPhoneId);
- return subscriptionManager.isActiveSubscriptionId(getSubId()) && isUiccApplicationEnabled;
- }
-
- public void fetchIMEI() {
- Rlog.d(LOG_TAG, "fetching device id");
- mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
- }
-
- @Override
- public void handleMessage(Message msg) {
- Rlog.d(LOG_TAG, "handleMessage: Event: " + msg.what);
- AsyncResult ar;
- switch(msg.what) {
-
- case EVENT_SIM_RECORDS_LOADED:
- if(isPhoneTypeGsm()) {
- Rlog.d(LOG_TAG, "notify call forward indication, phone id:" + mPhoneId);
- notifyCallForwardingIndicator();
- }
-
- super.handleMessage(msg);
- break;
-
- case EVENT_RADIO_AVAILABLE:
- mIsPhoneReadySent = false;
- super.handleMessage(msg);
- break;
-
- case EVENT_RIL_CONNECTED:
- mIsPhoneReadySent = false;
- super.handleMessage(msg);
- break;
-
- default: {
- super.handleMessage(msg);
- }
-
- }
- }
-
- // In DSDA, char 'D' is used as DTMF char for playing supervisory tone for G/W.
- // For CDMA, '#' is used. A, B, C & D are also supported as DTMF digits for G/W networks.
- @Override
- public void startDtmf(char c) {
- if (!(PhoneNumberUtils.is12Key(c) || (c == 'D'))) {
- Rlog.e(LOG_TAG, "startDtmf called with invalid character '" + c + "'");
- } else {
- if (isPhoneTypeCdma() && c == 'D') {
- c = '#';
- }
- mCi.startDtmf(c, null);
- }
- }
-
- // For CDMA sendBurstDtmf is used, if dtmf char is 'D' then it with '#'
- // since 'D' is used for SCH tone and for CDMA it has to be '#'.
- @Override
- public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
- Character c = dtmfString.charAt(0);
- if(dtmfString.length() == 1 && c == 'D') {
- dtmfString = c.toString();
- }
- super.sendBurstDtmf(dtmfString, on, off, onComplete);
- }
-
- // When OOS occurs, IMS registration may be still available so that IMS service
- // state is also in-service, then reports in-service to upper layer.
- // Add a precondition to merge IMS service so that notifies proper service state
- // after IMS changes RAT.
- @Override
- public ServiceState getServiceState() {
- if (mSST == null || mSST.mSS.getState() != ServiceState.STATE_IN_SERVICE) {
- // Ensure UE has IMS service capability, then merge IMS service state.
- // Video enabled includes WIFI video
- final boolean isImsEnabled = mImsPhone != null && (mImsPhone.isVolteEnabled()
- || mImsPhone.isVideoEnabled()
- || mImsPhone.isWifiCallingEnabled());
- if (isImsEnabled) {
- return ServiceState.mergeServiceStates(
- ((mSST == null) ? new ServiceState() : mSST.mSS),
- mImsPhone.getServiceState());
- }
- }
-
- if (mSST != null) {
- return mSST.mSS;
- } else {
- // avoid potential NPE in EmergencyCallHelper during Phone switch
- return new ServiceState();
- }
- }
-
- private void logd(String msg) {
- Rlog.d(LOG_TAG, "[" + mPhoneId +" ] " + msg);
- }
-
- private void loge(String msg) {
- Rlog.e(LOG_TAG, "[" + mPhoneId +" ] " + msg);
- }
-}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorMultiSimSettingController.java b/src/java/com/android/internal/telephony/vendor/VendorMultiSimSettingController.java
deleted file mode 100644
index 026b6f57dc..0000000000
--- a/src/java/com/android/internal/telephony/vendor/VendorMultiSimSettingController.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.vendor;
-
-import android.content.Context;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.telephony.SubscriptionInfo;
-import android.util.Log;
-import com.android.internal.telephony.GlobalSettingsHelper;
-import com.android.internal.telephony.MultiSimSettingController;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/*
- * Extending VendorMultiSimSettingController to override default
- * behavior for mobile data
- */
-public class VendorMultiSimSettingController extends MultiSimSettingController {
-
- private static final String LOG_TAG = "VendorMultiSimSettingController";
-
- public static MultiSimSettingController init(Context context, SubscriptionController sc) {
- synchronized (VendorMultiSimSettingController.class) {
- if (sInstance == null) {
- sInstance = new VendorMultiSimSettingController(context,
- SubscriptionController.getInstance());
- } else {
- Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
- }
- }
- return sInstance;
- }
-
- private VendorMultiSimSettingController(Context context, SubscriptionController sc) {
- super(context, sc);
- }
-
- public static VendorMultiSimSettingController getInstance() {
- return (VendorMultiSimSettingController)sInstance;
- }
-
- @Override
- protected void disableDataForNonDefaultNonOpportunisticSubscriptions() {
- log("disableDataForNonDefaultNonOpportunisticSubscriptions - do nothing");
- }
-
- protected synchronized void onUserDataEnabled(int subId, boolean enable) {
- log("onUserDataEnabled");
- // Make sure MOBILE_DATA of subscriptions in same group are synced.
- setUserDataEnabledForGroup(subId, enable);
- }
-
- /**
- * Make sure MOBILE_DATA of subscriptions in the same group with the subId
- * are synced.
- */
- @Override
- protected synchronized void setUserDataEnabledForGroup(int subId, boolean enable) {
- log("setUserDataEnabledForGroup subId " + subId + " enable " + enable);
- List<SubscriptionInfo> infoList = mSubController.getSubscriptionsInGroup(
- mSubController.getGroupUuid(subId), mContext.getOpPackageName(),
- null);
-
- if (infoList == null) return;
-
- for (SubscriptionInfo info : infoList) {
- int currentSubId = info.getSubscriptionId();
- if (currentSubId == subId) continue;
- // TODO: simplify when setUserDataEnabled becomes singleton
- if (mSubController.isActiveSubId(currentSubId)) {
- // For active subscription, call setUserDataEnabled through DataEnabledSettings.
- Phone phone = PhoneFactory.getPhone(mSubController.getPhoneId(currentSubId));
- if (phone != null) {
- phone.getDataEnabledSettings().setUserDataEnabled(enable);
- }
- } else {
- // For inactive subscription, directly write into global settings.
- GlobalSettingsHelper.setBoolean(
- mContext, Settings.Global.MOBILE_DATA, currentSubId, enable);
- }
- }
- }
-
- @Override
- protected void updateDefaults() {
- log("updateDefaults");
-
- }
-
- protected void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorPhoneSwitcher.java b/src/java/com/android/internal/telephony/vendor/VendorPhoneSwitcher.java
deleted file mode 100644
index d12ea33454..0000000000
--- a/src/java/com/android/internal/telephony/vendor/VendorPhoneSwitcher.java
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.vendor;
-
-import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static android.telephony.TelephonyManager.RADIO_POWER_UNAVAILABLE;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.os.AsyncResult;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Registrant;
-import android.os.SystemProperties;
-import android.telephony.data.ApnSetting;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
-
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.dataconnection.DcRequest;
-import com.android.internal.telephony.dataconnection.DataEnabledSettings;
-import com.android.internal.telephony.GsmCdmaCall;
-import com.android.internal.telephony.imsphone.ImsPhone;
-import com.android.internal.telephony.imsphone.ImsPhoneCall;
-import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.PhoneSwitcher;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.TelephonyIntents;
-
-import java.lang.Integer;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.List;
-import android.net.StringNetworkSpecifier;
-import android.net.NetworkSpecifier;
-
-public class VendorPhoneSwitcher extends PhoneSwitcher {
-
- private final int MAX_CONNECT_FAILURE_COUNT = 5;
- private final int[] mRetryArray = new int []{5,10,20,40,60};
- private int[] mAllowDataFailure;
- private boolean[] mDdsRequestSent;
- private boolean mManualDdsSwitch = false;
- private int mDefaultDataPhoneId = -1;
- private String [] mSimStates;
- private List<Integer> mNewActivePhones;
- private boolean mWaitForDetachResponse = false;
- private DdsSwitchState mDdsSwitchState = DdsSwitchState.NONE;
- private final int USER_INITIATED_SWITCH = 0;
- private final int NONUSER_INITIATED_SWITCH = 1;
- private final String PROPERTY_TEMP_DDSSWITCH = "persist.vendor.radio.enable_temp_dds";
- private final GsmCdmaCall[] mFgCsCalls;
- private final GsmCdmaCall[] mBgCsCalls;
- private final GsmCdmaCall[] mRiCsCalls;
- private final ImsPhone[] mImsPhones;
- private final ImsPhoneCall[] mFgImsCalls;
- private final ImsPhoneCall[] mBgImsCalls;
- private final ImsPhoneCall[] mRiImsCalls;
-
- private final int EVENT_ALLOW_DATA_FALSE_RESPONSE = 201;
- private final int EVENT_ALLOW_DATA_TRUE_RESPONSE = 202;
- private final int EVENT_DDS_SWITCH_RESPONSE = 203;
- private final int EVENT_PREFERRED_SUB_VALID = 204;
-
- private enum DdsSwitchState {
- NONE, REQUIRED, DONE
- }
-
- public VendorPhoneSwitcher(int maxActivePhones, Context context, Looper looper) {
- super (maxActivePhones, context, looper);
- mAllowDataFailure = new int[mActiveModemCount];
- mDdsRequestSent = new boolean[mActiveModemCount];
- mSimStates = new String[mActiveModemCount];
- IntentFilter filter = new IntentFilter();
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- mContext.registerReceiver(mSimStateIntentReceiver, filter);
-
- mImsPhones = new ImsPhone[mActiveModemCount];
- mFgCsCalls = new GsmCdmaCall[mActiveModemCount];
- mBgCsCalls = new GsmCdmaCall[mActiveModemCount];
- mRiCsCalls = new GsmCdmaCall[mActiveModemCount];
- mFgImsCalls = new ImsPhoneCall[mActiveModemCount];
- mBgImsCalls = new ImsPhoneCall[mActiveModemCount];
- mRiImsCalls = new ImsPhoneCall[mActiveModemCount];
-
- for (int i=0; i < mActiveModemCount; i++) {
- if (PhoneFactory.getPhone(i) != null) {
- mFgCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getForegroundCall();
- mBgCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getBackgroundCall();
- mRiCsCalls[i] = (GsmCdmaCall) PhoneFactory.getPhone(i).getRingingCall();
- }
- mImsPhones[i] = (ImsPhone)PhoneFactory.getPhone(i).getImsPhone();
- if (mImsPhones[i] != null) {
- mFgImsCalls[i] = mImsPhones[i].getForegroundCall();
- mBgImsCalls[i] = mImsPhones[i].getBackgroundCall();
- mRiImsCalls[i] = mImsPhones[i].getRingingCall();
- }
-
- mDdsRequestSent[i] = false;
- }
- }
-
- public static VendorPhoneSwitcher make(int maxActivePhones, Context context, Looper looper) {
- if (sPhoneSwitcher == null) {
- sPhoneSwitcher = new VendorPhoneSwitcher(maxActivePhones, context, looper);
- }
-
- return (VendorPhoneSwitcher)sPhoneSwitcher;
- }
-
- private BroadcastReceiver mSimStateIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
- String value = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
- SubscriptionManager.INVALID_PHONE_INDEX);
- log("mSimStateIntentReceiver: phoneId = " + phoneId + " value = " + value);
- if (SubscriptionManager.isValidPhoneId(phoneId)) {
- mSimStates[phoneId] = value;
- // If SIM is absent, allow DDS request always, which avoids DDS switch
- // can't be completed in the no-SIM case because the sent status of the
- // old preferred phone has no chance to reset in hot-swap
- if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(value)) {
- mDdsRequestSent[phoneId] = false;
- }
- }
-
- if (isSimReady(phoneId) && (getConnectFailureCount(phoneId) > 0)) {
- sendRilCommands(phoneId);
- }
- }
- }
- };
-
- @Override
- public void handleMessage(Message msg) {
- final int ddsSubId = mSubscriptionController.getDefaultDataSubId();
- final int ddsPhoneId = mSubscriptionController.getPhoneId(ddsSubId);
-
- log("handle event - " + msg.what);
- AsyncResult ar = null;
- switch (msg.what) {
- case EVENT_SUBSCRIPTION_CHANGED: {
- if (mHalCommandToUse == HAL_COMMAND_UNKNOWN) {
- log("EVENT_SUBSCRIPTION_CHANGED: update HAL command");
- mHalCommandToUse = mRadioConfig.isSetPreferredDataCommandSupported()
- ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA;
- }
- onEvaluate(REQUESTS_UNCHANGED, "subChanged");
- break;
- }
- case EVENT_PRECISE_CALL_STATE_CHANGED: {
- log("EVENT_PRECISE_CALL_STATE_CHANGED");
- if (!isAnyVoiceCallActiveOnDevice()) {
- for (int i = 0; i < mActiveModemCount; i++) {
- if ((getConnectFailureCount(i) > 0) &&
- isPhoneIdValidForRetry(i)) {
- sendRilCommands(i);
- break;
- }
- }
- }
- super.handleMessage(msg);
- break;
- }
- case EVENT_ALLOW_DATA_TRUE_RESPONSE: {
- log("EVENT_ALLOW_DATA_TRUE_RESPONSE");
- onDdsSwitchResponse(msg.arg1, (AsyncResult)msg.obj);
- break;
- }
- case EVENT_ALLOW_DATA_FALSE_RESPONSE: {
- log("EVENT_ALLOW_DATA_FALSE_RESPONSE");
- mWaitForDetachResponse = false;
- for (int phoneId : mNewActivePhones) {
- activate(phoneId);
- }
- if (mNewActivePhones.contains(ddsPhoneId)) {
- mManualDdsSwitch = false;
- }
- break;
- }
- case EVENT_DDS_SWITCH_RESPONSE: {
- log("EVENT_DDS_SWITCH_RESPONSE");
- onDdsSwitchResponse(msg.arg1, (AsyncResult)msg.obj);
- break;
- }
- case EVENT_PREFERRED_SUB_VALID: {
- log("EVENT_PREFERRED_SUB_VALID");
- notifyDdsSwitchDone();
- break;
- }
- default:
- super.handleMessage(msg);
- }
- }
-
- private boolean isSimReady(int phoneId) {
- if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
- return false;
- }
-
- if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(mSimStates[phoneId]) ||
- IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(mSimStates[phoneId]) ||
- IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(mSimStates[phoneId])) {
- log("SIM READY for phoneId: " + phoneId);
- return true;
- } else {
- return false;
- }
- }
-
- @Override
- protected boolean onEvaluate(boolean requestsChanged, String reason) {
- StringBuilder sb = new StringBuilder(reason);
-
- boolean diffDetected = requestsChanged;
-
- // Check if user setting of default non-opportunistic data sub is changed.
- final int primaryDataSubId = mSubscriptionController.getDefaultDataSubId();
- final int ddsPhoneId = mSubscriptionController.getPhoneId(primaryDataSubId);
- if (primaryDataSubId != mPrimaryDataSubId) {
- sb.append(" mPrimaryDataSubId ").append(mPrimaryDataSubId).append("->")
- .append(primaryDataSubId);
- mManualDdsSwitch = true;
- mPrimaryDataSubId = primaryDataSubId;
- }
-
- // Check to see if there is any active subscription on any phone
- boolean hasAnyActiveSubscription = false;
- boolean hasSubRefreshedOnThePreferredPhone = false;
-
- // Check if phoneId to subId mapping is changed.
- for (int i = 0; i < mActiveModemCount; i++) {
- int sub = mSubscriptionController.getSubIdUsingPhoneId(i);
-
- if (SubscriptionManager.isValidSubscriptionId(sub)) hasAnyActiveSubscription = true;
-
- if (sub != mPhoneSubscriptions[i]) {
- sb.append(" phone[").append(i).append("] ").append(mPhoneSubscriptions[i]);
- sb.append("->").append(sub);
- if (SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get())
- && mPhoneSubscriptions[i] == mPreferredDataSubId.get()) {
- sb.append("sub refreshed");
- hasSubRefreshedOnThePreferredPhone = true;
- }
- mPhoneSubscriptions[i] = sub;
- diffDetected = true;
- }
- }
-
- if (!hasAnyActiveSubscription) {
- transitionToEmergencyPhone();
- } else {
- if (VDBG) log("Found an active subscription");
- }
- final boolean isOldPeferredDataSubValid =
- SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get());
- // Check if phoneId for preferred data is changed.
- int oldPreferredDataPhoneId = mPreferredDataPhoneId;
-
- // When there are no subscriptions, the preferred data phone ID is invalid, but we want
- // to keep a valid phoneId for Emergency, so skip logic that updates for preferred data
- // phone ID. Ideally there should be a single set of checks that evaluate the correct
- // phoneId on a service-by-service basis (EIMS being one), but for now... just bypass
- // this logic in the no-SIM case.
- if (hasAnyActiveSubscription) updatePreferredDataPhoneId();
-
- final boolean isPeferredDataSubValid =
- SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get());
-
- if(!isOldPeferredDataSubValid && isPeferredDataSubValid) {
- // To avoid race condition, I'd like to send a msg in OnEvalute
- // This is used to ensure informing active phones again after the preferred
- // SUB is valid
- sendEmptyMessage(EVENT_PREFERRED_SUB_VALID);
- }
-
- if (oldPreferredDataPhoneId != mPreferredDataPhoneId) {
- sb.append(" preferred phoneId ").append(oldPreferredDataPhoneId)
- .append("->").append(mPreferredDataPhoneId);
- if (SubscriptionManager.isValidPhoneId(oldPreferredDataPhoneId)) {
- mDdsRequestSent[oldPreferredDataPhoneId] = false;
- }
- mDdsSwitchState = DdsSwitchState.REQUIRED;
- diffDetected = true;
- } else if (hasSubRefreshedOnThePreferredPhone) {
- // Tell connectivity the real active data phone
- notifyPreferredDataSubIdChanged();
- }
-
- if (diffDetected) {
- log("evaluating due to " + sb.toString());
- if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) {
- // With HAL_COMMAND_PREFERRED_DATA, all phones are assumed to allow PS attach.
- // So marking all phone as active.
- for (int phoneId = 0; phoneId < mActiveModemCount; phoneId++) {
- activate(phoneId);
- }
- sendRilCommands(mPreferredDataPhoneId);
- } else {
- List<Integer> newActivePhones = new ArrayList<Integer>();
-
- for (DcRequest dcRequest : mPrioritizedDcRequests) {
- int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest,
- dcRequest.apnType);
- if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
- if (newActivePhones.contains(phoneIdForRequest)) continue;
- newActivePhones.add(phoneIdForRequest);
- if (newActivePhones.size() >= mMaxDataAttachModemCount) break;
- }
-
- if (VDBG) {
- log("default subId = " + mPrimaryDataSubId);
- log("preferred subId = " + mPreferredDataSubId.get());
- for (int i = 0; i < mActiveModemCount; i++) {
- log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
- }
- log(" newActivePhones:");
- for (Integer i : newActivePhones) log(" " + i);
- }
-
- mNewActivePhones = newActivePhones;
- for (int phoneId = 0; (phoneId < mActiveModemCount); phoneId++) {
- if (!newActivePhones.contains(phoneId)) {
- deactivate(phoneId);
- }
- }
- if (!mWaitForDetachResponse) {
- // only activate phones up to the limit
- final boolean activateDdsPhone = mNewActivePhones.contains(ddsPhoneId);
- if (activateDdsPhone && mManualDdsSwitch) {
- activate(ddsPhoneId);
- } else {
- for (int phoneId : newActivePhones) {
- activate(phoneId);
- }
- }
- if (activateDdsPhone) {
- mManualDdsSwitch = false;
- }
- }
- }
- }
-
- return diffDetected;
- }
-
- /* Determine the phone id on which PS attach needs to be done
- */
- protected int phoneIdForRequest(NetworkRequest netRequest, int apnType) {
- int subId = getSubIdFromNetworkSpecifier(netRequest.networkCapabilities
- .getNetworkSpecifier());
-
- if (subId == DEFAULT_SUBSCRIPTION_ID) return mPreferredDataPhoneId;
- if (subId == INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX;
-
- int preferredDataSubId = SubscriptionManager.isValidPhoneId(mPreferredDataPhoneId)
- ? mPhoneSubscriptions[mPreferredDataPhoneId] : INVALID_SUBSCRIPTION_ID;
- // Currently we assume multi-SIM devices will only support one Internet PDN connection. So
- // if Internet PDN is established on the non-preferred phone, it will interrupt
- // Internet connection on the preferred phone. So we only accept Internet request with
- // preferred data subscription or no specified subscription.
- if (netRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- && netRequest.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- && subId != preferredDataSubId && subId != mValidator.getSubIdInValidation()) {
- // Returning INVALID_PHONE_INDEX will result in netRequest not being handled.
- return INVALID_PHONE_INDEX;
- }
-
- // This is for Volte+PS case
- if ((ApnSetting.TYPE_IMS == apnType) && mManualDdsSwitch
- && mMaxDataAttachModemCount != mActiveModemCount) {
- subId = mPrimaryDataSubId;
- }
-
- // Try to find matching phone ID. If it doesn't exist, we'll end up returning INVALID.
- int phoneId = INVALID_PHONE_INDEX;
- for (int i = 0; i < mActiveModemCount; i++) {
- if (mPhoneSubscriptions[i] == subId) {
- phoneId = i;
- break;
- }
- }
- return phoneId;
- }
-
- protected boolean isUiccProvisioned(int phoneId) {
- boolean isUiccApplicationEnabled = true;
- // FIXME get the SubscriptionManager.UICC_APPLICATIONS_ENABLED value and use it here
- log("isUiccProvisioned: status= " + isUiccApplicationEnabled + " phoneid=" + phoneId);
- return mSubscriptionController.isActiveSubId(mPhoneSubscriptions[phoneId]) && isUiccApplicationEnabled;
- }
-
- @Override
- protected void deactivate(int phoneId) {
- PhoneState state = mPhoneStates[phoneId];
- if (state.active == false) {
- return;
- }
- state.active = false;
- log("deactivate " + phoneId);
- state.lastRequested = System.currentTimeMillis();
- if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) {
- if (mSubscriptionController.isActiveSubId(mPhoneSubscriptions[phoneId])) {
- PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(false,
- obtainMessage(EVENT_ALLOW_DATA_FALSE_RESPONSE));
- mWaitForDetachResponse = true;
- }
- }
- }
-
- @Override
- protected void activate(int phoneId) {
- PhoneState state = mPhoneStates[phoneId];
- if ((state.active == true) && !mManualDdsSwitch &&
- (getConnectFailureCount(phoneId) == 0)) return;
- state.active = true;
- log("activate " + phoneId);
- state.lastRequested = System.currentTimeMillis();
- if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) {
- PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(true,
- obtainMessage(EVENT_ALLOW_DATA_TRUE_RESPONSE, phoneId, 0));
- }
- }
-
- @Override
- protected void sendRilCommands(int phoneId) {
- if (!SubscriptionManager.isValidPhoneId(phoneId) || phoneId >= mActiveModemCount) {
- log("sendRilCommands: skip dds switch due to invalid phoneid=" + phoneId);
- return;
- }
-
- if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA || mHalCommandToUse == HAL_COMMAND_UNKNOWN) {
- PhoneFactory.getPhone(phoneId).mCi.setDataAllowed(isPhoneActive(phoneId),
- obtainMessage(isPhoneActive(phoneId) ? EVENT_ALLOW_DATA_TRUE_RESPONSE
- : EVENT_ALLOW_DATA_FALSE_RESPONSE, phoneId, 0));
- } else if (phoneId == mPreferredDataPhoneId) {
- if (!mDdsRequestSent[phoneId]) {
- // Only setPreferredDataModem if the phoneId equals to current mPreferredDataPhoneId
- log("sendRilCommands: setPreferredDataModem - phoneId: " + phoneId);
- mRadioConfig.setPreferredDataModem(phoneId,
- obtainMessage(EVENT_DDS_SWITCH_RESPONSE, phoneId, 0));
- mDdsRequestSent[phoneId] = true;
- } else {
- log("sendRilCommands: setPreferredDataModem request already sent on phoneId: " +
- phoneId);
- }
- }
- }
-
- /*
- * Method to check if any of the calls are started
- */
- @Override
- protected boolean isPhoneInVoiceCall(Phone phone) {
- if (phone == null) {
- return false;
- }
- boolean dataDuringCallsEnabled = false;
- DataEnabledSettings dataEnabledSettings = phone.getDataEnabledSettings();
- if (dataEnabledSettings != null) {
- dataDuringCallsEnabled = dataEnabledSettings.isDataAllowedInVoiceCall();
- }
- if (!dataDuringCallsEnabled) {
- log("isPhoneInVoiceCall: dataDuringCallsEnabled=" + dataDuringCallsEnabled);
- return false;
- }
- int phoneId = phone.getPhoneId();
- return (mFgCsCalls[phoneId].getState().isAlive() ||
- mBgCsCalls[phoneId].getState().isAlive() ||
- mRiCsCalls[phoneId].getState().isAlive() ||
- mFgImsCalls[phoneId].getState().isAlive() ||
- mBgImsCalls[phoneId].getState().isAlive() ||
- mRiImsCalls[phoneId].getState().isAlive());
- }
-
- private void resetConnectFailureCount(int phoneId) {
- mAllowDataFailure[phoneId] = 0;
- }
-
- private void incConnectFailureCount(int phoneId) {
- mAllowDataFailure[phoneId]++;
- }
-
- @VisibleForTesting
- public int getConnectFailureCount(int phoneId) {
- return mAllowDataFailure[phoneId];
- }
-
- private void handleConnectMaxFailure(int phoneId) {
- resetConnectFailureCount(phoneId);
- int ddsSubId = mSubscriptionController.getDefaultDataSubId();
- int ddsPhoneId = mSubscriptionController.getPhoneId(ddsSubId);
- if (SubscriptionManager.isValidPhoneId(ddsPhoneId) && phoneId != ddsPhoneId) {
- log("ALLOW_DATA retries exhausted on phoneId = " + phoneId);
- enforceDds(ddsPhoneId);
- }
- }
-
- private void enforceDds(int phoneId) {
- int[] subId = mSubscriptionController.getSubId(phoneId);
- log("enforceDds: subId = " + subId[0]);
- mSubscriptionController.setDefaultDataSubId(subId[0]);
- }
-
- private boolean isAnyVoiceCallActiveOnDevice() {
- boolean ret = (CallManager.getInstance().getState() != PhoneConstants.State.IDLE);
- log("isAnyVoiceCallActiveOnDevice: " + ret);
- return ret;
- }
-
- private void onDdsSwitchResponse(int phoneId, AsyncResult ar) {
- if (ar.exception != null) {
- mDdsRequestSent[phoneId] = false;
- incConnectFailureCount(phoneId);
- log("Dds switch failed on phoneId = " + phoneId + ", failureCount = "
- + getConnectFailureCount(phoneId));
-
- if (isAnyVoiceCallActiveOnDevice()) {
- boolean isTempSwitchPropEnabled = SystemProperties.getBoolean(
- PROPERTY_TEMP_DDSSWITCH, false);
- int ddsPhoneId = mSubscriptionController.getPhoneId(
- mSubscriptionController.getDefaultDataSubId());
- log("onDdsSwitchResponse: isTempSwitchPropEnabled=" + isTempSwitchPropEnabled +
- ", ddsPhoneId=" + ddsPhoneId + ", mPreferredDataPhoneId=" +
- mPreferredDataPhoneId);
- if (isTempSwitchPropEnabled && (phoneId != ddsPhoneId) &&
- getConnectFailureCount(phoneId) < MAX_CONNECT_FAILURE_COUNT) {
- log("Retry Temporary DDS switch on phoneId:" + phoneId);
- sendRilCommands(phoneId);
- } else {
- /* Any DDS retry while voice call is active is in vain
- Wait for call to get disconnected */
- log("Wait for call end indication");
- }
- return;
- }
-
- if (!isSimReady(phoneId)) {
- /* If there is a attach failure due to sim not ready then
- hold the retry until sim gets ready */
- log("Wait for SIM to get READY");
- return;
- }
-
- int ddsSwitchFailureCount = getConnectFailureCount(phoneId);
- if (ddsSwitchFailureCount > MAX_CONNECT_FAILURE_COUNT) {
- handleConnectMaxFailure(phoneId);
- } else {
- int retryDelay = mRetryArray[ddsSwitchFailureCount - 1] * 1000;
- log("Scheduling DDS switch retry after: " + retryDelay);
- postDelayed(new Runnable() {
- @Override
- public void run() {
- log("Running DDS switch retry");
- if (isPhoneIdValidForRetry(phoneId)) {
- sendRilCommands(phoneId);
- } else {
- log("Abandon DDS switch retry");
- resetConnectFailureCount(phoneId);
- }
- }}, retryDelay);
- }
- } else {
- log("DDS switch success on phoneId = " + phoneId);
- resetConnectFailureCount(phoneId);
- if (mDdsSwitchState == DdsSwitchState.REQUIRED) {
- mDdsSwitchState = DdsSwitchState.DONE;
- }
- notifyDdsSwitchDone();
- }
- }
-
- private void notifyDdsSwitchDone() {
- log("notifyDdsSwitchDone on the preferred data SUB = " + mPreferredDataSubId.get()
- + " and the preferred phone ID = " + mPreferredDataPhoneId);
- // Notify all registrants.
- mActivePhoneRegistrants.notifyRegistrants();
- notifyPreferredDataSubIdChanged();
-
- if (mDdsSwitchState == DdsSwitchState.DONE
- && SubscriptionManager.isValidSubscriptionId(mPreferredDataSubId.get())) {
- mDdsSwitchState = mDdsSwitchState.NONE;
- Intent intent = new Intent(
- "org.codeaurora.intent.action.ACTION_DDS_SWITCH_DONE");
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mPreferredDataSubId.get());
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- log("Broadcast dds switch done intent on " + mPreferredDataSubId.get());
- mContext.sendBroadcast(intent);
- }
- }
-
- private boolean isPhoneIdValidForRetry(int phoneId) {
- boolean isValid = false;
- int phoneIdForRequest = INVALID_PHONE_INDEX;
- int ddsPhoneId = mSubscriptionController.getPhoneId(
- mSubscriptionController.getDefaultDataSubId());
- if (ddsPhoneId != INVALID_PHONE_INDEX && ddsPhoneId == phoneId) {
- isValid = true;
- } else {
- if (mPrioritizedDcRequests.size() > 0) {
- for (int i = 0; i < mMaxDataAttachModemCount; i++) {
- DcRequest dcRequest = mPrioritizedDcRequests.get(i);
- if (dcRequest != null) {
- phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest,
- dcRequest.apnType);
- if (phoneIdForRequest == phoneId) {
- isValid = true;
- break;
- }
- }
- }
- }
- }
- return isValid;
- }
-
- /*
- * Returns true if mPhoneIdInVoiceCall is set for active calls
- */
- private boolean isCallInProgress() {
- return SubscriptionManager.isValidPhoneId(mPhoneIdInVoiceCall);
- }
-}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java b/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java
deleted file mode 100644
index de9d42cc6d..0000000000
--- a/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.vendor;
-
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.os.AsyncResult;
-import android.os.Message;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.ServiceStateTracker;
-
-public class VendorServiceStateTracker extends ServiceStateTracker {
- private static final String LOG_TAG = "VendorServiceStateTracker";
- private static final boolean DBG = true;
- private static final boolean VDBG = false; // STOPSHIP if true
- private static final String ACTION_MANAGED_ROAMING_IND =
- "android.intent.action.ACTION_MANAGED_ROAMING_IND";
-
- public VendorServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
- super(phone,ci);
- }
-
- @Override
- protected void handlePollStateResultMessage(int what, AsyncResult ar) {
- switch (what) {
- case EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION: {
- super.handlePollStateResultMessage(what, ar);
- if (mPhone.isPhoneTypeGsm()) {
- NetworkRegistrationInfo regStates = (NetworkRegistrationInfo) ar.result;
- int regState = regStates.getRegistrationState();
-
- if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED) {
- int rejCode = regStates.getRejectCause();
- // Check if rejCode is "Persistent location update reject",
- if (rejCode == 10) {
- log(" Posting Managed roaming intent sub = "
- + mPhone.getSubId());
- try {
- Intent intent =
- new Intent(ACTION_MANAGED_ROAMING_IND);
- // component would display Dialog to perform Manual scan
- // if current Network selection Mode is Manual.
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY,
- mPhone.getSubId());
- mPhone.getContext().startActivity(intent);
- } catch (ActivityNotFoundException e) {
- loge("unable to start activity: " + e);
- }
- }
- }
- }
- break;
- }
-
- default:
- super.handlePollStateResultMessage(what, ar);
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == EVENT_RADIO_STATE_CHANGED) {
- if (mPhone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
- setPowerStateToDesired();
- log("Trigger as manual polling");
- pollState();
- } else {
- super.handleMessage(msg);
- }
- } else {
- super.handleMessage(msg);
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java b/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java
deleted file mode 100644
index 0f8b98787e..0000000000
--- a/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.vendor;
-
-import android.Manifest;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Registrant;
-import android.os.RegistrantList;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.Phone;
-
-import java.util.Iterator;
-import java.util.List;
-
-/*
- * Extending SubscriptionController here:
- * To implement fall back of sms/data user preferred subId value to next
- * available subId when current preferred SIM deactivated or removed.
- */
-public class VendorSubscriptionController extends SubscriptionController {
- static final String LOG_TAG = "VendorSubscriptionController";
- private static final boolean DBG = true;
- private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
-
- private static int sNumPhones;
-
- private static final int EVENT_UICC_APPS_ENABLEMENT_DONE = 101;
-
- private static final int PROVISIONED = 1;
- private static final int NOT_PROVISIONED = 0;
-
- private TelecomManager mTelecomManager;
- private TelephonyManager mTelephonyManager;
-
- private RegistrantList mAddSubscriptionRecordRegistrants = new RegistrantList();
-
- private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
- /**
- * This intent would be broadcasted when a subId/slotId pair added to the
- * sSlotIdxToSubId hashmap.
- */
- private static final String ACTION_SUBSCRIPTION_RECORD_ADDED =
- "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED";
-
- public static VendorSubscriptionController init(Context c) {
- synchronized (VendorSubscriptionController.class) {
- if (sInstance == null) {
- sInstance = new VendorSubscriptionController(c);
- } else {
- Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
- }
- return (VendorSubscriptionController)sInstance;
- }
- }
-
- public static VendorSubscriptionController getInstance() {
- if (sInstance == null) {
- Log.wtf(LOG_TAG, "getInstance null");
- }
-
- return (VendorSubscriptionController)sInstance;
- }
-
- protected VendorSubscriptionController(Context c) {
- super(c);
- if (DBG) logd(" init by Context");
-
- mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- sNumPhones = TelephonyManager.getDefault().getPhoneCount();
- }
-
- public void registerForAddSubscriptionRecord(Handler handler, int what, Object obj) {
- Registrant r = new Registrant(handler, what, obj);
- synchronized (mAddSubscriptionRecordRegistrants) {
- mAddSubscriptionRecordRegistrants.add(r);
- List<SubscriptionInfo> subInfoList =
- getActiveSubscriptionInfoList(mContext.getOpPackageName());
- if (subInfoList != null) {
- r.notifyRegistrant();
- }
- }
- }
-
- public void unregisterForAddSubscriptionRecord(Handler handler) {
- synchronized (mAddSubscriptionRecordRegistrants) {
- mAddSubscriptionRecordRegistrants.remove(handler);
- }
- }
-
- @Override
- public int addSubInfoRecord(String iccId, int slotIndex) {
- logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "]");
- return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
- }
-
- @Override
- public int addSubInfo(String uniqueId, String displayName, int slotIndex,
- int subscriptionType) {
-
- int retVal = super.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType);
-
- int[] subId = getSubId(slotIndex);
- if (subId != null && (subId.length > 0)) {
- // When a new entry added in sSlotIdxToSubId for slotId, broadcast intent
- logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "] = " + subId[0]);
- mAddSubscriptionRecordRegistrants.notifyRegistrants(
- new AsyncResult(null, slotIndex, null));
- Intent intent = new Intent(ACTION_SUBSCRIPTION_RECORD_ADDED);
- SubscriptionManager.putPhoneIdAndSubIdExtra(intent, slotIndex, subId[0]);
- mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
- }
- return retVal;
- }
-
- @Override
- public int setUiccApplicationsEnabled(boolean enabled, int subId) {
- if (DBG) logd("[setUiccApplicationsEnabled]+ enabled:" + enabled + " subId:" + subId);
-
- ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.UICC_APPLICATIONS_ENABLED, enabled);
-
- int result = mContext.getContentResolver().update(
- SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);
-
- // Refresh the Cache of Active Subscription Info List
- refreshCachedActiveSubscriptionInfoList();
-
- notifySubscriptionInfoChanged();
-
- if (isActiveSubId(subId)) {
- Phone phone = PhoneFactory.getPhone(getPhoneId(subId));
- phone.enableUiccApplications(enabled, Message.obtain(
- mSubscriptionHandler, EVENT_UICC_APPS_ENABLEMENT_DONE, enabled));
- }
-
- return result;
- }
-
- /*
- * Handler Class
- */
- private Handler mSubscriptionHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_UICC_APPS_ENABLEMENT_DONE: {
- logd("EVENT_UICC_APPS_ENABLEMENT_DONE");
- AsyncResult ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- logd("Received exception: " + ar.exception);
- return;
- }
- updateUserPreferences();
- break;
- }
- }
- }
- };
-
- protected boolean isRadioAvailableOnAllSubs() {
- for (int i = 0; i < sNumPhones; i++) {
- if (PhoneFactory.getPhone(i).mCi != null &&
- PhoneFactory.getPhone(i).mCi.getRadioState() ==
- TelephonyManager.RADIO_POWER_UNAVAILABLE) {
- return false;
- }
- }
- return true;
- }
-
- protected boolean isShuttingDown() {
- for (int i = 0; i < sNumPhones; i++) {
- if (PhoneFactory.getPhone(i) != null &&
- PhoneFactory.getPhone(i).isShuttingDown()) return true;
- }
- return false;
- }
-
- public boolean isRadioInValidState() {
-
- // Radio Unavailable, do not updateUserPrefs. As this may happened due to SSR or RIL Crash.
- if (!isRadioAvailableOnAllSubs()) {
- logd(" isRadioInValidState, radio not available");
- return false;
- }
-
- //Do not updateUserPrefs when Shutdown is in progress
- if (isShuttingDown()) {
- logd(" isRadioInValidState: device shutdown in progress ");
- return false;
- }
- return true;
- }
-
- // If any of the voice/data/sms preference related SIM
- // deactivated/re-activated this will update the preference
- // with next available/activated SIM.
- public void updateUserPreferences() {
- SubscriptionInfo mNextActivatedSub = null;
- int activeCount = 0;
- if (!isRadioInValidState()) {
- logd("Radio is in Invalid state, Ignore Updating User Preference!!!");
- return;
- }
- List<SubscriptionInfo> sil = getActiveSubscriptionInfoList(mContext.getOpPackageName());
- // If list of active subscriptions empty OR non of the SIM provisioned
- // clear defaults preference of voice/sms/data.
- if (sil == null || sil.size() < 1) {
- logi("updateUserPreferences: Subscription list is empty");
- return;
- }
-
- // Do not fallback to next available sub if AOSP feature
- // "User choice of selecting data/sms fallback preference" enabled.
- if (SystemProperties.getBoolean("persist.vendor.radio.aosp_usr_pref_sel", false)) {
- logi("updateUserPreferences: AOSP user preference option enabled ");
- return;
- }
-
- final int defaultVoiceSubId = getDefaultVoiceSubId();
- final int defaultDataSubId = getDefaultDataSubId();
- final int defaultSmsSubId = getDefaultSmsSubId();
-
- //Get num of activated Subs and next available activated sub info.
- for (SubscriptionInfo subInfo : sil) {
- if (isUiccProvisioned(subInfo.getSimSlotIndex())) {
- activeCount++;
- if (mNextActivatedSub == null) mNextActivatedSub = subInfo;
- }
- }
- logd("updateUserPreferences:: active sub count = " + activeCount + " dds = "
- + defaultDataSubId + " voice = " + defaultVoiceSubId +
- " sms = " + defaultSmsSubId);
-
- // If active SUB count is 1, Always Ask Prompt to be disabled and
- // preference fallback to the next available SUB.
- if (activeCount == 1) {
- setSmsPromptEnabled(false);
- }
-
- // TODO Set all prompt options to false ?
-
- // in Single SIM case or if there are no activated subs available, no need to update. EXIT.
- if ((mNextActivatedSub == null) || (getActiveSubInfoCountMax() == 1)) return;
-
- handleDataPreference(mNextActivatedSub.getSubscriptionId());
-
- if ((defaultSmsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
- || activeCount == 1) && !isSubProvisioned(defaultSmsSubId)) {
- setDefaultSmsSubId(mNextActivatedSub.getSubscriptionId());
- }
-
- if ((defaultVoiceSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
- || activeCount == 1) && !isSubProvisioned(defaultVoiceSubId)) {
- setDefaultVoiceSubId(mNextActivatedSub.getSubscriptionId());
- }
-
- // voice preference is handled in such a way that
- // 1. Whenever current Sub is deactivated or removed It fall backs to
- // next available Sub.
- // 2. When device is flashed for the first time, initial voice preference
- // would be set to always ask.
- if (!isNonSimAccountFound() && activeCount == 1) {
- final int subId = mNextActivatedSub.getSubscriptionId();
- PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(subId);
- logi("set default phoneaccount to " + subId);
- mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
- }
- if (!isSubProvisioned(sDefaultFallbackSubId.get())) {
- setDefaultFallbackSubId(mNextActivatedSub.getSubscriptionId(),
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
- }
-
- notifySubscriptionInfoChanged();
- logd("updateUserPreferences: after currentDds = " + getDefaultDataSubId() + " voice = " +
- getDefaultVoiceSubId() + " sms = " + getDefaultSmsSubId());
- }
-
- protected void handleDataPreference(int nextActiveSubId) {
- int userPrefDataSubId = getUserPrefDataSubIdFromDB();
- int currentDataSubId = getDefaultDataSubId();
-
- List<SubscriptionInfo> subInfoList =
- getActiveSubscriptionInfoList(mContext.getOpPackageName());
- if (subInfoList == null) {
- return;
- }
- boolean userPrefSubValid = false;
- for (SubscriptionInfo subInfo : subInfoList) {
- if (subInfo.getSubscriptionId() == userPrefDataSubId) {
- userPrefSubValid = true;
- }
- }
- logd("havePrefSub = " + userPrefSubValid + " user pref subId = "
- + userPrefDataSubId + " current dds " + currentDataSubId
- + " next active subId " + nextActiveSubId);
-
- // If earlier user selected DDS is now available, set that as DDS subId.
- if (userPrefSubValid && isSubProvisioned(userPrefDataSubId) &&
- (currentDataSubId != userPrefDataSubId)) {
- setDefaultDataSubId(userPrefDataSubId);
- } else if (!isSubProvisioned(currentDataSubId)) {
- setDefaultDataSubId(nextActiveSubId);
- }
-
- }
-
- protected boolean isUiccProvisioned(int slotId) {
-// return isSubscriptionEnabled();
- return true;
- }
-
- // This method returns true if subId and corresponding slotId is in valid
- // range and the Uicc card corresponds to this slot is provisioned.
- protected boolean isSubProvisioned(int subId) {
- boolean isSubIdUsable = SubscriptionManager.isUsableSubIdValue(subId);
-
- if (isSubIdUsable) {
- int slotId = getSlotIndex(subId);
- if (!SubscriptionManager.isValidSlotIndex(slotId)) {
- loge(" Invalid slotId " + slotId + " or subId = " + subId);
- isSubIdUsable = false;
- } else {
- if (!isUiccProvisioned(slotId)) {
- isSubIdUsable = false;
- }
- loge("isSubProvisioned, state = " + isSubIdUsable + " subId = " + subId);
- }
- }
- return isSubIdUsable;
- }
-
- /* Returns User SMS Prompt property, enabled or not */
- public boolean isSmsPromptEnabled() {
- boolean prompt = false;
- int value = 0;
- try {
- value = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_SMS_PROMPT);
- } catch (SettingNotFoundException snfe) {
- loge("Settings Exception Reading Dual Sim SMS Prompt Values");
- }
- prompt = (value == 0) ? false : true ;
- if (VDBG) logd("SMS Prompt option:" + prompt);
-
- return prompt;
- }
-
- /*Sets User SMS Prompt property, enable or not */
- public void setSmsPromptEnabled(boolean enabled) {
- enforceModifyPhoneState("setSMSPromptEnabled");
- int value = (enabled == false) ? 0 : 1;
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.MULTI_SIM_SMS_PROMPT, value);
- logi("setSMSPromptOption to " + enabled);
- }
-
- protected boolean isNonSimAccountFound() {
- final Iterator<PhoneAccountHandle> phoneAccounts =
- mTelecomManager.getCallCapablePhoneAccounts().listIterator();
-
- while (phoneAccounts.hasNext()) {
- final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
- final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
- if (mTelephonyManager.getSubIdForPhoneAccount(phoneAccount) ==
- SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- logi("Other than SIM account found. ");
- return true;
- }
- }
- logi("Other than SIM account not found ");
- return false;
- }
-
- protected PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
- final Iterator<PhoneAccountHandle> phoneAccounts =
- mTelecomManager.getCallCapablePhoneAccounts().listIterator();
-
- while (phoneAccounts.hasNext()) {
- final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
- final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
- if (subId == mTelephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
- return phoneAccountHandle;
- }
- }
-
- return null;
- }
-
- protected int getUserPrefDataSubIdFromDB() {
- return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- }
-
- private void logd(String string) {
- if (DBG) Rlog.d(LOG_TAG, string);
- }
-
- private void logi(String string) {
- Rlog.i(LOG_TAG, string);
- }
-
- private void loge(String string) {
- Rlog.e(LOG_TAG, string);
- }
-}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorSubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/vendor/VendorSubscriptionInfoUpdater.java
deleted file mode 100644
index 2060553eac..0000000000
--- a/src/java/com/android/internal/telephony/vendor/VendorSubscriptionInfoUpdater.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.vendor;
-
-import android.content.Context;
-import android.os.Looper;
-import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.SubscriptionController;
-import com.android.internal.telephony.SubscriptionInfoUpdater;
-import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccSlot;
-import com.android.internal.telephony.uicc.IccUtils;
-
-
-/**
- * To reduce delay in SubInfo records availability to clients, add subInfo record
- * to table without waiting for SIM state moves to LOADED.
- */
-public class VendorSubscriptionInfoUpdater extends SubscriptionInfoUpdater {
- private static final String LOG_TAG = "VendorSubscriptionInfoUpdater";
-
- private static final String ICCID_STRING_FOR_NO_SIM = "";
- private static Context sContext = null;
-
- protected boolean[] mIsRecordUpdateRequired;
- protected static VendorSubscriptionInfoUpdater sInstance = null;
- private static final int SUPPORTED_MODEM_COUNT = TelephonyManager.getDefault()
- .getSupportedModemCount();
-
- static VendorSubscriptionInfoUpdater init(Looper looper, Context context,
- CommandsInterface[] ci) {
- synchronized (VendorSubscriptionInfoUpdater.class) {
- if (sInstance == null) {
- sInstance = new VendorSubscriptionInfoUpdater(looper, context, ci);
- } else {
- Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
- }
- return sInstance;
- }
- }
-
- public static VendorSubscriptionInfoUpdater getInstance() {
- if (sInstance == null) {
- Log.wtf(LOG_TAG, "getInstance null");
- }
- return sInstance;
- }
-
- protected VendorSubscriptionInfoUpdater(Looper looper, Context context,
- CommandsInterface[] ci) {
- super(looper, context, ci);
- sContext = context;
- mIsRecordUpdateRequired = new boolean[SUPPORTED_MODEM_COUNT];
-
- for (int index = 0; index < SUPPORTED_MODEM_COUNT; index++) {
- mIsRecordUpdateRequired[index] = true;
- }
- }
-
- @Override
- protected void handleSimReady(int phoneId) {
- List<Integer> cardIds = new ArrayList<>();
- Rlog.d(LOG_TAG, "handleSimReady: phoneId: " + phoneId);
-
- if (sIccId[phoneId] != null && sIccId[phoneId].equals(ICCID_STRING_FOR_NO_SIM)) {
- Rlog.d(LOG_TAG, " SIM" + (phoneId + 1) + " hot plug in");
- sIccId[phoneId] = null;
- }
- UiccSlot uiccSlot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
- if (uiccSlot == null) {
- Rlog.d(LOG_TAG, "handleSimReady: uiccSlot null");
- return;
- }
-
- String iccId = uiccSlot.getIccId();
- if (IccUtils.stripTrailingFs(iccId) == null) {
- Rlog.d(LOG_TAG, "handleSimReady: IccID null");
- return;
- }
- sIccId[phoneId] = IccUtils.stripTrailingFs(iccId);
-
- updateSubscriptionInfoByIccId(phoneId, true /* updateEmbeddedSubs */);
-
- cardIds.add(getCardIdFromPhoneId(phoneId));
- updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
- if (hasChanges) {
- SubscriptionController.getInstance().notifySubscriptionInfoChanged();
- }
- });
- broadcastSimStateChanged(phoneId, IccCardConstants.INTENT_VALUE_ICC_READY, null);
- broadcastSimCardStateChanged(phoneId, TelephonyManager.SIM_STATE_PRESENT);
- broadcastSimApplicationStateChanged(phoneId, TelephonyManager.SIM_STATE_NOT_READY);
- }
-
- @Override
- protected void handleSimLoaded(int phoneId) {
- // mIsRecordUpdateRequired set to false if sIccId has a valid Iccid to skip
- // adding subId once again from here.
- if ((sIccId[phoneId] != null) && (sIccId[phoneId] != ICCID_STRING_FOR_NO_SIM)) {
- mIsRecordUpdateRequired[phoneId] = false;
- }
- Rlog.d(LOG_TAG, "handleSimLoaded: phoneId: " + phoneId);
- super.handleSimLoaded(phoneId);
- }
-
- @Override
- synchronized protected void updateSubscriptionInfoByIccId(int phoneId,
- boolean updateEmbeddedSubs) {
-
- if (mIsRecordUpdateRequired[phoneId] == true) {
- super.updateSubscriptionInfoByIccId(phoneId, updateEmbeddedSubs);
- } else {
- Rlog.d(LOG_TAG, "Ignoring subscription update event " + phoneId);
- mIsRecordUpdateRequired[phoneId] = true;
- }
- }
-}
diff --git a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDataResetEventTracker.java b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDataResetEventTracker.java
deleted file mode 100644
index 492ffd1a53..0000000000
--- a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDataResetEventTracker.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.telephony.vendor.dataconnection;
-
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.TelephonyIntents;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-
-import android.telephony.CellLocation;
-import android.telephony.PhoneStateListener;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.gsm.GsmCellLocation;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import java.util.ArrayList;
-
-public class VendorDataResetEventTracker {
-
- public static interface ResetEventListener {
- public void onResetEvent(boolean retry);
- }
-
- private static final boolean DBG = true;
-
- private TelephonyManager mTelephonyManager = null;
- private GsmCellLocation mPreviousLocation = null;
- private PhoneStateListener mPhoneStateListener = null;
- private Context mContext = null;
- private Phone mPhone = null;
- private ResetEventListener mListener = null;
- private int mPreviousRAT = 0;
- private int mTransportType;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case DctConstants.EVENT_DATA_RAT_CHANGED: {
- AsyncResult ar = (AsyncResult) msg.obj;
- Pair<Integer, Integer> result = (Pair<Integer, Integer>) ar.result;
- if (result != null) {
- if (mPreviousRAT > 0 && result.second > 0
- && mPreviousRAT != result.second) {
- if (DBG) log("RAT CHANGED, " + mPreviousRAT
- + "->" + result.second);
- notifyResetEvent("DATA_RAT_CHANGED", false);
- }
- mPreviousRAT = result.second;
- }
- break;
- }
- case DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE: {
- if (DBG) log("EVENT_RADIO_OFF_OR_NOT_AVAILABLE");
- notifyResetEvent("RADIO_OFF_OR_NOT_AVAILABLE", false);
- break;
- }
- }
- }
- };
-
- private BroadcastReceiver mSimStateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
- final String stateExtra = intent
- .getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
- log("ACTION_SIM_STATE_CHANGED, action " + stateExtra);
- if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
- notifyResetEvent("SIM_STATE_ABSENT", false);
- }
- }
- }
- };
-
- public VendorDataResetEventTracker(int transportType, Phone phone,
- ResetEventListener listener) {
- if (DBG) log("VendorDataResetEventTracker constructor: " + this);
- mPhone = phone;
- mContext = mPhone.getContext();
- this.mListener = listener;
- mTransportType = transportType;
- mTelephonyManager = (TelephonyManager) mContext
- .getSystemService(Context.TELEPHONY_SERVICE);
- }
-
- /**
- * Register listener for RAU update and RAT change.
- */
- public void startResetEventTracker() {
- if (DBG) log("startResetEventTracker");
- mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(
- mTransportType, mHandler, DctConstants.EVENT_DATA_RAT_CHANGED, null);
- mPhone.mCi.registerForOffOrNotAvailable(mHandler,
- DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
- mContext.registerReceiver(mSimStateReceiver, new IntentFilter(
- TelephonyIntents.ACTION_SIM_STATE_CHANGED));
-
- CellLocation currentCellLocation = mPhone.getCellIdentity().asCellLocation();
- if (currentCellLocation instanceof GsmCellLocation) {
- mPreviousLocation = (GsmCellLocation) mPhone.getCellIdentity().asCellLocation();
- if (DBG) log("DataConnection mPreviousLocation : " + mPreviousLocation);
- }
- int ddsSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-
- if (mPhoneStateListener == null) {
- mPhoneStateListener = new PhoneStateListener() {
- public void onCellLocationChanged(CellLocation location) {
- if (DBG) log("DataConnection onCellLocationChanged : "
- + location);
-
- if (location instanceof GsmCellLocation) {
- GsmCellLocation currentLocation = (GsmCellLocation) location;
-
- if (mPreviousLocation != null
- && currentLocation != null) {
- if (mPreviousLocation.getCid() != currentLocation
- .getCid()
- || mPreviousLocation.getLac() != currentLocation
- .getLac()) {
- if (DBG) log("DataConnection location updated");
- notifyResetEvent("LOCATION_UPDATED", true);
- }
- }
- mPreviousLocation = currentLocation;
- }
- }
- };
- }
-
- mTelephonyManager.
- createForSubscriptionId(ddsSubId).
- listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
- }
-
- /**
- * Unregister for RAU update and RAT change.
- */
- public void stopResetEventTracker() {
- if (DBG) log("stopResetTimer");
- try {
- mPreviousRAT = 0;
- mPreviousLocation = null;
- if (mPhoneStateListener != null) {
- mTelephonyManager.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_NONE);
- }
- mPhone.getServiceStateTracker()
- .unregisterForDataRegStateOrRatChanged(mTransportType, mHandler);
- mPhone.mCi.unregisterForOffOrNotAvailable(mHandler);
- mContext.unregisterReceiver(mSimStateReceiver);
- } catch (Exception e) {
- if (DBG) log("error:" + e.getMessage());
- e.printStackTrace();
- }
- }
-
- public void dispose() {
- if (DBG) log("dispose");
- stopResetEventTracker();
- mTelephonyManager = null;
- }
-
- /**
- * notify the listener for reset event
- */
- private void notifyResetEvent(String reason, boolean retry) {
- if (DBG) log("notifyResetEvent: reason=" + reason + ", retry=" + retry);
- stopResetEventTracker();
- if (mListener != null) {
- mListener.onResetEvent(retry);
- }
- }
-
- private void log(String log) {
- Rlog.d("VendorDataResetEventTracker", log);
- }
-}
diff --git a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
deleted file mode 100644
index 2e256ff847..0000000000
--- a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.vendor.dataconnection;
-
-import android.app.AlertDialog;
-import android.view.WindowManager;
-
-import android.telephony.AccessNetworkConstants;
-import android.telephony.Annotation.DataFailureCause;
-import android.telephony.CarrierConfigManager;
-import android.telephony.DataFailCause;
-import android.telephony.data.ApnSetting;
-import android.telephony.Rlog;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.telephony.dataconnection.ApnContext;
-import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.AsyncChannel;
-
-import android.database.Cursor;
-import android.content.Context;
-import android.os.PersistableBundle;
-import android.provider.Telephony;
-
-import java.util.HashSet;
-import java.util.Iterator;
-
-public class VendorDcTracker extends DcTracker {
- private String LOG_TAG = "VendorDCT";
- private HashSet<String> mIccidSet = new HashSet<String>();
- private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
-
- // Maximum data reject count
- public static final int MAX_PDP_REJECT_COUNT = 3;
-
- // Data reset event tracker to know reset events.
- private VendorDataResetEventTracker mVendorDataResetEventTracker = null;
-
- // data reject dialog, made static because only one dialog object can be
- // used between multiple dataconnection objects.
- protected static AlertDialog mDataRejectDialog = null;
-
- //Store data reject cause for comparison
- private String mDataRejectReason = "NONE";
-
- //Store data reject count
- private int mDataRejectCount = 0;
-
- //Store data reject cause code
- private int mPdpRejectCauseCode = 0;
-
- // Constructor
- public VendorDcTracker(Phone phone, int transportType) {
- super(phone, transportType);
- mTransportType = transportType;
- LOG_TAG = LOG_TAG + "-" +
- ((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) ? "C" : "I");
- if (DBG) log(LOG_TAG + ".constructor");
- fillIccIdSet();
- }
-
- @Override
- protected boolean allowInitialAttachForOperator() {
- String iccId = mPhone.getIccSerialNumber();
- if (iccId != null) {
- Iterator<String> itr = mIccidSet.iterator();
- while (itr.hasNext()) {
- if (iccId.contains(itr.next())) {
- return false;
- }
- }
- }
- return true;
- }
-
- private void fillIccIdSet() {
- mIccidSet.add("8991840");
- mIccidSet.add("8991854");
- mIccidSet.add("8991855");
- mIccidSet.add("8991856");
- mIccidSet.add("8991857");
- mIccidSet.add("8991858");
- mIccidSet.add("8991859");
- mIccidSet.add("899186");
- mIccidSet.add("8991870");
- mIccidSet.add("8991871");
- mIccidSet.add("8991872");
- mIccidSet.add("8991873");
- mIccidSet.add("8991874");
- }
-
- @Override
- protected void onVoiceCallEnded() {
- if (DBG) log("onVoiceCallEnded");
- mInVoiceCall = false;
- if (isConnected()) {
- if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
- startNetStatPoll();
- startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
- mPhone.notifyAllActiveDataConnections();
- } else {
- // clean slate after call end.
- resetPollStats();
- }
- }
- //Allow data call retry only on DDS sub
- if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
- // reset reconnect timer
- setupDataOnAllConnectableApns(Phone.REASON_VOICE_CALL_ENDED, RetryFailures.ALWAYS);
-
- }
- }
-
- @Override
- protected void setupDataOnConnectableApn(ApnContext apnContext, String reason,
- RetryFailures retryFailures) {
- if (mPhone.getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_pdp_reject_enable_retry) &&
- mDataRejectCount > 0) {
- log("setupDataOnConnectableApn: data retry in progress, skip processing");
- } else {
- super.setupDataOnConnectableApn(apnContext, reason, retryFailures);
- }
- }
-
- @Override
- protected void onDataSetupComplete(ApnContext apnContext, boolean success, int cause,
- @RequestNetworkType int requestType) {
- boolean isPdpRejectConfigEnabled = mPhone.getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_pdp_reject_enable_retry);
- if (success) {
- if (isPdpRejectConfigEnabled) {
- handlePdpRejectCauseSuccess();
- }
- } else {
- mPdpRejectCauseCode = cause;
- }
-
- super.onDataSetupComplete(apnContext, success, cause, requestType);
- }
-
- @Override
- protected void onDataSetupCompleteError(ApnContext apnContext,
- @RequestNetworkType int requestType) {
- long delay = apnContext.getDelayForNextApn(mFailFast);
- if (mPhone.getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_pdp_reject_enable_retry)) {
- String reason = DataFailCause.toString(mPdpRejectCauseCode);
-
- if (isMatchingPdpRejectCause(reason)) {
- if (mVendorDataResetEventTracker == null) {
- mVendorDataResetEventTracker = new VendorDataResetEventTracker(mTransportType,
- mPhone, mResetEventListener);
- }
- if (mDataRejectCount == 0) {
- mVendorDataResetEventTracker.startResetEventTracker();
- }
- boolean isHandled = handlePdpRejectCauseFailure(reason);
-
- /* If MAX Reject count reached, display pop-up to user */
- if (MAX_PDP_REJECT_COUNT <= mDataRejectCount) {
- if (DBG) log("onDataSetupCompleteError: reached max retry count");
- displayPopup(mDataRejectReason);
- delay = -1;
- } else if (isHandled) {
- delay = mPhone.getContext().getResources().getInteger(
- com.android.internal.R.integer.config_pdp_reject_retry_delay_ms);
- if (DBG) log("onDataSetupCompleteError: delay from config: " + delay);
- }
- } else {
- if (DBG) log("onDataSetupCompleteError: reset reject count");
- resetDataRejectCounter();
- }
- }
-
- // Check if we need to retry or not.
- // TODO: We should support handover retry in the future.
- if (delay >= 0) {
- if (DBG) log("onDataSetupCompleteError: Try next APN. delay = " + delay);
- apnContext.setState(DctConstants.State.RETRYING);
- // Wait a bit before trying the next APN, so that
- // we're not tying up the RIL command channel
-
- startReconnect(delay, apnContext);
- } else {
- // If we are not going to retry any APN, set this APN context to failed state.
- // This would be the final state of a data connection.
- apnContext.setState(DctConstants.State.FAILED);
- mPhone.notifyDataConnection(apnContext.getApnType());
- apnContext.setDataConnection(null);
- if (DBG) log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay
- + ", requestType=" + requestTypeToString(requestType));
- }
- }
-
- /*
- * Reset data reject params on data call success
- */
- private void handlePdpRejectCauseSuccess() {
- if (mDataRejectCount > 0) {
- if (DBG) log("handlePdpRejectCauseSuccess: reset reject count");
- resetDataRejectCounter();
- }
- }
-
- /*
- * Process data failure if RAT is WCDMA
- * And if the failure cause matches one of the following cause codes:
- * 1. USER_AUTHENTICATION
- * 2. SERVICE_OPTION_NOT_SUBSCRIBED
- * 3. MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED
- */
- private boolean handlePdpRejectCauseFailure(String reason) {
- boolean handleFailure = false;
-
- // Check if data rat is WCDMA
- if (isWCDMA(getDataRat())) {
- if (DBG) log("handlePdpRejectCauseFailure: reason=" + reason +
- ", mDataRejectReason=" + mDataRejectReason);
- /*
- * If previously rejected code is not same as current data reject reason,
- * then reset the count and reset the reject reason
- */
- if (!reason.equalsIgnoreCase(mDataRejectReason)) {
- resetDataRejectCounter();
- }
-
- /*
- * If failure reason is USER_AUTHENTICATION or
- * SERVICE_OPTION_NOT_SUBSCRIBED or MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
- * increment counter and store reject cause
- */
- if (isMatchingPdpRejectCause(reason)) {
- mDataRejectCount++;
- mDataRejectReason = reason;
- if (DBG) log ("handlePdpRejectCauseFailure: DataRejectCount = " +
- mDataRejectCount);
- handleFailure = true;
- }
- } else {
- if (DBG) log("isPdpRejectCauseFailureHandled: DataConnection not on wcdma");
- resetDataRejectCounter();
- }
-
- return handleFailure;
- }
-
- /*
- * Data reset event listener. Dc will get get onResetEvent
- * whenever any data reset event occurs
- */
- private VendorDataResetEventTracker.ResetEventListener mResetEventListener =
- new VendorDataResetEventTracker.ResetEventListener() {
- @Override
- public void onResetEvent(boolean retry) {
- if (DBG) log("onResetEvent: retry=" + retry);
-
- //Dismiss dialog
- if (mDataRejectDialog != null && mDataRejectDialog.isShowing()) {
- if (DBG) log("onResetEvent: Dismiss dialog");
- mDataRejectDialog.dismiss();
- }
- mVendorDataResetEventTracker.stopResetEventTracker();
-
- for (ApnContext apnContext : mApnContexts.values()) {
- if (mDataRejectCount > 0) {
- if (DBG) log("onResetEvent: reset reject count=" + mDataRejectCount);
- resetDataRejectCounter();
- cancelReconnect(apnContext);
- if (retry) {
- if (DBG) log("onResetEvent: retry data call on apnContext=" + apnContext);
- sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
- }
- }
- }
- }
- };
-
- /**
- * This function will display the pdp reject message
- */
- private void displayPopup(String pdpRejectCause) {
- if (DBG) log("displayPopup : " + pdpRejectCause);
- String title = mPhone.getContext().getResources().
- getString(com.android.internal.R.string.config_pdp_reject_dialog_title);
- String message = null;
- if (pdpRejectCause.equalsIgnoreCase("USER_AUTHENTICATION")) {
- message = mPhone.getContext().getResources().
- getString(com.android.internal.R.string.config_pdp_reject_user_authentication_failed);
- } else if (pdpRejectCause.equalsIgnoreCase("SERVICE_OPTION_NOT_SUBSCRIBED")) {
- message = mPhone.getContext().getResources().getString(
- com.android.internal.R.string.config_pdp_reject_service_not_subscribed);
- } else if (pdpRejectCause.equalsIgnoreCase("MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED")) {
- message = mPhone.getContext().getResources().getString(
- com.android.internal.R.string.config_pdp_reject_multi_conn_to_same_pdn_not_allowed);
- }
- if (mDataRejectDialog == null || !mDataRejectDialog.isShowing()) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- mPhone.getContext());
- builder.setPositiveButton(android.R.string.ok, null);
- mDataRejectDialog = builder.create();
- }
- mDataRejectDialog.setMessage(message);
- mDataRejectDialog.setCanceledOnTouchOutside(false);
- mDataRejectDialog.setTitle(title);
- mDataRejectDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- mDataRejectDialog.show();
- }
-
- /*
- * returns true if data reject cause matches errors listed
- */
- private boolean isMatchingPdpRejectCause(String reason) {
- return reason.equalsIgnoreCase("USER_AUTHENTICATION") ||
- reason.equalsIgnoreCase("SERVICE_OPTION_NOT_SUBSCRIBED") ||
- reason.equalsIgnoreCase("MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
- }
-
- /**
- * returns true if radioTechnology is WCDMA rat, else false
- */
- private boolean isWCDMA(int radioTechnology) {
- return radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UMTS
- || radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA
- || radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA
- || radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSPA
- || radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP;
-
- }
-
- /*
- * Reset data reject count and reason
- */
- private void resetDataRejectCounter() {
- mDataRejectCount = 0;
- mDataRejectReason = "NONE";
- }
-
- @Override
- protected void log(String s) {
- Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
- }
-}
diff --git a/testing/Android.bp b/testing/Android.bp
new file mode 100644
index 0000000000..a5f9d7dce9
--- /dev/null
+++ b/testing/Android.bp
@@ -0,0 +1,27 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+ name: "telephony-common-testing",
+
+ srcs: ["**/*.java"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "guava",
+ "junit",
+ "mockito-target-minus-junit4",
+ "telephony-common",
+ "truth-prebuilt",
+ ],
+
+ sdk_version: "test_current",
+
+ visibility: [
+ "//cts/tests/tests/simphonebookprovider",
+ "//cts/tests/tests/simphonebookprovider/nosim",
+ "//frameworks/opt/telephony/tests",
+ "//packages/services/Telephony/tests",
+ ],
+}
diff --git a/testing/AndroidManifest.xml b/testing/AndroidManifest.xml
new file mode 100644
index 0000000000..6be665b2c6
--- /dev/null
+++ b/testing/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.telephony.testing">
+
+ <application>
+
+ </application>
+
+</manifest>
+
diff --git a/testing/src/com/android/internal/telephony/testing/CursorSubject.java b/testing/src/com/android/internal/telephony/testing/CursorSubject.java
new file mode 100644
index 0000000000..999e92cce0
--- /dev/null
+++ b/testing/src/com/android/internal/telephony/testing/CursorSubject.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.testing;
+
+import static com.google.common.truth.Truth.assertAbout;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.database.DatabaseUtils;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.IntegerSubject;
+import com.google.common.truth.IterableSubject;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Subject;
+import com.google.common.truth.Truth;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Truth subject for making assertions about {@link Cursor}s. */
+public final class CursorSubject extends Subject {
+
+ private final Cursor mActual;
+
+ private CursorSubject(FailureMetadata metadata, @Nullable Cursor actual) {
+ super(metadata, new StringableCursor(actual));
+ this.mActual = new StringableCursor(actual);
+ }
+
+ /** Returns the factory for this subject. */
+ public static Factory<CursorSubject, Cursor> cursors() {
+ return CursorSubject::new;
+ }
+
+ /** Starts an assertion. */
+ public static CursorSubject assertThat(Cursor cursor) {
+ return assertAbout(cursors()).that(cursor);
+ }
+
+ /** Asserts {@link Cursor#getCount()} has the specified value. */
+ public void hasCount(int count) {
+ check("getCount()").that(mActual.getCount()).isEqualTo(count);
+ }
+
+ /** Asserts {@link Cursor#getColumnNames()} match those specified. */
+ public void hasColumnNames(String... columnNames) {
+ check("getColumnNames()").that(mActual.getColumnNames()).asList()
+ .containsExactlyElementsIn(columnNames).inOrder();
+ }
+
+ /** Positions the cursor under test at the specified row to make an assertion about it. */
+ public CursorSubject atRow(int position) {
+ check("moveToPosition").that(mActual.moveToPosition(position)).isTrue();
+ return this;
+ }
+
+ /** Asserts that the row at the cursor's current position has the specified values. */
+ public CursorSubject hasRowValues(Object... values) {
+ check("getColumnCount()").that(mActual.getColumnCount()).isEqualTo(values.length);
+ ContentValues expectedValues = new ContentValues();
+ for (int i = 0; i < values.length; i++) {
+ expectedValues.put(mActual.getColumnName(i), values[i].toString());
+ }
+
+ ContentValues actualValues = new ContentValues();
+ DatabaseUtils.cursorRowToContentValues(mActual, actualValues);
+
+ check("Row: %s", mActual.getPosition()).that(actualValues).isEqualTo(expectedValues);
+ return this;
+ }
+
+ /** Asserts that the cursor has a single row with the specified values. */
+ public void hasSingleRow(Object... values) {
+ hasCount(1);
+ atRow(0).hasRowValues(values);
+ }
+
+ /**
+ * Asserts that the row at the cursor's current position has the specified value for the
+ * specified column.
+ */
+ public CursorSubject hasRowValue(String columnName, Object value) {
+ int index = mActual.getColumnIndex(columnName);
+ check("getColumnIndex()").that(index).isNotEqualTo(-1);
+
+ check("Row[%s]: %s", columnName, index).that(mActual.getString(index))
+ .isEqualTo(value.toString());
+ return this;
+ }
+
+ /** Starts an assertion about the value of the specified column for the current row. */
+ public IntegerSubject intField(String columnName) {
+ int index = mActual.getColumnIndex(columnName);
+ check("getColumnIndex()").that(index).isNotEqualTo(-1);
+ check("getType()").that(mActual.getType(index)).isEqualTo(Cursor.FIELD_TYPE_INTEGER);
+
+ return check("getInt()").that(mActual.getInt(index));
+ }
+
+ /** Starts an assertion about the value of the specified column for the current row. */
+ public StringSubject stringField(String columnName) {
+ int index = mActual.getColumnIndex(columnName);
+ check("getColumnIndex()").that(index).isNotEqualTo(-1);
+ check("getType()").that(mActual.getType(index)).isEqualTo(Cursor.FIELD_TYPE_STRING);
+
+ return check("getString()").that(mActual.getString(index));
+ }
+
+ /** Asserts that the cursor rows match the data specified. */
+ public void hasData(Object[][] rows) {
+ hasCount(rows.length);
+ for (int i = 0; i < rows.length; i++) {
+ atRow(i).hasRowValues(rows[i]);
+ }
+ }
+
+ /** Starts an assertion about the cursor's rows. */
+ public IterableSubject asLists() {
+ List<List<String>> result = new ArrayList<>();
+ mActual.moveToPosition(-1);
+ while (mActual.moveToNext()) {
+ List<String> row = new ArrayList<>();
+ for (int i = 0; i < mActual.getColumnCount(); i++) {
+ row.add(mActual.getString(i));
+ }
+ result.add(row);
+ }
+ return Truth.assertThat(result);
+ }
+
+ private static class StringableCursor extends CursorWrapper {
+
+ StringableCursor(Cursor cursor) {
+ super(cursor);
+ }
+
+ @Override
+ public String toString() {
+ return DatabaseUtils.dumpCursorToString(getWrappedCursor());
+ }
+ }
+
+}
diff --git a/testing/src/com/android/internal/telephony/testing/TelephonyAssertions.java b/testing/src/com/android/internal/telephony/testing/TelephonyAssertions.java
new file mode 100644
index 0000000000..d18a5cf30a
--- /dev/null
+++ b/testing/src/com/android/internal/telephony/testing/TelephonyAssertions.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Assert;
+
+/** Assertions used by telephony tests. */
+public class TelephonyAssertions {
+
+ /**
+ * Asserts that the provided action throws the specified exception.
+ *
+ * <p>TODO: Replace with org.junit.Assert.assertThrows when Android upgrades to JUnit 4.12
+ *
+ * @return the exception that was thrown when the assertion passes.
+ */
+ public static <T extends Throwable> T assertThrows(
+ Class<T> throwableClass, ThrowingRunnable action) {
+ try {
+ action.run();
+ } catch (Throwable t) {
+ assertThat(t).isInstanceOf(throwableClass);
+ return throwableClass.cast(t);
+ }
+ Assert.fail("Expected " + throwableClass.getSimpleName() + " but no exception was thrown");
+ // This is unreachable but needed to compile.
+ return null;
+ }
+
+ /** Runnable that can throw a checked exception. */
+ public interface ThrowingRunnable {
+ /** Method with code that may throw a checked exception. */
+ void run() throws Exception;
+ }
+}
diff --git a/tests/telephonytests/Android.bp b/tests/telephonytests/Android.bp
index 52cd7c5649..a4861df4fb 100644
--- a/tests/telephonytests/Android.bp
+++ b/tests/telephonytests/Android.bp
@@ -1,5 +1,17 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_opt_telephony_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ // SPDX-license-identifier-BSD
+ default_applicable_licenses: ["frameworks_opt_telephony_license"],
+}
+
android_test {
name: "FrameworksTelephonyTests",
+ // For access hidden connectivity methods in tests
+ defaults: ["framework-connectivity-test-defaults"],
srcs: ["**/*.java"],
@@ -25,7 +37,6 @@ android_test {
"testables",
],
- platform_apis: true,
jarjar_rules: ":jarjar-rules-telephony-tests",
test_suites: [
diff --git a/tests/telephonytests/AndroidManifest.xml b/tests/telephonytests/AndroidManifest.xml
index 3fbaacc2f3..dc367774d7 100644
--- a/tests/telephonytests/AndroidManifest.xml
+++ b/tests/telephonytests/AndroidManifest.xml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,28 +15,30 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.telephonytests">
+ package="com.android.frameworks.telephonytests">
<application android:name="com.android.internal.telephony.TestApplication">
- <uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.test.runner"/>
<activity android:label="TelephonyTest"
- android:name="TelephonyTest">
+ android:name="TelephonyTest"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.frameworks.telephonytests"
- android:label="Frameworks Telephony Tests">
+ android:targetPackage="com.android.frameworks.telephonytests"
+ android:label="Frameworks Telephony Tests">
</instrumentation>
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<uses-permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE"/>
- <uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
+ <uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
+ <uses-permission android:name="android.permission.BATTERY_STATS"/>
</manifest>
diff --git a/tests/telephonytests/AndroidTest.xml b/tests/telephonytests/AndroidTest.xml
index c3a405b1ef..5aa89221c4 100644
--- a/tests/telephonytests/AndroidTest.xml
+++ b/tests/telephonytests/AndroidTest.xml
@@ -23,6 +23,8 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.telephonytests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+ <option name="exclude-annotation" value="org.junit.Ignore" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>
diff --git a/tests/telephonytests/assets/eccdata b/tests/telephonytests/assets/eccdata
index 2bce958585..4c5959b4e8 100644
--- a/tests/telephonytests/assets/eccdata
+++ b/tests/telephonytests/assets/eccdata
Binary files differ
diff --git a/tests/telephonytests/assets/eccdata_input.txt b/tests/telephonytests/assets/eccdata_input.txt
new file mode 100644
index 0000000000..8dcfa753b5
--- /dev/null
+++ b/tests/telephonytests/assets/eccdata_input.txt
@@ -0,0 +1,15 @@
+revision: 1
+countries {
+ iso_code: "US"
+ eccs {
+ phone_number: "54321"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ types: MARINE_GUARD
+ types: MOUNTAIN_RESCUE
+ types: MIEC
+ types: AIEC
+ }
+ ecc_fallback: "911"
+}
diff --git a/tests/telephonytests/assets/eccdata_ota b/tests/telephonytests/assets/eccdata_ota
new file mode 100644
index 0000000000..69ddb46dcd
--- /dev/null
+++ b/tests/telephonytests/assets/eccdata_ota
Binary files differ
diff --git a/tests/telephonytests/assets/eccdata_ota_input.txt b/tests/telephonytests/assets/eccdata_ota_input.txt
new file mode 100644
index 0000000000..43795ce60e
--- /dev/null
+++ b/tests/telephonytests/assets/eccdata_ota_input.txt
@@ -0,0 +1,11 @@
+revision: 999999
+countries {
+ iso_code: "US"
+ eccs {
+ phone_number: "98765"
+ types: POLICE
+ types: AMBULANCE
+ types: FIRE
+ }
+ ecc_fallback: "911"
+} \ No newline at end of file
diff --git a/tests/telephonytests/jarjar-rules-tests.txt b/tests/telephonytests/jarjar-rules-tests.txt
index 91b5b322a7..dd698704d7 100644
--- a/tests/telephonytests/jarjar-rules-tests.txt
+++ b/tests/telephonytests/jarjar-rules-tests.txt
@@ -4,3 +4,11 @@
# by services-net and one by telephony-common), similarly to what happens on a
# real device, except that in the test they have different package names.
rule android.net.NetworkFactory* android.net.services.NetworkFactory@1
+
+# Telephony-common has already included net-utils-framework-common lib and jarjars
+# the package name. FrameworksTelephonyTests also includes net-utils-framework-common
+# via net-tests-utils and tries to jarjar it again. Rename the package names with
+# a different prefix in the test jarjar rule to avoid duplicate jar entries.
+rule com.android.net.module.util.** com.android.internal.telephony.util.test.@1
+# Similarly, this is needed for the build utils.
+rule com.android.modules.utils.** com.android.internal.telephony.util.test.@1
diff --git a/tests/telephonytests/src/android/telephony/BinderCacheManagerTest.java b/tests/telephonytests/src/android/telephony/BinderCacheManagerTest.java
new file mode 100644
index 0000000000..e5b5f3afcc
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/BinderCacheManagerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
+import android.os.IInterface;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+
+@RunWith(AndroidJUnit4.class)
+public class BinderCacheManagerTest {
+
+ @Mock IInterface mInterface;
+ @Mock IBinder mIBinder;
+
+ private BinderCacheManager<IInterface> mBinderCache;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mInterface.asBinder()).thenReturn(mIBinder);
+ mBinderCache = new BinderCacheManager<>(() -> mInterface);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetConnection() {
+ when(mIBinder.isBinderAlive()).thenReturn(true);
+ assertEquals(mInterface, mBinderCache.getBinder());
+ when(mIBinder.isBinderAlive()).thenReturn(false);
+ assertNull(mBinderCache.getBinder());
+ }
+
+ @Test
+ @SmallTest
+ public void testAddListenerAndDie() throws Exception {
+ IBinder.DeathRecipient recipient = populateCacheCaptureDeathRecipent();
+ CountDownLatch l = new CountDownLatch(1);
+ assertEquals(mInterface, mBinderCache.listenOnBinder(l, l::countDown));
+ when(mIBinder.isBinderAlive()).thenReturn(false);
+ recipient.binderDied();
+ assertEquals(0, l.getCount());
+ assertNull(mBinderCache.getBinder());
+ // Listeners should not be abailable wile remote process is not available.
+ assertNull(mBinderCache.listenOnBinder(l, l::countDown));
+ assertNull(mBinderCache.removeRunnable(l));
+ verify(mIBinder).unlinkToDeath(eq(recipient), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testListenerNotCalledAfterRemoved() throws Exception {
+ IBinder.DeathRecipient recipient = populateCacheCaptureDeathRecipent();
+ CountDownLatch l = new CountDownLatch(1);
+ assertEquals(mInterface, mBinderCache.listenOnBinder(l, l::countDown));
+ assertEquals(mInterface, mBinderCache.removeRunnable(l));
+ when(mIBinder.isBinderAlive()).thenReturn(false);
+ recipient.binderDied();
+ // Callback should never have been called because the runnable was removed before it died.
+ assertNotEquals(0, l.getCount());
+ }
+
+ @Test
+ @SmallTest
+ public void testAddListenerAlreadyDead() throws Exception {
+ IBinder.DeathRecipient recipient = populateCacheCaptureDeathRecipent();
+ when(mIBinder.isBinderAlive()).thenReturn(false);
+ recipient.binderDied();
+ CountDownLatch l = new CountDownLatch(1);
+ assertNull(mBinderCache.listenOnBinder(l, l::countDown));
+ assertNull(mBinderCache.removeRunnable(l));
+ // Callback shouldn't be called if it was never added in the first place.
+ assertNotEquals(0, l.getCount());
+ }
+
+ /**
+ * Populate the cache with mInterface & capture the associated DeathRecipient
+ */
+ private IBinder.DeathRecipient populateCacheCaptureDeathRecipent() throws Exception {
+ when(mIBinder.isBinderAlive()).thenReturn(true);
+ // Call getBinder() to populate cache the first time.
+ assertEquals(mInterface, mBinderCache.getBinder());
+ ArgumentCaptor<IBinder.DeathRecipient> captor = ArgumentCaptor.forClass(
+ IBinder.DeathRecipient.class);
+ verify(mIBinder).linkToDeath(captor.capture(), anyInt());
+ IBinder.DeathRecipient recipient = captor.getValue();
+ assertNotNull(recipient);
+ return recipient;
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/SmsMessageTest.java b/tests/telephonytests/src/android/telephony/SmsMessageTest.java
index 2bbad419be..2bd865de81 100644
--- a/tests/telephonytests/src/android/telephony/SmsMessageTest.java
+++ b/tests/telephonytests/src/android/telephony/SmsMessageTest.java
@@ -31,6 +31,5 @@ public class SmsMessageTest {
assertEquals(null, SmsMessage.createFromPdu(null, SmsConstants.FORMAT_3GPP2));
assertEquals(null, SmsMessage.createFromPdu(null, SmsConstants.FORMAT_3GPP));
assertEquals(null, SmsMessage.createFromPdu(null));
- assertEquals(null, SmsMessage.newFromCMT(null));
}
}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsCallSessionListenerTests.java b/tests/telephonytests/src/android/telephony/ims/ImsCallSessionListenerTests.java
index 99461c1a8d..cf4abb4365 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsCallSessionListenerTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsCallSessionListenerTests.java
@@ -16,21 +16,29 @@
package android.telephony.ims;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.aidl.IImsCallSessionListener;
+import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
public class ImsCallSessionListenerTests {
@@ -75,4 +83,52 @@ public class ImsCallSessionListenerTests {
eq(TelephonyManager.NETWORK_TYPE_IWLAN), eq(imsReasonInfo));
}
+ @Test
+ public void testCallSessionDtmfReceived() throws Exception {
+ ImsCallSessionListener mTestListener = new ImsCallSessionListener(mMockListener);
+ mTestListener.callSessionDtmfReceived('A');
+ mTestListener.callSessionDtmfReceived('a');
+ verify(mMockListener, times(2)).callSessionDtmfReceived(eq('A'));
+
+ mTestListener.callSessionDtmfReceived('B');
+ mTestListener.callSessionDtmfReceived('b');
+ verify(mMockListener, times(2)).callSessionDtmfReceived(eq('B'));
+
+ mTestListener.callSessionDtmfReceived('0');
+ verify(mMockListener, times(1)).callSessionDtmfReceived(eq('0'));
+
+ mTestListener.callSessionDtmfReceived('*');
+ verify(mMockListener, times(1)).callSessionDtmfReceived(eq('*'));
+ mTestListener.callSessionDtmfReceived('#');
+ verify(mMockListener, times(1)).callSessionDtmfReceived(eq('#'));
+
+ try {
+ mTestListener.callSessionDtmfReceived('P');
+ fail("expected exception");
+ } catch (IllegalArgumentException illegalArgumentException) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCallSessionRtpExtensionHeadersReceived() throws Exception {
+ ImsCallSessionListener mTestListener = new ImsCallSessionListener(mMockListener);
+ ArraySet<RtpHeaderExtension> headers = new ArraySet<RtpHeaderExtension>();
+ RtpHeaderExtension extension = new RtpHeaderExtension(1, new byte[1]);
+ headers.add(extension);
+ mTestListener.callSessionRtpHeaderExtensionsReceived(headers);
+ final ArgumentCaptor<List<RtpHeaderExtension>> listCaptor =
+ ArgumentCaptor.forClass((Class) List.class);
+ verify(mMockListener).callSessionRtpHeaderExtensionsReceived(
+ listCaptor.capture());
+ assertEquals(1, listCaptor.getValue().size());
+ assertEquals(extension.getLocalIdentifier(),
+ listCaptor.getValue().get(0).getLocalIdentifier());
+ try {
+ mTestListener.callSessionRtpHeaderExtensionsReceived(null);
+ fail("expected exception");
+ } catch (NullPointerException npe) {
+ // expected
+ }
+ }
}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsConfigImplTest.java b/tests/telephonytests/src/android/telephony/ims/ImsConfigImplTest.java
new file mode 100644
index 0000000000..ccf494b26a
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsConfigImplTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.util.Pair;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.LinkedBlockingQueue;
+
+@RunWith(AndroidJUnit4.class)
+public class ImsConfigImplTest {
+
+ private static final int TEST_KEY = 1;
+ private static final int TEST_INT_VALUE = 2;
+ private static final String TEST_STRING_VALUE = "abc";
+
+ private static class ImsConfigImpl extends ImsConfigImplBase {
+
+ public Pair<Integer, Integer> latestIntConfig;
+ public Pair<Integer, String> latestStringConfig;
+ // for testing caching
+ private boolean mIsGetConfigCalled = false;
+
+ @Override
+ public int setConfig(int item, int value) {
+ latestIntConfig = new Pair<>(item, value);
+ return CONFIG_RESULT_SUCCESS;
+ }
+
+ @Override
+ public int setConfig(int item, String value) {
+ latestStringConfig = new Pair<>(item, value);
+ return CONFIG_RESULT_SUCCESS;
+ }
+
+ @Override
+ public int getConfigInt(int item) {
+ mIsGetConfigCalled = true;
+ if (latestIntConfig == null) {
+ return CONFIG_RESULT_UNKNOWN;
+ }
+ if (latestIntConfig.first == item) {
+ return latestIntConfig.second;
+ }
+ return CONFIG_RESULT_UNKNOWN;
+ }
+
+ @Override
+ public String getConfigString(int item) {
+ mIsGetConfigCalled = true;
+ if (latestStringConfig == null) {
+ return null;
+ }
+ if (latestStringConfig.first == item) {
+ return latestStringConfig.second;
+ }
+ return null;
+ }
+
+ public boolean getIsGetConfigCalledAndReset() {
+ boolean result = mIsGetConfigCalled;
+ mIsGetConfigCalled = false;
+ return result;
+ }
+ }
+
+ private ImsConfigImpl mConfigUT;
+ private IImsConfig mConfigBinder;
+
+ @Before
+ public void setUp() throws Exception {
+ mConfigUT = new ImsConfigImpl();
+ mConfigBinder = mConfigUT.getIImsConfig();
+ }
+
+ @After
+ public void tearDown() {
+ mConfigUT = null;
+ mConfigBinder = null;
+ }
+
+ @Test
+ public void testIntCaching() throws Exception {
+ final LinkedBlockingQueue<Pair<Integer, Integer>> mConfigChanges =
+ new LinkedBlockingQueue<>();
+ final IImsConfigCallback mConfigCallback = new IImsConfigCallback.Stub() {
+
+ @Override
+ public void onIntConfigChanged(int item, int value) {
+ mConfigChanges.offer(new Pair<>(item, value));
+ }
+ @Override
+ public void onStringConfigChanged(int item, String value) {}
+ };
+ mConfigBinder.addImsConfigCallback(mConfigCallback);
+ mConfigBinder.setConfigInt(TEST_KEY, TEST_INT_VALUE);
+ // verify callback is called properly
+ Pair<Integer, Integer> callbackResult = mConfigChanges.poll();
+ assertNotNull(callbackResult);
+ assertEquals(TEST_KEY, callbackResult.first.intValue());
+ assertEquals(TEST_INT_VALUE, callbackResult.second.intValue());
+ // verify set is called on Impl
+ assertNotNull(mConfigUT.latestIntConfig);
+ assertEquals(TEST_KEY, mConfigUT.latestIntConfig.first.intValue());
+ assertEquals(TEST_INT_VALUE, mConfigUT.latestIntConfig.second.intValue());
+ // Now get the test key, this impl should not be called, as it is cached internally.
+ assertEquals(TEST_INT_VALUE, mConfigBinder.getConfigInt(TEST_KEY));
+ assertFalse(mConfigUT.getIsGetConfigCalledAndReset());
+ }
+
+ @Test
+ public void testStringCaching() throws Exception {
+ final LinkedBlockingQueue<Pair<Integer, String>> mConfigChanges =
+ new LinkedBlockingQueue<>();
+ final IImsConfigCallback mConfigCallback = new IImsConfigCallback.Stub() {
+
+ @Override
+ public void onIntConfigChanged(int item, int value) {}
+ @Override
+ public void onStringConfigChanged(int item, String value) {
+ mConfigChanges.offer(new Pair<>(item, value));
+ }
+ };
+ mConfigBinder.addImsConfigCallback(mConfigCallback);
+ mConfigBinder.setConfigString(TEST_KEY, TEST_STRING_VALUE);
+ // verify callback is called properly
+ Pair<Integer, String> callbackResult = mConfigChanges.poll();
+ assertNotNull(callbackResult);
+ assertEquals(TEST_KEY, callbackResult.first.intValue());
+ assertEquals(TEST_STRING_VALUE, callbackResult.second);
+ // verify set is called on Impl
+ assertNotNull(mConfigUT.latestStringConfig);
+ assertEquals(TEST_KEY, mConfigUT.latestStringConfig.first.intValue());
+ assertEquals(TEST_STRING_VALUE, mConfigUT.latestStringConfig.second);
+ // Now get the test key, this impl should not be called, as it is cached internally.
+ assertEquals(TEST_STRING_VALUE, mConfigBinder.getConfigString(TEST_KEY));
+ assertFalse(mConfigUT.getIsGetConfigCalledAndReset());
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
index 319d2008fa..80167d6ece 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
@@ -248,6 +248,8 @@ public class ImsFeatureTest {
// add some capabilities
request.addCapabilitiesToEnableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ request.addCapabilitiesToEnableForTech(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
request.addCapabilitiesToEnableForTech(
MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java b/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
index 7cfb8b0ead..ac151bb4e5 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
@@ -18,14 +18,11 @@ package android.telephony.ims;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
import android.telephony.AccessNetworkConstants;
+import android.telephony.BinderCacheManager;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
@@ -41,10 +38,8 @@ import org.mockito.Mock;
public class ImsMmTelManagerTests extends TelephonyTest {
- @Mock
- ITelephony.Stub mMockTelephonyInterface;
- @Mock
- IPackageManager.Stub mMockPackageManager;
+ @Mock ITelephony mMockTelephonyInterface;
+ @Mock BinderCacheManager<ITelephony> mBinderCache;
public class LocalCallback extends ImsMmTelManager.RegistrationCallback {
int mRegResult = -1;
@@ -58,13 +53,7 @@ public class ImsMmTelManagerTests extends TelephonyTest {
@Before
public void setUp() throws Exception {
super.setUp("ImsMmTelManagerTests");
- doReturn(mMockTelephonyInterface).when(mMockTelephonyInterface).queryLocalInterface(
- anyString());
- doReturn(mMockPackageManager).when(mMockPackageManager).queryLocalInterface(anyString());
- doReturn(true).when(mMockPackageManager).hasSystemFeature(
- eq(PackageManager.FEATURE_TELEPHONY_IMS), anyInt());
- mServiceManagerMockedServices.put("phone", mMockTelephonyInterface);
- mServiceManagerMockedServices.put("package", mMockPackageManager);
+ doReturn(mMockTelephonyInterface).when(mBinderCache).getBinder();
}
@After
@@ -78,9 +67,9 @@ public class ImsMmTelManagerTests extends TelephonyTest {
*/
@SmallTest
@Test
- public void testCallbackValues() throws Exception {
+ public void testDeprecatedCallbackValues() throws Exception {
LocalCallback cb = new LocalCallback();
- ImsMmTelManager managerUT = new ImsMmTelManager(0);
+ ImsMmTelManager managerUT = new ImsMmTelManager(0, mBinderCache);
managerUT.registerImsRegistrationCallback(Runnable::run, cb);
// Capture the RegistrationCallback that was registered.
@@ -91,14 +80,17 @@ public class ImsMmTelManagerTests extends TelephonyTest {
IImsRegistrationCallback cbBinder = callbackCaptor.getValue();
// Ensure the transport types are correct
- cbBinder.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ cbBinder.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build());
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, cb.mRegResult);
- cbBinder.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ cbBinder.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).build());
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WLAN, cb.mRegResult);
- cbBinder.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ cbBinder.onRegistered(new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_NONE).build());
assertEquals(-1, cb.mRegResult);
// Wacky value
- cbBinder.onRegistered(0xDEADBEEF);
+ cbBinder.onRegistered(new ImsRegistrationAttributes.Builder(0xDEADBEEF).build());
assertEquals(-1, cb.mRegResult);
}
}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
index aa37ff7efb..ce375f3176 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsRegistrationTests.java
@@ -26,13 +26,13 @@ import static org.mockito.Mockito.verify;
import android.net.Uri;
import android.os.Parcel;
import android.os.RemoteException;
-import android.telephony.ServiceState;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
@@ -40,6 +40,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@@ -120,17 +121,24 @@ public class ImsRegistrationTests {
@SmallTest
@Test
public void testRegistrationCallbackOnRegistered() throws RemoteException {
- mRegistration.onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
-
- verify(mCallback).onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ final ArraySet<String> features = new ArraySet<>();
+ features.add("feature1");
+ features.add("feature2");
+ ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).setFeatureTags(features).build();
+ mRegistration.onRegistered(attr);
+
+ verify(mCallback).onRegistered(attr);
}
@SmallTest
@Test
public void testRegistrationCallbackOnRegistering() throws RemoteException {
- mRegistration.onRegistering(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build();
+ mRegistration.onRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
- verify(mCallback).onRegistering(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ verify(mCallback).onRegistering(attr);
}
@SmallTest
@@ -169,19 +177,26 @@ public class ImsRegistrationTests {
public void testRegistrationCallbackAfterUnregistered() throws RemoteException {
mRegBinder.removeRegistrationCallback(mCallback);
- mRegistration.onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build();
+ mRegistration.onRegistered(attr);
- verify(mCallback, never()).onRegistered(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ verify(mCallback, never()).onRegistered(attr);
}
@SmallTest
@Test
public void testRegistrationCallbackSendCurrentState() throws RemoteException {
- mRegistration.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ final ArraySet<String> features = new ArraySet<>();
+ features.add("feature1");
+ features.add("feature2");
+ ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE).setFeatureTags(features).build();
+ mRegistration.onRegistered(attr);
mRegBinder.addRegistrationCallback(mCallback2);
- verify(mCallback2).onRegistered(eq(ImsRegistrationImplBase.REGISTRATION_TECH_LTE));
+ verify(mCallback2).onRegistered(attr);
}
@SmallTest
@@ -229,4 +244,35 @@ public class ImsRegistrationTests {
// with onUnregistered.
verify(mCallback2, never()).onDeregistered(any(ImsReasonInfo.class));
}
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackCalledOnAdd() throws RemoteException {
+ mRegistration.onSubscriberAssociatedUriChanged(new Uri[] { null, null });
+
+ mRegBinder.addRegistrationCallback(mCallback2);
+
+ verify(mCallback2).onSubscriberAssociatedUriChanged(
+ AdditionalMatchers.aryEq(new Uri[] { null, null }));
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackNotCalledOnAddAfterDeregistered() throws RemoteException {
+ ImsReasonInfo info = new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, 0);
+ mRegistration.onSubscriberAssociatedUriChanged(new Uri[] { null, null });
+
+ mRegistration.onDeregistered(info);
+ mRegBinder.addRegistrationCallback(mCallback2);
+
+ verify(mCallback2, never()).onSubscriberAssociatedUriChanged(any());
+ }
+
+ @SmallTest
+ @Test
+ public void testRegistrationCallbackNotCalledOnAddAndNoSubscriberChanged()
+ throws RemoteException {
+ mRegBinder.addRegistrationCallback(mCallback2);
+ verify(mCallback2, never()).onSubscriberAssociatedUriChanged(any());
+ }
}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
index 80cae9485b..79395a1ee3 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
@@ -85,7 +85,9 @@ public class ImsServiceTest {
@Test
@SmallTest
public void testCreateMMTelFeature() throws RemoteException {
- IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
+ IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0);
+ mTestImsServiceBinder.addFeatureStatusCallback(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
+ mTestCallback);
mTestImsService.mTestMmTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
SparseArray<ImsFeature> features = mTestImsService.getFeatures(TEST_SLOT_0);
@@ -106,10 +108,13 @@ public class ImsServiceTest {
@Test
@SmallTest
public void testRemoveMMTelFeature() throws RemoteException {
- mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
+ mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0);
+ mTestImsServiceBinder.addFeatureStatusCallback(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
+ mTestCallback);
- mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
+ mTestImsServiceBinder.removeFeatureStatusCallback(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
mTestCallback);
+ mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL);
verify(mTestImsService.mSpyMmTelFeature).onFeatureRemoved();
verify(mTestImsService.mSpyMmTelFeature).removeImsFeatureStatusCallback(mTestCallback);
@@ -120,7 +125,9 @@ public class ImsServiceTest {
@Test
@SmallTest
public void testCallMethodOnCreatedFeature() throws RemoteException {
- IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0, mTestCallback);
+ IImsMmTelFeature f = mTestImsServiceBinder.createMmTelFeature(TEST_SLOT_0);
+ mTestImsServiceBinder.addFeatureStatusCallback(TEST_SLOT_0, ImsFeature.FEATURE_MMTEL,
+ mTestCallback);
f.getUtInterface();
@@ -143,4 +150,23 @@ public class ImsServiceTest {
assertEquals(config, result);
}
+
+ /**
+ * Tests that ImsService capability sanitization works correctly.
+ */
+ @Test
+ @SmallTest
+ public void testCapsSanitized() throws RemoteException {
+ long validCaps =
+ ImsService.CAPABILITY_SIP_DELEGATE_CREATION;
+ // emergency over MMTEL should not be set here, but rather internally in Telephony.
+ long invalidCaps = 0xDEADBEEF00000000L | ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL;
+ invalidCaps |= validCaps;
+
+ mTestImsService.testCaps = validCaps;
+ assertEquals(validCaps, mTestImsServiceBinder.getImsServiceCapabilities());
+ mTestImsService.testCaps = invalidCaps;
+ // The extra bits should have been removed, leaving only the valid remaining
+ assertEquals(validCaps, mTestImsServiceBinder.getImsServiceCapabilities());
+ }
}
diff --git a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
index a1a9baf7b1..a067604237 100644
--- a/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/MmTelFeatureTests.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -47,6 +48,9 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
public class MmTelFeatureTests extends ImsTestBase {
@@ -171,4 +175,20 @@ public class MmTelFeatureTests extends ImsTestBase {
waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
assertTrue(mHandlerResults[TEST_SEND_DTMF_RESULT]);
}
+
+ @SmallTest
+ @Test
+ public void testChangeOfferedRtpHeaderExtensionTypes() throws Exception {
+ Message resultMessage = Message.obtain(mHandler, TEST_SEND_DTMF_RESULT);
+ resultMessage.replyTo = mHandlerMessenger;
+ RtpHeaderExtensionType type = new RtpHeaderExtensionType(1,
+ Uri.parse("http://developer.android.com/test"));
+ ArrayList<RtpHeaderExtensionType> types = new ArrayList<>();
+ types.add(type);
+ mFeatureBinder.changeOfferedRtpHeaderExtensionTypes(types);
+ waitForHandlerAction(mHandler, TEST_RESULT_DELAY_MS);
+ mFeature.configuredRtpHeaderExtensions.await(TEST_RESULT_DELAY_MS, TimeUnit.MILLISECONDS);
+ assertEquals(types.size(), mFeature.receivedExtensions.size());
+ assertEquals(types.get(0), mFeature.receivedExtensions.iterator().next());
+ }
}
diff --git a/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
new file mode 100644
index 0000000000..a81e93fef3
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.telephony.ims;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.RcsConfig;
+import android.telephony.ims.RcsConfig.Characteristic;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.telephony.FakeTelephonyProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public final class RcsConfigTest {
+
+ private static final String TEST_RCS_CONFIG = "<?xml version=\"1.0\"?>\n"
+ + "<wap-provisioningdoc version=\"1.1\">\n"
+ + "\t<characteristic type=\"APPLICATION\">\n"
+ + "\t\t<parm name=\"AppID\" value=\"urn:oma:mo:ext-3gpp-ims:1.0\"/>\n"
+ + "\t\t<characteristic type=\"3GPP_IMS\">\n"
+ + "\t\t\t<parm name=\"AppID\" value=\"ap2001\"/>\n"
+ + "\t\t\t<parm name=\"Name\" value=\"RCS IMS Settings\"/>\n"
+ + "\t\t\t<characteristic type=\"Ext\">\n"
+ + "\t\t\t\t<characteristic type=\"GSMA\">\n"
+ + "\t\t\t\t\t<parm name=\"AppRef\" value=\"IMS-Setting\"/>\n"
+ + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\"1\"/>\n"
+ + "\t\t\t\t</characteristic>\n"
+ + "\t\t\t</characteristic>\n"
+ + "\t\t</characteristic>\n"
+ + "\t\t<characteristic type=\"SERVICES\">\n"
+ + "\t\t\t<parm name=\"SupportedRCSProfileVersions\" value=\"UP_2.3\"/>\n"
+ + "\t\t\t<parm name=\"ChatAuth\" value=\"1\"/>\n"
+ + "\t\t\t<parm name=\"GroupChatAuth\" value=\"1\"/>\n"
+ + "\t\t\t<parm name=\"ftAuth\" value=\"1\"/>\n"
+ + "\t\t\t<parm name=\"standaloneMsgAuth\" value=\"1\"/>\n"
+ + "\t\t\t<parm name=\"geolocPushAuth\" value=\"1\"/>\n"
+ + "\t\t\t<characteristic type=\"Ext\">\n"
+ + "\t\t\t\t<characteristic type=\"DataOff\">\n"
+ + "\t\t\t\t\t<parm name=\"rcsMessagingDataOff\" value=\"1\"/>\n"
+ + "\t\t\t\t\t<parm name=\"fileTransferDataOff\" value=\"1\"/>\n"
+ + "\t\t\t\t\t<parm name=\"mmsDataOff\" value=\"1\"/>\n"
+ + "\t\t\t\t\t<parm name=\"syncDataOff\" value=\"1\"/>\n"
+ + "\t\t\t\t\t<characteristic type=\"Ext\"/>\n"
+ + "\t\t\t\t</characteristic>\n"
+ + "\t\t\t</characteristic>\n"
+ + "\t\t</characteristic>\n"
+ + "\t\t<characteristic type=\"PRESENCE\">\n"
+ + "\t\t\t<parm name=\"client-obj-datalimit\" value=\"8192\"/>\n"
+ + "\t\t\t<parm name=\"content-serveruri\" value=\"X\"/>\n"
+ + "\t\t\t<parm name=\"source-throttlepublish\" value=\"32\"/>\n"
+ + "\t\t\t<parm name=\"max-number-ofsubscriptions-inpresence-list\" value=\"8\"/>\n"
+ + "\t\t\t<parm name=\"service-uritemplate\" value=\"X\"/>\n"
+ + "\t\t\t<parm name=\"RLS-URI\" value=\"X\"/>\n"
+ + "\t\t</characteristic>\n"
+ + "\t\t<characteristic type=\"MESSAGING\">\n"
+ + "\t\t\t<characteristic type=\"StandaloneMsg\">\n"
+ + "\t\t\t\t<parm name=\"MaxSize\" value=\"8192\"/>\n"
+ + "\t\t\t\t<parm name=\"SwitchoverSize\" value=\"1024\"/>\n"
+ + "\t\t\t\t<parm name=\"exploder-uri\" value=\"X\"/>\n"
+ + "\t\t\t</characteristic>\n"
+ + "\t\t\t<characteristic type=\"Chat\">\n"
+ + "\t\t\t\t<parm name=\"max_adhoc_group_size\" value=\"60\"/>\n"
+ + "\t\t\t\t<parm name=\"conf-fcty-uri\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"AutAccept\" value=\"1\"/>\n"
+ + "\t\t\t\t<parm name=\"AutAcceptGroupChat\" value=\"1\"/>\n"
+ + "\t\t\t\t<parm name=\"TimerIdle\" value=\"120\"/>\n"
+ + "\t\t\t\t<parm name=\"MaxSize\" value=\"16384\"/>\n"
+ + "\t\t\t\t<parm name=\"ChatRevokeTimer\" value=\"0\"/>\n"
+ + "\t\t\t\t<parm name=\"reconnectGuardTimer\" value=\"0\"/>\n"
+ + "\t\t\t\t<parm name=\"cfsTrigger\" value=\"1\"/>\n"
+ + "\t\t\t</characteristic>\n"
+ + "\t\t\t<parm name=\"max1ToManyRecipients\" value=\"8\"/>\n"
+ + "\t\t\t<parm name=\"1toManySelectedTech\" value=\"1\"/>\n"
+ + "\t\t\t<parm name=\"displayNotificationSwitch\" value=\"0\"/>\n"
+ + "\t\t\t<parm name=\"contentCompressSize\" value=\"1024\"/>\n"
+ + "\t\t\t<characteristic type=\"FileTransfer\">\n"
+ + "\t\t\t\t<parm name=\"ftWarnSize\" value=\"0\"/>\n"
+ + "\t\t\t\t<parm name=\"MaxSizeFileTr\" value=\"65536\"/>\n"
+ + "\t\t\t\t<parm name=\"ftAutAccept\" value=\"1\"/>\n"
+ + "\t\t\t\t<parm name=\"ftHTTPCSURI\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"ftHTTPDLURI\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"ftHTTPCSUser\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"ftHTTPCSPwd\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"ftHTTPFallback\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\" ftMax1ToManyRecipients\" value=\"0\"/>\n"
+ + "\t\t\t</characteristic>\n"
+ + "\t\t\t<characteristic type=\"Chatbot\">\n"
+ + "\t\t\t\t<parm name=\"ChatbotDirectory\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"BotinfoFQDNRoot\" value=\"X\"/>\n"
+ + "\t\t\t\t<part name=\"SpecificChatbotsList\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"IdentityInEnrichedSearch\" value=\"1\"/>\n"
+ + "\t\t\t\t<parm name=\"PrivacyDisable\" value=\"0\"/>\n"
+ + "\t\t\t\t<parm name=\"ChatbotMsgTech\" value=\"1\"/>\n"
+ + "\t\t\t</characteristic>\n"
+ + "\t\t\t<characteristic type=\"MessageStore\">\n"
+ + "\t\t\t\t<parm name=\"MsgStoreUrl\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"MsgStoreNotifUrl\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"MsgStoreAuth\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"MsgStoreUserName\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"MsgStoreUserPwd\" value=\"X\"/>\n"
+ + "\t\t\t\t<parm name=\"EventRpting\" value=\"1\"/>\n"
+ + "\t\t\t\t<parm name=\"AuthArchive\" value=\"1\"/>\n"
+ + "\t\t\t\t<parm name=\"SMSStore\" value=\"1\"/>\n"
+ + "\t\t\t\t<parm name=\"MMSStore\" value=\"1\"/>\n"
+ + "\t\t\t</characteristic>\n"
+ + "\t\t\t<characteristic type=\"Ext\"/>\n"
+ + "\t\t</characteristic>\n"
+ + "\t</characteristic>\n"
+ + "</wap-provisioningdoc>\n";
+
+ private static final String[][] TEST_CONFIG_VALUES = {{"rcsVolteSingleRegistration", "1"},
+ {"SupportedRCSProfileVersions", "UP_2.3"}, {"ChatAuth", "1"}, {"GroupChatAuth", "1"},
+ {"ftAuth", "1"}, {"standaloneMsgAuth", "1"}, {"geolocPushAuth", "1"},
+ {"rcsMessagingDataOff", "1"}, {"fileTransferDataOff", "1"}, {"mmsDataOff", "1"},
+ {"syncDataOff", "1"}};
+
+ private static final String[] VALID_CHARACTERISTICS = {"APPLICATION", "3GPP_IMS", "Ext",
+ "GSMA", "SERVICES", "DaTAOFF", "PRESENCE", "MESSAGING", "Chat", "FileTransfer",
+ "Chatbot", "MessageSTORE"};
+ private static final String[] INVALID_CHARACTERISTICS = {"APP2LICATION", "3GPPIMS", "Exte",
+ "GSMf", "SERVICE", "DaTAOn", "PRESENCE2", "MESSAG", "Ch", "File", "STORE"};
+ private static final String[][] SUB_CHARACTERISTICS = {
+ {"APPLICATION", "3GPP_IMS", "Ext", "GSMA"},
+ {"APPLICATION", "SERVICES", "Ext", "DataOff", "Ext"}};
+ private static final String[][] SAME_PARMS_DIFF_CHARS_VALUE_MAP = {
+ {"MaxSize", "Chat", "16384"}, {"MaxSize", "StandaloneMsg", "8192"}};
+
+ private static final int FAKE_SUB_ID = 1;
+ private MockContentResolver mContentResolver;
+ private FakeTelephonyProvider mFakeTelephonyProvider;
+ @Mock
+ Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mFakeTelephonyProvider = new FakeTelephonyProvider();
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(SubscriptionManager.CONTENT_URI.getAuthority(),
+ mFakeTelephonyProvider);
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ createFakeSimInfo();
+ }
+
+ @Test
+ @SmallTest
+ public void testLoadAndUpdateConfigForSub() {
+
+ byte[] currentData = RcsConfig.loadRcsConfigForSub(mContext, FAKE_SUB_ID, false);
+
+ RcsConfig.updateConfigForSub(mContext, FAKE_SUB_ID, null, false);
+ byte[] updatedData = RcsConfig.loadRcsConfigForSub(mContext, FAKE_SUB_ID, false);
+ assertNull(updatedData);
+
+ RcsConfig.updateConfigForSub(mContext, FAKE_SUB_ID, TEST_RCS_CONFIG.getBytes(), false);
+ updatedData = RcsConfig.loadRcsConfigForSub(mContext, FAKE_SUB_ID, false);
+ assertTrue(Arrays.equals(updatedData, TEST_RCS_CONFIG.getBytes()));
+
+ RcsConfig.updateConfigForSub(mContext, FAKE_SUB_ID, currentData, false);
+ updatedData = RcsConfig.loadRcsConfigForSub(mContext, FAKE_SUB_ID, false);
+ assertTrue(Arrays.equals(currentData, updatedData));
+ }
+
+ @Test
+ @SmallTest
+ public void testCompressAndDecompress() {
+ byte[] compressedData = RcsConfig.compressGzip(TEST_RCS_CONFIG.getBytes());
+ assertFalse(Arrays.equals(compressedData, TEST_RCS_CONFIG.getBytes()));
+ byte[] decompressedData = RcsConfig.decompressGzip(compressedData);
+ assertTrue(Arrays.equals(decompressedData, TEST_RCS_CONFIG.getBytes()));
+ assertNull(RcsConfig.compressGzip(null));
+ assertNull(RcsConfig.decompressGzip(null));
+ byte[] emptyData = new byte[0];
+ assertEquals(emptyData, RcsConfig.compressGzip(emptyData));
+ assertEquals(emptyData, RcsConfig.decompressGzip(emptyData));
+ }
+
+ @Test
+ @SmallTest
+ public void testParseConfig() {
+ RcsConfig config = new RcsConfig(TEST_RCS_CONFIG.getBytes());
+
+ for (int i = 0; i < TEST_CONFIG_VALUES.length; i++) {
+ assertEquals(config.getString(TEST_CONFIG_VALUES[i][0], null),
+ TEST_CONFIG_VALUES[i][1]);
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testGetCharacteristic() {
+ RcsConfig config = new RcsConfig(TEST_RCS_CONFIG.getBytes());
+
+ for (int i = 0; i < VALID_CHARACTERISTICS.length; i++) {
+ assertNotNull(config.getCharacteristic(VALID_CHARACTERISTICS[i]));
+ }
+
+ for (int i = 0; i < INVALID_CHARACTERISTICS.length; i++) {
+ assertNull(config.getCharacteristic(INVALID_CHARACTERISTICS[i]));
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testSetAndMoveCharacteristic() {
+ RcsConfig config = new RcsConfig(TEST_RCS_CONFIG.getBytes());
+
+ for (String[] sub : SUB_CHARACTERISTICS) {
+ Characteristic[] cl = new Characteristic[sub.length];
+ int c = 0;
+ for (String cur : sub) {
+ cl[c] = config.getCharacteristic(cur);
+ assertNotNull(cl[c]);
+ config.setCurrentCharacteristic(cl[c]);
+ c++;
+ }
+
+ while (c > 0) {
+ assertEquals(cl[--c], config.getCurrentCharacteristic());
+ config.moveToParent();
+ }
+
+ assertEquals(config.getRoot(), config.getCurrentCharacteristic());
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testGetDuplicateParmInDifferentCharacteristics() {
+ RcsConfig config = new RcsConfig(TEST_RCS_CONFIG.getBytes());
+ for (String[] sub : SAME_PARMS_DIFF_CHARS_VALUE_MAP) {
+ config.moveToRoot();
+ if (!sub[1].isEmpty()) {
+ config.setCurrentCharacteristic(config.getCharacteristic(sub[1]));
+ }
+
+ String value = config.getString(sub[0], "");
+
+ assertEquals(value, sub[2]);
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testIsRcsVolteSingleRegistrationSupported() {
+ String[] vals = new String[]{"0", "1", "2"};
+ boolean[] expectedResHome = new boolean[]{false, true, true};
+ boolean[] expectedResRoaming = new boolean[]{false, true, false};
+ for (int i = 0; i < vals.length; i++) {
+ String xml = "\t\t\t\t<characteristic type=\"GSMA\">\n"
+ + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\""
+ + vals[i] + "\"/>\n" + "\t\t\t\t</characteristic>\n";
+ RcsConfig config = new RcsConfig(xml.getBytes());
+ assertEquals(config.isRcsVolteSingleRegistrationSupported(false), expectedResHome[i]);
+ assertEquals(config.isRcsVolteSingleRegistrationSupported(true),
+ expectedResRoaming[i]);
+ }
+ }
+
+ private void createFakeSimInfo() {
+ ContentValues contentValues = new ContentValues();
+ final String fakeIccId = "fakeleIccId";
+ final String fakeCardId = "fakeCardId";
+ contentValues.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, FAKE_SUB_ID);
+ contentValues.put(SubscriptionManager.ICC_ID, fakeIccId);
+ contentValues.put(SubscriptionManager.CARD_ID, fakeCardId);
+ mContentResolver.insert(SubscriptionManager.CONTENT_URI, contentValues);
+ }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestImsService.java b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
index 944514a118..ff4781e47a 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestImsService.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
@@ -36,6 +36,8 @@ public class TestImsService extends android.telephony.ims.ImsService {
public ImsFeatureConfiguration testFeatureConfig;
+ public long testCaps;
+
public TestImsService(Context context) {
attachBaseContext(context);
MockitoAnnotations.initMocks(this);
@@ -58,4 +60,9 @@ public class TestImsService extends android.telephony.ims.ImsService {
public ImsFeatureConfiguration querySupportedImsFeatures() {
return testFeatureConfig;
}
+
+ @Override
+ public long getImsServiceCapabilities() {
+ return testCaps;
+ }
}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
index fc1d67007a..67b9bad410 100644
--- a/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
+++ b/tests/telephonytests/src/android/telephony/ims/TestMmTelFeature.java
@@ -28,12 +28,17 @@ import android.telephony.ims.stub.ImsMultiEndpointImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
public class TestMmTelFeature extends MmTelFeature {
public boolean queryConfigurationResult = false;
public int setCapabilitiesResult = ImsFeature.CAPABILITY_SUCCESS;
public CapabilityChangeRequest lastRequest;
public boolean isUtInterfaceCalled = false;
+ public CountDownLatch configuredRtpHeaderExtensions = new CountDownLatch(1);
+ Set<RtpHeaderExtensionType> receivedExtensions = null;
private final TestImsCallSession mCallSession = new TestImsCallSession();
private class TestImsCallSession extends ImsCallSessionImplBase {
@@ -61,6 +66,12 @@ public class TestMmTelFeature extends MmTelFeature {
}
@Override
+ public void changeOfferedRtpHeaderExtensionTypes(Set<RtpHeaderExtensionType> types) {
+ receivedExtensions = types;
+ configuredRtpHeaderExtensions.countDown();
+ }
+
+ @Override
public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
return mCallSession;
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java b/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java
index 3b786b0162..8fe809c9f0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java
@@ -16,19 +16,23 @@
package com.android.internal.telephony;
+import static com.google.common.truth.Truth.assertThat;
+
import android.os.Parcel;
-import java.util.Arrays;
-import junit.framework.TestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.IccUtils;
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
/**
* {@hide}
*/
public class AdnRecordTest extends TestCase {
-
+
@SmallTest
public void testBasic() throws Exception {
AdnRecord adn;
@@ -189,6 +193,20 @@ public class AdnRecordTest extends TestCase {
assertEquals(adn.getNumber(), copy.getNumber());
assertTrue(Arrays.equals(adn.getEmails(), copy.getEmails()));
}
+
+ public void testGetMaxAlphaTagBytes() {
+ assertThat(AdnRecord.getMaxAlphaTagBytes(-1)).isEqualTo(0);
+ assertThat(AdnRecord.getMaxAlphaTagBytes(0)).isEqualTo(0);
+ assertThat(AdnRecord.getMaxAlphaTagBytes(5)).isEqualTo(0);
+ assertThat(AdnRecord.getMaxAlphaTagBytes(14)).isEqualTo(0);
+ assertThat(AdnRecord.getMaxAlphaTagBytes(15)).isEqualTo(1);
+ assertThat(AdnRecord.getMaxAlphaTagBytes(25)).isEqualTo(11);
+ assertThat(AdnRecord.getMaxAlphaTagBytes(30)).isEqualTo(16);
+ }
+
+ public void testGetMaxPhoneNumberDigits() {
+ assertThat(AdnRecord.getMaxPhoneNumberDigits()).isEqualTo(20);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/BarringInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/BarringInfoTest.java
index 6720b41287..7718774d2d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/BarringInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/BarringInfoTest.java
@@ -49,12 +49,12 @@ public class BarringInfoTest {
BarringInfo.BARRING_SERVICE_TYPE_SMS,
};
- /** Return a dummy set of barring info */
+ /** Return a placeholder set of barring info */
private static SparseArray<BarringServiceInfo> getBarringServiceInfos() {
return getBarringServiceInfos(false);
}
- /** Return a dummy set of barring info
+ /** Return a placeholder set of barring info
*
* @param isConditionallyBarred set the flag for whether the conditionally barred service has
* been evaluated and is actually barred based on the conditional barring parameters.
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
index fd9ef94822..ff18a07720 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
@@ -173,4 +173,99 @@ public class CarrierDisplayNameResolverTest extends TelephonyTest {
CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
assertThat(data.getPlmn()).isEqualTo(PNN_HOME_NAME_FROM_USIM);
}
+
+ @Test
+ public void testShouldShowPLMNFromSourceBandOverride_notShowPLMN() {
+ // Update ef records from brand override
+ mCdnr.updateEfForBrandOverride("spn from brand override");
+
+ mSS.setRoaming(ROAMING);
+
+ CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
+ assertThat(data.shouldShowPlmn()).isFalse();
+ }
+
+ @Test
+ public void testShouldShowSPNFromSourceCC_conditionOverrideShowPLMN_notShowSPN() {
+ // Carrier config source > sim record source
+ mConfig.putInt(CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, 1);
+
+ // Update ef records from carrier config
+ mCdnr.updateEfFromCarrierConfig(mConfig);
+
+ CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
+ assertThat(data.shouldShowSpn()).isFalse();
+ }
+
+ @Test
+ public void testShouldShowPLMNFromSourceCC_conditionOverrideShowPLMN_shouldShowPLMN() {
+ // Carrier config source > sim record source
+ mConfig.putInt(CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, 1);
+
+ // Update ef records from carrier config
+ mCdnr.updateEfFromCarrierConfig(mConfig);
+
+ CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
+ assertThat(data.shouldShowPlmn()).isTrue();
+ }
+
+ @Test
+ public void testShouldShowPLMNLongName_plmnNotInProvidedList_showPLMNLongName() {
+ // Carrier config source > sim record source
+ mConfig.putInt(CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, 1);
+
+ // Update ef records from carrier config
+ mCdnr.updateEfFromCarrierConfig(mConfig);
+
+ SIMRecords usim = Mockito.mock(SIMRecords.class);
+ doReturn(SPN_FROM_USIM).when(usim).getServiceProviderName();
+ mCdnr.updateEfFromUsim(usim);
+
+ CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
+
+ assertThat(data.shouldShowPlmn()).isTrue();
+ assertThat(data.getPlmn()).isEqualTo("long name");
+ }
+
+ @Test
+ public void testShouldShowPLMNShortName_plmnNotInProvidedList_showPLMNShortName() {
+ // Carrier config source > sim record source
+ mConfig.putInt(CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, 1);
+
+ // Update ef records from carrier config
+ mCdnr.updateEfFromCarrierConfig(mConfig);
+
+ SIMRecords usim = Mockito.mock(SIMRecords.class);
+ doReturn(SPN_FROM_USIM).when(usim).getServiceProviderName();
+ mCdnr.updateEfFromUsim(usim);
+
+ // long name empty
+ mSS.setOperatorName("", "short name", HOME_PLMN_NUMERIC);
+
+ CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
+
+ assertThat(data.shouldShowPlmn()).isTrue();
+ assertThat(data.getPlmn()).isEqualTo("short name");
+ }
+
+ @Test
+ public void testShouldShowPLMNNumeric_plmnNotInProvidedList_showPLMNNumeric() {
+ // Carrier config source > sim record source
+ mConfig.putInt(CarrierConfigManager.KEY_SPN_DISPLAY_CONDITION_OVERRIDE_INT, 1);
+
+ // Update ef records from carrier config
+ mCdnr.updateEfFromCarrierConfig(mConfig);
+
+ SIMRecords usim = Mockito.mock(SIMRecords.class);
+ doReturn(SPN_FROM_USIM).when(usim).getServiceProviderName();
+ mCdnr.updateEfFromUsim(usim);
+
+ // long name and short name empty
+ mSS.setOperatorName("", "", HOME_PLMN_NUMERIC);
+
+ CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
+
+ assertThat(data.shouldShowPlmn()).isTrue();
+ assertThat(data.getPlmn()).isEqualTo(HOME_PLMN_NUMERIC);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
index 5a7f688ff2..4cd5d68960 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -15,13 +15,14 @@
*/
package com.android.internal.telephony;
-import static android.preference.PreferenceManager.getDefaultSharedPreferences;
+import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -30,10 +31,10 @@ import static org.mockito.Mockito.when;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ImsiEncryptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -104,7 +105,8 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
String dateExpected = dt.format(expectedCal.getTime());
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
"keyIdentifier", publicKey, date);
- when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt(), anyBoolean()))
+ .thenReturn(imsiEncryptionInfo);
Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
assertTrue(dt.format(expirationDate).equals(dateExpected));
}
@@ -130,7 +132,8 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
Date maxExpirationDate = maxExpirationCal.getTime();
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
"keyIdentifier", publicKey, date);
- when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt(), anyBoolean()))
+ .thenReturn(imsiEncryptionInfo);
Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
assertTrue(expirationDate.before(minExpirationDate));
assertTrue(expirationDate.after(maxExpirationDate));
@@ -151,7 +154,7 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
}
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
"key1=value", keyInfo.first, new Date(keyInfo.second));
- String mccMnc = "310:270";
+ String mccMnc = "310270";
mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr, mccMnc);
verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
(Matchers.refEq(imsiEncryptionInfo)));
@@ -172,7 +175,7 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
}
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", 2,
"key1=value", keyInfo.first, new Date(keyInfo.second));
- String mccMnc = "310:270";
+ String mccMnc = "310270";
mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr1, mccMnc);
verify(mPhone, times(2)).setCarrierInfoForImsiEncryption(
(Matchers.refEq(imsiEncryptionInfo)));
@@ -185,7 +188,7 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
@Test
@SmallTest
public void testParseBadJsonFail() {
- String mccMnc = "310:290";
+ String mccMnc = "310290";
String badJsonStr = "{badJsonString}";
mCarrierKeyDM.parseJsonAndPersistKey(badJsonStr, mccMnc);
verify(mPhone, times(0)).setCarrierInfoForImsiEncryption(any());
@@ -198,9 +201,13 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
@Test
@SmallTest
public void testIsValidDownload() {
- String mccMnc = "310:260";
- when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
- assertTrue(mCarrierKeyDM.isValidDownload(mccMnc));
+ String currentMccMnc = "310260";
+ long currentDownloadId = 1;
+ // mock downloadId to match
+ mCarrierKeyDM.mMccMncForDownload = currentMccMnc;
+ mCarrierKeyDM.mDownloadId = currentDownloadId;
+
+ assertTrue(mCarrierKeyDM.isValidDownload(currentMccMnc, currentDownloadId));
}
/**
@@ -210,9 +217,18 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
@Test
@SmallTest
public void testIsValidDownloadFail() {
- String mccMnc = "310:290";
- when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
- assertFalse(mCarrierKeyDM.isValidDownload(mccMnc));
+ String currentMccMnc = "310260";
+ long currentDownloadId = 1;
+
+ // mock downloadId to match, mccmnc so it doesn't match
+ mCarrierKeyDM.mMccMncForDownload = "310290";
+ mCarrierKeyDM.mDownloadId = currentDownloadId;
+ assertFalse(mCarrierKeyDM.isValidDownload(currentMccMnc, currentDownloadId));
+
+ // pass in mccmnc to match, and mock shared pref downloadId so it doesn't match
+ currentMccMnc = "310290";
+ mCarrierKeyDM.mDownloadId = currentDownloadId + 1;
+ assertFalse(mCarrierKeyDM.isValidDownload(currentMccMnc, currentDownloadId));
}
/**
@@ -240,11 +256,10 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
@Test
@SmallTest
public void testDownloadComplete() {
- SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit();
- String mccMnc = "310:260";
- int slotId = mPhone.getPhoneId();
- editor.putString("CARRIER_KEY_DM_MCC_MNC" + slotId, mccMnc);
- editor.commit();
+ String mccMnc = "310260";
+ long downloadId = 1;
+ mCarrierKeyDM.mMccMncForDownload = mccMnc;
+ mCarrierKeyDM.mDownloadId = downloadId;
SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd");
Calendar expectedCal = new GregorianCalendar();
@@ -253,6 +268,7 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
Intent mIntent = new Intent(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mIntent.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downloadId);
mContext.sendBroadcast(mIntent);
processAllMessages();
Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
@@ -278,9 +294,30 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
mContext.sendBroadcast(mIntent);
processAllMessages();
- SharedPreferences preferences = getDefaultSharedPreferences(mContext);
- String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
- assertTrue(mccMnc.equals("310:260"));
+ assertEquals("310260", mCarrierKeyDM.mMccMncForDownload);
+ }
+
+ /**
+ * Tests sending the ACTION_CARRIER_CONFIG_CHANGED intent with an empty key.
+ * Verify that the carrier keys are removed if IMSI_KEY_DOWNLOAD_URL_STRING is null.
+ */
+ @Test
+ @SmallTest
+ public void testCarrierConfigChangedEmptyKey() {
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ int slotId = mPhone.getPhoneId();
+ PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
+ bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
+ bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, null);
+
+ Intent mIntent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+ mContext.sendBroadcast(mIntent);
+ processAllMessages();
+ assertNull(mCarrierKeyDM.mMccMncForDownload);
+
+ verify(mPhone).deleteCarrierInfoForImsiEncryption();
}
/**
@@ -292,19 +329,17 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
public void testAlarmRenewal() {
CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- int slotId = mPhone.getPhoneId();
- PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotId);
+ int slotIndex = SubscriptionManager.getSlotIndex(mPhone.getSubId());
+ PersistableBundle bundle = carrierConfigManager.getConfigForSubId(slotIndex);
bundle.putInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT, 3);
bundle.putString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING, mURL);
when(mTelephonyManager.getSimOperator(anyInt())).thenReturn("310260");
- Intent mIntent = new Intent("com.android.internal.telephony.carrier_key_download_alarm"
- + slotId);
+ Intent mIntent = new Intent("com.android.internal.telephony.carrier_key_download_alarm");
+ mIntent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex);
mContext.sendBroadcast(mIntent);
processAllMessages();
- SharedPreferences preferences = getDefaultSharedPreferences(mContext);
- String mccMnc = preferences.getString("CARRIER_KEY_DM_MCC_MNC" + slotId, null);
- assertTrue(mccMnc.equals("310:260"));
+ assertEquals("310260", mCarrierKeyDM.mMccMncForDownload);
}
/**
@@ -322,7 +357,7 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("310", "270",
TelephonyManager.KEY_TYPE_WLAN, "key1=value", keyInfo.first,
new Date(CERT_EXPIRATION));
- String mccMnc = "310:270";
+ String mccMnc = "310270";
mCarrierKeyDM.parseJsonAndPersistKey(mJsonStr3GppSpec, mccMnc);
verify(mPhone).setCarrierInfoForImsiEncryption(
(Matchers.refEq(imsiEncryptionInfo)));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
index 79c8042347..aa5d726e4c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
@@ -46,6 +46,7 @@ import android.os.Message;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
@@ -114,8 +115,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
mHandler = new CarrierPrivilegesTrackerTestHandler();
// set mock behavior so CarrierPrivilegeTracker initializes with no privileged UIDs
- setupCarrierConfigCerts();
- setupSimLoadedCerts();
+ setupCarrierConfigRules();
+ setupSimLoadedRules();
setupInstalledPackages();
}
@@ -124,16 +125,33 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
super.tearDown();
}
- private void setupCarrierConfigCerts(String... certHashes) {
- mCarrierConfigs.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, certHashes);
+ /** @param rules can either be "hash" or "hash:package[,package...]" */
+ private void setupCarrierConfigRules(String... rules) {
+ mCarrierConfigs.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, rules);
mCarrierConfigs.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfigs);
}
- private void setupSimLoadedCerts(String... certHashes) {
+ private static String carrierConfigRuleString(String certificateHash, String... packageNames) {
+ if (packageNames == null || packageNames.length == 0) {
+ return certificateHash;
+ }
+ return certificateHash + ':' + String.join(",", packageNames);
+ }
+
+ private void setupSimLoadedRules(UiccAccessRule... certHashes) {
when(mTelephonyManager.hasIccCard(PHONE_ID)).thenReturn(true);
- when(mTelephonyManager.getCertsFromCarrierPrivilegeAccessRules())
- .thenReturn(Arrays.asList(certHashes));
+ when(mUiccProfile.getCarrierPrivilegeAccessRules()).thenReturn(Arrays.asList(certHashes));
+ }
+
+ private static UiccAccessRule ruleWithHashOnly(String certificateHash) {
+ return ruleWithHashAndPackage(certificateHash, null /* packageName */);
+ }
+
+ private static UiccAccessRule ruleWithHashAndPackage(
+ String certificateHash, String packageName) {
+ return new UiccAccessRule(
+ IccUtils.hexStringToBytes(certificateHash), packageName, /* accessType= */ 0L);
}
private void setupInstalledPackages(PackageCertInfo... pkgCertInfos) throws Exception {
@@ -165,7 +183,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
* <p>The initial configuration of the CarrierPrivilegesTracker will be based on the current
* state of certificate hashes and installed packages.
*
- * See #setupCarrierConfigCerts, #setupSimLoadedCerts, #setupInstalledPackages.
+ * <p>See {@link #setupCarrierConfigRules}, {@link #setupSimLoadedRules}, {@link
+ * #setupInstalledPackages}.
*/
private CarrierPrivilegesTracker createCarrierPrivilegesTracker() throws Exception {
CarrierPrivilegesTracker cpt =
@@ -180,7 +199,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
}
private void setupCarrierPrivilegesTrackerWithCarrierConfigUids() throws Exception {
- setupCarrierConfigCerts(getHash(CERT_1), getHash(CERT_2));
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
setupInstalledPackages(
new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
@@ -188,7 +208,7 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
}
private void setupCarrierPrivilegesTrackerWithSimLoadedUids() throws Exception {
- setupSimLoadedCerts(getHash(CERT_1), getHash(CERT_2));
+ setupSimLoadedRules(ruleWithHashOnly(getHash(CERT_1)), ruleWithHashOnly(getHash(CERT_2)));
setupInstalledPackages(
new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
@@ -202,15 +222,13 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case REGISTRANT_WHAT: {
+ case REGISTRANT_WHAT:
AsyncResult asyncResult = (AsyncResult) msg.obj;
privilegedUids = (int[]) asyncResult.result;
numUidUpdates++;
break;
- }
- default: {
+ default:
fail("Unexpected msg received. what=" + msg.what);
- }
}
}
@@ -271,10 +289,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
- mCarrierConfigs.putStringArray(
- KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
- new String[] {getHash(CERT_1), getHash(CERT_2)});
- when(mCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfigs);
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID);
mTestableLooper.processAllMessages();
@@ -319,6 +335,34 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
}
@Test
+ public void testCarrierConfigUpdatedExplicitPackageNames() throws Exception {
+ // Start with privileges specified just by wildcard certificate hashes, verify specifying
+ // package names clears privileges on UIDs that don't match the updated rules.
+ setupCarrierPrivilegesTrackerWithCarrierConfigUids();
+
+ // Package 1 keeps its privileges by matching the first rule; the second rule no longer
+ // matches package 2.
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1), PACKAGE_1),
+ carrierConfigRuleString(getHash(CERT_2), PACKAGE_1));
+
+ sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID);
+ mTestableLooper.processAllMessages();
+
+ verifyPrivilegedUids(new int[] {UID_1} /* expectedUids */, 1 /* expectedUidUpdates */);
+
+ // Give package 2 privileges again.
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1), PACKAGE_1),
+ carrierConfigRuleString(getHash(CERT_2), PACKAGE_1, PACKAGE_2));
+
+ sendCarrierConfigChangedIntent(SUB_ID, PHONE_ID);
+ mTestableLooper.processAllMessages();
+
+ verifyPrivilegedUids(PRIVILEGED_UIDS /* expectedUids */, 2 /* expectedUidUpdates */);
+ }
+
+ @Test
public void testSimCardStateChanged() throws Exception {
// Start with packages installed and no certs
setupInstalledPackages(
@@ -326,9 +370,7 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
- when(mTelephonyManager.getCertsFromCarrierPrivilegeAccessRules())
- .thenReturn(Arrays.asList(getHash(CERT_1), getHash(CERT_2)));
- when(mTelephonyManager.hasIccCard(PHONE_ID)).thenReturn(true);
+ setupSimLoadedRules(ruleWithHashOnly(getHash(CERT_1)), ruleWithHashOnly(getHash(CERT_2)));
sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED);
mTestableLooper.processAllMessages();
@@ -344,9 +386,7 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
- when(mTelephonyManager.getCertsFromCarrierPrivilegeAccessRules())
- .thenReturn(Arrays.asList(getHash(CERT_1), getHash(CERT_2)));
- when(mTelephonyManager.hasIccCard(PHONE_ID)).thenReturn(true);
+ setupSimLoadedRules(ruleWithHashOnly(getHash(CERT_1)), ruleWithHashOnly(getHash(CERT_2)));
sendSimApplicationStateChangedIntent(PHONE_ID, SIM_STATE_LOADED);
mTestableLooper.processAllMessages();
@@ -392,9 +432,38 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
}
@Test
+ public void testSimStateChangedExplicitPackageNames() throws Exception {
+ // Start with privileges specified just by wildcard certificate hashes, verify specifying
+ // package names clears privileges on UIDs that don't match the updated rules.
+ setupCarrierPrivilegesTrackerWithSimLoadedUids();
+
+ // Package 1 keeps its privileges by matching the first rule; the second rule no longer
+ // matches package 2.
+ setupSimLoadedRules(
+ ruleWithHashAndPackage(getHash(CERT_1), PACKAGE_1),
+ ruleWithHashAndPackage(getHash(CERT_2), PACKAGE_1));
+
+ sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED);
+ mTestableLooper.processAllMessages();
+
+ verifyPrivilegedUids(new int[] {UID_1} /* expectedUids */, 1 /* expectedUidUpdates */);
+
+ // Give package 2 privileges again.
+ setupSimLoadedRules(
+ ruleWithHashAndPackage(getHash(CERT_1), PACKAGE_1),
+ ruleWithHashAndPackage(getHash(CERT_2), PACKAGE_1),
+ ruleWithHashAndPackage(getHash(CERT_2), PACKAGE_2));
+
+ sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_LOADED);
+ mTestableLooper.processAllMessages();
+
+ verifyPrivilegedUids(PRIVILEGED_UIDS /* expectedUids */, 2 /* expectedUidUpdates */);
+ }
+
+ @Test
public void testPackageAdded() throws Exception {
// Start with certs and no packages installed
- setupCarrierConfigCerts(getHash(CERT_1));
+ setupCarrierConfigRules(carrierConfigRuleString(getHash(CERT_1)));
mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1));
@@ -408,7 +477,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
@Test
public void testPackageAddedMultipleUsers() throws Exception {
// Start with certs and no packages installed
- setupCarrierConfigCerts(getHash(CERT_1), getHash(CERT_2));
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
setupInstalledPackages(
@@ -424,7 +494,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
@Test
public void testPackageReplaced() throws Exception {
// Start with certs and an unmatched package
- setupCarrierConfigCerts(getHash(CERT_1), getHash(CERT_2));
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_3, USER_1, UID_1));
mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
@@ -441,7 +512,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
@Test
public void testPackageAddedOrReplacedNoSignatures() throws Exception {
// Start with certs and packages installed
- setupCarrierConfigCerts(getHash(CERT_1), getHash(CERT_2));
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
setupInstalledPackages(
new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
@@ -462,7 +534,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
@Test
public void testPackageAddedOrReplacedSignatureChanged() throws Exception {
// Start with certs and packages installed
- setupCarrierConfigCerts(getHash(CERT_1), getHash(CERT_2));
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
setupInstalledPackages(
new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
@@ -482,7 +555,8 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
@Test
public void testPackageRemoved() throws Exception {
// Start with certs and packages installed
- setupCarrierConfigCerts(getHash(CERT_1), getHash(CERT_2));
+ setupCarrierConfigRules(
+ carrierConfigRuleString(getHash(CERT_1)), carrierConfigRuleString(getHash(CERT_2)));
setupInstalledPackages(
new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
@@ -531,9 +605,7 @@ public class CarrierPrivilegesTrackerTest extends TelephonyTest {
mContext.sendBroadcast(new Intent(action, new Uri.Builder().path(pkgName).build()));
}
- /**
- * Returns the SHA-1 hash (as a hex String) for the given hex String.
- */
+ /** Returns the SHA-1 hash (as a hex String) for the given hex String. */
private static String getHash(String hexString) throws Exception {
MessageDigest sha1 = MessageDigest.getInstance(SHA_1);
byte[] result = sha1.digest(IccUtils.hexStringToBytes(hexString));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
index f72ee2fef8..453dbd6994 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
import android.database.Cursor;
import android.database.MatrixCursor;
@@ -257,12 +258,15 @@ public class CarrierResolverTest extends TelephonyTest {
assertEquals(CID_VZW, mCarrierResolver.getCarrierId());
assertEquals(NAME, mCarrierResolver.getCarrierName());
// mock apn
+ doReturn(IccCardConstants.State.LOADED).when(mUiccProfile).getState();
((MockContentResolver) mContext.getContentResolver()).addProvider(
Carriers.CONTENT_URI.getAuthority(), new CarrierIdContentProvider());
mCarrierResolver.sendEmptyMessage(PREFER_APN_SET_EVENT);
processAllMessages();
assertEquals(CID_DOCOMO, mCarrierResolver.getCarrierId());
assertEquals(NAME_DOCOMO, mCarrierResolver.getCarrierName());
+ verify(mCarrierConfigManager).updateConfigForPhoneId(phoneId,
+ IccCardConstants.INTENT_VALUE_ICC_LOADED);
}
private class CarrierIdContentProvider extends MockContentProvider {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index 27ffeefb2a..c3eac09a1e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -31,9 +31,10 @@ import android.content.Context;
import android.content.Intent;
import android.os.Message;
import android.os.PersistableBundle;
-import android.provider.Settings;
import android.telephony.CarrierConfigManager;
+import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -69,12 +70,15 @@ public class CarrierServiceStateTrackerTest extends TelephonyTest {
super.setUp(getClass().getSimpleName());
mBundle = mContextFixture.getCarrierConfigBundle();
when(mPhone.getSubId()).thenReturn(SUB_ID);
+
mCarrierSST = new CarrierServiceStateTracker(mPhone, mSST);
mSpyCarrierSST = spy(mCarrierSST);
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
+ setCarrierPrivilegesForSubId(true, SUB_ID);
+
setDefaultValues();
processAllMessages();
}
@@ -150,21 +154,29 @@ public class CarrierServiceStateTrackerTest extends TelephonyTest {
doReturn(true).when(mSST).isRadioOn();
doReturn(mNotificationBuilder).when(spyPrefNetworkNotification).getNotificationBuilder();
- String prefNetworkMode = Settings.Global.PREFERRED_NETWORK_MODE + mPhone.getSubId();
- Settings.Global.putInt(mContext.getContentResolver(), prefNetworkMode,
- RILConstants.NETWORK_MODE_LTE_CDMA_EVDO);
- mSpyCarrierSST.getContentObserver().dispatchChange(false,
- Settings.Global.getUriFor(prefNetworkMode));
+
+ long networkType = (long) RadioAccessFamily.getRafFromNetworkType(
+ TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO);
+ int allowedNetworkTypeReason = TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER;
+ long allowedNetworkTypeValue = networkType;
+ doReturn(networkType).when(mPhone).getAllowedNetworkTypes(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER);
+ mSpyCarrierSST.getAllowedNetworkTypesChangedListener().onAllowedNetworkTypesChanged(
+ allowedNetworkTypeReason, allowedNetworkTypeValue);
+
processAllMessages();
verify(mNotificationManager, atLeast(1)).notify(
eq(CarrierServiceStateTracker.PREF_NETWORK_NOTIFICATION_TAG),
eq(SUB_ID), isA(Notification.class));
+ networkType = (long) RadioAccessFamily.getRafFromNetworkType(
+ TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+ allowedNetworkTypeValue = networkType;
+ doReturn(networkType).when(mPhone).getAllowedNetworkTypes(
+ TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER);
+ mSpyCarrierSST.getAllowedNetworkTypesChangedListener().onAllowedNetworkTypesChanged(
+ allowedNetworkTypeReason, allowedNetworkTypeValue);
- Settings.Global.putInt(mContext.getContentResolver(), prefNetworkMode,
- RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
- mSpyCarrierSST.getContentObserver().dispatchChange(false,
- Settings.Global.getUriFor(prefNetworkMode));
processAllMessages();
verify(mNotificationManager, atLeast(1)).cancel(
CarrierServiceStateTracker.PREF_NETWORK_NOTIFICATION_TAG, SUB_ID);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
index 9030124f81..85aafcfb61 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
@@ -18,20 +18,34 @@ package com.android.internal.telephony;
import static android.telephony.TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE;
import static android.telephony.TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
+import android.os.Build;
import android.os.Message;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
+import android.telephony.DataFailCause;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -45,7 +59,10 @@ import org.mockito.Mock;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Map;
import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -55,6 +72,38 @@ public class CarrierSignalAgentTest extends TelephonyTest {
private PersistableBundle mBundle;
private static final String PCO_RECEIVER = "pak/PCO_RECEIVER";
private static final String DC_ERROR_RECEIVER = "pak/DC_ERROR_RECEIVER";
+ private static final String LEGACY_RECEIVER = "old.pkg/LEGACY_RECEIVER";
+ private static final String PCO_PACKAGE = "pak";
+
+ private static final Intent FAKE_PCO_INTENT;
+ private static final Intent FAKE_REDIRECTED_INTENT;
+ private static final Intent FAKE_NETWORK_FAILED_INTENT;
+ private static final Intent FAKE_RESET_INTENT;
+ private static final Intent FAKE_DEFAULT_NETWORK_INTENT;
+ static {
+ FAKE_PCO_INTENT = new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE);
+ FAKE_PCO_INTENT.putExtra(TelephonyManager.EXTRA_APN_TYPE, ApnSetting.TYPE_MMS);
+ FAKE_PCO_INTENT.putExtra(TelephonyManager.EXTRA_APN_PROTOCOL, ApnSetting.PROTOCOL_IP);
+ FAKE_PCO_INTENT.putExtra(TelephonyManager.EXTRA_PCO_ID, 500);
+ FAKE_PCO_INTENT.putExtra(TelephonyManager.EXTRA_PCO_VALUE, new byte[]{1, 2, 3});
+
+ FAKE_REDIRECTED_INTENT = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED);
+ FAKE_REDIRECTED_INTENT.putExtra(TelephonyManager.EXTRA_APN_TYPE, ApnSetting.TYPE_MMS);
+ FAKE_REDIRECTED_INTENT.putExtra(TelephonyManager.EXTRA_REDIRECTION_URL, "example.com");
+
+ FAKE_NETWORK_FAILED_INTENT = new Intent(ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED);
+ FAKE_NETWORK_FAILED_INTENT.putExtra(TelephonyManager.EXTRA_APN_TYPE, ApnSetting.TYPE_MMS);
+ FAKE_NETWORK_FAILED_INTENT.putExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE,
+ DataFailCause.UNKNOWN_PDP_CONTEXT);
+
+ FAKE_RESET_INTENT = new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_RESET);
+
+ FAKE_DEFAULT_NETWORK_INTENT =
+ new Intent(TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE);
+ FAKE_DEFAULT_NETWORK_INTENT.putExtra(
+ TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, true);
+ }
+
@Mock
ResolveInfo mResolveInfo;
@@ -64,6 +113,21 @@ public class CarrierSignalAgentTest extends TelephonyTest {
super.setUp(getClass().getSimpleName());
mBundle = mContextFixture.getCarrierConfigBundle();
mCarrierSignalAgentUT = new CarrierSignalAgent(mPhone);
+
+ ComponentName legacyReceiverComponent = ComponentName.unflattenFromString(LEGACY_RECEIVER);
+ ApplicationInfo fakeLegacyApplicationInfo = new ApplicationInfo();
+ fakeLegacyApplicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
+
+ ApplicationInfo fakeApplicationInfo = new ApplicationInfo();
+ fakeApplicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(mContext.getPackageManager().getApplicationInfo(
+ nullable(String.class), anyInt()))
+ .thenReturn(fakeApplicationInfo);
+ when(mContext.getPackageManager().getApplicationInfo(
+ eq(legacyReceiverComponent.getPackageName()), anyInt()))
+ .thenReturn(fakeLegacyApplicationInfo);
+
processAllMessages();
logd("CarrierSignalAgentTest -Setup!");
}
@@ -78,11 +142,12 @@ public class CarrierSignalAgentTest extends TelephonyTest {
public void testNotifyManifestReceivers() throws Exception {
// Broadcast count
int count = 0;
- Intent intent = new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE);
+ Intent intent = new Intent(FAKE_PCO_INTENT);
mBundle.putStringArray(
CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
new String[]{PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE,
- DC_ERROR_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE
+ DC_ERROR_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE,
+ LEGACY_RECEIVER + ":" + TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE
});
// Verify no broadcast has been sent without carrier config
@@ -103,18 +168,142 @@ public class CarrierSignalAgentTest extends TelephonyTest {
doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
.when(mPackageManager).queryBroadcastReceivers((Intent) any(), anyInt());
mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
- count += 2;
+ count += 3;
mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
logd(mCaptorIntent.getAllValues().toString());
- Intent capturedIntent = mCaptorIntent.getAllValues().get(1);
- assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, capturedIntent.getAction());
- assertEquals(DC_ERROR_RECEIVER, capturedIntent.getComponent().flattenToString());
+ Map<String, Intent> componentToIntent = mCaptorIntent.getAllValues().stream()
+ .collect(Collectors.toMap((i) ->
+ i.getComponent() == null ? "null" : i.getComponent().flattenToString(),
+ Function.identity()));
+ Intent dcErrorIntent = componentToIntent.get(DC_ERROR_RECEIVER);
+ assertNotNull(dcErrorIntent);
+ assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, dcErrorIntent.getAction());
+
+ Intent pcoReceiverIntent = componentToIntent.get(PCO_RECEIVER);
+ assertNotNull(pcoReceiverIntent);
+ assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, pcoReceiverIntent.getAction());
+
+ Intent legacyReceiverIntent = componentToIntent.get(LEGACY_RECEIVER);
+ assertNotNull(legacyReceiverIntent);
+ assertEquals(TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+ legacyReceiverIntent.getAction());
+ }
+
+ @Test
+ @SmallTest
+ public void testLegacyConversionSupport() {
+ mBundle.putStringArray(
+ CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ new String[]{LEGACY_RECEIVER + ":" + String.join(",",
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET,
+ TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE)
+ });
+
+ // Trigger carrier config reloading
+ mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ processAllMessages();
+
+ // Verify broadcast has been sent to two different registered manifest receivers
+ doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
+ .when(mPackageManager).queryBroadcastReceivers((Intent) any(), anyInt());
+
+ int broadcastCount = 1;
+ {
+ mCarrierSignalAgentUT.notifyCarrierSignalReceivers(new Intent(FAKE_PCO_INTENT));
+ broadcastCount++;
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(broadcastCount)).sendBroadcast(intentCaptor.capture());
+ Intent intent = intentCaptor.getValue();
+ assertTrue(intent.hasExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX));
+
+ assertEquals(TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE, intent.getAction());
+ verifyLegacyApnTypes(intent, FAKE_PCO_INTENT);
+ assertEquals(FAKE_PCO_INTENT.getIntExtra(
+ TelephonyManager.EXTRA_PCO_ID, Integer.MAX_VALUE),
+ intent.getIntExtra(TelephonyIntents.EXTRA_PCO_ID, Integer.MIN_VALUE));
+ assertArrayEquals(FAKE_PCO_INTENT.getByteArrayExtra(
+ TelephonyManager.EXTRA_PCO_VALUE),
+ intent.getByteArrayExtra(TelephonyIntents.EXTRA_PCO_VALUE));
+ }
+
+ {
+ mCarrierSignalAgentUT.notifyCarrierSignalReceivers(new Intent(FAKE_REDIRECTED_INTENT));
+ broadcastCount++;
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(broadcastCount)).sendBroadcast(intentCaptor.capture());
+ Intent intent = intentCaptor.getValue();
+ assertTrue(intent.hasExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX));
+
+ assertEquals(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED, intent.getAction());
+ verifyLegacyApnTypes(intent, FAKE_REDIRECTED_INTENT);
+ assertEquals(
+ FAKE_REDIRECTED_INTENT.getStringExtra(TelephonyManager.EXTRA_REDIRECTION_URL),
+ intent.getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL));
+ }
- capturedIntent = mCaptorIntent.getAllValues().get(2);
- assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, capturedIntent.getAction());
- assertEquals(PCO_RECEIVER, capturedIntent.getComponent().flattenToString());
+ {
+ mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+ new Intent(FAKE_NETWORK_FAILED_INTENT));
+ broadcastCount++;
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(broadcastCount)).sendBroadcast(intentCaptor.capture());
+ Intent intent = intentCaptor.getValue();
+ assertTrue(intent.hasExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX));
+
+ assertEquals(TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ intent.getAction());
+ verifyLegacyApnTypes(intent, FAKE_REDIRECTED_INTENT);
+ assertEquals(
+ FAKE_NETWORK_FAILED_INTENT.getIntExtra(
+ TelephonyManager.EXTRA_DATA_FAIL_CAUSE, Integer.MAX_VALUE),
+ intent.getIntExtra(TelephonyIntents.EXTRA_ERROR_CODE, Integer.MIN_VALUE));
+ }
+
+ {
+ mCarrierSignalAgentUT.notifyCarrierSignalReceivers(new Intent(FAKE_RESET_INTENT));
+ broadcastCount++;
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(broadcastCount)).sendBroadcast(intentCaptor.capture());
+ Intent intent = intentCaptor.getValue();
+ assertTrue(intent.hasExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX));
+
+ assertEquals(TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET, intent.getAction());
+ }
+
+ {
+ mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+ new Intent(FAKE_DEFAULT_NETWORK_INTENT));
+ broadcastCount++;
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(broadcastCount)).sendBroadcast(intentCaptor.capture());
+ Intent intent = intentCaptor.getValue();
+ assertTrue(intent.hasExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX));
+
+ assertEquals(TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
+ intent.getAction());
+
+ assertEquals(
+ FAKE_DEFAULT_NETWORK_INTENT.getBooleanExtra(
+ TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, false),
+ intent.getBooleanExtra(TelephonyIntents.EXTRA_DEFAULT_NETWORK_AVAILABLE, true));
+ }
+ }
+
+ private void verifyLegacyApnTypes(Intent legacyIntent, Intent originalIntent) {
+ int apnType = originalIntent.getIntExtra(TelephonyManager.EXTRA_APN_TYPE,
+ Integer.MAX_VALUE);
+ String apnTypeString = ApnSetting.getApnTypeString(apnType);
+ assertNotEquals("Unknown", apnTypeString);
+
+ assertEquals(apnTypeString,
+ legacyIntent.getStringExtra(TelephonyIntents.EXTRA_APN_TYPE));
+ assertEquals(apnType,
+ legacyIntent.getIntExtra(TelephonyIntents.EXTRA_APN_TYPE_INT, Integer.MIN_VALUE));
}
@Test
@@ -142,7 +331,7 @@ public class CarrierSignalAgentTest extends TelephonyTest {
verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE,
mCaptorIntent.getValue().getAction());
- assertEquals(PCO_RECEIVER, mCaptorIntent.getValue().getComponent().flattenToString());
+ assertEquals(PCO_PACKAGE, mCaptorIntent.getValue().getPackage());
// Verify no broadcast has been sent to manifest receivers (bad config)
doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
@@ -189,7 +378,7 @@ public class CarrierSignalAgentTest extends TelephonyTest {
verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
assertEquals(ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
mCaptorIntent.getValue().getAction());
- assertEquals(PCO_RECEIVER, mCaptorIntent.getValue().getComponent().flattenToString());
+ assertEquals(PCO_PACKAGE, mCaptorIntent.getValue().getPackage());
// Both wake and no-wake signals are declared in the manifest
doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
index 77bea60049..d3b3e59720 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellIdentityLteTest.java
@@ -22,7 +22,10 @@ import android.telephony.CellInfo;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.stream.Collectors;
/** Unit tests for {@link CellIdentityLte}. */
@@ -253,4 +256,15 @@ public class CellIdentityLteTest extends AndroidTestCase {
CellIdentityLte newCi = CellIdentityLte.CREATOR.createFromParcel(p);
assertEquals(ci, newCi);
}
+
+ @SmallTest
+ public void testBands() {
+ android.hardware.radio.V1_5.CellIdentityLte cid =
+ new android.hardware.radio.V1_5.CellIdentityLte();
+ cid.bands = Arrays.stream(BANDS).boxed().collect(Collectors.toCollection(ArrayList::new));
+
+ CellIdentityLte cellIdentityLte = new CellIdentityLte(cid);
+ assertTrue(Arrays.equals(cellIdentityLte.getBands(), BANDS));
+
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
index a2e765ed5e..a29447102d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
@@ -18,19 +18,31 @@ package com.android.internal.telephony;
import static com.google.common.truth.Truth.assertThat;
-import android.hardware.radio.V1_4.NrSignalStrength;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+
+import android.hardware.radio.V1_6.NrSignalStrength;
import android.os.Parcel;
import android.telephony.CellInfo;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthNr;
-import android.test.AndroidTestCase;
+import android.telephony.ServiceState;
import com.google.common.collect.BoundType;
import com.google.common.collect.Range;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
-public class CellSignalStrengthNrTest extends AndroidTestCase {
+public class CellSignalStrengthNrTest extends TelephonyTest {
private static final int CSIRSRP = -123;
private static final int CSIRSRQ = -11;
private static final int ANOTHER_CSIRSRP = -111;
@@ -38,20 +50,44 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
private static final int INVALID_CSIRSRP = Integer.MAX_VALUE;
private static final int INVALID_SSRSRP = Integer.MAX_VALUE;
private static final int CSISINR = 18;
+ private static final int CSICQI_TABLE_INDEX = 1;
+ private static final ArrayList<Byte> CSICQI_REPORT =
+ new ArrayList<>(Arrays.asList((byte) 3, (byte) 2, (byte) 1));
private static final int SSRSRP = -112;
private static final int SSRSRQ = -13;
private static final int SSSINR = 32;
+ @Mock
+ ServiceState mSS;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private List<Integer> getCsiCqiList() {
+ return CSICQI_REPORT.stream()
+ .map(cqi -> new Integer(Byte.toUnsignedInt(cqi)))
+ .collect(Collectors.toList());
+ }
+
@Test
public void testGetMethod() {
// GIVEN an instance of CellSignalStrengthNr
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
// THEN the get method should return correct value
assertThat(css.getCsiRsrp()).isEqualTo(CSIRSRP);
assertThat(css.getCsiRsrq()).isEqualTo(CSIRSRQ);
assertThat(css.getCsiSinr()).isEqualTo(CSISINR);
+ assertThat(css.getCsiCqiTableIndex()).isEqualTo(CSICQI_TABLE_INDEX);
+ assertThat(css.getCsiCqiReport()).isEqualTo(getCsiCqiList());
assertThat(css.getSsRsrp()).isEqualTo(SSRSRP);
assertThat(css.getSsRsrq()).isEqualTo(SSRSRQ);
assertThat(css.getSsSinr()).isEqualTo(SSSINR);
@@ -62,18 +98,22 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
public void testGetMethodWithHal() {
// GIVEN an instance of NrSignalStrength with some positive values
NrSignalStrength nrSignalStrength = new NrSignalStrength();
- nrSignalStrength.csiRsrp = -CSIRSRP;
- nrSignalStrength.csiRsrq = -CSIRSRQ;
- nrSignalStrength.csiSinr = CSISINR;
- nrSignalStrength.ssRsrp = -SSRSRP;
- nrSignalStrength.ssRsrq = -SSRSRQ;
- nrSignalStrength.ssSinr = SSSINR;
+ nrSignalStrength.base.csiRsrp = -CSIRSRP;
+ nrSignalStrength.base.csiRsrq = -CSIRSRQ;
+ nrSignalStrength.base.csiSinr = CSISINR;
+ nrSignalStrength.csiCqiTableIndex = CSICQI_TABLE_INDEX;
+ nrSignalStrength.csiCqiReport = CSICQI_REPORT;
+ nrSignalStrength.base.ssRsrp = -SSRSRP;
+ nrSignalStrength.base.ssRsrq = -SSRSRQ;
+ nrSignalStrength.base.ssSinr = SSSINR;
// THEN the get method should return the correct value
CellSignalStrengthNr css = new CellSignalStrengthNr(nrSignalStrength);
assertThat(css.getCsiRsrp()).isEqualTo(CSIRSRP);
assertThat(css.getCsiRsrq()).isEqualTo(CSIRSRQ);
assertThat(css.getCsiSinr()).isEqualTo(CSISINR);
+ assertThat(css.getCsiCqiTableIndex()).isEqualTo(CSICQI_TABLE_INDEX);
+ assertThat(css.getCsiCqiReport()).isEqualTo(getCsiCqiList());
assertThat(css.getSsRsrp()).isEqualTo(SSRSRP);
assertThat(css.getSsRsrq()).isEqualTo(SSRSRQ);
assertThat(css.getSsSinr()).isEqualTo(SSSINR);
@@ -84,18 +124,22 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
public void testUnavailableValueWithHal() {
// GIVEN an instance of NrSignalStrength
NrSignalStrength nrSignalStrength = new NrSignalStrength();
- nrSignalStrength.csiRsrp = CellInfo.UNAVAILABLE;
- nrSignalStrength.csiRsrq = CellInfo.UNAVAILABLE;
- nrSignalStrength.csiSinr = CellInfo.UNAVAILABLE;
- nrSignalStrength.ssRsrp = CellInfo.UNAVAILABLE;
- nrSignalStrength.ssRsrq = CellInfo.UNAVAILABLE;
- nrSignalStrength.ssSinr = CellInfo.UNAVAILABLE;
+ nrSignalStrength.base.csiRsrp = CellInfo.UNAVAILABLE;
+ nrSignalStrength.base.csiRsrq = CellInfo.UNAVAILABLE;
+ nrSignalStrength.base.csiSinr = CellInfo.UNAVAILABLE;
+ nrSignalStrength.csiCqiTableIndex = CellInfo.UNAVAILABLE;
+ nrSignalStrength.csiCqiReport = new ArrayList<Byte>();
+ nrSignalStrength.base.ssRsrp = CellInfo.UNAVAILABLE;
+ nrSignalStrength.base.ssRsrq = CellInfo.UNAVAILABLE;
+ nrSignalStrength.base.ssSinr = CellInfo.UNAVAILABLE;
// THEN the get method should return unavailable value
CellSignalStrengthNr css = new CellSignalStrengthNr(nrSignalStrength);
assertThat(css.getCsiRsrp()).isEqualTo(CellInfo.UNAVAILABLE);
assertThat(css.getCsiRsrq()).isEqualTo(CellInfo.UNAVAILABLE);
assertThat(css.getCsiSinr()).isEqualTo(CellInfo.UNAVAILABLE);
+ assertThat(css.getCsiCqiTableIndex()).isEqualTo(CellInfo.UNAVAILABLE);
+ assertThat(css.getCsiCqiReport()).isEqualTo(Collections.emptyList());
assertThat(css.getSsRsrp()).isEqualTo(CellInfo.UNAVAILABLE);
assertThat(css.getSsRsrq()).isEqualTo(CellInfo.UNAVAILABLE);
assertThat(css.getSsSinr()).isEqualTo(CellInfo.UNAVAILABLE);
@@ -105,10 +149,10 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
@Test
public void testEquals_sameParameters() {
// GIVEN an instance of CellSignalStrengthNr and another object with the same parameters
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
- CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
// THEN this two objects are equivalent
assertThat(css).isEqualTo(anotherCss);
@@ -118,10 +162,11 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
public void testEquals_differentParameters() {
// GIVEN an instance of CellSignalStrengthNr and another object with some different
// parameters
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
- CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(
- ANOTHER_CSIRSRP, ANOTHER_CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr anotherCss = new CellSignalStrengthNr(ANOTHER_CSIRSRP,
+ ANOTHER_CSIRSRQ, CSISINR, CSICQI_TABLE_INDEX, CSICQI_REPORT,
+ SSRSRP, SSRSRQ, SSSINR);
// THEN this two objects are different
assertThat(css).isNotEqualTo(anotherCss);
@@ -130,8 +175,8 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
@Test
public void testAusLevel_validValue() {
// GIVEN an instance of CellSignalStrengthNr with valid csirsrp
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
// THEN the asu level is in range [0, 97]
assertThat(css.getAsuLevel()).isIn(Range.range(0, BoundType.CLOSED, 97, BoundType.CLOSED));
@@ -140,8 +185,8 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
@Test
public void testAsuLevel_invalidValue() {
// GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, INVALID_SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, INVALID_SSRSRP, SSRSRQ, SSSINR);
// THEN the asu level is unknown
assertThat(css.getAsuLevel()).isEqualTo(CellSignalStrengthNr.UNKNOWN_ASU_LEVEL);
@@ -151,8 +196,8 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
public void testSignalLevel_validValue() {
for (int ssRsrp = -140; ssRsrp <= -44; ssRsrp++) {
// GIVEN an instance of CellSignalStrengthNr with valid csirsrp
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, ssRsrp, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, INVALID_SSRSRP, SSRSRQ, SSSINR);
// THEN the signal level is valid
assertThat(css.getLevel()).isIn(Range.range(
@@ -164,8 +209,8 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
@Test
public void testSignalLevel_invalidValue() {
// GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, INVALID_SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
// THEN the signal level is unknown
assertThat(css.getLevel()).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
@@ -174,8 +219,8 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
@Test
public void testParcel() {
// GIVEN an instance of CellSignalStrengthNr
- CellSignalStrengthNr css = new CellSignalStrengthNr(
- CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR,
+ CSICQI_TABLE_INDEX, CSICQI_REPORT, SSRSRP, SSRSRQ, SSSINR);
// WHEN write the object to parcel and create another object with that parcel
Parcel parcel = Parcel.obtain();
@@ -188,8 +233,25 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
assertThat(anotherCss.getCsiRsrp()).isEqualTo(CSIRSRP);
assertThat(anotherCss.getCsiRsrq()).isEqualTo(CSIRSRQ);
assertThat(anotherCss.getCsiSinr()).isEqualTo(CSISINR);
+ assertThat(css.getCsiCqiTableIndex()).isEqualTo(CSICQI_TABLE_INDEX);
+ assertThat(css.getCsiCqiReport()).isEqualTo(getCsiCqiList());
assertThat(anotherCss.getSsRsrp()).isEqualTo(SSRSRP);
assertThat(anotherCss.getSsRsrq()).isEqualTo(SSRSRQ);
assertThat(anotherCss.getSsSinr()).isEqualTo(SSSINR);
}
+
+ @Test
+ public void testLevel() {
+ CellSignalStrengthNr css = new CellSignalStrengthNr(CSIRSRP, CSIRSRQ, CSISINR, SSRSRP,
+ SSRSRQ, SSSINR);
+
+ // No keys in the bundle - should use RSRP and default levels.
+ css.updateLevel(null, null);
+ assertEquals(0 /* NONE or UNKNOWN */, css.getLevel());
+
+ doReturn(10).when(mSS).getArfcnRsrpBoost();
+ // Add rsrp boost and level should change to 1 - POOR
+ css.updateLevel(null, mSS);
+ assertEquals(1 /* MODERATE */, css.getLevel());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
index e2bacc24bf..1b187ef452 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkServiceTest.java
@@ -32,8 +32,10 @@ import android.telephony.LteVopsSupportInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
import android.telephony.NetworkServiceCallback;
+import android.telephony.NrVopsSupportInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
+import android.telephony.VopsSupportInfo;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.internal.R;
@@ -156,7 +158,7 @@ public class CellularNetworkServiceTest extends TelephonyTest {
assertTrue(false);
}
- LteVopsSupportInfo lteVopsSupportInfo =
+ VopsSupportInfo lteVopsSupportInfo =
new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
@@ -164,7 +166,278 @@ public class CellularNetworkServiceTest extends TelephonyTest {
domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN, voiceRegState,
ServiceState.rilRadioTechnologyToNetworkType(voiceRadioTech), reasonForDenial,
false, availableServices, null, "", maxDataCalls, false, false, false,
- lteVopsSupportInfo, false);
+ lteVopsSupportInfo);
+
+ try {
+ verify(mCallback, timeout(1000).times(1)).onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ @MediumTest
+ public void testGetNetworkRegistrationInfoV1_5() {
+ // common parameters
+ int regState = NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+ int radioTech = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ int reasonForDenial = 0;
+
+ // voice services
+ List<Integer> availableVoiceServices = new ArrayList<>(Arrays.asList(new Integer[] {
+ NetworkRegistrationInfo.SERVICE_TYPE_VOICE,
+ NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+ NetworkRegistrationInfo.SERVICE_TYPE_VIDEO
+ }));
+
+ // Default value per 24.008
+ int maxDataCalls = 16;
+ // data service
+ List<Integer> availableDataServices = Arrays.asList(
+ NetworkRegistrationInfo.SERVICE_TYPE_DATA);
+
+ // ENDC parameters
+ boolean isEndcAvailable = true;
+ boolean isDcNrRestricted = false;
+ boolean isNrAvailable = true;
+
+ // LTE VoPS parameters
+ boolean isVopsSupported = true;
+ boolean isEmcBearerSupported = true;
+
+ android.hardware.radio.V1_5.RegStateResult regResult =
+ new android.hardware.radio.V1_5.RegStateResult();
+
+ regResult.regState = regState;
+ regResult.rat = radioTech;
+ regResult.reasonForDenial = reasonForDenial;
+
+ android.hardware.radio.V1_5.RegStateResult.AccessTechnologySpecificInfo
+ .EutranRegistrationInfo eutranInfo = new android.hardware.radio.V1_5
+ .RegStateResult.AccessTechnologySpecificInfo.EutranRegistrationInfo();
+ eutranInfo.lteVopsInfo.isVopsSupported = isVopsSupported;
+ eutranInfo.lteVopsInfo.isEmcBearerSupported = isEmcBearerSupported;
+ eutranInfo.nrIndicators.isEndcAvailable = isEndcAvailable;
+ eutranInfo.nrIndicators.isDcNrRestricted = isDcNrRestricted;
+ eutranInfo.nrIndicators.isNrAvailable = isNrAvailable;
+ regResult.accessTechnologySpecificInfo.eutranInfo(eutranInfo);
+
+ VopsSupportInfo vops = new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED, LteVopsSupportInfo.LTE_STATUS_SUPPORTED);
+
+ mSimulatedCommands.setVoiceRegStateResult(regResult);
+ mSimulatedCommands.setDataRegStateResult(regResult);
+
+ // test voice registration state
+ try {
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_CS, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ NetworkRegistrationInfo expectedState = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ regState, ServiceState.rilRadioTechnologyToNetworkType(radioTech), reasonForDenial,
+ false, availableVoiceServices, null, "", false, 0, 0, 0);
+
+ try {
+ verify(mCallback, timeout(1000).times(1)).onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // test data registration state
+ try {
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ expectedState = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ regState, ServiceState.rilRadioTechnologyToNetworkType(radioTech), reasonForDenial,
+ false, availableDataServices, null, "", maxDataCalls, isDcNrRestricted,
+ isNrAvailable, isEndcAvailable, vops);
+
+ try {
+ verify(mCallback, timeout(1000).times(1)).onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ @MediumTest
+ public void testGetNetworkRegistrationInfoV1_6WithLte() {
+ // common parameters
+ int regState = NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+ int radioTech = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ int reasonForDenial = 0;
+
+ // voice services
+ List<Integer> availableVoiceServices = new ArrayList<>(Arrays.asList(new Integer[] {
+ NetworkRegistrationInfo.SERVICE_TYPE_VOICE,
+ NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+ NetworkRegistrationInfo.SERVICE_TYPE_VIDEO
+ }));
+
+ // Default value per 24.008
+ int maxDataCalls = 16;
+ // data service
+ List<Integer> availableDataServices = Arrays.asList(
+ NetworkRegistrationInfo.SERVICE_TYPE_DATA);
+
+ // ENDC parameters
+ boolean isEndcAvailable = true;
+ boolean isDcNrRestricted = false;
+ boolean isNrAvailable = true;
+
+ // LTE VoPS parameters
+ boolean isVopsSupported = true;
+ boolean isEmcBearerSupported = true;
+
+ android.hardware.radio.V1_6.RegStateResult regResult =
+ new android.hardware.radio.V1_6.RegStateResult();
+
+ regResult.regState = regState;
+ regResult.rat = radioTech;
+ regResult.reasonForDenial = reasonForDenial;
+
+ android.hardware.radio.V1_5.RegStateResult.AccessTechnologySpecificInfo
+ .EutranRegistrationInfo eutranInfo = new android.hardware.radio.V1_5
+ .RegStateResult.AccessTechnologySpecificInfo.EutranRegistrationInfo();
+ eutranInfo.lteVopsInfo.isVopsSupported = isVopsSupported;
+ eutranInfo.lteVopsInfo.isEmcBearerSupported = isEmcBearerSupported;
+ eutranInfo.nrIndicators.isEndcAvailable = isEndcAvailable;
+ eutranInfo.nrIndicators.isDcNrRestricted = isDcNrRestricted;
+ eutranInfo.nrIndicators.isNrAvailable = isNrAvailable;
+ regResult.accessTechnologySpecificInfo.eutranInfo(eutranInfo);
+
+ VopsSupportInfo vops = new LteVopsSupportInfo(
+ LteVopsSupportInfo.LTE_STATUS_SUPPORTED, LteVopsSupportInfo.LTE_STATUS_SUPPORTED);
+
+ mSimulatedCommands.setVoiceRegStateResult(regResult);
+ mSimulatedCommands.setDataRegStateResult(regResult);
+
+ // test voice registration state
+ try {
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_CS, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ NetworkRegistrationInfo expectedState = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ regState, ServiceState.rilRadioTechnologyToNetworkType(radioTech), reasonForDenial,
+ false, availableVoiceServices, null, "", false, 0, 0, 0);
+
+ try {
+ verify(mCallback, timeout(1000).times(1)).onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // test data registration state
+ try {
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ expectedState = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ regState, ServiceState.rilRadioTechnologyToNetworkType(radioTech), reasonForDenial,
+ false, availableDataServices, null, "", maxDataCalls, isDcNrRestricted,
+ isNrAvailable, isEndcAvailable, vops);
+
+ try {
+ verify(mCallback, timeout(1000).times(1)).onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+
+ @Test
+ @MediumTest
+ public void testGetNetworkRegistrationInfoV1_6WithNr() {
+ // common parameters
+ int regState = NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+ int radioTech = ServiceState.RIL_RADIO_TECHNOLOGY_NR;
+ int reasonForDenial = 0;
+
+ // voice services
+ List<Integer> availableVoiceServices = new ArrayList<>(Arrays.asList(new Integer[] {
+ NetworkRegistrationInfo.SERVICE_TYPE_VOICE,
+ NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+ NetworkRegistrationInfo.SERVICE_TYPE_VIDEO
+ }));
+
+ // Default value per 24.008
+ int maxDataCalls = 16;
+ // data service
+ List<Integer> availableDataServices = Arrays.asList(
+ NetworkRegistrationInfo.SERVICE_TYPE_DATA);
+
+ // NR VoPS parameters
+ byte vopsSupported = android.hardware.radio.V1_6.VopsIndicator.VOPS_OVER_3GPP;
+ byte emcSupported = android.hardware.radio.V1_6.EmcIndicator.EMC_NR_CONNECTED_TO_5GCN;
+ byte emfSupported = android.hardware.radio.V1_6.EmfIndicator.EMF_NR_CONNECTED_TO_5GCN;
+
+ android.hardware.radio.V1_6.RegStateResult regResult =
+ new android.hardware.radio.V1_6.RegStateResult();
+
+ regResult.regState = regState;
+ regResult.rat = radioTech;
+ regResult.reasonForDenial = reasonForDenial;
+
+ regResult.accessTechnologySpecificInfo.ngranNrVopsInfo(new android.hardware.radio.V1_6
+ .NrVopsInfo());
+ regResult.accessTechnologySpecificInfo.ngranNrVopsInfo().vopsSupported = vopsSupported;
+ regResult.accessTechnologySpecificInfo.ngranNrVopsInfo().emcSupported = emcSupported;
+ regResult.accessTechnologySpecificInfo.ngranNrVopsInfo().emfSupported = emfSupported;
+ regResult.accessTechnologySpecificInfo.ngranNrVopsInfo(
+ regResult.accessTechnologySpecificInfo.ngranNrVopsInfo());
+
+ VopsSupportInfo vops = new NrVopsSupportInfo(vopsSupported, emcSupported, emfSupported);
+ mSimulatedCommands.setVoiceRegStateResult(regResult);
+ mSimulatedCommands.setDataRegStateResult(regResult);
+
+ // test voice registration state
+ try {
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_CS, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ NetworkRegistrationInfo expectedState = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ regState, ServiceState.rilRadioTechnologyToNetworkType(radioTech), reasonForDenial,
+ false, availableVoiceServices, null, "", false, 0, 0, 0);
+
+ try {
+ verify(mCallback, timeout(1000).times(1)).onRequestNetworkRegistrationInfoComplete(
+ eq(NetworkServiceCallback.RESULT_SUCCESS), eq(expectedState));
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ // test data registration state
+ try {
+ mBinder.requestNetworkRegistrationInfo(0, NetworkRegistrationInfo.DOMAIN_PS, mCallback);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+
+ expectedState = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ regState, ServiceState.rilRadioTechnologyToNetworkType(radioTech), reasonForDenial,
+ false, availableDataServices, null, "", maxDataCalls, false, false, false, vops);
try {
verify(mCallback, timeout(1000).times(1)).onRequestNetworkRegistrationInfoComplete(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java
index 0601384d0b..2c1e18b275 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellularNetworkValidatorTest.java
@@ -57,9 +57,9 @@ import org.mockito.Mock;
public class CellularNetworkValidatorTest extends TelephonyTest {
private CellularNetworkValidator mValidatorUT;
private static final PhoneCapability CAPABILITY_WITH_VALIDATION_SUPPORTED =
- new PhoneCapability(1, 1, 0, null, true);
+ new PhoneCapability(1, 1, null, true, new int[0]);
private static final PhoneCapability CAPABILITY_WITHOUT_VALIDATION_SUPPORTED =
- new PhoneCapability(1, 1, 0, null, false);
+ new PhoneCapability(1, 1, null, false, new int[0]);
private final CellIdentityLte mCellIdentityLte1 = new CellIdentityLte(123, 456, 0, 0, 111);
private final CellIdentityLte mCellIdentityLte2 = new CellIdentityLte(321, 654, 0, 0, 222);
@Mock
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 106a2a8576..f04e69f321 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -31,7 +31,9 @@ import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.DownloadManager;
+import android.app.KeyguardManager;
import android.app.NotificationManager;
+import android.app.UiModeManager;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -54,9 +56,11 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.Uri;
+import android.net.vcn.VcnManager;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
import android.os.Bundle;
@@ -79,6 +83,7 @@ import android.telephony.euicc.EuiccManager;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
+import android.util.DisplayMetrics;
import android.util.Log;
import com.google.common.collect.ArrayListMultimap;
@@ -96,6 +101,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
/**
* Controls a test {@link Context} as would be provided by the Android framework to an
@@ -192,6 +198,9 @@ public class ContextFixture implements TestFixture<Context> {
Intent serviceIntent,
ServiceConnection connection,
int flags) {
+ if (mMockBindingFailureForPackage.contains(serviceIntent.getPackage())) {
+ return false;
+ }
if (mServiceByServiceConnection.containsKey(connection)) {
throw new RuntimeException("ServiceConnection already bound: " + connection);
}
@@ -213,10 +222,11 @@ public class ContextFixture implements TestFixture<Context> {
public void unbindService(
ServiceConnection connection) {
IInterface service = mServiceByServiceConnection.remove(connection);
- if (service == null) {
- throw new RuntimeException("ServiceConnection not found: " + connection);
+ if (service != null) {
+ connection.onServiceDisconnected(mComponentNameByService.get(service));
+ } else {
+ logd("unbindService: ServiceConnection not found: " + connection);
}
- connection.onServiceDisconnected(mComponentNameByService.get(service));
}
@Override
@@ -260,15 +270,22 @@ public class ContextFixture implements TestFixture<Context> {
return mTelephonyRegistryManager;
case Context.SYSTEM_CONFIG_SERVICE:
return mSystemConfigManager;
+ case Context.KEYGUARD_SERVICE:
+ return mKeyguardManager;
+ case Context.VCN_MANAGEMENT_SERVICE:
+ return mVcnManager;
case Context.BATTERY_STATS_SERVICE:
case Context.DISPLAY_SERVICE:
case Context.POWER_SERVICE:
case Context.PERMISSION_SERVICE:
+ case Context.LEGACY_PERMISSION_SERVICE:
// These are final classes so cannot be mocked,
// return real services.
return TestApplication.getAppContext().getSystemService(name);
case Context.POWER_WHITELIST_MANAGER:
return mPowerWhitelistManager;
+ case Context.LOCATION_SERVICE:
+ return mLocationManager;
default:
return null;
}
@@ -290,6 +307,20 @@ public class ContextFixture implements TestFixture<Context> {
return Context.POWER_WHITELIST_MANAGER;
} else if (serviceClass == SystemConfigManager.class) {
return Context.SYSTEM_CONFIG_SERVICE;
+ } else if (serviceClass == ActivityManager.class) {
+ return Context.ACTIVITY_SERVICE;
+ } else if (serviceClass == LocationManager.class) {
+ return Context.LOCATION_SERVICE;
+ } else if (serviceClass == CarrierConfigManager.class) {
+ return Context.CARRIER_CONFIG_SERVICE;
+ } else if (serviceClass == TelephonyManager.class) {
+ return Context.TELEPHONY_SERVICE;
+ } else if (serviceClass == UiModeManager.class) {
+ return Context.UI_MODE_SERVICE;
+ } else if (serviceClass == KeyguardManager.class) {
+ return Context.KEYGUARD_SERVICE;
+ } else if (serviceClass == VcnManager.class) {
+ return Context.VCN_MANAGEMENT_SERVICE;
}
return super.getSystemServiceName(serviceClass);
}
@@ -310,6 +341,11 @@ public class ContextFixture implements TestFixture<Context> {
}
@Override
+ public Context createConfigurationContext(Configuration overrideConfiguration) {
+ return spy(new FakeContext());
+ }
+
+ @Override
public ApplicationInfo getApplicationInfo() {
return mApplicationInfo;
}
@@ -440,6 +476,12 @@ public class ContextFixture implements TestFixture<Context> {
}
@Override
+ public void sendBroadcastMultiplePermissions(Intent intent,
+ String[] includePermissions, String[] excludePermissions) {
+ sendBroadcast(intent);
+ }
+
+ @Override
public Context createContextAsUser(UserHandle user, int flags) {
return this;
}
@@ -536,6 +578,11 @@ public class ContextFixture implements TestFixture<Context> {
}
@Override
+ public void enforceCallingPermission(String permission, String message) {
+ enforceCallingOrSelfPermission(permission, message);
+ }
+
+ @Override
public int checkCallingOrSelfPermission(String permission) {
if (mPermissionTable.contains(permission)
|| mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {
@@ -582,6 +629,7 @@ public class ContextFixture implements TestFixture<Context> {
private final Map<ComponentName, IntentFilter> mIntentFilterByComponentName = new HashMap<>();
private final Map<IInterface, ComponentName> mComponentNameByService =
new HashMap<IInterface, ComponentName>();
+ private final Set<String> mMockBindingFailureForPackage = new HashSet();
private final Map<ServiceConnection, IInterface> mServiceByServiceConnection =
new HashMap<ServiceConnection, IInterface>();
private final Multimap<String, BroadcastReceiver> mBroadcastReceiversByAction =
@@ -598,7 +646,6 @@ public class ContextFixture implements TestFixture<Context> {
// The application context is the most important object this class provides to the system
// under test.
private final Context mContext = spy(new FakeContext());
-
// We then create a spy on the application context allowing standard Mockito-style
// when(...) logic to be used to add specific little responses where needed.
@@ -626,10 +673,14 @@ public class ContextFixture implements TestFixture<Context> {
mock(TelephonyRegistryManager.class);
private final SystemConfigManager mSystemConfigManager = mock(SystemConfigManager.class);
private final PowerWhitelistManager mPowerWhitelistManager = mock(PowerWhitelistManager.class);
+ private final LocationManager mLocationManager = mock(LocationManager.class);
+ private final KeyguardManager mKeyguardManager = mock(KeyguardManager.class);
+ private final VcnManager mVcnManager = mock(VcnManager.class);
private final ContentProvider mContentProvider = spy(new FakeContentProvider());
private final Configuration mConfiguration = new Configuration();
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private final SharedPreferences mSharedPreferences = PreferenceManager
.getDefaultSharedPreferences(TestApplication.getAppContext());
private final MockContentResolver mContentResolver = new MockContentResolver();
@@ -677,13 +728,15 @@ public class ContextFixture implements TestFixture<Context> {
doReturn(mBundle).when(mCarrierConfigManager).getConfig();
doReturn(mock(Network.class)).when(mConnectivityManager).registerNetworkAgent(
- any(), any(), any(), any(), anyInt(), any(), anyInt());
+ any(), any(), any(), any(), any(), any(), anyInt());
doReturn(true).when(mEuiccManager).isEnabled();
mConfiguration.locale = Locale.US;
doReturn(mConfiguration).when(mResources).getConfiguration();
+ mDisplayMetrics.density = 2.25f;
+ doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider);
// Settings caches the provider after first get/set call, this is needed to make sure
// Settings is using mContentProvider as the cached provider across all tests.
@@ -744,6 +797,10 @@ public class ContextFixture implements TestFixture<Context> {
mComponentNameByService.put(service, name);
}
+ public void mockBindingFailureForPackage(String packageName) {
+ mMockBindingFailureForPackage.add(packageName);
+ }
+
private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
List<ResolveInfo> result = new ArrayList<ResolveInfo>();
for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 81c2f5607d..c6be1a47ef 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -24,13 +24,11 @@ import static org.mockito.Mockito.verify;
import android.telephony.CellIdentityGsm;
import android.telephony.CellInfo;
-import android.telephony.DataFailCause;
import android.telephony.DisconnectCause;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDisconnectCause;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.PhoneInternalInterface.DataActivityState;
@@ -114,7 +112,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
doReturn(1).when(mPhone).getSubId();
doReturn(2).when(mPhone).getPhoneId();
mDefaultPhoneNotifierUT.notifySignalStrength(mPhone);
- verify(mTelephonyRegistryManager).notifySignalStrengthChanged(eq(1), eq(2),
+ verify(mTelephonyRegistryManager).notifySignalStrengthChanged(eq(2), eq(1),
signalStrengthArgumentCaptor.capture());
assertEquals(99, signalStrengthArgumentCaptor.getValue().getGsmSignalStrength());
}
@@ -137,19 +135,19 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
public void testNotifyMessageWaiting() throws Exception {
doReturn(1).when(mPhone).getPhoneId();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(0, 1, false);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(1, 0, false);
doReturn(2).when(mPhone).getPhoneId();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(0, 2, false);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(2, 0, false);
doReturn(1).when(mPhone).getSubId();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(1, 2, false);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(2, 1, false);
doReturn(true).when(mPhone).getMessageWaitingIndicator();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(1, 2, true);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(2, 1, true);
}
@Test @SmallTest
@@ -168,28 +166,6 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
}
@Test @SmallTest
- public void testNotifyDataConnectionFailed() throws Exception {
- mDefaultPhoneNotifierUT.notifyDataConnectionFailed(mPhone, "default", "APN_0",
- DataFailCause.INSUFFICIENT_RESOURCES);
- verify(mTelephonyRegistryManager).notifyPreciseDataConnectionFailed(
- eq(0), eq(0), eq(ApnSetting.TYPE_DEFAULT), eq("APN_0"),
- eq(DataFailCause.INSUFFICIENT_RESOURCES));
-
- mDefaultPhoneNotifierUT.notifyDataConnectionFailed(mPhone, "default", "APN_1",
- DataFailCause.INSUFFICIENT_RESOURCES);
- verify(mTelephonyRegistryManager).notifyPreciseDataConnectionFailed(
- eq(0), eq(0), eq(ApnSetting.TYPE_DEFAULT), eq("APN_1"),
- eq(DataFailCause.INSUFFICIENT_RESOURCES));
-
- doReturn(1).when(mPhone).getSubId();
- mDefaultPhoneNotifierUT.notifyDataConnectionFailed(mPhone, "default", "APN_1",
- DataFailCause.INSUFFICIENT_RESOURCES);
- verify(mTelephonyRegistryManager).notifyPreciseDataConnectionFailed(
- eq(1), eq(0), eq(ApnSetting.TYPE_DEFAULT), eq("APN_1"),
- eq(DataFailCause.INSUFFICIENT_RESOURCES));
- }
-
- @Test @SmallTest
public void testNotifyPreciseCallState() throws Exception {
//mock forground/background/ringing call and call state
@@ -253,7 +229,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
// mock gsm cell location
CellIdentityGsm mGsmCellLocation = new CellIdentityGsm(
2, 3, 0, 0, null, null, null, null, Collections.emptyList());
- doReturn(mGsmCellLocation).when(mPhone).getCellIdentity();
+ doReturn(mGsmCellLocation).when(mPhone).getCurrentCellIdentity();
ArgumentCaptor<CellIdentityGsm> cellLocationCapture =
ArgumentCaptor.forClass(CellIdentityGsm.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
index be03ecaefc..44f3aa5bfd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -32,9 +33,12 @@ import static org.mockito.Mockito.verify;
import static java.util.Arrays.asList;
import android.annotation.IntDef;
+import android.app.UiModeManager;
+import android.content.Context;
import android.content.Intent;
import android.hardware.radio.V1_5.IndicationFilter;
import android.net.ConnectivityManager;
+import android.net.TetheringManager;
import android.os.BatteryManager;
import android.os.Message;
import android.test.suitebuilder.annotation.MediumTest;
@@ -46,6 +50,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -127,6 +132,8 @@ public class DeviceStateMonitorTest extends TelephonyTest {
STATE_TYPE_CHARGING, STATE_TYPE_SCREEN, STATE_TYPE_TETHERING}
);
+ @Mock
+ UiModeManager mUiModeManager;
private DeviceStateMonitor mDSM;
// Given a stateType, return the event type that can change the state
private int state2Event(@StateType int stateType) {
@@ -154,6 +161,9 @@ public class DeviceStateMonitorTest extends TelephonyTest {
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
+ mContextFixture.setSystemService(Context.UI_MODE_SERVICE, mUiModeManager);
+ // We don't even need a mock executor, we just need to not throw.
+ doReturn(null).when(mContextFixture.getTestDouble()).getMainExecutor();
mDSM = new DeviceStateMonitor(mPhone);
// Initialize with ALL states off
@@ -241,7 +251,7 @@ public class DeviceStateMonitorTest extends TelephonyTest {
@Test
public void testTethering() {
// Turn tethering on
- Intent intent = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+ Intent intent = new Intent(TetheringManager.ACTION_TETHER_STATE_CHANGED);
intent.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, new ArrayList<>(asList("abc")));
mContext.sendBroadcast(intent);
processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 86cf43f8c3..f2624afae5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -112,8 +112,12 @@ public class FakeTelephonyProvider extends MockContentProvider {
+ Telephony.SimInfo.COLUMN_IMSI + " TEXT,"
+ Telephony.SimInfo.COLUMN_ACCESS_RULES_FROM_CARRIER_CONFIGS + " BLOB,"
+ Telephony.SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED + " INTEGER DEFAULT 1,"
- + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES + " BIGINT DEFAULT -1, "
- + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0"
+ + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES + " BIGINT DEFAULT -1,"
+ + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0, "
+ + Telephony.SimInfo.COLUMN_RCS_CONFIG + " BLOB,"
+ + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS + " TEXT,"
+ + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING + " INTEGER DEFAULT 0,"
+ + Telephony.SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS + "TEXT"
+ ");";
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
new file mode 100644
index 0000000000..8939977166
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.IBootstrapAuthenticationCallback;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.GbaAuthRequest;
+import android.telephony.gba.GbaService;
+import android.telephony.gba.IGbaService;
+import android.telephony.gba.UaSecurityProtocolIdentifier;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for GbaManager
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public final class GbaManagerTest {
+ private static final String LOG_TAG = "GbaManagerTest";
+
+ private static final ComponentName TEST_DEFAULT_SERVICE_NAME = new ComponentName(
+ "TestGbaPkg", "TestGbaService");
+ private static final ComponentName TEST_SERVICE2_NAME = new ComponentName(
+ "TestGbaPkg2", "TestGbaService2");
+ private static final int RELEASE_NEVER = -1;
+ private static final int RELEASE_IMMEDIATELY = 0;
+ private static final int RELEASE_TIME_60S = 60 * 1000;
+ private static final int TEST_SUB_ID = Integer.MAX_VALUE;
+
+ @Mock Context mMockContext;
+ @Mock IBinder mMockBinder;
+ @Mock IGbaService mMockGbaServiceBinder;
+ @Mock IBootstrapAuthenticationCallback mMockCallback;
+ private GbaManager mTestGbaManager;
+ private Handler mHandler;
+ private TestableLooper mLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ log("setUp");
+ MockitoAnnotations.initMocks(this);
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+ when(mMockGbaServiceBinder.asBinder()).thenReturn(mMockBinder);
+ mTestGbaManager = new GbaManager(mMockContext, TEST_SUB_ID, null, 0);
+ mHandler = mTestGbaManager.getHandler();
+ try {
+ mLooper = new TestableLooper(mHandler.getLooper());
+ } catch (Exception e) {
+ fail("Unable to create looper from handler.");
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ log("tearDown");
+ mTestGbaManager.destroy();
+ mTestGbaManager = null;
+ mLooper.destroy();
+ mLooper = null;
+ }
+
+ @Test
+ @SmallTest
+ public void testFailOnRequest() throws Exception {
+ GbaAuthRequest request = createDefaultRequest();
+
+ mTestGbaManager.bootstrapAuthenticationRequest(request);
+ mLooper.processAllMessages();
+
+ verify(mMockContext, never()).bindService(any(), any(), anyInt());
+ verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt());
+ assertTrue(!mTestGbaManager.isServiceConnected());
+ }
+
+ @Test
+ @SmallTest
+ public void testBindServiceOnRequest() throws Exception {
+ mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+ GbaAuthRequest request = createDefaultRequest();
+
+ mTestGbaManager.bootstrapAuthenticationRequest(request);
+ mLooper.processAllMessages();
+ bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
+ mLooper.processAllMessages();
+
+ verify(mMockGbaServiceBinder).authenticationRequest(any());
+ assertTrue(mTestGbaManager.isServiceConnected());
+ }
+
+ @Test
+ @SmallTest
+ public void testFailAndRetryOnRequest() throws RemoteException {
+ when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(false);
+ mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+ GbaAuthRequest request = createDefaultRequest();
+
+ mTestGbaManager.bootstrapAuthenticationRequest(request);
+
+ for (int i = 0; i < GbaManager.MAX_RETRY; i++) {
+ mLooper.processAllMessages();
+ verify(mMockContext, times(i + 1)).bindService(any(), any(), anyInt());
+ try {
+ Thread.sleep(GbaManager.RETRY_TIME_MS + 500);
+ } catch (InterruptedException e) {
+ }
+ }
+ assertTrue(!mTestGbaManager.isServiceConnected());
+ mLooper.processAllMessages();
+ verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt());
+ }
+
+ @Test
+ @SmallTest
+ public void testBindServiceWhenPackageNameChanged() {
+ mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+ mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S);
+ GbaAuthRequest request = createDefaultRequest();
+
+ mTestGbaManager.bootstrapAuthenticationRequest(request);
+ mLooper.processAllMessages();
+ ServiceConnection conn = bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
+ mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName());
+
+ assertEquals(TEST_SERVICE2_NAME.getPackageName(), mTestGbaManager.getServicePackage());
+
+ mLooper.processAllMessages();
+ unbindService(conn);
+ bindAndConnectService(TEST_SERVICE2_NAME);
+ assertTrue(mTestGbaManager.isServiceConnected());
+ }
+
+ @Test
+ @SmallTest
+ public void testBindServiceWhenReleaseTimeChanged() {
+ mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+ mTestGbaManager.overrideReleaseTime(RELEASE_NEVER);
+
+ assertEquals(RELEASE_NEVER, mTestGbaManager.getReleaseTime());
+ mLooper.processAllMessages();
+ bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
+
+ assertTrue(mTestGbaManager.isServiceConnected());
+ }
+
+ @Test
+ @SmallTest
+ public void testDontBindServiceWhenPackageNameChanged() {
+ mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName());
+
+ mLooper.processAllMessages();
+
+ verify(mMockContext, never()).bindService(any(), any(), anyInt());
+ assertTrue(!mTestGbaManager.isServiceConnected());
+ }
+
+ @Test
+ @SmallTest
+ public void testDontBindServiceWhenReleaseTimeChanged() {
+ mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+ mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S);
+
+ mLooper.processAllMessages();
+
+ verify(mMockContext, never()).bindService(any(), any(), anyInt());
+ assertTrue(!mTestGbaManager.isServiceConnected());
+ }
+
+ private ServiceConnection bindAndConnectService(ComponentName component) {
+ ServiceConnection connection = bindService(component);
+ IGbaService.Stub serviceStub = mock(IGbaService.Stub.class);
+ when(mMockBinder.isBinderAlive()).thenReturn(true);
+ when(serviceStub.queryLocalInterface(any())).thenReturn(mMockGbaServiceBinder);
+ connection.onServiceConnected(component, serviceStub);
+ return connection;
+ }
+
+ private ServiceConnection bindService(ComponentName component) {
+ ArgumentCaptor<Intent> intentCaptor =
+ ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<ServiceConnection> serviceCaptor =
+ ArgumentCaptor.forClass(ServiceConnection.class);
+ verify(mMockContext, atLeastOnce()).bindService(intentCaptor.capture(),
+ serviceCaptor.capture(), eq(
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE));
+ Intent testIntent = intentCaptor.getValue();
+ assertEquals(GbaService.SERVICE_INTERFACE, testIntent.getAction());
+ assertEquals(component.getPackageName(), testIntent.getPackage());
+ return serviceCaptor.getValue();
+ }
+
+ private void unbindService(ServiceConnection conn) {
+ verify(mMockContext).unbindService(eq(conn));
+ }
+
+ private GbaAuthRequest createDefaultRequest() {
+ final String naf = "3GPP-bootstrapping@naf1.operator.com";
+ final UaSecurityProtocolIdentifier.Builder builder =
+ new UaSecurityProtocolIdentifier.Builder();
+ builder.setOrg(UaSecurityProtocolIdentifier.ORG_3GPP).setProtocol(
+ UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS);
+ return new GbaAuthRequest(TEST_SUB_ID, TelephonyManager.APPTYPE_USIM,
+ Uri.parse(naf), builder.build().toByteArray(), true, mMockCallback);
+ }
+
+ private void log(String str) {
+ Log.d(LOG_TAG, str);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTest.java
index d4c3d029a8..15eb59eed3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTest.java
@@ -121,6 +121,8 @@ public class GsmCdmaCallTest extends TelephonyTest {
doReturn(Call.State.DISCONNECTED).when(mConnection2).getState();
mCallUnderTest.connectionDisconnected(null);
assertEquals(Call.State.DISCONNECTED, mCallUnderTest.getState());
+ mCallUnderTest.onHangupLocal();
+ assertEquals(Call.State.DISCONNECTED, mCallUnderTest.getState());
}
} \ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index c0b07a8437..3fdcc78c75 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -32,6 +32,7 @@ import android.os.Message;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
+import android.telephony.emergency.EmergencyNumber;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -39,6 +40,8 @@ import android.testing.TestableLooper;
import androidx.test.filters.FlakyTest;
+import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -53,6 +56,7 @@ import org.mockito.Mock;
public class GsmCdmaCallTrackerTest extends TelephonyTest {
private static final int VOICE_CALL_STARTED_EVENT = 0;
private static final int VOICE_CALL_ENDED_EVENT = 1;
+ private static final String TEST_DIAL_STRING = "54321";
private String mDialString = PhoneNumberUtils.stripSeparators("+17005554141");
/* Handler class initiated at the HandlerThread */
private GsmCdmaCallTracker mCTUT;
@@ -83,6 +87,16 @@ public class GsmCdmaCallTrackerTest extends TelephonyTest {
@Test
@SmallTest
+ public void testCLIRMode() throws Exception {
+ DialArgs dialArgs = new DialArgs.Builder().setIsEmergency(true).build();
+ mCTUT.dialGsm(TEST_DIAL_STRING, dialArgs);
+ verify(mSimulatedCommandsVerifier).dial(eq(TEST_DIAL_STRING), eq(true),
+ isA(EmergencyNumber.class), eq(false), eq(CommandsInterface.CLIR_SUPPRESSION),
+ eq((UUSInfo) null), isA(Message.class));
+ }
+
+ @Test
+ @SmallTest
public void testMOCallDial() {
doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
@@ -90,7 +104,7 @@ public class GsmCdmaCallTrackerTest extends TelephonyTest {
assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
try {
- mCTUT.dial(mDialString, new Bundle());
+ mCTUT.dial(mDialString, new DialArgs.Builder().build());
processAllMessages();
} catch(Exception ex) {
ex.printStackTrace();
@@ -203,7 +217,7 @@ public class GsmCdmaCallTrackerTest extends TelephonyTest {
String mDialString = PhoneNumberUtils.stripSeparators("+17005554142");
try {
- mCTUT.dial(mDialString, new Bundle());
+ mCTUT.dial(mDialString, new DialArgs.Builder().build());
} catch(Exception ex) {
ex.printStackTrace();
Assert.fail("unexpected exception thrown" + ex.getMessage());
@@ -449,7 +463,7 @@ public class GsmCdmaCallTrackerTest extends TelephonyTest {
processAllMessages();
// Try to place another call.
try {
- mCTUT.dial("650-555-1212", new Bundle());
+ mCTUT.dial("650-555-1212", new DialArgs.Builder().build());
} catch (CallStateException cse) {
assertEquals(CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS, cse.getError());
return;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
index 93e3d87997..a5d0b4d790 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
@@ -30,12 +30,17 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import java.util.ArrayList;
+import java.util.Arrays;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class GsmCdmaConnectionTest extends TelephonyTest {
@@ -57,7 +62,10 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
@After
public void tearDown() throws Exception {
- connection = null;
+ if (connection != null) {
+ connection.dispose();
+ connection = null;
+ }
super.tearDown();
}
@@ -65,7 +73,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
public void testFormatDialString(){
connection = new GsmCdmaConnection(mPhone, String.format(
"+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
/* case 1: If PAUSE/WAIT sequence at the end, ignore them */
String formattedDialStr = connection.formatDialString(
String.format("+1 (700).555-41NN1234%c", PhoneNumberUtils.PAUSE));
@@ -81,12 +89,12 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
public void testOriginalDialString(){
doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
connection = new GsmCdmaConnection(mPhone, "+8610000", mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
assertEquals("+8610000", connection.getOrigDialString());
doReturn(PhoneConstants.PHONE_TYPE_GSM).when(mPhone).getPhoneType();
connection = new GsmCdmaConnection(mPhone, "+8610000", mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
assertEquals("+8610000", connection.getOrigDialString());
}
@@ -94,7 +102,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
public void testSanityGSM() {
connection = new GsmCdmaConnection(mPhone, String.format(
"+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
logd("Testing initial state of GsmCdmaConnection");
assertEquals(GsmCdmaCall.State.IDLE, connection.getState());
assertEquals(Connection.PostDialState.NOT_STARTED, connection.getPostDialState());
@@ -112,7 +120,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
connection = new GsmCdmaConnection(mPhone, String.format(
"+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
logd("Testing initial state of GsmCdmaConnection");
assertEquals(GsmCdmaCall.State.IDLE, connection.getState());
assertEquals(Connection.PostDialState.NOT_STARTED, connection.getPostDialState());
@@ -129,7 +137,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
public void testConnectionStateUpdate() {
connection = new GsmCdmaConnection(mPhone, String.format(
"+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
logd("Update the connection state from idle to active");
mDC.state = DriverCall.State.ACTIVE;
connection.update(mDC);
@@ -149,7 +157,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
connection = new GsmCdmaConnection(mPhone, String.format(
"+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
logd("Mock connection state from alerting to active ");
mDC.state = DriverCall.State.ALERTING;
connection.update(mDC);
@@ -167,7 +175,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
public void testGSMPostDialPause() {
connection = new GsmCdmaConnection(mPhone, String.format(
"+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
logd("Mock connection state from alerting to active ");
mDC.state = DriverCall.State.ALERTING;
connection.update(mDC);
@@ -187,7 +195,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
connection = new GsmCdmaConnection(mPhone,
String.format("+1 (700).555-41NN%c1234", PhoneNumberUtils.WAIT),mCT,null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
logd("Mock connection state transition from alerting to active ");
mDC.state = DriverCall.State.ALERTING;
connection.update(mDC);
@@ -204,7 +212,7 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
public void testHangUpConnection() {
connection = new GsmCdmaConnection(mPhone, String.format(
"+1 (700).555-41NN%c1234", PhoneNumberUtils.PAUSE), mCT, null,
- false /*isEmergencyCall*/);
+ new DialArgs.Builder().build());
mDC.state = DriverCall.State.ACTIVE;
connection.update(mDC);
logd("Hangup the connection locally");
@@ -229,7 +237,8 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
{"+8112345*00000", "+8112345", "+8112345*00000"}};
mDC.state = DriverCall.State.ALERTING;
for (String[] testAddress : testAddressMappingSet) {
- connection = new GsmCdmaConnection(mPhone, testAddress[0], mCT, null, false);
+ connection = new GsmCdmaConnection(mPhone, testAddress[0], mCT, null,
+ new DialArgs.Builder().build());
connection.setIsIncoming(true);
mDC.number = testAddress[1];
connection.update(mDC);
@@ -243,10 +252,30 @@ public class GsmCdmaConnectionTest extends TelephonyTest {
@Test @SmallTest
public void testAddressUpdateOutgoing() {
mDC.state = DriverCall.State.ALERTING;
- connection = new GsmCdmaConnection(mPhone, "12345", mCT, null, false);
+ connection = new GsmCdmaConnection(mPhone, "12345", mCT, null,
+ new DialArgs.Builder().build());
connection.setIsIncoming(false);
mDC.number = "678";
connection.update(mDC);
assertEquals("12345", connection.getAddress());
}
+
+ @Test @SmallTest
+ public void testRedirectingAddressUpdate() {
+ String forwardedNumber = "11111";
+
+ connection = new GsmCdmaConnection(mPhone, "12345", mCT, null,
+ new DialArgs.Builder().build());
+ connection.setIsIncoming(true);
+ assertEquals(null, connection.getForwardedNumber());
+ mDC.state = DriverCall.State.ALERTING;
+ mDC.forwardedNumber = forwardedNumber;
+ connection.update(mDC);
+ assertEquals(new ArrayList<String>(Arrays.asList(forwardedNumber)),
+ connection.getForwardedNumber());
+
+ connection = new GsmCdmaConnection(mPhone, mDC, mCT, 0);
+ assertEquals(new ArrayList<String>(Arrays.asList(forwardedNumber)),
+ connection.getForwardedNumber());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 562a60808c..502399c9db 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -25,6 +25,8 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -43,6 +45,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -58,10 +61,12 @@ import android.telephony.CarrierConfigManager;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -69,6 +74,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.FlakyTest;
import com.android.internal.telephony.test.SimulatedCommands;
+import com.android.internal.telephony.test.SimulatedCommandsVerifier;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccRecords;
@@ -87,6 +93,7 @@ import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
+import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@@ -105,6 +112,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
private static final int EVENT_EMERGENCY_CALLBACK_MODE_EXIT = 1;
private static final int EVENT_EMERGENCY_CALL_TOGGLE = 2;
private static final int EVENT_SET_ICC_LOCK_ENABLED = 3;
+ private static final int EVENT_SILENT_REDIAL_CONNECTION_CHANGED = 4;
private void switchToGsm() {
mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
@@ -127,9 +135,10 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
super.setUp(getClass().getSimpleName());
doReturn(false).when(mSST).isDeviceShuttingDown();
+ doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
mPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
- PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory);
+ PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
mPhoneUT.setVoiceCallSessionStats(mVoiceCallSessionStats);
ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mUiccController).registerForIccChanged(eq(mPhoneUT), integerArgumentCaptor.capture(),
@@ -380,7 +389,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
WorkSource workSource = new WorkSource(Process.myUid(),
mContext.getPackageName());
doReturn(cellLocation).when(mSST).getCellIdentity();
- assertEquals(cellLocation, mPhoneUT.getCellIdentity());
+ assertEquals(cellLocation, mPhoneUT.getCurrentCellIdentity());
// Switch to CDMA
switchToCdma();
@@ -389,7 +398,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
doReturn(cdmaCellLocation).when(mSST).getCellIdentity();
CellIdentityCdma actualCellLocation =
- (CellIdentityCdma) mPhoneUT.getCellIdentity();
+ (CellIdentityCdma) mPhoneUT.getCurrentCellIdentity();
assertEquals(actualCellLocation, cdmaCellLocation);
}
@@ -412,29 +421,29 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
// 1. GSM, getCurrentDataConnectionState != STATE_IN_SERVICE, apn != APN_TYPE_EMERGENCY
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST).getCurrentDataConnectionState();
assertEquals(PhoneConstants.DataState.DISCONNECTED, mPhoneUT.getDataConnectionState(
- PhoneConstants.APN_TYPE_ALL));
+ ApnSetting.TYPE_ALL_STRING));
// 2. GSM, getCurrentDataConnectionState != STATE_IN_SERVICE, apn = APN_TYPE_EMERGENCY, apn
// not connected
doReturn(DctConstants.State.IDLE).when(mDcTracker).getState(
- PhoneConstants.APN_TYPE_EMERGENCY);
+ ApnSetting.TYPE_EMERGENCY_STRING);
assertEquals(PhoneConstants.DataState.DISCONNECTED, mPhoneUT.getDataConnectionState(
- PhoneConstants.APN_TYPE_EMERGENCY));
+ ApnSetting.TYPE_EMERGENCY_STRING));
// 3. GSM, getCurrentDataConnectionState != STATE_IN_SERVICE, apn = APN_TYPE_EMERGENCY,
// APN is connected, callTracker state = idle
doReturn(DctConstants.State.CONNECTED).when(mDcTracker).getState(
- PhoneConstants.APN_TYPE_EMERGENCY);
+ ApnSetting.TYPE_EMERGENCY_STRING);
mCT.mState = PhoneConstants.State.IDLE;
assertEquals(PhoneConstants.DataState.CONNECTED, mPhoneUT.getDataConnectionState(
- PhoneConstants.APN_TYPE_EMERGENCY));
+ ApnSetting.TYPE_EMERGENCY_STRING));
// 3. GSM, getCurrentDataConnectionState != STATE_IN_SERVICE, apn = APN_TYPE_EMERGENCY,
// APN enabled and CONNECTED, callTracker state != idle, !isConcurrentVoiceAndDataAllowed
mCT.mState = PhoneConstants.State.RINGING;
doReturn(false).when(mSST).isConcurrentVoiceAndDataAllowed();
assertEquals(PhoneConstants.DataState.SUSPENDED, mPhoneUT.getDataConnectionState(
- PhoneConstants.APN_TYPE_EMERGENCY));
+ ApnSetting.TYPE_EMERGENCY_STRING));
}
@Test
@@ -486,7 +495,140 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
Connection connection = mPhoneUT.dial("1234567890",
new PhoneInternalInterface.DialArgs.Builder().build());
- verify(mCT).dialGsm("1234567890", null, null);
+ verify(mCT).dialGsm(eq("1234567890"), any(PhoneInternalInterface.DialArgs.class));
+ } catch (CallStateException e) {
+ fail();
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testWpsDialOverCs() throws Exception {
+ try {
+ setupForWpsCallTest();
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, false);
+
+ mPhoneUT.dial("*27216505551212", new PhoneInternalInterface.DialArgs.Builder().build());
+
+ verify(mCT).dialGsm(eq("*27216505551212"), any(PhoneInternalInterface.DialArgs.class));
+ verify(mImsCT).hangupAllConnections();
+ } catch (CallStateException e) {
+ fail();
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testClirCs() {
+ mPhoneUT.mCi = mMockCi;
+ // Start out with no preference set and ensure CommandsInterface receives setClir with
+ // the default set.
+ mPhoneUT.sendEmptyMessage(Phone.EVENT_REGISTERED_TO_NETWORK);
+ processAllMessages();
+ verify(mMockCi).setCLIR(eq(CommandsInterface.CLIR_DEFAULT), any());
+ // Now set the CLIR mode explicitly
+ mPhoneUT.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION, null);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockCi).setCLIR(eq(CommandsInterface.CLIR_SUPPRESSION), messageCaptor.capture());
+ Message message = messageCaptor.getValue();
+ assertNotNull(message);
+ message.obj = AsyncResult.forMessage(message);
+ // Now Call registered to network again and the CLIR mode sent should reflect the new value.
+ mPhoneUT.sendEmptyMessage(Phone.EVENT_REGISTERED_TO_NETWORK);
+ processAllMessages();
+ verify(mMockCi).setCLIR(eq(CommandsInterface.CLIR_SUPPRESSION), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testWpsClirActiveDialOverCs() throws Exception {
+ try {
+ setupForWpsCallTest();
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, false);
+
+ mPhoneUT.dial("*31#*27216505551212",
+ new PhoneInternalInterface.DialArgs.Builder().build());
+
+ verify(mCT).dialGsm("*27216505551212", CommandsInterface.CLIR_SUPPRESSION, null, null);
+ verify(mImsCT).hangupAllConnections();
+ } catch (CallStateException e) {
+ fail();
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testWpsClirInactiveDialOverCs() throws Exception {
+ try {
+ setupForWpsCallTest();
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, false);
+
+ mPhoneUT.dial("#31#*27216505551212",
+ new PhoneInternalInterface.DialArgs.Builder().build());
+
+ verify(mCT).dialGsm("*27216505551212", CommandsInterface.CLIR_INVOCATION, null, null);
+ verify(mImsCT).hangupAllConnections();
+ } catch (CallStateException e) {
+ fail();
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testWpsDialOverIms() throws Exception {
+ try {
+ setupForWpsCallTest();
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+
+ mPhoneUT.dial("*27216505551212",
+ new PhoneInternalInterface.DialArgs.Builder().build());
+ verify(mCT).dialGsm(eq("*27216505551212"), any(PhoneInternalInterface.DialArgs.class));
+ verify(mImsCT, never()).hangupAllConnections();
+ } catch (CallStateException e) {
+ fail();
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testWpsClirActiveDialOverIms() throws Exception {
+ try {
+ setupForWpsCallTest();
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+
+ mPhoneUT.dial("*31#*27216505551212",
+ new PhoneInternalInterface.DialArgs.Builder().build());
+ verify(mCT).dialGsm("*27216505551212", CommandsInterface.CLIR_SUPPRESSION, null, null);
+ verify(mImsCT, never()).hangupAllConnections();
+ } catch (CallStateException e) {
+ fail();
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testWpsClirInactiveDialOverIms() throws Exception {
+ try {
+ setupForWpsCallTest();
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+
+ mPhoneUT.dial("#31#*27216505551212",
+ new PhoneInternalInterface.DialArgs.Builder().build());
+
+ verify(mCT).dialGsm("*27216505551212", CommandsInterface.CLIR_INVOCATION, null, null);
+ verify(mImsCT, never()).hangupAllConnections();
} catch (CallStateException e) {
fail();
}
@@ -498,6 +640,31 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
assertFalse(mPhoneUT.handlePinMmi("1234567890"));
}
+ /**
+ * Verifies that silent redial connection changes are raised on the phone itself, not on the
+ * ImsPhone.
+ */
+ @Test
+ @SmallTest
+ public void testSilentRedialNotify() {
+ mPhoneUT.setAreThreadChecksEnabled(false);
+ mPhoneUT.registerForRedialConnectionChanged(mTestHandler,
+ EVENT_SILENT_REDIAL_CONNECTION_CHANGED, null);
+
+ // Raise a silent redial on the GsmCdmaPhone.
+ Phone.SilentRedialParam params = new Phone.SilentRedialParam("6505551212", 0, null);
+ AsyncResult result = new AsyncResult(null, params, null);
+ Message.obtain(mPhoneUT, Phone.EVENT_INITIATE_SILENT_REDIAL, result).sendToTarget();
+ processAllMessages();
+
+ // We expect our test handler to have the registered redial connection changed event
+ // raised on it.
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mTestHandler).sendMessageDelayed(messageCaptor.capture(), anyLong());
+ assertEquals(1, messageCaptor.getAllValues().size());
+ assertEquals(EVENT_SILENT_REDIAL_CONNECTION_CHANGED, messageCaptor.getValue().what);
+ }
+
@Test
@SmallTest
public void testEmergencySmsMode() {
@@ -869,6 +1036,32 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
}
@Test
+ public void testZeroMeid() {
+ doReturn(false).when(mSST).isDeviceShuttingDown();
+
+ SimulatedCommands sc = new SimulatedCommands() {
+ @Override
+ public void getDeviceIdentity(Message response) {
+ SimulatedCommandsVerifier.getInstance().getDeviceIdentity(response);
+ resultSuccess(response, new String[] {FAKE_IMEI, FAKE_IMEISV, FAKE_ESN, "0000000"});
+ }
+ };
+
+ Phone phone = new GsmCdmaPhone(mContext, sc, mNotifier, true, 0,
+ PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
+ phone.setVoiceCallSessionStats(mVoiceCallSessionStats);
+ ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mUiccController).registerForIccChanged(eq(phone), integerArgumentCaptor.capture(),
+ nullable(Object.class));
+ Message msg = Message.obtain();
+ msg.what = integerArgumentCaptor.getValue();
+ phone.sendMessage(msg);
+ processAllMessages();
+
+ assertNull(phone.getMeid());
+ }
+
+ @Test
@SmallTest
public void testEmergencyCallbackMessages() throws Exception {
verify(mSimulatedCommandsVerifier).setEmergencyCallbackMode(eq(mPhoneUT), anyInt(),
@@ -1069,7 +1262,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
@Test
@SmallTest
public void testGetIccCardUnknownAndAbsent() {
- // If UiccSlot.isStateUnknown is true, we should return a dummy IccCard with the state
+ // If UiccSlot.isStateUnknown is true, we should return a placeholder IccCard with the state
// set to UNKNOWN
doReturn(null).when(mUiccController).getUiccProfileForPhone(anyInt());
UiccSlot mockSlot = mock(UiccSlot.class);
@@ -1079,7 +1272,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
IccCard iccCard = mPhoneUT.getIccCard();
assertEquals(IccCardConstants.State.UNKNOWN, iccCard.getState());
- // if isStateUnknown is false, we should return a dummy IccCard with the state set to
+ // if isStateUnknown is false, we should return a placeholder IccCard with the state set to
// ABSENT
doReturn(false).when(mockSlot).isStateUnknown();
iccCard = mPhoneUT.getIccCard();
@@ -1093,7 +1286,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
IccCard iccCard = mPhoneUT.getIccCard();
- // The iccCard should be a dummy object, not null.
+ // The iccCard should be a placeholder object, not null.
assertTrue(!(iccCard instanceof UiccProfile));
assertTrue(iccCard != null);
@@ -1259,11 +1452,25 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
@SmallTest
public void testSetRadioPower() throws Exception {
mPhoneUT.setRadioPower(false);
- verify(mSST).setRadioPower(false, false, false, false);
+ verify(mSST).setRadioPowerForReason(false, false, false, false,
+ Phone.RADIO_POWER_REASON_USER);
// Turn on radio for emergency call.
mPhoneUT.setRadioPower(true, true, false, true);
- verify(mSST).setRadioPower(true, true, false, true);
+ verify(mSST).setRadioPowerForReason(true, true, false, true, Phone.RADIO_POWER_REASON_USER);
+ }
+
+ @Test
+ @SmallTest
+ public void testSetRadioPowerOnForTestEmergencyCall() {
+ mPhoneUT.setRadioPower(false);
+ verify(mSST).setRadioPowerForReason(false, false, false, false,
+ Phone.RADIO_POWER_REASON_USER);
+
+ mPhoneUT.setRadioPowerOnForTestEmergencyCall(false);
+ verify(mSST).clearAllRadioOffReasons();
+ verify(mSST).setRadioPowerForReason(eq(true), eq(false), anyBoolean(), eq(false),
+ eq(Phone.RADIO_POWER_REASON_USER));
}
@Test
@@ -1371,8 +1578,74 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
// Throw CallStateException(Phone.CS_FALLBACK) from ImsPhone.dial().
doReturn(Phone.CS_FALLBACK).when(callStateException).getMessage();
- doThrow(callStateException).when(mImsPhone).dial("*135#", dialArgs);
+ doThrow(callStateException).when(mImsPhone).dial(eq("*135#"),
+ any(PhoneInternalInterface.DialArgs.class));
replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
}
+
+ @Test
+ public void testEventCarrierConfigChanged() {
+ mPhoneUT.mCi = mMockCi;
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(Phone.EVENT_CARRIER_CONFIG_CHANGED));
+ processAllMessages();
+
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockCi).getRadioCapability(captor.capture());
+ assertEquals(captor.getValue().what, Phone.EVENT_GET_RADIO_CAPABILITY);
+ }
+
+ private void setupForWpsCallTest() throws Exception {
+ mSST.mSS = mServiceState;
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+ when(mImsPhone.getCallTracker()).thenReturn(mImsCT);
+ mCT.mForegroundCall = mGsmCdmaCall;
+ mCT.mBackgroundCall = mGsmCdmaCall;
+ mCT.mRingingCall = mGsmCdmaCall;
+ doReturn(GsmCdmaCall.State.IDLE).when(mGsmCdmaCall).getState();
+ replaceInstance(Phone.class, "mImsPhone", mPhoneUT, mImsPhone);
+ }
+
+ @Test
+ public void testEventLceUpdate() {
+ mPhoneUT.mCi = mMockCi;
+
+ ArgumentCaptor<List<LinkCapacityEstimate>> captor = ArgumentCaptor.forClass(List.class);
+ List<LinkCapacityEstimate> lceList1 = new ArrayList<>();
+ lceList1.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_PRIMARY, 2000, 5000));
+ lceList1.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_SECONDARY, 1000, 1500));
+
+ List<LinkCapacityEstimate> lceList2 = new ArrayList<>();
+ lceList2.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED, 2000, 5000));
+
+ List<LinkCapacityEstimate> lceList3 = new ArrayList<>();
+ lceList3.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED, 2000,
+ LinkCapacityEstimate.INVALID));
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(GsmCdmaPhone.EVENT_LINK_CAPACITY_CHANGED,
+ new AsyncResult(null, lceList1, null)));
+ processAllMessages();
+ verify(mNotifier, times(1))
+ .notifyLinkCapacityEstimateChanged(any(), captor.capture());
+ assertEquals(2, captor.getValue().size());
+ LinkCapacityEstimate lce1 = captor.getValue().get(1);
+ assertEquals(1000, lce1.getDownlinkCapacityKbps());
+ assertEquals(LinkCapacityEstimate.LCE_TYPE_SECONDARY, lce1.getType());
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(GsmCdmaPhone.EVENT_LINK_CAPACITY_CHANGED,
+ new AsyncResult(null, lceList2, null)));
+ processAllMessages();
+ verify(mNotifier, times(2))
+ .notifyLinkCapacityEstimateChanged(any(), captor.capture());
+ assertEquals(1, captor.getValue().size());
+
+ mPhoneUT.sendMessage(mPhoneUT.obtainMessage(GsmCdmaPhone.EVENT_LINK_CAPACITY_CHANGED,
+ new AsyncResult(null, lceList3, null)));
+ processAllMessages();
+ verify(mNotifier, times(3))
+ .notifyLinkCapacityEstimateChanged(any(), captor.capture());
+ LinkCapacityEstimate lce3 = captor.getValue().get(0);
+ assertEquals(LinkCapacityEstimate.INVALID, lce3.getUplinkCapacityKbps());
+ assertEquals(LinkCapacityEstimate.LCE_TYPE_COMBINED, lce3.getType());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
new file mode 100644
index 0000000000..93cc4e9466
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/IccSmsInterfaceManagerTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import android.os.AsyncResult;
+import android.os.Message;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class IccSmsInterfaceManagerTest extends TelephonyTest {
+ // object under test
+ private IccSmsInterfaceManager mIccSmsInterfaceManager;
+
+ @Mock
+ private SmsPermissions mMockSmsPermissions;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mIccSmsInterfaceManager = new IccSmsInterfaceManager(mPhone, mContext, mAppOpsManager,
+ mSmsDispatchersController, mMockSmsPermissions);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testSynchronization() throws Exception {
+ mContextFixture.addCallingOrSelfPermission(android.Manifest.permission
+ .RECEIVE_EMERGENCY_BROADCAST);
+ when(mMockSmsPermissions.checkCallingOrSelfCanGetSmscAddress(anyString(), anyString()))
+ .thenReturn(true);
+ mSimulatedCommands.mSendSetGsmBroadcastConfigResponse = false;
+ mSimulatedCommands.mSendGetSmscAddressResponse = false;
+ CountDownLatch enableRangeLatch = new CountDownLatch(1);
+ CountDownLatch getSmscLatch = new CountDownLatch(1);
+
+ // call enableGsmBroadcastRange from first thread
+ Thread enableRangeThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ mIccSmsInterfaceManager.enableGsmBroadcastRange(0, 5);
+ enableRangeLatch.countDown();
+ }
+ });
+ enableRangeThread.start();
+
+ // call getSmscAddressFromIccEf from second thread
+ Thread getSmscThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ mIccSmsInterfaceManager.getSmscAddressFromIccEf("calling package");
+ getSmscLatch.countDown();
+ }
+ });
+ getSmscThread.start();
+
+ // wait for half a second to let the threads send their messages
+ waitForMs(500);
+ processAllMessages();
+
+ // latch count should not be reduced until response is sent
+ assertEquals("enableRangeLatch.getCount should be 1", 1, enableRangeLatch.getCount());
+ assertEquals("getSmscLatch.getCount should be 1", 1, getSmscLatch.getCount());
+
+ // send back response for first request and assert that only the first thread is unblocked
+ ArgumentCaptor<Message> enableRangeCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mSimulatedCommandsVerifier).setGsmBroadcastConfig(any(),
+ enableRangeCaptor.capture());
+ Message enableRangeResponse = enableRangeCaptor.getValue();
+ AsyncResult.forMessage(enableRangeResponse);
+ enableRangeResponse.sendToTarget();
+ processAllMessages();
+
+ // the response triggers setGsmBroadcastActivation and since that's on another thread
+ // (enableRangeThread), wait for half a second for the response message of that to reach
+ // the handler before calling processAllMessages(). Consider increasing this timeout if the
+ // test fails.
+ waitForMs(500);
+ processAllMessages();
+
+ try {
+ assertEquals("enableRangeLatch.await should be true", true,
+ enableRangeLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException ie) {
+ fail("enableRangeLatch.await interrupted");
+ }
+ assertEquals("getSmscLatch.getCount should still be 1", 1, getSmscLatch.getCount());
+
+ // send back response for second request and assert that the second thread gets unblocked
+ ArgumentCaptor<Message> getSmscCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mSimulatedCommandsVerifier).getSmscAddress(getSmscCaptor.capture());
+ Message getSmscResponse = getSmscCaptor.getValue();
+ AsyncResult.forMessage(getSmscResponse);
+ getSmscResponse.sendToTarget();
+ processAllMessages();
+
+ try {
+ assertEquals("getSmscLatch.await should be true", true,
+ getSmscLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException ie) {
+ fail("getSmscLatch.await interrupted");
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
index d13152efb1..d4ac4d5fc0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -21,49 +21,71 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.os.Looper;
import android.telephony.SmsMessage;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import com.android.ims.FeatureConnector;
+import com.android.ims.ImsManager;
import com.android.internal.util.HexDump;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.stubbing.Answer;
import java.util.HashMap;
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class ImsSmsDispatcherTest extends TelephonyTest {
@Mock private SmsDispatchersController mSmsDispatchersController;
@Mock private SMSDispatcher.SmsTracker mSmsTracker;
+ @Mock private ImsSmsDispatcher.FeatureConnectorFactory mConnectorFactory;
+ @Mock private FeatureConnector<ImsManager> mMockConnector;
+ private FeatureConnector.Listener<ImsManager> mImsManagerListener;
private HashMap<String, Object> mTrackerData;
private ImsSmsDispatcher mImsSmsDispatcher;
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- mImsSmsDispatcher = spy(new ImsSmsDispatcher(mPhone, mSmsDispatchersController));
+ doAnswer((Answer<FeatureConnector<ImsManager>>) invocation -> {
+ mImsManagerListener =
+ (FeatureConnector.Listener<ImsManager>) invocation.getArguments()[3];
+ return mMockConnector;
+ }).when(mConnectorFactory).create(any(), anyInt(), anyString(), any(), any());
+ mImsSmsDispatcher = new ImsSmsDispatcher(mPhone, mSmsDispatchersController,
+ mConnectorFactory);
+ processAllMessages();
+ // set the ImsManager instance
+ verify(mMockConnector).connect();
+ mImsManagerListener.connectionReady(mImsManager);
when(mSmsDispatchersController.isIms()).thenReturn(true);
-
mTrackerData = new HashMap<>(1);
when(mSmsTracker.getData()).thenReturn(mTrackerData);
}
+ @After
+ public void tearDown() throws Exception {
+ mImsSmsDispatcher = null;
+ super.tearDown();
+ }
+
/**
* Send an SMS and verify that the token and PDU is correct.
*/
@@ -149,22 +171,15 @@ public class ImsSmsDispatcherTest extends TelephonyTest {
@Test
@SmallTest
public void testReceiveGsmSmsStatusReport() throws Exception {
- int sentSmsToken = mImsSmsDispatcher.mNextToken.get();
int statusReportToken = 456; // Generated by IMS providers
int messageRef = 123; // TP-MR for sent SMS
- int trackersSize = mImsSmsDispatcher.mTrackers.size();
// PDU for SMS-STATUS-REPORT
byte[] pdu = HexDump.hexStringToByteArray("0006000681214365919061800000639190618000006300");
// Set TP-MR
pdu[2] = (byte) messageRef;
- mSmsTracker.mMessageRef = messageRef;
- mImsSmsDispatcher.mTrackers.put(sentSmsToken, mSmsTracker);
when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
- when(mSmsDispatchersController.handleSmsStatusReport(
- eq(mSmsTracker), eq(SmsMessage.FORMAT_3GPP), eq(pdu)))
- .thenReturn(new Pair(true, true));
// Receive the status report
mImsSmsDispatcher
@@ -177,7 +192,6 @@ public class ImsSmsDispatcherTest extends TelephonyTest {
eq(statusReportToken),
eq(messageRef),
eq(ImsSmsImplBase.STATUS_REPORT_STATUS_OK));
- assertEquals(trackersSize, mImsSmsDispatcher.mTrackers.size());
}
/**
@@ -199,10 +213,4 @@ public class ImsSmsDispatcherTest extends TelephonyTest {
ImsSmsImplBase.SEND_STATUS_ERROR, 0, 41);
verify(mSmsTracker).onFailed(any(Context.class), anyInt(), eq(41));
}
-
- @After
- public void tearDown() throws Exception {
- mImsSmsDispatcher = null;
- super.tearDown();
- }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
index cd97bdc1d2..7bc26b79ca 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
@@ -51,7 +51,8 @@ public class InboundSmsTrackerTest {
mInboundSmsTracker = new InboundSmsTracker(InstrumentationRegistry.getContext(),
FAKE_PDU, FAKE_TIMESTAMP, FAKE_DEST_PORT, false,
FAKE_ADDRESS, FAKE_DISPLAY_ADDRESS, FAKE_REFERENCE_NUMBER, FAKE_SEQUENCE_NUMBER,
- FAKE_MESSAGE_COUNT, false, FAKE_MESSAGE_BODY, false /* isClass0 */, FAKE_SUBID);
+ FAKE_MESSAGE_COUNT, false, FAKE_MESSAGE_BODY, false /* isClass0 */, FAKE_SUBID,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
}
public static MatrixCursor createFakeCursor() {
@@ -87,6 +88,7 @@ public class InboundSmsTrackerTest {
assertEquals(FAKE_DISPLAY_ADDRESS, mInboundSmsTracker.getDisplayAddress());
assertEquals(false, mInboundSmsTracker.isClass0());
assertEquals(FAKE_SUBID, mInboundSmsTracker.getSubId());
+ assertEquals(InboundSmsHandler.SOURCE_NOT_INJECTED, mInboundSmsTracker.getSource());
// assertNotEquals(0L, mInboundSmsTracker.getMessageId());
String[] args = new String[]{"123"};
diff --git a/tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java
index a6fb620cbe..ee75826822 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java
@@ -40,7 +40,7 @@ public class IntRangeManagerTest extends AndroidTestCase {
private static final int ALL_FLAGS_SET = FLAG_START_UPDATE_CALLED | FLAG_ADD_RANGE_CALLED |
FLAG_FINISH_UPDATE_CALLED;
- /** Dummy IntRangeManager for testing. */
+ /** IntRangeManager for testing. */
class TestIntRangeManager extends IntRangeManager {
ArrayList<SmsBroadcastConfigInfo> mConfigList =
new ArrayList<SmsBroadcastConfigInfo>();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
index 5b147c0cd1..bab6408e34 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -87,6 +87,7 @@ public class LocaleTrackerTest extends TelephonyTest {
m.sendToTarget();
return null; }).when(mPhone).requestCellInfoUpdate(any(), any());
+ doReturn(true).when(mPhone).isRadioOn();
processAllMessages();
logd("LocaleTrackerTest -Setup!");
}
@@ -135,6 +136,7 @@ public class LocaleTrackerTest extends TelephonyTest {
@Test
@SmallTest
public void testUpdateOperatorNumericSync() throws Exception {
+ doReturn(false).when(mPhone).isRadioOn();
mLocaleTracker.updateOperatorNumeric(US_MCC + FAKE_MNC);
// Because the service state is in APM, the country ISO should be set empty.
assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
@@ -161,6 +163,7 @@ public class LocaleTrackerTest extends TelephonyTest {
@SmallTest
public void testBootupInAirplaneModeOn() throws Exception {
mLocaleTracker.updateOperatorNumeric("");
+ doReturn(false).when(mPhone).isRadioOn();
sendServiceState(ServiceState.STATE_POWER_OFF);
assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
verifyCountryCodeNotified(new String[]{COUNTRY_CODE_UNAVAILABLE});
@@ -170,6 +173,7 @@ public class LocaleTrackerTest extends TelephonyTest {
@Test
@SmallTest
public void testToggleAirplaneModeOn() throws Exception {
+ doReturn(true).when(mPhone).isRadioOn();
sendServiceState(ServiceState.STATE_IN_SERVICE);
mLocaleTracker.updateOperatorNumeric(US_MCC + FAKE_MNC);
assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
@@ -183,6 +187,7 @@ public class LocaleTrackerTest extends TelephonyTest {
assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
assertEquals(US_COUNTRY_CODE, mLocaleTracker.getLastKnownCountryIso());
verifyCountryCodeNotified(new String[]{COUNTRY_CODE_UNAVAILABLE, US_COUNTRY_CODE});
+ doReturn(false).when(mPhone).isRadioOn();
sendServiceState(ServiceState.STATE_POWER_OFF);
assertFalse(mLocaleTracker.isTracking());
@@ -198,6 +203,7 @@ public class LocaleTrackerTest extends TelephonyTest {
@Test
@SmallTest
public void testToggleAirplaneModeOff() throws Exception {
+ doReturn(false).when(mPhone).isRadioOn();
sendServiceState(ServiceState.STATE_POWER_OFF);
mLocaleTracker.updateOperatorNumeric("");
processAllMessages();
@@ -205,6 +211,7 @@ public class LocaleTrackerTest extends TelephonyTest {
verifyCountryCodeNotified(new String[]{COUNTRY_CODE_UNAVAILABLE});
assertFalse(mLocaleTracker.isTracking());
+ doReturn(true).when(mPhone).isRadioOn();
sendServiceState(ServiceState.STATE_OUT_OF_SERVICE);
processAllMessages();
assertTrue(mLocaleTracker.isTracking());
@@ -214,6 +221,7 @@ public class LocaleTrackerTest extends TelephonyTest {
@Test
@SmallTest
public void testToggleAirplaneModeOosPlmn() throws Exception {
+ doReturn(false).when(mPhone).isRadioOn();
sendServiceState(ServiceState.STATE_POWER_OFF);
mLocaleTracker.updateOperatorNumeric("");
processAllMessages();
@@ -228,6 +236,7 @@ public class LocaleTrackerTest extends TelephonyTest {
m.sendToTarget();
return null; }).when(mPhone).requestCellInfoUpdate(any(), any());
+ doReturn(true).when(mPhone).isRadioOn();
sendServiceState(ServiceState.STATE_OUT_OF_SERVICE);
processAllMessages();
assertTrue(mLocaleTracker.isTracking());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index af72c705a2..2551678818 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -20,6 +20,7 @@ import static android.telephony.TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIS
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
+import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS;
import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
import static org.junit.Assert.assertEquals;
@@ -34,9 +35,12 @@ import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Intent;
+import android.content.res.Resources;
+import android.os.HandlerThread;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.provider.Settings;
@@ -48,18 +52,25 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.InstrumentationRegistry;
+
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -78,6 +89,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
@Mock
private DataEnabledSettings mDataEnabledSettingsMock2;
private Phone[] mPhones;
+ @Mock
+ private CommandsInterface mMockCi;
ParcelUuid mGroupUuid1 = new ParcelUuid(UUID.randomUUID());
@@ -122,6 +135,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
doReturn(true).when(mSubControllerMock).isOpportunistic(5);
doReturn(1).when(mPhoneMock1).getSubId();
doReturn(2).when(mPhoneMock2).getSubId();
+ mPhoneMock1.mCi = mSimulatedCommands;
+ mPhoneMock2.mCi = mSimulatedCommands;
List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo1, mSubInfo2);
doReturn(infoList).when(mSubControllerMock)
.getActiveSubscriptionInfoList(anyString(), nullable(String.class));
@@ -153,7 +168,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
@Test
@SmallTest
- public void testTestSubInfoChangeBeforeAllSubReady() throws Exception {
+ public void testSubInfoChangeBeforeAllSubReady() throws Exception {
doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
.getDefaultDataSubId();
doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubControllerMock)
@@ -185,8 +200,51 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
verify(mSubControllerMock).setDefaultDataSubId(1);
verify(mSubControllerMock).setDefaultVoiceSubId(1);
verify(mSubControllerMock).setDefaultSmsSubId(1);
- // No dialog or notification is needed. So no intent is expected to be broadcast.
- verify(mContext, never()).sendBroadcast(any());
+ verifyDismissIntentSent();
+ }
+
+ @Test
+ public void testSubInfoChangeAfterRadioUnavailable() throws Exception {
+ mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ processAllMessages();
+
+ // Notify radio unavailable.
+ replaceInstance(BaseCommands.class, "mState", mSimulatedCommands,
+ TelephonyManager.RADIO_POWER_UNAVAILABLE);
+ mMultiSimSettingControllerUT.obtainMessage(
+ MultiSimSettingController.EVENT_RADIO_STATE_CHANGED).sendToTarget();
+
+ // Mark all subs as inactive.
+ doReturn(false).when(mSubControllerMock).isActiveSubId(1);
+ doReturn(false).when(mSubControllerMock).isActiveSubId(2);
+ doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(1);
+ doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(2);
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock1).getSubId();
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock2).getSubId();
+ List<SubscriptionInfo> infoList = new ArrayList<>();
+ doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
+ nullable(String.class));
+ doReturn(new int[]{}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ clearInvocations(mSubControllerMock);
+
+ // The below sub info change should be ignored.
+ mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
+ processAllMessages();
+ verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
+ verify(mSubControllerMock, never()).setDefaultVoiceSubId(anyInt());
+ verify(mSubControllerMock, never()).setDefaultSmsSubId(anyInt());
+
+ // Send all sub ready notification
+ mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+ processAllMessages();
+
+ // Everything should be set to invalid since nothing is active.
+ verify(mSubControllerMock).setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ verify(mSubControllerMock)
+ .setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ verify(mSubControllerMock).setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
@Test
@@ -211,6 +269,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
processAllMessages();
+ verifyDismissIntentSent();
+ clearInvocations(mContext);
// Sub 1 should be default sub silently.
// Sub 1 switches to sub 2 in the same slot.
@@ -232,8 +292,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
verify(mSubControllerMock).setDefaultDataSubId(2);
verify(mSubControllerMock).setDefaultVoiceSubId(2);
verify(mSubControllerMock).setDefaultSmsSubId(2);
- // No dialog or notification is needed. So no intent is expected to be broadcast.
- verify(mContext, never()).sendBroadcast(any());
+ verifyDismissIntentSent();
}
@Test
@@ -256,11 +315,11 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
verify(mSubControllerMock).setDefaultDataSubId(1);
verify(mSubControllerMock).setDefaultVoiceSubId(1);
verify(mSubControllerMock).setDefaultSmsSubId(1);
- // No dialog or notification is needed. So no intent is expected to be broadcast.
- verify(mContext, never()).sendBroadcast(any());
+ verifyDismissIntentSent();
// Mark sub 2 as active in phone[1].
clearInvocations(mSubControllerMock);
+ clearInvocations(mContext);
doReturn(true).when(mSubControllerMock).isActiveSubId(2);
doReturn(1).when(mSubControllerMock).getPhoneId(2);
doReturn(2).when(mPhoneMock2).getSubId();
@@ -313,7 +372,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mDataEnabledSettingsMock2).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
// Enable on non-default sub should trigger setDefaultDataSubId.
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
@@ -324,7 +384,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
processAllMessages();
- verify(mDataEnabledSettingsMock1).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock1).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
doReturn(1).when(mSubControllerMock).getDefaultSmsSubId();
@@ -355,6 +416,41 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
@Test
@SmallTest
+ public void testSimpleDsdsFirstBoot() {
+ // at first boot default is not set
+ doReturn(-1).when(mSubControllerMock).getDefaultDataSubId();
+
+ doReturn(true).when(mPhoneMock1).isUserDataEnabled();
+ doReturn(true).when(mPhoneMock2).isUserDataEnabled();
+ // After initialization, sub 2 should have mobile data off.
+ mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ processAllMessages();
+ verify(mDataEnabledSettingsMock1).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ verify(mDataEnabledSettingsMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
+
+ // as a result of the above calls, update new values to be returned
+ doReturn(false).when(mPhoneMock1).isUserDataEnabled();
+ doReturn(false).when(mPhoneMock2).isUserDataEnabled();
+
+ Intent intent = captureBroadcastIntent();
+ assertEquals(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED, intent.getAction());
+ assertEquals(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA,
+ intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, -1));
+
+ // Setting default data should not trigger any more setDataEnabled().
+ doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
+ mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
+ processAllMessages();
+ verify(mDataEnabledSettingsMock1, times(1)).setDataEnabled(anyInt(), anyBoolean());
+ verify(mDataEnabledSettingsMock2, times(1)).setDataEnabled(anyInt(), anyBoolean());
+ }
+
+ @Test
+ @SmallTest
public void testDsdsGrouping() {
doReturn(2).when(mSubControllerMock).getDefaultDataSubId();
doReturn(false).when(mPhoneMock1).isUserDataEnabled();
@@ -391,7 +487,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
mMultiSimSettingControllerUT.notifyDefaultDataSubChanged();
processAllMessages();
- verify(mDataEnabledSettingsMock2).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, false);
processAllMessages();
assertFalse(GlobalSettingsHelper.getBoolean(
@@ -439,9 +536,9 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
processAllMessages();
verify(mSubControllerMock).setDefaultDataSubId(2);
- verify(mDataEnabledSettingsMock1, never()).setUserDataEnabled(anyBoolean());
- // No user selection needed, no intent should be sent.
- verify(mContext, never()).sendBroadcast(any());
+ verify(mDataEnabledSettingsMock1, never()).setDataEnabled(
+ anyInt(), anyBoolean());
+ verifyDismissIntentSent();
clearInvocations(mSubControllerMock);
clearInvocations(mDataEnabledSettingsMock1);
@@ -456,8 +553,17 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
processAllMessages();
verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
- verify(mDataEnabledSettingsMock1, never()).setUserDataEnabled(anyBoolean());
- verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(anyBoolean());
+ verify(mDataEnabledSettingsMock1, never()).setDataEnabled(
+ eq(TelephonyManager.DATA_ENABLED_REASON_USER), anyBoolean());
+ verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ }
+
+ private void verifyDismissIntentSent() {
+ Intent intentSent = captureBroadcastIntent();
+ assertEquals(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DISMISS,
+ intentSent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, -1));
+ assertEquals(ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED, intentSent.getAction());
}
@Test
@@ -478,8 +584,6 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
processAllMessages();
verify(mSubControllerMock).setDefaultDataSubId(2);
- // No user selection needed, no intent should be sent.
- verify(mContext, never()).sendBroadcast(any());
// Mark sub 2 as data off.
doReturn(false).when(mPhoneMock2).isUserDataEnabled();
@@ -500,8 +604,47 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
mMultiSimSettingControllerUT.notifyUserDataEnabled(2, true);
processAllMessages();
verify(mDataEnabledSettingsMock1).setUserDataEnabled(true, false);
- // No user selection needed, no intent should be sent.
- verify(mContext, never()).sendBroadcast(any());
+ verifyDismissIntentSent();
+ }
+
+ @Test
+ @SmallTest
+ public void testGroupedPrimaryRemoved() throws Exception {
+ // Create subscription grouping of subs 1 and 2.
+ replaceInstance(SubscriptionInfo.class, "mGroupUUID", mSubInfo1, mGroupUuid1);
+ doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(1);
+ doReturn(mGroupUuid1).when(mSubControllerMock).getGroupUuid(2);
+ doReturn(Arrays.asList(mSubInfo1, mSubInfo2)).when(mSubControllerMock)
+ .getSubscriptionsInGroup(any(), anyString(), nullable(String.class));
+
+ mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+ mMultiSimSettingControllerUT.notifySubscriptionGroupChanged(mGroupUuid1);
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ processAllMessages();
+
+ // Defaults not touched, sub 1 is already default.
+ verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
+
+ // Take out SIM 1.
+ clearInvocations(mSubControllerMock);
+ doReturn(false).when(mSubControllerMock).isActiveSubId(1);
+ doReturn(SubscriptionManager.INVALID_PHONE_INDEX).when(mSubControllerMock).getPhoneId(1);
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mPhoneMock1).getSubId();
+ List<SubscriptionInfo> infoList = Arrays.asList(mSubInfo2);
+ doReturn(infoList).when(mSubControllerMock).getActiveSubscriptionInfoList(anyString(),
+ nullable(String.class));
+ doReturn(new int[]{2}).when(mSubControllerMock).getActiveSubIdList(anyBoolean());
+ mMultiSimSettingControllerUT.notifySubscriptionInfoChanged();
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(
+ 0, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ processAllMessages();
+
+ // Sub 2 should be made the default sub silently.
+ verify(mSubControllerMock).setDefaultDataSubId(2);
+ verify(mSubControllerMock).setDefaultVoiceSubId(2);
+ verify(mSubControllerMock).setDefaultSmsSubId(2);
+ verifyDismissIntentSent();
}
private Intent captureBroadcastIntent() {
@@ -551,13 +694,16 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
// loaded on both subscriptions.
mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
processAllMessages();
- verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0, 1);
processAllMessages();
- verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
processAllMessages();
- verify(mDataEnabledSettingsMock2).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
// Switch from sub 2 to sub 3 in phone[1].
clearInvocations(mSubControllerMock);
@@ -639,7 +785,8 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
processAllMessages();
// Nothing should happen as carrier config is not ready for sub 2.
- verify(mDataEnabledSettingsMock2, never()).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock2, never()).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
// Still notify carrier config without specifying subId2, but this time subController
// and CarrierConfigManager have subId 2 active and ready.
@@ -651,6 +798,73 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
processAllMessages();
// This time user data should be disabled on phone1.
- verify(mDataEnabledSettingsMock2).setUserDataEnabled(false);
+ verify(mDataEnabledSettingsMock2).setDataEnabled(
+ TelephonyManager.DATA_ENABLED_REASON_USER, false);
+ }
+
+ @Test
+ @SmallTest
+ public void testOnPhoneRemoved() {
+ try {
+ mMultiSimSettingControllerUT.onPhoneRemoved();
+ } catch (RuntimeException re) {
+ Assert.fail("Exception not expected when calling from the same thread");
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testOnPhoneRemoved_DifferentThread() {
+ AtomicBoolean result = new AtomicBoolean(false);
+ CountDownLatch latch = new CountDownLatch(1);
+ HandlerThread handlerThread = new HandlerThread("MultiSimSettingControllerTest") {
+ public void onLooperPrepared() {
+ try {
+ mMultiSimSettingControllerUT.onPhoneRemoved();
+ } catch (RuntimeException re) {
+ result.set(true); // true to indicate that the test passed
+ }
+ latch.countDown();
+ }
+ };
+ handlerThread.start();
+ try {
+ if (!latch.await(5, TimeUnit.SECONDS)) {
+ Assert.fail("CountDownLatch did not reach 0");
+ } else if (!result.get()) {
+ Assert.fail("Exception expected when not calling from the same thread");
+ }
+ } catch (InterruptedException ie) {
+ Assert.fail("InterruptedException during latch.await");
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testVoiceDataSmsAutoFallback() throws Exception {
+ doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
+ mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1);
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(2, 3);
+ processAllMessages();
+ verify(mSubControllerMock, never()).setDefaultDataSubId(anyInt());
+ verify(mSubControllerMock, never()).getActiveSubInfoCountMax();
+ doReturn(2).when(mSubControllerMock).getActiveSubInfoCountMax();
+ mPhoneMock1.mCi = mMockCi;
+ mPhoneMock2.mCi = mMockCi;
+ doReturn(TelephonyManager.RADIO_POWER_ON).when(mMockCi).getRadioState();
+ doReturn(false).when(mPhoneMock1).isShuttingDown();
+ doReturn(false).when(mPhoneMock2).isShuttingDown();
+ android.provider.Settings.Global.putInt(InstrumentationRegistry.getTargetContext().
+ getContentResolver(), "user_preferred_data_sub", 2);
+ Resources resources = mContext.getResources();
+ doReturn(true).when(resources).getBoolean(
+ com.android.internal.R.bool.config_voice_data_sms_auto_fallback);
+ mMultiSimSettingControllerUT.notifyAllSubscriptionLoaded();
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(0,1);
+ mMultiSimSettingControllerUT.notifyCarrierConfigChanged(1, 2);
+ processAllMessages();
+ verify(mSubControllerMock).getActiveSubInfoCountMax();
+ verify(mSubControllerMock).setDefaultDataSubId(anyInt());
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index eafc486e70..be23df8ba1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -17,6 +17,9 @@
package com.android.internal.telephony;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import android.content.Intent;
@@ -26,12 +29,17 @@ import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PcoData;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.telephony.dataconnection.DataConnection;
import com.android.internal.telephony.dataconnection.DcController;
import com.android.internal.util.IState;
import com.android.internal.util.StateMachine;
@@ -40,8 +48,11 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -58,9 +69,15 @@ public class NetworkTypeControllerTest extends TelephonyTest {
private static final int EVENT_RADIO_OFF_OR_UNAVAILABLE = 10;
private static final int EVENT_PREFERRED_NETWORK_MODE_CHANGED = 11;
private static final int EVENT_INITIALIZE = 12;
+ private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 13;
+ private static final int EVENT_PCO_DATA_CHANGED = 14;
private NetworkTypeController mNetworkTypeController;
private PersistableBundle mBundle;
+ @Mock
+ DataConnection mDataConnection;
+ @Mock
+ ApnSetting mApnSetting;
private IState getCurrentState() throws Exception {
Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
@@ -92,8 +109,11 @@ public class NetworkTypeControllerTest extends TelephonyTest {
broadcastCarrierConfigs();
replaceInstance(Handler.class, "mLooper", mDisplayInfoController, Looper.myLooper());
- doReturn(TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA).when(mPhone)
- .getCachedPreferredNetworkType();
+ doReturn(RadioAccessFamily.getRafFromNetworkType(
+ TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA)).when(
+ mPhone).getCachedAllowedNetworkTypesBitmask();
+ doReturn(false).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
processAllMessages();
}
@@ -107,8 +127,6 @@ public class NetworkTypeControllerTest extends TelephonyTest {
@Test
public void testUpdateOverrideNetworkTypeNrNsa() throws Exception {
- doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
-
// not NR
doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
updateOverrideNetworkType();
@@ -137,21 +155,73 @@ public class NetworkTypeControllerTest extends TelephonyTest {
// NR NSA, millimeter wave frequency
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
updateOverrideNetworkType();
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
}
@Test
- public void testUpdateOverrideNetworkTypeLte() throws Exception {
+ public void testUpdateOverrideNetworkTypeNrSa() throws Exception {
+ // not NR
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ updateOverrideNetworkType();
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // NR SA connected
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+
+ updateOverrideNetworkType();
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ }
+
+ @Test
+ public void testUpdateOverrideNetworkTypeNrSaMmwave() throws Exception {
+ // not NR
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ updateOverrideNetworkType();
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // NR SA connected and millimeter wave frequency
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+
+ updateOverrideNetworkType();
+
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ }
+
+ @Test
+ public void testUpdateOverrideNetworkTypeLte() throws Exception {
// normal LTE
updateOverrideNetworkType();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
- // LTE CA
+ // LTE CA at bandwidth threshold
doReturn(true).when(mServiceState).isUsingCarrierAggregation();
+ doReturn(new int[] {20000}).when(mServiceState).getCellBandwidths();
+ updateOverrideNetworkType();
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // LTE CA above bandwidth threshold
+ doReturn(new int[] {20000, 1400}).when(mServiceState).getCellBandwidths();
updateOverrideNetworkType();
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
mNetworkTypeController.getOverrideNetworkType());
@@ -167,6 +237,22 @@ public class NetworkTypeControllerTest extends TelephonyTest {
}
@Test
+ public void testUpdateOverrideNetworkType() throws Exception {
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_HSPAP)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(nri).when(mServiceState).getNetworkRegistrationInfo(anyInt(), anyInt());
+ updateOverrideNetworkType();
+
+ // override shouldn't be NR if not on LTE despite NR_STATE_CONNECTED
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ }
+
+ @Test
public void testTransitionToCurrentStateLegacy() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
doReturn(TelephonyManager.NETWORK_TYPE_HSPAP).when(mServiceState).getDataNetworkType();
@@ -200,6 +286,43 @@ public class NetworkTypeControllerTest extends TelephonyTest {
}
@Test
+ public void testTransitionToCurrentStateIdleSupportPhysicalChannelConfig1_6() throws Exception {
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ processAllMessages();
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ setPhysicalLinkState(false);
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateIdle_usingUserDataForRrcDetection() throws Exception {
+ mBundle.putBoolean(
+ CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true);
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ broadcastCarrierConfigs();
+ processAllMessages();
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+ new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null));
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+
+ processAllMessages();
+
+ assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+ }
+
+ @Test
public void testTransitionToCurrentStateLteConnected() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
@@ -212,6 +335,46 @@ public class NetworkTypeControllerTest extends TelephonyTest {
}
@Test
+ public void testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6()
+ throws Exception {
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ broadcastCarrierConfigs();
+ processAllMessages();
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ setPhysicalLinkState(true);
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateLteConnected_usingUserDataForRrcDetection()
+ throws Exception {
+ mBundle.putBoolean(
+ CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true);
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ broadcastCarrierConfigs();
+ processAllMessages();
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+ new AsyncResult(null, DcController.PHYSICAL_LINK_ACTIVE, null));
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+
+ processAllMessages();
+
+ assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+ }
+
+ @Test
public void testTransitionToCurrentStateNrConnected() throws Exception {
assertEquals("DefaultState", getCurrentState().getName());
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
@@ -235,6 +398,138 @@ public class NetworkTypeControllerTest extends TelephonyTest {
}
@Test
+ public void testTransitionToCurrentStateNrConnectedMmwaveWithAdditionalBandAndNoMmwave()
+ throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
+ mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+ new int[]{41});
+ PhysicalChannelConfig physicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setBand(41)
+ .build();
+ List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+ lastPhysicalChannelConfigList.add(physicalChannelConfig);
+ doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
+ broadcastCarrierConfigs();
+
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateNrConnectedWithNoAdditionalBandAndNoMmwave()
+ throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_HIGH).when(mServiceState).getNrFrequencyRange();
+ mBundle.putIntArray(CarrierConfigManager.KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY,
+ new int[]{41});
+ PhysicalChannelConfig physicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setBand(2)
+ .build();
+ List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+ lastPhysicalChannelConfigList.add(physicalChannelConfig);
+ doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
+ broadcastCarrierConfigs();
+
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("connected", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapable() throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
+ broadcastCarrierConfigs();
+
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateNrConnectedWithPcoAndNoNrAdvancedCapable()
+ throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+ broadcastCarrierConfigs();
+ int cid = 1;
+ byte[] contents = new byte[]{0};
+ doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
+ doReturn(mApnSetting).when(mDataConnection).getApnSetting();
+ doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+ broadcastCarrierConfigs();
+
+
+ mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
+ new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("connected", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateNrConnectedWithWrongPcoAndNoNrAdvancedCapable()
+ throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+ broadcastCarrierConfigs();
+ int cid = 1;
+ byte[] contents = new byte[]{1};
+ doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
+ doReturn(mApnSetting).when(mDataConnection).getApnSetting();
+ doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF00);
+ broadcastCarrierConfigs();
+
+
+ mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
+ new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("connected", getCurrentState().getName());
+ }
+
+ @Test
+ public void testTransitionToCurrentStateNrConnectedWithNrAdvancedCapableAndPco()
+ throws Exception {
+ assertEquals("DefaultState", getCurrentState().getName());
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ int cid = 1;
+ byte[] contents = new byte[]{1};
+ doReturn(mDataConnection).when(mDcTracker).getDataConnectionByContextId(cid);
+ doReturn(mApnSetting).when(mDataConnection).getApnSetting();
+ doReturn(true).when(mApnSetting).canHandleType(ApnSetting.TYPE_DEFAULT);
+ mBundle.putInt(CarrierConfigManager.KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0xFF03);
+ broadcastCarrierConfigs();
+
+ mNetworkTypeController.sendMessage(EVENT_PCO_DATA_CHANGED,
+ new AsyncResult(null, new PcoData(cid, "", 0xff03, contents), null));
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ }
+
+ @Test
public void testEventDataRatChanged() throws Exception {
testTransitionToCurrentStateLegacy();
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
@@ -294,12 +589,170 @@ public class NetworkTypeControllerTest extends TelephonyTest {
}
@Test
+ public void testNrPhysicalChannelChange1_6FromNrConnectedMmwaveToLteConnected()
+ throws Exception {
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ processAllMessages();
+ testTransitionToCurrentStateNrConnectedMmwave();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ setPhysicalLinkState(true);
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+
+ processAllMessages();
+
+ assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+ }
+
+
+ @Test
+ public void testUsingUserDataForRrcDetection_FromNrConnectedMmwaveToLteConnected()
+ throws Exception {
+ mBundle.putBoolean(
+ CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true);
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ broadcastCarrierConfigs();
+ processAllMessages();
+ testTransitionToCurrentStateNrConnectedMmwave();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+ new AsyncResult(null, DcController.PHYSICAL_LINK_ACTIVE, null));
+ mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+
+ processAllMessages();
+
+ assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+ }
+
+ @Test
+ public void testEventPhysicalChannelChangeFromLteToLteCaInLegacyState() throws Exception {
+ testTransitionToCurrentStateLegacy();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ updateOverrideNetworkType();
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ doReturn(true).when(mServiceState).isUsingCarrierAggregation();
+ doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ processAllMessages();
+
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+ mNetworkTypeController.getOverrideNetworkType());
+ }
+
+ @Test
+ public void testEventPhysicalChannelChangeFromLteToLteCaInLteConnectedState() throws Exception {
+ // Remove RRC idle/RRC connected from 5G override
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
+ "connected_mmwave:5G_Plus,connected:5G");
+ broadcastCarrierConfigs();
+
+ // Transition to LTE connected state
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+ new AsyncResult(null, DcController.PHYSICAL_LINK_ACTIVE, null));
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("not_restricted_rrc_con", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // LTE -> LTE+
+ doReturn(true).when(mServiceState).isUsingCarrierAggregation();
+ doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ processAllMessages();
+
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+ mNetworkTypeController.getOverrideNetworkType());
+ }
+
+ @Test
+ public void testEventPhysicalChannelChangeFromLteToLteCaInIdleState() throws Exception {
+ // Remove RRC idle/RRC connected from 5G override
+ mBundle = mContextFixture.getCarrierConfigBundle();
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING,
+ "connected_mmwave:5G_Plus,connected:5G");
+ broadcastCarrierConfigs();
+
+ // Transition to idle state
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+ new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null));
+ mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
+ processAllMessages();
+ assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // LTE -> LTE+
+ doReturn(true).when(mServiceState).isUsingCarrierAggregation();
+ doReturn(new int[] {30000}).when(mServiceState).getCellBandwidths();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ processAllMessages();
+
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA,
+ mNetworkTypeController.getOverrideNetworkType());
+ }
+
+ @Test
public void testEventPhysicalLinkStateChanged() throws Exception {
testTransitionToCurrentStateLteConnected();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null));
+
processAllMessages();
+
+ assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+ }
+
+ @Test
+ public void testEventPhysicalLinkStateChangedSupportPhysicalChannelConfig1_6()
+ throws Exception {
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ processAllMessages();
+ testTransitionToCurrentStateLteConnectedSupportPhysicalChannelConfig1_6();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ setPhysicalLinkState(false);
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+
+ processAllMessages();
+
+ assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
+ }
+
+ @Test
+ public void testEventPhysicalLinkStateChanged_UsingUserDataForRrcDetection()
+ throws Exception {
+ mBundle.putBoolean(
+ CarrierConfigManager.KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, true);
+ doReturn(true).when(mTelephonyManager).isRadioInterfaceCapabilitySupported(
+ TelephonyManager.CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED);
+ mNetworkTypeController = new NetworkTypeController(mPhone, mDisplayInfoController);
+ broadcastCarrierConfigs();
+ processAllMessages();
+ testTransitionToCurrentStateLteConnected_usingUserDataForRrcDetection();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ mNetworkTypeController.sendMessage(EVENT_PHYSICAL_LINK_STATE_CHANGED,
+ new AsyncResult(null, DcController.PHYSICAL_LINK_NOT_ACTIVE, null));
+
+ processAllMessages();
+
assertEquals("not_restricted_rrc_idle", getCurrentState().getName());
}
@@ -338,8 +791,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
mNetworkTypeController.getOverrideNetworkType());
// remove NR from preferred network types
- doReturn(TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA).when(mPhone)
- .getCachedPreferredNetworkType();
+ doReturn(RadioAccessFamily.getRafFromNetworkType(
+ TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)).when(
+ mPhone).getCachedAllowedNetworkTypesBitmask();
mNetworkTypeController.sendMessage(EVENT_PREFERRED_NETWORK_MODE_CHANGED);
processAllMessages();
@@ -349,6 +803,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
@Test
public void testPrimaryTimerExpire() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
@@ -366,6 +821,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// timer expires
moveTimeForward(10 * 1000);
@@ -374,10 +830,12 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
}
@Test
public void testPrimaryTimerReset() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
@@ -395,6 +853,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// reconnect to NR in the middle of the timer
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
@@ -408,10 +867,12 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
}
@Test
public void testPrimaryTimerExpireMmwave() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
@@ -419,7 +880,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
broadcastCarrierConfigs();
assertEquals("connected_mmwave", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
// should trigger 10 second timer
@@ -427,10 +888,10 @@ public class NetworkTypeControllerTest extends TelephonyTest {
mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
processAllMessages();
-
assertEquals("connected", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// timer expires
moveTimeForward(10 * 1000);
@@ -439,10 +900,12 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
}
@Test
public void testPrimaryTimerResetMmwave() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
@@ -450,7 +913,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
broadcastCarrierConfigs();
assertEquals("connected_mmwave", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
// trigger 10 second timer after disconnecting from NR
@@ -459,8 +922,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
processAllMessages();
assertEquals("connected", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// reconnect to NR in the middle of the timer
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
@@ -472,12 +936,14 @@ public class NetworkTypeControllerTest extends TelephonyTest {
// timer should not have gone off
assertEquals("connected_mmwave", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
}
@Test
public void testSecondaryTimerExpire() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
@@ -497,6 +963,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -506,6 +973,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// secondary timer expires
moveTimeForward(30 * 1000);
@@ -514,10 +982,12 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
}
@Test
public void testSecondaryTimerReset() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
"connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
@@ -537,6 +1007,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -546,6 +1017,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("legacy", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// reconnect to NR in the middle of the timer
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
@@ -559,10 +1031,12 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
}
@Test
public void testSecondaryTimerExpireMmwave() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
@@ -572,7 +1046,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
broadcastCarrierConfigs();
assertEquals("connected_mmwave", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
// should trigger 10 second primary timer
@@ -581,8 +1055,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
processAllMessages();
assertEquals("connected", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -590,8 +1065,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
// should trigger 30 second secondary timer
assertEquals("connected", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// secondary timer expires
moveTimeForward(30 * 1000);
@@ -600,10 +1076,12 @@ public class NetworkTypeControllerTest extends TelephonyTest {
assertEquals("connected", getCurrentState().getName());
assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
}
@Test
public void testSecondaryTimerResetMmwave() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
@@ -613,7 +1091,7 @@ public class NetworkTypeControllerTest extends TelephonyTest {
broadcastCarrierConfigs();
assertEquals("connected_mmwave", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
// should trigger 10 second primary timer
@@ -622,8 +1100,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
processAllMessages();
assertEquals("connected", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// primary timer expires
moveTimeForward(10 * 1000);
@@ -631,8 +1110,9 @@ public class NetworkTypeControllerTest extends TelephonyTest {
// should trigger 30 second secondary timer
assertEquals("connected", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
// reconnect to NR in the middle of the timer
doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
@@ -644,7 +1124,66 @@ public class NetworkTypeControllerTest extends TelephonyTest {
// timer should not have gone off
assertEquals("connected_mmwave", getCurrentState().getName());
- assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE,
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ }
+
+ @Test
+ public void testNrTimerResetIn3g() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ doReturn(ServiceState.FREQUENCY_RANGE_MMWAVE).when(mServiceState).getNrFrequencyRange();
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,30");
+ broadcastCarrierConfigs();
+
+ assertEquals("connected_mmwave", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
mNetworkTypeController.getOverrideNetworkType());
+
+ // should trigger 10 second primary timer
+ doReturn(ServiceState.FREQUENCY_RANGE_LOW).when(mServiceState).getNrFrequencyRange();
+ mNetworkTypeController.sendMessage(EVENT_NR_FREQUENCY_CHANGED);
+ processAllMessages();
+
+ assertEquals("connected", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertTrue(mNetworkTypeController.is5GHysteresisActive());
+
+ // rat is UMTS, should stop timer
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_UMTS)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(nri).when(mServiceState).getNetworkRegistrationInfo(anyInt(), anyInt());
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(EVENT_DATA_RAT_CHANGED);
+ processAllMessages();
+
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ }
+
+ private void setPhysicalLinkState(Boolean state) {
+ List<PhysicalChannelConfig> lastPhysicalChannelConfigList = new ArrayList<>();
+ // If PhysicalChannelConfigList is empty, PhysicalLinkState is DcController
+ // .PHYSICAL_LINK_NOT_ACTIVE
+ // If PhysicalChannelConfigList is not empty, PhysicalLinkState is DcController
+ // .PHYSICAL_LINK_ACTIVE
+
+ if (state) {
+ PhysicalChannelConfig physicalChannelConfig = new PhysicalChannelConfig.Builder()
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+ .setBand(41)
+ .build();
+ lastPhysicalChannelConfigList.add(physicalChannelConfig);
+ }
+ doReturn(lastPhysicalChannelConfigList).when(mSST).getPhysicalChannelConfigList();
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java
index a504dc14df..24106251b7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneCapabilityTest.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -35,23 +36,25 @@ public class PhoneCapabilityTest {
public void basicTests() throws Exception {
int maxActiveVoiceCalls = 1;
int maxActiveData = 2;
- int max5G = 3;
ModemInfo modemInfo = new ModemInfo(1, 2, true, false);
List<ModemInfo> logicalModemList = new ArrayList<>();
logicalModemList.add(modemInfo);
+ int[] deviceNrCapabilities = new int[]{PhoneCapability.DEVICE_NR_CAPABILITY_SA};
- PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G,
- logicalModemList, false);
+ PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData,
+ logicalModemList, false, deviceNrCapabilities);
- assertEquals(maxActiveVoiceCalls, capability.maxActiveVoiceCalls);
- assertEquals(maxActiveData, capability.maxActiveData);
- assertEquals(max5G, capability.max5G);
- assertEquals(1, capability.logicalModemList.size());
- assertEquals(modemInfo, capability.logicalModemList.get(0));
- PhoneCapability toCompare = new PhoneCapability(
- maxActiveVoiceCalls + 1, maxActiveData - 1, max5G, logicalModemList, false);
- assertEquals(capability, new PhoneCapability(
- maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList, false));
+ assertEquals(maxActiveVoiceCalls, capability.getMaxActiveVoiceSubscriptions());
+ assertEquals(maxActiveData, capability.getMaxActiveDataSubscriptions());
+ assertEquals(1, capability.getLogicalModemList().size());
+ assertEquals(modemInfo, capability.getLogicalModemList().get(0));
+ assertArrayEquals(deviceNrCapabilities, capability.getDeviceNrCapabilities());
+
+ PhoneCapability toCompare = new PhoneCapability(maxActiveVoiceCalls + 1, maxActiveData - 1,
+ logicalModemList, false, deviceNrCapabilities);
+ assertEquals(capability,
+ new PhoneCapability(maxActiveVoiceCalls, maxActiveData, logicalModemList,
+ false, deviceNrCapabilities));
assertNotEquals(capability, toCompare);
}
@@ -60,24 +63,24 @@ public class PhoneCapabilityTest {
public void parcelReadWrite() throws Exception {
int maxActiveVoiceCalls = 1;
int maxActiveData = 2;
- int max5G = 3;
ModemInfo modemInfo = new ModemInfo(1, 2, true, false);
List<ModemInfo> logicalModemList = new ArrayList<>();
logicalModemList.add(modemInfo);
+ int[] deviceNrCapabilities = new int[]{};
- PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData, max5G,
- logicalModemList, false);
+ PhoneCapability capability = new PhoneCapability(maxActiveVoiceCalls, maxActiveData,
+ logicalModemList, false, deviceNrCapabilities);
Parcel parcel = Parcel.obtain();
capability.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
PhoneCapability toCompare = PhoneCapability.CREATOR.createFromParcel(parcel);
- assertEquals(maxActiveVoiceCalls, toCompare.maxActiveVoiceCalls);
- assertEquals(maxActiveData, toCompare.maxActiveData);
- assertEquals(max5G, toCompare.max5G);
- assertEquals(1, toCompare.logicalModemList.size());
- assertEquals(modemInfo, toCompare.logicalModemList.get(0));
+ assertEquals(maxActiveVoiceCalls, toCompare.getMaxActiveVoiceSubscriptions());
+ assertEquals(maxActiveData, toCompare.getMaxActiveDataSubscriptions());
+ assertEquals(1, toCompare.getLogicalModemList().size());
+ assertEquals(modemInfo, toCompare.getLogicalModemList().get(0));
+ assertArrayEquals(deviceNrCapabilities, toCompare.getDeviceNrCapabilities());
assertEquals(capability, toCompare);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 07edd34ad1..a702aace4c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -21,12 +21,14 @@ import static android.telephony.TelephonyManager.EXTRA_ACTIVE_SIM_SUPPORTED_COUN
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Intent;
@@ -51,7 +53,11 @@ public class PhoneConfigurationManagerTest extends TelephonyTest {
@Mock
Handler mHandler;
@Mock
- CommandsInterface mMockCi;
+ CommandsInterface mMockCi0;
+ @Mock
+ CommandsInterface mMockCi1;
+ @Mock
+ private Phone mPhone1; // mPhone as phone 0 is already defined in TelephonyTest.
@Mock
PhoneConfigurationManager.MockableInterface mMi;
@@ -61,8 +67,9 @@ public class PhoneConfigurationManagerTest extends TelephonyTest {
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- mPhone.mCi = mMockCi;
- mCT.mCi = mMockCi;
+ mPhone.mCi = mMockCi0;
+ mCT.mCi = mMockCi0;
+ mPhone1.mCi = mMockCi1;
}
@After
@@ -105,7 +112,7 @@ public class PhoneConfigurationManagerTest extends TelephonyTest {
Message message = new Message();
mPcm.enablePhone(mPhone, false, message);
- verify(mMockCi).enableModem(eq(false), eq(message));
+ verify(mMockCi0).enableModem(eq(false), eq(message));
}
@Test
@@ -140,6 +147,8 @@ public class PhoneConfigurationManagerTest extends TelephonyTest {
@SmallTest
public void testSwitchMultiSimConfig_dsdsCapable_noRebootRequired() throws Exception {
init(1);
+ verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any());
+
// Register for multi SIM config change.
mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
@@ -151,9 +160,13 @@ public class PhoneConfigurationManagerTest extends TelephonyTest {
// Send static capability back to indicate DSDS is supported.
clearInvocations(mMockRadioConfig);
testGetDsdsCapability();
+ // testGetDsdsCapability leads to another call to registerForAvailable()
+ verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any());
// Try to switch to DSDS.
setRebootRequiredForConfigSwitch(false);
+ mPhones = new Phone[]{mPhone, mPhone1};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
mPcm.switchMultiSimConfig(2);
ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
verify(mMockRadioConfig).setModemsConfig(eq(2), captor.capture());
@@ -182,7 +195,66 @@ public class PhoneConfigurationManagerTest extends TelephonyTest {
assertEquals(2, intent.getIntExtra(
EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0));
- // Verify RIL notification.
- verify(mMockCi).onSlotActiveStatusChange(true);
+ // Verify registerForAvailable() and onSlotActiveStatusChange() are called for the second
+ // phone, and not for the first phone (registerForAvailable() was already called twice
+ // earlier so verify that the count is still at 2)
+ verify(mMockCi0, times(2)).registerForAvailable(any(), anyInt(), any());
+ verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean());
+ verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
+ verify(mMockCi1, times(1)).onSlotActiveStatusChange(anyBoolean());
+ }
+
+ @Test
+ @SmallTest
+ public void testSwitchMultiSimConfig_multiSimToSingleSim() throws Exception {
+ mPhones = new Phone[]{mPhone, mPhone1};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ init(2);
+ verify(mMockCi0, times(1)).registerForAvailable(any(), anyInt(), any());
+ verify(mMockCi1, times(1)).registerForAvailable(any(), anyInt(), any());
+
+ // Register for multi SIM config change.
+ mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
+ verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
+
+ // Switch to single sim.
+ setRebootRequiredForConfigSwitch(false);
+ mPcm.switchMultiSimConfig(1);
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockRadioConfig).setModemsConfig(eq(1), captor.capture());
+
+ // Send message back to indicate switch success.
+ Message message = captor.getValue();
+ AsyncResult.forMessage(message, null, null);
+ message.sendToTarget();
+ processAllMessages();
+
+ // Verify set system property being called.
+ verify(mMi).setMultiSimProperties(1);
+ verify(mMi).notifyPhoneFactoryOnMultiSimConfigChanged(any(), eq(1));
+
+ // Capture and verify registration notification.
+ verify(mHandler).sendMessageAtTime(captor.capture(), anyLong());
+ message = captor.getValue();
+ assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what);
+ assertEquals(1, ((AsyncResult) message.obj).result);
+
+ // Capture and verify broadcast.
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcast(intentCaptor.capture());
+ Intent intent = intentCaptor.getValue();
+ assertEquals(ACTION_MULTI_SIM_CONFIG_CHANGED, intent.getAction());
+ assertEquals(1, intent.getIntExtra(
+ EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, 0));
+
+ // Verify clearSubInfoRecord() and onSlotActiveStatusChange() are called for second phone,
+ // and not for the first one
+ verify(mSubscriptionController).clearSubInfoRecord(1);
+ verify(mMockCi1).onSlotActiveStatusChange(anyBoolean());
+ verify(mSubscriptionController, never()).clearSubInfoRecord(0);
+ verify(mMockCi0, never()).onSlotActiveStatusChange(anyBoolean());
+
+ // Verify onPhoneRemoved() gets called on MultiSimSettingController phone
+ verify(mMultiSimSettingController).onPhoneRemoved();
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
index 8c55442922..4747598530 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.verify;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
import android.telephony.emergency.EmergencyNumber;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -61,7 +62,8 @@ public class PhoneStateListenerTest extends TelephonyTest {
mUserMobileDataState = true;
}
- public void onOutgoingEmergencyCall(EmergencyNumber emergencyNumber) {
+ public void onOutgoingEmergencyCall(EmergencyNumber emergencyNumber,
+ int subscriptionId) {
logd("OutgoingCallEmergencyNumber Changed");
mCalledEmergencyNumber = emergencyNumber;
}
@@ -125,7 +127,7 @@ public class PhoneStateListenerTest extends TelephonyTest {
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
((IPhoneStateListener) field.get(mPhoneStateListenerUT)).onOutgoingEmergencyCall(
- emergencyNumber);
+ emergencyNumber, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
processAllMessages();
assertTrue(mCalledEmergencyNumber.equals(emergencyNumber));
@@ -148,7 +150,7 @@ public class PhoneStateListenerTest extends TelephonyTest {
EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
((IPhoneStateListener) field.get(mPhoneStateListenerUT)).onOutgoingEmergencySms(
- emergencyNumber);
+ emergencyNumber, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
processAllMessages();
assertTrue(mTextedEmergencyNumber.equals(emergencyNumber));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index 6480f65438..05256b8d67 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -16,8 +16,6 @@
package com.android.internal.telephony;
import static android.Manifest.permission.READ_PHONE_STATE;
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
-import static android.Manifest.permission.READ_SMS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -31,6 +29,8 @@ import static org.mockito.Mockito.eq;
import android.app.AppOpsManager;
import android.app.PropertyInvalidatedCache;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Build;
import android.test.suitebuilder.annotation.SmallTest;
@@ -45,6 +45,7 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
private PhoneSubInfoController mPhoneSubInfoControllerUT;
private AppOpsManager mAppOsMgr;
+ private PackageManager mPm;
@Mock
GsmCdmaPhone mSecondPhone;
@@ -70,6 +71,7 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
doReturn(mContext).when(mSecondPhone).getContext();
mAppOsMgr = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mPm = mContext.getPackageManager();
replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mSecondPhone});
mPhoneSubInfoControllerUT = new PhoneSubInfoController(mContext);
@@ -82,6 +84,9 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOpNoThrow(
eq(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS), anyInt(), eq(TAG), eq(FEATURE_ID),
nullable(String.class));
+
+ // Bypass calling package check.
+ doReturn(Binder.getCallingUid()).when(mPm).getPackageUid(eq(TAG), anyInt());
}
@After
@@ -589,16 +594,11 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
/* case 1: no READ_PRIVILEGED_PHONE_STATE & READ_PHONE_STATE &
READ_SMS and no OP_WRITE_SMS & OP_READ_SMS from appOsMgr */
+ // All permission checks are handled by the LegacyPermissionManager, so this test only
+ // requires three case; all permissions / appops denied, READ_PHONE_STATE permission
+ // granted without the appop, and one or more of the permissions / appops granted.
mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_WRITE_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ setPhoneNumberAccess(PackageManager.PERMISSION_DENIED);
try {
mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID);
Assert.fail("expected Security Exception Thrown");
@@ -613,61 +613,13 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
assertTrue(ex instanceof SecurityException);
}
- /* case 2: only enable WRITE_SMS permission */
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_WRITE_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- assertEquals("+18051234567",
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID));
- assertEquals("+18052345678",
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(1, TAG, FEATURE_ID));
-
- /* case 3: only enable READ_PRIVILEGED_PHONE_STATE */
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_WRITE_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
- assertEquals("+18051234567",
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID));
- assertEquals("+18052345678",
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(1, TAG, FEATURE_ID));
-
- /* case 4: only enable READ_PHONE_STATE permission */
- mContextFixture.removeCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
- mContextFixture.addCallingOrSelfPermission(READ_PHONE_STATE);
+ /* case 2: only enable READ_PHONE_STATE permission */
+ setPhoneNumberAccess(AppOpsManager.MODE_IGNORED);
assertNull(mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID));
assertNull(mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(1, TAG, FEATURE_ID));
- /* case 5: enable appOsMgr READ_PHONE_PERMISSION & READ_PHONE_STATE */
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- assertEquals("+18051234567",
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID));
- assertEquals("+18052345678",
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(1, TAG, FEATURE_ID));
-
- /* case 6: only enable READ_SMS; without the appop should throw SecurityException */
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- mContextFixture.removeCallingOrSelfPermission(READ_PHONE_STATE);
- mContextFixture.addCallingOrSelfPermission(READ_SMS);
- try {
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID);
- Assert.fail("expected SecurityException thrown");
- } catch (SecurityException expected) {
- }
- try {
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(1, TAG, FEATURE_ID);
- Assert.fail("expected SecurityException thrown");
- } catch (SecurityException expected) {
- }
-
- /* case 7: enable READ_SMS and OP_READ_SMS */
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ /* case 3: enable READ_SMS and OP_READ_SMS */
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
assertEquals("+18051234567",
mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID));
assertEquals("+18052345678",
@@ -684,15 +636,7 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
/* case 1: no READ_PRIVILEGED_PHONE_STATE & READ_PHONE_STATE &
READ_SMS and no OP_WRITE_SMS & OP_READ_SMS from appOsMgr */
mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_WRITE_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ setPhoneNumberAccess(PackageManager.PERMISSION_DENIED);
try {
mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID);
Assert.fail("expected Security Exception Thrown");
@@ -707,32 +651,8 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
assertTrue(ex instanceof SecurityException);
}
- /* case 2: enable READ_PHONE_STATE permission */
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- try {
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID);
- Assert.fail("expected Security Exception Thrown");
- } catch (Exception ex) {
- assertTrue(ex instanceof SecurityException);
- }
-
- try {
- mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(1, TAG, FEATURE_ID);
- Assert.fail("expected Security Exception Thrown");
- } catch (Exception ex) {
- assertTrue(ex instanceof SecurityException);
- }
-
- /* case 3: enable READ_SMS and OP_READ_SMS */
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- mContextFixture.addCallingOrSelfPermission(READ_SMS);
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ /* case 2: enable READ_SMS and OP_READ_SMS */
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
assertEquals("+18051234567",
mPhoneSubInfoControllerUT.getLine1NumberForSubscriber(0, TAG, FEATURE_ID));
assertEquals("+18052345678",
@@ -817,16 +737,10 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
doReturn("+18052345678").when(mSecondPhone).getMsisdn();
/* case 1: no READ_PRIVILEGED_PHONE_STATE & READ_PHONE_STATE from appOsMgr */
+ // The LegacyPermissionManager handles these checks, so set its return code to indicate
+ // none of these have been granted.
mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_WRITE_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ setPhoneNumberAccess(PackageManager.PERMISSION_DENIED);
try {
mPhoneSubInfoControllerUT.getMsisdnForSubscriber(0, TAG, FEATURE_ID);
Assert.fail("expected Security Exception Thrown");
@@ -842,14 +756,14 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
}
/* case 2: only enable READ_PHONE_STATE permission */
- mContextFixture.addCallingOrSelfPermission(READ_PHONE_STATE);
+ // The LegacyPermissionManager will return AppOpsManager.MODE_IGNORED if the target SDK
+ // version < R and the READ_PHONE_STATE permission has been granted without the appop.
+ setPhoneNumberAccess(AppOpsManager.MODE_IGNORED);
assertNull(mPhoneSubInfoControllerUT.getMsisdnForSubscriber(0, TAG, FEATURE_ID));
assertNull(mPhoneSubInfoControllerUT.getMsisdnForSubscriber(1, TAG, FEATURE_ID));
/* case 3: enable appOsMgr READ_PHONE_PERMISSION & READ_PHONE_STATE */
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
assertEquals("+18051234567",
mPhoneSubInfoControllerUT.getMsisdnForSubscriber(0, TAG, FEATURE_ID));
assertEquals("+18052345678",
@@ -865,16 +779,12 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
/* case 1: no READ_PRIVILEGED_PHONE_STATE & READ_PHONE_STATE &
READ_SMS and no OP_WRITE_SMS & OP_READ_SMS from appOsMgr */
+ // Since the LegacyPermissionManager is performing this check the service will perform
+ // the READ_PHONE_STATE checks based on target SDK version; for apps targeting R+ it
+ // will not check the READ_PHONE_STATE permission and appop and will only return
+ // permission granted / denied.
mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_WRITE_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ setPhoneNumberAccess(PackageManager.PERMISSION_DENIED);
try {
mPhoneSubInfoControllerUT.getMsisdnForSubscriber(0, TAG, FEATURE_ID);
Assert.fail("expected Security Exception Thrown");
@@ -889,32 +799,8 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
assertTrue(ex instanceof SecurityException);
}
- /* case 2: only enable READ_PHONE_STATE permission */
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- try {
- mPhoneSubInfoControllerUT.getMsisdnForSubscriber(0, TAG, FEATURE_ID);
- Assert.fail("expected Security Exception Thrown");
- } catch (Exception ex) {
- assertTrue(ex instanceof SecurityException);
- }
-
- try {
- mPhoneSubInfoControllerUT.getMsisdnForSubscriber(1, TAG, FEATURE_ID);
- Assert.fail("expected Security Exception Thrown");
- } catch (Exception ex) {
- assertTrue(ex instanceof SecurityException);
- }
-
- /* case 3: enable READ_SMS and OP_READ_SMS */
- doReturn(AppOpsManager.MODE_ERRORED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_PHONE_STATE), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
- mContextFixture.addCallingOrSelfPermission(READ_SMS);
- doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOsMgr).noteOp(
- eq(AppOpsManager.OPSTR_READ_SMS), anyInt(), eq(TAG), eq(FEATURE_ID),
- nullable(String.class));
+ /* case 2: enable READ_SMS and OP_READ_SMS */
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
assertEquals("+18051234567",
mPhoneSubInfoControllerUT.getMsisdnForSubscriber(0, TAG, FEATURE_ID));
assertEquals("+18052345678",
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index e13bacaa97..b0e9a37c4b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -17,12 +17,19 @@
package com.android.internal.telephony;
import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG;
+import static android.telephony.TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED;
+import static android.telephony.TelephonyManager.EXTRA_SIM_STATE;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
+import static android.telephony.TelephonyManager.SIM_STATE_LOADED;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
import static com.android.internal.telephony.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
import static com.android.internal.telephony.PhoneSwitcher.EVENT_DATA_ENABLED_CHANGED;
+import static com.android.internal.telephony.PhoneSwitcher.EVENT_IMS_RADIO_TECH_CHANGED;
import static com.android.internal.telephony.PhoneSwitcher.EVENT_MULTI_SIM_CONFIG_CHANGED;
import static com.android.internal.telephony.PhoneSwitcher.EVENT_PRECISE_CALL_STATE_CHANGED;
@@ -55,7 +62,9 @@ import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.telephony.PhoneCapability;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -76,6 +85,8 @@ import java.util.concurrent.LinkedBlockingQueue;
@TestableLooper.RunWithLooper
public class PhoneSwitcherTest extends TelephonyTest {
private static final int ACTIVE_PHONE_SWITCH = 1;
+ private static final int EVENT_RADIO_ON = 108;
+ private static final int EVENT_MODEM_COMMAND_DONE = 112;
@Mock
private CommandsInterface mCommandsInterface0;
@@ -84,6 +95,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
@Mock
private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
@Mock
+ private Phone mImsPhone;
+ @Mock
private DataEnabledSettings mDataEnabledSettings2;
@Mock
private Handler mActivePhoneSwitchHandler;
@@ -99,6 +112,10 @@ public class PhoneSwitcherTest extends TelephonyTest {
private ISetOpportunisticDataCallback mSetOpptDataCallback2;
@Mock
CompletableFuture<Boolean> mFuturePhone;
+ @Mock
+ PhoneSwitcher.ImsRegTechProvider mMockImsRegTechProvider;
+ @Mock
+ private SubscriptionInfo mSubscriptionInfo;
private PhoneSwitcher mPhoneSwitcher;
private SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener;
@@ -116,7 +133,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- PhoneCapability phoneCapability = new PhoneCapability(1, 1, 0, null, false);
+ PhoneCapability phoneCapability = new PhoneCapability(1, 1, null, false, new int[0]);
doReturn(phoneCapability).when(mPhoneConfigurationManager).getCurrentPhoneCapability();
doReturn(Call.State.ACTIVE).when(mActiveCall).getState();
@@ -166,6 +183,9 @@ public class PhoneSwitcherTest extends TelephonyTest {
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
+ AsyncResult res = new AsyncResult(1, null, null);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
@@ -185,6 +205,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
// 1 lose default via default sub change
setDefaultDataSubId(1);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
@@ -193,6 +215,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
@@ -201,6 +225,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
// 2 gain default via default sub change
setDefaultDataSubId(0);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[1]);
@@ -210,6 +236,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
setSlotIndexToSubId(0, 2);
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
@@ -221,6 +249,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
@@ -229,6 +259,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
// 5 lose default network request
releaseNetworkRequest(internetNetworkRequest);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
@@ -237,6 +269,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
// 6 gain subscription-specific request
NetworkRequest specificInternetRequest = addInternetNetworkRequest(0, 50);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
@@ -247,6 +281,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
@@ -257,6 +293,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
mSubChangedListener.onSubscriptionsChanged();
processAllMessages();
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertTrue("data not allowed", mDataAllowed[0]);
@@ -265,6 +303,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
// 9 lose subscription-specific request
releaseNetworkRequest(specificInternetRequest);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
clearInvocations(mActivePhoneSwitchHandler);
assertFalse("data allowed", mDataAllowed[0]);
@@ -328,6 +368,10 @@ public class PhoneSwitcherTest extends TelephonyTest {
// now start a higher priority connection on the other sub
addMmsNetworkRequest(1);
+ AsyncResult res = new AsyncResult(1, null, null);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
+
// After gain of network request, mActivePhoneSwitchHandler should be notified 2 times.
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
assertFalse("data allowed", mDataAllowed[0]);
@@ -429,8 +473,10 @@ public class PhoneSwitcherTest extends TelephonyTest {
setDefaultDataSubId(1);
// Phone 0 (sub 1) should be preferred data phone as it has default data sub.
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ AsyncResult res = new AsyncResult(1, null, null);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
-
clearInvocations(mMockRadioConfig);
clearInvocations(mActivePhoneSwitchHandler);
@@ -452,6 +498,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 0));
@@ -469,6 +517,8 @@ public class PhoneSwitcherTest extends TelephonyTest {
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
verify(mActivePhoneSwitchHandler, times(2)).sendMessageAtTime(any(), anyLong());
assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(internetRequest, 0));
assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(mmsRequest, 0));
@@ -478,6 +528,12 @@ public class PhoneSwitcherTest extends TelephonyTest {
// SetDataAllowed should never be triggered.
verify(mCommandsInterface0, never()).setDataAllowed(anyBoolean(), any());
verify(mCommandsInterface1, never()).setDataAllowed(anyBoolean(), any());
+
+ // Set preferred data modem should be triggered after radio on or available.
+ clearInvocations(mMockRadioConfig);
+ Message.obtain(mPhoneSwitcher, EVENT_RADIO_ON, res).sendToTarget();
+ processAllMessages();
+ verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
}
@Test
@@ -529,6 +585,102 @@ public class PhoneSwitcherTest extends TelephonyTest {
assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
}
+ private void mockImsRegTech(int phoneId, int regTech) {
+ doReturn(regTech).when(mMockImsRegTechProvider).get(any(), eq(phoneId));
+ mPhoneSwitcher.mImsRegTechProvider = mMockImsRegTechProvider;
+ }
+
+ @Test
+ @SmallTest
+ public void testNonDefaultDataPhoneInCall_ImsCallOnLte_shouldSwitchDds() throws Exception {
+ initialize();
+ setAllPhonesInactive();
+
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ // Both are active subscriptions are active sub, as they are in both active slots.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+ processAllMessages();
+
+ // Phone 0 should be the default data phoneId.
+ assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+
+ // Phone2 has active IMS call on LTE. And data of DEFAULT apn is enabled. This should
+ // trigger data switch.
+ doReturn(mImsPhone).when(mPhone2).getImsPhone();
+ doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ mockImsRegTech(1, REGISTRATION_TECH_LTE);
+ notifyPhoneAsInCall(mImsPhone);
+
+ // Phone 1 should become the default data phone.
+ assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+ }
+
+ @Test
+ @SmallTest
+ public void testNonDefaultDataPhoneInCall_ImsCallOnWlan_shouldNotSwitchDds() throws Exception {
+ initialize();
+ setAllPhonesInactive();
+
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ // Both are active subscriptions are active sub, as they are in both active slots.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+ processAllMessages();
+
+ // Phone 0 should be the default data phoneId.
+ assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+
+ // Phone2 has active call, but data is turned off. So no data switching should happen.
+ doReturn(mImsPhone).when(mPhone2).getImsPhone();
+ doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ mockImsRegTech(1, REGISTRATION_TECH_IWLAN);
+ notifyPhoneAsInCall(mImsPhone);
+
+ // Phone 0 should remain the default data phone.
+ assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+ }
+
+ @Test
+ @SmallTest
+ public void testNonDefaultDataPhoneInCall_ImsCallOnCrossSIM_HandoverToLTE() throws Exception {
+ initialize();
+ setAllPhonesInactive();
+
+ // Phone 0 has sub 1, phone 1 has sub 2.
+ // Sub 1 is default data sub.
+ // Both are active subscriptions are active sub, as they are in both active slots.
+ setSlotIndexToSubId(0, 1);
+ setSlotIndexToSubId(1, 2);
+ setDefaultDataSubId(1);
+ processAllMessages();
+
+ // Phone 0 should be the default data phoneId.
+ assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+
+ // Phone 1 has active IMS call on CROSS_SIM. And data of DEFAULT apn is enabled. This should
+ // not trigger data switch.
+ doReturn(mImsPhone).when(mPhone2).getImsPhone();
+ doReturn(true).when(mDataEnabledSettings2).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+ mockImsRegTech(1, REGISTRATION_TECH_CROSS_SIM);
+ notifyPhoneAsInCall(mImsPhone);
+
+ // Phone 0 should remain the default data phone.
+ assertEquals(0, mPhoneSwitcher.getPreferredDataPhoneId());
+
+ // Phone 1 has has handed over the call to LTE. And data of DEFAULT apn is enabled.
+ // This should trigger data switch.
+ mockImsRegTech(1, REGISTRATION_TECH_LTE);
+ notifyImsRegistrationTechChange(mPhone2);
+
+ // Phone 1 should become the default data phone.
+ assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
+ }
+
@Test
@SmallTest
public void testNonDefaultDataPhoneInCall() throws Exception {
@@ -613,6 +765,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
sendPreferredDataSuccessResult(1);
processAllMessages();
verify(mFuturePhone).complete(true);
+
// Make sure the correct broadcast is sent out for the overridden phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(2));
}
@@ -667,6 +820,10 @@ public class PhoneSwitcherTest extends TelephonyTest {
moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 1000);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ AsyncResult res = new AsyncResult(1, null, null);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
+
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@@ -714,6 +871,9 @@ public class PhoneSwitcherTest extends TelephonyTest {
moveTimeForward(1000);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ AsyncResult res = new AsyncResult(1, null, null);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@@ -742,6 +902,10 @@ public class PhoneSwitcherTest extends TelephonyTest {
moveTimeForward(PhoneSwitcher.DEFAULT_DATA_OVERRIDE_TIMEOUT_MS);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ AsyncResult res = new AsyncResult(1, null, null);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
+
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@@ -789,6 +953,10 @@ public class PhoneSwitcherTest extends TelephonyTest {
moveTimeForward(ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS + 1000);
processAllMessages();
verify(mMockRadioConfig).setPreferredDataModem(eq(0), any());
+ AsyncResult res = new AsyncResult(1, null, null);
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
+ processAllMessages();
+
// Make sure the correct broadcast is sent out for the phone ID
verify(mTelephonyRegistryManager).notifyActiveDataSubIdChanged(eq(1));
}
@@ -996,6 +1164,59 @@ public class PhoneSwitcherTest extends TelephonyTest {
verify(mMockRadioConfig).setPreferredDataModem(eq(1), any());
}
+ @Test
+ public void testRetry_DDS_switch_Failure() throws Exception {
+ doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
+ mActiveModemCount = 2;
+ initialize();
+ setSlotIndexToSubId(0, 1);
+ setDefaultDataSubId(1);
+
+ clearInvocations(mMockRadioConfig);
+ // for exceptions OP_NOT_ALLOWED_DURING_VOICE_CALL and INVALID_SIM_STATE,
+ // modem retry not invoked.
+ AsyncResult res1 = new AsyncResult(0, null,
+ new CommandException(CommandException.Error.INVALID_SIM_STATE));
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res1).sendToTarget();
+ processAllMessages();
+ moveTimeForward(5000);
+ processAllMessages();
+ verify(mMockRadioConfig, times(0)).setPreferredDataModem(eq(0), any());
+
+ doReturn(0).when(mSubscriptionController).getPhoneId(anyInt());
+ AsyncResult res2 = new AsyncResult(0, null,
+ new CommandException(CommandException.Error.NETWORK_NOT_READY));
+ Message.obtain(mPhoneSwitcher, EVENT_MODEM_COMMAND_DONE, res2).sendToTarget();
+ processAllMessages();
+ moveTimeForward(5000);
+ processAllMessages();
+
+ verify(mMockRadioConfig, times(1)).setPreferredDataModem(eq(0), any());
+
+ clearInvocations(mMockRadioConfig);
+ doReturn(mSubscriptionInfo).when(mSubscriptionController)
+ .getActiveSubscriptionInfoForSimSlotIndex(eq(0), any(), any());
+ doReturn(true).when(mSubscriptionInfo).areUiccApplicationsEnabled();
+ doReturn(mIccCard).when(mPhone).getIccCard();
+ doReturn(true).when(mIccCard).isEmptyProfile();
+ final Intent intent1 = new Intent(ACTION_SIM_APPLICATION_STATE_CHANGED);
+ intent1.putExtra(EXTRA_SIM_STATE, SIM_STATE_LOADED);
+ intent1.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+ mContext.sendBroadcast(intent1);
+ processAllMessages();
+
+ verify(mMockRadioConfig, times(0)).setPreferredDataModem(eq(0), any());
+
+ doReturn(false).when(mIccCard).isEmptyProfile();
+ final Intent intent2 = new Intent(ACTION_SIM_APPLICATION_STATE_CHANGED);
+ intent2.putExtra(EXTRA_SIM_STATE, SIM_STATE_LOADED);
+ intent2.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+ mContext.sendBroadcast(intent2);
+ processAllMessages();
+
+ verify(mMockRadioConfig, times(1)).setPreferredDataModem(eq(0), any());
+ }
+
/* Private utility methods start here */
private void setAllPhonesInactive() {
@@ -1005,6 +1226,9 @@ public class PhoneSwitcherTest extends TelephonyTest {
doReturn(mInactiveCall).when(mPhone2).getForegroundCall();
doReturn(mInactiveCall).when(mPhone2).getBackgroundCall();
doReturn(mInactiveCall).when(mPhone2).getRingingCall();
+ doReturn(mInactiveCall).when(mImsPhone).getForegroundCall();
+ doReturn(mInactiveCall).when(mImsPhone).getBackgroundCall();
+ doReturn(mInactiveCall).when(mImsPhone).getRingingCall();
}
private void notifyPhoneAsInCall(Phone phone) {
@@ -1032,6 +1256,11 @@ public class PhoneSwitcherTest extends TelephonyTest {
processAllMessages();
}
+ private void notifyImsRegistrationTechChange(Phone phone) {
+ mPhoneSwitcher.sendEmptyMessage(EVENT_IMS_RADIO_TECH_CHANGED);
+ processAllMessages();
+ }
+
private Message getEcbmRegistration(Phone phone) {
ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java
index 535f7b28c5..1811073f5d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhysicalChannelConfigTest.java
@@ -17,63 +17,156 @@ package com.android.internal.telephony;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.fail;
+
import android.os.Parcel;
+import android.telephony.AccessNetworkConstants;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PhysicalChannelConfig.Builder;
import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import org.junit.Test;
/** Unit test for {@link android.telephony.PhysicalChannelConfig}. */
public class PhysicalChannelConfigTest {
- private static final int RAT = ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+ private static final int NETWORK_TYPE_NR = TelephonyManager.NETWORK_TYPE_NR;
+ private static final int NETWORK_TYPE_LTE = TelephonyManager.NETWORK_TYPE_LTE;
+ private static final int NETWORK_TYPE_UMTS = TelephonyManager.NETWORK_TYPE_UMTS;
+ private static final int NETWORK_TYPE_GSM = TelephonyManager.NETWORK_TYPE_GSM;
private static final int CONNECTION_STATUS = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
private static final int CELL_BANDWIDTH = 12345;
private static final int FREQUENCY_RANGE = 1;
private static final int CHANNEL_NUMBER = 1234;
private static final int[] CONTEXT_IDS = new int[] {123, 555, 1, 0};
private static final int PHYSICAL_CELL_ID = 502;
+ private static final int BAND = 1;
+ public static final int INVALID_FREQUENCY = -1;
- @Test
- public void testBuilder() {
- PhysicalChannelConfig config = new Builder()
- .setRat(RAT)
+ private PhysicalChannelConfig mPhysicalChannelConfig;
+
+ private void setUpPhysicalChannelConfig(int networkType, int band, int downlinkChannelNumber,
+ int uplinkChannelNumber, int frequencyRange) {
+ mPhysicalChannelConfig = new Builder()
.setCellConnectionStatus(CONNECTION_STATUS)
.setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
- .setFrequencyRange(FREQUENCY_RANGE)
- .setChannelNumber(CHANNEL_NUMBER)
+ .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
.setContextIds(CONTEXT_IDS)
.setPhysicalCellId(PHYSICAL_CELL_ID)
+ .setNetworkType(networkType)
+ .setFrequencyRange(frequencyRange)
+ .setDownlinkChannelNumber(downlinkChannelNumber)
+ .setUplinkChannelNumber(uplinkChannelNumber)
+ .setBand(band)
.build();
+ }
+
+ @Test
+ public void testDownlinkFrequencyForNrArfcn(){
+ setUpPhysicalChannelConfig(NETWORK_TYPE_NR, AccessNetworkConstants.NgranBands.BAND_1,
+ CHANNEL_NUMBER, CHANNEL_NUMBER, ServiceState.FREQUENCY_RANGE_MID);
+
+ // 3GPP TS 38.104 Table 5.4.2.1-1, {@link AccessNetworkUtils#getFrequencyFromNrArfcn}.
+ // Formula of NR-ARFCN convert to actual frequency:
+ // Actual frequency(kHz) = (RANGE_OFFSET + GLOBAL_KHZ * (ARFCN - ARFCN_OFFSET))
+ assertThat(mPhysicalChannelConfig.getDownlinkFrequencyKhz()).isEqualTo(6170);
+ }
+
+ @Test
+ public void testDownlinkandUplinkFrequencyForEarfcn(){
+ setUpPhysicalChannelConfig(NETWORK_TYPE_LTE, AccessNetworkConstants.EutranBand.BAND_3,
+ CHANNEL_NUMBER, 19500, ServiceState.FREQUENCY_RANGE_MID);
+
+ // 3GPP TS 36.101 Table 5.7.3-1, {@link AccessNetworkUtils#getFrequencyFromEarfcn}.
+ // Formula of E-UTRA ARFCN convert to actual frequency:
+ // Actual frequency(kHz) = (DOWNLINK_LOW + 0.1 * (ARFCN - DOWNLINK_OFFSET)) * FREQUENCY_KHZ
+ // Actual frequency(kHz) = (UPLINK_LOW + 0.1 * (ARFCN - UPLINK_OFFSET)) * FREQUENCY_KHZ
+ assertThat(mPhysicalChannelConfig.getDownlinkFrequencyKhz()).isEqualTo(1808400);
+ assertThat(mPhysicalChannelConfig.getUplinkFrequencyKhz()).isEqualTo(1740000);
+ }
+
+ @Test
+ public void testDownlinkandUplinkFrequencyForUarfcn(){
+ setUpPhysicalChannelConfig(NETWORK_TYPE_UMTS, AccessNetworkConstants.UtranBand.BAND_3,
+ CHANNEL_NUMBER, 940, ServiceState.FREQUENCY_RANGE_MID);
+
+ // 3GPP TS 25.101, {@link AccessNetworkUtils#getFrequencyFromUarfcn}.
+ // Formula of UTRA ARFCN convert to actual frequency:
+ // For general bands:
+ // Downlink actual frequency(kHz) = (DOWNLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ
+ // Uplink actual frequency(kHz) = (UPLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ
+ assertThat(mPhysicalChannelConfig.getDownlinkFrequencyKhz()).isEqualTo(1821800);
+ assertThat(mPhysicalChannelConfig.getUplinkFrequencyKhz()).isEqualTo(1713000);
+ }
+
+ @Test
+ public void testDownlinkFrequencyForArfcn(){
+ setUpPhysicalChannelConfig(NETWORK_TYPE_GSM, AccessNetworkConstants.GeranBand.BAND_450,
+ 270, 270, ServiceState.FREQUENCY_RANGE_LOW);
+
+ // 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN
+ // Formula of Geran ARFCN convert to actual frequency:
+ // Uplink actual frequency(kHz) =
+ // (UPLINK_FREQUENCY_FIRST + 0.2 * (ARFCN - ARFCN_RANGE_FIRST)) * FREQUENCY_KHZ
+ // Downlink actual frequency(kHz) = Uplink actual frequency + 10
+ assertThat(mPhysicalChannelConfig.getDownlinkFrequencyKhz()).isEqualTo(452810);
+ }
+
+ @Test
+ public void testDownlinkandUplinkFrequencyForEarfcnWithIncorrectRange() {
+ setUpPhysicalChannelConfig(NETWORK_TYPE_LTE, AccessNetworkConstants.EutranBand.BAND_3,
+ 900, 900, ServiceState.FREQUENCY_RANGE_MID);
+
+ assertThat(mPhysicalChannelConfig.getDownlinkFrequencyKhz()).isEqualTo(INVALID_FREQUENCY);
+ }
+
+ @Test
+ public void testFrequencyRangeWithoutBand() {
+ try {
+ setUpPhysicalChannelConfig(NETWORK_TYPE_UMTS, 0, CHANNEL_NUMBER, CHANNEL_NUMBER,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN);
+ fail("Frequency range: 0 is invalid.");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testFrequencyRangeForNrArfcn() {
+ setUpPhysicalChannelConfig(NETWORK_TYPE_NR, AccessNetworkConstants.NgranBands.BAND_79,
+ 4500, 4500, ServiceState.FREQUENCY_RANGE_HIGH);
- assertThat(config.getRat()).isEqualTo(RAT);
- assertThat(config.getConnectionStatus()).isEqualTo(CONNECTION_STATUS);
- assertThat(config.getCellBandwidthDownlink()).isEqualTo(CELL_BANDWIDTH);
- assertThat(config.getFrequencyRange()).isEqualTo(FREQUENCY_RANGE);
- assertThat(config.getChannelNumber()).isEqualTo(CHANNEL_NUMBER);
- assertThat(config.getContextIds()).isEqualTo(CONTEXT_IDS);
- assertThat(config.getPhysicalCellId()).isEqualTo(PHYSICAL_CELL_ID);
+ assertThat(mPhysicalChannelConfig.getFrequencyRange()).isEqualTo(
+ ServiceState.FREQUENCY_RANGE_HIGH);
+ }
+
+ @Test
+ public void testBuilder() {
+ setUpPhysicalChannelConfig(NETWORK_TYPE_LTE, BAND, CHANNEL_NUMBER, CHANNEL_NUMBER,
+ FREQUENCY_RANGE);
+
+ assertThat(mPhysicalChannelConfig.getNetworkType()).isEqualTo(NETWORK_TYPE_LTE);
+ assertThat(mPhysicalChannelConfig.getConnectionStatus()).isEqualTo(CONNECTION_STATUS);
+ assertThat(mPhysicalChannelConfig.getCellBandwidthDownlinkKhz()).isEqualTo(CELL_BANDWIDTH);
+ assertThat(mPhysicalChannelConfig.getCellBandwidthUplinkKhz()).isEqualTo(CELL_BANDWIDTH);
+ assertThat(mPhysicalChannelConfig.getFrequencyRange()).isEqualTo(FREQUENCY_RANGE);
+ assertThat(mPhysicalChannelConfig.getContextIds()).isEqualTo(CONTEXT_IDS);
+ assertThat(mPhysicalChannelConfig.getPhysicalCellId()).isEqualTo(PHYSICAL_CELL_ID);
+ assertThat(mPhysicalChannelConfig.getDownlinkChannelNumber()).isEqualTo(CHANNEL_NUMBER);
+ assertThat(mPhysicalChannelConfig.getUplinkChannelNumber()).isEqualTo(CHANNEL_NUMBER);
}
@Test
public void testParcel() {
- PhysicalChannelConfig config = new Builder()
- .setRat(RAT)
- .setCellConnectionStatus(CONNECTION_STATUS)
- .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
- .setFrequencyRange(FREQUENCY_RANGE)
- .setChannelNumber(CHANNEL_NUMBER)
- .setContextIds(CONTEXT_IDS)
- .setPhysicalCellId(PHYSICAL_CELL_ID)
- .build();
+ setUpPhysicalChannelConfig(NETWORK_TYPE_LTE, BAND, CHANNEL_NUMBER, CHANNEL_NUMBER,
+ ServiceState.FREQUENCY_RANGE_MID);
Parcel parcel = Parcel.obtain();
- config.writeToParcel(parcel, 0 /* flags */);
+ mPhysicalChannelConfig.writeToParcel(parcel, 0 /* flags */);
parcel.setDataPosition(0);
PhysicalChannelConfig fromParcel = PhysicalChannelConfig.CREATOR.createFromParcel(parcel);
- assertThat(fromParcel).isEqualTo(config);
+ assertThat(fromParcel).isEqualTo(mPhysicalChannelConfig);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 792e525b22..2b4e484899 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -19,6 +19,8 @@ package com.android.internal.telephony;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ALLOW_DATA;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CHANGE_SIM_PIN;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CHANGE_SIM_PIN2;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CONFERENCE;
@@ -41,6 +43,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_HARDWA
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_IMSI;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_RADIO_CAPABILITY;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIM_STATUS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLICING_CONFIG;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SMSC_ADDRESS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
@@ -110,9 +113,9 @@ import android.hardware.radio.V1_0.NvWriteItem;
import android.hardware.radio.V1_0.RadioError;
import android.hardware.radio.V1_0.RadioResponseInfo;
import android.hardware.radio.V1_0.RadioResponseType;
+import android.hardware.radio.V1_0.RadioTechnologyFamily;
import android.hardware.radio.V1_0.SmsWriteArgs;
-import android.hardware.radio.V1_5.IRadio;
-import android.hardware.radio.deprecated.V1_0.IOemHook;
+import android.hardware.radio.V1_6.IRadio;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.LinkAddress;
@@ -145,6 +148,7 @@ import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.NetworkScanRequest;
+import android.telephony.RadioAccessFamily;
import android.telephony.RadioAccessSpecifier;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -153,6 +157,10 @@ import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
+import android.telephony.data.EpsQos;
+import android.telephony.data.QosBearerFilter;
+import android.telephony.data.QosBearerSession;
+import android.telephony.data.TrafficDescriptor;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -167,6 +175,9 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -191,8 +202,6 @@ public class RILTest extends TelephonyTest {
private TelephonyManager mTelephonyManager;
@Mock
private IRadio mRadioProxy;
- @Mock
- private IOemHook mOemHookProxy;
private HalVersion mRadioVersionV10 = new HalVersion(1, 0);
private HalVersion mRadioVersionV11 = new HalVersion(1, 1);
@@ -200,6 +209,7 @@ public class RILTest extends TelephonyTest {
private HalVersion mRadioVersionV13 = new HalVersion(1, 3);
private HalVersion mRadioVersionV14 = new HalVersion(1, 4);
private HalVersion mRadioVersionV15 = new HalVersion(1, 5);
+ private HalVersion mRadioVersionV16 = new HalVersion(1, 6);
private RIL mRILInstance;
private RIL mRILUnderTest;
@@ -266,7 +276,7 @@ public class RILTest extends TelephonyTest {
private static final int MAX_CONNS = 3;
private static final int WAIT_TIME = 10;
private static final boolean APN_ENABLED = true;
- private static final int SUPPORTED_APNT_YPES_BITMAK = 123456;
+ private static final int SUPPORTED_APN_TYPES_BITMASK = 123456;
private static final int ROAMING_PROTOCOL = ApnSetting.PROTOCOL_IPV6;
private static final int BEARER_BITMASK = 123123;
private static final int MTU = 1234;
@@ -298,11 +308,11 @@ public class RILTest extends TelephonyTest {
doReturn(powerManager).when(context).getSystemService(Context.POWER_SERVICE);
doReturn(new ApplicationInfo()).when(context).getApplicationInfo();
- mRILInstance = new RIL(context, RILConstants.PREFERRED_NETWORK_MODE,
+ mRILInstance = new RIL(context,
+ RadioAccessFamily.getRafFromNetworkType(RILConstants.PREFERRED_NETWORK_MODE),
Phone.PREFERRED_CDMA_SUBSCRIPTION, 0);
mRILUnderTest = spy(mRILInstance);
doReturn(mRadioProxy).when(mRILUnderTest).getRadioProxy(any());
- doReturn(mOemHookProxy).when(mRILUnderTest).getOemHookProxy(any());
try {
replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV10);
@@ -707,6 +717,29 @@ public class RILTest extends TelephonyTest {
@FlakyTest
@Test
+ public void testSetRadioPower_1_6() throws Exception {
+ boolean on = true, forEmergencyCall = false, preferredForEmergencyCall = false;
+
+ // Use Radio HAL v1.6
+ try {
+ replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ } catch (Exception e) {
+ }
+
+ mRILUnderTest.setRadioPower(
+ on, forEmergencyCall, preferredForEmergencyCall, obtainMessage());
+ verify(mRadioProxy)
+ .setRadioPower_1_6(
+ mSerialNumberCaptor.capture(),
+ eq(on),
+ eq(forEmergencyCall),
+ eq(preferredForEmergencyCall));
+ verifyRILResponse_1_6(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_RADIO_POWER);
+ }
+
+ @FlakyTest
+ @Test
public void testSendDtmf() throws Exception {
char c = 'c';
mRILUnderTest.sendDtmf(c, obtainMessage());
@@ -729,6 +762,24 @@ public class RILTest extends TelephonyTest {
@FlakyTest
@Test
+ public void testSendSMS_1_6() throws Exception {
+ // Use Radio HAL v1.6
+ try {
+ replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ } catch (Exception e) {
+ }
+ String smscPdu = "smscPdu";
+ String pdu = "pdu";
+ GsmSmsMessage msg = new GsmSmsMessage();
+ msg.smscPdu = smscPdu;
+ msg.pdu = pdu;
+ mRILUnderTest.sendSMS(smscPdu, pdu, obtainMessage());
+ verify(mRadioProxy).sendSms_1_6(mSerialNumberCaptor.capture(), eq(msg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SEND_SMS);
+ }
+
+ @FlakyTest
+ @Test
public void testSendSMSExpectMore() throws Exception {
String smscPdu = "smscPdu";
String pdu = "pdu";
@@ -743,6 +794,93 @@ public class RILTest extends TelephonyTest {
@FlakyTest
@Test
+ public void testSendSMSExpectMore_1_6() throws Exception {
+ // Use Radio HAL v1.6
+ try {
+ replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ } catch (Exception e) {
+ }
+ String smscPdu = "smscPdu";
+ String pdu = "pdu";
+ GsmSmsMessage msg = new GsmSmsMessage();
+ msg.smscPdu = smscPdu;
+ msg.pdu = pdu;
+ mRILUnderTest.sendSMSExpectMore(smscPdu, pdu, obtainMessage());
+ verify(mRadioProxy).sendSmsExpectMore_1_6(mSerialNumberCaptor.capture(), eq(msg));
+ verifyRILResponse(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_SEND_SMS_EXPECT_MORE);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendCdmaSMS_1_6() throws Exception {
+ // Use Radio HAL v1.6
+ try {
+ replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ } catch (Exception e) {
+ }
+ byte[] pdu = "000010020000000000000000000000000000000000".getBytes();
+ CdmaSmsMessage msg = new CdmaSmsMessage();
+ constructCdmaSendSmsRilRequest(msg, pdu);
+ mRILUnderTest.sendCdmaSms(pdu, obtainMessage());
+ verify(mRadioProxy).sendCdmaSms_1_6(mSerialNumberCaptor.capture(), eq(msg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_CDMA_SEND_SMS);
+ }
+
+ @FlakyTest
+ @Test
+ public void testSendCdmaSMSExpectMore_1_6() throws Exception {
+ // Use Radio HAL v1.6
+ try {
+ replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ } catch (Exception e) {
+ }
+ byte[] pdu = "000010020000000000000000000000000000000000".getBytes();
+ CdmaSmsMessage msg = new CdmaSmsMessage();
+ constructCdmaSendSmsRilRequest(msg, pdu);
+ mRILUnderTest.sendCdmaSMSExpectMore(pdu, obtainMessage());
+ verify(mRadioProxy).sendCdmaSmsExpectMore_1_6(mSerialNumberCaptor.capture(), eq(msg));
+ verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
+ RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE);
+ }
+
+ private void constructCdmaSendSmsRilRequest(CdmaSmsMessage msg, byte[] pdu) {
+ int addrNbrOfDigits;
+ int subaddrNbrOfDigits;
+ int bearerDataLength;
+ ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
+ DataInputStream dis = new DataInputStream(bais);
+
+ try {
+ msg.teleserviceId = dis.readInt(); // teleServiceId
+ msg.isServicePresent = (byte) dis.readInt() == 1 ? true : false; // servicePresent
+ msg.serviceCategory = dis.readInt(); // serviceCategory
+ msg.address.digitMode = dis.read(); // address digit mode
+ msg.address.numberMode = dis.read(); // address number mode
+ msg.address.numberType = dis.read(); // address number type
+ msg.address.numberPlan = dis.read(); // address number plan
+ addrNbrOfDigits = (byte) dis.read();
+ for (int i = 0; i < addrNbrOfDigits; i++) {
+ msg.address.digits.add(dis.readByte()); // address_orig_bytes[i]
+ }
+ msg.subAddress.subaddressType = dis.read(); //subaddressType
+ msg.subAddress.odd = (byte) dis.read() == 1 ? true : false; //subaddr odd
+ subaddrNbrOfDigits = (byte) dis.read();
+ for (int i = 0; i < subaddrNbrOfDigits; i++) {
+ msg.subAddress.digits.add(dis.readByte()); //subaddr_orig_bytes[i]
+ }
+
+ bearerDataLength = dis.read();
+ for (int i = 0; i < bearerDataLength; i++) {
+ msg.bearerData.add(dis.readByte()); //bearerData[i]
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @FlakyTest
+ @Test
public void testWriteSmsToSim() throws Exception {
String smscPdu = "smscPdu";
String pdu = "pdu";
@@ -928,13 +1066,13 @@ public class RILTest extends TelephonyTest {
gsmMsg.pdu = pdu;
ImsSmsMessage firstMsg = new ImsSmsMessage();
- firstMsg.tech = RILConstants.GSM_PHONE;
+ firstMsg.tech = RadioTechnologyFamily.THREE_GPP;
firstMsg.retry = false;
firstMsg.messageRef = 0;
firstMsg.gsmMessage.add(gsmMsg);
ImsSmsMessage retryMsg = new ImsSmsMessage();
- retryMsg.tech = RILConstants.GSM_PHONE;
+ retryMsg.tech = RadioTechnologyFamily.THREE_GPP;
retryMsg.retry = true;
retryMsg.messageRef = 0;
retryMsg.gsmMessage.add(gsmMsg);
@@ -963,13 +1101,13 @@ public class RILTest extends TelephonyTest {
CdmaSmsMessage cdmaMsg = new CdmaSmsMessage();
ImsSmsMessage firstMsg = new ImsSmsMessage();
- firstMsg.tech = RILConstants.CDMA_PHONE;
+ firstMsg.tech = RadioTechnologyFamily.THREE_GPP2;
firstMsg.retry = false;
firstMsg.messageRef = 0;
firstMsg.cdmaMessage.add(cdmaMsg);
ImsSmsMessage retryMsg = new ImsSmsMessage();
- retryMsg.tech = RILConstants.CDMA_PHONE;
+ retryMsg.tech = RadioTechnologyFamily.THREE_GPP2;
retryMsg.retry = true;
retryMsg.messageRef = 0;
retryMsg.cdmaMessage.add(cdmaMsg);
@@ -1241,22 +1379,6 @@ public class RILTest extends TelephonyTest {
mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_BARRING_INFO);
}
- @Test
- public void testInvokeOemRilRequestStrings() throws Exception {
- String[] strings = new String[]{"a", "b", "c"};
- mRILUnderTest.invokeOemRilRequestStrings(strings, obtainMessage());
- verify(mOemHookProxy).sendRequestStrings(
- mSerialNumberCaptor.capture(), eq(new ArrayList<>(Arrays.asList(strings))));
- }
-
- @Test
- public void testInvokeOemRilRequestRaw() throws Exception {
- byte[] data = new byte[]{1, 2, 3};
- mRILUnderTest.invokeOemRilRequestRaw(data, obtainMessage());
- verify(mOemHookProxy).sendRequestRaw(
- mSerialNumberCaptor.capture(), eq(mRILUnderTest.primitiveArrayToArrayList(data)));
- }
-
private Message obtainMessage() {
return mRILUnderTest.getRilHandler().obtainMessage();
}
@@ -1277,6 +1399,23 @@ public class RILTest extends TelephonyTest {
assertFalse(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
}
+ private static void verifyRILResponse_1_6(RIL ril, int serial, int requestType) {
+ android.hardware.radio.V1_6.RadioResponseInfo responseInfo =
+ createFakeRadioResponseInfo_1_6(
+ serial, RadioError.NONE, RadioResponseType.SOLICITED);
+
+ RILRequest rr = ril.processResponse_1_6(responseInfo);
+ assertNotNull(rr);
+
+ assertEquals(serial, rr.getSerial());
+ assertEquals(requestType, rr.getRequest());
+ assertTrue(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
+
+ ril.processResponseDone_1_6(rr, responseInfo, null);
+ assertEquals(0, ril.getRilRequestList().size());
+ assertFalse(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
+ }
+
private static void verifyRILErrorResponse(RIL ril, int serial, int requestType, int error) {
RadioResponseInfo responseInfo =
createFakeRadioResponseInfo(serial, error, RadioResponseType.SOLICITED);
@@ -1322,6 +1461,16 @@ public class RILTest extends TelephonyTest {
return respInfo;
}
+ private static android.hardware.radio.V1_6.RadioResponseInfo createFakeRadioResponseInfo_1_6(
+ int serial, int error, int type) {
+ android.hardware.radio.V1_6.RadioResponseInfo respInfo =
+ new android.hardware.radio.V1_6.RadioResponseInfo();
+ respInfo.serial = serial;
+ respInfo.error = error;
+ respInfo.type = type;
+ return respInfo;
+ }
+
@Test
public void testConvertHalCellInfoListForLTE() {
android.hardware.radio.V1_0.CellInfoLte lte = new android.hardware.radio.V1_0.CellInfoLte();
@@ -1994,7 +2143,7 @@ public class RILTest extends TelephonyTest {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1L)
.setId(0)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
@@ -2013,6 +2162,8 @@ public class RILTest extends TelephonyTest {
.setMtu(1500)
.setMtuV4(1500)
.setMtuV6(1500)
+ .setQosBearerSessions(new ArrayList<>())
+ .setTrafficDescriptors(new ArrayList<>())
.build();
assertEquals(response, RIL.convertDataCallResult(result10));
@@ -2067,7 +2218,7 @@ public class RILTest extends TelephonyTest {
response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1L)
.setId(0)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
@@ -2086,9 +2237,138 @@ public class RILTest extends TelephonyTest {
.setMtu(3000)
.setMtuV4(1500)
.setMtuV6(3000)
+ .setQosBearerSessions(new ArrayList<>())
+ .setTrafficDescriptors(new ArrayList<>())
.build();
assertEquals(response, RIL.convertDataCallResult(result15));
+
+ // Test V1.6 SetupDataCallResult
+ android.hardware.radio.V1_6.SetupDataCallResult result16 =
+ new android.hardware.radio.V1_6.SetupDataCallResult();
+ result16.cause = android.hardware.radio.V1_4.DataCallFailCause.NONE;
+ result16.suggestedRetryTime = -1;
+ result16.cid = 0;
+ result16.active = android.hardware.radio.V1_4.DataConnActiveStatus.ACTIVE;
+ result16.type = android.hardware.radio.V1_4.PdpProtocolType.IPV4V6;
+ result16.ifname = "ifname";
+
+ result16.addresses = new ArrayList<>(Arrays.asList(la1, la2));
+ result16.dnses = new ArrayList<>(Arrays.asList("10.0.2.3", "fd00:976a::9"));
+ result16.gateways = new ArrayList<>(Arrays.asList("10.0.2.15", "fe80::2"));
+ result16.pcscf = new ArrayList<>(Arrays.asList(
+ "fd00:976a:c206:20::6", "fd00:976a:c206:20::9", "fd00:976a:c202:1d::9"));
+ result16.mtuV4 = 1500;
+ result16.mtuV6 = 3000;
+ result16.handoverFailureMode = android.hardware.radio.V1_6.HandoverFailureMode.LEGACY;
+
+ // Build android.hardware.radio.V1_6.EpsQos
+ android.hardware.radio.V1_6.EpsQos halEpsQos = new android.hardware.radio.V1_6.EpsQos();
+ halEpsQos.qci = 4;
+ halEpsQos.downlink.maxBitrateKbps = 4;
+ halEpsQos.downlink.guaranteedBitrateKbps = 7;
+ halEpsQos.uplink.maxBitrateKbps = 5;
+ halEpsQos.uplink.guaranteedBitrateKbps = 8;
+
+ result16.defaultQos.eps(halEpsQos);
+
+ // android.hardware.radio.V1_6.PortRange
+ android.hardware.radio.V1_6.PortRange localPort =
+ new android.hardware.radio.V1_6.PortRange();
+ android.hardware.radio.V1_6.PortRange remotePort =
+ new android.hardware.radio.V1_6.PortRange();
+ localPort.start = 123;
+ localPort.end = 123;
+ remotePort.start = 223;
+ remotePort.end = 223;
+
+ // android.hardware.radio.V1_6.QosFilter
+ android.hardware.radio.V1_6.QosFilter halQosFilter =
+ new android.hardware.radio.V1_6.QosFilter();
+ halQosFilter.localAddresses = new ArrayList<>(Arrays.asList("122.22.22.22"));
+ halQosFilter.remoteAddresses = new ArrayList<>(Arrays.asList("144.44.44.44"));
+ halQosFilter.localPort.range(localPort);
+ halQosFilter.remotePort.range(remotePort);
+ halQosFilter.protocol = android.hardware.radio.V1_6.QosProtocol.UDP;
+ halQosFilter.tos.value((byte)7);
+ halQosFilter.flowLabel.value(987);
+ halQosFilter.spi.value(678);
+ halQosFilter.direction = android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
+ halQosFilter.precedence = 45;
+
+ // android.hardware.radio.V1_6.QosSession
+ android.hardware.radio.V1_6.QosSession halQosSession =
+ new android.hardware.radio.V1_6.QosSession();
+ halQosSession.qosSessionId = 1234;
+ halQosSession.qos.eps(halEpsQos);
+ halQosSession.qosFilters = new ArrayList<>(Arrays.asList(halQosFilter));
+
+ result16.qosSessions = new ArrayList<>(Arrays.asList(halQosSession));
+
+ EpsQos epsQos = new EpsQos(halEpsQos);
+ QosBearerFilter qosFilter = new QosBearerFilter(
+ Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress("122.22.22.22"), 32)),
+ Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 32)),
+ new QosBearerFilter.PortRange(123, 123), new QosBearerFilter.PortRange(223, 223),
+ QosBearerFilter.QOS_PROTOCOL_UDP, 7, 987, 678,
+ QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, 45);
+ ArrayList<QosBearerFilter> qosFilters = new ArrayList<>();
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosFilters.add(qosFilter);
+ QosBearerSession qosSession = new QosBearerSession(1234, epsQos, qosFilters);
+ qosSessions.add(qosSession);
+
+ // android.hardware.radio.V1_6.TrafficDescriptor
+ android.hardware.radio.V1_6.TrafficDescriptor halTrafficDescriptor =
+ new android.hardware.radio.V1_6.TrafficDescriptor();
+ android.hardware.radio.V1_6.OptionalDnn halDnn =
+ new android.hardware.radio.V1_6.OptionalDnn();
+ halDnn.value("DNN");
+
+ android.hardware.radio.V1_6.OptionalOsAppId halOsAppId =
+ new android.hardware.radio.V1_6.OptionalOsAppId();
+ android.hardware.radio.V1_6.OsAppId osAppId = new android.hardware.radio.V1_6.OsAppId();
+ byte[] osAppIdArray = {1, 2, 3, 4};
+ osAppId.osAppId = mRILUnderTest.primitiveArrayToArrayList(osAppIdArray);
+ halOsAppId.value(osAppId);
+
+ halTrafficDescriptor.dnn = halDnn;
+ halTrafficDescriptor.osAppId = halOsAppId;
+ result16.trafficDescriptors = new ArrayList<>(Arrays.asList(halTrafficDescriptor));
+
+ List<TrafficDescriptor> trafficDescriptors = Arrays.asList(
+ new TrafficDescriptor("DNN", osAppIdArray));
+
+ response = new DataCallResponse.Builder()
+ .setCause(0)
+ .setRetryDurationMillis(-1L)
+ .setId(0)
+ .setLinkStatus(2)
+ .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
+ .setInterfaceName("ifname")
+ .setAddresses(Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress("10.0.2.15"), 32),
+ new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")))
+ .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
+ InetAddresses.parseNumericAddress("fd00:976a::9")))
+ .setGatewayAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.15"),
+ InetAddresses.parseNumericAddress("fe80::2")))
+ .setPcscfAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("fd00:976a:c206:20::6"),
+ InetAddresses.parseNumericAddress("fd00:976a:c206:20::9"),
+ InetAddresses.parseNumericAddress("fd00:976a:c202:1d::9")))
+ .setMtu(3000)
+ .setMtuV4(1500)
+ .setMtuV6(3000)
+ .setHandoverFailureMode(DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY)
+ .setDefaultQos(epsQos)
+ .setQosBearerSessions(qosSessions)
+ .setTrafficDescriptors(trafficDescriptors)
+ .build();
+
+ assertEquals(response, RIL.convertDataCallResult(result16));
}
@Test
@@ -2334,7 +2614,7 @@ public class RILTest extends TelephonyTest {
.setMaxConnections(MAX_CONNS)
.setWaitTime(WAIT_TIME)
.enable(APN_ENABLED)
- .setSupportedApnTypesBitmask(SUPPORTED_APNT_YPES_BITMAK)
+ .setSupportedApnTypesBitmask(SUPPORTED_APN_TYPES_BITMASK)
.setRoamingProtocolType(ROAMING_PROTOCOL)
.setBearerBitmask(BEARER_BITMASK)
.setMtu(MTU)
@@ -2343,7 +2623,8 @@ public class RILTest extends TelephonyTest {
.build();
mRILUnderTest.setupDataCall(AccessNetworkConstants.AccessNetworkType.EUTRAN, dp, false,
- false, 0, null, obtainMessage());
+ false, 0, null,
+ DataCallResponse.PDU_SESSION_ID_NOT_SET, null, null, true, obtainMessage());
ArgumentCaptor<DataProfileInfo> dpiCaptor = ArgumentCaptor.forClass(DataProfileInfo.class);
verify(mRadioProxy).setupDataCall(
mSerialNumberCaptor.capture(), eq(AccessNetworkConstants.AccessNetworkType.EUTRAN),
@@ -2362,7 +2643,7 @@ public class RILTest extends TelephonyTest {
assertEquals(MAX_CONNS, dpi.maxConns);
assertEquals(WAIT_TIME, dpi.waitTime);
assertEquals(APN_ENABLED, dpi.enabled);
- assertEquals(SUPPORTED_APNT_YPES_BITMAK, dpi.supportedApnTypesBitmap);
+ assertEquals(SUPPORTED_APN_TYPES_BITMASK, dpi.supportedApnTypesBitmap);
assertEquals(ROAMING_PROTOCOL, ApnSetting.getProtocolIntFromString(dpi.protocol));
assertEquals(
BEARER_BITMASK,
@@ -2522,4 +2803,18 @@ public class RILTest extends TelephonyTest {
mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_5);
assertEquals(RIL.RADIO_HAL_VERSION_1_3, mRILUnderTest.getCompatVersion(testRequest));
}
+
+ @FlakyTest
+ @Test
+ public void testGetSlicingConfig() throws Exception {
+ // Use Radio HAL v1.6
+ try {
+ replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV16);
+ } catch (Exception e) {
+ }
+ mRILUnderTest.getSlicingConfig(obtainMessage());
+ verify(mRadioProxy).getSlicingConfig(mSerialNumberCaptor.capture());
+ verifyRILResponse_1_6(
+ mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_GET_SLICING_CONFIG);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
new file mode 100644
index 0000000000..d529d6791b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/RadioConfigResponseTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertFalse;
+
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+@RunWith(AndroidTestingRunner.class)
+public class RadioConfigResponseTest extends TelephonyTest {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(RadioConfigResponseTest.class.getSimpleName());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testVersion_1_5() {
+ Set<String> caps = RadioConfigResponse.getCaps(RIL.RADIO_HAL_VERSION_1_5, false);
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK));
+ assertFalse(
+ caps.contains(
+ TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING));
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED));
+ }
+
+ @Test
+ public void testReducedFeatureSet() {
+ Set<String> caps = RadioConfigResponse.getCaps(RIL.RADIO_HAL_VERSION_1_6, true);
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
+ assertTrue(
+ caps.contains(TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK));
+ assertFalse(
+ caps.contains(
+ TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING));
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED));
+ assertTrue(
+ caps.contains(TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM));
+ }
+
+ @Test
+ public void testNonReducedFeatureSet() {
+ Set<String> caps = RadioConfigResponse.getCaps(RIL.RADIO_HAL_VERSION_1_6, false);
+ assertTrue(
+ caps.contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
+ assertTrue(
+ caps.contains(TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK));
+ assertTrue(
+ caps.contains(
+ TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE));
+ assertTrue(
+ caps.contains(TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING));
+ assertTrue(
+ caps.contains(TelephonyManager.CAPABILITY_SLICING_CONFIG_SUPPORTED));
+ assertFalse(
+ caps.contains(TelephonyManager.CAPABILITY_SIM_PHONEBOOK_IN_MODEM));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RadioInterfaceCapabilityControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/RadioInterfaceCapabilityControllerTest.java
new file mode 100644
index 0000000000..e8e11249ea
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/RadioInterfaceCapabilityControllerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class RadioInterfaceCapabilityControllerTest extends TelephonyTest {
+ @Mock
+ RadioConfig mMockRadioConfig;
+
+ @Mock
+ CommandsInterface mMockCommandsInterface;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testRadioInterfaceCapabilities() {
+ final RadioInterfaceCapabilityController capabilities =
+ new RadioInterfaceCapabilityController(mMockRadioConfig, mMockCommandsInterface,
+ mTestableLooper.getLooper());
+
+ // The capabilities to test for
+ final Set<String> capabilitySet = new HashSet<>();
+ capabilitySet.add(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE);
+
+ registerForRadioAvailable();
+ getHalDeviceCapabilities(capabilitySet);
+
+ // Test for the capabilities
+ assertEquals(1, capabilities.getCapabilities().size());
+ assertTrue(capabilities.getCapabilities()
+ .contains(TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE));
+ }
+
+ private void registerForRadioAvailable() {
+ // Capture radio avaialble
+ final ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ final ArgumentCaptor<Integer> whatCaptor = ArgumentCaptor.forClass(Integer.class);
+ final ArgumentCaptor<Object> objCaptor = ArgumentCaptor.forClass(Object.class);
+ verify(mMockCommandsInterface, times(1)).registerForAvailable(
+ handlerCaptor.capture(), whatCaptor.capture(), objCaptor.capture());
+
+ // Send Message back through handler
+ final Message m = Message.obtain(
+ handlerCaptor.getValue(), whatCaptor.getValue(), objCaptor.getValue());
+ m.sendToTarget();
+ processAllMessages();
+ }
+
+ private void getHalDeviceCapabilities(final Set<String> capabilitySet) {
+ // Capture Message when the capabilities are requested
+ final ArgumentCaptor<Message> deviceCapsMessage = ArgumentCaptor.forClass(Message.class);
+ verify(mMockRadioConfig, times(1))
+ .getHalDeviceCapabilities(deviceCapsMessage.capture());
+
+ // Send Message back through handler
+ final Message m = deviceCapsMessage.getValue();
+ AsyncResult.forMessage(m, capabilitySet, null);
+ m.sendToTarget();
+ processAllMessages();
+ }
+
+ @Test
+ public void testEmptyRadioInterfaceCapabilities() {
+ final RadioInterfaceCapabilityController capabilities =
+ new RadioInterfaceCapabilityController(mMockRadioConfig, null,
+ mTestableLooper.getLooper());
+
+ // Test for the capabilities
+ assertEquals(0, capabilities.getCapabilities().size());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java b/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
index b0cc83ee16..c3789950ee 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RatRatcheterTest.java
@@ -104,8 +104,7 @@ public class RatRatcheterTest extends TelephonyTest {
false, // isDcNrRestricted
false, // isNrAvailable
false, // isEndcAvailable
- lteVopsSupportInfo, // lteVopsSupportInfo
- isUsingCarrierAggregation); // isUsingCarrierAggregation
+ lteVopsSupportInfo); // lteVopsSupportInfo
}
private void setNetworkRegistrationInfo(ServiceState ss, int accessNetworkTechnology) {
@@ -141,7 +140,7 @@ public class RatRatcheterTest extends TelephonyTest {
setNetworkRegistrationInfo(newSS, TelephonyManager.NETWORK_TYPE_LTE);
RatRatcheter ratRatcheter = new RatRatcheter(mPhone);
- ratRatcheter.ratchet(oldSS, newSS, false);
+ ratRatcheter.ratchet(oldSS, newSS);
assertTrue(newSS.isUsingCarrierAggregation());
}
@@ -158,7 +157,7 @@ public class RatRatcheterTest extends TelephonyTest {
setNetworkRegistrationInfo(newSS, TelephonyManager.NETWORK_TYPE_LTE);
RatRatcheter ratRatcheter = new RatRatcheter(mPhone);
- ratRatcheter.ratchet(oldSS, newSS, false);
+ ratRatcheter.ratchet(oldSS, newSS);
assertFalse(newSS.isUsingCarrierAggregation());
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
index 7bb6476a90..9057935ee9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTest.java
@@ -335,7 +335,7 @@ public class ServiceStateTest extends TestCase {
wwanDataRegState = new NetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- 0, 0, 0, true, null, null, "", 0, false, false, false, lteVopsSupportInfo, false);
+ 0, 0, 0, true, null, null, "", 0, false, false, false, lteVopsSupportInfo);
ss.addNetworkRegistrationInfo(wwanDataRegState);
assertEquals(ss.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN), wwanDataRegState);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index cc7c8e904f..98d5da31c2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -36,7 +36,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.IAlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -65,6 +64,9 @@ import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityNr;
+import android.telephony.CellIdentityTdscdma;
+import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.CellSignalStrength;
@@ -78,6 +80,7 @@ import android.telephony.INetworkService;
import android.telephony.LteVopsSupportInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
+import android.telephony.NrVopsSupportInfo;
import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -86,6 +89,7 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
@@ -95,6 +99,7 @@ import androidx.test.filters.FlakyTest;
import com.android.internal.R;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
+import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccRecords;
@@ -107,6 +112,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -119,8 +125,6 @@ public class ServiceStateTrackerTest extends TelephonyTest {
private ProxyController mProxyController;
@Mock
private Handler mTestHandler;
- @Mock
- protected IAlarmManager mAlarmManager;
private CellularNetworkService mCellularNetworkService;
@@ -132,6 +136,9 @@ public class ServiceStateTrackerTest extends TelephonyTest {
@Mock
private SubscriptionInfo mSubInfo;
+ @Mock
+ private ServiceStateStats mServiceStateStats;
+
private ServiceStateTracker sst;
private ServiceStateTrackerTestHandler mSSTTestHandler;
private PersistableBundle mBundle;
@@ -154,6 +161,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
private static final String CARRIER_NAME_DISPLAY_NO_SERVICE = "No service";
private static final String CARRIER_NAME_DISPLAY_EMERGENCY_CALL = "emergency call";
private static final String WIFI_CALLING_VOICE_FORMAT = "%s wifi calling";
+ private static final String CROSS_SIM_CALLING_VOICE_FORMAT = "%s Cross-SIM Calling";
private static final String WIFI_CALLING_DATA_FORMAT = "%s wifi data";
private static final String WIFI_CALLING_FLIGHT_MODE_FORMAT = "%s flight mode";
@@ -181,6 +189,8 @@ public class ServiceStateTrackerTest extends TelephonyTest {
@Override
public void onLooperPrepared() {
sst = new ServiceStateTracker(mPhone, mSimulatedCommands);
+ sst.setServiceStateStats(mServiceStateStats);
+ doReturn(sst).when(mPhone).getServiceStateTracker();
setReady(true);
}
}
@@ -229,7 +239,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
doReturn(mIwlanNetworkServiceStub).when(mIwlanNetworkServiceStub).asBinder();
addNetworkService();
- doReturn(true).when(mDcTracker).isDisconnected();
+ doReturn(true).when(mDcTracker).areAllDataDisconnected();
doReturn(new ServiceState()).when(mPhone).getServiceState();
@@ -314,10 +324,10 @@ public class ServiceStateTrackerTest extends TelephonyTest {
});
mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
new int[] {
- -16, /* SIGNAL_STRENGTH_POOR */
- -12, /* SIGNAL_STRENGTH_MODERATE */
- -9, /* SIGNAL_STRENGTH_GOOD */
- -6 /* SIGNAL_STRENGTH_GREAT */
+ -31, /* SIGNAL_STRENGTH_POOR */
+ -19, /* SIGNAL_STRENGTH_MODERATE */
+ -7, /* SIGNAL_STRENGTH_GOOD */
+ 6 /* SIGNAL_STRENGTH_GREAT */
});
mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
new int[] {
@@ -393,6 +403,42 @@ public class ServiceStateTrackerTest extends TelephonyTest {
@Test
@MediumTest
+ public void testSetRadioPowerForReason() {
+ // Radio does not turn on if off for other reason and not emergency call.
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
+ assertTrue(sst.getRadioPowerOffReasons().isEmpty());
+ sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
+ assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+ assertTrue(sst.getRadioPowerOffReasons().size() == 1);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
+ sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_USER);
+ assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+ assertTrue(sst.getRadioPowerOffReasons().size() == 1);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
+
+ // Radio power state reason is removed and radio turns on if turned on for same reason it
+ // had been turned off for.
+ sst.setRadioPowerForReason(true, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
+ assertTrue(sst.getRadioPowerOffReasons().isEmpty());
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
+
+ // Turn radio off, then successfully turn radio on for emergency call.
+ sst.setRadioPowerForReason(false, false, false, false, Phone.RADIO_POWER_REASON_THERMAL);
+ assertTrue(sst.getRadioPowerOffReasons().contains(Phone.RADIO_POWER_REASON_THERMAL));
+ assertTrue(sst.getRadioPowerOffReasons().size() == 1);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
+ sst.setRadioPower(true, true, true, false);
+ assertTrue(sst.getRadioPowerOffReasons().isEmpty());
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(mSimulatedCommands.getRadioState() == TelephonyManager.RADIO_POWER_ON);
+ }
+
+ @Test
+ @MediumTest
public void testSetRadioPowerFromCarrier() {
// Carrier disable radio power
sst.setRadioPowerFromCarrier(false);
@@ -790,7 +836,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
-20, /** csiRsrq NONE */
-23, /** CsiSinr NONE */
-44, /** SsRsrp SIGNAL_STRENGTH_GREAT */
- -20, /** SsRsrq NONE */
+ -32, /** SsRsrq NONE */
-23) /** SsSinr NONE */
);
@@ -1793,22 +1839,6 @@ public class ServiceStateTrackerTest extends TelephonyTest {
}
@Test
- @MediumTest
- public void testEnableLocationUpdates() throws Exception {
- sst.enableLocationUpdates();
- verify(mSimulatedCommandsVerifier, times(1)).setLocationUpdates(eq(true),
- any(Message.class));
- }
-
- @Test
- @SmallTest
- public void testDisableLocationUpdates() throws Exception {
- sst.disableLocationUpdates();
- verify(mSimulatedCommandsVerifier, times(1)).setLocationUpdates(eq(false),
- nullable(Message.class));
- }
-
- @Test
@SmallTest
public void testGetCurrentDataRegState() throws Exception {
sst.mSS.setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -1890,6 +1920,93 @@ public class ServiceStateTrackerTest extends TelephonyTest {
@Test
@SmallTest
+ public void testSetImsRegisteredStateRunsShutdownImmediately() throws Exception {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+ sst.setImsRegistrationState(true);
+ mSimulatedCommands.setRadioPowerFailResponse(false);
+ sst.setRadioPower(true);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+ sst.requestShutdown();
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ sst.setImsRegistrationState(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_UNAVAILABLE, mSimulatedCommands.getRadioState());
+ }
+
+ @Test
+ @SmallTest
+ public void testImsRegisteredDelayShutDown() throws Exception {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+ sst.setImsRegistrationState(true);
+ mSimulatedCommands.setRadioPowerFailResponse(false);
+ sst.setRadioPower(true);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ // Turn off the radio and ensure radio power is still on
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+ sst.setRadioPower(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+
+ // Now set IMS reg state to false and ensure we see the modem move to power off.
+ sst.setImsRegistrationState(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
+ }
+
+ @Test
+ @SmallTest
+ public void testImsRegisteredDelayShutDownTimeout() throws Exception {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+ sst.setImsRegistrationState(true);
+ mSimulatedCommands.setRadioPowerFailResponse(false);
+ sst.setRadioPower(true);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ // Turn off the radio and ensure radio power is still on
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+ sst.setRadioPower(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+
+ // Ensure that if we never turn deregister for IMS, we still eventually see radio state
+ // move to off.
+ // Timeout for IMS reg + some extra time to remove race conditions
+ waitForDelayedHandlerAction(mSSTTestHandler.getThreadHandler(),
+ ServiceStateTracker.DELAY_RADIO_OFF_FOR_IMS_DEREG_TIMEOUT + 100, 1000);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
+ }
+
+ @Test
+ @SmallTest
+ public void testImsRegisteredAPMOnOffToggle() throws Exception {
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+ sst.setImsRegistrationState(true);
+ mSimulatedCommands.setRadioPowerFailResponse(false);
+ sst.setRadioPower(true);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ // Turn off the radio and ensure radio power is still on and then turn it back on again
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+ sst.setRadioPower(false);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ sst.setRadioPower(true);
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+
+ // Ensure the timeout was cancelled and we still see radio power is on.
+ // Timeout for IMS reg + some extra time to remove race conditions
+ waitForDelayedHandlerAction(mSSTTestHandler.getThreadHandler(),
+ ServiceStateTracker.DELAY_RADIO_OFF_FOR_IMS_DEREG_TIMEOUT + 100, 1000);
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+ }
+
+ @Test
+ @SmallTest
public void testSetTimeFromNITZStr() throws Exception {
{
// Mock sending incorrect nitz str from RIL
@@ -1927,7 +2044,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
state, dataRat, 0, false, null, cid, getPlmnFromCellIdentity(cid),
- 1, false, false, false, lteVopsSupportInfo, false);
+ 1, false, false, false, lteVopsSupportInfo);
sst.mPollingContext[0] = 2;
// update data reg state to be in service
sst.sendMessage(sst.obtainMessage(
@@ -1955,7 +2072,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
state, dataRat, 0, false, null, cid, getPlmnFromCellIdentity(cid),
- 1, false, false, false, lteVopsSupportInfo, false);
+ 1, false, false, false, lteVopsSupportInfo);
sst.sendMessage(sst.obtainMessage(
ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
new AsyncResult(sst.mPollingContext, dataResult, null)));
@@ -1974,7 +2091,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
NetworkRegistrationInfo dataIwlanResult = new NetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN,
iwlanState, iwlanDataRat, 0, false,
- null, null, "", 1, false, false, false, lteVopsSupportInfo, false);
+ null, null, "", 1, false, false, false, lteVopsSupportInfo);
sst.sendMessage(sst.obtainMessage(
ServiceStateTracker.EVENT_POLL_STATE_PS_IWLAN_REGISTRATION,
new AsyncResult(sst.mPollingContext, dataIwlanResult, null)));
@@ -2001,6 +2118,64 @@ public class ServiceStateTrackerTest extends TelephonyTest {
assertEquals(null, sst.getServiceState().getOperatorAlpha());
}
+ @Test
+ public void testCSEmergencyRegistrationState() throws Exception {
+ CellIdentityGsm cellIdentity =
+ new CellIdentityGsm(0, 1, 900, 5, "001", "01", "test", "tst",
+ Collections.emptyList());
+
+ NetworkRegistrationInfo dataReg = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ 0, 16, 0, false, null, cellIdentity, getPlmnFromCellIdentity(cellIdentity),
+ 1, false, false, false, null);
+
+ NetworkRegistrationInfo voiceReg = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ 0, 16, 0, true, null, cellIdentity, getPlmnFromCellIdentity(cellIdentity),
+ false, 0, 0, 0);
+
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in oos
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataReg, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceReg, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(sst.mSS.isEmergencyOnly());
+ }
+
+ @Test
+ public void testPSEmergencyRegistrationState() throws Exception {
+ CellIdentityGsm cellIdentity =
+ new CellIdentityGsm(0, 1, 900, 5, "001", "01", "test", "tst",
+ Collections.emptyList());
+
+ NetworkRegistrationInfo dataReg = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ 0, 16, 0, true, null, cellIdentity, getPlmnFromCellIdentity(cellIdentity),
+ 1, false, false, false, null);
+
+ NetworkRegistrationInfo voiceReg = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ 0, 16, 0, false, null, cellIdentity, getPlmnFromCellIdentity(cellIdentity),
+ false, 0, 0, 0);
+
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in oos
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataReg, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceReg, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertTrue(sst.mSS.isEmergencyOnly());
+ }
+
// Edge and GPRS are grouped under the same family and Edge has higher rate than GPRS.
// Expect no rat update when move from E to G.
@Test
@@ -2040,37 +2215,15 @@ public class ServiceStateTrackerTest extends TelephonyTest {
}
- // TODO(nharold): This actually seems like broken behavior; rather than preserve it, we should
- // probably remove it.
- // GSM, Edge, GPRS are grouped under the same family where Edge > GPRS > GSM.
- // Expect no rat update from E to G immediately following cell id change.
- // Expect ratratchet (from G to E) for the following rat update within the cell location.
- @Test
- public void testRatRatchetWithCellChangeBeforeRatChange() throws Exception {
- // cell ID update
- CellIdentityGsm cellIdentity =
- new CellIdentityGsm(0, 1, 900, 5, "001", "01", "test", "tst",
- Collections.emptyList());
- changeRegState(1, cellIdentity, 16, 2);
- assertEquals(ServiceState.STATE_IN_SERVICE, sst.getCurrentDataConnectionState());
- assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilDataRadioTechnology());
-
- // RAT: EDGE -> GPRS, cell ID unchanged. Expect no rat ratchet following cell Id change.
- changeRegState(1, cellIdentity, 16, 1);
- assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_GPRS, sst.mSS.getRilDataRadioTechnology());
-
- // RAT: GPRS -> EDGE should ratchet.
- changeRegState(1, cellIdentity, 16, 2);
- assertEquals(ServiceState.RIL_RADIO_TECHNOLOGY_EDGE, sst.mSS.getRilDataRadioTechnology());
- }
-
- private void sendPhyChanConfigChange(int[] bandwidths) {
+ private void sendPhyChanConfigChange(int[] bandwidths, int networkType, int pci) {
ArrayList<PhysicalChannelConfig> pc = new ArrayList<>();
int ssType = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
for (int bw : bandwidths) {
pc.add(new PhysicalChannelConfig.Builder()
.setCellConnectionStatus(ssType)
.setCellBandwidthDownlinkKhz(bw)
+ .setNetworkType(networkType)
+ .setPhysicalCellId(pci)
.build());
// All cells after the first are secondary serving cells.
@@ -2089,7 +2242,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, TelephonyManager.NETWORK_TYPE_LTE,
0, false, null, cellId, getPlmnFromCellIdentity(cellId), 1, false, false, false,
- lteVopsSupportInfo, false);
+ lteVopsSupportInfo);
NetworkRegistrationInfo voiceResult = new NetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, TelephonyManager.NETWORK_TYPE_LTE,
@@ -2106,6 +2259,31 @@ public class ServiceStateTrackerTest extends TelephonyTest {
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
}
+ private void sendRegStateUpdateForNrCellId(CellIdentityNr cellId) {
+ LteVopsSupportInfo lteVopsSupportInfo =
+ new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+ LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
+ NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, TelephonyManager.NETWORK_TYPE_NR,
+ 0, false, null, cellId, getPlmnFromCellIdentity(cellId), 1, false, false, false,
+ lteVopsSupportInfo);
+ NetworkRegistrationInfo voiceResult = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, TelephonyManager.NETWORK_TYPE_NR,
+ 0, false, null, cellId, getPlmnFromCellIdentity(cellId), false, 0, 0, 0);
+ sst.mPollingContext[0] = 2;
+ // update data reg state to be in service
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ }
+
@Test
public void testPhyChanBandwidthUpdatedOnDataRegState() throws Exception {
// Cell ID change should trigger hasLocationChanged.
@@ -2113,7 +2291,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
"tst", Collections.emptyList(), null);
- sendPhyChanConfigChange(new int[] {10000});
+ sendPhyChanConfigChange(new int[] {10000}, TelephonyManager.NETWORK_TYPE_LTE, 1);
sendRegStateUpdateForLteCellId(cellIdentity5);
assertTrue(Arrays.equals(new int[] {5000}, sst.mSS.getCellBandwidths()));
}
@@ -2125,7 +2303,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 12345, "001", "01", "test",
"tst", Collections.emptyList(), null);
- sendPhyChanConfigChange(new int[] {10000});
+ sendPhyChanConfigChange(new int[] {10000}, TelephonyManager.NETWORK_TYPE_LTE, 1);
sendRegStateUpdateForLteCellId(cellIdentityInv);
assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
}
@@ -2137,7 +2315,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 10000, "001", "01", "test",
"tst", Collections.emptyList(), null);
- sendPhyChanConfigChange(new int[] {10000, 5000});
+ sendPhyChanConfigChange(new int[] {10000, 5000}, TelephonyManager.NETWORK_TYPE_LTE, 1);
sendRegStateUpdateForLteCellId(cellIdentity10);
assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
}
@@ -2151,7 +2329,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
sendRegStateUpdateForLteCellId(cellIdentity10);
assertTrue(Arrays.equals(new int[] {10000}, sst.mSS.getCellBandwidths()));
- sendPhyChanConfigChange(new int[] {10000, 5000});
+ sendPhyChanConfigChange(new int[] {10000, 5000}, TelephonyManager.NETWORK_TYPE_LTE, 1);
assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
}
@@ -2165,7 +2343,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
TelephonyManager.NETWORK_TYPE_UNKNOWN, 0, false, null, null, "", 1, false, false,
- false, lteVopsSupportInfo, false);
+ false, lteVopsSupportInfo);
NetworkRegistrationInfo voiceResult = new NetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
@@ -2182,6 +2360,17 @@ public class ServiceStateTrackerTest extends TelephonyTest {
assertTrue(Arrays.equals(new int[0], sst.mSS.getCellBandwidths()));
}
+ @Test
+ public void testPhyChanBandwidthForNr() {
+ // NR Cell with bandwidth = 10000
+ CellIdentityNr nrCi = new CellIdentityNr(
+ 0, 0, 0, new int[] {}, "", "", 5, "", "", Collections.emptyList());
+
+ sendPhyChanConfigChange(new int[] {10000, 5000}, TelephonyManager.NETWORK_TYPE_NR, 0);
+ sendRegStateUpdateForNrCellId(nrCi);
+ assertTrue(Arrays.equals(new int[] {10000, 5000}, sst.mSS.getCellBandwidths()));
+ }
+
/**
* Ensure that TransportManager changes due to transport preference changes are picked up in the
* new ServiceState when a poll event occurs. This causes ServiceState#getRilDataRadioTechnology
@@ -2343,7 +2532,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
@Test
@SmallTest
- public void testOnVopsInfoChanged() {
+ public void testOnLteVopsInfoChanged() {
ServiceState ss = new ServiceState();
ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
ss.setDataRegState(ServiceState.STATE_IN_SERVICE);
@@ -2359,7 +2548,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME, TelephonyManager.NETWORK_TYPE_LTE,
- 0, false, null, cellId, "00101", 1, false, false, false, lteVopsSupportInfo, false);
+ 0, false, null, cellId, "00101", 1, false, false, false, lteVopsSupportInfo);
sst.mPollingContext[0] = 2;
sst.sendMessage(sst.obtainMessage(
@@ -2389,7 +2578,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
TelephonyManager.NETWORK_TYPE_LTE, 0, false, null, cellId, "00101",
- 1, false, false, false, lteVopsSupportInfo, false);
+ 1, false, false, false, lteVopsSupportInfo);
sst.mPollingContext[0] = 1;
sst.sendMessage(sst.obtainMessage(
ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
@@ -2404,6 +2593,70 @@ public class ServiceStateTrackerTest extends TelephonyTest {
@Test
@SmallTest
+ public void testOnNrVopsInfoChanged() {
+ ServiceState ss = new ServiceState();
+ ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+ ss.setDataRegState(ServiceState.STATE_IN_SERVICE);
+ sst.mSS = ss;
+
+ CellIdentityLte cellId =
+ new CellIdentityLte(1, 1, 5, 1, new int[] {1, 2}, 5000, "001", "01", "test",
+ "tst", Collections.emptyList(), null);
+ NrVopsSupportInfo nrVopsSupportInfo = new NrVopsSupportInfo(
+ NrVopsSupportInfo.NR_STATUS_VOPS_NOT_SUPPORTED,
+ NrVopsSupportInfo.NR_STATUS_EMC_NOT_SUPPORTED,
+ NrVopsSupportInfo.NR_STATUS_EMF_NOT_SUPPORTED);
+
+ NetworkRegistrationInfo dataResult = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME, TelephonyManager.NETWORK_TYPE_NR,
+ 0, false, null, cellId, "00101", 1, false, false, false, nrVopsSupportInfo);
+ sst.mPollingContext[0] = 2;
+
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ NetworkRegistrationInfo voiceResult = new NetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+ TelephonyManager.NETWORK_TYPE_NR, 0,
+ false, null, cellId, "00101", false, 0, 0, 0);
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, voiceResult, null)));
+
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+ assertEquals(ServiceState.STATE_IN_SERVICE, sst.getCurrentDataConnectionState());
+ NetworkRegistrationInfo sSnetworkRegistrationInfo =
+ sst.mSS.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ assertEquals(nrVopsSupportInfo,
+ sSnetworkRegistrationInfo.getDataSpecificInfo().getVopsSupportInfo());
+
+ nrVopsSupportInfo = new NrVopsSupportInfo(
+ NrVopsSupportInfo.NR_STATUS_VOPS_3GPP_SUPPORTED,
+ NrVopsSupportInfo.NR_STATUS_EMC_5GCN_ONLY,
+ NrVopsSupportInfo.NR_STATUS_EMF_5GCN_ONLY);
+ dataResult = new NetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+ TelephonyManager.NETWORK_TYPE_NR, 0, false, null, cellId, "00101",
+ 1, false, false, false, nrVopsSupportInfo);
+ sst.mPollingContext[0] = 1;
+ sst.sendMessage(sst.obtainMessage(
+ ServiceStateTracker.EVENT_POLL_STATE_PS_CELLULAR_REGISTRATION,
+ new AsyncResult(sst.mPollingContext, dataResult, null)));
+ waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+ sSnetworkRegistrationInfo =
+ sst.mSS.getNetworkRegistrationInfo(2, 1);
+ assertEquals(nrVopsSupportInfo,
+ sSnetworkRegistrationInfo.getDataSpecificInfo().getVopsSupportInfo());
+ }
+
+
+ @Test
+ @SmallTest
public void testEriLoading() {
sst.obtainMessage(GsmCdmaPhone.EVENT_CARRIER_CONFIG_CHANGED, null).sendToTarget();
waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
@@ -2420,7 +2673,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
ss.setVoiceRegState(ServiceState.STATE_EMERGENCY_ONLY);
ss.setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
ss.setEmergencyOnly(true);
- doReturn(ss).when(mSST).getServiceState();
+ sst.mSS = ss;
// update the spn
sst.updateSpnDisplay();
@@ -2442,7 +2695,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
ss.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
ss.setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
ss.setEmergencyOnly(false);
- doReturn(ss).when(mSST).getServiceState();
+ sst.mSS = ss;
// update the spn
sst.updateSpnDisplay();
@@ -2455,7 +2708,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
}
@Test
- public void testUpdateSpnDisplay_flightMode_displayOOS() {
+ public void testUpdateSpnDisplay_flightMode_displayNull() {
// GSM phone
doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -2463,19 +2716,80 @@ public class ServiceStateTrackerTest extends TelephonyTest {
ServiceState ss = new ServiceState();
ss.setVoiceRegState(ServiceState.STATE_POWER_OFF);
ss.setDataRegState(ServiceState.STATE_POWER_OFF);
- doReturn(ss).when(mSST).getServiceState();
+ sst.mSS = ss;
// update the spn
sst.updateSpnDisplay();
- // Plmn should be shown, and the string is "No service"
+ // Plmn should be shown, and the string is null
Bundle b = getExtrasFromLastSpnUpdateIntent();
- assertThat(b.getString(TelephonyManager.EXTRA_PLMN))
- .isEqualTo(CARRIER_NAME_DISPLAY_NO_SERVICE);
+ assertThat(b.getString(TelephonyManager.EXTRA_PLMN)).isEqualTo(null);
assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_PLMN)).isTrue();
}
@Test
+ public void testUpdateSpnDisplay_flightModeNoWifiCalling_showSpnAndPlmn() {
+ // GSM phone
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+ // Flight mode and connected to WiFI
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ sst.mSS = mServiceState;
+
+ // wifi-calling is disable
+ doReturn(false).when(mPhone).isWifiCallingEnabled();
+
+ // update the spn
+ sst.updateSpnDisplay();
+
+ // Show both spn & plmn
+ String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+ String plmn = mBundle.getStringArray(CarrierConfigManager.KEY_PNN_OVERRIDE_STRING_ARRAY)[0];
+ plmn = plmn.split("\\s*,\\s*")[0];
+ Bundle b = getExtrasFromLastSpnUpdateIntent();
+ assertThat(b.getString(TelephonyManager.EXTRA_SPN)).isEqualTo(spn);
+ assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_SPN)).isTrue();
+ assertThat(b.getString(TelephonyManager.EXTRA_PLMN)).isEqualTo(plmn);
+ assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_PLMN)).isTrue();
+ }
+
+ @Test
+ public void testUpdateSpnDisplay_spnNotEmptyAndCrossSimCallingEnabled_showSpnOnly() {
+ // GSM phone
+
+ doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+ // In Service
+ ServiceState ss = new ServiceState();
+ ss.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+ ss.setDataRegState(ServiceState.STATE_IN_SERVICE);
+ sst.mSS = ss;
+
+ // cross-sim-calling is enable
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).when(mImsPhone)
+ .getImsRegistrationTech();
+ String[] formats = {CROSS_SIM_CALLING_VOICE_FORMAT, "%s"};
+ Resources r = mContext.getResources();
+ doReturn(formats).when(r).getStringArray(anyInt());
+
+ // update the spn
+ sst.updateSpnDisplay();
+
+ // Only spn should be shown
+ String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
+ Bundle b = getExtrasFromLastSpnUpdateIntent();
+ assertThat(b.getString(TelephonyManager.EXTRA_SPN))
+ .isEqualTo(String.format(CROSS_SIM_CALLING_VOICE_FORMAT, spn));
+ assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_SPN)).isTrue();
+ assertThat(b.getString(TelephonyManager.EXTRA_DATA_SPN))
+ .isEqualTo(String.format(CROSS_SIM_CALLING_VOICE_FORMAT, spn));
+ assertThat(b.getBoolean(TelephonyManager.EXTRA_SHOW_PLMN)).isFalse();
+ }
+
+ @Test
public void testUpdateSpnDisplay_spnNotEmptyAndWifiCallingEnabled_showSpnOnly() {
// GSM phone
doReturn(true).when(mPhone).isPhoneTypeGsm();
@@ -2564,7 +2878,7 @@ public class ServiceStateTrackerTest extends TelephonyTest {
@Test
public void testShouldForceDisplayNoService_forceBasedOnLocale() {
// set up unaffected locale (US) and clear the resource
- doReturn("us").when(mLocaleTracker).getCurrentCountry();
+ doReturn("us").when(mLocaleTracker).getLastKnownCountryIso();
mContextFixture.putStringArrayResource(
com.android.internal.R.array.config_display_no_service_when_sim_unready,
new String[0]);
@@ -2574,11 +2888,11 @@ public class ServiceStateTrackerTest extends TelephonyTest {
mContextFixture.putStringArrayResource(
com.android.internal.R.array.config_display_no_service_when_sim_unready,
new String[]{"de"});
- doReturn("us").when(mLocaleTracker).getCurrentCountry();
+ doReturn("us").when(mLocaleTracker).getLastKnownCountryIso();
assertFalse(sst.shouldForceDisplayNoService());
// mock the locale to Germany
- doReturn("de").when(mLocaleTracker).getCurrentCountry();
+ doReturn("de").when(mLocaleTracker).getLastKnownCountryIso();
assertTrue(sst.shouldForceDisplayNoService());
}
@@ -2692,4 +3006,69 @@ public class ServiceStateTrackerTest extends TelephonyTest {
assertEquals(cids.get(0), cellIdentityLte);
assertEquals(cids.get(1), cellIdentityGsm);
}
+
+ @Test
+ public void testGetCidFromCellIdentity() throws Exception {
+ CellIdentity gsmCi = new CellIdentityGsm(
+ 0, 1, 0, 0, "", "", "", "", Collections.emptyList());
+ CellIdentity wcdmaCi = new CellIdentityWcdma(
+ 0, 2, 0, 0, "", "", "", "", Collections.emptyList(), null);
+ CellIdentity tdscdmaCi = new CellIdentityTdscdma(
+ "", "", 0, 3, 0, 0, "", "", Collections.emptyList(), null);
+ CellIdentity lteCi = new CellIdentityLte(0, 0, 4, 0, 0);
+ CellIdentity nrCi = new CellIdentityNr(
+ 0, 0, 0, new int[] {}, "", "", 5, "", "", Collections.emptyList());
+
+ Method method = ServiceStateTracker.class.getDeclaredMethod(
+ "getCidFromCellIdentity", CellIdentity.class);
+ method.setAccessible(true);
+ assertEquals(1, (long) method.invoke(mSST, gsmCi));
+ assertEquals(2, (long) method.invoke(mSST, wcdmaCi));
+ assertEquals(3, (long) method.invoke(mSST, tdscdmaCi));
+ assertEquals(4, (long) method.invoke(mSST, lteCi));
+ assertEquals(5, (long) method.invoke(mSST, nrCi));
+ }
+
+ @Test
+ public void testGetCombinedRegState() {
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+
+ // If voice/data out of service, return out of service.
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegistrationState();
+ assertEquals(ServiceState.STATE_OUT_OF_SERVICE, sst.getCombinedRegState(mServiceState));
+
+ // If voice is emergency only, return emergency only
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getState();
+ assertEquals(ServiceState.STATE_EMERGENCY_ONLY, sst.getCombinedRegState(mServiceState));
+
+ // If data in service, return in service.
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getDataRegistrationState();
+ assertEquals(ServiceState.STATE_IN_SERVICE, sst.getCombinedRegState(mServiceState));
+
+ // Check emergency
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getState();
+ assertEquals(ServiceState.STATE_EMERGENCY_ONLY, sst.getCombinedRegState(mServiceState));
+
+ // If data in service and network is IWLAN but WiFi calling is off, return out of service.
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ doReturn(false).when(mImsPhone).isWifiCallingEnabled();
+ assertEquals(ServiceState.STATE_OUT_OF_SERVICE, sst.getCombinedRegState(mServiceState));
+
+ // Check emrgency
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getState();
+ assertEquals(ServiceState.STATE_EMERGENCY_ONLY, sst.getCombinedRegState(mServiceState));
+
+ // If data in service and network is IWLAN and WiFi calling is on, return in service.
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ doReturn(true).when(mImsPhone).isWifiCallingEnabled();
+ assertEquals(ServiceState.STATE_IN_SERVICE, sst.getCombinedRegState(mServiceState));
+
+ // Check emergency
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getState();
+ assertEquals(ServiceState.STATE_EMERGENCY_ONLY, sst.getCombinedRegState(mServiceState));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
index 589d6b61a3..f15845cd42 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
@@ -38,6 +38,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/** Unit tests for {@link IpSecConfig}. */
@@ -88,13 +90,15 @@ public class SignalStrengthTest {
public void testParcelUnparcel() throws Exception {
assertParcelingIsLossless(new SignalStrength());
+ ArrayList<Byte> NrCqiReport = new ArrayList<>(
+ Arrays.asList((byte) 3, (byte) 2 , (byte) 1));
SignalStrength s = new SignalStrength(
new CellSignalStrengthCdma(-93, -132, -89, -125, 5),
new CellSignalStrengthGsm(-79, 2, 5),
new CellSignalStrengthWcdma(-94, 4, -102, -5),
new CellSignalStrengthTdscdma(-95, 2, -103),
- new CellSignalStrengthLte(-85, -91, -6, -10, 12, 1),
- new CellSignalStrengthNr(-91, -6, 3, -80, -7, 4));
+ new CellSignalStrengthLte(-85, -91, -6, -10, 1, 12, 1),
+ new CellSignalStrengthNr(-91, -6, 3, 1, NrCqiReport, -80, -7, 4));
assertParcelingIsLossless(s);
PersistableBundle bundle = new PersistableBundle();
@@ -125,7 +129,7 @@ public class SignalStrengthTest {
@Test
public void testGetCellSignalStrengths() throws Exception {
- CellSignalStrengthLte lte = new CellSignalStrengthLte(-85, -91, -6, -10, 12, 1);
+ CellSignalStrengthLte lte = new CellSignalStrengthLte(-85, -91, -6, -10, 1, 12, 1);
CellSignalStrengthGsm gsm = new CellSignalStrengthGsm(-79, 2, 5);
CellSignalStrengthCdma cdma = new CellSignalStrengthCdma(-93, -132, -89, -125, 5);
CellSignalStrengthWcdma wcdma = new CellSignalStrengthWcdma(-94, 4, -102, -5);
@@ -166,6 +170,7 @@ public class SignalStrengthTest {
lteRsrp, // rsrp
lteRsrq, // rsrq
-25, // rssnr
+ CellInfo.UNAVAILABLE, // cqiTableIndex
CellInfo.UNAVAILABLE, // cqi
CellInfo.UNAVAILABLE); // timingAdvance
@@ -198,6 +203,7 @@ public class SignalStrengthTest {
lteRsrp, // rsrp
15, // rsrq
lteRssnr, // rssnr
+ CellInfo.UNAVAILABLE, // cqiTableIndex
CellInfo.UNAVAILABLE, // cqi
CellInfo.UNAVAILABLE); // timingAdvance
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthUpdateRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthUpdateRequestTest.java
new file mode 100644
index 0000000000..9c29eda557
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthUpdateRequestTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.SignalStrengthUpdateRequest;
+import android.telephony.SignalThresholdInfo;
+
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.List;
+
+public class SignalStrengthUpdateRequestTest extends TestCase {
+
+ private SignalThresholdInfo mRssiInfo = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(new int[]{-109, -103, -97, -89})
+ .build();
+
+ private SignalThresholdInfo mRscpInfo = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setThresholds(new int[]{-115, -105, -95, -85})
+ .build();
+
+ @Test
+ @SmallTest
+ public void testPublicConstructorWithInvalidParam() {
+ // null Collection
+ validateBuilderWithInvalidParam(null);
+
+ // duplication of SignalMeasurementType in Collection
+ validateBuilderWithInvalidParam(List.of(mRssiInfo, mRssiInfo));
+
+ // The following two cases can not turn on until the implement is ready:
+ // empty Collections
+ // validateBuilderWithInvalidParam(List.of());
+ }
+
+ @Test
+ @SmallTest
+ public void testPublicConstructorWithValidParam() {
+ Collection<SignalThresholdInfo> infos = List.of(mRssiInfo, mRscpInfo);
+ SignalStrengthUpdateRequest request = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos).setReportingRequestedWhileIdle(false).build();
+ assertFalse(request.isReportingRequestedWhileIdle());
+ assertFalse(request.isSystemThresholdReportingRequestedWhileIdle());
+ assertEquals(infos, request.getSignalThresholdInfos());
+ }
+
+ @Test
+ @SmallTest
+ public void testParcel() {
+ Collection<SignalThresholdInfo> infos = List.of(mRssiInfo, mRscpInfo);
+ SignalStrengthUpdateRequest request = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos).setReportingRequestedWhileIdle(true).build();
+
+ Parcel p = Parcel.obtain();
+ request.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ SignalStrengthUpdateRequest newRequest =
+ SignalStrengthUpdateRequest.CREATOR.createFromParcel(p);
+ assertThat(newRequest).isEqualTo(request);
+ }
+
+ @Test
+ @SmallTest
+ public void testEquals() {
+ Collection<SignalThresholdInfo> infos1 = List.of(mRssiInfo, mRscpInfo);
+ SignalStrengthUpdateRequest request1 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos1).setReportingRequestedWhileIdle(false).build();
+
+ assertTrue(request1.equals(request1));
+
+ // Ordering does not matter
+ Collection<SignalThresholdInfo> infos2 = List.of(mRscpInfo, mRssiInfo);
+ SignalStrengthUpdateRequest request2 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos2).setReportingRequestedWhileIdle(false).build();
+ assertTrue(request1.equals(request2));
+
+ SignalStrengthUpdateRequest request3 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos1).setReportingRequestedWhileIdle(true).build();
+ assertFalse(request1.equals(request3));
+
+ SignalStrengthUpdateRequest request4 = new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos1).setReportingRequestedWhileIdle(false)
+ .setSystemThresholdReportingRequestedWhileIdle(true).build();
+
+ // return false if the object is not SignalStrengthUpdateRequest
+ assertFalse(request1.equals("test"));
+ }
+
+ private void validateBuilderWithInvalidParam(Collection<SignalThresholdInfo> infos) {
+ try {
+ new SignalStrengthUpdateRequest.Builder()
+ .setSignalThresholdInfos(infos).setReportingRequestedWhileIdle(false).build();
+ fail("Exception expected");
+ } catch (IllegalArgumentException | NullPointerException expected) {
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
index ddf5bcdaea..b282c55cbf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.telephony.AccessNetworkConstants;
import android.telephony.SignalThresholdInfo;
import androidx.test.filters.SmallTest;
@@ -33,32 +34,94 @@ import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
@RunWith(JUnit4.class)
public class SignalThresholdInfoTest extends TestCase {
private static final int HYSTERESIS_DB = 2;
private static final int HYSTERESIS_MS = 30;
- private static final int[] SSRSRP_THRESHOLDS = new int[] {-30, 10, 45, 130};
+ private static final int[] SSRSRP_THRESHOLDS = new int[]{-120, -100, -80, -60};
- private final int[] mRssiThresholds = new int[] {-109, -103, -97, -89};
- private final int[] mRscpThresholds = new int[] {-115, -105, -95, -85};
- private final int[] mRsrpThresholds = new int[] {-128, -118, -108, -98};
- private final int[] mRsrqThresholds = new int[] {-19, -17, -14, -12};
- private final int[] mRssnrThresholds = new int[] {-30, 10, 45, 130};
- private final int[][] mThresholds = new int[5][];
+ // Map of SignalMeasurementType to invalid thresholds edge values.
+ // Each invalid value will be constructed with a thresholds array to test separately.
+ private static final Map<Integer, List<Integer>> INVALID_THRESHOLDS_MAP = Map.of(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ List.of(SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
+ List.of(SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ List.of(SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ List.of(SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
+ List.of(SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ List.of(SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ List.of(SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
+ List.of(SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE + 1)
+ );
+
+ // Map of RAN to allowed SignalMeasurementType set.
+ // RAN/TYPE pair will be used to verify the validation of the combo
+ private static final Map<Integer, Set<Integer>> VALID_RAN_TO_MEASUREMENT_TYPE_MAP = Map.of(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP),
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR),
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ );
+
+ // Deliberately picking up the max/min value in each range to test the edge cases
+ private final int[] mRssiThresholds = new int[]{-113, -103, -97, -51};
+ private final int[] mRscpThresholds = new int[]{-120, -105, -95, -25};
+ private final int[] mRsrpThresholds = new int[]{-140, -118, -108, -44};
+ private final int[] mRsrqThresholds = new int[]{-34, -17, -14, 3};
+ private final int[] mRssnrThresholds = new int[]{-20, 10, 20, 30};
+ private final int[] mSsrsrpThresholds = new int[]{-140, -118, -98, -44};
+ private final int[] mSsrsrqThresholds = new int[]{-43, -17, -14, 20};
+ private final int[] mSssinrThresholds = new int[]{-23, -16, -10, 40};
+
+ private final int[][] mThresholds = {mRssiThresholds, mRscpThresholds, mRsrpThresholds,
+ mRsrqThresholds, mRssnrThresholds, mSsrsrpThresholds, mSsrsrqThresholds,
+ mSssinrThresholds};
@Test
@SmallTest
public void testSignalThresholdInfo() throws Exception {
- SignalThresholdInfo signalThresholdInfo = new SignalThresholdInfo(
- SignalThresholdInfo.SIGNAL_SSRSRP,
- HYSTERESIS_MS,
- HYSTERESIS_DB,
- SSRSRP_THRESHOLDS,
- false);
-
- assertEquals(SignalThresholdInfo.SIGNAL_SSRSRP,
- signalThresholdInfo.getSignalMeasurement());
+ SignalThresholdInfo signalThresholdInfo =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(SSRSRP_THRESHOLDS)
+ .setIsEnabled(false)
+ .build();
+
+ assertEquals(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ signalThresholdInfo.getSignalMeasurementType());
assertEquals(HYSTERESIS_MS, signalThresholdInfo.getHysteresisMs());
assertEquals(HYSTERESIS_DB, signalThresholdInfo.getHysteresisDb());
assertEquals(Arrays.toString(SSRSRP_THRESHOLDS), Arrays.toString(
@@ -68,9 +131,8 @@ public class SignalThresholdInfoTest extends TestCase {
@Test
@SmallTest
- public void testDefaultThresholdsConstruction() {
- setThresholds();
- ArrayList<SignalThresholdInfo> stList = setSignalThresholdInfoConstructor();
+ public void testBuilderWithAllFields() {
+ ArrayList<SignalThresholdInfo> stList = buildSignalThresholdInfoWithAllFields();
int count = 0;
for (SignalThresholdInfo st : stList) {
@@ -82,7 +144,7 @@ public class SignalThresholdInfoTest extends TestCase {
@Test
@SmallTest
public void testDefaultThresholdsParcel() {
- ArrayList<SignalThresholdInfo> stList = setSignalThresholdInfoConstructor();
+ ArrayList<SignalThresholdInfo> stList = buildSignalThresholdInfoWithAllFields();
for (SignalThresholdInfo st : stList) {
Parcel p = Parcel.obtain();
@@ -98,58 +160,275 @@ public class SignalThresholdInfoTest extends TestCase {
@SmallTest
public void testGetSignalThresholdInfo() {
ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
- stList.add(new SignalThresholdInfo(0, 0, 0, null, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSSI, HYSTERESIS_MS,
- HYSTERESIS_DB, mRssiThresholds, false));
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(0)
+ .setHysteresisDb(0)
+ .setThresholds(new int[]{}, true /*isSystem*/)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds)
+ .setIsEnabled(false)
+ .build());
- assertThat(stList.get(0).getThresholds()).isEqualTo(null);
- assertThat(stList.get(1).getSignalMeasurement()).isEqualTo(SignalThresholdInfo.SIGNAL_RSSI);
+ assertThat(stList.get(0).getThresholds()).isEqualTo(new int[]{});
+ assertThat(stList.get(1).getSignalMeasurementType()).isEqualTo(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI);
assertThat(stList.get(1).getThresholds()).isEqualTo(mRssiThresholds);
}
@Test
@SmallTest
public void testEqualsSignalThresholdInfo() {
- final int[] dummyThresholds = new int[] {-100, -1, 1, 100};
- SignalThresholdInfo st1 = new SignalThresholdInfo(1, HYSTERESIS_MS, HYSTERESIS_DB,
- mRssiThresholds, false);
- SignalThresholdInfo st2 = new SignalThresholdInfo(2, HYSTERESIS_MS, HYSTERESIS_DB,
- mRssiThresholds, false);
- SignalThresholdInfo st3 = new SignalThresholdInfo(1, HYSTERESIS_MS, HYSTERESIS_DB,
- dummyThresholds, false);
- SignalThresholdInfo st4 = new SignalThresholdInfo(1, HYSTERESIS_MS, HYSTERESIS_DB,
- mRssiThresholds, false);
+ final int[] dummyThresholds = new int[]{-100, -90, -70, -60};
+ final int[] dummyThreholdsDisordered = new int[]{-60, -90, -100, -70};
+ SignalThresholdInfo st1 = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(1).setSignalMeasurementType(1)
+ .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st2 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(2)
+ .setSignalMeasurementType(2).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st3 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st4 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st5 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThreholdsDisordered)
+ .setIsEnabled(false).build();
//Return true if all SignalThresholdInfo values match.
assertTrue(st1.equals(st1));
assertFalse(st1.equals(st2));
assertFalse(st1.equals(st3));
assertTrue(st1.equals(st4));
+ //Threshold values ordering doesn't matter
+ assertTrue(st3.equals(st5));
//Return false if the object of argument is other than SignalThresholdInfo.
assertFalse(st1.equals(new String("test")));
}
- private void setThresholds() {
- mThresholds[0] = mRssiThresholds;
- mThresholds[1] = mRscpThresholds;
- mThresholds[2] = mRsrpThresholds;
- mThresholds[3] = mRsrqThresholds;
- mThresholds[4] = mRssnrThresholds;
+ @Test
+ @SmallTest
+ public void testBuilderWithValidParameters() {
+ ArrayList<SignalThresholdInfo> stList = buildSignalThresholdInfoWithPublicFields();
+
+ for (int i = 0; i < stList.size(); i++) {
+ SignalThresholdInfo st = stList.get(i);
+ assertThat(st.getThresholds()).isEqualTo(mThresholds[i]);
+ assertThat(st.getHysteresisMs()).isEqualTo(SignalThresholdInfo.HYSTERESIS_MS_DISABLED);
+ assertThat(st.getHysteresisDb()).isEqualTo(SignalThresholdInfo.HYSTERESIS_DB_DISABLED);
+ assertFalse(st.isEnabled());
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testBuilderWithInvalidParameter() {
+ // Invalid signal measurement type
+ int[] invalidSignalMeasurementTypes = new int[]{-1, 0, 9};
+ for (int signalMeasurementType : invalidSignalMeasurementTypes) {
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN, signalMeasurementType,
+ new int[]{-1});
+ }
+
+ // Null thresholds array
+ buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, null);
+
+ // Empty thresholds
+ buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, new int[]{});
+
+
+ // Too long thresholds array
+ buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ new int[]{-100, -90, -70, -60, -58});
+
+ // Thresholds value out of range
+ for (int signalMeasurementType : INVALID_THRESHOLDS_MAP.keySet()) {
+ List<Integer> invalidThresholds = INVALID_THRESHOLDS_MAP.get(signalMeasurementType);
+ for (int threshold : invalidThresholds) {
+ buildWithInvalidParameterThrowException(getValidRan(signalMeasurementType),
+ signalMeasurementType, new int[]{threshold});
+ }
+ }
+
+ // Invalid RAN/Measurement type combos
+ for (int ran : VALID_RAN_TO_MEASUREMENT_TYPE_MAP.keySet()) {
+ Set validTypes = VALID_RAN_TO_MEASUREMENT_TYPE_MAP.get(ran);
+ for (int type = SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
+ type <= SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR; type++) {
+ if (!validTypes.contains(type)) {
+ buildWithInvalidParameterThrowException(ran, type, new int[]{-1});
+ }
+ }
+ }
+ }
+
+ private void buildWithInvalidParameterThrowException(int ran, int signalMeasurementType,
+ int[] thresholds) {
+ try {
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(ran)
+ .setSignalMeasurementType(signalMeasurementType)
+ .setThresholds(thresholds)
+ .build();
+ fail("exception expected");
+ } catch (IllegalArgumentException | NullPointerException expected) {
+ }
}
- private ArrayList<SignalThresholdInfo> setSignalThresholdInfoConstructor() {
+ private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithAllFields() {
ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSSI, HYSTERESIS_MS,
- HYSTERESIS_DB, mRssiThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSCP, HYSTERESIS_MS,
- HYSTERESIS_DB, mRscpThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSRP, HYSTERESIS_MS,
- HYSTERESIS_DB, mRsrpThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSRQ, HYSTERESIS_MS,
- HYSTERESIS_DB, mRsrqThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSSNR, HYSTERESIS_MS,
- HYSTERESIS_DB, mRssnrThresholds, false));
+
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRscpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRsrpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRsrqThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssnrThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSsrsrpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSsrsrqThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSssinrThresholds)
+ .setIsEnabled(false)
+ .build());
return stList;
}
+
+ private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithPublicFields() {
+ ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
+
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(mRssiThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setThresholds(mRscpThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setThresholds(mRsrpThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+ .setThresholds(mRsrqThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+ .setThresholds(mRssnrThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setThresholds(mSsrsrpThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+ .setThresholds(mSsrsrqThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ .setThresholds(mSssinrThresholds)
+ .build());
+
+ return stList;
+ }
+
+ /**
+ * Return a possible valid RAN value for the measurement type. This is used to prevent the
+ * invalid ran/type causing IllegalArgumentException when testing other invalid input cases.
+ */
+ private static int getValidRan(@SignalThresholdInfo.SignalMeasurementType int type) {
+ switch (type) {
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI:
+ return AccessNetworkConstants.AccessNetworkType.GERAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP:
+ return AccessNetworkConstants.AccessNetworkType.UTRAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR:
+ return AccessNetworkConstants.AccessNetworkType.EUTRAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR:
+ return AccessNetworkConstants.AccessNetworkType.NGRAN;
+ default:
+ return AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java
index 15e16fbcbf..ea6f19ddae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java
@@ -16,9 +16,12 @@
package com.android.internal.telephony;
+import android.content.ContentValues;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.test.suitebuilder.annotation.Suppress;
+import com.android.internal.telephony.IccProvider;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.IccConstants;
@@ -38,15 +41,17 @@ public class SimPhoneBookTest extends TestCase {
.get());
assertNotNull(simPhoneBook);
- int size[] = simPhoneBook.getAdnRecordsSize(IccConstants.EF_ADN);
+ final int subId = SubscriptionManager.getDefaultSubscriptionId();
+ int size[] = simPhoneBook.getAdnRecordsSizeForSubscriber(subId, IccConstants.EF_ADN);
assertNotNull(size);
assertEquals(3, size.length);
assertEquals(size[0] * size[2], size[1]);
assertTrue(size[2] >= 100);
- List<AdnRecord> adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ List<AdnRecord> adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
// do it twice cause the second time shall read from cache only
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ adnRecordList = simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
assertNotNull(adnRecordList);
// Test for phone book update
@@ -75,37 +80,66 @@ public class SimPhoneBookTest extends TestCase {
String pin2 = null;
// udpate by index
- boolean success = simPhoneBook.updateAdnRecordsInEfByIndex(IccConstants.EF_ADN,
- firstAdn.getAlphaTag(), firstAdn.getNumber(), adnIndex, pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
- AdnRecord tmpAdn = adnRecordList.get(listIndex);
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_NEW_TAG, firstAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, firstAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ boolean success = simPhoneBook.updateAdnRecordsInEfByIndexForSubscriber(subId,
+ IccConstants.EF_ADN, values, adnIndex, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
+ AdnRecord tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertTrue(firstAdn.isEqual(tmpAdn));
// replace by search
- success = simPhoneBook.updateAdnRecordsInEfBySearch(IccConstants.EF_ADN,
- firstAdn.getAlphaTag(), firstAdn.getNumber(),
- secondAdn.getAlphaTag(), secondAdn.getNumber(), pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ ContentValues values2 = new ContentValues();
+ values.put(IccProvider.STR_TAG, firstAdn.getAlphaTag());
+ values.put(IccProvider.STR_NUMBER, firstAdn.getNumber());
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, secondAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, secondAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ success = simPhoneBook.updateAdnRecordsInEfBySearchForSubscriber(subId,
+ IccConstants.EF_ADN, values2, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertFalse(firstAdn.isEqual(tmpAdn));
assertTrue(secondAdn.isEqual(tmpAdn));
// erase be search
- success = simPhoneBook.updateAdnRecordsInEfBySearch(IccConstants.EF_ADN,
- secondAdn.getAlphaTag(), secondAdn.getNumber(),
- emptyAdn.getAlphaTag(), emptyAdn.getNumber(), pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ ContentValues values3 = new ContentValues();
+ values.put(IccProvider.STR_TAG, secondAdn.getAlphaTag());
+ values.put(IccProvider.STR_NUMBER, secondAdn.getNumber());
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, emptyAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, emptyAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ success = simPhoneBook.updateAdnRecordsInEfBySearchForSubscriber(subId,
+ IccConstants.EF_ADN, values3, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertTrue(tmpAdn.isEmpty());
// restore the orginial adn
- success = simPhoneBook.updateAdnRecordsInEfByIndex(IccConstants.EF_ADN,
- originalAdn.getAlphaTag(), originalAdn.getNumber(), adnIndex,
- pin2);
- adnRecordList = simPhoneBook.getAdnRecordsInEf(IccConstants.EF_ADN);
+ ContentValues values4 = new ContentValues();
+ values.put(IccProvider.STR_NEW_TAG, originalAdn.getAlphaTag());
+ values.put(IccProvider.STR_NEW_NUMBER, originalAdn.getNumber());
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+ success = simPhoneBook.updateAdnRecordsInEfByIndexForSubscriber(subId,
+ IccConstants.EF_ADN, values4, adnIndex, pin2);
+ adnRecordList =
+ simPhoneBook.getAdnRecordsInEfForSubscriber(subId, IccConstants.EF_ADN);
tmpAdn = adnRecordList.get(listIndex);
assertTrue(success);
assertTrue(originalAdn.isEqual(tmpAdn));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index 967ed49d93..c545cdd43f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -47,9 +47,10 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SignalThresholdInfo;
import android.telephony.TelephonyManager;
-import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,6 +68,9 @@ import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.uicc.AdnCapacity;
+import com.android.internal.telephony.uicc.ReceivedPhonebookRecords;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -136,7 +140,8 @@ public class SimulatedCommands extends BaseCommands
boolean mSimFdnEnabled;
int mPin2UnlockAttempts;
int mPuk2UnlockAttempts;
- int mNetworkType;
+ int mPreferredNetworkType;
+ int mAllowedNetworkType;
String mPin2Code;
boolean mSsnNotifyOn = false;
private int mVoiceRegState = NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
@@ -149,6 +154,8 @@ public class SimulatedCommands extends BaseCommands
public int mDefaultRoamingIndicator;
public int mReasonForDenial;
public int mMaxDataCalls;
+ public boolean mSendSetGsmBroadcastConfigResponse = true;
+ public boolean mSendGetSmscAddressResponse = true;
private SignalStrength mSignalStrength;
private List<CellInfo> mCellInfoList = null;
@@ -159,6 +166,9 @@ public class SimulatedCommands extends BaseCommands
private IccIoResult mIccIoResultForApduLogicalChannel;
private int mChannelId = IccOpenLogicalChannelResponse.INVALID_CHANNEL;
+ private Object mDataRegStateResult;
+ private Object mVoiceRegStateResult;
+
int mPausedResponseCount;
ArrayList<Message> mPausedResponses = new ArrayList<Message>();
@@ -997,14 +1007,17 @@ public class SimulatedCommands extends BaseCommands
public void getVoiceRegistrationState(Message result) {
mGetVoiceRegistrationStateCallCount.incrementAndGet();
- VoiceRegStateResult ret = new VoiceRegStateResult();
- ret.regState = mVoiceRegState;
- ret.rat = mVoiceRadioTech;
- ret.cssSupported = mCssSupported;
- ret.roamingIndicator = mRoamingIndicator;
- ret.systemIsInPrl = mSystemIsInPrl;
- ret.defaultRoamingIndicator = mDefaultRoamingIndicator;
- ret.reasonForDenial = mReasonForDenial;
+ Object ret = mVoiceRegStateResult;
+ if (ret == null) {
+ ret = new VoiceRegStateResult();
+ ((VoiceRegStateResult) ret).regState = mVoiceRegState;
+ ((VoiceRegStateResult) ret).rat = mVoiceRadioTech;
+ ((VoiceRegStateResult) ret).cssSupported = mCssSupported;
+ ((VoiceRegStateResult) ret).roamingIndicator = mRoamingIndicator;
+ ((VoiceRegStateResult) ret).systemIsInPrl = mSystemIsInPrl;
+ ((VoiceRegStateResult) ret).defaultRoamingIndicator = mDefaultRoamingIndicator;
+ ((VoiceRegStateResult) ret).reasonForDenial = mReasonForDenial;
+ }
resultSuccess(result, ret);
}
@@ -1025,14 +1038,17 @@ public class SimulatedCommands extends BaseCommands
}
@Override
- public void getDataRegistrationState (Message result) {
+ public void getDataRegistrationState(Message result) {
mGetDataRegistrationStateCallCount.incrementAndGet();
- DataRegStateResult ret = new DataRegStateResult();
- ret.regState = mDataRegState;
- ret.rat = mDataRadioTech;
- ret.maxDataCalls = mMaxDataCalls;
- ret.reasonDataDenied = mReasonForDenial;
+ Object ret = mDataRegStateResult;
+ if (ret == null) {
+ ret = new DataRegStateResult();
+ ((DataRegStateResult) ret).regState = mDataRegState;
+ ((DataRegStateResult) ret).rat = mDataRadioTech;
+ ((DataRegStateResult) ret).maxDataCalls = mMaxDataCalls;
+ ((DataRegStateResult) ret).reasonDataDenied = mReasonForDenial;
+ }
resultSuccess(result, ret);
}
@@ -1133,7 +1149,8 @@ public class SimulatedCommands extends BaseCommands
*/
@Override
public void sendSMSExpectMore (String smscPDU, String pdu, Message result) {
- unimplemented(result);
+ SimulatedCommandsVerifier.getInstance().sendSMSExpectMore(smscPDU, pdu, result);
+ resultSuccess(result, new SmsResponse(0 /*messageRef*/, null, SmsResponse.NO_ERROR_CODE));
}
@Override
@@ -1174,11 +1191,13 @@ public class SimulatedCommands extends BaseCommands
@Override
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- Message result) {
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, Message result) {
SimulatedCommandsVerifier.getInstance().setupDataCall(accessNetworkType, dataProfile,
- isRoaming, allowRoaming, reason, linkProperties, result);
+ isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo,
+ trafficDescriptor, matchAllRuleAllowed, result);
if (mSetupDataCallResult == null) {
try {
@@ -1200,14 +1219,6 @@ public class SimulatedCommands extends BaseCommands
}
}
- // Store different cids to simulate concurrent IMS and default data calls
- if ((dataProfile.getSupportedApnTypesBitmask() & ApnSetting.TYPE_IMS)
- == ApnSetting.TYPE_IMS) {
- mSetupDataCallResult.cid = 0;
- } else {
- mSetupDataCallResult.cid = 1;
- }
-
DataCallResponse response = RIL.convertDataCallResult(mSetupDataCallResult);
if (mDcSuccess) {
resultSuccess(result, response);
@@ -1225,7 +1236,7 @@ public class SimulatedCommands extends BaseCommands
@Override
public void setPreferredNetworkType(int networkType , Message result) {
SimulatedCommandsVerifier.getInstance().setPreferredNetworkType(networkType, result);
- mNetworkType = networkType;
+ mPreferredNetworkType = networkType;
resultSuccess(result, null);
}
@@ -1234,11 +1245,29 @@ public class SimulatedCommands extends BaseCommands
SimulatedCommandsVerifier.getInstance().getPreferredNetworkType(result);
int ret[] = new int[1];
- ret[0] = mNetworkType;
+ ret[0] = mPreferredNetworkType;
resultSuccess(result, ret);
}
@Override
+ public void setAllowedNetworkTypesBitmap(
+ @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message response) {
+ SimulatedCommandsVerifier.getInstance()
+ .setAllowedNetworkTypesBitmap(networkTypeBitmask, response);
+ mAllowedNetworkType = networkTypeBitmask;
+ resultSuccess(response, null);
+ }
+
+ @Override
+ public void getAllowedNetworkTypesBitmap(Message response) {
+ SimulatedCommandsVerifier.getInstance().getAllowedNetworkTypesBitmap(response);
+ int[] ret = new int[1];
+
+ ret[0] = mAllowedNetworkType;
+ resultSuccess(response, ret);
+ }
+
+ @Override
public void setLocationUpdates(boolean enable, Message response) {
SimulatedCommandsVerifier.getInstance().setLocationUpdates(enable, response);
resultSuccess(response, null);
@@ -1246,7 +1275,10 @@ public class SimulatedCommands extends BaseCommands
@Override
public void getSmscAddress(Message result) {
- unimplemented(result);
+ SimulatedCommandsVerifier.getInstance().getSmscAddress(result);
+ if (mSendGetSmscAddressResponse) {
+ unimplemented(result);
+ }
}
@Override
@@ -1708,7 +1740,7 @@ public class SimulatedCommands extends BaseCommands
}
@UnsupportedAppUsage
- private void resultSuccess(Message result, Object ret) {
+ protected void resultSuccess(Message result, Object ret) {
if (result != null) {
AsyncResult.forMessage(result).result = ret;
if (mPausedResponseCount > 0) {
@@ -1867,7 +1899,10 @@ public class SimulatedCommands extends BaseCommands
@Override
public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
- unimplemented(response);
+ SimulatedCommandsVerifier.getInstance().setGsmBroadcastConfig(config, response);
+ if (mSendSetGsmBroadcastConfigResponse) {
+ unimplemented(response);
+ }
}
@Override
@@ -2392,4 +2427,59 @@ public class SimulatedCommands extends BaseCommands
SimulatedCommandsVerifier.getInstance().getBarringInfo(result);
resultSuccess(result, null);
}
+
+ @Override
+ public void allocatePduSessionId(Message message) {
+ SimulatedCommandsVerifier.getInstance().allocatePduSessionId(message);
+ resultSuccess(message, 1);
+ }
+
+ @Override
+ public void releasePduSessionId(Message message, int pduSessionId) {
+ SimulatedCommandsVerifier.getInstance().releasePduSessionId(message, pduSessionId);
+ resultSuccess(message, null);
+ }
+
+ @Override
+ public void getSlicingConfig(Message result) {
+ SimulatedCommandsVerifier.getInstance().getSlicingConfig(result);
+ resultSuccess(result, null);
+ }
+
+ @VisibleForTesting
+ public void setDataRegStateResult(Object regStateResult) {
+ mDataRegStateResult = regStateResult;
+ }
+
+ @VisibleForTesting
+ public void setVoiceRegStateResult(Object regStateResult) {
+ mVoiceRegStateResult = regStateResult;
+ }
+
+ @Override
+ public void getSimPhonebookRecords(Message result) {
+ resultSuccess(result, null);
+
+ // send a fake result
+ List<SimPhonebookRecord> phonebookRecordInfoGroup = new ArrayList<SimPhonebookRecord>();
+ mSimPhonebookRecordsReceivedRegistrants.notifyRegistrants(
+ new AsyncResult(null,
+ new ReceivedPhonebookRecords(4, phonebookRecordInfoGroup), null));
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result) {
+ resultSuccess(result, new AdnCapacity(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result) {
+ resultSuccess(result, new int[]{phonebookRecord.getRecordIndex()});
+ notifySimPhonebookChanged();
+ }
+
+ @VisibleForTesting
+ public void notifySimPhonebookChanged() {
+ mSimPhonebookChangedRegistrants.notifyRegistrants();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index 6646c35d59..e9c74ae237 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -26,14 +26,17 @@ import android.telephony.NetworkScanRequest;
import android.telephony.SignalThresholdInfo;
import android.telephony.TelephonyManager;
import android.telephony.data.DataProfile;
+import android.telephony.data.NetworkSliceInfo;
+import android.telephony.data.TrafficDescriptor;
import android.telephony.emergency.EmergencyNumber;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.RadioCapability;
import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
+import com.android.internal.telephony.uicc.SimPhonebookRecord;
public class SimulatedCommandsVerifier implements CommandsInterface {
private static SimulatedCommandsVerifier sInstance;
@@ -181,6 +184,16 @@ public class SimulatedCommandsVerifier implements CommandsInterface {
}
@Override
+ public void registerForApnUnthrottled(Handler h, int what, Object obj) {
+
+ }
+
+ @Override
+ public void unregisterForApnUnthrottled(Handler h) {
+
+ }
+
+ @Override
public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj) {
}
@@ -1049,6 +1062,17 @@ public class SimulatedCommandsVerifier implements CommandsInterface {
}
@Override
+ public void setAllowedNetworkTypesBitmap(
+ @TelephonyManager.NetworkTypeBitMask int networkTypeBitmask, Message response) {
+
+ }
+
+ @Override
+ public void getAllowedNetworkTypesBitmap(Message response) {
+
+ }
+
+ @Override
public void setLocationUpdates(boolean enable, Message response) {
}
@@ -1180,8 +1204,9 @@ public class SimulatedCommandsVerifier implements CommandsInterface {
@Override
public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
- boolean allowRoaming, int reason, LinkProperties linkProperties,
- Message result) {
+ boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId,
+ NetworkSliceInfo sliceInfo, TrafficDescriptor trafficDescriptor,
+ boolean matchAllRuleAllowed, Message result) {
}
@Override
@@ -1223,11 +1248,6 @@ public class SimulatedCommandsVerifier implements CommandsInterface {
}
@Override
- public int getLteOnCdmaMode() {
- return 0;
- }
-
- @Override
public void requestIccSimAuthentication(int authContext, String data, String aid,
Message response) {
@@ -1465,4 +1485,44 @@ public class SimulatedCommandsVerifier implements CommandsInterface {
@Override
public void getBarringInfo(Message result) {
}
+
+ @Override
+ public void allocatePduSessionId(Message result) {
+ }
+
+ @Override
+ public void releasePduSessionId(Message result, int pduSessionId) {
+ }
+
+ @Override
+ public void getSlicingConfig(Message result) {
+ }
+
+ @Override
+ public void getSimPhonebookRecords(Message result){
+ }
+
+ @Override
+ public void getSimPhonebookCapacity(Message result){
+ }
+
+ @Override
+ public void updateSimPhonebookRecord(SimPhonebookRecord phonebookRecord, Message result){
+ }
+
+ @Override
+ public void registerForSimPhonebookChanged(Handler h, int what, Object obj){
+ }
+
+ @Override
+ public void unregisterForSimPhonebookChanged(Handler h){
+ }
+
+ @Override
+ public void registerForSimPhonebookRecordsReceived(Handler h, int what, Object obj){
+ }
+
+ @Override
+ public void unregisterForSimPhonebookRecordsReceived(Handler h){
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SlidingWindowEventCounterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SlidingWindowEventCounterTest.java
new file mode 100644
index 0000000000..da8b4c2f6c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SlidingWindowEventCounterTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SlidingWindowEventCounterTest {
+ long mInitialTime;
+
+ @Before
+ public void setUp() throws Exception {
+ mInitialTime = SystemClock.elapsedRealtime();
+ }
+
+ @Test
+ public void test_returnsTrue_ifEnoughEntriesInWindow() {
+ SlidingWindowEventCounter counter = new SlidingWindowEventCounter(100, 3);
+ for (int i = 0; i < 3; i++) {
+ counter.addOccurrence(mInitialTime + i);
+ }
+ assertTrue(counter.isInWindow());
+ }
+
+ @Test
+ public void test_returnsTrue_ifMoreThanEnoughEntriesInWindow() {
+ SlidingWindowEventCounter counter = new SlidingWindowEventCounter(100, 3);
+ for (int i = 0; i < 3; i++) {
+ counter.addOccurrence(mInitialTime + i);
+ }
+ for (int i = 0; i < 3; i++) {
+ counter.addOccurrence(mInitialTime + i + 50);
+ }
+ assertTrue(counter.isInWindow());
+ }
+
+ @Test
+ public void test_returnsFalse_ifNotEnoughEntriesInWindow() {
+ SlidingWindowEventCounter counter = new SlidingWindowEventCounter(100, 3);
+ for (int i = 0; i < 2; i++) {
+ counter.addOccurrence(mInitialTime + i);
+ }
+ assertFalse(counter.isInWindow());
+ }
+
+ @Test
+ public void test_returnsFalse_ifEnoughEntriesButTooFarApart() {
+ SlidingWindowEventCounter counter = new SlidingWindowEventCounter(100, 3);
+ for (int i = 0; i < 2; i++) {
+ counter.addOccurrence(mInitialTime + i);
+ }
+ counter.addOccurrence(mInitialTime + 101);
+ counter.addOccurrence(mInitialTime + 102);
+ assertFalse(counter.isInWindow());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 90ec3d5865..ee9e2b865f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -27,10 +29,12 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.os.Message;
import android.provider.Telephony.Sms.Intents;
+import android.telephony.SmsManager;
import android.test.FlakyTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -44,6 +48,8 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import java.util.HashMap;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class SmsDispatchersControllerTest extends TelephonyTest {
@@ -138,6 +144,22 @@ public class SmsDispatchersControllerTest extends TelephonyTest {
}
@Test @SmallTest
+ public void testSendRetrySmsNullPdu() throws Exception {
+ HashMap<String, Object> map = new HashMap<>();
+ map.put("scAddr", "");
+ map.put("destAddr", "");
+ map.put("text", null);
+ map.put("destPort", 0);
+ switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
+ replaceInstance(SMSDispatcher.SmsTracker.class, "mFormat", mTracker,
+ SmsConstants.FORMAT_3GPP2);
+ when(mTracker.getData()).thenReturn(map);
+ mSmsDispatchersController.sendRetrySms(mTracker);
+ verify(mTracker).onFailed(eq(mContext), eq(SmsManager.RESULT_SMS_SEND_RETRY_FAILED),
+ eq(NO_ERROR_CODE));
+ }
+
+ @Test @SmallTest
public void testInjectNullSmsPdu() throws Exception {
// unmock ActivityManager to be able to register receiver, create real PendingIntent and
// receive TEST_INTENT
@@ -145,7 +167,7 @@ public class SmsDispatchersControllerTest extends TelephonyTest {
restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
// inject null sms pdu. This should cause intent to be received since pdu is null.
- mSmsDispatchersController.injectSmsPdu(null, SmsConstants.FORMAT_3GPP,
+ mSmsDispatchersController.injectSmsPdu(null, SmsConstants.FORMAT_3GPP, true,
(SmsDispatchersController.SmsInjectionCallback) result -> {
mInjectionCallbackTriggered = true;
assertEquals(Intents.RESULT_SMS_GENERIC_ERROR, result);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 57c9eb28cf..b79b3178d9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -24,37 +24,41 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.Manifest;
-import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.UiccSlotInfo;
import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.UiccController;
@@ -76,6 +80,7 @@ import java.util.UUID;
public class SubscriptionControllerTest extends TelephonyTest {
private static final int SINGLE_SIM = 1;
private static final int DUAL_SIM = 2;
+ private static final int FAKE_SUBID = 123;
private String mCallingPackage;
private String mCallingFeature;
private SubscriptionController mSubscriptionControllerUT;
@@ -91,6 +96,9 @@ public class SubscriptionControllerTest extends TelephonyTest {
private ISetOpportunisticDataCallback mSetOpptDataCallback;
@Mock
private Handler mHandler;
+ @Mock
+ private SubscriptionInfo mMockSubscriptionInfo;
+ private PersistableBundle mCarrierConfigs;
private static final String MAC_ADDRESS_PREFIX = "mac_";
private static final String DISPLAY_NAME_PREFIX = "my_phone_";
@@ -98,11 +106,16 @@ public class SubscriptionControllerTest extends TelephonyTest {
private static final String UNAVAILABLE_ICCID = "";
private static final String UNAVAILABLE_NUMBER = "";
private static final String DISPLAY_NUMBER = "123456";
+ private static final String DISPLAY_NAME = "testing_display_name";
@Before
public void setUp() throws Exception {
super.setUp("SubscriptionControllerTest");
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
doReturn(SINGLE_SIM).when(mTelephonyManager).getSimCount();
doReturn(SINGLE_SIM).when(mTelephonyManager).getPhoneCount();
mMockContentResolver = (MockContentResolver) mContext.getContentResolver();
@@ -118,6 +131,10 @@ public class SubscriptionControllerTest extends TelephonyTest {
mCallingFeature = mContext.getAttributionTag();
doReturn(1).when(mProxyController).getMaxRafSupported();
+
+ // Carrier Config
+ mCarrierConfigs = mContextFixture.getCarrierConfigBundle();
+
mContextFixture.putIntArrayResource(com.android.internal.R.array.sim_colors, new int[]{5});
setupMocksForTelephonyPermissions(Build.VERSION_CODES.R);
@@ -131,8 +148,10 @@ public class SubscriptionControllerTest extends TelephonyTest {
/*clear sub info in mSubscriptionControllerUT since they will otherwise be persistent
* between each test case. */
- mSubscriptionControllerUT.clearSubInfo();
- mSubscriptionControllerUT.resetStaticMembers();
+ if (mSubscriptionControllerUT != null) {
+ mSubscriptionControllerUT.clearSubInfo();
+ mSubscriptionControllerUT.resetStaticMembers();
+ }
/* clear settings for default voice/data/sms sub ID */
Settings.Global.putInt(mContext.getContentResolver(),
@@ -244,6 +263,244 @@ public class SubscriptionControllerTest extends TelephonyTest {
}
+ private void setSimEmbedded(boolean isEmbedded) throws Exception {
+ ContentValues values = new ContentValues();
+ values.put(SubscriptionManager.IS_EMBEDDED, isEmbedded ? 1 : 0);
+ mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
+ SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + getFirstSubId(),
+ null);
+ }
+
+ @Test @SmallTest
+ public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithEmbeddedSim()
+ throws Exception {
+ testInsertSim();
+
+ // Set values of DB
+ setSimEmbedded(true);
+ int subId = getFirstSubId();
+ int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
+
+ // Update with new value
+ String newDisplayName = "display_name_pnn";
+ int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_PNN;
+
+ // Save to DB after updated
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
+
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
+
+ assertNotNull(subInfo);
+ assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
+ assertEquals(nameSource, subInfo.getNameSource());
+ }
+
+ @Test @SmallTest
+ public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithConfigIsNull()
+ throws Exception {
+ testInsertSim();
+
+ // Set values of DB
+ setSimEmbedded(false);
+ int subId = getFirstSubId();
+ int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
+
+ // Update with new value
+ String newDisplayName = "display_name_spn";
+ int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
+ when(mCarrierConfigManager.getConfigForSubId(subId)).thenReturn(null);
+
+ // Save to DB after updated
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
+
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
+
+ assertNotNull(subInfo);
+ assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
+ assertEquals(nameSource, subInfo.getNameSource());
+ }
+
+ @Test @SmallTest
+ public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithCarrierNameOverride()
+ throws Exception {
+ testInsertSim();
+
+ // Set values of DB
+ setSimEmbedded(false);
+ int subId = getFirstSubId();
+ int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
+
+ // Update with new value
+ int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
+ String newDisplayName = "display_name_spn";
+ mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, true);
+
+ // Save to DB after updated
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
+
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
+
+ assertNotNull(subInfo);
+ assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
+ assertEquals(nameSource, subInfo.getNameSource());
+ }
+
+ @Test @SmallTest
+ public void testSetGetDisplayNameSrc_updateNameSourceCarrierWithSpnAndCarrierName()
+ throws Exception {
+ testInsertSim();
+
+ // Set values of DB
+ setSimEmbedded(false);
+ int subId = getFirstSubId();
+ int nameSource = SubscriptionManager.NAME_SOURCE_CARRIER;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
+
+ // Update with new value
+ int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
+ String carrierName = "testing_carrier_name";
+ String newDisplayName = "display_name_spn";
+ when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
+ when(mUiccProfile.getServiceProviderName()).thenReturn(null);
+ mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
+ mCarrierConfigs.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING, carrierName);
+
+ // Save to DB after updated
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
+
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
+
+ assertNotNull(subInfo);
+ assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
+ assertEquals(nameSource, subInfo.getNameSource());
+ }
+
+ @Test @SmallTest
+ public void testSetGetDisplayNameSrc_updateNameSourcePnnToNameSourceCarrierId()
+ throws Exception {
+ testInsertSim();
+
+ // Set values of DB
+ int subId = getFirstSubId();
+ int nameSource = SubscriptionManager.NAME_SOURCE_SIM_PNN;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
+
+ // Update with new value
+ String newDisplayName = "display_name_carrier_id";
+ int newNameSource = SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+ when(mPhone.getPlmn()).thenReturn("testing_pnn");
+
+ // Save to DB after updated
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
+
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
+
+ assertNotNull(subInfo);
+ assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
+ assertEquals(nameSource, subInfo.getNameSource());
+ }
+
+ @Test @SmallTest
+ public void testSetGetDisplayNameSrc_updateNameSourceUserInputToNameSourceSpn()
+ throws Exception {
+ testInsertSim();
+
+ // Set values of DB
+ int subId = getFirstSubId();
+ int nameSource = SubscriptionManager.NAME_SOURCE_USER_INPUT;
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(DISPLAY_NAME, subId, nameSource);
+
+ // Update with new value
+ String newDisplayName = "display_name_spn";
+ int newNameSource = SubscriptionManager.NAME_SOURCE_SIM_SPN;
+
+ // Save to DB after updated
+ mSubscriptionControllerUT.setDisplayNameUsingSrc(newDisplayName, subId, newNameSource);
+
+ SubscriptionInfo subInfo = mSubscriptionControllerUT
+ .getActiveSubscriptionInfo(subId, mCallingPackage, mCallingFeature);
+
+ assertNotNull(subInfo);
+ assertEquals(DISPLAY_NAME, subInfo.getDisplayName());
+ assertEquals(nameSource, subInfo.getNameSource());
+ }
+
+ @Test @SmallTest
+ public void testIsExistingNameSourceStillValid_pnnIsNotNull_returnTrue() {
+ when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
+ when(mMockSubscriptionInfo.getNameSource())
+ .thenReturn(SubscriptionManager.NAME_SOURCE_SIM_PNN);
+ when(mPhone.getPlmn()).thenReturn("testing_pnn");
+
+ assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
+ }
+
+ @Test @SmallTest
+ public void testIsExistingNameSourceStillValid_spnIsNotNull_returnTrue() {
+ when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
+ when(mMockSubscriptionInfo.getNameSource())
+ .thenReturn(SubscriptionManager.NAME_SOURCE_SIM_SPN);
+ when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
+ when(mUiccProfile.getServiceProviderName()).thenReturn("testing_spn");
+
+ assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
+ }
+
+ @Test @SmallTest
+ public void testIsExistingNameSourceStillValid_simIsEmbedded_returnTrue() {
+ when(mMockSubscriptionInfo.isEmbedded()).thenReturn(true);
+ when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
+ when(mMockSubscriptionInfo.getNameSource())
+ .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
+
+ assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
+ }
+
+ @Test @SmallTest
+ public void testIsExistingNameSourceStillValid_carrierConfigIsNull_returnTrue() {
+ when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
+ when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
+ when(mMockSubscriptionInfo.getNameSource())
+ .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
+ when(mCarrierConfigManager.getConfigForSubId(FAKE_SUBID)).thenReturn(null);
+
+ assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
+ }
+
+ @Test @SmallTest
+ public void testIsExistingNameSourceStillValid_carrierNameOverrideIsTrue_returnTrue() {
+ when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
+ when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
+ when(mMockSubscriptionInfo.getNameSource())
+ .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
+ mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, true);
+
+ assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
+ }
+
+ @Test @SmallTest
+ public void testIsExistingNameSourceStillValid_spnIsNullAndCarrierNameIsNotNull_returnTrue() {
+ when(mMockSubscriptionInfo.isEmbedded()).thenReturn(false);
+ when((mMockSubscriptionInfo).getSubscriptionId()).thenReturn(FAKE_SUBID);
+ when(mMockSubscriptionInfo.getNameSource())
+ .thenReturn(SubscriptionManager.NAME_SOURCE_CARRIER);
+ when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
+ when(mUiccProfile.getServiceProviderName()).thenReturn(null);
+ mCarrierConfigs.putBoolean(CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
+ mCarrierConfigs.putString(CarrierConfigManager.KEY_CARRIER_NAME_STRING,
+ "testing_carrier_name");
+
+ assertTrue(mSubscriptionControllerUT.isExistingNameSourceStillValid(mMockSubscriptionInfo));
+ }
+
@Test @SmallTest
public void testCleanUpSIM() {
testInsertSim();
@@ -1070,12 +1327,13 @@ public class SubscriptionControllerTest extends TelephonyTest {
}
@Test
- public void testGetActiveSubscriptionWithReadPhoneNumbers() throws Exception {
- // If the calling package has the READ_PHONE_NUMBERS permission the number should be
- // available in the SubscriptionInfo.
+ public void testGetActiveSubscriptionWithPhoneNumberAccess() throws Exception {
+ // If the calling package meets any of the requirements for the
+ // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
+ // in the SubscriptionInfo.
testInsertSim();
setupReadPhoneNumbersTest();
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_NUMBERS);
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
int subId = getFirstSubId();
SubscriptionInfo subscriptionInfo = mSubscriptionControllerUT.getActiveSubscriptionInfo(
@@ -1153,13 +1411,14 @@ public class SubscriptionControllerTest extends TelephonyTest {
}
@Test
- public void testGetActiveSubscriptionInfoForSimSlotIndexWithReadPhoneNumbers()
+ public void testGetActiveSubscriptionInfoForSimSlotIndexWithPhoneNumberAccess()
throws Exception {
- // If the calling package has the READ_PHONE_NUMBERS permission the number should be
- // available in the SubscriptionInfo.
+ // If the calling package meets any of the requirements for the
+ // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
+ // in the SubscriptionInfo.
testInsertSim();
setupReadPhoneNumbersTest();
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_NUMBERS);
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
SubscriptionInfo subscriptionInfo =
mSubscriptionControllerUT.getActiveSubscriptionInfoForSimSlotIndex(0,
@@ -1239,12 +1498,13 @@ public class SubscriptionControllerTest extends TelephonyTest {
}
@Test
- public void testGetActiveSubscriptionInfoListWithReadPhoneNumbers() throws Exception {
- // If the calling package has the READ_PHONE_NUMBERS permission the number should be
- // available in the SubscriptionInfo.
+ public void testGetActiveSubscriptionInfoListWithPhoneNumberAccess() throws Exception {
+ // If the calling package meets any of the requirements for the
+ // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
+ // in the SubscriptionInfo.
testInsertSim();
setupReadPhoneNumbersTest();
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_NUMBERS);
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
List<SubscriptionInfo> subInfoList =
mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
@@ -1301,6 +1561,29 @@ public class SubscriptionControllerTest extends TelephonyTest {
}
@Test
+ public void testGetActiveSubscriptionInfoListCheckOrder()
+ throws Exception {
+ // If an app does not have the READ_PHONE_STATE permission but has carrier privileges on one
+ // out of multiple sub IDs then the SubscriptionInfo for that subId should be returned with
+ // the ICC ID and phone number.
+ testInsertSim();
+ doReturn(2).when(mTelephonyManager).getPhoneCount();
+ mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
+ int firstSubId = getFirstSubId();
+ int secondSubId = getSubIdAtIndex(1);
+ setupIdentifierCarrierPrivilegesTest();
+ setCarrierPrivilegesForSubId(false, firstSubId);
+ setCarrierPrivilegesForSubId(true, secondSubId);
+
+ List<SubscriptionInfo> subInfoList =
+ mSubscriptionControllerUT.getActiveSubscriptionInfoList(mCallingPackage,
+ mCallingFeature);
+
+ assertEquals(2, subInfoList.size());
+ assertTrue(subInfoList.get(0).getSubscriptionId() < subInfoList.get(1).getSubscriptionId());
+ }
+
+ @Test
public void testGetActiveSubscriptionInfoListWithIdentifierAccessWithoutNumberAccess()
throws Exception {
// An app with access to device identifiers may not have access to the device phone number
@@ -1375,12 +1658,13 @@ public class SubscriptionControllerTest extends TelephonyTest {
}
@Test
- public void testGetSubscriptionInGroupWithReadPhoneNumbers() throws Exception {
- // If the calling package has the READ_PHONE_NUMBERS permission the number should be
- // available in the SubscriptionInfo.
+ public void testGetSubscriptionInGroupWithPhoneNumberAccess() throws Exception {
+ // If the calling package meets any of the requirements for the
+ // LegacyPermissionManager#checkPhoneNumberAccess test then the number should be available
+ // in the SubscriptionInfo.
ParcelUuid groupUuid = setupGetSubscriptionsInGroupTest();
setupReadPhoneNumbersTest();
- mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_NUMBERS);
+ setPhoneNumberAccess(PackageManager.PERMISSION_GRANTED);
List<SubscriptionInfo> subInfoList = mSubscriptionControllerUT.getSubscriptionsInGroup(
groupUuid, mCallingPackage, mCallingFeature);
@@ -1437,9 +1721,7 @@ public class SubscriptionControllerTest extends TelephonyTest {
mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
setupMocksForTelephonyPermissions(Build.VERSION_CODES.R);
- doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).noteOp(
- eq(AppOpsManager.OPSTR_WRITE_SMS), anyInt(), anyString(),
- nullable(String.class), nullable(String.class));
+ setPhoneNumberAccess(PackageManager.PERMISSION_DENIED);
}
private void setupIdentifierCarrierPrivilegesTest() throws Exception {
@@ -1641,4 +1923,38 @@ public class SubscriptionControllerTest extends TelephonyTest {
assertFalse(mSubscriptionControllerUT.getActiveSubscriptionInfo(
1, mContext.getOpPackageName(), null).areUiccApplicationsEnabled());
}
+
+ @Test
+ @SmallTest
+ public void testInsertEmptySubInfoRecord_returnsNull_ifRecordExists() {
+ final String mockedIccid = "123456789";
+ final int mockedSlotIndex = 1;
+
+ assertNotNull(mSubscriptionControllerUT.insertEmptySubInfoRecord(
+ mockedIccid, mockedSlotIndex));
+ // Insert second time with the same iccid should result in no-op and return null.
+ assertNull(mSubscriptionControllerUT.insertEmptySubInfoRecord(
+ mockedIccid, mockedSlotIndex));
+ assertEquals(
+ 1,
+ mSubscriptionControllerUT
+ .getAllSubInfoList(mCallingPackage, mCallingFeature).size());
+ }
+
+ @Test
+ @SmallTest
+ public void testCheckPhoneIdAndIccIdMatch() {
+ try {
+ testSetSubscriptionGroupWithModifyPermission();
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+
+ mSubscriptionControllerUT.addSubInfoRecord("test3",
+ SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+
+ assertTrue(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test"));
+ assertTrue(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test2"));
+ assertFalse(mSubscriptionControllerUT.checkPhoneIdAndIccIdMatch(0, "test3"));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index ce5be8e50d..c92d4f9d95 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -42,7 +42,6 @@ import android.net.Uri;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
-import android.permission.IPermissionManager;
import android.service.euicc.EuiccProfileInfo;
import android.service.euicc.EuiccService;
import android.service.euicc.GetEuiccProfileInfoListResult;
@@ -85,8 +84,10 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
private static final int FAKE_SUB_ID_2 = 1;
private static final int FAKE_CARD_ID = 0;
private static final String FAKE_EID = "89049032000001000000031328322874";
+ private static final String FAKE_ICCID_1 = "89012604200000000000";
private static final String FAKE_MCC_MNC_1 = "123456";
private static final String FAKE_MCC_MNC_2 = "456789";
+ private static final int FAKE_PHONE_ID_1 = 0;
private SubscriptionInfoUpdater mUpdater;
private IccRecords mIccRecord;
@@ -107,8 +108,6 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
@Mock
private IPackageManager mPackageManager;
@Mock
- private IPermissionManager mPermissionManager;
- @Mock
private UiccSlot mUiccSlot;
/*Custom ContentProvider */
@@ -161,8 +160,8 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
.getActiveSubIdList(/*visibleOnly*/false);
mIccRecord = mUiccProfile.getIccRecords();
- mUpdater = new SubscriptionInfoUpdater(Looper.myLooper(), mContext,
- new CommandsInterface[]{mSimulatedCommands});
+ mUpdater =
+ new SubscriptionInfoUpdater(Looper.myLooper(), mContext, mSubscriptionController);
processAllMessages();
assertFalse(mUpdater.isSubInfoInitialized());
@@ -238,17 +237,17 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
@SmallTest
public void testSimNotReady() throws Exception {
mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
+ IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
processAllMessages();
assertFalse(mUpdater.isSubInfoInitialized());
verify(mSubscriptionContent, never()).put(anyString(), any());
CarrierConfigManager mConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager, never()).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
+ verify(mConfigManager, never()).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
- verify(mSubscriptionController, never()).clearSubInfo();
- verify(mSubscriptionController, never()).notifySubscriptionInfoChanged();
+ verify(mSubscriptionController).clearSubInfoRecord(FAKE_PHONE_ID_1);
+ verify(mSubscriptionController).notifySubscriptionInfoChanged();
}
@Test
@@ -258,19 +257,19 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
doReturn(true).when(mIccCard).isEmptyProfile();
mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
+ IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
processAllMessages();
assertTrue(mUpdater.isSubInfoInitialized());
// Sub info should be cleared and change should be notified.
- verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
+ verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_PHONE_ID_1));
verify(mSubscriptionController).notifySubscriptionInfoChanged();
// No new sub should be added.
verify(mSubscriptionManager, never()).addSubscriptionInfoRecord(any(), anyInt());
verify(mSubscriptionContent, never()).put(anyString(), any());
CarrierConfigManager mConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
+ verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
}
@@ -285,24 +284,24 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
doReturn(false).when(mSubInfo).areUiccApplicationsEnabled();
mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_SUB_ID_1);
+ IccCardConstants.INTENT_VALUE_ICC_NOT_READY, null, FAKE_PHONE_ID_1);
processAllMessages();
assertTrue(mUpdater.isSubInfoInitialized());
// Sub info should be cleared and change should be notified.
- verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
+ verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_PHONE_ID_1));
verify(mSubscriptionController).notifySubscriptionInfoChanged();
// No new sub should be added.
verify(mSubscriptionManager, never()).addSubscriptionInfoRecord(any(), anyInt());
verify(mSubscriptionContent, never()).put(anyString(), any());
CarrierConfigManager mConfigManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
+ verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_PHONE_ID_1),
eq(IccCardConstants.INTENT_VALUE_ICC_NOT_READY));
// When becomes ABSENT, UICC_APPLICATIONS_ENABLED should be reset to true.
mUpdater.updateInternalIccState(
- IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_PHONE_ID_1);
processAllMessages();
ArgumentCaptor<ContentValues> valueCapture = ArgumentCaptor.forClass(ContentValues.class);
verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), valueCapture.capture(),
@@ -314,6 +313,24 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
@Test
@SmallTest
+ public void testSimRemovedWhileDisablingUiccApps() throws Exception {
+ loadSim();
+
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, FAKE_SUB_ID_1);
+ processAllMessages();
+
+ // UICC_APPLICATIONS_ENABLED should be reset to true.
+ ArgumentCaptor<ContentValues> valueCapture = ArgumentCaptor.forClass(ContentValues.class);
+ verify(mContentProvider).update(eq(SubscriptionManager.CONTENT_URI), valueCapture.capture(),
+ eq(SubscriptionManager.ICC_ID + "=\'" + FAKE_ICCID_1 + "\'"), eq(null));
+ ContentValues contentValues = valueCapture.getValue();
+ assertTrue(contentValues != null && contentValues.getAsBoolean(
+ UICC_APPLICATIONS_ENABLED));
+ }
+
+ @Test
+ @SmallTest
public void testSimError() throws Exception {
mUpdater.updateInternalIccState(
IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR, null, FAKE_SUB_ID_1);
@@ -346,13 +363,11 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
}
- @Test
- @SmallTest
- public void testSimLoaded() throws Exception {
+ private void loadSim() {
doReturn(FAKE_SUB_ID_1).when(mSubInfo).getSubscriptionId();
doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
.getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1));
- doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
+ doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
when(mActivityManager.updateMccMncConfiguration(anyString(), anyString())).thenReturn(
true);
@@ -363,6 +378,17 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
processAllMessages();
assertTrue(mUpdater.isSubInfoInitialized());
+ CarrierConfigManager mConfigManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
+ eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
+ }
+
+ @Test
+ @SmallTest
+ public void testSimLoaded() throws Exception {
+ loadSim();
+
// verify SIM_STATE_CHANGED broadcast. It should be broadcast twice, once for
// READ_PHONE_STATE and once for READ_PRIVILEGED_PHONE_STATE
/* todo: cannot verify as intent is sent using ActivityManagerNative.broadcastStickyIntent()
@@ -384,7 +410,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
+ eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
verify(mSubscriptionController, times(1)).setMccMnc(FAKE_MCC_MNC_1, FAKE_SUB_ID_1);
verify(mSubscriptionController, times(0)).clearSubInfo();
@@ -417,7 +443,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
@Test
@SmallTest
public void testSimLoadedEmptyOperatorNumeric() throws Exception {
- doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
+ doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
// operator numeric is empty
doReturn("").when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
doReturn(FAKE_SUB_ID_1).when(mSubInfo).getSubscriptionId();
@@ -431,7 +457,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
- eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
+ eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
verify(mSubscriptionController, times(0)).clearSubInfo();
@@ -491,7 +517,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_1));
doReturn(FAKE_MCC_MNC_2).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_2));
verify(mSubscriptionController, times(0)).clearSubInfo();
- doReturn("89012604200000000000").when(mIccRecord).getFullIccId();
+ doReturn(FAKE_ICCID_1).when(mIccRecord).getFullIccId();
SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(anyString(), anyInt());
verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
@@ -519,7 +545,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
IccCardConstants.INTENT_VALUE_ICC_LOADED, null, FAKE_SUB_ID_2);
processAllMessages();
- verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000000"),
+ verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq(FAKE_ICCID_1),
eq(FAKE_SUB_ID_1));
verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000001"),
eq(FAKE_SUB_ID_2));
@@ -536,7 +562,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
doReturn("98106240020000000000").when(mIccRecord).getFullIccId();
replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,
- new String[]{"89012604200000000000"});
+ new String[]{FAKE_ICCID_1});
mUpdater.updateInternalIccState(
IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
@@ -725,9 +751,10 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
mUpdater.updateSubscriptionByCarrierConfig(mPhone.getPhoneId(),
carrierPackageName, new PersistableBundle());
- verify(mContentProvider, never()).update(any(), any(), any(), any());
- verify(mSubscriptionController, never()).refreshCachedActiveSubscriptionInfoList();
- verify(mSubscriptionController, never()).notifySubscriptionInfoChanged();
+ //at each call to updateSubscriptionByCarrierConfig, only carrier certs are updated
+ verify(mContentProvider, times(1)).update(any(), any(), any(), any());
+ verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
+ verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList();
}
@Test
@@ -756,7 +783,8 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
cvCaptor.capture(), eq(null), eq(null));
assertEquals(1, cvCaptor.getValue().getAsInteger(
SubscriptionManager.IS_OPPORTUNISTIC).intValue());
- assertEquals(1, cvCaptor.getValue().size());
+ // 2 updates: isOpportunistic, and carrier certs:
+ assertEquals(2, cvCaptor.getValue().size());
verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList();
verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
}
@@ -792,7 +820,8 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID));
assertEquals(carrierPackageName,
cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_OWNER));
- assertEquals(3, cvCaptor.getValue().size());
+ // 4 updates: isOpportunistic, groupUuid, groupOwner, and carrier certs:
+ assertEquals(4, cvCaptor.getValue().size());
}
@Test
@@ -825,7 +854,8 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
assertEquals(1, cvCaptor.getValue().getAsInteger(
SubscriptionManager.IS_OPPORTUNISTIC).intValue());
assertNull(cvCaptor.getValue().getAsString(SubscriptionManager.GROUP_UUID));
- assertEquals(2, cvCaptor.getValue().size());
+ // 3 updates: isOpportunistic, groupUuid, and carrier certs:
+ assertEquals(3, cvCaptor.getValue().size());
}
@Test
@@ -870,4 +900,44 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
verify(mSubscriptionController, times(1)).refreshCachedActiveSubscriptionInfoList();
verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
}
+
+ @Test
+ @SmallTest
+ public void testSimReady() throws Exception {
+ replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,new String[]{""});
+
+ doReturn(FAKE_ICCID_1).when(mUiccSlot).getIccId();
+
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_READY, "TESTING", FAKE_SUB_ID_1);
+ processAllMessages();
+
+ verify(mSubscriptionController).clearSubInfoRecord(eq(FAKE_SUB_ID_1));
+ verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
+ eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
+ assertTrue(mUpdater.isSubInfoInitialized());
+ verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
+ }
+
+ @Test
+ @SmallTest
+ public void testSimReadyAndLoaded() throws Exception {
+ replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,new String[]{""});
+
+ doReturn(null).when(mUiccSlot).getIccId();
+
+ mUpdater.updateInternalIccState(
+ IccCardConstants.INTENT_VALUE_ICC_READY, "TESTING", FAKE_SUB_ID_1);
+ processAllMessages();
+
+ verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(
+ eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
+
+ loadSim();
+
+ SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
+ verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
+ eq(FAKE_ICCID_1), eq(FAKE_SUB_ID_1));
+ verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
index dbaa29a76d..b545c2b7d1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -36,7 +36,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ServiceManager;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -46,7 +46,7 @@ import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.LegacyPermissionManagerService;
import org.junit.Before;
import org.junit.Test;
@@ -88,7 +88,7 @@ public class TelephonyPermissionsTest {
@Mock
private TelephonyManager mTelephonyManagerMockForSub2;
@Mock
- private PermissionManagerService mMockPermissionManagerService;
+ private LegacyPermissionManagerService mMockLegacyPermissionManagerService;
private MockContentResolver mMockContentResolver;
private FakeSettingsConfigProvider mFakeSettingsConfigProvider;
@@ -107,10 +107,10 @@ public class TelephonyPermissionsTest {
when(mMockSubscriptionManager.getCompleteActiveSubscriptionIdList()).thenReturn(
new int[]{SUB_ID});
- PermissionManager permissionManager = new PermissionManager(mMockContext, null,
- mMockPermissionManagerService);
- when(mMockContext.getSystemService(Context.PERMISSION_SERVICE)).thenReturn(
- permissionManager);
+ LegacyPermissionManager legacyPermissionManager = new LegacyPermissionManager(
+ mMockLegacyPermissionManagerService);
+ when(mMockContext.getSystemService(Context.LEGACY_PERMISSION_SERVICE)).thenReturn(
+ legacyPermissionManager);
// By default, assume we have no permissions or app-ops bits.
doThrow(new SecurityException()).when(mMockContext)
@@ -233,79 +233,42 @@ public class TelephonyPermissionsTest {
}
@Test
- public void testCheckReadPhoneNumber_defaultSmsApp() throws Exception {
- setupMocksForDeviceIdentifiersErrorPath();
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_WRITE_SMS), eq(UID), eq(PACKAGE),
- eq(FEATURE), nullable(String.class))).thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(
- mMockContext, SUB_ID, PID, UID, PACKAGE, FEATURE, MSG));
- }
-
- @Test
- public void testCheckReadPhoneNumber_hasPrivilegedPhoneStatePermission() throws Exception {
- setupMocksForDeviceIdentifiersErrorPath();
- doNothing().when(mMockContext).enforcePermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, PID, UID, MSG);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(
- mMockContext, SUB_ID, PID, UID, PACKAGE, FEATURE, MSG));
- }
-
- @Test
- public void testCheckReadPhoneNumber_hasReadSms() throws Exception {
- setupMocksForDeviceIdentifiersErrorPath();
- doNothing().when(mMockContext).enforcePermission(
- android.Manifest.permission.READ_SMS, PID, UID, MSG);
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_READ_SMS), eq(UID), eq(PACKAGE), eq(FEATURE),
- nullable(String.class))).thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(
- mMockContext, SUB_ID, PID, UID, PACKAGE, FEATURE, MSG));
+ public void testCheckReadPhoneNumber_targetPreRWithReadPhoneStateNoAppop() throws Exception {
+ // If ap app is targeting SDK version < R then the phone number should be accessible with
+ // both the READ_PHONE_STATE permission and appop granted; if only the permission is granted
+ // but the appop is denied then the LegacyPermissionManager should return MODE_IGNORED
+ // to indicate the check should fail silently (return empty / null data).
+ when(mMockLegacyPermissionManagerService.checkPhoneNumberAccess(PACKAGE, MSG, FEATURE,
+ PID, UID)).thenReturn(AppOpsManager.MODE_IGNORED);
+ assertFalse(
+ TelephonyPermissions.checkReadPhoneNumber(mMockContext, SUB_ID, PID, UID, PACKAGE,
+ FEATURE, MSG));
}
@Test
- public void testCheckReadPhoneNumber_hasReadPhoneNumbers() throws Exception {
- setupMocksForDeviceIdentifiersErrorPath();
- doNothing().when(mMockContext).enforcePermission(
- android.Manifest.permission.READ_PHONE_NUMBERS, PID, UID, MSG);
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_READ_PHONE_NUMBERS), eq(UID), eq(PACKAGE),
- eq(FEATURE), nullable(String.class))).thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(
- mMockContext, SUB_ID, PID, UID, PACKAGE, FEATURE, MSG));
+ public void testCheckReadPhoneNumber_hasPermissionManagerPhoneNumberAccess() {
+ // To limit binder transactions the targetSdkVersion, permission, and appop checks are all
+ // performed by the LegacyPermissionManager; this test verifies when this API returns
+ // the calling package meets the requirements for phone number access the telephony
+ // check also returns true.
+ when(mMockLegacyPermissionManagerService.checkPhoneNumberAccess(PACKAGE, MSG, FEATURE,
+ PID, UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
+ assertTrue(
+ TelephonyPermissions.checkReadPhoneNumber(mMockContext, SUB_ID, PID, UID, PACKAGE,
+ FEATURE, MSG));
}
@Test
- public void testCheckReadPhoneNumber_hasReadSmsNoAppop() throws Exception {
- // If an app has been granted the READ_SMS permission, but the OPSTR_READ_SMS appop has been
- // revoked then instead of immediately returning false the phone number access check should
- // check if the caller has the READ_PHONE_NUMBERS permission and appop.
- setupMocksForDeviceIdentifiersErrorPath();
- doNothing().when(mMockContext).enforcePermission(
- android.Manifest.permission.READ_SMS, PID, UID, MSG);
- doNothing().when(mMockContext).enforcePermission(
- android.Manifest.permission.READ_PHONE_NUMBERS, PID, UID, MSG);
- when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_READ_PHONE_NUMBERS), eq(UID), eq(PACKAGE),
- eq(FEATURE), nullable(String.class))).thenReturn(AppOpsManager.MODE_ALLOWED);
- assertTrue(TelephonyPermissions.checkReadPhoneNumber(
- mMockContext, SUB_ID, PID, UID, PACKAGE, FEATURE, MSG));
+ public void testCheckReadPhoneNumber_hasCarrierPrivileges() throws Exception {
+ when(mTelephonyManagerMock.createForSubscriptionId(eq(SUB_ID))).thenReturn(
+ mTelephonyManagerMockForSub1);
+ when(mTelephonyManagerMockForSub1.getCarrierPrivilegeStatus(anyInt())).thenReturn(
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ assertTrue(
+ TelephonyPermissions.checkReadPhoneNumber(mMockContext, SUB_ID, PID, UID, PACKAGE,
+ FEATURE, MSG));
}
- @Test
- public void testCheckReadPhoneNumber_hasReadSmsAndReadPhoneNumbersNoAppops() throws Exception {
- // If an app has both the READ_SMS and READ_PHONE_NUMBERS permissions granted but does not
- // have the corresponding appops instead of returning false for not having the appop granted
- // a SecurityException should be thrown.
- setupMocksForDeviceIdentifiersErrorPath();
- doNothing().when(mMockContext).enforcePermission(
- android.Manifest.permission.READ_SMS, PID, UID, MSG);
- doNothing().when(mMockContext).enforcePermission(
- android.Manifest.permission.READ_PHONE_NUMBERS, PID, UID, MSG);
- try {
- TelephonyPermissions.checkReadPhoneNumber(
- mMockContext, SUB_ID, PID, UID, PACKAGE, FEATURE, MSG);
- fail("Should have thrown SecurityException");
- } catch (SecurityException e) {
- // expected
- }
- }
@Test
public void testCheckReadDeviceIdentifiers_noPermissions() throws Exception {
@@ -325,8 +288,8 @@ public class TelephonyPermissionsTest {
// performed by a SystemAPI in PermissionManager; this test verifies when this API returns
// the calling package meets the requirements for device identifier access the telephony
// check also returns true.
- when(mMockPermissionManagerService.checkDeviceIdentifierAccess(PACKAGE, MSG, FEATURE, PID,
- UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mMockLegacyPermissionManagerService.checkDeviceIdentifierAccess(PACKAGE, MSG, FEATURE,
+ PID, UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
assertTrue(
TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
SUB_ID, PACKAGE, FEATURE, MSG));
@@ -473,7 +436,7 @@ public class TelephonyPermissionsTest {
*/
@Test
public void
- testEnforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege_noPermissions()
+ testEnforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege_noPermissions()
throws Exception {
// revoke permission READ_PRIVILEGED_PHONE_STATE
when(mMockContext.checkCallingOrSelfPermission(
@@ -485,7 +448,7 @@ public class TelephonyPermissionsTest {
PackageManager.PERMISSION_DENIED);
try {
TelephonyPermissions
- .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
mMockContext, SUB_ID, MSG);
fail("Should have thrown SecurityException");
} catch (SecurityException se) {
@@ -499,7 +462,7 @@ public class TelephonyPermissionsTest {
*/
@Test
public void
- testEnforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege_withPermissions()
+ testEnforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege_withPermissions()
throws Exception {
// grant READ_PRIVILEGED_PHONE_STATE permission
when(mMockContext.checkCallingOrSelfPermission(
@@ -507,7 +470,7 @@ public class TelephonyPermissionsTest {
PackageManager.PERMISSION_GRANTED);
try {
TelephonyPermissions
- .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
mMockContext, SUB_ID, MSG);
} catch (SecurityException se) {
fail();
@@ -524,7 +487,7 @@ public class TelephonyPermissionsTest {
PackageManager.PERMISSION_GRANTED);
try {
TelephonyPermissions
- .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
mMockContext, SUB_ID, MSG);
} catch (SecurityException se) {
fail();
@@ -536,7 +499,7 @@ public class TelephonyPermissionsTest {
*/
@Test
public void
- testEnforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege_withPrivileges()
+ testEnforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege_withPrivileges()
throws Exception {
// revoke permission READ_PRIVILEGED_PHONE_STATE
when(mMockContext.checkCallingOrSelfPermission(
@@ -554,7 +517,7 @@ public class TelephonyPermissionsTest {
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
try {
TelephonyPermissions
- .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
mMockContext, SUB_ID, MSG);
} catch (SecurityException se) {
fail("Should NOT throw SecurityException");
@@ -616,7 +579,9 @@ public class TelephonyPermissionsTest {
android.Manifest.permission.READ_DEVICE_CONFIG)).thenReturn(
PackageManager.PERMISSION_GRANTED);
- when(mMockPermissionManagerService.checkDeviceIdentifierAccess(any(), any(), any(),
+ when(mMockLegacyPermissionManagerService.checkDeviceIdentifierAccess(any(), any(), any(),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockLegacyPermissionManagerService.checkPhoneNumberAccess(any(), any(), any(),
anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
// TelephonyPermissions queries DeviceConfig to determine if the identifier access
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 626dee2d86..1d5bce64db 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -15,10 +15,8 @@
*/
package com.android.internal.telephony;
-import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
-import static android.telephony.PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE;
-import static android.telephony.PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED;
-import static android.telephony.PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED;
+import static android.telephony.PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
+import static android.telephony.ServiceState.FREQUENCY_RANGE_LOW;
import static android.telephony.SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
@@ -27,21 +25,33 @@ import static android.telephony.TelephonyManager.RADIO_POWER_ON;
import static android.telephony.TelephonyManager.RADIO_POWER_UNAVAILABLE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.net.LinkProperties;
+import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellLocation;
+import android.telephony.LinkCapacityEstimate;
import android.telephony.PhoneCapability;
-import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseDataConnectionState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -49,16 +59,22 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.NonNull;
+
import com.android.server.TelephonyRegistry;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@RunWith(AndroidTestingRunner.class)
@@ -66,59 +82,82 @@ import java.util.concurrent.atomic.AtomicInteger;
public class TelephonyRegistryTest extends TelephonyTest {
@Mock
private SubscriptionInfo mMockSubInfo;
- private PhoneStateListenerWrapper mPhoneStateListener;
+ private TelephonyCallbackWrapper mTelephonyCallback;
+ private List<LinkCapacityEstimate> mLinkCapacityEstimateList;
private TelephonyRegistry mTelephonyRegistry;
private PhoneCapability mPhoneCapability;
private int mActiveSubId;
private TelephonyDisplayInfo mTelephonyDisplayInfo;
private int mSrvccState = -1;
private int mRadioPowerState = RADIO_POWER_UNAVAILABLE;
- // All events contribute to TelephonyRegistry.ENFORCE_PHONE_STATE_PERMISSION_MASK
- private static final Map<Integer, String> READ_PHONE_STATE_EVENTS = Map.of(
- PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR,
- "PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR",
- PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR,
- "PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR",
- PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST,
- "PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST");
-
- // All events contribute to TelephonyRegistry.PRECISE_PHONE_STATE_PERMISSION_MASK
- private static final Map<Integer, String> READ_PRECISE_PHONE_STATE_EVENTS = Map.of(
- PhoneStateListener.LISTEN_PRECISE_CALL_STATE,
- "PhoneStateListener.LISTEN_PRECISE_CALL_STATE",
- PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE,
- "PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE",
- PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES,
- "PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES",
- PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED,
- "PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED",
- PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES,
- "PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES",
- PhoneStateListener.LISTEN_REGISTRATION_FAILURE,
- "PhoneStateListener.LISTEN_REGISTRATION_FAILURE",
- PhoneStateListener.LISTEN_BARRING_INFO,
- "PhoneStateListener.LISTEN_BARRING_INFO");
-
- // All events contribute to TelephonyRegistry.PREVILEGED_PHONE_STATE_PERMISSION_MASK
+ private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+ private TelephonyRegistry.ConfigurationProvider mMockConfigurationProvider;
+ private CellLocation mCellLocation;
+
+ // All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired
+ private static final Set<Integer> READ_PHONE_STATE_EVENTS;
+ static {
+ READ_PHONE_STATE_EVENTS = new HashSet<>();
+ READ_PHONE_STATE_EVENTS.add(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED);
+ READ_PHONE_STATE_EVENTS.add(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED);
+ READ_PHONE_STATE_EVENTS.add(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED);
+ }
+
+ // All events contribute to TelephonyRegistry#isPrecisePhoneStatePermissionRequired
+ private static final Set<Integer> READ_PRECISE_PHONE_STATE_EVENTS;
+ static {
+ READ_PRECISE_PHONE_STATE_EVENTS = new HashSet<>();
+ READ_PRECISE_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(TelephonyCallback.EVENT_REGISTRATION_FAILURE);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED);
+ READ_PRECISE_PHONE_STATE_EVENTS.add(TelephonyCallback.EVENT_DATA_ENABLED_CHANGED);
+ }
+
+ // All events contribute to TelephonyRegistry#isPrivilegedPhoneStatePermissionRequired
// TODO: b/148021947 will create the permission group with PREVILIGED_STATE_PERMISSION_MASK
- private static final Map<Integer, String> READ_PREVILIGED_PHONE_STATE_EVENTS = Map.of(
- PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED,
- "PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED",
- PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT,
- "PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT",
- PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED,
- "PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED",
- PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE,
- "PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE");
-
- // All events contribute to TelephonyRegistry.READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK
- private static final Map<Integer, String> READ_ACTIVE_EMERGENCY_SESSION_EVENTS = Map.of(
- PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL,
- "PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL",
- PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS,
- "PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS");
-
- public class PhoneStateListenerWrapper extends PhoneStateListener {
+ private static final Set<Integer> READ_PRIVILEGED_PHONE_STATE_EVENTS;
+ static {
+ READ_PRIVILEGED_PHONE_STATE_EVENTS = new HashSet<>();
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.add( TelephonyCallback.EVENT_SRVCC_STATE_CHANGED);
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.add( TelephonyCallback.EVENT_OEM_HOOK_RAW);
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.add( TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED);
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.add(
+ TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
+ }
+
+ // All events contribute to TelephonyRegistry#isActiveEmergencySessionPermissionRequired
+ private static final Set<Integer> READ_ACTIVE_EMERGENCY_SESSION_EVENTS;
+ static {
+ READ_ACTIVE_EMERGENCY_SESSION_EVENTS = new HashSet<>();
+ READ_ACTIVE_EMERGENCY_SESSION_EVENTS.add(
+ TelephonyCallback.EVENT_OUTGOING_EMERGENCY_CALL);
+ READ_ACTIVE_EMERGENCY_SESSION_EVENTS.add(
+ TelephonyCallback.EVENT_OUTGOING_EMERGENCY_SMS);
+ }
+
+ public class TelephonyCallbackWrapper extends TelephonyCallback implements
+ TelephonyCallback.SrvccStateListener,
+ TelephonyCallback.PhoneCapabilityListener,
+ TelephonyCallback.ActiveDataSubscriptionIdListener,
+ TelephonyCallback.RadioPowerStateListener,
+ TelephonyCallback.PreciseDataConnectionStateListener,
+ TelephonyCallback.DisplayInfoListener,
+ TelephonyCallback.LinkCapacityEstimateChangedListener,
+ TelephonyCallback.PhysicalChannelConfigListener,
+ TelephonyCallback.CellLocationListener {
// This class isn't mockable to get invocation counts because the IBinder is null and
// crashes the TelephonyRegistry. Make a cheesy verify(times()) alternative.
public AtomicInteger invocationCount = new AtomicInteger(0);
@@ -152,6 +191,22 @@ public class TelephonyRegistryTest extends TelephonyTest {
public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfo) {
mTelephonyDisplayInfo = displayInfo;
}
+
+ @Override
+ public void onLinkCapacityEstimateChanged(
+ List<LinkCapacityEstimate> linkCapacityEstimateList) {
+ mLinkCapacityEstimateList = linkCapacityEstimateList;
+ }
+
+ @Override
+ public void onCellLocationChanged(CellLocation location) {
+ mCellLocation = location;
+ }
+
+ @Override
+ public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs) {
+ mPhysicalChannelConfigs = configs;
+ }
}
private void addTelephonyRegistryService() {
@@ -159,17 +214,34 @@ public class TelephonyRegistryTest extends TelephonyTest {
mTelephonyRegistry.systemRunning();
}
+ private Executor mSimpleExecutor = new Executor() {
+ @Override
+ public void execute(Runnable r) {
+ r.run();
+ }
+ };
+
@Before
public void setUp() throws Exception {
super.setUp("TelephonyRegistryTest");
- TelephonyRegistry.ConfigurationProvider mockConfigurationProvider =
- mock(TelephonyRegistry.ConfigurationProvider.class);
- when(mockConfigurationProvider.getRegistrationLimit()).thenReturn(-1);
- when(mockConfigurationProvider.isRegistrationLimitEnabledInPlatformCompat(anyInt()))
+ mMockConfigurationProvider = mock(TelephonyRegistry.ConfigurationProvider.class);
+ when(mMockConfigurationProvider.getRegistrationLimit()).thenReturn(-1);
+ when(mMockConfigurationProvider.isRegistrationLimitEnabledInPlatformCompat(anyInt()))
.thenReturn(false);
- mTelephonyRegistry = new TelephonyRegistry(mContext, mockConfigurationProvider);
+ when(mMockConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+ anyString(), any())).thenReturn(false);
+ when(mMockConfigurationProvider.isActiveDataSubIdReadPhoneStateEnforcedInPlatformCompat(
+ anyString(), any())).thenReturn(false);
+ when(mMockConfigurationProvider.isCellInfoReadPhoneStateEnforcedInPlatformCompat(
+ anyString(), any())).thenReturn(false);
+ when(mMockConfigurationProvider.isDisplayInfoReadPhoneStateEnforcedInPlatformCompat(
+ anyString(), any())).thenReturn(false);
+ when(mMockConfigurationProvider.isDisplayInfoNrAdvancedSupported(
+ anyString(), any())).thenReturn(false);
+ mTelephonyRegistry = new TelephonyRegistry(mContext, mMockConfigurationProvider);
addTelephonyRegistryService();
- mPhoneStateListener = new PhoneStateListenerWrapper();
+ mTelephonyCallback = new TelephonyCallbackWrapper();
+ mTelephonyCallback.init(mSimpleExecutor);
processAllMessages();
assertEquals(mTelephonyRegistry.asBinder(),
ServiceManager.getService("telephony.registry"));
@@ -186,16 +258,16 @@ public class TelephonyRegistryTest extends TelephonyTest {
doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
// mTelephonyRegistry.listen with notifyNow = true should trigger callback immediately.
- PhoneCapability phoneCapability = new PhoneCapability(1, 2, 3, null, false);
+ PhoneCapability phoneCapability = new PhoneCapability(1, 2, null, false, new int[0]);
+ int[] events = {TelephonyCallback.EVENT_PHONE_CAPABILITY_CHANGED};
mTelephonyRegistry.notifyPhoneCapabilityChanged(phoneCapability);
- mTelephonyRegistry.listenWithFeature(mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- LISTEN_PHONE_CAPABILITY_CHANGE, true);
+ mTelephonyRegistry.listenWithEventList(0, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
processAllMessages();
assertEquals(phoneCapability, mPhoneCapability);
// notifyPhoneCapabilityChanged with a new capability. Callback should be triggered.
- phoneCapability = new PhoneCapability(3, 2, 2, null, false);
+ phoneCapability = new PhoneCapability(3, 2, null, false, new int[0]);
mTelephonyRegistry.notifyPhoneCapabilityChanged(phoneCapability);
processAllMessages();
assertEquals(phoneCapability, mPhoneCapability);
@@ -206,12 +278,12 @@ public class TelephonyRegistryTest extends TelephonyTest {
public void testActiveDataSubChanged() {
// mTelephonyRegistry.listen with notifyNow = true should trigger callback immediately.
int[] activeSubs = {0, 1, 2};
+ int[] events = {TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED};
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(activeSubs);
int activeSubId = 0;
mTelephonyRegistry.notifyActiveDataSubIdChanged(activeSubId);
- mTelephonyRegistry.listenWithFeature(mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE, true);
+ mTelephonyRegistry.listenWithEventList(activeSubId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
processAllMessages();
assertEquals(activeSubId, mActiveSubId);
@@ -234,11 +306,11 @@ public class TelephonyRegistryTest extends TelephonyTest {
doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
int srvccState = TelephonyManager.SRVCC_STATE_HANDOVER_STARTED;
+ int[] events = {TelephonyCallback.EVENT_SRVCC_STATE_CHANGED};
mTelephonyRegistry.notifySrvccStateChanged(1 /*subId*/, srvccState);
// Should receive callback when listen is called that contains the latest notify result.
- mTelephonyRegistry.listenForSubscriber(1 /*subId*/, mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- LISTEN_SRVCC_STATE_CHANGED, true);
+ mTelephonyRegistry.listenWithEventList(1 /*subId*/, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
processAllMessages();
assertEquals(srvccState, mSrvccState);
@@ -259,11 +331,11 @@ public class TelephonyRegistryTest extends TelephonyTest {
// Clear all permission grants for test.
mContextFixture.addCallingOrSelfPermission("");
int srvccState = TelephonyManager.SRVCC_STATE_HANDOVER_STARTED;
+ int[] events = {TelephonyCallback.EVENT_SRVCC_STATE_CHANGED};
mTelephonyRegistry.notifySrvccStateChanged(0 /*subId*/, srvccState);
try {
- mTelephonyRegistry.listenForSubscriber(0 /*subId*/, mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- LISTEN_SRVCC_STATE_CHANGED, true);
+ mTelephonyRegistry.listenWithEventList(0 /*subId*/, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
fail();
} catch (SecurityException e) {
// pass test!
@@ -275,9 +347,9 @@ public class TelephonyRegistryTest extends TelephonyTest {
*/
@Test
public void testMultiSimConfigChange() {
- mTelephonyRegistry.listenForSubscriber(1, mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- LISTEN_RADIO_POWER_STATE_CHANGED, true);
+ int[] events = {TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED};
+ mTelephonyRegistry.listenWithEventList(1, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
processAllMessages();
assertEquals(RADIO_POWER_UNAVAILABLE, mRadioPowerState);
@@ -307,50 +379,108 @@ public class TelephonyRegistryTest extends TelephonyTest {
@Test
public void testPreciseDataConnectionStateChanged() {
final int subId = 1;
+ int[] events = {TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED};
doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
// Initialize the PSL with a PreciseDataConnection
mTelephonyRegistry.notifyDataConnectionForSubscriber(
- /*phoneId*/ 0, subId, ApnSetting.TYPE_DEFAULT,
- new PreciseDataConnectionState(
- 0, 0, 0, "default", new LinkProperties(), 0, null));
- mTelephonyRegistry.listenForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE, true);
+ /*phoneId*/ 0, subId,
+ new PreciseDataConnectionState.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setId(1)
+ .setState(TelephonyManager.DATA_CONNECTED)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setApnSetting(new ApnSetting.Builder()
+ .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
+ .setApnName("default")
+ .setEntryName("default")
+ .build())
+ .setLinkProperties(new LinkProperties())
+ .setFailCause(0)
+ .build());
+ mTelephonyRegistry.listenWithEventList(subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
processAllMessages();
// Verify that the PDCS is reported for the only APN
- assertEquals(mPhoneStateListener.invocationCount.get(), 1);
+ assertEquals(1, mTelephonyCallback.invocationCount.get());
// Add IMS APN and verify that the listener is invoked for the IMS APN
mTelephonyRegistry.notifyDataConnectionForSubscriber(
- /*phoneId*/ 0, subId, ApnSetting.TYPE_IMS,
- new PreciseDataConnectionState(
- 0, 0, 0, "ims", new LinkProperties(), 0, null));
+ /*phoneId*/ 0, subId,
+ new PreciseDataConnectionState.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setId(2)
+ .setState(TelephonyManager.DATA_CONNECTED)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setApnSetting(new ApnSetting.Builder()
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setApnName("ims")
+ .setEntryName("ims")
+ .build())
+ .setLinkProperties(new LinkProperties())
+ .setFailCause(0)
+ .build());
processAllMessages();
- assertEquals(mPhoneStateListener.invocationCount.get(), 2);
+ assertEquals(mTelephonyCallback.invocationCount.get(), 2);
// Unregister the listener
- mTelephonyRegistry.listenForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- PhoneStateListener.LISTEN_NONE, true);
+ mTelephonyRegistry.listenWithEventList(subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, new int[0], true);
processAllMessages();
// Re-register the listener and ensure that both APN types are reported
- mTelephonyRegistry.listenForSubscriber(subId, mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE, true);
+ mTelephonyRegistry.listenWithEventList(subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
processAllMessages();
- assertEquals(mPhoneStateListener.invocationCount.get(), 4);
+ assertEquals(4, mTelephonyCallback.invocationCount.get());
// Send a duplicate event to the TelephonyRegistry and verify that the listener isn't
// invoked.
mTelephonyRegistry.notifyDataConnectionForSubscriber(
- /*phoneId*/ 0, subId, ApnSetting.TYPE_IMS,
- new PreciseDataConnectionState(
- 0, 0, 0, "ims", new LinkProperties(), 0, null));
+ /*phoneId*/ 0, subId,
+ new PreciseDataConnectionState.Builder()
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setId(2)
+ .setState(TelephonyManager.DATA_CONNECTED)
+ .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+ .setApnSetting(new ApnSetting.Builder()
+ .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+ .setApnName("ims")
+ .setEntryName("ims")
+ .build())
+ .setLinkProperties(new LinkProperties())
+ .setFailCause(0)
+ .build());
processAllMessages();
- assertEquals(mPhoneStateListener.invocationCount.get(), 4);
+ assertEquals(4, mTelephonyCallback.invocationCount.get());
+ }
+
+ @Test
+ public void testPhysicalChannelConfigChanged() {
+ // Return a slotIndex / phoneId of 0 for all sub ids given.
+ doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+ doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+
+ final int subId = 1;
+ int[] events = {TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED};
+ // Construct PhysicalChannelConfig with minimum fields set (The default value for
+ // frequencyRange and band fields throw IAE)
+ PhysicalChannelConfig config = new PhysicalChannelConfig.Builder()
+ .setFrequencyRange(FREQUENCY_RANGE_LOW)
+ .setBand(1)
+ .setPhysicalCellId(2)
+ .build();
+ List<PhysicalChannelConfig> configs = new ArrayList<>(1);
+ configs.add(config);
+
+ mTelephonyRegistry.notifyPhysicalChannelConfigForSubscriber(0, subId, configs);
+ mTelephonyRegistry.listenWithEventList(subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+ processAllMessages();
+
+ assertNotNull(mPhysicalChannelConfigs);
+ assertEquals(PHYSICAL_CELL_ID_UNKNOWN, mPhysicalChannelConfigs.get(0).getPhysicalCellId());
}
/**
@@ -360,37 +490,86 @@ public class TelephonyRegistryTest extends TelephonyTest {
public void testReadPhoneStatePermission() {
// Clear all permission grants for test.
mContextFixture.addCallingOrSelfPermission("");
- for (Map.Entry<Integer, String> entry : READ_PHONE_STATE_EVENTS.entrySet()) {
- assertSecurityExceptionThrown(entry.getKey(), entry.getValue());
- }
+ assertSecurityExceptionThrown(
+ READ_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
- // Grant permssion
+ // Grant permission
mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE);
- for (Map.Entry<Integer, String> entry : READ_PHONE_STATE_EVENTS.entrySet()) {
- assertSecurityExceptionNotThrown(entry.getKey(), entry.getValue());
- }
+ assertSecurityExceptionNotThrown(
+ READ_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
+ }
+
+ /**
+ * Test enforcement of READ_PHONE_STATE for call state related events.
+ */
+ @Test
+ public void testCallStateChangedPermission() {
+ int[] events = new int[] {TelephonyCallback.EVENT_CALL_STATE_CHANGED,
+ TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED};
+ // Disable change ID for READ_PHONE_STATE enforcement
+ when(mMockConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+ anyString(), any())).thenReturn(false);
+ // Start without READ_PHONE_STATE permission
+ mContextFixture.addCallingOrSelfPermission("");
+ assertSecurityExceptionNotThrown(events);
+ // Grant permission
+ mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE);
+ assertSecurityExceptionNotThrown(events);
+ //Enable READ_PHONE_STATE enforcement
+ when(mMockConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+ anyString(), any())).thenReturn(true);
+ assertSecurityExceptionNotThrown(events);
+ // revoke READ_PHONE_STATE permission
+ mContextFixture.removeCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE);
+ assertSecurityExceptionThrown(events);
+
}
/**
* Test listen to events that require READ_PRECISE_PHONE_STATE permission.
*/
- // FIXME(b/159082270) - Simply not granting location permission doesn't fix the test because
- // Location is soft-denied to apps that aren't in the foreground, and soft-denial currently
- // short-circuits the test.
- @Ignore("Skip due to b/159082270")
@Test
public void testReadPrecisePhoneStatePermission() {
// Clear all permission grants for test.
mContextFixture.addCallingOrSelfPermission("");
- for (Map.Entry<Integer, String> entry : READ_PRECISE_PHONE_STATE_EVENTS.entrySet()) {
- assertSecurityExceptionThrown(entry.getKey(), entry.getValue());
- }
+ // Many of the events require LOCATION permission, but without READ_PHONE_STATE, they will
+ // still throw exceptions. Since this test is testing READ_PRECISE_PHONE_STATE, all other
+ // permissions should be granted up-front.
+ mContextFixture.addCallingOrSelfPermission(
+ android.Manifest.permission.READ_PHONE_STATE);
+ mContextFixture.addCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_FINE_LOCATION);
+ assertSecurityExceptionThrown(
+ READ_PRECISE_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
- // Grant permssion
+ // Grant permission
mContextFixture.addCallingOrSelfPermission(
android.Manifest.permission.READ_PRECISE_PHONE_STATE);
- for (Map.Entry<Integer, String> entry : READ_PRECISE_PHONE_STATE_EVENTS.entrySet()) {
- assertSecurityExceptionNotThrown(entry.getKey(), entry.getValue());
+ assertSecurityExceptionNotThrown(
+ READ_PRECISE_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
+
+ }
+
+ /**
+ * Test a bit-fiddling method in TelephonyRegistry
+ */
+ @Test
+ public void testGetApnTypesStringFromBitmask() {
+ {
+ int mask = 0;
+ assertEquals("", TelephonyRegistry.getApnTypesStringFromBitmask(mask));
+ }
+
+ {
+ int mask = ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS;
+ assertEquals(String.join(
+ ",", ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING),
+ TelephonyRegistry.getApnTypesStringFromBitmask(mask));
+ }
+
+ {
+ int mask = 1 << 31;
+ assertEquals("", TelephonyRegistry.getApnTypesStringFromBitmask(mask));
}
}
@@ -401,16 +580,14 @@ public class TelephonyRegistryTest extends TelephonyTest {
public void testReadPrivilegedPhoneStatePermission() {
// Clear all permission grants for test.
mContextFixture.addCallingOrSelfPermission("");
- for (Map.Entry<Integer, String> entry : READ_PREVILIGED_PHONE_STATE_EVENTS.entrySet()) {
- assertSecurityExceptionThrown(entry.getKey(), entry.getValue());
- }
+ assertSecurityExceptionThrown(
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
- // Grant permssion
+ // Grant permission
mContextFixture.addCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
- for (Map.Entry<Integer, String> entry : READ_PREVILIGED_PHONE_STATE_EVENTS.entrySet()) {
- assertSecurityExceptionNotThrown(entry.getKey(), entry.getValue());
- }
+ assertSecurityExceptionNotThrown(
+ READ_PRIVILEGED_PHONE_STATE_EVENTS.stream().mapToInt(i -> i).toArray());
}
/**
@@ -420,16 +597,14 @@ public class TelephonyRegistryTest extends TelephonyTest {
public void testReadActiveEmergencySessionPermission() {
// Clear all permission grants for test.
mContextFixture.addCallingOrSelfPermission("");
- for (Map.Entry<Integer, String> entry : READ_ACTIVE_EMERGENCY_SESSION_EVENTS.entrySet()) {
- assertSecurityExceptionThrown(entry.getKey(), entry.getValue());
- }
+ assertSecurityExceptionThrown(
+ READ_ACTIVE_EMERGENCY_SESSION_EVENTS.stream().mapToInt(i -> i).toArray());
- // Grant permssion
+ // Grant permission
mContextFixture.addCallingOrSelfPermission(
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION);
- for (Map.Entry<Integer, String> entry : READ_ACTIVE_EMERGENCY_SESSION_EVENTS.entrySet()) {
- assertSecurityExceptionNotThrown(entry.getKey(), entry.getValue());
- }
+ assertSecurityExceptionNotThrown(
+ READ_ACTIVE_EMERGENCY_SESSION_EVENTS.stream().mapToInt(i -> i).toArray());
}
@Test
@@ -438,9 +613,9 @@ public class TelephonyRegistryTest extends TelephonyTest {
.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 12)
.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0));
processAllMessages();
- mTelephonyRegistry.listenForSubscriber(2, mContext.getOpPackageName(),
- mContext.getAttributionTag(), mPhoneStateListener.callback,
- PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED, false);
+ int[] events = {TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED};
+ mTelephonyRegistry.listenWithEventList(2, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, false);
// Notify with invalid subId on default phone. Should NOT trigger callback.
TelephonyDisplayInfo displayInfo = new TelephonyDisplayInfo(0, 0);
@@ -454,21 +629,99 @@ public class TelephonyRegistryTest extends TelephonyTest {
assertEquals(displayInfo, mTelephonyDisplayInfo);
}
- private void assertSecurityExceptionThrown(int event, String eventDesc) {
+ @Test
+ public void testNotifyCellLocationForSubscriberByUserSwitched() throws RemoteException {
+ final int phoneId = 0;
+ final int subId = 1;
+
+ // Return a slotIndex / phoneId of 0 for subId 1.
+ doReturn(new int[] {subId}).when(mSubscriptionController).getSubId(phoneId);
+ doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(subId);
+ doReturn(phoneId).when(mMockSubInfo).getSimSlotIndex();
+ mServiceManagerMockedServices.put("isub", mSubscriptionController);
+ doReturn(mSubscriptionController).when(mSubscriptionController)
+ .queryLocalInterface(anyString());
+
+ UserInfo userInfo = new UserInfo(UserHandle.myUserId(), "" /* name */, 0 /* flags */);
+ doReturn(userInfo.id).when(mIActivityManager).getCurrentUserId();
+
+ doReturn(true).when(mLocationManager).isLocationEnabledForUser(any(UserHandle.class));
+
+ CellIdentity cellIdentity = new CellIdentityGsm(-1, -1, -1, -1, null, null, null, null,
+ Collections.emptyList());
+ mTelephonyRegistry.notifyCellLocationForSubscriber(subId, cellIdentity);
+ processAllMessages();
+
+ // Listen to EVENT_CELL_LOCATION_CHANGED for the current user Id.
+ int[] events = {TelephonyCallback.EVENT_CELL_LOCATION_CHANGED};
+ mTelephonyRegistry.listenWithEventList(subId, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, events, false);
+
+ // Broadcast ACTION_USER_SWITCHED for USER_SYSTEM. Callback should be triggered.
+ mCellLocation = null;
+ mContext.sendBroadcast(new Intent(Intent.ACTION_USER_SWITCHED));
+
+ processAllMessages();
+ assertEquals(cellIdentity.asCellLocation(), mCellLocation);
+
+ // Broadcast ACTION_USER_SWITCHED for the current user Id + 1. Callback shouldn't be
+ // triggered.
+ userInfo.id++;
+ doReturn(userInfo.id).when(mIActivityManager).getCurrentUserId();
+ mCellLocation = null;
+ mContext.sendBroadcast(new Intent(Intent.ACTION_USER_SWITCHED));
+
+ processAllMessages();
+ assertEquals(null, mCellLocation);
+ }
+
+ private void assertSecurityExceptionThrown(int[] event) {
try {
- mTelephonyRegistry.listen(mContext.getOpPackageName(),
- mPhoneStateListener.callback, event, true);
- fail("SecurityException should throw when listen " + eventDesc + " without permission");
+ mTelephonyRegistry.listenWithEventList(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, event, true);
+ fail("SecurityException should throw without permission");
} catch (SecurityException expected) {
}
}
- private void assertSecurityExceptionNotThrown(int event, String eventDesc) {
+ private void assertSecurityExceptionNotThrown(int[] event) {
try {
- mTelephonyRegistry.listen(mContext.getOpPackageName(),
- mPhoneStateListener.callback, event, true);
+ mTelephonyRegistry.listenWithEventList(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback, event, true);
} catch (SecurityException unexpected) {
- fail("SecurityException thrown when listen " + eventDesc + " with permission");
+ fail("SecurityException thrown with permission");
}
}
+
+ @Test
+ public void testNotifyLinkCapacityEstimateChanged() {
+ mContext.sendBroadcast(new Intent(ACTION_DEFAULT_SUBSCRIPTION_CHANGED)
+ .putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 2)
+ .putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0));
+ processAllMessages();
+ int[] events = {TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED};
+ mTelephonyRegistry.listenWithEventList(2, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), mTelephonyCallback.callback,
+ events, false);
+
+ // Notify with invalid subId / phoneId on default phone. Should NOT trigger callback.
+ List<LinkCapacityEstimate> lceList = new ArrayList<>();
+ lceList.add(new LinkCapacityEstimate(LinkCapacityEstimate.LCE_TYPE_COMBINED, 4000,
+ LinkCapacityEstimate.INVALID));
+ mTelephonyRegistry.notifyLinkCapacityEstimateChanged(1, INVALID_SUBSCRIPTION_ID, lceList);
+ processAllMessages();
+ assertEquals(null, mLinkCapacityEstimateList);
+
+ // Notify with invalid phoneId. Should NOT trigger callback.
+ mTelephonyRegistry.notifyLinkCapacityEstimateChanged(2, 2, lceList);
+ processAllMessages();
+ assertEquals(null, mLinkCapacityEstimateList);
+
+ // Notify with the matching subId on default phone. Should trigger callback.
+ mTelephonyRegistry.notifyLinkCapacityEstimateChanged(0, 2, lceList);
+ processAllMessages();
+ assertEquals(lceList, mLinkCapacityEstimateList);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index b8277ad9e5..c101b30ae2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.eq;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.usage.NetworkStatsManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -40,7 +41,13 @@ import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.location.LocationManager;
import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnManager;
+import android.net.vcn.VcnNetworkPolicyResult;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -51,14 +58,17 @@ import android.os.MessageQueue;
import android.os.RegistrantList;
import android.os.ServiceManager;
import android.os.UserManager;
-import android.permission.PermissionManager;
+import android.permission.LegacyPermissionManager;
import android.provider.BlockedNumberContract;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
+import android.telephony.CellLocation;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyRegistryManager;
@@ -78,28 +88,34 @@ import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriManager;
import com.android.internal.telephony.dataconnection.DataEnabledOverride;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
+import com.android.internal.telephony.dataconnection.DataThrottler;
import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.metrics.ImsStats;
import com.android.internal.telephony.metrics.MetricsCollector;
import com.android.internal.telephony.metrics.PersistAtomsStorage;
+import com.android.internal.telephony.metrics.SmsStats;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.IsimUiccRecords;
+import com.android.internal.telephony.uicc.PinStorage;
import com.android.internal.telephony.uicc.RuimRecords;
import com.android.internal.telephony.uicc.SIMRecords;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.internal.telephony.uicc.UiccSlot;
import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.LegacyPermissionManagerService;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -195,7 +211,7 @@ public abstract class TelephonyTest {
@Mock
protected PackageManagerService mMockPackageManager;
@Mock
- protected PermissionManagerService mMockPermissionManager;
+ protected LegacyPermissionManagerService mMockLegacyPermissionManager;
protected NetworkRegistrationInfo mNetworkRegistrationInfo =
new NetworkRegistrationInfo.Builder()
@@ -294,6 +310,28 @@ public abstract class TelephonyTest {
protected PersistAtomsStorage mPersistAtomsStorage;
@Mock
protected MetricsCollector mMetricsCollector;
+ @Mock
+ protected SmsStats mSmsStats;
+ @Mock
+ protected DataThrottler mDataThrottler;
+ @Mock
+ protected SignalStrength mSignalStrength;
+ @Mock
+ protected WifiManager mWifiManager;
+ @Mock
+ protected WifiInfo mWifiInfo;
+ @Mock
+ protected ImsStats mImsStats;
+ @Mock
+ protected LinkBandwidthEstimator mLinkBandwidthEstimator;
+ @Mock
+ protected PinStorage mPinStorage;
+ @Mock
+ protected LocationManager mLocationManager;
+ @Mock
+ protected CellIdentity mCellIdentity;
+ @Mock
+ protected CellLocation mCellLocation;
protected ActivityManager mActivityManager;
protected ImsCallProfile mImsCallProfile;
@@ -306,6 +344,8 @@ public abstract class TelephonyTest {
protected AppOpsManager mAppOpsManager;
protected CarrierConfigManager mCarrierConfigManager;
protected UserManager mUserManager;
+ protected KeyguardManager mKeyguardManager;
+ protected VcnManager mVcnManager;
protected SimulatedCommands mSimulatedCommands;
protected ContextFixture mContextFixture;
protected Context mContext;
@@ -317,7 +357,6 @@ public abstract class TelephonyTest {
protected List<TestableLooper> mTestableLoopers = new ArrayList<>();
protected TestableLooper mTestableLooper;
- protected HashMap<Integer, ImsManager> mImsManagerInstances = new HashMap<>();
private HashMap<InstanceKey, Object> mOldInstances = new HashMap<InstanceKey, Object>();
private LinkedList<InstanceKey> mInstanceKeys = new LinkedList<InstanceKey>();
@@ -416,6 +455,9 @@ public abstract class TelephonyTest {
MockitoAnnotations.initMocks(this);
TelephonyManager.disableServiceHandleCaching();
SubscriptionController.disableCaching();
+ // For testing do not allow Log.WTF as it can cause test process to crash
+ Log.setWtfHandler((tagString, what, system) -> Log.d(TAG, "WTF captured, ignoring. Tag: "
+ + tagString + ", exception: " + what));
mPhones = new Phone[] {mPhone};
mImsCallProfile = new ImsCallProfile();
@@ -429,6 +471,9 @@ public abstract class TelephonyTest {
BlockedNumberContract.AUTHORITY, mFakeBlockedNumberContentProvider);
mPhone.mCi = mSimulatedCommands;
mCT.mCi = mSimulatedCommands;
+ mCT.mForegroundCall = new GsmCdmaCall(mCT);
+ mCT.mBackgroundCall = new GsmCdmaCall(mCT);
+ mCT.mRingingCall = new GsmCdmaCall(mCT);
doReturn(mUiccCard).when(mPhone).getUiccCard();
doReturn(mUiccProfile).when(mUiccCard).getUiccProfile();
@@ -446,6 +491,9 @@ public abstract class TelephonyTest {
mCarrierConfigManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mVcnManager = mContext.getSystemService(VcnManager.class);
+ mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
//mTelephonyComponentFactory
doReturn(mTelephonyComponentFactory).when(mTelephonyComponentFactory).inject(anyString());
@@ -498,6 +546,8 @@ public abstract class TelephonyTest {
.makeDataEnabledSettings(nullable(Phone.class));
doReturn(mEriManager).when(mTelephonyComponentFactory)
.makeEriManager(nullable(Phone.class), anyInt());
+ doReturn(mLinkBandwidthEstimator).when(mTelephonyComponentFactory)
+ .makeLinkBandwidthEstimator(nullable(Phone.class));
//mPhone
doReturn(mContext).when(mPhone).getContext();
@@ -522,9 +572,15 @@ public abstract class TelephonyTest {
doReturn(mDataEnabledSettings).when(mPhone).getDataEnabledSettings();
doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
+ doReturn(mSignalStrength).when(mPhone).getSignalStrength();
doReturn(mVoiceCallSessionStats).when(mPhone).getVoiceCallSessionStats();
doReturn(mVoiceCallSessionStats).when(mImsPhone).getVoiceCallSessionStats();
+ doReturn(mSmsStats).when(mPhone).getSmsStats();
+ doReturn(mImsStats).when(mImsPhone).getImsStats();
mIccSmsInterfaceManager.mDispatchersController = mSmsDispatchersController;
+ doReturn(mLinkBandwidthEstimator).when(mPhone).getLinkBandwidthEstimator();
+ doReturn(mCellIdentity).when(mPhone).getCurrentCellIdentity();
+ doReturn(mCellLocation).when(mCellIdentity).asCellLocation();
//mUiccController
doReturn(mUiccCardApplication3gpp).when(mUiccController).getUiccCardApplication(anyInt(),
@@ -533,6 +589,7 @@ public abstract class TelephonyTest {
eq(UiccController.APP_FAM_3GPP2));
doReturn(mUiccCardApplicationIms).when(mUiccController).getUiccCardApplication(anyInt(),
eq(UiccController.APP_FAM_IMS));
+ doReturn(mUiccCard).when(mUiccController).getUiccCard(anyInt());
doAnswer(new Answer<IccRecords>() {
public IccRecords answer(InvocationOnMock invocation) {
@@ -549,6 +606,8 @@ public abstract class TelephonyTest {
}
}
}).when(mUiccController).getIccRecords(anyInt(), anyInt());
+ doReturn(new UiccSlot[] {}).when(mUiccController).getUiccSlots();
+ doReturn(mPinStorage).when(mUiccController).getPinStorage();
//UiccCardApplication
doReturn(mSimRecords).when(mUiccCardApplication3gpp).getIccRecords();
@@ -582,7 +641,6 @@ public abstract class TelephonyTest {
doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS).when(mServiceState).
getRilDataRadioTechnology();
doReturn(mPhone).when(mCT).getPhone();
- mImsManagerInstances.put(mPhone.getPhoneId(), mImsManager);
doReturn(mImsEcbm).when(mImsManager).getEcbmInterface();
doReturn(mPhone).when(mInboundSmsHandler).getPhone();
doReturn(mImsCallProfile).when(mImsCall).getCallProfile();
@@ -600,8 +658,8 @@ public abstract class TelephonyTest {
mSST.mRestrictedState = mRestrictedState;
mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
mServiceManagerMockedServices.put("package", mMockPackageManager);
- mServiceManagerMockedServices.put("permissionmgr", mMockPermissionManager);
- logd("mMockPermissionManager replaced");
+ mServiceManagerMockedServices.put("legacy_permission", mMockLegacyPermissionManager);
+ logd("mMockLegacyPermissionManager replaced");
doReturn(new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN})
.when(mTransportManager).getAvailableTransports();
@@ -613,6 +671,18 @@ public abstract class TelephonyTest {
doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
anyInt(), anyInt());
doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
+ doReturn(2).when(mSignalStrength).getLevel();
+
+ // WiFi
+ doReturn(mWifiInfo).when(mWifiManager).getConnectionInfo();
+ doReturn(2).when(mWifiManager).calculateSignalLevel(anyInt());
+ doReturn(4).when(mWifiManager).getMaxSignalLevel();
+
+ doAnswer(invocation -> {
+ NetworkCapabilities nc = invocation.getArgument(0);
+ return new VcnNetworkPolicyResult(
+ false /* isTearDownRequested */, nc);
+ }).when(mVcnManager).applyVcnNetworkPolicy(any(), any());
//SIM
doReturn(1).when(mTelephonyManager).getSimCount();
@@ -631,6 +701,8 @@ public abstract class TelephonyTest {
Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 1);
Settings.Global.putInt(resolver,
Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 1);
+ doReturn(mDataThrottler).when(mDcTracker).getDataThrottler();
+ doReturn(-1L).when(mDataThrottler).getRetryTime(anyInt());
// CellularNetworkValidator
doReturn(SubscriptionManager.INVALID_PHONE_INDEX)
@@ -640,6 +712,7 @@ public abstract class TelephonyTest {
// Metrics
doReturn(null).when(mContext).getFileStreamPath(anyString());
doReturn(mPersistAtomsStorage).when(mMetricsCollector).getAtomsStorage();
+ doReturn(mWifiManager).when(mContext).getSystemService(eq(Context.WIFI_SERVICE));
//Use reflection to mock singletons
replaceInstance(CallManager.class, "INSTANCE", null, mCallManager);
@@ -647,7 +720,6 @@ public abstract class TelephonyTest {
mTelephonyComponentFactory);
replaceInstance(UiccController.class, "mInstance", null, mUiccController);
replaceInstance(CdmaSubscriptionSourceManager.class, "sInstance", null, mCdmaSSM);
- replaceInstance(ImsManager.class, "sImsManagerInstances", null, mImsManagerInstances);
replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
replaceInstance(ActivityManager.class, "IActivityManagerSingleton", null,
@@ -796,12 +868,21 @@ public abstract class TelephonyTest {
// TelephonyPermissions uses a SystemAPI to check if the calling package meets any of the
// generic requirements for device identifier access (currently READ_PRIVILEGED_PHONE_STATE,
- // appop, and device / profile owner checks. This sets up the PermissionManager to return
+ // appop, and device / profile owner checks). This sets up the PermissionManager to return
// that access requirements are met.
setIdentifierAccess(true);
- PermissionManager permissionManager = new PermissionManager(mContext, null,
- mMockPermissionManager);
- doReturn(permissionManager).when(mContext).getSystemService(eq(Context.PERMISSION_SERVICE));
+ LegacyPermissionManager legacyPermissionManager =
+ new LegacyPermissionManager(mMockLegacyPermissionManager);
+ doReturn(legacyPermissionManager).when(mContext)
+ .getSystemService(Context.LEGACY_PERMISSION_SERVICE);
+ // Also make sure all appop checks fails, to not interfere tests. Tests should explicitly
+ // mock AppOpManager to return allowed/default mode. Note by default a mock returns 0 which
+ // is MODE_ALLOWED, hence this setup is necessary.
+ doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).noteOpNoThrow(
+ /* op= */ anyString(), /* uid= */ anyInt(),
+ /* packageName= */ nullable(String.class),
+ /* attributionTag= */ nullable(String.class),
+ /* message= */ nullable(String.class));
// TelephonyPermissions queries DeviceConfig to determine if the identifier access
// restrictions should be enabled; this results in a NPE when DeviceConfig uses
@@ -828,9 +909,13 @@ public abstract class TelephonyTest {
protected void setIdentifierAccess(boolean hasAccess) {
doReturn(hasAccess ? PackageManager.PERMISSION_GRANTED
- : PackageManager.PERMISSION_DENIED).when(
- mMockPermissionManager).checkDeviceIdentifierAccess(any(), any(), any(), anyInt(),
- anyInt());
+ : PackageManager.PERMISSION_DENIED).when(mMockLegacyPermissionManager)
+ .checkDeviceIdentifierAccess(any(), any(), any(), anyInt(), anyInt());
+ }
+
+ protected void setPhoneNumberAccess(int value) {
+ doReturn(value).when(mMockLegacyPermissionManager).checkPhoneNumberAccess(any(), any(),
+ any(), anyInt(), anyInt());
}
protected void setCarrierPrivileges(boolean hasCarrierPrivileges) {
@@ -848,6 +933,19 @@ public abstract class TelephonyTest {
mockTelephonyManager).getCarrierPrivilegeStatus(anyInt());
}
+ protected final void waitForDelayedHandlerAction(Handler h, long delayMillis,
+ long timeoutMillis) {
+ final CountDownLatch lock = new CountDownLatch(1);
+ h.postDelayed(lock::countDown, delayMillis);
+ while (lock.getCount() > 0) {
+ try {
+ lock.await(delayMillis + timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ }
+
protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
final CountDownLatch lock = new CountDownLatch(1);
h.post(lock::countDown);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TestExecutorService.java b/tests/telephonytests/src/com/android/internal/telephony/TestExecutorService.java
new file mode 100644
index 0000000000..b2d3315c37
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/TestExecutorService.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class TestExecutorService implements ScheduledExecutorService {
+ private static final String TAG = "TestExecutorService";
+
+ private class CompletedFuture<T> implements Future<T>, ScheduledFuture<T> {
+
+ private final Callable<T> mTask;
+ private final long mDelayMs;
+ private Runnable mRunnable;
+
+ CompletedFuture(Callable<T> task) {
+ mTask = task;
+ mDelayMs = 0;
+ }
+
+ CompletedFuture(Callable<T> task, long delayMs) {
+ mTask = task;
+ mDelayMs = delayMs;
+ }
+
+ CompletedFuture(Runnable task, long delayMs) {
+ mRunnable = task;
+ mTask = (Callable<T>) Executors.callable(task);
+ mDelayMs = delayMs;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ cancelRunnable(mRunnable);
+ return true;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return true;
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ try {
+ return mTask.call();
+ } catch (Exception e) {
+ throw new ExecutionException(e);
+ }
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ try {
+ return mTask.call();
+ } catch (Exception e) {
+ throw new ExecutionException(e);
+ }
+ }
+
+ @Override
+ public long getDelay(TimeUnit unit) {
+ if (unit == TimeUnit.MILLISECONDS) {
+ return mDelayMs;
+ } else {
+ // not implemented
+ return 0;
+ }
+ }
+
+ @Override
+ public int compareTo(Delayed o) {
+ if (o == null) return 1;
+ if (o.getDelay(TimeUnit.MILLISECONDS) > mDelayMs) return -1;
+ if (o.getDelay(TimeUnit.MILLISECONDS) < mDelayMs) return 1;
+ return 0;
+ }
+ }
+
+ private long mClock = 0;
+ private Map<Long, Runnable> mScheduledRunnables = new HashMap<>();
+ private Map<Runnable, Long> mRepeatDuration = new HashMap<>();
+
+ @Override
+ public void shutdown() {
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return null;
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return false;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) {
+ return false;
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return new com.android.internal.telephony.TestExecutorService.CompletedFuture<>(task);
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ task.run();
+ return new com.android.internal.telephony.TestExecutorService.CompletedFuture<>(() -> null);
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+ TimeUnit unit) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ // Schedule the runnable for execution at the specified time.
+ long scheduledTime = getNextExecutionTime(delay, unit);
+ mScheduledRunnables.put(scheduledTime, command);
+
+ Log.i(TAG, "schedule: runnable=" + System.identityHashCode(command) + ", time="
+ + scheduledTime);
+
+ return new com.android.internal.telephony.TestExecutorService.CompletedFuture<Runnable>(
+ command, delay);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
+ TimeUnit unit) {
+ return scheduleWithFixedDelay(command, initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+ long delay, TimeUnit unit) {
+ // Schedule the runnable for execution at the specified time.
+ long nextScheduledTime = getNextExecutionTime(delay, unit);
+ mScheduledRunnables.put(nextScheduledTime, command);
+ mRepeatDuration.put(command, unit.toMillis(delay));
+
+ return new com.android.internal.telephony.TestExecutorService.CompletedFuture<Runnable>(
+ command, delay);
+ }
+
+ private long getNextExecutionTime(long delay, TimeUnit unit) {
+ long delayMillis = unit.toMillis(delay);
+ return mClock + delayMillis;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+
+ /**
+ * Used in unit tests, used to add a delta to the "clock" so that we can fire off scheduled
+ * items and reschedule the repeats.
+ * @param duration The duration (millis) to add to the clock.
+ */
+ public void advanceTime(long duration) {
+ Map<Long, Runnable> nextRepeats = new HashMap<>();
+ List<Runnable> toRun = new ArrayList<>();
+ mClock += duration;
+ Iterator<Map.Entry<Long, Runnable>> iterator = mScheduledRunnables.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<Long, Runnable> entry = iterator.next();
+ if (mClock >= entry.getKey()) {
+ toRun.add(entry.getValue());
+
+ Runnable r = entry.getValue();
+ Log.i(TAG, "advanceTime: runningRunnable=" + System.identityHashCode(r));
+ // If this is a repeating scheduled item, schedule the repeat.
+ if (mRepeatDuration.containsKey(r)) {
+ // schedule next execution
+ nextRepeats.put(mClock + mRepeatDuration.get(r), entry.getValue());
+ }
+ iterator.remove();
+ }
+ }
+
+ // Update things at the end to avoid concurrent access.
+ mScheduledRunnables.putAll(nextRepeats);
+ toRun.forEach(r -> r.run());
+ }
+
+ /**
+ * Used from a {@link CompletedFuture} as defined above to cancel a scheduled task.
+ * @param r The runnable to cancel.
+ */
+ private void cancelRunnable(Runnable r) {
+ Optional<Map.Entry<Long, Runnable>> found = mScheduledRunnables.entrySet().stream()
+ .filter(e -> e.getValue() == r)
+ .findFirst();
+ if (found.isPresent()) {
+ mScheduledRunnables.remove(found.get().getKey());
+ }
+ mRepeatDuration.remove(r);
+ Log.i(TAG, "cancelRunnable: runnable=" + System.identityHashCode(r));
+ }
+}
+
diff --git a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
index 48adb64d41..ca1fca8b0d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
@@ -29,7 +29,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
@@ -92,7 +91,7 @@ public class WapPushOverSmsTest extends TelephonyTest {
eq(android.Manifest.permission.RECEIVE_WAP_PUSH),
eq(AppOpsManager.OPSTR_RECEIVE_WAP_PUSH),
nullable(Bundle.class),
- isNull(BroadcastReceiver.class),
+ isNull(InboundSmsHandler.SmsBroadcastReceiver.class),
eq(UserHandle.SYSTEM),
anyInt());
Intent intent = intentArgumentCaptor.getValue();
@@ -147,7 +146,7 @@ public class WapPushOverSmsTest extends TelephonyTest {
any(String.class),
any(String.class),
any(Bundle.class),
- any(BroadcastReceiver.class),
+ any(InboundSmsHandler.SmsBroadcastReceiver.class),
any(UserHandle.class),
anyInt());
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index a6d30f90af..68831f66d8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -130,18 +130,20 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest {
"1234567890", /* displayAddress */
"This is the message body of a single-part message" /* messageBody */,
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(Cursor.class), anyBoolean());
@@ -234,12 +236,13 @@ public class CdmaInboundSmsHandlerTest extends TelephonyTest {
blockedNumber, /* displayAddress */
"This is the message body of a single-part message" /* messageBody */,
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
transitionFromStartupToIdle();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
index 3d707dacaf..0dc96476ab 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
@@ -65,7 +65,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
msg.isServicePresent = true;
msg.serviceCategory = serviceCategory;
- // dummy address (RIL may generate a different dummy address for broadcasts)
+ // placeholder address (RIL may generate a different placeholder address for broadcasts)
msg.address.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
msg.address.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
msg.address.numberType = CdmaSmsAddress.TON_UNKNOWN;
@@ -601,7 +601,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
msg.isServicePresent = false;
msg.serviceCategory = 0;
- // dummy address (RIL may generate a different dummy address for broadcasts)
+ // placeholder address (RIL may generate a different placeholder address for broadcasts)
msg.address.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
msg.address.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
msg.address.numberType = CdmaSmsAddress.TON_UNKNOWN;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java
new file mode 100644
index 0000000000..5681dc444f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/CommunicatorTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+
+import android.telecom.Connection;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class CommunicatorTest {
+ private List<TransportProtocol> mTransportProtocols = new ArrayList<>();
+ private TransportProtocol.Callback mCallback;
+ private Communicator mCommunicator;
+ @Mock
+ private Communicator.Callback mCommunicatorCallback;
+ @Captor
+ private ArgumentCaptor<Set<Communicator.Message>> mMessagesCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ TransportProtocol protocol1 = getMockTransportProtocol();
+ TransportProtocol protocol2 = getMockTransportProtocol();
+ mTransportProtocols.add(protocol1);
+ mTransportProtocols.add(protocol2);
+ }
+
+ /**
+ * Verifies that we can setup the communicator and negotiate a transport.
+ */
+ @SmallTest
+ @Test
+ public void testNegotiate() {
+ mCommunicator = new Communicator(mTransportProtocols, mCommunicatorCallback);
+ mCommunicator.onStateChanged(null, Connection.STATE_ACTIVE);
+ verify(mTransportProtocols.get(0)).startNegotiation();
+ assertEquals(mTransportProtocols.get(0), mCommunicator.getActiveTransport());
+ // Assume negotiation on the first one failed.
+ mCallback.onNegotiationFailed(mTransportProtocols.get(0));
+ verify(mTransportProtocols.get(1)).startNegotiation();
+ assertEquals(mTransportProtocols.get(1), mCommunicator.getActiveTransport());
+ mCallback.onNegotiationSuccess(mTransportProtocols.get(1));
+ verify(mCommunicatorCallback).onD2DAvailabilitychanged(eq(true));
+ }
+
+ /**
+ * Verifies that D2D negotiation failed callback is invoked when D2D could not be negotiated.
+ */
+ @SmallTest
+ @Test
+ public void testNegotiationFailed() {
+ mCommunicator = new Communicator(mTransportProtocols, mCommunicatorCallback);
+ mCommunicator.onStateChanged(null, Connection.STATE_ACTIVE);
+ verify(mTransportProtocols.get(0)).startNegotiation();
+ assertEquals(mTransportProtocols.get(0), mCommunicator.getActiveTransport());
+ // Assume negotiation on the first one failed.
+ mCallback.onNegotiationFailed(mTransportProtocols.get(0));
+ verify(mTransportProtocols.get(1)).startNegotiation();
+ assertEquals(mTransportProtocols.get(1), mCommunicator.getActiveTransport());
+ // Oops, the second one failed too; not negotiated!
+ mCallback.onNegotiationFailed(mTransportProtocols.get(1));
+ verify(mCommunicatorCallback).onD2DAvailabilitychanged(eq(false));
+ }
+
+ /**
+ * Verifies that D2D negotiation failed callback is invoked when no transports are available.
+ */
+ @SmallTest
+ @Test
+ public void testNegotiationFailedNoProtocols() {
+ mCommunicator = new Communicator(Collections.EMPTY_LIST, mCommunicatorCallback);
+ mCommunicator.onStateChanged(null, Connection.STATE_ACTIVE);
+ verify(mCommunicatorCallback).onD2DAvailabilitychanged(eq(false));
+ }
+
+ /**
+ * Verifies that we can relay messages to send via the active transport.
+ */
+ @SmallTest
+ @Test
+ public void testSendMessage() {
+ testNegotiate();
+ TransportProtocol protocol = mCommunicator.getActiveTransport();
+
+ // Send a couple test messages.
+ ArraySet<Communicator.Message> test = new ArraySet<>();
+ test.add(new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_GOOD));
+ test.add(new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_EVS));
+ mCommunicator.sendMessages(test);
+
+ // Ensure they got relayed to the transport protocol intact.
+ verify(protocol).sendMessages(mMessagesCaptor.capture());
+ Set<Communicator.Message> send = mMessagesCaptor.getValue();
+ assertEquals(test, send);
+ }
+
+ /**
+ * Verifies that we can relay messages received via the active transport to interested parties.
+ */
+ @SmallTest
+ @Test
+ public void testReceiveMessage() {
+ testNegotiate();
+ TransportProtocol protocol = mCommunicator.getActiveTransport();
+
+ // Receive some messages
+ ArraySet<Communicator.Message> test = new ArraySet<>();
+ test.add(new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_GOOD));
+ test.add(new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_EVS));
+ mCallback.onMessagesReceived(test);
+
+ // Ensure they got relayed to the communicator listener.
+ verify(mCommunicatorCallback).onMessagesReceived(mMessagesCaptor.capture());
+ Set<Communicator.Message> send = mMessagesCaptor.getValue();
+ assertEquals(test, send);
+ }
+
+
+ /**
+ * @return a mocked instance of {@link TransportProtocol}.
+ */
+ private TransportProtocol getMockTransportProtocol() {
+ TransportProtocol transportProtocol = Mockito.mock(TransportProtocol.class);
+ doNothing().when(transportProtocol).startNegotiation();
+ doNothing().when(transportProtocol).sendMessages(any());
+ doAnswer(invocation -> {
+ mCallback = (TransportProtocol.Callback) invocation.getArgument(0);
+ return true;
+ }).when(transportProtocol).setCallback(any());
+ return transportProtocol;
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportConversionTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportConversionTest.java
new file mode 100644
index 0000000000..6662bbdb99
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportConversionTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.internal.telephony.TestExecutorService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Verifies bidirectional conversion between {@link Communicator.Message}s and DTMF character
+ * sequences.
+ * This class verifies only the conversion between the two and not the actual sending/receipt. See
+ * {@link DtmfTransportTest} for verification of the send/receive of messages using DTMF.
+ */
+@RunWith(Parameterized.class)
+public class DtmfTransportConversionTest {
+ private static class TestParams {
+ public Communicator.Message commMessage;
+ public Pair<String,String> messageAndValueDigits;
+ public String fullMessage;
+
+ TestParams(Communicator.Message theMessage, Pair<String,String> theDigits,
+ String theFullMessage) {
+ commMessage = theMessage;
+ messageAndValueDigits = theDigits;
+ fullMessage = theFullMessage;
+ }
+
+ public String toString() {
+ return "Params{msg = " + commMessage + ", digits = " + messageAndValueDigits
+ + ", enc=" + fullMessage + "}";
+ }
+ }
+
+ @Mock
+ private DtmfAdapter mDtmfAdapter;
+ @Mock
+ private TransportProtocol.Callback mCallback;
+ @Mock
+ private Timeouts.Adapter mTimeouts;
+ private TestExecutorService mScheduledExecutorService = new TestExecutorService();
+
+ private final TestParams mParams;
+ private DtmfTransport mDtmfTransport;
+
+ public DtmfTransportConversionTest(DtmfTransportConversionTest.TestParams params) {
+ mParams = params;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mDtmfTransport = new DtmfTransport(mDtmfAdapter, mTimeouts, mScheduledExecutorService);
+ mDtmfTransport.setCallback(mCallback);
+ }
+
+ /**
+ * Setup the test cases.
+ *
+ * @return the test cases
+ */
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<DtmfTransportConversionTest.TestParams> generateTestCases() {
+ List<DtmfTransportConversionTest.TestParams> result = new ArrayList<>();
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_LTE),
+ new Pair<>("A", "A"),
+ "AADAD"
+ ));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_IWLAN),
+ new Pair<>("A", "B"),
+ "AADBD"
+ ));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_NR),
+ new Pair<>("A", "C"),
+ "AADCD"
+ ));
+
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_EVS),
+ new Pair<>("B", "A"),
+ "ABDAD"
+ ));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_WB),
+ new Pair<>("B", "B"),
+ "ABDBD"
+ ));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_NB),
+ new Pair<>("B", "C"),
+ "ABDCD"
+ ));
+
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_LOW),
+ new Pair<>("C", "A"),
+ "ACDAD"
+ ));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_GOOD),
+ new Pair<>("C", "B"),
+ "ACDBD"
+ ));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_CHARGING),
+ new Pair<>("C", "C"),
+ "ACDCD"
+ ));
+
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ Communicator.COVERAGE_GOOD),
+ new Pair<>("AA", "A"),
+ "AAADAD"
+ ));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ Communicator.COVERAGE_POOR),
+ new Pair<>("AA", "B"),
+ "AAADBD"
+ ));
+
+ return result;
+ }
+
+ /**
+ * Verify generation of DTMF digits for messages.
+ */
+ @SmallTest
+ @Test
+ public void testMessageToDtmf() {
+ char[] dtmfSequence = mDtmfTransport.getMessageDigits(mParams.commMessage);
+ assertArrayEquals(mParams.fullMessage.toCharArray(), dtmfSequence);
+ }
+
+ /**
+ * Verify generation of messages from DTMF digits
+ */
+ @SmallTest
+ @Test
+ public void testDtmfToMessage() {
+ Communicator.Message message = mDtmfTransport.extractMessage(
+ mParams.messageAndValueDigits.first, mParams.messageAndValueDigits.second);
+ assertEquals(mParams.commMessage, message);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportTest.java
new file mode 100644
index 0000000000..a35abd1927
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/DtmfTransportTest.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.TestExecutorService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class DtmfTransportTest {
+ private static final long DIGIT_INTERVAL_MILLIS = 10L;
+ private static final long MSG_TIMEOUT_MILLIS = 1000L;
+ private static final long NEGOTIATION_TIMEOUT_MILLIS = 2000L;
+ private static final String EXPECTED_PROBE = "AAD";
+
+ @Mock
+ private DtmfAdapter mDtmfAdapter;
+ @Mock
+ private TransportProtocol.Callback mCallback;
+ @Mock
+ private Timeouts.Adapter mTimeouts;
+ @Captor
+ private ArgumentCaptor<Set<Communicator.Message>> mMessagesCaptor;
+ @Captor
+ private ArgumentCaptor<Character> mDigitsCaptor;
+
+ private TestExecutorService mTestExecutorService = new TestExecutorService();
+ private DtmfTransport mDtmfTransport;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mTimeouts.getDtmfMinimumIntervalMillis()).thenReturn(DIGIT_INTERVAL_MILLIS);
+ when(mTimeouts.getMaxDurationOfDtmfMessageMillis()).thenReturn(MSG_TIMEOUT_MILLIS);
+ when(mTimeouts.getDtmfNegotiationTimeoutMillis()).thenReturn(NEGOTIATION_TIMEOUT_MILLIS);
+ when(mTimeouts.getDtmfDurationFuzzMillis()).thenReturn(0L);
+ mDtmfTransport = new DtmfTransport(mDtmfAdapter, mTimeouts,
+ mTestExecutorService /* Executors.newSingleThreadScheduledExecutor() in prod. */);
+ mDtmfTransport.setCallback(mCallback);
+ }
+
+ /**
+ * Verify starting state when newly initialized.
+ */
+ @SmallTest
+ @Test
+ public void testIdle() {
+ assertEquals(DtmfTransport.STATE_IDLE, mDtmfTransport.getTransportState());
+ }
+
+ /**
+ * Verify negotiation start
+ */
+ @SmallTest
+ @Test
+ public void testStartNegotiation() {
+ mDtmfTransport.startNegotiation();
+ assertEquals(DtmfTransport.STATE_NEGOTIATING, mDtmfTransport.getTransportState());
+
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+
+ verify(mDtmfAdapter, times(3)).sendDtmf(mDigitsCaptor.capture());
+ String probeString = mDigitsCaptor.getAllValues().stream()
+ .map(c -> String.valueOf(c)).collect(Collectors.joining());
+ assertEquals(EXPECTED_PROBE, probeString);
+ }
+
+ /**
+ * Verify negotiation fails due to lack of received digit prior to timeout.
+ */
+ @SmallTest
+ @Test
+ public void testNegotiationFailsDueToTimeout() {
+ mDtmfTransport.startNegotiation();
+
+ mTestExecutorService.advanceTime(NEGOTIATION_TIMEOUT_MILLIS);
+
+ verify(mCallback).onNegotiationFailed(eq(mDtmfTransport));
+ assertEquals(DtmfTransport.STATE_NEGOTIATION_FAILED, mDtmfTransport.getTransportState());
+ }
+
+ /**
+ * Verify negotiation failed due to invalid response
+ */
+ @SmallTest
+ @Test
+ public void testNegotiationFailedInvalidResponse() {
+ testStartNegotiation();
+
+ // Received something other than the probe; it should be ignored
+ mDtmfTransport.onDtmfReceived('1');
+ // Super short message.
+ mDtmfTransport.onDtmfReceived('A');
+ mDtmfTransport.onDtmfReceived('D');
+
+ verify(mCallback).onNegotiationFailed(eq(mDtmfTransport));
+ }
+
+ /**
+ * Verify negotiation completed
+ */
+ @SmallTest
+ @Test
+ public void testNegotiationSuccess() {
+ testStartNegotiation();
+
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+
+ verify(mCallback).onNegotiationSuccess(eq(mDtmfTransport));
+ assertEquals(DtmfTransport.STATE_NEGOTIATED, mDtmfTransport.getTransportState());
+ }
+
+ /**
+ * Verify negotiation completed and that we don't subsequently cancel the negotiation
+ */
+ @SmallTest
+ @Test
+ public void testNegotiationSuccessAndDoesNotTimeOut() {
+ testNegotiationSuccess();
+
+ mTestExecutorService.advanceTime(NEGOTIATION_TIMEOUT_MILLIS);
+
+ // Even though we timeout period has passed, we should NOT have failed negotiation and the
+ // state should remain negotiated.
+ verify(mCallback, never()).onNegotiationFailed(eq(mDtmfTransport));
+ assertEquals(DtmfTransport.STATE_NEGOTIATED, mDtmfTransport.getTransportState());
+ }
+
+ /**
+ * Verify receipt of a single message within the overall {@link #MSG_TIMEOUT_MILLIS} message
+ * window.
+ */
+ @SmallTest
+ @Test
+ public void testReceiveSuccess() {
+ testNegotiationSuccess();
+
+ // Receive message with typical digit spacing
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('C');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+
+ verify(mCallback).onMessagesReceived(mMessagesCaptor.capture());
+ assertEquals(1, mMessagesCaptor.getValue().size());
+ assertTrue(mMessagesCaptor.getValue().contains(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_NB)));
+ }
+
+ /**
+ * Verify invalid message start digits are ignored.
+ */
+ @SmallTest
+ @Test
+ public void testReceiveInvalidMessageStart() {
+ testNegotiationSuccess();
+
+ // Receive message with invalid start digit; it should be ignored.
+ mDtmfTransport.onDtmfReceived('B');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ // Receive random 0-9 digits user might have typed; should be ignored.
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('1');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('2');
+
+ // Back to regularly scheduled message.
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('C');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(MSG_TIMEOUT_MILLIS);
+
+ verify(mCallback, times(1)).onMessagesReceived(mMessagesCaptor.capture());
+ assertEquals(1, mMessagesCaptor.getAllValues().get(0).size());
+ assertTrue(mMessagesCaptor.getAllValues().get(0).contains(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_NB)));
+ }
+
+ /**
+ * Verify invalid messages
+ */
+ @SmallTest
+ @Test
+ public void testReceiveInvalidMessage() {
+ testNegotiationSuccess();
+
+ // Garbage message with no actual values.
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+
+ // An unknown message!
+ mTestExecutorService.advanceTime(MSG_TIMEOUT_MILLIS);
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+
+ verify(mCallback, never()).onMessagesReceived(mMessagesCaptor.capture());
+ }
+
+ /**
+ * Verify receipt of two messages back to back..
+ */
+ @SmallTest
+ @Test
+ public void testReceiveMultipleSuccess() {
+ testNegotiationSuccess();
+
+ // Receive message with typical digit spacing
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('C');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(MSG_TIMEOUT_MILLIS);
+
+ verify(mCallback, times(1)).onMessagesReceived(mMessagesCaptor.capture());
+ assertEquals(1, mMessagesCaptor.getAllValues().get(0).size());
+ assertTrue(mMessagesCaptor.getAllValues().get(0).contains(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_NB)));
+
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('C');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+
+ // Note: Reusing the captor here appends all call arguments on to mMessagesCaptor, so
+ // we need to look at index 2 in getAllValues.
+ verify(mCallback, times(2)).onMessagesReceived(mMessagesCaptor.capture());
+ assertEquals(1, mMessagesCaptor.getAllValues().get(2).size());
+ assertTrue(mMessagesCaptor.getAllValues().get(2).contains(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_LOW)));
+ }
+
+ /**
+ * Verify operation of timeout when receiving a first message, followed by successful receipt of
+ * a second message.
+ */
+ @SmallTest
+ @Test
+ public void testReceiveTimeout() {
+ testNegotiationSuccess();
+
+ // Receive a partial first message.
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('B');
+ // Timeout
+ mTestExecutorService.advanceTime(MSG_TIMEOUT_MILLIS);
+
+ // Receive second message.
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('C');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('A');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+ mDtmfTransport.onDtmfReceived('D');
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS);
+
+ // First message should not be present, but the second one should be.
+ verify(mCallback, times(1)).onMessagesReceived(mMessagesCaptor.capture());
+ assertTrue(mMessagesCaptor.getValue().contains(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_LOW)));
+ }
+
+ /**
+ * Basic test of sending a message using the DTMF transport.
+ * See {@link DtmfTransportConversionTest} for tests that confirm the translation between
+ * messages and DTMF sequences.
+ * This test verifies that trans of DMTF digits are sent in groups separated by the required
+ * inter-message delay and that digits are separated by the inter-digit delay.
+ */
+ @SmallTest
+ @Test
+ public void testSendSuccess() {
+ testNegotiationSuccess();
+
+ Set<Communicator.Message> messages = new ArraySet<>();
+ messages.add(new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_NB));
+ messages.add(new Communicator.Message(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ Communicator.COVERAGE_POOR));
+ mDtmfTransport.sendMessages(messages);
+
+ // Advance the "clock" by the DTMF interval to cause the scheduler to run the scheduled
+ // repeating task which sends the digits.
+ // First digit train; sequences are separated by the longer timeout.
+ mTestExecutorService.advanceTime(MSG_TIMEOUT_MILLIS); // A message start
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // B audio codec
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // D message type terminator
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // C AMR NB value
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // D message value terminator
+
+ // Second digit train; longer due to longer message type.
+ mTestExecutorService.advanceTime(MSG_TIMEOUT_MILLIS); // A message start
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // A coverage
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // A coverage
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // D message type terminator
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // B poor value
+ mTestExecutorService.advanceTime(DIGIT_INTERVAL_MILLIS); // D message value terminator
+
+ ArgumentCaptor<Character> captor = ArgumentCaptor.forClass(Character.class);
+ // Extra invocations are from negotiation
+ verify(mDtmfAdapter, times(14)).sendDtmf(captor.capture());
+
+ // Expected digits includes initial probe send.
+ String expectedDigits = "AADABDCDAAADBD";
+ String actualDigits = captor.getAllValues().stream()
+ .map( c-> String.valueOf(c) )
+ .collect(Collectors.joining());
+ assertEquals(expectedDigits, actualDigits);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java
new file mode 100644
index 0000000000..0c251a0392
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportConversionTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.telephony.ims.RtpHeaderExtension;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Verifies the ability of the {@link RtpTransport} class to successfully encode and decode the
+ * device to device communication messages we expect to handle.
+ */
+@RunWith(Parameterized.class)
+public class RtpTransportConversionTest {
+ private static class TestParams {
+ public Communicator.Message commMessage;
+ public RtpHeaderExtension extension;
+
+ public TestParams(Communicator.Message theMessage, RtpHeaderExtension theExtension) {
+ commMessage = theMessage;
+ extension = theExtension;
+ }
+
+ public String toString() {
+ return "Params{msg = " + commMessage + ", ext = "
+ + Integer.toBinaryString(extension.getExtensionData()[0]) + "}";
+ }
+ }
+
+ private static final int CALL_STATE_LOCAL_IDENTIFIER = 1;
+ private static final int DEVICE_STATE_LOCAL_IDENTIFIER = 2;
+
+ private RtpTransport mRtpTransport;
+ @Mock private Timeouts.Adapter mTimeoutsAdapter;
+ @Mock private RtpAdapter mRtpAdapter;
+ @Mock private Handler mHandler;
+ @Mock private TransportProtocol.Callback mCallback;
+ @Captor private ArgumentCaptor<Set<RtpHeaderExtension>> mHeaderExtensionCaptor;
+ @Captor private ArgumentCaptor<Set<Communicator.Message>> mMessagesCaptor;
+ private final TestParams mParams;
+
+ public RtpTransportConversionTest(TestParams params) {
+ mParams = params;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mRtpTransport = new RtpTransport(mRtpAdapter, mTimeoutsAdapter, mHandler, true /* sdp */);
+ mRtpTransport.setCallback(mCallback);
+
+ when(mRtpAdapter.getAcceptedRtpHeaderExtensions()).thenReturn(
+ RtpTransportTest.ALL_HEADER_EXTENSION_TYPES);
+ mRtpTransport.startNegotiation();
+ verify(mCallback).onNegotiationSuccess(any());
+ verify(mCallback, never()).onNegotiationFailed(any());
+ }
+
+ /**
+ * Setup the test cases.
+ * @return the test cases
+ */
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<TestParams> generateTestCases() {
+ List<TestParams> result = new ArrayList<>();
+
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_EVS),
+ new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00010010})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_WB),
+ new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00100010})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_AUDIO_CODEC,
+ Communicator.AUDIO_CODEC_AMR_NB),
+ new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00110010})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_LTE),
+ new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00010001})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_IWLAN),
+ new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00100001})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_CALL_RADIO_ACCESS_TYPE,
+ Communicator.RADIO_ACCESS_TYPE_NR),
+ new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00110001})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_LOW),
+ new RtpHeaderExtension(DEVICE_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00000001})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_GOOD),
+ new RtpHeaderExtension(DEVICE_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00010001})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_BATTERY_STATE,
+ Communicator.BATTERY_STATE_CHARGING),
+ new RtpHeaderExtension(DEVICE_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00110001})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ Communicator.COVERAGE_POOR),
+ new RtpHeaderExtension(DEVICE_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00000010})));
+ result.add(new TestParams(
+ new Communicator.Message(Communicator.MESSAGE_DEVICE_NETWORK_COVERAGE,
+ Communicator.COVERAGE_GOOD),
+ new RtpHeaderExtension(DEVICE_STATE_LOCAL_IDENTIFIER,
+ new byte[]{0b00010010})));
+ return result;
+ }
+
+ /**
+ * Verify generation of RTP header extension data for messages.
+ */
+ @SmallTest
+ @Test
+ public void testSendMessages() {
+ ArraySet<Communicator.Message> messages = new ArraySet<>();
+ messages.add(mParams.commMessage);
+
+ mRtpTransport.sendMessages(messages);
+ verify(mRtpAdapter).sendRtpHeaderExtensions(mHeaderExtensionCaptor.capture());
+ Set<RtpHeaderExtension> extensions = mHeaderExtensionCaptor.getValue();
+ assertEquals(1, extensions.size());
+ assertTrue(extensions.contains(mParams.extension));
+ }
+
+ /**
+ * Verify translation from raw RTP data into message/value pairs.
+ */
+ @SmallTest
+ @Test
+ public void testReceiveMessage() {
+ ArraySet<RtpHeaderExtension> extension = new ArraySet<>();
+ extension.add(mParams.extension);
+ mRtpTransport.onRtpHeaderExtensionsReceived(extension);
+
+ verify(mCallback).onMessagesReceived(mMessagesCaptor.capture());
+ Set<Communicator.Message> messages = mMessagesCaptor.getValue();
+ assertEquals(1, messages.size());
+ assertTrue(messages.contains(mParams.commMessage));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java
new file mode 100644
index 0000000000..c241cf285d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/d2d/RtpTransportTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.d2d;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.telephony.ims.RtpHeaderExtension;
+import android.telephony.ims.RtpHeaderExtensionType;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class RtpTransportTest {
+ private static final int CALL_STATE_LOCAL_IDENTIFIER = 1;
+ private static final int DEVICE_STATE_LOCAL_IDENTIFIER = 2;
+
+ public static final ArraySet<RtpHeaderExtensionType> ALL_HEADER_EXTENSION_TYPES =
+ new ArraySet<>();
+ static {
+ ALL_HEADER_EXTENSION_TYPES.add(new RtpHeaderExtensionType(CALL_STATE_LOCAL_IDENTIFIER,
+ RtpTransport.CALL_STATE_RTP_HEADER_EXTENSION));
+ ALL_HEADER_EXTENSION_TYPES.add(new RtpHeaderExtensionType(DEVICE_STATE_LOCAL_IDENTIFIER,
+ RtpTransport.DEVICE_STATE_RTP_HEADER_EXTENSION));
+ }
+ private static final ArraySet<RtpHeaderExtensionType> NO_SUPPORTED_HEADER_EXTENSION_TYPES =
+ new ArraySet<>();
+
+ private RtpTransport mRtpTransport;
+ @Mock private Timeouts.Adapter mTimeoutsAdapter;
+ @Mock private RtpAdapter mRtpAdapter;
+ @Mock private Handler mHandler;
+ @Mock private TransportProtocol.Callback mCallback;
+ @Captor private ArgumentCaptor<Set<RtpHeaderExtension>> mHeaderExtensionCaptor;
+ @Captor private ArgumentCaptor<Set<Communicator.Message>> mMessagesCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mRtpTransport = new RtpTransport(mRtpAdapter, mTimeoutsAdapter, mHandler, true /* sdp */);
+ mRtpTransport.setCallback(mCallback);
+ }
+
+ /**
+ * Nominal case; assume the remote side accepted all RTP header extension types we need for D2D
+ * communications. The should get an instant negotiation success.
+ */
+ @SmallTest
+ @Test
+ public void testAllHeaderExtensionsSupported() {
+ when(mRtpAdapter.getAcceptedRtpHeaderExtensions()).thenReturn(ALL_HEADER_EXTENSION_TYPES);
+ mRtpTransport.startNegotiation();
+ verify(mCallback).onNegotiationSuccess(any());
+ verify(mCallback, never()).onNegotiationFailed(any());
+ }
+
+ /**
+ * Verify the case where the RTP header extensions are not supported.
+ */
+ @SmallTest
+ @Test
+ public void testRtpHeaderExtensionsNotSupported() {
+ when(mRtpAdapter.getAcceptedRtpHeaderExtensions()).thenReturn(
+ NO_SUPPORTED_HEADER_EXTENSION_TYPES);
+ mRtpTransport.startNegotiation();
+ verify(mCallback, never()).onNegotiationSuccess(any());
+ verify(mCallback).onNegotiationFailed(any());
+ }
+
+ /**
+ * Verifies that unrecognized RTP header extensions are ignored.
+ */
+ @SmallTest
+ @Test
+ public void testIgnoreInvalidMessage() {
+ testAllHeaderExtensionsSupported();
+
+ ArraySet<RtpHeaderExtension> extensions = new ArraySet<>();
+ // Invalid because it has an unrecognized local identifier
+ extensions.add(new RtpHeaderExtension(1, new byte[] {0b01010101}));
+ // Invalid because it has an unknown message type within a valid identifier.
+ extensions.add(new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[] {0b00101111}));
+ // Invalid because it has an unknown message value within a valid type.
+ extensions.add(new RtpHeaderExtension(CALL_STATE_LOCAL_IDENTIFIER,
+ new byte[] {0b01110010}));
+ mRtpTransport.onRtpHeaderExtensionsReceived(extensions);
+ verify(mCallback, never()).onMessagesReceived(any());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java
index aa97c0244f..532dbe0f35 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnConfigTypeRepositoryTest.java
@@ -75,7 +75,7 @@ public class ApnConfigTypeRepositoryTest {
//Priorities must be integers
apnConfigStringArray.add("default:10a");
- //Key isn't case sensitive, which means that this prority should be taken
+ //Key isn't case sensitive, which means that this priority should be taken
apnConfigStringArray.add("fotA:10");
mCarrierConfig.putStringArray(CarrierConfigManager.KEY_APN_PRIORITY_STRING_ARRAY,
@@ -87,11 +87,17 @@ public class ApnConfigTypeRepositoryTest {
}
private void checkDefaults(ApnConfigTypeRepository repository) {
- assertEquals(0, repository.getByType(ApnSetting.TYPE_DEFAULT).getPriority());
+ assertEquals(0, repository.getByType(ApnSetting.TYPE_ENTERPRISE).getPriority());
+ assertEquals(1, repository.getByType(ApnSetting.TYPE_DEFAULT).getPriority());
+ assertEquals(2, repository.getByType(ApnSetting.TYPE_MMS).getPriority());
assertEquals(2, repository.getByType(ApnSetting.TYPE_SUPL).getPriority());
+ assertEquals(2, repository.getByType(ApnSetting.TYPE_DUN).getPriority());
assertEquals(3, repository.getByType(ApnSetting.TYPE_HIPRI).getPriority());
+ assertEquals(2, repository.getByType(ApnSetting.TYPE_IMS).getPriority());
assertEquals(2, repository.getByType(ApnSetting.TYPE_CBS).getPriority());
+ assertEquals(2, repository.getByType(ApnSetting.TYPE_IA).getPriority());
assertEquals(2, repository.getByType(ApnSetting.TYPE_EMERGENCY).getPriority());
+ assertEquals(3, repository.getByType(ApnSetting.TYPE_MCX).getPriority());
assertEquals(3, repository.getByType(ApnSetting.TYPE_XCAP).getPriority());
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
index 525afe2e60..85d1e5e750 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnContextTest.java
@@ -26,14 +26,12 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
import android.net.NetworkRequest;
import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.R;
import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
@@ -42,9 +40,6 @@ import org.junit.Test;
import org.mockito.Mock;
public class ApnContextTest extends TelephonyTest {
-
- @Mock
- NetworkConfig mNetworkConfig;
@Mock
ApnSetting mApnSetting;
@@ -53,7 +48,7 @@ public class ApnContextTest extends TelephonyTest {
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- mNetworkConfig.dependencyMet = true;
+
mApnContext = new ApnContext(mPhone, ApnSetting.TYPE_DEFAULT, TAG, mDcTracker, 1);
}
@@ -72,8 +67,8 @@ public class ApnContextTest extends TelephonyTest {
@Test
@SmallTest
- public void testGetApnType() throws Exception {
- assertEquals(PhoneConstants.APN_TYPE_DEFAULT, mApnContext.getApnType());
+ public void testGetApnType() {
+ assertEquals(ApnSetting.TYPE_DEFAULT_STRING, mApnContext.getApnType());
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index de7d12a966..0b19baf19b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -31,7 +31,6 @@ import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
@@ -73,17 +72,17 @@ public class ApnSettingTest extends TelephonyTest {
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- null, // proxy
+ null, // proxy
-1, // port
- null, // mmsc
- null, // mmsproxy
+ null, // mmsc
+ null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
- apnTypeBitmask, // types
- ApnSetting.PROTOCOL_IP, // protocol
- ApnSetting.PROTOCOL_IP, // roaming_protocol
+ apnTypeBitmask, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
carrierEnabled, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
@@ -135,7 +134,7 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testFromString() throws Exception {
+ public void testFromString() {
final int dunTypesBitmask = ApnSetting.TYPE_DUN;
final int mmsTypesBitmask = ApnSetting.TYPE_MMS | ApnSetting.TYPE_ALL;
@@ -264,7 +263,7 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testArrayFromString() throws Exception {
+ public void testArrayFromString() {
final int mmsTypesBitmask = ApnSetting.TYPE_MMS;
// Test a multiple v3 string.
String testString =
@@ -297,7 +296,7 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testToString() throws Exception {
+ public void testToString() {
// Use default apn_set_id constructor.
ApnSetting apn = ApnSetting.makeApnSetting(
99, "12345", "Name", "apn", null, 10,
@@ -323,9 +322,9 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testIsMetered() throws Exception {
+ public void testIsMetered() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
@@ -360,10 +359,11 @@ public class ApnSettingTest extends TelephonyTest {
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_IA, mPhone));
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_HIPRI, mPhone));
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
// Carrier config settings changes.
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
@@ -371,9 +371,9 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testIsRoamingMetered() throws Exception {
+ public void testIsRoamingMetered() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
@@ -400,19 +400,20 @@ public class ApnSettingTest extends TelephonyTest {
// Carrier config settings changes.
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_FOTA});
+ new String[]{ApnSetting.TYPE_FOTA_STRING});
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_DEFAULT, mPhone));
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_MMS, mPhone));
assertTrue(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_FOTA, mPhone));
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
}
@Test
@SmallTest
- public void testIsMeteredAnother() throws Exception {
+ public void testIsMeteredAnother() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
+ new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_CBS_STRING});
doReturn(false).when(mServiceState).getDataRoaming();
doReturn(1).when(mPhone).getSubId();
@@ -437,13 +438,15 @@ public class ApnSettingTest extends TelephonyTest {
assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_IMS), mPhone));
assertFalse(ApnSettingUtils.isMetered(createApnSetting(ApnSetting.TYPE_XCAP), mPhone));
+ assertFalse(ApnSettingUtils.isMetered(
+ createApnSetting(ApnSetting.TYPE_ENTERPRISE), mPhone));
}
@Test
@SmallTest
- public void testIsRoamingMeteredAnother() throws Exception {
+ public void testIsRoamingMeteredAnother() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
+ new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_CBS_STRING});
doReturn(true).when(mServiceState).getDataRoaming();
doReturn(2).when(mPhone).getSubId();
@@ -476,11 +479,12 @@ public class ApnSettingTest extends TelephonyTest {
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_IA, mPhone));
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_HIPRI, mPhone));
assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_XCAP, mPhone));
+ assertFalse(ApnSettingUtils.isMeteredApnType(ApnSetting.TYPE_ENTERPRISE, mPhone));
}
@Test
@SmallTest
- public void testIsMeteredNothingCharged() throws Exception {
+ public void testIsMeteredNothingCharged() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{});
@@ -501,7 +505,7 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testIsRoamingMeteredNothingCharged() throws Exception {
+ public void testIsRoamingMeteredNothingCharged() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{});
doReturn(true).when(mServiceState).getDataRoaming();
@@ -521,7 +525,7 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testCanHandleType() throws Exception {
+ public void testCanHandleType() {
String types[] = {"mms"};
assertTrue(createApnSetting(ApnSetting.TYPE_ALL)
@@ -556,7 +560,7 @@ public class ApnSettingTest extends TelephonyTest {
ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_IA)
.canHandleType(ApnSetting.TYPE_IA));
- // same for emergency, mcx, and xcap
+ // same for emergency, mcx, xcap, and enterprise
assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
.canHandleType(ApnSetting.TYPE_EMERGENCY));
assertTrue(createApnSetting(
@@ -572,6 +576,11 @@ public class ApnSettingTest extends TelephonyTest {
assertTrue(createApnSetting(
ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP)
.canHandleType(ApnSetting.TYPE_XCAP));
+ assertFalse(createApnSetting(ApnSetting.TYPE_ALL)
+ .canHandleType(ApnSetting.TYPE_ENTERPRISE));
+ assertTrue(createApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_ENTERPRISE)
+ .canHandleType(ApnSetting.TYPE_ENTERPRISE));
// check carrier disabled
assertFalse(createDisabledApnSetting(ApnSetting.TYPE_ALL)
@@ -590,6 +599,9 @@ public class ApnSettingTest extends TelephonyTest {
assertFalse(createDisabledApnSetting(
ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP)
.canHandleType(ApnSetting.TYPE_XCAP));
+ assertFalse(createDisabledApnSetting(
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_MMS | ApnSetting.TYPE_ENTERPRISE)
+ .canHandleType(ApnSetting.TYPE_ENTERPRISE));
}
@Test
@@ -639,7 +651,7 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testEqualsRoamingProtocol() throws Exception {
+ public void testEqualsRoamingProtocol() {
ApnSetting apn1 = ApnSetting.makeApnSetting(
1234,
"310260",
@@ -700,7 +712,7 @@ public class ApnSettingTest extends TelephonyTest {
@Test
@SmallTest
- public void testCanHandleNetwork() throws Exception {
+ public void testCanHandleNetwork() {
ApnSetting apn1 = ApnSetting.makeApnSetting(
1234,
"310260",
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
index 529e81e89f..6f9170938a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
@@ -27,18 +27,24 @@ import android.net.LinkAddress;
import android.os.Parcel;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.EpsQos;
+import android.telephony.data.TrafficDescriptor;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.ArrayList;
import java.util.Arrays;
public class DataCallResponseTest extends AndroidTestCase {
+ public static final String FAKE_DNN = "FAKE_DNN";
+ public static final byte[] FAKE_OS_APP_ID = {1, 2, 3, 4};
+ public static final byte[] FAKE_OS_APP_ID_2 = {5, 6, 8, 9};
@SmallTest
- public void testParcel() throws Exception {
+ public void testParcel() {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1L)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -51,6 +57,10 @@ public class DataCallResponseTest extends AndroidTestCase {
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1440)
+ .setDefaultQos(new EpsQos())
+ .setQosBearerSessions(new ArrayList<>())
+ .setTrafficDescriptors(
+ Arrays.asList(new TrafficDescriptor(FAKE_DNN, FAKE_OS_APP_ID)))
.build();
Parcel p = Parcel.obtain();
@@ -62,10 +72,10 @@ public class DataCallResponseTest extends AndroidTestCase {
}
@SmallTest
- public void testEquals() throws Exception {
+ public void testEquals() {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1L)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -78,11 +88,13 @@ public class DataCallResponseTest extends AndroidTestCase {
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1400)
+ .setTrafficDescriptors(
+ Arrays.asList(new TrafficDescriptor(FAKE_DNN, FAKE_OS_APP_ID)))
.build();
DataCallResponse response1 = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1L)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -95,6 +107,8 @@ public class DataCallResponseTest extends AndroidTestCase {
Arrays.asList(InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1440)
.setMtuV6(1400)
+ .setTrafficDescriptors(
+ Arrays.asList(new TrafficDescriptor(FAKE_DNN, FAKE_OS_APP_ID)))
.build();
assertEquals(response, response);
@@ -102,7 +116,7 @@ public class DataCallResponseTest extends AndroidTestCase {
DataCallResponse response2 = new DataCallResponse.Builder()
.setCause(1)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1L)
.setId(1)
.setLinkStatus(3)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -117,6 +131,8 @@ public class DataCallResponseTest extends AndroidTestCase {
InetAddresses.parseNumericAddress(FAKE_PCSCF_ADDRESS)))
.setMtuV4(1441)
.setMtuV6(1440)
+ .setTrafficDescriptors(
+ Arrays.asList(new TrafficDescriptor("FAKE_DNN_2", FAKE_OS_APP_ID_2)))
.build();
assertNotSame(response1, response2);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index e79bdfdf22..2ae734bfc2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -19,8 +19,6 @@ package com.android.internal.telephony.dataconnection;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
-import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
@@ -32,22 +30,23 @@ import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_P
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.content.IntentFilter;
-import android.content.pm.ServiceInfo;
import android.net.InetAddresses;
import android.net.KeepalivePacketData;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NattKeepalivePacketData;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
@@ -56,12 +55,17 @@ import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.ServiceState.RegState;
+import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
+import android.telephony.data.DataServiceCallback;
+import android.telephony.data.TrafficDescriptor;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
import com.android.internal.R;
import com.android.internal.telephony.PhoneConstants;
@@ -70,8 +74,7 @@ import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
import com.android.internal.telephony.dataconnection.DataConnection.SetupResult;
-import com.android.internal.util.IState;
-import com.android.internal.util.StateMachine;
+import com.android.internal.telephony.metrics.DataCallSessionStats;
import org.junit.After;
import org.junit.Before;
@@ -81,9 +84,12 @@ import org.mockito.Mock;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
public class DataConnectionTest extends TelephonyTest {
+ private static final int DEFAULT_DC_CID = 10;
@Mock
DcTesterFailBringUpAll mDcTesterFailBringUpAll;
@@ -94,29 +100,36 @@ public class DataConnectionTest extends TelephonyTest {
@Mock
ApnContext mApnContext;
@Mock
+ ApnContext mEnterpriseApnContext;
+ @Mock
DcFailBringUp mDcFailBringUp;
+ @Mock
+ DataCallSessionStats mDataCallSessionStats;
+ @Mock
+ DataConnection mDefaultDc;
+ @Mock
+ DataServiceManager mDataServiceManager;
private DataConnection mDc;
private DataConnectionTestHandler mDataConnectionTestHandler;
private DcController mDcc;
- private CellularDataService mCellularDataService;
private ApnSetting mApn1 = ApnSetting.makeApnSetting(
2163, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- null, // proxy
+ null, // proxy
-1, // port
- null, // mmsc
- null, // mmsproxy
+ null, // mmsc
+ null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
- ApnSetting.PROTOCOL_IP, // protocol
- ApnSetting.PROTOCOL_IP, // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
@@ -133,17 +146,17 @@ public class DataConnectionTest extends TelephonyTest {
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
- null, // proxy
+ null, // proxy
-1, // port
- null, // mmsc
- null, // mmsproxy
+ null, // mmsc
+ null, // mmsproxy
-1, // mmsport
"", // user
"", // password
-1, // authtype
- ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, // types
- ApnSetting.PROTOCOL_IP, // protocol
- ApnSetting.PROTOCOL_IP, // roaming_protocol
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
true, // carrier_enabled
0, // networktype_bitmask
0, // profile_id
@@ -156,7 +169,7 @@ public class DataConnectionTest extends TelephonyTest {
""); // mnvo_match_data
private ApnSetting mApn3 = ApnSetting.makeApnSetting(
- 2164, // id
+ 2165, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
@@ -186,7 +199,7 @@ public class DataConnectionTest extends TelephonyTest {
1); // skip_464xlat
private ApnSetting mApn4 = ApnSetting.makeApnSetting(
- 2164, // id
+ 2166, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
@@ -213,7 +226,7 @@ public class DataConnectionTest extends TelephonyTest {
""); // mnvo_match_data
private ApnSetting mApn5 = ApnSetting.makeApnSetting(
- 2164, // id
+ 2167, // id
"44010", // numeric
"sp-mode", // name
"spmode.ne.jp", // apn
@@ -242,6 +255,33 @@ public class DataConnectionTest extends TelephonyTest {
-1, // carrier_id
0); // skip_464xlat
+ private ApnSetting mApn6 = ApnSetting.makeApnSetting(
+ 2168, // id
+ "44010", // numeric
+ "sp-mode", // name
+ "spmode.ne.jp", // apn
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ ApnSetting.TYPE_EMERGENCY, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
+ true, // carrier_enabled
+ 0, // networktype_bitmask
+ 0, // profile_id
+ false, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ -1, // mvno_type
+ ""); // mnvo_match_data
+
private class DataConnectionTestHandler extends HandlerThread {
private DataConnectionTestHandler(String name) {
@@ -251,31 +291,61 @@ public class DataConnectionTest extends TelephonyTest {
@Override
public void onLooperPrepared() {
Handler h = new Handler();
-
- DataServiceManager manager = new DataServiceManager(mPhone,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN, "");
- mDcc = DcController.makeDcc(mPhone, mDcTracker, manager, h, "");
- mDcc.start();
- mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, manager,
- mDcTesterFailBringUpAll, mDcc);
+ mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, h.getLooper(), "");
+ mDc = DataConnection.makeDataConnection(mPhone, 0, mDcTracker, mDataServiceManager,
+ mDcTesterFailBringUpAll, mDcc, true);
}
}
- private void addDataService() {
- mCellularDataService = new CellularDataService();
- ServiceInfo serviceInfo = new ServiceInfo();
- serviceInfo.packageName = "com.android.phone";
- serviceInfo.permission = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
- IntentFilter filter = new IntentFilter();
- mContextFixture.addService(
- DataService.SERVICE_INTERFACE,
- null,
- "com.android.phone",
- mCellularDataService.mBinder,
- serviceInfo,
- filter);
+ private void setSuccessfulSetupDataResponse(int cid) {
+ doAnswer(invocation -> {
+ final Message msg = (Message) invocation.getArguments()[10];
+
+ DataCallResponse response = new DataCallResponse.Builder()
+ .setCause(0)
+ .setRetryDurationMillis(-1L)
+ .setId(cid)
+ .setLinkStatus(2)
+ .setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
+ .setInterfaceName("ifname")
+ .setAddresses(Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress("10.0.2.15"), 32),
+ new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")))
+ .setDnsAddresses(Arrays.asList(InetAddresses.parseNumericAddress("10.0.2.3"),
+ InetAddresses.parseNumericAddress("fd00:976a::9")))
+ .setGatewayAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("10.0.2.15"),
+ InetAddresses.parseNumericAddress("fe80::2")))
+ .setPcscfAddresses(Arrays.asList(
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::8"),
+ InetAddresses.parseNumericAddress("fd00:976a:c202:1d::7"),
+ InetAddresses.parseNumericAddress("fd00:976a:c305:1d::5")))
+ .setMtu(1500)
+ .setMtuV4(1500)
+ .setMtuV6(1500)
+ .setPduSessionId(1)
+ .setQosBearerSessions(new ArrayList<>())
+ .setTrafficDescriptors(new ArrayList<>())
+ .build();
+ msg.getData().putParcelable("data_call_response", response);
+ msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
+ msg.sendToTarget();
+ return null;
+ }).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
+ anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
+ any(Message.class));
}
+ private void setFailedSetupDataResponse(@DataServiceCallback.ResultCode int resultCode) {
+ doAnswer(invocation -> {
+ final Message msg = (Message) invocation.getArguments()[10];
+ msg.arg1 = resultCode;
+ msg.sendToTarget();
+ return null;
+ }).when(mDataServiceManager).setupDataCall(anyInt(), any(DataProfile.class), anyBoolean(),
+ anyBoolean(), anyInt(), any(), anyInt(), any(), any(), anyBoolean(),
+ any(Message.class));
+ }
@Before
public void setUp() throws Exception {
@@ -286,7 +356,8 @@ public class DataConnectionTest extends TelephonyTest {
replaceInstance(ConnectionParams.class, "mRilRat", mCp,
ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
doReturn(mApn1).when(mApnContext).getApnSetting();
- doReturn(PhoneConstants.APN_TYPE_DEFAULT).when(mApnContext).getApnType();
+ doReturn(ApnSetting.TYPE_DEFAULT_STRING).when(mApnContext).getApnType();
+ doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
mDcFailBringUp.saveParameters(0, 0, -2);
doReturn(mDcFailBringUp).when(mDcTesterFailBringUpAll).getDcFailBringUp();
@@ -308,12 +379,25 @@ public class DataConnectionTest extends TelephonyTest {
"com.android.phone");
mDcp.mApnContext = mApnContext;
- addDataService();
+
+ setSuccessfulSetupDataResponse(DEFAULT_DC_CID);
+
+ doAnswer(invocation -> {
+ final Message msg = (Message) invocation.getArguments()[2];
+ msg.arg1 = DataServiceCallback.RESULT_SUCCESS;
+ msg.sendToTarget();
+ return null;
+ }).when(mDataServiceManager).deactivateDataCall(anyInt(), anyInt(), any(Message.class));
+
+ doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mDataServiceManager)
+ .getTransportType();
mDataConnectionTestHandler = new DataConnectionTestHandler(getClass().getSimpleName());
mDataConnectionTestHandler.start();
waitForMs(200);
+ mDc.setDataCallSessionStats(mDataCallSessionStats);
+
logd("-Setup!");
}
@@ -324,16 +408,9 @@ public class DataConnectionTest extends TelephonyTest {
mDcc = null;
mDataConnectionTestHandler.quit();
mDataConnectionTestHandler.join();
- mCellularDataService.onDestroy();
super.tearDown();
}
- private IState getCurrentState() throws Exception {
- Method method = StateMachine.class.getDeclaredMethod("getCurrentState");
- method.setAccessible(true);
- return (IState) method.invoke(mDc);
- }
-
private long getSuggestedRetryDelay(DataCallResponse response) throws Exception {
Class[] cArgs = new Class[1];
cArgs[0] = DataCallResponse.class;
@@ -348,8 +425,19 @@ public class DataConnectionTest extends TelephonyTest {
return (boolean) method.invoke(mDc);
}
- private SetupResult setLinkProperties(DataCallResponse response,
- LinkProperties linkProperties)
+ private boolean isEnterpriseUse() throws Exception {
+ Method method = DataConnection.class.getDeclaredMethod("isEnterpriseUse");
+ method.setAccessible(true);
+ return (boolean) method.invoke(mDc);
+ }
+
+ private boolean isSuspended() throws Exception {
+ Field field = DataConnection.class.getDeclaredField("mIsSuspended");
+ field.setAccessible(true);
+ return field.getBoolean(mDc);
+ }
+
+ private SetupResult setLinkProperties(DataCallResponse response, LinkProperties linkProperties)
throws Exception {
Class[] cArgs = new Class[2];
cArgs[0] = DataCallResponse.class;
@@ -361,17 +449,9 @@ public class DataConnectionTest extends TelephonyTest {
@Test
@SmallTest
- public void testSanity() throws Exception {
- assertEquals("DcInactiveState", getCurrentState().getName());
- }
-
- @Test
- @SmallTest
- public void testConnectEvent() throws Exception {
- testSanity();
-
- mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
- waitForMs(200);
+ public void testConnectEvent() {
+ assertTrue(mDc.isInactive());
+ connectEvent(true);
verify(mCT, times(1)).registerForVoiceCallStarted(any(Handler.class),
eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED), eq(null));
@@ -383,16 +463,38 @@ public class DataConnectionTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1))
.registerForLceInfo(any(Handler.class),
eq(DataConnection.EVENT_LINK_CAPACITY_CHANGED), eq(null));
+ verify(mVcnManager, atLeastOnce())
+ .applyVcnNetworkPolicy(
+ argThat(caps ->
+ caps.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)),
+ any());
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
- verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+ ArgumentCaptor<TrafficDescriptor> tdCaptor =
+ ArgumentCaptor.forClass(TrafficDescriptor.class);
+ verify(mDataServiceManager, times(1)).setupDataCall(
eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
- eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(), any(Message.class));
+ eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ anyInt(), any(), tdCaptor.capture(), anyBoolean(), any(Message.class));
- assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
+ verify(mSimulatedCommandsVerifier, times(1))
+ .allocatePduSessionId(any());
- assertEquals("DcActiveState", getCurrentState().getName());
+ assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
+ if (tdCaptor.getValue() != null) {
+ if (mApnContext.getApnTypeBitmask() == ApnSetting.TYPE_ENTERPRISE) {
+ assertEquals(null, tdCaptor.getValue().getDataNetworkName());
+ assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
+ tdCaptor.getValue().getOsAppId()));
+ } else {
+ assertEquals("spmode.ne.jp", tdCaptor.getValue().getDataNetworkName());
+ assertEquals(null, tdCaptor.getValue().getOsAppId());
+ }
+ }
+ assertTrue(mDc.isActive());
+ assertEquals(mDc.getPduSessionId(), 1);
assertEquals(3, mDc.getPcscfAddresses().length);
assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c305:1d::8"::equals));
assertTrue(Arrays.stream(mDc.getPcscfAddresses()).anyMatch("fd00:976a:c202:1d::7"::equals));
@@ -400,20 +502,79 @@ public class DataConnectionTest extends TelephonyTest {
}
@Test
+ public void testConnectEventDuplicateContextIds() throws Exception {
+ setUpDefaultData(DEFAULT_DC_CID);
+
+ // Try to connect ENTERPRISE with the same CID as default
+ replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
+ doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
+ doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
+ doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
+
+ // Verify that ENTERPRISE wasn't set up
+ connectEvent(false);
+ assertTrue(mDc.isInactive());
+
+ // Change the CID
+ setSuccessfulSetupDataResponse(DEFAULT_DC_CID + 1);
+
+ // Verify that ENTERPRISE was set up
+ connectEvent(true);
+ assertTrue(mDc.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
+ }
+
+ @Test
+ public void testConnectEventNoDefaultData() throws Exception {
+ assertFalse(mDefaultDc.isActive());
+
+ // Try to connect ENTERPRISE when default data doesn't exist
+ replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
+ doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
+ doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
+ doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
+
+ // Verify that ENTERPRISE wasn't set up
+ connectEvent(false);
+ assertTrue(mDc.isInactive());
+
+ // Set up default data
+ replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
+ setUpDefaultData(1);
+
+ // Verify that ENTERPRISE was set up
+ replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
+ connectEvent(true);
+ assertTrue(mDc.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
+ }
+
+ private void setUpDefaultData(int cid) throws Exception {
+ replaceInstance(DataConnection.class, "mCid", mDefaultDc, cid);
+ doReturn(true).when(mDefaultDc).isActive();
+ doReturn(Arrays.asList(mApnContext)).when(mDefaultDc).getApnContexts();
+ mDcc.addActiveDcByCid(mDefaultDc);
+ assertTrue(mDefaultDc.getApnContexts().stream()
+ .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT));
+ }
+
+ @Test
@SmallTest
- public void testDisconnectEvent() throws Exception {
+ public void testDisconnectEvent() {
testConnectEvent();
- mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
- waitForMs(100);
+ mDc.setPduSessionId(5);
+ disconnectEvent();
verify(mSimulatedCommandsVerifier, times(1)).unregisterForLceInfo(any(Handler.class));
verify(mSimulatedCommandsVerifier, times(1))
.unregisterForNattKeepaliveStatus(any(Handler.class));
- verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(eq(1),
+ verify(mDataServiceManager, times(1)).deactivateDataCall(eq(DEFAULT_DC_CID),
eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+ verify(mSimulatedCommandsVerifier, times(1))
+ .releasePduSessionId(any(), eq(5));
- assertEquals("DcInactiveState", getCurrentState().getName());
+ assertTrue(mDc.isInactive());
}
@Test
@@ -421,7 +582,7 @@ public class DataConnectionTest extends TelephonyTest {
public void testModemSuggestRetry() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(0)
+ .setRetryDurationMillis(0)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -439,7 +600,7 @@ public class DataConnectionTest extends TelephonyTest {
response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(1000)
+ .setRetryDurationMillis(1000)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -457,7 +618,7 @@ public class DataConnectionTest extends TelephonyTest {
response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(9999)
+ .setRetryDurationMillis(9999)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -479,7 +640,7 @@ public class DataConnectionTest extends TelephonyTest {
public void testModemNotSuggestRetry() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -497,7 +658,7 @@ public class DataConnectionTest extends TelephonyTest {
response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-5)
+ .setRetryDurationMillis(-5)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -515,7 +676,7 @@ public class DataConnectionTest extends TelephonyTest {
response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(Integer.MIN_VALUE)
+ .setRetryDurationMillis(Long.MIN_VALUE)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -537,7 +698,7 @@ public class DataConnectionTest extends TelephonyTest {
public void testModemSuggestNoRetry() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(Integer.MAX_VALUE)
+ .setRetryDurationMillis(Long.MAX_VALUE)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -554,12 +715,6 @@ public class DataConnectionTest extends TelephonyTest {
assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(response));
}
- private NetworkInfo getNetworkInfo() throws Exception {
- Field f = DataConnection.class.getDeclaredField("mNetworkInfo");
- f.setAccessible(true);
- return (NetworkInfo) f.get(mDc);
- }
-
private NetworkCapabilities getNetworkCapabilities() throws Exception {
Method method = DataConnection.class.getDeclaredMethod("getNetworkCapabilities");
method.setAccessible(true);
@@ -587,16 +742,16 @@ public class DataConnectionTest extends TelephonyTest {
.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
mContextFixture.getCarrierConfigBundle().putStringArray(
CarrierConfigManager.KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
new String[] {"supl"});
- mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
- waitForMs(100);
+ disconnectEvent();
doReturn(mApn1).when(mApnContext).getApnSetting();
- mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
- waitForMs(200);
+ connectEvent(true);
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
@@ -604,6 +759,44 @@ public class DataConnectionTest extends TelephonyTest {
.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
+ }
+
+ @Test
+ @SmallTest
+ public void testEnterpriseNetworkCapability() throws Exception {
+ mContextFixture.getCarrierConfigBundle().putStringArray(
+ CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[] { "default" });
+ doReturn(mApn2).when(mApnContext).getApnSetting();
+ testConnectEvent();
+
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
+
+ disconnectEvent();
+ setUpDefaultData(1);
+ replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
+ doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
+ doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
+ doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
+ connectEvent(true);
+
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+ assertFalse("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL));
+ assertTrue("capabilities: " + getNetworkCapabilities(), getNetworkCapabilities()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
}
@Test
@@ -617,7 +810,6 @@ public class DataConnectionTest extends TelephonyTest {
testConnectEvent();
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
}
@Test
@@ -641,17 +833,21 @@ public class DataConnectionTest extends TelephonyTest {
new String[] { "default" });
testConnectEvent();
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- mDc.onSubscriptionOverride(SUBSCRIPTION_OVERRIDE_UNMETERED,
- SUBSCRIPTION_OVERRIDE_UNMETERED);
+ mDc.onMeterednessChanged(true);
+ waitForMs(100);
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- mDc.onSubscriptionOverride(SUBSCRIPTION_OVERRIDE_UNMETERED, 0);
+ mDc.onMeterednessChanged(false);
+ waitForMs(100);
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
}
@@ -664,21 +860,35 @@ public class DataConnectionTest extends TelephonyTest {
testConnectEvent();
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- mDc.onSubscriptionOverride(SUBSCRIPTION_OVERRIDE_CONGESTED,
- SUBSCRIPTION_OVERRIDE_CONGESTED);
+ mDc.onCongestednessChanged(true);
+ waitForMs(100);
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- mDc.onSubscriptionOverride(SUBSCRIPTION_OVERRIDE_CONGESTED, 0);
+ mDc.onCongestednessChanged(false);
+ waitForMs(100);
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
assertTrue(getNetworkCapabilities().hasCapability(NET_CAPABILITY_NOT_CONGESTED));
}
@Test
+ public void testSubscriptionIds() throws Exception {
+ mContextFixture.getCarrierConfigBundle().putStringArray(
+ CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[] { "default" });
+ testConnectEvent();
+
+ assertEquals(Collections.singleton(0), getNetworkCapabilities().getSubscriptionIds());
+ }
+
+ @Test
public void testShouldSkip464Xlat() throws Exception {
assertFalse(testShouldSkip464XlatEvent(mApn1));
disconnectEvent();
@@ -698,27 +908,36 @@ public class DataConnectionTest extends TelephonyTest {
method.setAccessible(true);
doReturn(apn).when(mApnContext).getApnSetting();
- connectEvent();
+ doReturn(apn.getApnTypeBitmask()).when(mApnContext).getApnTypeBitmask();
+ connectEvent(true);
logd(getNetworkCapabilities().toString());
return (Boolean) method.invoke(mDc);
}
- private void connectEvent() throws Exception {
+ private void connectEvent(boolean validate) {
mDc.sendMessage(DataConnection.EVENT_CONNECT, mCp);
waitForMs(200);
- assertEquals("DcActiveState", getCurrentState().getName());
+ if (validate) {
+ assertTrue(mDc.isActive());
+ }
}
- private void disconnectEvent() throws Exception {
+ private void disconnectEvent() {
mDc.sendMessage(DataConnection.EVENT_DISCONNECT, mDcp);
waitForMs(100);
- assertEquals("DcInactiveState", getCurrentState().getName());
+ assertTrue(mDc.isInactive());
+ }
+
+ private void serviceStateChangedEvent(@RegState int dataRegState, @RilRadioTechnology int rat) {
+ mDc.obtainMessage(DataConnection.EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED,
+ new AsyncResult(null, new Pair<>(dataRegState, rat), null)).sendToTarget();
+ waitForMs(100);
}
@Test
@SmallTest
- public void testIsIpAddress() throws Exception {
+ public void testIsIpAddress() {
// IPv4
assertTrue(DataConnection.isIpAddress("1.2.3.4"));
assertTrue(DataConnection.isIpAddress("127.0.0.1"));
@@ -733,7 +952,7 @@ public class DataConnectionTest extends TelephonyTest {
public void testSetLinkProperties() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -789,7 +1008,7 @@ public class DataConnectionTest extends TelephonyTest {
// 224.224.224.224 is an invalid address.
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -812,7 +1031,7 @@ public class DataConnectionTest extends TelephonyTest {
// Empty dns entry.
DataCallResponse response = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(2)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -1025,6 +1244,24 @@ public class DataConnectionTest extends TelephonyTest {
}
@Test
+ public void testIsEnterpriseUse() throws Exception {
+ assertFalse(isEnterpriseUse());
+ assertFalse(mDc.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
+
+ setUpDefaultData(1);
+ replaceInstance(ConnectionParams.class, "mApnContext", mCp, mEnterpriseApnContext);
+ doReturn(mApn1).when(mEnterpriseApnContext).getApnSetting();
+ doReturn(ApnSetting.TYPE_ENTERPRISE_STRING).when(mEnterpriseApnContext).getApnType();
+ doReturn(ApnSetting.TYPE_ENTERPRISE).when(mEnterpriseApnContext).getApnTypeBitmask();
+ connectEvent(true);
+
+ assertTrue(isEnterpriseUse());
+ assertTrue(mDc.getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_ENTERPRISE));
+ }
+
+ @Test
@SmallTest
public void testGetDisallowedApnTypes() throws Exception {
mContextFixture.getCarrierConfigBundle().putStringArray(
@@ -1035,4 +1272,66 @@ public class DataConnectionTest extends TelephonyTest {
assertEquals(ApnSetting.TYPE_MMS | ApnSetting.TYPE_SUPL | ApnSetting.TYPE_FOTA,
getDisallowedApnTypes());
}
+
+ @Test
+ public void testIsSuspended() throws Exception {
+ // Return false if not active state
+ assertTrue(mDc.isInactive());
+ assertFalse(isSuspended());
+
+ // Return false for emergency APN
+ doReturn(mApn6).when(mApnContext).getApnSetting();
+ doReturn(ApnSetting.TYPE_EMERGENCY).when(mApnContext).getApnTypeBitmask();
+ connectEvent(true);
+ assertFalse(isSuspended());
+
+ // Back to DEFAULT APN
+ disconnectEvent();
+ assertTrue(mDc.isInactive());
+ doReturn(mApn1).when(mApnContext).getApnSetting();
+ doReturn(ApnSetting.TYPE_DEFAULT).when(mApnContext).getApnTypeBitmask();
+ doReturn(true).when(mSST).isConcurrentVoiceAndDataAllowed();
+ connectEvent(true);
+
+ // Before getting any service state event, the connection should not be suspended.
+ assertFalse(isSuspended());
+
+ // Return true if combined reg state is not in service
+ serviceStateChangedEvent(ServiceState.STATE_OUT_OF_SERVICE,
+ ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN);
+ assertTrue(isSuspended());
+
+ // Return false if in service and concurrent voice and data is allowed
+ serviceStateChangedEvent(ServiceState.STATE_IN_SERVICE,
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+ assertFalse(isSuspended());
+
+ // Return false if in service and concurrent voice/data not allowed but call state is idle
+ doReturn(false).when(mSST).isConcurrentVoiceAndDataAllowed();
+ doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
+ mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
+ waitForMs(100);
+ assertFalse(isSuspended());
+
+ // Return true if in service, concurrent voice/data not allowed, and call state not idle
+ doReturn(PhoneConstants.State.RINGING).when(mCT).getState();
+ mDc.sendMessage(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED);
+ waitForMs(100);
+ assertTrue(isSuspended());
+ }
+
+ @Test
+ public void testDataServiceTempUnavailable() throws Exception {
+ setFailedSetupDataResponse(DataServiceCallback.RESULT_ERROR_TEMPORARILY_UNAVAILABLE);
+ replaceInstance(ConnectionParams.class, "mRequestType", mCp,
+ DcTracker.REQUEST_TYPE_NORMAL);
+ // Verify that no data was setup
+ connectEvent(false);
+ assertTrue(mDc.isInactive());
+
+ // Verify that data service did not suggest any retry (i.e. Frameworks uses configured
+ // retry timer).
+ verify(mDataThrottler).setRetryTime(eq(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL),
+ eq(RetryManager.NO_SUGGESTED_RETRY_DELAY), eq(DcTracker.REQUEST_TYPE_NORMAL));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
index 52c9573a87..08fa5b640b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataEnabledSettingsTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.os.HandlerThread;
+import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
@@ -104,7 +105,7 @@ public class DataEnabledSettingsTest extends TelephonyTest {
@Test
@SmallTest
public void testSetAlwaysAllowMmsData() throws Exception {
- mDataEnabledSettingsUT.setUserDataEnabled(false);
+ mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, false);
assertTrue(mDataEnabledSettingsUT.setAlwaysAllowMmsData(true));
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
verify(mSubscriptionController).setDataEnabledOverrideRules(anyInt(),
@@ -120,7 +121,23 @@ public class DataEnabledSettingsTest extends TelephonyTest {
assertEquals("", stringCaptor.getValue());
assertFalse(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
- mDataEnabledSettingsUT.setUserDataEnabled(true);
+ mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_USER, true);
assertTrue(mDataEnabledSettingsUT.isDataEnabled(ApnSetting.TYPE_MMS));
}
+
+ @Test
+ @SmallTest
+ public void testSetThermalDataEnabled() throws Exception {
+ mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
+ false);
+ assertFalse(mDataEnabledSettingsUT.isDataEnabledForReason(
+ TelephonyManager.DATA_ENABLED_REASON_THERMAL));
+ assertFalse(mDataEnabledSettingsUT.isDataEnabled());
+
+ mDataEnabledSettingsUT.setDataEnabled(TelephonyManager.DATA_ENABLED_REASON_THERMAL,
+ true);
+ assertTrue(mDataEnabledSettingsUT.isDataEnabledForReason(
+ TelephonyManager.DATA_ENABLED_REASON_THERMAL));
+ assertTrue(mDataEnabledSettingsUT.isDataEnabled());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
index 55f621ab64..b641ecd6ff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
@@ -107,6 +107,33 @@ public class DataProfileTest extends TestCase {
-1, // mvno_type
""); // mnvo_match_data
+ private ApnSetting mApn4 = ApnSetting.makeApnSetting(
+ 2163, // id
+ "44010", // numeric
+ "sp-mode", // name
+ "fake_apn", // apn
+ null, // proxy
+ -1, // port
+ null, // mmsc
+ null, // mmsproxy
+ -1, // mmsport
+ "user", // user
+ "passwd", // password
+ -1, // authtype
+ ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_SUPL, // types
+ ApnSetting.PROTOCOL_IP, // protocol
+ ApnSetting.PROTOCOL_IP, // roaming_protocol
+ true, // carrier_enabled
+ 10360, // networktype_bitmask
+ 1234, // profile_id
+ false, // modem_cognitive
+ 111, // max_conns
+ 456, // wait_time
+ 789, // max_conns_time
+ 0, // mtu
+ -1, // mvno_type
+ ""); // mnvo_match_data
+
@SmallTest
public void testCreateFromApnSetting() throws Exception {
DataProfile dp = DcTracker.createDataProfile(mApn1, mApn1.getProfileId(), false);
@@ -132,11 +159,13 @@ public class DataProfileTest extends TestCase {
assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.getAuthType());
assertEquals(mApn3.getUser(), dp.getUserName());
assertEquals(mApn3.getPassword(), dp.getPassword());
- assertEquals(2, dp.getType()); // TYPE_3GPP2
+ assertEquals(0, dp.getType()); // TYPE_COMMON
assertEquals(mApn3.getWaitTime(), dp.getWaitTime());
assertEquals(mApn3.isEnabled(), dp.isEnabled());
int expectedBearerBitmap = mApn3.getNetworkTypeBitmask();
assertEquals(expectedBearerBitmap, dp.getBearerBitmask());
+ dp = DcTracker.createDataProfile(mApn4, mApn4.getProfileId(), false);
+ assertEquals(2, dp.getType()); // TYPE_3GPP2
}
@SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
new file mode 100644
index 0000000000..fb9a8b679b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
@@ -0,0 +1,235 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER;
+import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_NORMAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.ThrottleStatus;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.RetryManager;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Data throttler tests
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DataThrottlerTest extends TelephonyTest {
+
+ private static final boolean DBG = true;
+ private DataThrottler mDataThrottler;
+
+ @Mock
+ private DataThrottler.Callback mMockChangedCallback1;
+
+ @Mock
+ private DataThrottler.Callback mMockChangedCallback2;
+
+ private static final int DEFAULT_APN_TYPE = ApnSetting.TYPE_DEFAULT & ~(ApnSetting.TYPE_HIPRI);
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mDataThrottler = new DataThrottler(mPhone, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ mDataThrottler.registerForThrottleStatusChanges(mMockChangedCallback1);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Test the behavior of a retry manager with no waiting APNs set.
+ */
+ @Test
+ @SmallTest
+ public void testSetRetryTime() throws Exception {
+ final ArgumentCaptor<List<ThrottleStatus>> statusCaptor =
+ ArgumentCaptor.forClass((Class) List.class);
+
+ List<List<ThrottleStatus>> expectedStatuses = new ArrayList<>();
+ processAllMessages();
+ expectedStatuses.add(List.of());
+
+ mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT, 1234567890L,
+ REQUEST_TYPE_NORMAL);
+ processAllMessages();
+ assertEquals(1234567890L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
+ assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
+ mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
+
+ processAllMessages();
+ expectedStatuses.add(List.of(
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setApnType(ApnSetting.TYPE_HIPRI)
+ .setThrottleExpiryTimeMillis(1234567890L)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION)
+ .build(),
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setApnType(DEFAULT_APN_TYPE)
+ .setThrottleExpiryTimeMillis(1234567890L)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION)
+ .build())
+ );
+
+
+ mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, 13579L,
+ REQUEST_TYPE_HANDOVER);
+ processAllMessages();
+ assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
+ assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DUN));
+
+ processAllMessages();
+ expectedStatuses.add(List.of(
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setApnType(ApnSetting.TYPE_HIPRI)
+ .setThrottleExpiryTimeMillis(13579L)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_HANDOVER)
+ .build(),
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setApnType(ApnSetting.TYPE_DUN)
+ .setThrottleExpiryTimeMillis(13579L)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_HANDOVER)
+ .build(),
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setApnType(DEFAULT_APN_TYPE)
+ .setThrottleExpiryTimeMillis(13579L)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_HANDOVER)
+ .build())
+ );
+
+
+ mDataThrottler.setRetryTime(ApnSetting.TYPE_MMS, -10,
+ REQUEST_TYPE_NORMAL);
+ processAllMessages();
+ assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
+ mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
+ processAllMessages();
+ expectedStatuses.add(List.of(
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setNoThrottle()
+ .setApnType(ApnSetting.TYPE_MMS)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_NEW_CONNECTION)
+ .build()
+ ));
+
+ mDataThrottler.setRetryTime(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_EMERGENCY,
+ RetryManager.NO_RETRY, REQUEST_TYPE_HANDOVER);
+
+ processAllMessages();
+ expectedStatuses.add(List.of(
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setApnType(ApnSetting.TYPE_EMERGENCY)
+ .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_NONE)
+ .build(),
+ new ThrottleStatus.Builder()
+ .setSlotIndex(0)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setApnType(ApnSetting.TYPE_FOTA)
+ .setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
+ .setRetryType(ThrottleStatus.RETRY_TYPE_NONE)
+ .build()
+ ));
+
+ assertEquals(RetryManager.NO_RETRY, mDataThrottler.getRetryTime(ApnSetting.TYPE_FOTA));
+ assertEquals(RetryManager.NO_RETRY, mDataThrottler.getRetryTime(ApnSetting.TYPE_EMERGENCY));
+
+
+ // Loop through statuses and test everything
+ verify(mMockChangedCallback1, times(expectedStatuses.size()))
+ .onThrottleStatusChanged(statusCaptor.capture());
+
+ // Check actual statuses
+ List<List<ThrottleStatus>> actualStatuses =
+ (List<List<ThrottleStatus>>) statusCaptor.getAllValues();
+ assertEquals(expectedStatuses.size(), actualStatuses.size());
+
+ if (DBG) {
+ logd("expectedStatuses.size() = " + expectedStatuses.size());
+ logd("actualStatuses.size() = " + actualStatuses.size());
+ }
+
+ Comparator<ThrottleStatus> comparator = (o1, o2) ->
+ Integer.compare(o1.getApnType(), o2.getApnType());
+
+ for (int i = 0; i < expectedStatuses.size(); i++) {
+ List<ThrottleStatus> atsExpected = new ArrayList<>(expectedStatuses.get(i));
+ List<ThrottleStatus> atsActual = new ArrayList<>(actualStatuses.get(i));
+
+ atsExpected.sort(comparator);
+ atsActual.sort(comparator);
+ assertEquals("Lists at index " + i + " don't match",
+ atsExpected, atsActual);
+ }
+
+ this.mDataThrottler.registerForThrottleStatusChanges(mMockChangedCallback2);
+ }
+
+ /**
+ * Test the behavior of a retry manager with no waiting APNs set.
+ */
+ @Test
+ @SmallTest
+ public void testUnthrottle() throws Exception {
+ mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT, 1234567890L,
+ REQUEST_TYPE_NORMAL);
+ processAllMessages();
+ assertEquals(1234567890L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
+
+ mDataThrottler.reset();
+ processAllMessages();
+ assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
+ mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
index a0c9913e0a..3c8e7ab29c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
@@ -22,7 +22,6 @@ import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_G
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
@@ -34,7 +33,6 @@ import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.os.AsyncResult;
-import android.os.Handler;
import android.os.Looper;
import android.telephony.AccessNetworkConstants;
import android.telephony.data.ApnSetting;
@@ -103,9 +101,8 @@ public class DcControllerTest extends TelephonyTest {
doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
.when(mDataServiceManager).getTransportType();
- mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager,
- new Handler(Looper.myLooper()), "");
- mDcc.start();
+ mDcc = DcController.makeDcc(mPhone, mDcTracker, mDataServiceManager, Looper.myLooper(),
+ "");
processAllMessages();
}
@@ -117,11 +114,10 @@ public class DcControllerTest extends TelephonyTest {
@Test
@SmallTest
public void testDataDormant() throws Exception {
- assertEquals("DccDefaultState", getCurrentState().getName());
ArrayList<DataCallResponse> l = new ArrayList<>();
DataCallResponse dcResponse = new DataCallResponse.Builder()
.setCause(0)
- .setSuggestedRetryTime(-1)
+ .setRetryDurationMillis(-1)
.setId(1)
.setLinkStatus(DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT)
.setProtocolType(ApnSetting.PROTOCOL_IP)
@@ -140,7 +136,8 @@ public class DcControllerTest extends TelephonyTest {
mDc.mCid = 1;
mDcc.addActiveDcByCid(mDc);
- mDcc.sendMessage(EVENT_DATA_STATE_CHANGED, new AsyncResult(null, l, null));
+ mDcc.sendMessage(mDcc.obtainMessage(EVENT_DATA_STATE_CHANGED,
+ new AsyncResult(null, l, null)));
processAllMessages();
verify(mDcTracker, times(1)).sendStopNetStatPoll(eq(DctConstants.Activity.DORMANT));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 3bcf99f85a..ca071d7cba 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -22,18 +22,20 @@ import static com.android.internal.telephony.dataconnection.ApnSettingTest.creat
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -56,17 +58,22 @@ import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
import android.net.Uri;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Telephony;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.Annotation;
import android.telephony.CarrierConfigManager;
+import android.telephony.DataFailCause;
import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
@@ -75,14 +82,17 @@ import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
+import android.telephony.data.TrafficDescriptor;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import android.util.Pair;
+import android.util.SparseArray;
import androidx.test.filters.FlakyTest;
@@ -90,7 +100,9 @@ import com.android.internal.R;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.ISub;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
import org.junit.After;
import org.junit.Before;
@@ -98,6 +110,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -109,9 +122,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
public class DcTrackerTest extends TelephonyTest {
public static final String FAKE_APN1 = "FAKE APN 1";
@@ -120,11 +136,16 @@ public class DcTrackerTest extends TelephonyTest {
public static final String FAKE_APN4 = "FAKE APN 4";
public static final String FAKE_APN5 = "FAKE APN 5";
public static final String FAKE_APN6 = "FAKE APN 6";
+ public static final String FAKE_APN7 = "FAKE APN 7";
+ public static final String FAKE_APN8 = "FAKE APN 8";
+ public static final String FAKE_APN9 = "FAKE APN 9";
public static final String FAKE_IFNAME = "FAKE IFNAME";
public static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
public static final String FAKE_GATEWAY = "11.22.33.44";
public static final String FAKE_DNS = "55.66.77.88";
public static final String FAKE_ADDRESS = "99.88.77.66";
+ private static final int NETWORK_TYPE_NR_BITMASK =
+ 1 << (TelephonyManager.NETWORK_TYPE_NR - 1);
private static final int NETWORK_TYPE_LTE_BITMASK =
1 << (TelephonyManager.NETWORK_TYPE_LTE - 1);
private static final int NETWORK_TYPE_EHRPD_BITMASK =
@@ -195,8 +216,379 @@ public class DcTrackerTest extends TelephonyTest {
}
private class ApnSettingContentProvider extends MockContentProvider {
+ public final String[] FAKE_APN_COLUMNS = new String[]{
+ Telephony.Carriers._ID, Telephony.Carriers.NUMERIC,
+ Telephony.Carriers.NAME, Telephony.Carriers.APN,
+ Telephony.Carriers.PROXY, Telephony.Carriers.PORT,
+ Telephony.Carriers.MMSC, Telephony.Carriers.MMSPROXY,
+ Telephony.Carriers.MMSPORT, Telephony.Carriers.USER,
+ Telephony.Carriers.PASSWORD, Telephony.Carriers.AUTH_TYPE,
+ Telephony.Carriers.TYPE,
+ Telephony.Carriers.PROTOCOL,
+ Telephony.Carriers.ROAMING_PROTOCOL,
+ Telephony.Carriers.CARRIER_ENABLED, Telephony.Carriers.BEARER,
+ Telephony.Carriers.BEARER_BITMASK,
+ Telephony.Carriers.PROFILE_ID,
+ Telephony.Carriers.MODEM_PERSIST,
+ Telephony.Carriers.MAX_CONNECTIONS,
+ Telephony.Carriers.WAIT_TIME_RETRY,
+ Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS,
+ Telephony.Carriers.MTU,
+ Telephony.Carriers.MVNO_TYPE,
+ Telephony.Carriers.MVNO_MATCH_DATA,
+ Telephony.Carriers.NETWORK_TYPE_BITMASK,
+ Telephony.Carriers.APN_SET_ID,
+ Telephony.Carriers.CARRIER_ID,
+ Telephony.Carriers.SKIP_464XLAT
+ };
+
private int mPreferredApnSet = 0;
+ private Object[] mPreferredApn = null;
+
+ private String mFakeApn1Types = "default,supl";
+
+ private String mFakeApn5Types = "dun";
+
+ private int mFakeApn1Bitmask = NETWORK_TYPE_LTE_BITMASK;
+
+ private int mRowIdOffset = 0;
+
+ public void setFakeApn1Types(String apnTypes) {
+ mFakeApn1Types = apnTypes;
+ }
+
+ public void setFakeApn5Types(String apnTypes) {
+ mFakeApn5Types = apnTypes;
+ }
+
+ public void setFakeApn1NetworkTypeBitmask(int bitmask) {
+ mFakeApn1Bitmask = bitmask;
+ }
+
+ public void setRowIdOffset(int rowIdOffset) {
+ mRowIdOffset = rowIdOffset;
+ }
+
+ public void setFakePreferredApn(Object[] fakeApn) {
+ mPreferredApn = fakeApn;
+ }
+
+ public Object[] getFakeApn1() {
+ return new Object[]{
+ 2163 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "sp-mode", // name
+ FAKE_APN1, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ mFakeApn1Types, // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ mFakeApn1Bitmask, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn2() {
+ return new Object[]{
+ 2164 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "mopera U", // name
+ FAKE_APN2, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "default,supl", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer,
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn3() {
+ return new Object[]{
+ 2165 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "b-mobile for Nexus", // name
+ FAKE_APN3, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "ims", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ 0, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ 0, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn4() {
+ return new Object[]{
+ 2166 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "sp-mode ehrpd", // name
+ FAKE_APN4, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "default,supl", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn5() {
+ return new Object[]{
+ 2167 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "b-mobile for Nexus", // name
+ FAKE_APN5, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ mFakeApn5Types, // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ 0, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ 0, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn6() {
+ return new Object[]{
+ 2168 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "sp-mode", // name
+ FAKE_APN6, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "mms,xcap", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn7() {
+ return new Object[]{
+ 2169 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "sp-mode", // name
+ FAKE_APN7, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "default", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ 1, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn8() {
+ return new Object[]{
+ 2170 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "IMS", // name
+ FAKE_APN8, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "ims", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
+ -1, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
+ public Object[] getFakeApn9() {
+ return new Object[]{
+ 2171 + mRowIdOffset, // id
+ FAKE_PLMN, // numeric
+ "sp-mode nr", // name
+ FAKE_APN9, // apn
+ "", // proxy
+ "", // port
+ "", // mmsc
+ "", // mmsproxy
+ "", // mmsport
+ "", // user
+ "", // password
+ -1, // authtype
+ "default,enterprise", // types
+ "IP", // protocol
+ "IP", // roaming_protocol
+ 1, // carrier_enabled
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
+ 0, // bearer_bitmask
+ 0, // profile_id
+ 1, // modem_cognitive
+ 0, // max_conns
+ 0, // wait_time
+ 0, // max_conns_time
+ 0, // mtu
+ "", // mvno_type
+ "", // mnvo_match_data
+ NETWORK_TYPE_NR_BITMASK, // network_type_bitmask
+ 0, // apn_set_id
+ -1, // carrier_id
+ -1 // skip_464xlat
+ };
+ }
+
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
@@ -215,228 +607,16 @@ public class DcTrackerTest extends TelephonyTest {
if (projection == null) {
logd("Query '" + FAKE_PLMN + "' APN settings");
- MatrixCursor mc = new MatrixCursor(
- new String[]{Telephony.Carriers._ID, Telephony.Carriers.NUMERIC,
- Telephony.Carriers.NAME, Telephony.Carriers.APN,
- Telephony.Carriers.PROXY, Telephony.Carriers.PORT,
- Telephony.Carriers.MMSC, Telephony.Carriers.MMSPROXY,
- Telephony.Carriers.MMSPORT, Telephony.Carriers.USER,
- Telephony.Carriers.PASSWORD, Telephony.Carriers.AUTH_TYPE,
- Telephony.Carriers.TYPE,
- Telephony.Carriers.PROTOCOL,
- Telephony.Carriers.ROAMING_PROTOCOL,
- Telephony.Carriers.CARRIER_ENABLED, Telephony.Carriers.BEARER,
- Telephony.Carriers.BEARER_BITMASK,
- Telephony.Carriers.PROFILE_ID,
- Telephony.Carriers.MODEM_PERSIST,
- Telephony.Carriers.MAX_CONNECTIONS,
- Telephony.Carriers.WAIT_TIME_RETRY,
- Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS,
- Telephony.Carriers.MTU,
- Telephony.Carriers.MVNO_TYPE,
- Telephony.Carriers.MVNO_MATCH_DATA,
- Telephony.Carriers.NETWORK_TYPE_BITMASK,
- Telephony.Carriers.APN_SET_ID,
- Telephony.Carriers.CARRIER_ID,
- Telephony.Carriers.SKIP_464XLAT});
-
- mc.addRow(new Object[]{
- 2163, // id
- FAKE_PLMN, // numeric
- "sp-mode", // name
- FAKE_APN1, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "default,supl", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1 // skip_464xlat
- });
-
- mc.addRow(new Object[]{
- 2164, // id
- FAKE_PLMN, // numeric
- "mopera U", // name
- FAKE_APN2, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "default,supl", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer,
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1 // skip_464xlat
- });
-
- mc.addRow(new Object[]{
- 2165, // id
- FAKE_PLMN, // numeric
- "b-mobile for Nexus", // name
- FAKE_APN3, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "ims", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- "", // mvno_type
- "", // mnvo_match_data
- 0, // network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1 // skip_464xlat
- });
-
- mc.addRow(new Object[]{
- 2166, // id
- FAKE_PLMN, // numeric
- "sp-mode ehrpd", // name
- FAKE_APN4, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "default,supl", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_EHRPD_BITMASK, // network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1 // skip_464xlat
- });
-
- mc.addRow(new Object[]{
- 2167, // id
- FAKE_PLMN, // numeric
- "b-mobile for Nexus", // name
- FAKE_APN5, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "dun", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- 0, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- "", // mvno_type
- "", // mnvo_match_data
- 0, // network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1 // skip_464xlat
- });
-
- mc.addRow(new Object[]{
- 2168, // id
- FAKE_PLMN, // numeric
- "sp-mode", // name
- FAKE_APN6, // apn
- "", // proxy
- "", // port
- "", // mmsc
- "", // mmsproxy
- "", // mmsport
- "", // user
- "", // password
- -1, // authtype
- "mms,xcap", // types
- "IP", // protocol
- "IP", // roaming_protocol
- 1, // carrier_enabled
- ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
- 0, // bearer_bitmask
- 0, // profile_id
- 1, // modem_cognitive
- 0, // max_conns
- 0, // wait_time
- 0, // max_conns_time
- 0, // mtu
- "", // mvno_type
- "", // mnvo_match_data
- NETWORK_TYPE_LTE_BITMASK, // network_type_bitmask
- 0, // apn_set_id
- -1, // carrier_id
- -1 // skip_464xlat
- });
+ MatrixCursor mc = new MatrixCursor(FAKE_APN_COLUMNS);
+ mc.addRow(getFakeApn1());
+ mc.addRow(getFakeApn2());
+ mc.addRow(getFakeApn3());
+ mc.addRow(getFakeApn4());
+ mc.addRow(getFakeApn5());
+ mc.addRow(getFakeApn6());
+ mc.addRow(getFakeApn7());
+ mc.addRow(getFakeApn8());
+ mc.addRow(getFakeApn9());
return mc;
}
@@ -448,6 +628,15 @@ public class DcTrackerTest extends TelephonyTest {
mc.addRow(new Object[]{ mPreferredApnSet });
mc.addRow(new Object[]{ 0 });
return mc;
+ } else if (isPathPrefixMatch(uri,
+ Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "preferapn_no_update"))) {
+ if (mPreferredApn == null) {
+ return null;
+ } else {
+ MatrixCursor mc = new MatrixCursor(FAKE_APN_COLUMNS);
+ mc.addRow(mPreferredApn);
+ return mc;
+ }
}
return null;
@@ -458,6 +647,16 @@ public class DcTrackerTest extends TelephonyTest {
mPreferredApnSet = values.getAsInteger(Telephony.Carriers.APN_SET_ID);
return 1;
}
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
}
@Before
@@ -491,6 +690,8 @@ public class DcTrackerTest extends TelephonyTest {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 0);
+ doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mTransportManager)
+ .getPreferredTransport(anyInt());
doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
doReturn(true).when(mSST).getDesiredPowerState();
doReturn(true).when(mSST).getPowerStateFromCarrier();
@@ -551,7 +752,7 @@ public class DcTrackerTest extends TelephonyTest {
}
// Create a successful data response
- private static SetupDataCallResult createSetupDataCallResult() throws Exception {
+ private static SetupDataCallResult createSetupDataCallResult() {
SetupDataCallResult result = new SetupDataCallResult();
result.status = 0;
result.suggestedRetryTime = -1;
@@ -587,19 +788,16 @@ public class DcTrackerTest extends TelephonyTest {
}
private void verifyDataConnected(final String apnSetting) {
- verify(mPhone, atLeastOnce()).notifyDataConnection(
- eq(PhoneConstants.APN_TYPE_DEFAULT));
-
verify(mAlarmManager, times(1)).set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
any(PendingIntent.class));
- assertEquals(apnSetting, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
- assertArrayEquals(new String[]{PhoneConstants.APN_TYPE_DEFAULT}, mDct.getActiveApnTypes());
+ assertEquals(apnSetting, mDct.getActiveApnString(ApnSetting.TYPE_DEFAULT_STRING));
+ assertArrayEquals(new String[]{ApnSetting.TYPE_DEFAULT_STRING}, mDct.getActiveApnTypes());
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
+ assertTrue(mDct.isAnyDataConnected());
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
- LinkProperties linkProperties = mDct.getLinkProperties(PhoneConstants.APN_TYPE_DEFAULT);
+ LinkProperties linkProperties = mDct.getLinkProperties(ApnSetting.TYPE_DEFAULT_STRING);
assertEquals(FAKE_IFNAME, linkProperties.getInterfaceName());
assertEquals(1, linkProperties.getAddresses().size());
assertEquals(FAKE_ADDRESS, linkProperties.getAddresses().get(0).getHostAddress());
@@ -608,44 +806,68 @@ public class DcTrackerTest extends TelephonyTest {
assertEquals(FAKE_GATEWAY, linkProperties.getRoutes().get(0).getGateway().getHostAddress());
}
- private boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) {
+ private boolean isHandoverPending(int apnType) {
try {
- Method method = DcTracker.class.getDeclaredMethod("isDataAllowed",
- DataConnectionReasons.class);
+ Method method = DcTracker.class.getDeclaredMethod("isHandoverPending",
+ int.class);
method.setAccessible(true);
- return (boolean) method.invoke(mDct, dataConnectionReasons);
+ return (boolean) method.invoke(mDct, apnType);
} catch (Exception e) {
fail(e.toString());
return false;
}
}
+ private void addHandoverCompleteMsg(Message onCompleteMsg,
+ @Annotation.ApnType int apnType) {
+ try {
+ Method method = DcTracker.class.getDeclaredMethod("addHandoverCompleteMsg",
+ Message.class, int.class);
+ method.setAccessible(true);
+ method.invoke(mDct, onCompleteMsg, apnType);
+ } catch (Exception e) {
+ fail(e.toString());
+ }
+ }
+
private void sendInitializationEvents() {
- logd("Sending EVENT_CARRIER_CONFIG_CHANGED");
+ sendCarrierConfigChanged("");
+
+ sendSimStateUpdated("");
+
+ sendEventDataConnectionAttached("");
+
+ waitForMs(200);
+ }
+
+ private void sendCarrierConfigChanged(String messagePrefix) {
+ logd(messagePrefix + "Sending EVENT_CARRIER_CONFIG_CHANGED");
mDct.sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ }
- logd("Sending EVENT_SIM_STATE_UPDATED");
+ private void sendSimStateUpdated(String messagePrefix) {
+ logd(messagePrefix + "Sending EVENT_SIM_STATE_UPDATED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_SIM_STATE_UPDATED,
TelephonyManager.SIM_STATE_LOADED, 0));
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ }
- logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+ private void sendEventDataConnectionAttached(String messagePrefix) {
+ logd(messagePrefix + "Sending EVENT_DATA_CONNECTION_ATTACHED");
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
-
- waitForMs(200);
}
// Test the unmetered APN setup when data is disabled.
@Test
@SmallTest
public void testTrySetupDataUnmeteredDefaultNotSelected() throws Exception {
- initApns(PhoneConstants.APN_TYPE_XCAP, new String[]{PhoneConstants.APN_TYPE_XCAP});
+ initApns(ApnSetting.TYPE_XCAP_STRING, new String[]{ApnSetting.TYPE_XCAP_STRING});
doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mIsub).getDefaultDataSubId();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
sendInitializationEvents();
@@ -657,17 +879,15 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the normal data call setup scenario.
@Test
@MediumTest
public void testDataSetup() throws Exception {
- mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
-
DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean allowed = isDataAllowed(dataConnectionReasons);
+ boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
assertFalse(dataConnectionReasons.toString(), allowed);
logd("Sending EVENT_ENABLE_APN");
@@ -678,7 +898,7 @@ public class DcTrackerTest extends TelephonyTest {
sendInitializationEvents();
dataConnectionReasons = new DataConnectionReasons();
- allowed = isDataAllowed(dataConnectionReasons);
+ allowed = mDct.isDataAllowed(dataConnectionReasons);
assertTrue(dataConnectionReasons.toString(), allowed);
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
@@ -686,7 +906,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
verifyDataConnected(FAKE_APN1);
@@ -709,7 +929,7 @@ public class DcTrackerTest extends TelephonyTest {
mSimulatedCommands.setDataCallResult(true, result);
DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean allowed = isDataAllowed(dataConnectionReasons);
+ boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
assertFalse(dataConnectionReasons.toString(), allowed);
logd("Sending EVENT_ENABLE_APN");
@@ -720,7 +940,7 @@ public class DcTrackerTest extends TelephonyTest {
sendInitializationEvents();
dataConnectionReasons = new DataConnectionReasons();
- allowed = isDataAllowed(dataConnectionReasons);
+ allowed = mDct.isDataAllowed(dataConnectionReasons);
assertTrue(dataConnectionReasons.toString(), allowed);
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
@@ -728,17 +948,16 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
// This time we'll let RIL command succeed.
mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
//Send event for reconnecting data
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
- mPhone.getPhoneId(), AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- mApnContext));
+ mPhone.getPhoneId(), DcTracker.REQUEST_TYPE_NORMAL, mApnContext));
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
// Data connection is running on a different thread. Have to wait.
@@ -748,7 +967,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
// Verify connected with APN2 setting.
@@ -759,11 +978,11 @@ public class DcTrackerTest extends TelephonyTest {
@MediumTest
@Ignore
@FlakyTest
- public void testUserDisableData() throws Exception {
+ public void testUserDisableData() {
//step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
//set Default and MMS to be metered in the CarrierConfigManager
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -773,7 +992,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, NETWORK_TYPE_LTE_BITMASK);
logd("Sending DATA_DISABLED_CMD");
@@ -790,16 +1009,16 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
- assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
+ assertTrue(mDct.isAnyDataConnected());
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
}
@Test
@MediumTest
- public void testTrySetupDataMmsAllowedDataDisabled() throws Exception {
+ public void testTrySetupDataMmsAllowedDataDisabled() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_MMS, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -809,8 +1028,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
-
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
List<DataProfile> dataProfiles = dpCaptor.getAllValues();
assertEquals(2, dataProfiles.size());
@@ -840,10 +1058,9 @@ public class DcTrackerTest extends TelephonyTest {
waitForMs(200);
// expected tear down all metered DataConnections
verify(mSimulatedCommandsVerifier, times(2)).deactivateDataCall(
- eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
- any(Message.class));
- assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
- assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_MMS));
+ anyInt(), eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_MMS_STRING));
clearInvocations(mSimulatedCommandsVerifier);
doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
@@ -855,14 +1072,85 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
- assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_MMS));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
+ }
+
+ @Test
+ @MediumTest
+ public void testTrySetupDataMmsAlwaysAllowedDataDisabled() {
+ mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
+ mApnSettingContentProvider.setFakeApn1Types("mms,xcap,default");
+ mDct.enableApn(ApnSetting.TYPE_MMS, DcTracker.REQUEST_TYPE_NORMAL, null);
+ sendInitializationEvents();
+
+ // Verify MMS was set up and is connected
+ ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+ verify(mSimulatedCommandsVerifier).setupDataCall(
+ eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+ verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0,
+ ApnSetting.TYPE_MMS | ApnSetting.TYPE_XCAP | ApnSetting.TYPE_DEFAULT,
+ 1, NETWORK_TYPE_LTE_BITMASK);
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
+
+ // Verify DC has all capabilities specified in fakeApn1Types
+ Map<Integer, ApnContext> apnContexts = mDct.getApnContexts().stream().collect(
+ Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+ assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
+ .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
+ assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
+ .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP));
+ assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
+ .getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET));
+
+ // Disable mobile data
+ doReturn(false).when(mDataEnabledSettings).isDataEnabled();
+ doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
+ doReturn(false).when(mDataEnabledSettings).isMmsAlwaysAllowed();
+ mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ // Expected tear down all metered DataConnections
+ waitForMs(200);
+ verify(mSimulatedCommandsVerifier).deactivateDataCall(
+ anyInt(), eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_MMS_STRING));
+
+ // Allow MMS unconditionally
+ clearInvocations(mSimulatedCommandsVerifier);
+ doReturn(true).when(mDataEnabledSettings).isMmsAlwaysAllowed();
+ doReturn(true).when(mDataEnabledSettings).isDataEnabled(ApnSetting.TYPE_MMS);
+ mDct.obtainMessage(DctConstants.EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED).sendToTarget();
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ // Verify MMS was set up and is connected
+ waitForMs(200);
+ verify(mSimulatedCommandsVerifier).setupDataCall(
+ eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_MMS_STRING));
+
+ // Ensure MMS data connection has the MMS capability only.
+ apnContexts = mDct.getApnContexts().stream().collect(
+ Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+ assertTrue(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
+ .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS));
+ assertFalse(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
+ .getNetworkCapabilities().hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP));
+ assertFalse(apnContexts.get(ApnSetting.TYPE_MMS).getDataConnection()
+ .getNetworkCapabilities().hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET));
}
@Test
@MediumTest
- public void testUserDisableRoaming() throws Exception {
+ public void testUserDisableRoaming() {
//step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
//step 2: set roaming disabled, data is enabled
//step 3: under roaming service
@@ -872,7 +1160,7 @@ public class DcTrackerTest extends TelephonyTest {
boolean roamingEnabled = mDct.getDataRoamingEnabled();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
waitForHandlerAction(mDct, 1000);
@@ -885,7 +1173,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
//user is in roaming
@@ -901,9 +1189,9 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
- assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
+ assertTrue(mDct.isAnyDataConnected());
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
// reset roaming settings / data enabled settings at end of this test
mDct.setDataRoamingEnabledByUser(roamingEnabled);
@@ -912,7 +1200,7 @@ public class DcTrackerTest extends TelephonyTest {
@Test
@MediumTest
- public void testDataCallOnUserDisableRoaming() throws Exception {
+ public void testDataCallOnUserDisableRoaming() {
//step 1: mock under roaming service and user disabled roaming from settings.
//step 2: user toggled data settings on
//step 3: only non-metered data call is established
@@ -922,7 +1210,7 @@ public class DcTrackerTest extends TelephonyTest {
//set Default and MMS to be metered in the CarrierConfigManager
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -936,12 +1224,12 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64, 0, 0);
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
- assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
+ assertTrue(mDct.isAnyDataConnected());
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_IMS_STRING));
// reset roaming settings / data enabled settings at end of this test
mDct.setDataRoamingEnabledByUser(roamingEnabled);
@@ -968,13 +1256,13 @@ public class DcTrackerTest extends TelephonyTest {
@Ignore
@Test
@MediumTest
- public void testCarrierActionSetMeteredApnsEnabled() throws Exception {
+ public void testCarrierActionSetMeteredApnsEnabled() {
//step 1: setup two DataCalls one for Internet and IMS
//step 2: set data is enabled
//step 3: cold sim is detected
//step 4: all data connection is torn down
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -985,9 +1273,9 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, NETWORK_TYPE_LTE_BITMASK);
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+ assertTrue(mDct.isAnyDataConnected());
AsyncResult ar = new AsyncResult(null,
new Pair<>(false, DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER), null);
@@ -1000,8 +1288,8 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
any(Message.class));
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
- assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
+ assertTrue(mDct.isAnyDataConnected());
+ assertEquals(DctConstants.State.IDLE, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
}
private void initApns(String targetApn, String[] canHandleTypes) {
@@ -1023,9 +1311,9 @@ public class DcTrackerTest extends TelephonyTest {
// Test the emergency APN setup.
@Test
@SmallTest
- public void testTrySetupDataEmergencyApn() throws Exception {
- initApns(PhoneConstants.APN_TYPE_EMERGENCY,
- new String[]{PhoneConstants.APN_TYPE_EMERGENCY});
+ public void testTrySetupDataEmergencyApn() {
+ initApns(ApnSetting.TYPE_EMERGENCY_STRING,
+ new String[]{ApnSetting.TYPE_EMERGENCY_STRING});
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
@@ -1033,30 +1321,191 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), any(Message.class));
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(),
+ anyBoolean(), any(Message.class));
}
// Test the XCAP APN setup.
@Test
@SmallTest
- public void testTrySetupDataXcapApn() throws Exception {
- initApns(PhoneConstants.APN_TYPE_XCAP, new String[]{PhoneConstants.APN_TYPE_XCAP});
+ public void testTrySetupDataXcapApn() {
+ initApns(ApnSetting.TYPE_XCAP_STRING, new String[]{ApnSetting.TYPE_XCAP_STRING});
mDct.enableApn(ApnSetting.TYPE_XCAP, DcTracker.REQUEST_TYPE_NORMAL, null);
sendInitializationEvents();
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
- eq(DataService.REQUEST_REASON_NORMAL), any(), any(Message.class));
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), any(),
+ anyBoolean(), any(Message.class));
+ }
+
+ // Test the ENTERPRISE APN setup.
+ @Test
+ public void testTrySetupDataEnterpriseApn() {
+ mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
+ sendInitializationEvents();
+
+ ArgumentCaptor<TrafficDescriptor> tdCaptor =
+ ArgumentCaptor.forClass(TrafficDescriptor.class);
+ verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+ eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
+ assertEquals(null, tdCaptor.getValue().getOsAppId());
+
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ SetupDataCallResult result = createSetupDataCallResult();
+ result.cid = 10;
+ mSimulatedCommands.setDataCallResult(true, result);
+ mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForMs(200);
+
+ verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+ eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ assertEquals(null, tdCaptor.getValue().getDataNetworkName());
+ assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
+ tdCaptor.getValue().getOsAppId()));
+ }
+
+ // Test the ENTERPRISE APN setup when default data is not set up yet.
+ @Test
+ public void testTrySetupDataEnterpriseApnNoDefaultData() {
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
+ sendInitializationEvents();
+
+ ArgumentCaptor<TrafficDescriptor> tdCaptor =
+ ArgumentCaptor.forClass(TrafficDescriptor.class);
+ verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+ eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ assertEquals(null, tdCaptor.getValue().getDataNetworkName());
+ assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
+ tdCaptor.getValue().getOsAppId()));
+
+ // Check APN contexts with no DEFAULT set up
+ Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+ assertEquals(DctConstants.State.IDLE, apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
+ assertEquals(DctConstants.State.FAILED,
+ apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
+
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ waitForMs(200);
+
+ verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+ eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
+ assertEquals(null, tdCaptor.getValue().getOsAppId());
+
+ // Check APN contexts after DEFAULT is set up (and ENTERPRISE failure)
+ apnContexts = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+ assertEquals(DctConstants.State.CONNECTED,
+ apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
+ assertEquals(DctConstants.State.FAILED,
+ apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
+
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ SetupDataCallResult result = createSetupDataCallResult();
+ result.cid = 10;
+ mSimulatedCommands.setDataCallResult(true, result);
+ mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ waitForMs(200);
+
+ verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
+ eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ assertEquals(null, tdCaptor.getValue().getDataNetworkName());
+ assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
+ tdCaptor.getValue().getOsAppId()));
+
+ // Check APN contexts after DEFAULT is set up (and ENTERPRISE reenabled)
+ apnContexts = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+ assertEquals(DctConstants.State.CONNECTED,
+ apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
+ assertEquals(DctConstants.State.CONNECTED,
+ apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
+ }
+
+ // Test the ENTERPRISE APN setup when the same CID is returned.
+ @Test
+ public void testTrySetupDataEnterpriseApnDuplicateCid() {
+ mApnSettingContentProvider.setFakeApn1NetworkTypeBitmask(
+ NETWORK_TYPE_LTE_BITMASK | NETWORK_TYPE_NR_BITMASK);
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ // mSimulatedCommandsVerifier will return the same CID in SetupDataCallResult
+ mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
+ mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
+ sendInitializationEvents();
+ waitForMs(200);
+
+ ArgumentCaptor<TrafficDescriptor> tdCaptor =
+ ArgumentCaptor.forClass(TrafficDescriptor.class);
+ verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
+ eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ List<TrafficDescriptor> tds = tdCaptor.getAllValues();
+ // [0] is default and [1] is enterprise, since default should be set up first
+ assertEquals(FAKE_APN1, tds.get(0).getDataNetworkName());
+ assertEquals(null, tds.get(0).getOsAppId());
+ assertEquals(null, tds.get(1).getDataNetworkName());
+ assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(), tds.get(1).getOsAppId()));
+
+ // Check APN contexts after DEFAULT and ENTERPRISE set up
+ Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+ assertEquals(DctConstants.State.CONNECTED,
+ apnContexts.get(ApnSetting.TYPE_DEFAULT).getState());
+ assertEquals(DctConstants.State.FAILED,
+ apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState());
}
@Test
@SmallTest
- public void testGetDataConnectionState() throws Exception {
- initApns(PhoneConstants.APN_TYPE_SUPL,
- new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_DEFAULT});
+ public void testGetDataConnectionState() {
+ initApns(ApnSetting.TYPE_SUPL_STRING,
+ new String[]{ApnSetting.TYPE_SUPL_STRING, ApnSetting.TYPE_DEFAULT_STRING});
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_SUPL, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -1064,20 +1513,20 @@ public class DcTrackerTest extends TelephonyTest {
// Assert that both APN_TYPE_SUPL & APN_TYPE_DEFAULT are connected even we only setup data
// for APN_TYPE_SUPL
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_SUPL));
- assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_SUPL_STRING));
+ assertEquals(DctConstants.State.CONNECTED, mDct.getState(ApnSetting.TYPE_DEFAULT_STRING));
}
// Test the unmetered APN setup when data is disabled.
@Test
@SmallTest
- public void testTrySetupDataUnmeteredDataDisabled() throws Exception {
- initApns(PhoneConstants.APN_TYPE_SUPL, new String[]{PhoneConstants.APN_TYPE_SUPL});
+ public void testTrySetupDataUnmeteredDataDisabled() {
+ initApns(ApnSetting.TYPE_SUPL_STRING, new String[]{ApnSetting.TYPE_SUPL_STRING});
doReturn(false).when(mDataEnabledSettings).isDataEnabled();
doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_FOTA});
+ new String[]{ApnSetting.TYPE_FOTA_STRING});
mDct.enableApn(ApnSetting.TYPE_SUPL, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -1086,20 +1535,20 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the unmetered default APN setup when data is disabled. Default APN should always honor
// the users's setting.
@Test
@SmallTest
- public void testTrySetupDataUnmeteredDefaultDataDisabled() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ public void testTrySetupDataUnmeteredDefaultDataDisabled() {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
doReturn(false).when(mDataEnabledSettings).isDataEnabled();
doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -1108,20 +1557,20 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, never()).setupDataCall(
eq(AccessNetworkType.EUTRAN), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the metered APN setup when data is disabled.
@Test
@SmallTest
- public void testTrySetupMeteredDataDisabled() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ public void testTrySetupMeteredDataDisabled() {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
doReturn(false).when(mDataEnabledSettings).isDataEnabled();
doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -1129,19 +1578,19 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the restricted data request when data is disabled.
@Test
@SmallTest
- public void testTrySetupRestrictedDataDisabled() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ public void testTrySetupRestrictedDataDisabled() {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
doReturn(false).when(mDataEnabledSettings).isDataEnabled();
doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
sendInitializationEvents();
@@ -1156,18 +1605,18 @@ public class DcTrackerTest extends TelephonyTest {
waitForMs(200);
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the restricted data request when roaming is disabled.
@Test
@SmallTest
- public void testTrySetupRestrictedRoamingDisabled() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ public void testTrySetupRestrictedRoamingDisabled() {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_DEFAULT_STRING});
mDct.setDataRoamingEnabledByUser(false);
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
//user is in roaming
doReturn(true).when(mServiceState).getDataRoaming();
@@ -1184,32 +1633,32 @@ public class DcTrackerTest extends TelephonyTest {
waitForMs(200);
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the default data when data is not connectable.
@Test
@SmallTest
- public void testTrySetupNotConnectable() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ public void testTrySetupNotConnectable() {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
doReturn(false).when(mApnContext).isConnectable();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
sendInitializationEvents();
verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the default data on IWLAN.
@Test
@SmallTest
- public void testTrySetupDefaultOnIWLAN() throws Exception {
+ public void testTrySetupDefaultOnIWLAN() {
doReturn(true).when(mTransportManager).isInLegacyMode();
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
.setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_IWLAN)
.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
@@ -1218,30 +1667,30 @@ public class DcTrackerTest extends TelephonyTest {
anyInt(), anyInt());
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
sendInitializationEvents();
verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test the default data when the phone is in ECBM.
@Test
@SmallTest
- public void testTrySetupDefaultInECBM() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ public void testTrySetupDefaultInECBM() {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
doReturn(true).when(mPhone).isInEcm();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
sendInitializationEvents();
verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
}
// Test update waiting apn list when on data rat change
@@ -1249,7 +1698,7 @@ public class DcTrackerTest extends TelephonyTest {
@Ignore
@Test
@SmallTest
- public void testUpdateWaitingApnListOnDataRatChange() throws Exception {
+ public void testUpdateWaitingApnListOnDataRatChange() {
mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
.setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
@@ -1257,9 +1706,9 @@ public class DcTrackerTest extends TelephonyTest {
doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
anyInt(), anyInt());
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
sendInitializationEvents();
@@ -1268,9 +1717,9 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier).setupDataCall(
eq(AccessNetworkType.CDMA2000), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 21, 2, NETWORK_TYPE_EHRPD_BITMASK);
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+ assertTrue(mDct.isAnyDataConnected());
//data rat change from ehrpd to lte
logd("Sending EVENT_DATA_RAT_CHANGED");
@@ -1294,10 +1743,9 @@ public class DcTrackerTest extends TelephonyTest {
anyLong(), any(PendingIntent.class));
//Send event for reconnecting data
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RECONNECT,
- mPhone.getPhoneId(), AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
- mApnContext));
+ mPhone.getPhoneId(), DcTracker.RELEASE_TYPE_NORMAL, mApnContext));
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
// Data connection is running on a different thread. Have to wait.
@@ -1306,9 +1754,9 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+ assertTrue(mDct.isAnyDataConnected());
}
// Test for fetchDunApns()
@@ -1339,16 +1787,14 @@ public class DcTrackerTest extends TelephonyTest {
@Test
@SmallTest
public void testFetchDunApnWithPreferredApnSet() {
- logd("Sending EVENT_CARRIER_CONFIG_CHANGED");
- mDct.sendEmptyMessage(DctConstants.EVENT_CARRIER_CONFIG_CHANGED);
- waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ sendCarrierConfigChanged("testFetchDunApnWithPreferredApnSet: ");
// apnSetId=1
String dunApnString1 = "[ApnSettingV5]HOT mobile PC,pc.hotm,,,,,,,,,440,10,,DUN,,,true,"
+ "0,,,,,,,,,,1";
// apnSetId=0
String dunApnString2 = "[ApnSettingV5]HOT mobile PC,pc.coldm,,,,,,,,,440,10,,DUN,,,true,"
- + "0,,,,,,,,,,0";
+ + "0,,,,,,,,,,2";
ApnSetting dunApnExpected = ApnSetting.fromString(dunApnString1);
@@ -1362,15 +1808,239 @@ public class DcTrackerTest extends TelephonyTest {
cr.update(PREFERAPN_URI, values, null, null);
// return APN from Setting with apnSetId=1
- ArrayList<ApnSetting> dunApns = mDct.sortApnListByPreferred(mDct.fetchDunApns());
- assertEquals(2, dunApns.size());
+ ArrayList<ApnSetting> dunApns = mDct.fetchDunApns();
+ assertEquals(1, dunApns.size());
+ assertEquals(1, dunApns.get(0).getApnSetId());
+ assertTrue(dunApnExpected.equals(dunApns.get(0)));
+
+ // set that we prefer apn set 2
+ values = new ContentValues();
+ values.put(Telephony.Carriers.APN_SET_ID, 2);
+ cr.update(PREFERAPN_URI, values, null, null);
+
+ // return APN from Setting with apnSetId=2
+ dunApns = mDct.fetchDunApns();
+ assertEquals(1, dunApns.size());
+ assertEquals(2, dunApns.get(0).getApnSetId());
+ dunApnExpected = ApnSetting.fromString(dunApnString2);
assertTrue(dunApnExpected.equals(dunApns.get(0)));
}
+ @Test
+ @SmallTest
+ public void testFetchDunApnWhileRoaming() {
+ doReturn(true).when(mServiceState).getRoaming();
+ mBundle.putBoolean(CarrierConfigManager
+ .KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, true);
+
+ sendInitializationEvents();
+
+ String dunApnString = "[ApnSettingV3]HOT mobile PC,pc.hotm,,,,,,,,,440,10,,DUN,,,true,"
+ + "0,,,,,,,,";
+
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.TETHER_DUN_APN, dunApnString);
+
+ DcTracker spyDct = spy(mDct);
+ doReturn(true).when(spyDct).isPreferredApnUserEdited();
+ // Expect non-empty DUN APN list
+ assertEquals(1, spyDct.fetchDunApns().size());
+
+ doReturn(false).when(spyDct).isPreferredApnUserEdited();
+ // Expect empty DUN APN list
+ assertEquals(0, spyDct.fetchDunApns().size());
+
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.TETHER_DUN_APN, null);
+ }
+
+ /**
+ * Test that fetchDunApns() returns list that prioritize the preferred APN when the preferred
+ * APN including DUN type.
+ */
+ @Test
+ public void testFetchDunApnWithPreferredApn() {
+ // Set support APN types of FAKE_APN1 and FAKE_APN5
+ mApnSettingContentProvider.setFakeApn1Types("default,dun");
+ mApnSettingContentProvider.setFakeApn5Types("default,dun");
+
+ // Set prefer apn set id.
+ ContentResolver cr = mContext.getContentResolver();
+ ContentValues values = new ContentValues();
+ values.put(Telephony.Carriers.APN_SET_ID, 0);
+ cr.update(PREFERAPN_URI, values, null, null);
+ // Set FAKE_APN5 as the preferred APN.
+ mApnSettingContentProvider.setFakePreferredApn(mApnSettingContentProvider.getFakeApn5());
+
+ sendInitializationEvents();
+
+ // Return the APN list that set the preferred APN at the top.
+ ArrayList<ApnSetting> dunApns = mDct.fetchDunApns();
+ assertEquals(2, dunApns.size());
+ assertEquals(FAKE_APN5, dunApns.get(0).getApnName());
+ assertEquals(FAKE_APN1, dunApns.get(1).getApnName());
+ }
+
+ // This tests simulates the race case where the sim status change event is triggered, the
+ // default data connection is attached, and then the carrier config gets changed which bumps
+ // the database id which we want to ignore when cleaning up connections and matching against
+ // the dun APN. Tests b/158908392.
+ @Test
+ @SmallTest
+ public void testCheckForCompatibleDataConnectionWithDunWhenIdsChange() {
+ //Set dun as a support apn type of FAKE_APN1
+ mApnSettingContentProvider.setFakeApn1Types("default,supl,dun");
+
+ // Enable the default apn
+ mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ //Load the sim and attach the data connection without firing the carrier changed event
+ final String logMsgPrefix = "testCheckForCompatibleDataConnectionWithDunWhenIdsChange: ";
+ sendSimStateUpdated(logMsgPrefix);
+ sendEventDataConnectionAttached(logMsgPrefix);
+ waitForMs(200);
+
+ // Confirm that FAKE_APN1 comes up as a dun candidate
+ ApnSetting dunApn = mDct.fetchDunApns().get(0);
+ assertEquals(dunApn.getApnName(), FAKE_APN1);
+ Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+
+ //Double check that the default apn content is connected while the dun apn context is not
+ assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getState(),
+ DctConstants.State.CONNECTED);
+ assertNotEquals(apnContexts.get(ApnSetting.TYPE_DUN).getState(),
+ DctConstants.State.CONNECTED);
+
+
+ //Change the row ids the same way as what happens when we have old apn values in the
+ //carrier table
+ mApnSettingContentProvider.setRowIdOffset(100);
+ sendCarrierConfigChanged(logMsgPrefix);
+ waitForMs(200);
+
+ mDct.enableApn(ApnSetting.TYPE_DUN, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ Map<Integer, ApnContext> apnContextsAfterRowIdsChanged = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+
+ //Make sure that the data connection used earlier wasn't cleaned up and still in use.
+ assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
+ apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DEFAULT).getDataConnection());
+
+ //Check that the DUN is using the same active data connection
+ assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
+ apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DUN).getDataConnection());
+ }
+
+ @Test
+ @SmallTest
+ public void testCheckForCompatibleDataConnectionWithEnterprise() {
+ // Allow both DEFAULT and ENTERPRISE to use APN 1
+ mApnSettingContentProvider.setFakeApn1NetworkTypeBitmask(
+ NETWORK_TYPE_LTE_BITMASK | NETWORK_TYPE_NR_BITMASK);
+
+ // Enable the DEFAULT APN
+ mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ sendInitializationEvents();
+
+ ArgumentCaptor<TrafficDescriptor> tdCaptor =
+ ArgumentCaptor.forClass(TrafficDescriptor.class);
+ verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+ eq(AccessNetworkType.EUTRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ assertEquals(FAKE_APN1, tdCaptor.getValue().getDataNetworkName());
+ assertEquals(null, tdCaptor.getValue().getOsAppId());
+
+ // Check APN contexts after DEFAULT is set up
+ Map<Integer, ApnContext> apnContexts = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+ assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getState(),
+ DctConstants.State.CONNECTED);
+ assertNotEquals(apnContexts.get(ApnSetting.TYPE_ENTERPRISE).getState(),
+ DctConstants.State.CONNECTED);
+
+ // Enable the ENTERPRISE APN
+ mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .build();
+ doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
+ anyInt(), anyInt());
+ SetupDataCallResult result = createSetupDataCallResult();
+ result.cid = 10;
+ mSimulatedCommands.setDataCallResult(true, result);
+ mDct.enableApn(ApnSetting.TYPE_ENTERPRISE, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForMs(200);
+
+ verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+ eq(AccessNetworkType.NGRAN), any(DataProfile.class), eq(false), eq(false),
+ eq(DataService.REQUEST_REASON_NORMAL), any(), anyInt(), any(), tdCaptor.capture(),
+ anyBoolean(), any(Message.class));
+ assertEquals(null, tdCaptor.getValue().getDataNetworkName());
+ assertTrue(Arrays.equals(DataConnection.getEnterpriseOsAppId(),
+ tdCaptor.getValue().getOsAppId()));
+
+ // Check APN contexts after ENTERPRISE is set up
+ Map<Integer, ApnContext> apnContextsAfterRowIdsChanged = mDct.getApnContexts()
+ .stream().collect(Collectors.toMap(ApnContext::getApnTypeBitmask, x -> x));
+
+ // Make sure that the data connection used earlier wasn't cleaned up and still in use.
+ assertEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
+ apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_DEFAULT).getDataConnection());
+
+ // Check that ENTERPRISE isn't using the same data connection as DEFAULT
+ assertNotEquals(apnContexts.get(ApnSetting.TYPE_DEFAULT).getDataConnection(),
+ apnContextsAfterRowIdsChanged.get(ApnSetting.TYPE_ENTERPRISE).getDataConnection());
+ }
+
+ // Test for Data setup with APN Set ID
+ @Test
+ @SmallTest
+ public void testDataSetupWithApnSetId() throws Exception {
+ // Set the prefer apn set id to "1"
+ ContentResolver cr = mContext.getContentResolver();
+ ContentValues values = new ContentValues();
+ values.put(Telephony.Carriers.APN_SET_ID, 1);
+ cr.update(PREFERAPN_URI, values, null, null);
+
+ mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
+ mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
+
+ sendInitializationEvents();
+
+ ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+ verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
+ eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
+ eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+
+ List<DataProfile> dataProfiles = dpCaptor.getAllValues();
+ assertEquals(2, dataProfiles.size());
+
+ // Verify to use FAKE APN7 which is Default APN with apnSetId=1(Same as the pereferred
+ // APN's set id).
+ Optional<DataProfile> fakeApn7 = dataProfiles.stream()
+ .filter(dp -> dp.getApn().equals(FAKE_APN7)).findFirst();
+ assertTrue(fakeApn7.isPresent());
+ verifyDataProfile(fakeApn7.get(), FAKE_APN7, 0, 17, 1, NETWORK_TYPE_LTE_BITMASK);
+
+ // Verify to use FAKE APN8 which is IMS APN with apnSetId=-1
+ // (Telephony.Carriers.MATCH_ALL_APN_SET_ID).
+ Optional<DataProfile> fakeApn8 = dataProfiles.stream()
+ .filter(dp -> dp.getApn().equals(FAKE_APN8)).findFirst();
+ assertTrue(fakeApn8.isPresent());
+ verifyDataProfile(fakeApn8.get(), FAKE_APN8, 2, 64, 1, NETWORK_TYPE_LTE_BITMASK);
+ }
+
// Test oos
@Test
@SmallTest
- public void testDataRatChangeOOS() throws Exception {
+ public void testDataRatChangeOOS() {
mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
.setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EHRPD)
.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
@@ -1379,9 +2049,9 @@ public class DcTrackerTest extends TelephonyTest {
anyInt(), anyInt());
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING});
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
sendInitializationEvents();
@@ -1390,9 +2060,9 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier).setupDataCall(
eq(AccessNetworkType.CDMA2000), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 21, 2, NETWORK_TYPE_EHRPD_BITMASK);
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+ assertTrue(mDct.isAnyDataConnected());
// Data rat change from ehrpd to unknown due to OOS
logd("Sending EVENT_DATA_RAT_CHANGED");
@@ -1423,8 +2093,8 @@ public class DcTrackerTest extends TelephonyTest {
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
// Verify the same data connection
- assertEquals(FAKE_APN4, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
- assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+ assertEquals(FAKE_APN4, mDct.getActiveApnString(ApnSetting.TYPE_DEFAULT_STRING));
+ assertTrue(mDct.isAnyDataConnected());
}
// Test provisioning
@@ -1504,9 +2174,9 @@ public class DcTrackerTest extends TelephonyTest {
@Test
@SmallTest
- public void testNetworkStatusChangedRecoveryOFF() throws Exception {
+ public void testNetworkStatusChangedRecoveryOFF() {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -1516,7 +2186,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
logd("Sending EVENT_NETWORK_STATUS_CHANGED");
@@ -1538,14 +2208,14 @@ public class DcTrackerTest extends TelephonyTest {
@FlakyTest
@Test
@SmallTest
- public void testNetworkStatusChangedRecoveryON() throws Exception {
+ public void testNetworkStatusChangedRecoveryON() {
ContentResolver resolver = mContext.getContentResolver();
Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 0);
doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -1555,7 +2225,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, timeout(TEST_TIMEOUT).times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
@@ -1577,7 +2247,7 @@ public class DcTrackerTest extends TelephonyTest {
@FlakyTest
@Test
@SmallTest
- public void testRecoveryStepPDPReset() throws Exception {
+ public void testRecoveryStepPDPReset() {
ContentResolver resolver = mContext.getContentResolver();
Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
Settings.Global.putLong(resolver,
@@ -1586,7 +2256,7 @@ public class DcTrackerTest extends TelephonyTest {
doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
@@ -1596,7 +2266,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, timeout(TEST_TIMEOUT).times(2)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
@@ -1615,16 +2285,17 @@ public class DcTrackerTest extends TelephonyTest {
@Test
@SmallTest
- public void testRecoveryStepReRegister() throws Exception {
+ public void testRecoveryStepReRegister() {
ContentResolver resolver = mContext.getContentResolver();
Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
Settings.Global.putLong(resolver,
Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 2);
doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
+ doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
sendInitializationEvents();
@@ -1633,7 +2304,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
@@ -1647,16 +2318,17 @@ public class DcTrackerTest extends TelephonyTest {
@Test
@SmallTest
- public void testRecoveryStepRestartRadio() throws Exception {
+ public void testRecoveryStepRestartRadio() {
ContentResolver resolver = mContext.getContentResolver();
Settings.Global.putInt(resolver, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 1);
Settings.Global.putLong(resolver,
Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS, 100);
Settings.System.putInt(resolver, "radio.data.stall.recovery.action", 3);
doReturn(new SignalStrength()).when(mPhone).getSignalStrength();
+ doReturn(PhoneConstants.State.IDLE).when(mPhone).getState();
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+ new String[]{ApnSetting.TYPE_DEFAULT_STRING, ApnSetting.TYPE_MMS_STRING});
mDct.enableApn(ApnSetting.TYPE_DEFAULT, DcTracker.REQUEST_TYPE_NORMAL, null);
sendInitializationEvents();
@@ -1665,7 +2337,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
logd("Sending EVENT_NETWORK_STATUS_CHANGED false");
@@ -1686,9 +2358,9 @@ public class DcTrackerTest extends TelephonyTest {
clearInvocations(mHandler);
}
- private void setUpSubscriptionPlans(boolean is5GUnmetered) throws Exception {
+ private void setUpSubscriptionPlans(boolean isNrUnmetered) throws Exception {
List<SubscriptionPlan> plans = new ArrayList<>();
- if (is5GUnmetered) {
+ if (isNrUnmetered) {
plans.add(SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
@@ -1706,6 +2378,28 @@ public class DcTrackerTest extends TelephonyTest {
replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, plans);
}
+ private void resetSubscriptionPlans() throws Exception {
+ replaceInstance(DcTracker.class, "mSubscriptionPlans", mDct, null);
+ }
+
+ private void setUpSubscriptionOverride(int[] networkTypes, boolean isUnmetered)
+ throws Exception {
+ List<Integer> networkTypesList = null;
+ if (networkTypes != null) {
+ networkTypesList = new ArrayList<>();
+ for (int networkType : networkTypes) {
+ networkTypesList.add(networkType);
+ }
+ }
+ replaceInstance(DcTracker.class, "mUnmeteredNetworkTypes", mDct, networkTypesList);
+ replaceInstance(DcTracker.class, "mUnmeteredOverride", mDct, isUnmetered);
+ }
+
+ private void resetSubscriptionOverride() throws Exception {
+ replaceInstance(DcTracker.class, "mUnmeteredNetworkTypes", mDct, null);
+ replaceInstance(DcTracker.class, "mUnmeteredOverride", mDct, false);
+ }
+
private boolean isNetworkTypeUnmetered(int networkType) throws Exception {
Method method = DcTracker.class.getDeclaredMethod(
"isNetworkTypeUnmetered", int.class);
@@ -1745,9 +2439,55 @@ public class DcTrackerTest extends TelephonyTest {
return (boolean) field.get(mDct);
}
+ private Map<Integer, List<Message>> getHandoverCompletionMessages() throws Exception {
+ Field field = DcTracker.class.getDeclaredField(("mHandoverCompletionMsgs"));
+ field.setAccessible(true);
+ return (Map<Integer, List<Message>>) field.get(mDct);
+ }
+
+ private void setUpTempNotMetered() {
+ doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
+ .when(mPhone).getRadioAccessFamily();
+ doReturn(1).when(mPhone).getSubId();
+ mBundle.putBoolean(CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true);
+ Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
+ mContext.sendBroadcast(intent);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ }
+
@Test
public void testIsNetworkTypeUnmetered() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
+
+ // only 5G unmetered
+ setUpSubscriptionOverride(new int[]{TelephonyManager.NETWORK_TYPE_NR}, true);
+
+ assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
+ assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
+ assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
+
+ // all network types metered
+ setUpSubscriptionOverride(TelephonyManager.getAllNetworkTypes(), false);
+
+ assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
+ assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
+ assertFalse(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
+
+ // all network types unmetered
+ setUpSubscriptionOverride(TelephonyManager.getAllNetworkTypes(), true);
+
+ assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
+ assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
+ assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
+
+ resetSubscriptionOverride();
+ }
+
+ @Test
+ public void testIsNetworkTypeUnmeteredViaSubscriptionPlans() throws Exception {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
// only 5G unmetered
setUpSubscriptionPlans(true);
@@ -1768,7 +2508,6 @@ public class DcTrackerTest extends TelephonyTest {
plans.add(SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
- .setTitle("Some 5GB Plan")
.setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
.build());
@@ -1777,18 +2516,69 @@ public class DcTrackerTest extends TelephonyTest {
assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_NR));
assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_LTE));
assertTrue(isNetworkTypeUnmetered(TelephonyManager.NETWORK_TYPE_UNKNOWN));
+
+ resetSubscriptionPlans();
}
@Test
- public void testIsFrequencyRangeUnmetered() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ public void testIsNrUnmeteredSubscriptionPlans() throws Exception {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
int id = setUpDataConnection();
setUpSubscriptionPlans(false);
setUpWatchdogTimer();
doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
.when(mDisplayInfoController).getTelephonyDisplayInfo();
- doReturn(1).when(mPhone).getSubId();
+ setUpTempNotMetered();
+
+ // NetCapability should be metered when connected to 5G with no unmetered plan or frequency
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ verify(mDataConnection, times(1)).onMeterednessChanged(false);
+
+ // Set SubscriptionPlans unmetered
+ setUpSubscriptionPlans(true);
+
+ // NetCapability should switch to unmetered with an unmetered plan
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ verify(mDataConnection, times(1)).onMeterednessChanged(true);
+
+ // Set MMWAVE frequency to unmetered
+ mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, true);
+ Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, mPhone.getSubId());
+ mContext.sendBroadcast(intent);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ // NetCapability should switch to metered without fr=MMWAVE
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ verify(mDataConnection, times(2)).onMeterednessChanged(false);
+
+ // NetCapability should switch to unmetered with fr=MMWAVE
+ doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
+ .when(mDisplayInfoController).getTelephonyDisplayInfo();
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ verify(mDataConnection, times(2)).onMeterednessChanged(true);
+
+ resetDataConnection(id);
+ resetSubscriptionPlans();
+ }
+
+ @Test
+ public void testIsNrUnmeteredCarrierConfigs() throws Exception {
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
+ int id = setUpDataConnection();
+ setUpSubscriptionPlans(false);
+ setUpWatchdogTimer();
+ doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA))
+ .when(mDisplayInfoController).getTelephonyDisplayInfo();
+ setUpTempNotMetered();
// NetCapability should be metered when connected to 5G with no unmetered plan or frequency
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
@@ -1796,6 +2586,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mDataConnection, times(1)).onMeterednessChanged(false);
// Set MMWAVE frequency to unmetered
+ mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_BOOL, true);
mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, true);
Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
@@ -1805,7 +2596,7 @@ public class DcTrackerTest extends TelephonyTest {
// NetCapability should switch to unmetered when fr=MMWAVE and MMWAVE unmetered
doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE))
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED))
.when(mDisplayInfoController).getTelephonyDisplayInfo();
mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TELEPHONY_DISPLAY_INFO_CHANGED));
waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
@@ -1821,6 +2612,7 @@ public class DcTrackerTest extends TelephonyTest {
// Set SUB6 frequency to unmetered
doReturn(2).when(mPhone).getSubId();
+ mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
mBundle.putBoolean(CarrierConfigManager.KEY_UNMETERED_NR_NSA_SUB6_BOOL, true);
intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, mPhone.getPhoneId());
@@ -1836,14 +2628,16 @@ public class DcTrackerTest extends TelephonyTest {
verify(mDataConnection, times(2)).onMeterednessChanged(true);
resetDataConnection(id);
+ resetSubscriptionPlans();
}
@Test
public void testReevaluateUnmeteredConnectionsOnNetworkChange() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
int id = setUpDataConnection();
setUpSubscriptionPlans(true);
setUpWatchdogTimer();
+ setUpTempNotMetered();
// NetCapability should be unmetered when connected to 5G
doReturn(new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
@@ -1864,11 +2658,12 @@ public class DcTrackerTest extends TelephonyTest {
verify(mDataConnection, times(1)).onMeterednessChanged(false);
resetDataConnection(id);
+ resetSubscriptionPlans();
}
@Test
public void testReevaluateUnmeteredConnectionsOnWatchdog() throws Exception {
- initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+ initApns(ApnSetting.TYPE_DEFAULT_STRING, new String[]{ApnSetting.TYPE_ALL_STRING});
int id = setUpDataConnection();
setUpSubscriptionPlans(true);
setUpWatchdogTimer();
@@ -1896,6 +2691,7 @@ public class DcTrackerTest extends TelephonyTest {
assertFalse(getWatchdogStatus());
resetDataConnection(id);
+ resetSubscriptionPlans();
}
/**
@@ -1947,10 +2743,8 @@ public class DcTrackerTest extends TelephonyTest {
@Test
public void testRatChanged() throws Exception {
- mSimulatedCommands.setDataCallResult(true, createSetupDataCallResult());
-
DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
- boolean allowed = isDataAllowed(dataConnectionReasons);
+ boolean allowed = mDct.isDataAllowed(dataConnectionReasons);
assertFalse(dataConnectionReasons.toString(), allowed);
logd("Sending EVENT_ENABLE_APN");
@@ -1960,7 +2754,7 @@ public class DcTrackerTest extends TelephonyTest {
sendInitializationEvents();
dataConnectionReasons = new DataConnectionReasons();
- allowed = isDataAllowed(dataConnectionReasons);
+ allowed = mDct.isDataAllowed(dataConnectionReasons);
assertTrue(dataConnectionReasons.toString(), allowed);
ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
@@ -1968,7 +2762,7 @@ public class DcTrackerTest extends TelephonyTest {
verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
eq(AccessNetworkType.EUTRAN), dpCaptor.capture(),
eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
- any(Message.class));
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 21, 1, NETWORK_TYPE_LTE_BITMASK);
verifyDataConnected(FAKE_APN1);
@@ -1995,7 +2789,7 @@ public class DcTrackerTest extends TelephonyTest {
}
@Test
- public void testApnConfigRepositoryUpdatedOnCarrierConfigChange() throws Exception {
+ public void testApnConfigRepositoryUpdatedOnCarrierConfigChange() {
assertPriority(ApnSetting.TYPE_CBS_STRING, 2);
assertPriority(ApnSetting.TYPE_MMS_STRING, 2);
@@ -2031,4 +2825,177 @@ public class DcTrackerTest extends TelephonyTest {
.filter(x -> x.getApnType().equals(type))
.findFirst().get().getPriority());
}
+
+ @Test
+ public void testProvisionBroadcastReceiver() {
+ Intent intent = new Intent("com.android.internal.telephony.PROVISION");
+ intent.putExtra("provision.phone.id", mPhone.getPhoneId());
+ try {
+ mContext.sendBroadcast(intent);
+ } catch (SecurityException e) {
+ fail();
+ }
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ }
+
+ @Test
+ public void testRetryHandoverWhenDisconnecting() throws Exception {
+ initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
+ setUpDataConnection();
+ SparseArray<ApnContext> apnContextsByType = Mockito.mock(SparseArray.class);
+ ConcurrentHashMap<String, ApnContext> apnContexts = Mockito.mock(ConcurrentHashMap.class);
+ doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
+ doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
+ doReturn(false).when(mApnContext).isConnectable();
+ doReturn(false).when(mDataEnabledSettings).isDataEnabled(anyInt());
+ doReturn(DctConstants.State.DISCONNECTING).when(mApnContext).getState();
+ replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
+ replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);
+
+ sendInitializationEvents();
+
+ logd("Sending EVENT_ENABLE_APN");
+ // APN id 0 is APN_TYPE_DEFAULT
+ mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER,
+ mDct.obtainMessage(12345));
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ assertTrue(isHandoverPending(ApnSetting.TYPE_IMS));
+
+ // Verify no handover request was sent
+ verify(mDataConnection, never()).bringUp(any(ApnContext.class), anyInt(), anyInt(),
+ any(Message.class), anyInt(), anyInt(), anyInt(), anyBoolean());
+
+ doReturn(DctConstants.State.RETRYING).when(mApnContext).getState();
+ // Data now is disconnected
+ doReturn(true).when(mApnContext).isConnectable();
+ doReturn(true).when(mDataEnabledSettings).isDataEnabled(anyInt());
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
+ new AsyncResult(Pair.create(mApnContext, 0), null, null)));
+
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ verify(mDataConnection).bringUp(any(ApnContext.class), anyInt(), anyInt(),
+ any(Message.class), anyInt(), eq(DcTracker.REQUEST_TYPE_HANDOVER), anyInt(),
+ anyBoolean());
+ }
+
+ @Test
+ public void testDataUnthrottled() throws Exception {
+ initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
+ replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
+ mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
+ sendInitializationEvents();
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_APN_UNTHROTTLED,
+ new AsyncResult(null, FAKE_APN3, null)));
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+
+ verify(mDataThrottler).setRetryTime(
+ eq(ApnSetting.TYPE_IMS),
+ eq(RetryManager.NO_SUGGESTED_RETRY_DELAY),
+ eq(DcTracker.REQUEST_TYPE_NORMAL));
+ }
+
+ @Test
+ public void testHandlingSecondHandoverRequest() throws Exception {
+ initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
+ setUpDataConnection();
+ SparseArray<ApnContext> apnContextsByType = Mockito.mock(SparseArray.class);
+ ConcurrentHashMap<String, ApnContext> apnContexts = Mockito.mock(ConcurrentHashMap.class);
+ doReturn(mApnContext).when(apnContextsByType).get(eq(ApnSetting.TYPE_IMS));
+ doReturn(mApnContext).when(apnContexts).get(eq(ApnSetting.TYPE_IMS_STRING));
+ doReturn(false).when(mApnContext).isConnectable();
+ doReturn(DctConstants.State.CONNECTING).when(mApnContext).getState();
+ replaceInstance(DcTracker.class, "mApnContextsByType", mDct, apnContextsByType);
+ replaceInstance(DcTracker.class, "mApnContexts", mDct, apnContexts);
+
+ sendInitializationEvents();
+
+ logd("Sending EVENT_ENABLE_APN");
+ // APN id 0 is APN_TYPE_DEFAULT
+ Message msg = mDct.obtainMessage(12345);
+ mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER, msg);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ Map<Integer, List<Message>> msgs = getHandoverCompletionMessages();
+ // Make sure the messages was queued properly instead of fired right away.
+ assertTrue(msgs.get(ApnSetting.TYPE_IMS).contains(msg));
+ }
+
+ @Test
+ public void testDataThrottledNotAllowData() throws Exception {
+ initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
+ replaceInstance(DcTracker.class, "mDataThrottler", mDct, mDataThrottler);
+ doReturn(SystemClock.elapsedRealtime() + 100000).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_IMS);
+ mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
+ sendInitializationEvents();
+
+ DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+ boolean allowed = mDct.isDataAllowed(mApnContext, DcTracker.REQUEST_TYPE_NORMAL,
+ dataConnectionReasons);
+ assertFalse(dataConnectionReasons.toString(), allowed);
+ assertTrue(dataConnectionReasons.contains(DataDisallowedReasonType.DATA_THROTTLED));
+
+ // Makre sure no data setup request
+ verify(mSimulatedCommandsVerifier, never()).setupDataCall(
+ anyInt(), any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(),
+ anyInt(), any(), any(), anyBoolean(), any(Message.class));
+ }
+
+ @Test
+ public void testNotifyDataDisconnected() {
+ // Verify notify data disconnected on DCT constructor, initialized in setUp()
+ ArgumentCaptor<PreciseDataConnectionState> captor =
+ ArgumentCaptor.forClass(PreciseDataConnectionState.class);
+ verify(mPhone, times(13)).notifyDataConnection(captor.capture());
+ for (PreciseDataConnectionState state : captor.getAllValues()) {
+ assertEquals(TelephonyManager.DATA_DISCONNECTED, state.getState());
+ }
+ }
+
+ /**
+ * There is a corresponding test {@link DataConnectionTest#testDataServiceTempUnavailable()} to
+ * test DataConnection behavior.
+ */
+ @Test
+ public void testDataServiceTempUnavailable() {
+ Handler handler = Mockito.mock(Handler.class);
+ Message handoverCompleteMessage = Message.obtain(handler);
+ addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
+ initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
+ mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_SETUP_COMPLETE,
+ DcTracker.REQUEST_TYPE_HANDOVER, DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN,
+ new AsyncResult(Pair.create(mApnContext, 0),
+ DataFailCause.SERVICE_TEMPORARILY_UNAVAILABLE, new Exception())));
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ // Ensure handover is not completed yet
+ verify(handler, never()).sendMessageDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testNormalRequestDoesNotFailHandoverRequest() {
+ Handler handler = Mockito.mock(Handler.class);
+ Message handoverCompleteMessage = Message.obtain(handler);
+ addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
+ initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
+ mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_NORMAL, null);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ // Ensure handover is not completed yet
+ verify(handler, never()).sendMessageDelayed(any(), anyLong());
+ }
+
+ @Test
+ public void testPreferenceChangedFallback() {
+ Handler handler = Mockito.mock(Handler.class);
+ doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN).when(mTransportManager)
+ .getPreferredTransport(anyInt());
+ Message handoverCompleteMessage = Message.obtain(handler);
+ addHandoverCompleteMsg(handoverCompleteMessage, ApnSetting.TYPE_IMS);
+ initApns(ApnSetting.TYPE_IMS_STRING, new String[]{ApnSetting.TYPE_IMS_STRING});
+ mDct.enableApn(ApnSetting.TYPE_IMS, DcTracker.REQUEST_TYPE_HANDOVER,
+ handoverCompleteMessage);
+ waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+ Bundle bundle = handoverCompleteMessage.getData();
+ assertTrue(bundle.getBoolean("extra_handover_failure_fallback"));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/LinkBandwidthEstimatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/LinkBandwidthEstimatorTest.java
new file mode 100644
index 0000000000..80fe817387
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/LinkBandwidthEstimatorTest.java
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.BW_STATS_COUNT_THRESHOLD;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.LINK_RX;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.LINK_TX;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_ACTIVE_PHONE_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_DEFAULT_NETWORK_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_MODEM_ACTIVITY_RETURNED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_NR_FREQUENCY_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_SCREEN_STATE_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.MSG_SIGNAL_STRENGTH_CHANGED;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.UNKNOWN_TAC;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.NetworkCapabilities;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.CellIdentityLte;
+import android.telephony.ModemActivityInfo;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Pair;
+
+import com.android.internal.telephony.TelephonyFacade;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class LinkBandwidthEstimatorTest extends TelephonyTest {
+ private LinkBandwidthEstimator mLBE;
+ private static final int [] TX_TIME_1_MS = new int[]{0, 0, 0, 0, 0};
+ private static final int [] TX_TIME_2_MS = new int[]{100, 0, 0, 0, 100};
+ private static final int RX_TIME_1_MS = 100;
+ private static final int RX_TIME_2_MS = 200;
+ private static final ModemActivityInfo MAI_INIT =
+ new ModemActivityInfo(0, 0, 0, TX_TIME_1_MS, RX_TIME_1_MS);
+ private static final ModemActivityInfo MAI_TX_RX_TIME_HIGH =
+ new ModemActivityInfo(100L, 0, 0, TX_TIME_2_MS, RX_TIME_2_MS);
+ private static final ModemActivityInfo MAI_RX_TIME_HIGH =
+ new ModemActivityInfo(100L, 0, 0, TX_TIME_1_MS, RX_TIME_2_MS);
+ private static final int EVENT_BANDWIDTH_ESTIMATOR_UPDATE = 1;
+ private NetworkCapabilities mNetworkCapabilities;
+ private CellIdentityLte mCellIdentity;
+ private long mElapsedTimeMs = 0;
+ private long mTxBytes = 0;
+ private long mRxBytes = 0;
+ @Mock
+ TelephonyFacade mTelephonyFacade;
+ @Mock
+ private Handler mTestHandler;
+ private NetworkRegistrationInfo mNri;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mNetworkCapabilities = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build();
+
+ mCellIdentity = new CellIdentityLte(310, 260, 1234, 123456, 366);
+ mNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(mNri);
+ when(mServiceState.getOperatorNumeric()).thenReturn("310260");
+ when(mTelephonyFacade.getElapsedSinceBootMillis()).thenReturn(0L);
+ when(mTelephonyFacade.getMobileTxBytes()).thenReturn(0L);
+ when(mTelephonyFacade.getMobileTxBytes()).thenReturn(0L);
+ when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+ when(mPhone.getSubId()).thenReturn(1);
+ when(mSignalStrength.getDbm()).thenReturn(-100);
+ when(mSignalStrength.getLevel()).thenReturn(1);
+ mLBE = new LinkBandwidthEstimator(mPhone, mTelephonyFacade);
+ mLBE.registerForBandwidthChanged(mTestHandler, EVENT_BANDWIDTH_ESTIMATOR_UPDATE, null);
+ mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, mNetworkCapabilities).sendToTarget();
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, false).sendToTarget();
+ mLBE.obtainMessage(MSG_ACTIVE_PHONE_CHANGED, 1).sendToTarget();
+ processAllMessages();
+ }
+
+ private void addElapsedTime(long timeMs) {
+ mElapsedTimeMs += timeMs;
+ when(mTelephonyFacade.getElapsedSinceBootMillis()).thenReturn(mElapsedTimeMs);
+ }
+
+ private void addTxBytes(long txBytes) {
+ mTxBytes += txBytes;
+ when(mTelephonyFacade.getMobileTxBytes()).thenReturn(mTxBytes);
+ }
+
+ private void addRxBytes(long rxBytes) {
+ mRxBytes += rxBytes;
+ when(mTelephonyFacade.getMobileRxBytes()).thenReturn(mRxBytes);
+ }
+
+ private void subtractRxBytes(long rxBytes) {
+ mRxBytes -= rxBytes;
+ when(mTelephonyFacade.getMobileRxBytes()).thenReturn(mRxBytes);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testScreenOnTxTrafficHighOneModemPoll() throws Exception {
+ addElapsedTime(4_100);
+ moveTimeForward(4_100);
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ addTxBytes(500_000L);
+ addRxBytes(10_000L);
+ addElapsedTime(2_100);
+ moveTimeForward(2_100);
+ processAllMessages();
+
+ verify(mTelephonyManager, times(1)).requestModemActivityInfo(any(), any());
+ }
+
+ @Test
+ public void testScreenOnTxTrafficHighNotActivePhoneNoModemPoll() throws Exception {
+ mLBE.obtainMessage(MSG_ACTIVE_PHONE_CHANGED, 0).sendToTarget();
+ addElapsedTime(4_100);
+ moveTimeForward(4_100);
+ processAllMessages();
+
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ addTxBytes(500_000L);
+ addRxBytes(10_000L);
+ addElapsedTime(2_100);
+ moveTimeForward(2_100);
+ processAllMessages();
+
+ verify(mTelephonyManager, times(0)).requestModemActivityInfo(any(), any());
+ }
+
+ private void verifyUpdateBandwidth(int txKbps, int rxKbps) {
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mTestHandler, atLeast(1))
+ .sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
+ assertEquals(EVENT_BANDWIDTH_ESTIMATOR_UPDATE, messageArgumentCaptor.getValue().what);
+ assertEquals(new Pair<Integer, Integer>(txKbps, rxKbps),
+ ((AsyncResult) messageArgumentCaptor.getValue().obj).result);
+ }
+
+ @Test
+ public void testScreenOnTxRxTrafficHighTwoModemPoll() throws Exception {
+ addElapsedTime(4_100);
+ moveTimeForward(4_100);
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ addTxBytes(10_000L);
+ addRxBytes(20_000L);
+ addElapsedTime(2_100);
+ moveTimeForward(2_100);
+ processAllMessages();
+ verify(mTelephonyManager, times(1)).requestModemActivityInfo(any(), any());
+
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_INIT).sendToTarget();
+ processAllMessages();
+
+ addTxBytes(100_000L);
+ addRxBytes(200_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_TX_RX_TIME_HIGH).sendToTarget();
+ processAllMessages();
+
+ verify(mTelephonyManager, times(2)).requestModemActivityInfo(any(), any());
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testScreenOnRxTrafficHighTwoModemPollRxTimeHigh() throws Exception {
+ addElapsedTime(4_100);
+ moveTimeForward(4_100);
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ addTxBytes(10_000L);
+ addRxBytes(20_000L);
+ addElapsedTime(2_100);
+ moveTimeForward(2_100);
+ processAllMessages();
+ verify(mTelephonyManager, times(1)).requestModemActivityInfo(any(), any());
+
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_INIT).sendToTarget();
+ processAllMessages();
+
+ addTxBytes(100_000L);
+ addRxBytes(200_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, MAI_RX_TIME_HIGH).sendToTarget();
+ processAllMessages();
+
+ verify(mTelephonyManager, times(2)).requestModemActivityInfo(any(), any());
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testScreenOnTxRxTrafficLow() throws Exception {
+ addElapsedTime(4_100);
+ moveTimeForward(4_100);
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ addTxBytes(10_000L);
+ addRxBytes(10_000L);
+ addElapsedTime(2_100);
+ moveTimeForward(2_100);
+ processAllMessages();
+ verify(mTelephonyManager, never()).requestModemActivityInfo(any(), any());
+ }
+
+ @Test
+ public void testScreenOnTrafficLowSampleHighAcc() throws Exception {
+ addElapsedTime(4_100);
+ moveTimeForward(4_100);
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ for (int i = 0; i < 30; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(19_000L);
+ addElapsedTime(1_100);
+ moveTimeForward(1_100);
+ processAllMessages();
+ }
+ verify(mTelephonyManager, times(2)).requestModemActivityInfo(any(), any());
+ }
+
+ @Test
+ public void testScreenOnDefaultNetworkToggleNoExtraTrafficPoll() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ addElapsedTime(500);
+ moveTimeForward(500);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, null).sendToTarget();
+ addElapsedTime(500);
+ moveTimeForward(500);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_DEFAULT_NETWORK_CHANGED, mNetworkCapabilities).sendToTarget();
+ for (int i = 0; i < 3; i++) {
+ addElapsedTime(1_100);
+ moveTimeForward(1_100);
+ processAllMessages();
+ }
+
+ verify(mTelephonyFacade, times(4)).getMobileTxBytes();
+ }
+
+ @Test
+ public void testRatChangeTriggerBandwidthUpdate() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ addTxBytes(10_000L);
+ addRxBytes(19_000L);
+ addElapsedTime(2000);
+ moveTimeForward(2000);
+ processAllMessages();
+
+ addTxBytes(10_000L);
+ addRxBytes(19_000L);
+ mNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(mNri);
+ addElapsedTime(6000);
+ moveTimeForward(6000);
+ processAllMessages();
+
+ verify(mTelephonyManager, times(0)).requestModemActivityInfo(any(), any());
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testSignalLevelChangeTriggerBandwidthUpdate() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 2; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ verifyUpdateBandwidth(-1, 19_597);
+
+ addTxBytes(20_000L);
+ addRxBytes(50_000L);
+ when(mSignalStrength.getDbm()).thenReturn(-110);
+ mLBE.obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, mSignalStrength).sendToTarget();
+ addElapsedTime(6000);
+ moveTimeForward(6000);
+ processAllMessages();
+
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testAvgBwForAllPossibleRat() throws Exception {
+ Pair<Integer, Integer> values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_GPRS);
+ assertEquals(24, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EDGE);
+ assertEquals(18, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_UMTS);
+ assertEquals(115, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_CDMA);
+ assertEquals(14, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_1xRTT);
+ assertEquals(30, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EVDO_0);
+ assertEquals(48, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EVDO_A);
+ assertEquals(550, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSDPA);
+ assertEquals(620, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSUPA);
+ assertEquals(1800, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSPA);
+ assertEquals(1800, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EVDO_B);
+ assertEquals(550, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_EHRPD);
+ assertEquals(750, (int) values.first);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_HSPAP);
+ assertEquals(3400, (int) values.second);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_TD_SCDMA);
+ assertEquals(115, (int) values.first);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_LTE);
+ assertEquals(15000, (int) values.second);
+ when(mServiceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ when(mServiceState.getNrFrequencyRange()).thenReturn(ServiceState.FREQUENCY_RANGE_MMWAVE);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_LTE);
+ assertEquals(145000, (int) values.first);
+ when(mServiceState.getNrFrequencyRange()).thenReturn(ServiceState.FREQUENCY_RANGE_UNKNOWN);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_LTE);
+ assertEquals(47000, (int) values.first);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_NR);
+ assertEquals(145_000, (int) values.first);
+ when(mServiceState.getNrFrequencyRange()).thenReturn(ServiceState.FREQUENCY_RANGE_MMWAVE);
+ values = mLBE.getStaticAvgBw(TelephonyManager.NETWORK_TYPE_NR);
+ assertEquals("NR_MMWAVE", mLBE.getDataRatName(TelephonyManager.NETWORK_TYPE_NR));
+ assertEquals(145_000, (int) values.first);
+ }
+
+ @Test
+ public void testSwitchToNrMmwaveTriggerBandwidthUpdate() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ addTxBytes(10_000L);
+ addRxBytes(19_000L);
+ addElapsedTime(2000);
+ moveTimeForward(2000);
+ processAllMessages();
+
+ addTxBytes(10_000L);
+ addRxBytes(19_000L);
+ when(mServiceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+ when(mServiceState.getNrFrequencyRange()).thenReturn(ServiceState.FREQUENCY_RANGE_MMWAVE);
+ when(mSignalStrength.getLevel()).thenReturn(2);
+ mLBE.obtainMessage(MSG_NR_FREQUENCY_CHANGED).sendToTarget();
+ addElapsedTime(6000);
+ moveTimeForward(6000);
+ processAllMessages();
+
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testEnoughModemPollTriggerBwUpdate() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 2; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ verify(mTelephonyManager, times(BW_STATS_COUNT_THRESHOLD + 2))
+ .requestModemActivityInfo(any(), any());
+ verifyUpdateBandwidth(-1, 19_597);
+ }
+
+ @Test
+ public void testAbnormalTrafficCountTriggerLessBwUpdate() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 2; i++) {
+ if (i == 1) {
+ addTxBytes(10_000L);
+ subtractRxBytes(500_000L);
+ } else {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ }
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ verify(mTelephonyManager, times(BW_STATS_COUNT_THRESHOLD))
+ .requestModemActivityInfo(any(), any());
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testUseCurrentTacStatsWithEnoughData() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ mCellIdentity = new CellIdentityLte(310, 260, 1235, 123457, 367);
+ when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+ for (int i = BW_STATS_COUNT_THRESHOLD; i < 3 * BW_STATS_COUNT_THRESHOLD; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ verifyUpdateBandwidth(-1, 19_597);
+ }
+
+ @Test
+ public void testUseAllTacStatsIfNoEnoughDataWithCurrentTac() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+ mLBE.obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, mSignalStrength).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(900_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ mCellIdentity = new CellIdentityLte(310, 260, 1234, 123456, 367);
+ when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+ for (int i = BW_STATS_COUNT_THRESHOLD; i < BW_STATS_COUNT_THRESHOLD * 3 / 2; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(1_000_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ LinkBandwidthEstimator.NetworkBandwidth network = mLBE.lookupNetwork("310260", 366, "LTE");
+ assertEquals(BW_STATS_COUNT_THRESHOLD - 1, network.getCount(LINK_RX, 1));
+ assertEquals(900_000L * 8 * 1000 / 200 / 1024 * (BW_STATS_COUNT_THRESHOLD - 1),
+ network.getValue(LINK_RX, 1));
+ network = mLBE.lookupNetwork("310260", 367, "LTE");
+ assertEquals(1, network.getCount(LINK_RX, 1));
+ assertEquals(1_000_000L * 8 * 1000 / 200 / 1024,
+ network.getValue(LINK_RX, 1));
+ network = mLBE.lookupNetwork("310260", UNKNOWN_TAC, "LTE");
+ assertEquals(BW_STATS_COUNT_THRESHOLD * 3 / 2 - 2, network.getCount(LINK_RX, 1));
+ assertEquals(179_686, network.getValue(LINK_RX, 1));
+ verifyUpdateBandwidth(-1, 37_350);
+ }
+
+ @Test
+ public void testSwitchCarrierFallbackToColdStartValue() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 5; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS)).sendToTarget();
+ processAllMessages();
+ }
+
+ verifyUpdateBandwidth(-1, 19_597);
+
+ mCellIdentity = new CellIdentityLte(320, 265, 1234, 123456, 366);
+ when(mPhone.getCurrentCellIdentity()).thenReturn(mCellIdentity);
+ when(mServiceState.getOperatorNumeric()).thenReturn("320265");
+
+ addTxBytes(10_000L);
+ addRxBytes(10_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testIgnoreLowTxRxTime() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 5; i++) {
+ addTxBytes(10_000L);
+ addRxBytes(500_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * 80)).sendToTarget();
+ processAllMessages();
+ }
+
+ verifyUpdateBandwidth(-1, -1);
+ }
+
+ @Test
+ public void testEdgeThenLteShouldIgnoreTransitionStats() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+ mNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_EDGE)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(mNri);
+ mLBE.obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, mSignalStrength).sendToTarget();
+ processAllMessages();
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD * 2; i++) {
+ addTxBytes(12_000L);
+ addRxBytes(24_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS * 5)).sendToTarget();
+ processAllMessages();
+ }
+
+ LinkBandwidthEstimator.NetworkBandwidth network = mLBE.lookupNetwork("310260", 366, "EDGE");
+
+ assertEquals(0, network.getCount(LINK_TX, 1));
+ assertEquals(BW_STATS_COUNT_THRESHOLD * 2 - 1, network.getCount(LINK_RX, 1));
+ assertEquals(24_000L * 8 / 1024 * (BW_STATS_COUNT_THRESHOLD * 2 - 1),
+ network.getValue(LINK_RX, 1));
+
+ mNri = new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build();
+ when(mServiceState.getNetworkRegistrationInfo(anyInt(), anyInt())).thenReturn(mNri);
+ for (int i = BW_STATS_COUNT_THRESHOLD * 2; i < BW_STATS_COUNT_THRESHOLD * 4; i++) {
+ addTxBytes(1_200_000L);
+ addRxBytes(2_400_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS * 10)).sendToTarget();
+ processAllMessages();
+ }
+ network = mLBE.lookupNetwork("310260", 366, "LTE");
+
+ assertEquals(BW_STATS_COUNT_THRESHOLD * 2 - 2, network.getCount(LINK_RX, 1));
+ assertEquals(0, network.getCount(LINK_TX, 1));
+ }
+
+
+ @Test
+ public void testVeryHighByteCountReturnNonNegativeValue() throws Exception {
+ mLBE.obtainMessage(MSG_SCREEN_STATE_CHANGED, true).sendToTarget();
+ processAllMessages();
+ mLBE.obtainMessage(MSG_SIGNAL_STRENGTH_CHANGED, mSignalStrength).sendToTarget();
+ processAllMessages();
+ for (int i = 0; i < BW_STATS_COUNT_THRESHOLD + 5; i++) {
+ addTxBytes(8_000_000_000L);
+ addRxBytes(16_000_000_000L);
+ addElapsedTime(5_100);
+ moveTimeForward(5_100);
+ processAllMessages();
+ mLBE.obtainMessage(MSG_MODEM_ACTIVITY_RETURNED, new ModemActivityInfo(
+ i * 5_100L, 0, 0, TX_TIME_2_MS, i * RX_TIME_2_MS * 5)).sendToTarget();
+ processAllMessages();
+ }
+
+ LinkBandwidthEstimator.NetworkBandwidth network = mLBE.lookupNetwork("310260", 366, "LTE");
+
+ assertEquals(BW_STATS_COUNT_THRESHOLD + 4, network.getCount(LINK_RX, 1));
+ assertEquals(0, network.getValue(LINK_TX, 1));
+ assertEquals(16_000_000_000L * 8 / 1024 * (BW_STATS_COUNT_THRESHOLD + 4),
+ network.getValue(LINK_RX, 1));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java
new file mode 100644
index 0000000000..5a8b54028a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.net.InetAddresses;
+import android.net.LinkAddress;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.telephony.data.EpsQos;
+import android.telephony.data.QosBearerFilter;
+import android.telephony.data.QosBearerSession;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class QosCallbackTrackerTest extends TelephonyTest {
+
+ class Filter implements QosCallbackTracker.IFilter {
+ InetSocketAddress localAddress;
+ InetSocketAddress remoteAddress;
+
+ Filter(@NonNull final InetSocketAddress localAddress) {
+ this.localAddress = localAddress;
+ this.remoteAddress = null;
+ }
+
+ Filter(@NonNull final InetSocketAddress localAddress,
+ @NonNull final InetSocketAddress remoteAddress) {
+ this.localAddress = localAddress;
+ this.remoteAddress = remoteAddress;
+ }
+
+ public boolean matchesLocalAddress(@NonNull final InetAddress address,
+ final int startPort, final int endPort) {
+ return startPort <= localAddress.getPort()
+ && endPort >= localAddress.getPort()
+ && localAddress.getAddress().equals(address);
+ }
+
+ public boolean matchesRemoteAddress(@NonNull final InetAddress address,
+ final int startPort, final int endPort) {
+ return remoteAddress != null
+ && startPort <= remoteAddress.getPort()
+ && endPort >= remoteAddress.getPort()
+ && remoteAddress.getAddress().equals(address);
+ }
+ }
+
+ @Mock
+ private DcNetworkAgent mDcNetworkAgent;
+ @Mock
+ private Network mNetwork;
+
+ private QosCallbackTracker mQosCallbackTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ doReturn(mNetwork).when(mDcNetworkAgent).getNetwork();
+ doReturn(100).when(mNetwork).getNetId();
+ mQosCallbackTracker = new QosCallbackTracker(mDcNetworkAgent);
+ processAllMessages();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private EpsQos createEpsQos(int dlMbr, int ulMbr, int dlGbr, int ulGbr) {
+ // Build android.hardware.radio.V1_6.EpsQos
+ android.hardware.radio.V1_6.EpsQos halEpsQos = new android.hardware.radio.V1_6.EpsQos();
+ halEpsQos.qci = 4;
+ halEpsQos.downlink.maxBitrateKbps = dlMbr;
+ halEpsQos.downlink.guaranteedBitrateKbps = dlGbr;
+ halEpsQos.uplink.maxBitrateKbps = ulMbr;
+ halEpsQos.uplink.guaranteedBitrateKbps = ulGbr;
+
+ return new EpsQos(halEpsQos);
+ }
+
+ private QosBearerFilter createIpv4QosFilter(String localAddress,
+ QosBearerFilter.PortRange localPort, int precedence) {
+ return new QosBearerFilter(
+ Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress(localAddress), 32)),
+ new ArrayList<LinkAddress>(), localPort, null, QosBearerFilter.QOS_PROTOCOL_TCP,
+ 7, 987, 678, QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, precedence);
+ }
+
+ private QosBearerFilter createIpv4QosFilter(String localAddress, String remoteAddress,
+ QosBearerFilter.PortRange localPort, QosBearerFilter.PortRange remotePort,
+ int precedence) {
+ return new QosBearerFilter(
+ Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress(localAddress), 32)),
+ Arrays.asList(
+ new LinkAddress(InetAddresses.parseNumericAddress(remoteAddress), 32)),
+ localPort, remotePort,
+ QosBearerFilter.QOS_PROTOCOL_TCP, 7, 987, 678,
+ QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, precedence);
+ }
+
+ @Test
+ @SmallTest
+ public void testAddFilterBeforeUpdateSessions() throws Exception {
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ // Non-matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mDcNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
+ eq(1234), any(EpsBearerQosSessionAttributes.class));
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ }
+
+
+ @Test
+ @SmallTest
+ public void testAddFilterAfterUpdateSessions() throws Exception {
+ // Non-matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ }
+
+ @Test
+ @SmallTest
+ public void testRemoveFilter() throws Exception {
+ // Add filter
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ // Non-matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mDcNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
+ eq(1234), any(EpsBearerQosSessionAttributes.class));
+
+ // Remove the filter
+ mQosCallbackTracker.removeFilter(1);
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Verify that notifyQosSessionAvailable is not invoked as the filter is already removed
+ verify(mDcNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ }
+
+ @Test
+ @SmallTest
+ public void testSessionLost() throws Exception {
+ // Non-matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 2223));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ // Remove the matching QosBearerFilter
+ qosSessions.remove(1);
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1235), eq(1));
+ }
+
+ @Test
+ @SmallTest
+ public void testModifiedQos() throws Exception {
+ // Non-matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 2223));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ reset(mDcNetworkAgent);
+
+ // Update the QOS
+ qosSessions.remove(1);
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(10, 12, 14, 16), qosFilters2));
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testUnmodifiedQos() throws Exception {
+ // Non-matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 2223));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ reset(mDcNetworkAgent);
+
+ // Update the same QOS
+ qosSessions.remove(1);
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mDcNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testEmptyQosSessions() throws Exception {
+ // Add filter
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("155.55.55.55"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 2223));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter2 = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 2223));
+ mQosCallbackTracker.addFilter(2, filter2);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1234), any(EpsBearerQosSessionAttributes.class));
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(2),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ // Update empty QOS sessions list
+ mQosCallbackTracker.updateSessions(new ArrayList<>());
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1));
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionLost(eq(2), eq(1235), eq(1));
+ }
+
+ @Test
+ @SmallTest
+ public void testMultipleQosSessions() throws Exception {
+ // Add filter 1
+ Filter filter1 = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("155.55.55.55"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 2223));
+ mQosCallbackTracker.addFilter(1, filter1);
+
+ // Add filter 2
+ Filter filter2 = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("177.77.77.77"), 2227));
+ mQosCallbackTracker.addFilter(2, filter2);
+
+ // QosBearerFilter 1
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+
+ // QosBearerFilter 2
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22", "177.77.77.77",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2227), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(7, 8, 9, 10), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(1),
+ eq(1234), any(EpsBearerQosSessionAttributes.class));
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionAvailable(eq(2),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ // Update empty QOS sessions list
+ mQosCallbackTracker.updateSessions(new ArrayList<>());
+
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionLost(eq(1), eq(1234), eq(1));
+ verify(mDcNetworkAgent, times(1)).notifyQosSessionLost(eq(2), eq(1235), eq(1));
+ }
+
+ @Test
+ @SmallTest
+ public void testQosSessionWithInvalidPortRange() throws Exception {
+ // Non-matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55",
+ new QosBearerFilter.PortRange(0,0), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+
+ // Matching QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22",
+ new QosBearerFilter.PortRange(-1, 1), 45));
+ qosSessions.add(new QosBearerSession(1235, createEpsQos(5, 6, 7, 8), qosFilters2));
+
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ // Add filter after updateSessions
+ Filter filter = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("122.22.22.22"), 2222));
+ mQosCallbackTracker.addFilter(1, filter);
+
+ verify(mDcNetworkAgent, never()).notifyQosSessionAvailable(eq(1),
+ eq(1235), any(EpsBearerQosSessionAttributes.class));
+
+ }
+}
+
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
index 334917f3bc..159e20416a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/RetryManagerTest.java
@@ -18,8 +18,11 @@ package com.android.internal.telephony.dataconnection;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.telephony.CarrierConfigManager;
import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
@@ -148,7 +151,7 @@ public class RetryManagerTest extends TelephonyTest {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
new String[]{"default:2000"});
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
long delay = rm.getDelayForNextApn(false);
assertEquals(RetryManager.NO_RETRY, delay);
@@ -173,7 +176,7 @@ public class RetryManagerTest extends TelephonyTest {
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -195,7 +198,7 @@ public class RetryManagerTest extends TelephonyTest {
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- RetryManager rm = new RetryManager(mPhone, "supl");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_SUPL);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -250,7 +253,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
waitingApns.add(ApnSetting.makeApnSetting(mApn2));
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -288,7 +291,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
waitingApns.add(ApnSetting.makeApnSetting(mApn2));
- RetryManager rm = new RetryManager(mPhone, "dun");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DUN);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -336,7 +339,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
waitingApns.add(ApnSetting.makeApnSetting(mApn2));
- RetryManager rm = new RetryManager(mPhone, "mms");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -384,7 +387,7 @@ public class RetryManagerTest extends TelephonyTest {
ApnSetting apn = ApnSetting.makeApnSetting(mApn1);
waitingApns.add(apn);
- RetryManager rm = new RetryManager(mPhone, "fota");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_FOTA);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -411,7 +414,7 @@ public class RetryManagerTest extends TelephonyTest {
public void testRetryManagerApnPermanentFailedWithTwoApns() throws Exception {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"xyz : 1000,4000,7000"});
+ new String[]{"mms : 1000,4000,7000"});
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
@@ -419,7 +422,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(myApn1);
waitingApns.add(myApn2);
- RetryManager rm = new RetryManager(mPhone, "xyz");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -478,7 +481,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(myApn2);
waitingApns.add(myApn3);
- RetryManager rm = new RetryManager(mPhone, "ims");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_IMS);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -535,7 +538,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(myApn1);
waitingApns.add(myApn2);
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -587,7 +590,7 @@ public class RetryManagerTest extends TelephonyTest {
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -625,7 +628,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
waitingApns.add(ApnSetting.makeApnSetting(mApn2));
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -683,7 +686,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
waitingApns.add(ApnSetting.makeApnSetting(mApn2));
- RetryManager rm = new RetryManager(mPhone, "hipri");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_HIPRI);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -753,7 +756,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(ApnSetting.makeApnSetting(mApn1));
waitingApns.add(ApnSetting.makeApnSetting(mApn2));
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -803,7 +806,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(myApn1);
waitingApns.add(myApn2);
- RetryManager rm = new RetryManager(mPhone, "dun");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DUN);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -868,6 +871,11 @@ public class RetryManagerTest extends TelephonyTest {
assertEquals(RetryManager.NO_RETRY, delay);
}
+ private void assertRange(long low, long high, long value) {
+ if (value >= low && value <= high) return;
+ fail("Not in range[" + low + "," + high + "], value=" + value);
+ }
+
/**
* Test the scenario where modem suggests retry the current APN once
*/
@@ -884,7 +892,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(myApn1);
waitingApns.add(myApn2);
- RetryManager rm = new RetryManager(mPhone, "mms");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_MMS);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -899,27 +907,32 @@ public class RetryManagerTest extends TelephonyTest {
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn1));
- // Modem suggests retrying the current APN
- rm.setModemSuggestedDelay(2500);
+ // Network suggests retrying the current APN
+ doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_MMS);
delay = rm.getDelayForNextApn(false);
- assertEquals(2500, delay);
+ assertRange(2450, 2500, delay);
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn1));
- rm.setModemSuggestedDelay(RetryManager.NO_SUGGESTED_RETRY_DELAY);
+ doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_MMS);
delay = rm.getDelayForNextApn(false);
assertEquals(20000, delay);
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn2));
// Modem suggests retrying the current APN
- rm.setModemSuggestedDelay(30000);
+ //rm.setModemSuggestedDelay(30000);
+ doReturn(30000 + SystemClock.elapsedRealtime()).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_MMS);
delay = rm.getDelayForNextApn(false);
- assertEquals(30000, delay);
+ assertRange(29950, 30000, delay);
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn2));
- rm.setModemSuggestedDelay(RetryManager.NO_SUGGESTED_RETRY_DELAY);
+ doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_MMS);
delay = rm.getDelayForNextApn(false);
assertEquals(4000, delay);
}
@@ -940,7 +953,7 @@ public class RetryManagerTest extends TelephonyTest {
waitingApns.add(myApn1);
waitingApns.add(myApn2);
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -956,34 +969,42 @@ public class RetryManagerTest extends TelephonyTest {
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn1));
// Modem suggests retrying the current APN
- rm.setModemSuggestedDelay(2500);
+ doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_DEFAULT);
delay = rm.getDelayForNextApn(false);
- assertEquals(2500, delay);
+ assertRange(2450, 2500, delay);
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn1));
- rm.setModemSuggestedDelay(RetryManager.NO_RETRY);
+ doReturn(RetryManager.NO_RETRY).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_DEFAULT);
delay = rm.getDelayForNextApn(false);
assertEquals(RetryManager.NO_RETRY, delay);
}
/**
- * Test the scenario where modem suggests the same retry for too many times
+ * Test the scenario that network suggests the same retry for too many times
*/
@Test
@SmallTest
- public void testRetryManagerModemSuggestedRetryTooManyTimes() throws Exception {
+ public void testRetryNetworkSuggestedRetryTooManyTimes() throws Exception {
mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
new String[]{"mms:2000,3000", "default:1000,4000,7000,9000"});
+ int maxRetryCount = 10;
+
+ mBundle.putInt(CarrierConfigManager
+ .KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT,
+ maxRetryCount);
+
ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
waitingApns.add(myApn1);
waitingApns.add(myApn2);
- RetryManager rm = new RetryManager(mPhone, "default");
+ RetryManager rm = new RetryManager(mPhone, mDataThrottler, ApnSetting.TYPE_DEFAULT);
rm.setWaitingApns(waitingApns);
ApnSetting nextApn = rm.getNextApnSetting();
@@ -996,61 +1017,27 @@ public class RetryManagerTest extends TelephonyTest {
delay = rm.getDelayForNextApn(false);
assertEquals(1000, delay);
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- rm.setModemSuggestedDelay(2500);
- delay = rm.getDelayForNextApn(false);
- assertEquals(2500, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- rm.setModemSuggestedDelay(2500);
- delay = rm.getDelayForNextApn(false);
- assertEquals(2500, delay);
-
- nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- rm.setModemSuggestedDelay(2500);
- delay = rm.getDelayForNextApn(false);
- assertEquals(2500, delay);
+ for (int i = 0; i < maxRetryCount; i++) {
+ nextApn = rm.getNextApnSetting();
+ assertTrue(nextApn.equals(mApn1));
+ doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_DEFAULT);
+ delay = rm.getDelayForNextApn(false);
+ assertRange(2450, 2500, delay);
+ }
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn1));
- rm.setModemSuggestedDelay(2500);
+ doReturn(2500 + SystemClock.elapsedRealtime()).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_DEFAULT);
delay = rm.getDelayForNextApn(false);
assertEquals(20000, delay);
nextApn = rm.getNextApnSetting();
assertTrue(nextApn.equals(mApn2));
- rm.setModemSuggestedDelay(RetryManager.NO_SUGGESTED_RETRY_DELAY);
+ doReturn(RetryManager.NO_SUGGESTED_RETRY_DELAY).when(mDataThrottler)
+ .getRetryTime(ApnSetting.TYPE_DEFAULT);
delay = rm.getDelayForNextApn(false);
assertEquals(4000, delay);
}
-
- /**
- * Test the scenario where reset happens before modem suggests retry.
- */
- @Test
- @SmallTest
- public void testRetryManagerResetBeforeModemSuggestedRetry() throws Exception {
-
- mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS,
- new String[]{"others:1000,4000,7000,9000"});
-
- ArrayList<ApnSetting> waitingApns = new ArrayList<ApnSetting>();
- ApnSetting myApn1 = ApnSetting.makeApnSetting(mApn1);
- ApnSetting myApn2 = ApnSetting.makeApnSetting(mApn2);
- waitingApns.add(myApn1);
- waitingApns.add(myApn2);
-
- RetryManager rm = new RetryManager(mPhone, "mms");
- rm.setWaitingApns(waitingApns);
-
- rm.setModemSuggestedDelay(10);
-
- ApnSetting nextApn = rm.getNextApnSetting();
- assertTrue(nextApn.equals(mApn1));
- long delay = rm.getDelayForNextApn(false);
- assertEquals(20000, delay);
- }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index 0aa2a4d173..1dfd48584f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -32,16 +32,20 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.net.NetworkCapabilities;
+import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.os.AsyncResult;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.telephony.AccessNetworkConstants;
import android.telephony.data.ApnSetting;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.ArraySet;
import androidx.test.filters.FlakyTest;
@@ -56,11 +60,14 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -77,7 +84,12 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest {
private String mTestName = "";
+ // List of all requests filed by a test
+ private final ArraySet<NetworkRequest> mAllNetworkRequestSet = new ArraySet<>();
+ // List of requests active in DcTracker
private final ArrayList<NetworkRequest> mNetworkRequestList = new ArrayList<>();
+ // List of complete messages associated with the network requests
+ private final Map<NetworkRequest, Message> mNetworkRequestMessageMap = new HashMap<>();
private TelephonyNetworkFactory mTelephonyNetworkFactoryUT;
private int mRequestId = 0;
@@ -155,7 +167,11 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest {
"mobile_ia,14,0,2,-1,true", "mobile_emergency,15,0,2,-1,true"});
doAnswer(invocation -> {
- mNetworkRequestList.add((NetworkRequest) invocation.getArguments()[0]);
+ final NetworkRequest req = (NetworkRequest) invocation.getArguments()[0];
+ final Message msg = (Message) invocation.getArguments()[2];
+ mNetworkRequestList.add(req);
+ mAllNetworkRequestSet.add(req);
+ mNetworkRequestMessageMap.put(req, msg);
return null;
}).when(mDcTracker).requestNetwork(any(), anyInt(), any());
@@ -174,8 +190,36 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest {
replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, mPhoneSwitcher);
mTelephonyNetworkFactoryUT = new TelephonyNetworkFactory(Looper.myLooper(), mPhone);
- verify(mConnectivityManager).registerNetworkProvider(any());
+ final ArgumentCaptor<NetworkProvider> providerCaptor =
+ ArgumentCaptor.forClass(NetworkProvider.class);
+ verify(mConnectivityManager).registerNetworkProvider(providerCaptor.capture());
+ // For NetworkFactory to function as expected, the provider ID must be set to some
+ // number > 0.
+ providerCaptor.getValue().setProviderId(1);
verify(mPhoneSwitcher).registerForActivePhoneSwitch(any(), anyInt(), any());
+
+ // Simulate the behavior of the system server. When offerNetwork is called, it will
+ // update the factory about all requests that pass the registered filter, by calling
+ // NetworkProvider#onNetworkNeeded or onNetworkUnneeded.
+ // Note that this simulation is a little bit incomplete, as the system server will
+ // *update* only for those requests for which the status has changed, but this
+ // simulation will send REQUEST_NETWORK or CANCEL_REQUEST for all registered requests.
+ // At this time it makes no difference in this test.
+ // Also, this test reads from mAllNetworkRequestSet, which is not really the list of
+ // requests sent to the system server as the test doesn't instrument that. Instead, it's
+ // the list of requests ever sent to the factory. This also makes no difference in this
+ // test at this time.
+ doAnswer(invocation -> {
+ final NetworkCapabilities capabilitiesFilter =
+ mTelephonyNetworkFactoryUT.makeNetworkFilter(
+ mSubscriptionController.getSubIdUsingPhoneId(0));
+ for (final NetworkRequest request : mAllNetworkRequestSet) {
+ final int message = request.canBeSatisfiedBy(capabilitiesFilter)
+ ? CMD_REQUEST_NETWORK : CMD_CANCEL_REQUEST;
+ mTelephonyNetworkFactoryUT.obtainMessage(message, 0, 0, request).sendToTarget();
+ }
+ return null;
+ }).when(mConnectivityManager).offerNetwork(anyInt(), any(), any(), any());
}
/**
@@ -388,4 +432,50 @@ public class TelephonyNetworkFactoryTest extends TelephonyTest {
verify(mDcTracker, times(1)).requestNetwork(any(), eq(1), any());
}
+ @Test
+ @SmallTest
+ public void testNetworkRequestReleasedDuringHandover() throws Exception {
+ createMockedTelephonyComponents();
+ doReturn(0).when(mSubscriptionController).getSubIdUsingPhoneId(0);
+ mTelephonyNetworkFactoryUT.mInternalHandler.sendEmptyMessage(
+ TelephonyNetworkFactory.EVENT_SUBSCRIPTION_CHANGED);
+
+ activatePhoneInPhoneSwitcher(0, true);
+ makeDefaultInternetRequest();
+
+ NetworkRequest mmsNetworkRequest = makeSubSpecificMmsRequest(0);
+ processAllMessages();
+
+ Field f = TelephonyNetworkFactory.class.getDeclaredField("mInternalHandler");
+ f.setAccessible(true);
+ Handler h = (Handler) f.get(mTelephonyNetworkFactoryUT);
+
+ HandoverCallback handoverCallback = mock(HandoverCallback.class);
+ //Mockito.reset(mDcTracker);
+ doReturn(mDataConnection).when(mDcTracker).getDataConnectionByApnType(anyString());
+ doReturn(true).when(mDataConnection).isActive();
+
+ HandoverParams hp = new HandoverParams(ApnSetting.TYPE_MMS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN, handoverCallback);
+ AsyncResult ar = new AsyncResult(null, hp, null);
+ h.sendMessage(h.obtainMessage(5 /* EVENT_DATA_HANDOVER_NEEDED */, ar));
+ processAllMessages();
+
+ // Now release the network request while handover is still ongoing.
+ mTelephonyNetworkFactoryUT.releaseNetworkFor(mmsNetworkRequest);
+ processAllMessages();
+
+ Message msg = mNetworkRequestMessageMap.get(mmsNetworkRequest);
+
+ Bundle bundle = msg.getData();
+ bundle.putParcelable("extra_network_request", mmsNetworkRequest);
+ bundle.putBoolean("extra_success", true);
+ bundle.putInt("extra_transport_type", AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ bundle.putBoolean("extra_handover_failure_fallback", false);
+ h.sendMessage(msg);
+ processAllMessages();
+
+ // Ensure the release is called one more time after the normal release
+ verify(mDcTracker, times(2)).releaseNetwork(any(), eq(1));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
index 12c72030c8..1e79ae1649 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
@@ -45,9 +45,9 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.lang.reflect.Field;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@@ -175,10 +175,10 @@ public class TransportManagerTest extends TelephonyTest {
verify(mTestHandler, never()).sendMessageAtTime(any(Message.class), anyLong());
}
- private LinkedList<List<QualifiedNetworks>> getAvailableNetworksList() throws Exception {
- Field f = TransportManager.class.getDeclaredField("mAvailableNetworksList");
+ private ArrayDeque<List<QualifiedNetworks>> getQueuedNetworksList() throws Exception {
+ Field f = TransportManager.class.getDeclaredField("mQueuedNetworksList");
f.setAccessible(true);
- return (LinkedList<List<QualifiedNetworks>>) f.get(mTransportManager);
+ return (ArrayDeque<List<QualifiedNetworks>>) f.get(mTransportManager);
}
@Test
@@ -235,7 +235,7 @@ public class TransportManagerTest extends TelephonyTest {
verify(mTestHandler, times(1)).sendMessageAtTime(messageArgumentCaptor.capture(),
anyLong());
- LinkedList<List<QualifiedNetworks>> listQueue = getAvailableNetworksList();
+ ArrayDeque<List<QualifiedNetworks>> listQueue = getQueuedNetworksList();
// Verify the list has been queued.
assertEquals(1, listQueue.size());
@@ -245,7 +245,7 @@ public class TransportManagerTest extends TelephonyTest {
mTransportManager.getCurrentTransport(ApnSetting.TYPE_IMS));
processAllMessages();
- listQueue = getAvailableNetworksList();
+ listQueue = getQueuedNetworksList();
// Verify the queue is empty.
assertEquals(0, listQueue.size());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
index 7a637cf1eb..2aa3f18900 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyNumberTrackerTest.java
@@ -22,6 +22,8 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import android.os.AsyncResult;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.telephony.emergency.EmergencyNumber;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -39,6 +41,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -50,6 +60,31 @@ import java.util.List;
@TestableLooper.RunWithLooper
public class EmergencyNumberTrackerTest extends TelephonyTest {
+ private static final String LOCAL_DOWNLOAD_DIRECTORY = "Download/Emergency_number_db_unit_test";
+ private static final String EMERGENCY_NUMBER_DB_OTA_FILE = "eccdata_ota";
+ private static final int CONFIG_UNIT_TEST_EMERGENCY_NUMBER_DB_VERSION = 99999;
+ private static final String CONFIG_EMERGENCY_NUMBER_ADDRESS = "54321";
+ private static final String CONFIG_EMERGENCY_NUMBER_COUNTRY = "us";
+ private static final String CONFIG_EMERGENCY_NUMBER_MNC = "";
+ private static final int CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES =
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC;
+ private static final ArrayList<String> CONFIG_EMERGENCY_NUMBER_SERVICE_URNS =
+ new ArrayList<String>();
+ private static final EmergencyNumber CONFIG_EMERGENCY_NUMBER = new EmergencyNumber(
+ CONFIG_EMERGENCY_NUMBER_ADDRESS, CONFIG_EMERGENCY_NUMBER_COUNTRY,
+ CONFIG_EMERGENCY_NUMBER_MNC, CONFIG_EMERGENCY_NUMBER_SERVICE_CATEGORIES,
+ CONFIG_EMERGENCY_NUMBER_SERVICE_URNS,
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+ private static final int OTA_UNIT_TEST_EMERGENCY_NUMBER_DB_VERSION = 999999;
+ private static final String OTA_EMERGENCY_NUMBER_ADDRESS = "98765";
+
@Mock
private Phone mPhone2; // mPhone as phone 1 is already defined in TelephonyTest.
@@ -62,6 +97,8 @@ public class EmergencyNumberTrackerTest extends TelephonyTest {
private EmergencyNumber mUsEmergencyNumber;
private String[] mEmergencyNumberPrefixTestSample = {"123", "456"};
+ private File mLocalDownloadDirectory;
+
@Before
public void setUp() throws Exception {
logd("EmergencyNumberTrackerTest +Setup!");
@@ -79,6 +116,10 @@ public class EmergencyNumberTrackerTest extends TelephonyTest {
mEmergencyNumberTrackerMock2 = new EmergencyNumberTracker(mPhone2, mSimulatedCommands);
doReturn(mEmergencyNumberTrackerMock2).when(mPhone2).getEmergencyNumberTracker();
mEmergencyNumberTrackerMock.DBG = true;
+
+ // Copy an OTA file to the test directory to similate the OTA mechanism
+ simulateOtaEmergencyNumberDb(mPhone);
+
processAllMessages();
logd("EmergencyNumberTrackerTest -Setup!");
}
@@ -87,6 +128,9 @@ public class EmergencyNumberTrackerTest extends TelephonyTest {
public void tearDown() throws Exception {
// Set back to single sim mode
setSinglePhone();
+ Path target = Paths.get(mLocalDownloadDirectory.getPath(), EMERGENCY_NUMBER_DB_OTA_FILE);
+ Files.deleteIfExists(target);
+ mLocalDownloadDirectory.delete();
super.tearDown();
}
@@ -125,6 +169,75 @@ public class EmergencyNumberTrackerTest extends TelephonyTest {
processAllMessages();
}
+ private void setOtaEmergencyNumberDbFileFolderForTesting(
+ EmergencyNumberTracker emergencyNumberTrackerMock, Phone phone) {
+ // Override the OTA emergency number database file path for testing
+ File file = new File(Environment.getExternalStorageDirectory(), LOCAL_DOWNLOAD_DIRECTORY
+ + "/" + EMERGENCY_NUMBER_DB_OTA_FILE);
+ try {
+ final ParcelFileDescriptor otaParcelFileDescriptor = ParcelFileDescriptor.open(
+ file, ParcelFileDescriptor.MODE_READ_ONLY);
+ emergencyNumberTrackerMock.obtainMessage(
+ EmergencyNumberTracker.EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH,
+ otaParcelFileDescriptor).sendToTarget();
+ logd("Changed emergency number db file folder for testing ");
+ } catch (FileNotFoundException e) {
+ logd("Failed to open emergency number db file folder for testing " + e.toString());
+ }
+ processAllMessages();
+ }
+
+ private void resetOtaEmergencyNumberDbFileFolderForTesting(
+ EmergencyNumberTracker emergencyNumberTrackerMock) {
+ emergencyNumberTrackerMock.obtainMessage(
+ EmergencyNumberTracker.EVENT_OVERRIDE_OTA_EMERGENCY_NUMBER_DB_FILE_PATH, null)
+ .sendToTarget();
+ processAllMessages();
+ }
+
+ private void sendOtaEmergencyNumberDb(EmergencyNumberTracker emergencyNumberTrackerMock) {
+ emergencyNumberTrackerMock.obtainMessage(
+ EmergencyNumberTracker.EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB).sendToTarget();
+ processAllMessages();
+ }
+
+ /**
+ * Copy an OTA file to the test directory to similate the OTA mechanism.
+ *
+ * Version: 999999
+ * Number: US, 98765, POLICE | AMBULANCE | FIRE
+ */
+ private void simulateOtaEmergencyNumberDb(Phone phone) {
+ try {
+ mLocalDownloadDirectory = new File(
+ Environment.getExternalStorageDirectory(), LOCAL_DOWNLOAD_DIRECTORY);
+ mLocalDownloadDirectory.mkdir();
+ final Path target = Paths.get(
+ mLocalDownloadDirectory.getPath(), EMERGENCY_NUMBER_DB_OTA_FILE);
+ Files.deleteIfExists(target);
+ final InputStream source = new BufferedInputStream(
+ phone.getContext().getAssets().open(EMERGENCY_NUMBER_DB_OTA_FILE));
+ Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+ logd("Copied test OTA database file to " + target);
+ } catch (Exception e) {
+ logd("Unable to copy downloaded file " + e);
+ }
+ }
+
+ private boolean hasDbEmergencyNumber(EmergencyNumber number, List<EmergencyNumber> list) {
+ return list.contains(number);
+ }
+
+ private boolean hasDbEmergencyNumber(String number, List<EmergencyNumber> list) {
+ boolean foundDbNumber = false;
+ for (EmergencyNumber num : list) {
+ if (num.getNumber().equals(number)) {
+ foundDbNumber = true;
+ }
+ }
+ return foundDbNumber;
+ }
+
private void setDsdsPhones() throws Exception {
mPhones = new Phone[] {mPhone, mPhone2};
replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
@@ -145,10 +258,16 @@ public class EmergencyNumberTrackerTest extends TelephonyTest {
@Test
public void testUpdateEmergencyCountryIso() throws Exception {
sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
+
mEmergencyNumberTrackerMock.updateEmergencyNumberDatabaseCountryChange("us");
processAllMessages();
-
assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("us"));
+ assertTrue(mEmergencyNumberTrackerMock.getLastKnownEmergencyCountryIso().equals("us"));
+
+ mEmergencyNumberTrackerMock.updateEmergencyNumberDatabaseCountryChange("");
+ processAllMessages();
+ assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals(""));
+ assertTrue(mEmergencyNumberTrackerMock.getLastKnownEmergencyCountryIso().equals("us"));
}
@Test
@@ -156,11 +275,20 @@ public class EmergencyNumberTrackerTest extends TelephonyTest {
setDsdsPhones();
sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock2);
+
mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("jp");
processAllMessages();
-
assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals("jp"));
+ assertTrue(mEmergencyNumberTrackerMock.getLastKnownEmergencyCountryIso().equals("jp"));
+ assertTrue(mEmergencyNumberTrackerMock2.getEmergencyCountryIso().equals("jp"));
+ assertTrue(mEmergencyNumberTrackerMock2.getLastKnownEmergencyCountryIso().equals("jp"));
+
+ mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("");
+ processAllMessages();
+ assertTrue(mEmergencyNumberTrackerMock.getEmergencyCountryIso().equals(""));
+ assertTrue(mEmergencyNumberTrackerMock.getLastKnownEmergencyCountryIso().equals("jp"));
assertTrue(mEmergencyNumberTrackerMock2.getEmergencyCountryIso().equals("jp"));
+ assertTrue(mEmergencyNumberTrackerMock2.getLastKnownEmergencyCountryIso().equals("jp"));
}
@Test
@@ -226,15 +354,53 @@ public class EmergencyNumberTrackerTest extends TelephonyTest {
sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
processAllMessages();
+ assertTrue(hasDbEmergencyNumber(CONFIG_EMERGENCY_NUMBER,
+ mEmergencyNumberTrackerMock.getEmergencyNumberList()));
+ }
- boolean hasDatabaseNumber = false;
- for (EmergencyNumber number : mEmergencyNumberTrackerMock.getEmergencyNumberList()) {
- if (number.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)) {
- hasDatabaseNumber = true;
- break;
- }
- }
- assertTrue(hasDatabaseNumber);
+ /**
+ * Test OTA Emergency Number Database Update Status.
+ */
+ @Test
+ public void testOtaEmergencyNumberDatabase() {
+ // Set up the Hal version as 1.4 to apply emergency number database
+ doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
+
+ sendEmergencyNumberPrefix(mEmergencyNumberTrackerMock);
+ mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("");
+ processAllMessages();
+
+ // Emergency Number Db is cached per country, given the country is empty at this time,
+ // we should not expect any db number there.
+ assertFalse(hasDbEmergencyNumber(CONFIG_EMERGENCY_NUMBER,
+ mEmergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ // Set up the OTA database file folder as sdcard for testing purposes
+ setOtaEmergencyNumberDbFileFolderForTesting(mEmergencyNumberTrackerMock, mPhone);
+ // Notify EmergerncyNumberTracker OTA database is installed.
+ sendOtaEmergencyNumberDb(mEmergencyNumberTrackerMock);
+ processAllMessages();
+
+ assertEquals(OTA_UNIT_TEST_EMERGENCY_NUMBER_DB_VERSION,
+ mEmergencyNumberTrackerMock.getEmergencyNumberDbVersion());
+
+ // Emergency Number Db is cached per country, given the country is empty at this time,
+ // we should not expect any db number there.
+ assertFalse(hasDbEmergencyNumber(OTA_EMERGENCY_NUMBER_ADDRESS,
+ mEmergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ mEmergencyNumberTrackerMock.updateEmergencyCountryIsoAllPhones("us");
+ processAllMessages();
+ assertEquals(OTA_UNIT_TEST_EMERGENCY_NUMBER_DB_VERSION,
+ mEmergencyNumberTrackerMock.getEmergencyNumberDbVersion());
+
+ // Emergency Number Db is cached per country, given the country is 'us' at this time,
+ // we should expect the 'us' db number there.
+ assertTrue(hasDbEmergencyNumber(OTA_EMERGENCY_NUMBER_ADDRESS,
+ mEmergencyNumberTrackerMock.getEmergencyNumberList()));
+
+ // Reset the OTA database file to default after testing completion
+ resetOtaEmergencyNumberDbFileFolderForTesting(mEmergencyNumberTrackerMock);
}
@Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
index 7c33f34e6e..43de6f64b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -1290,7 +1290,8 @@ public class EuiccControllerTest extends TelephonyTest {
private void callGetDownloadableSubscriptionMetadata(DownloadableSubscription subscription,
boolean complete, GetDownloadableSubscriptionMetadataResult result) {
prepareGetDownloadableSubscriptionMetadataCall(complete, result);
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
mController.getDownloadableSubscriptionMetadata(0, subscription, PACKAGE_NAME,
resultCallback);
}
@@ -1311,14 +1312,16 @@ public class EuiccControllerTest extends TelephonyTest {
}
}).when(mMockConnector).getDefaultDownloadableSubscriptionList(anyInt(), anyBoolean(),
any());
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
mController.getDefaultDownloadableSubscriptionList(CARD_ID, PACKAGE_NAME, resultCallback);
}
private void callDownloadSubscription(DownloadableSubscription subscription,
boolean switchAfterDownload, final boolean complete, final int result,
final int resolvableError, String callingPackage) {
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1345,7 +1348,8 @@ public class EuiccControllerTest extends TelephonyTest {
private void callDeleteSubscription(int subscriptionId, String iccid, final boolean complete,
final int result, String callingPackage) {
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1364,7 +1368,8 @@ public class EuiccControllerTest extends TelephonyTest {
private void callSwitchToSubscription(int subscriptionId, String iccid, final boolean complete,
final int result, String callingPackage) {
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1383,7 +1388,8 @@ public class EuiccControllerTest extends TelephonyTest {
private void callUpdateSubscriptionNickname(int subscriptionId, String iccid, String nickname,
final boolean complete, final int result, String callingPackage) {
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1403,7 +1409,8 @@ public class EuiccControllerTest extends TelephonyTest {
}
private void callEraseSubscriptions(final boolean complete, final int result) {
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1421,7 +1428,8 @@ public class EuiccControllerTest extends TelephonyTest {
}
private void callEraseSubscriptionsWithOptions(final boolean complete, final int result) {
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
@@ -1440,7 +1448,8 @@ public class EuiccControllerTest extends TelephonyTest {
}
private void callRetainSubscriptionsForFactoryReset(final boolean complete, final int result) {
- PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Exception {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 552b548174..298c3e4daa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -20,13 +20,13 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
@@ -34,7 +34,10 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
@@ -60,12 +63,10 @@ import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.InboundSmsTracker;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsBroadcastUndelivered;
-import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
-import com.android.internal.util.HexDump;
import com.android.internal.util.IState;
import com.android.internal.util.StateMachine;
@@ -75,9 +76,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
+import org.mockito.verification.VerificationMode;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@@ -92,16 +97,16 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
@Mock
private SmsHeader mSmsHeader;
private InboundSmsTracker mInboundSmsTracker;
+ private InboundSmsTracker mInboundSmsTrackerSub1;
private InboundSmsTracker mInboundSmsTrackerPart1;
private InboundSmsTracker mInboundSmsTrackerPart2;
@Mock
- private InboundSmsTracker mMockInboundSmsTracker;
- private ContentValues mInboundSmsTrackerCV;
+ private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
@Mock
- private InboundSmsTracker mMockInboundSmsTrackerSub1;
- private ContentValues mInboundSmsTrackerCVSub1;
+ private InboundSmsHandler.SmsFilter mSmsFilter;
@Mock
- private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
+ private InboundSmsHandler.SmsFilter mSmsFilter2;
+ private List<InboundSmsHandler.SmsFilter> mSmsFilters;
private GsmInboundSmsHandler mGsmInboundSmsHandler;
@@ -130,63 +135,25 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
}
/**
- * This is used only for InboundSmsTracker constructed through Cursor. For other cases
- * real objects should be used. This should be used only for tests related to
- * SmsBroadcastUndelivered.
+ * This is used only for InboundSmsTracker constructed through Cursor. This should be used only
+ * for tests related to SmsBroadcastUndelivered. Also, this adds a second tracker for multisim.
*/
- private void createMockInboundSmsTracker() {
- mInboundSmsTrackerCV = new ContentValues();
- mInboundSmsTrackerCV.put("destination_port", InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
- mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(mSmsPdu));
- mInboundSmsTrackerCV.put("address", "1234567890");
- mInboundSmsTrackerCV.put("reference_number", 1);
- mInboundSmsTrackerCV.put("sequence", 1);
- mInboundSmsTrackerCV.put("count", 1);
- mInboundSmsTrackerCV.put("date", System.currentTimeMillis());
- mInboundSmsTrackerCV.put("message_body", mMessageBody);
- mInboundSmsTrackerCV.put("display_originating_addr", "1234567890");
- mInboundSmsTrackerCV.put("sub_id", mSubId0);
-
- doReturn(1).when(mMockInboundSmsTracker).getMessageCount();
- doReturn(1).when(mMockInboundSmsTracker).getReferenceNumber();
- doReturn("1234567890").when(mMockInboundSmsTracker).getAddress();
- doReturn(1).when(mMockInboundSmsTracker).getSequenceNumber();
- doReturn(1).when(mMockInboundSmsTracker).getIndexOffset();
- doReturn(-1).when(mMockInboundSmsTracker).getDestPort();
- doReturn(mMessageBody).when(mMockInboundSmsTracker).getMessageBody();
- doReturn(mSmsPdu).when(mMockInboundSmsTracker).getPdu();
- doReturn(mInboundSmsTrackerCV.get("date")).when(mMockInboundSmsTracker).getTimestamp();
- doReturn(SmsConstants.FORMAT_3GPP).when(mMockInboundSmsTracker).getFormat();
- doReturn(mInboundSmsTrackerCV).when(mMockInboundSmsTracker).getContentValues();
- doReturn(mSubId0).when(mMockInboundSmsTracker).getSubId();
-
- mInboundSmsTrackerCVSub1 = new ContentValues();
- mInboundSmsTrackerCVSub1.put("destination_port", InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
- mInboundSmsTrackerCVSub1.put("pdu", HexDump.toHexString(mSmsPdu));
- mInboundSmsTrackerCVSub1.put("address", "1234567890");
- mInboundSmsTrackerCVSub1.put("reference_number", 1);
- mInboundSmsTrackerCVSub1.put("sequence", 1);
- mInboundSmsTrackerCVSub1.put("count", 1);
- mInboundSmsTrackerCVSub1.put("date", System.currentTimeMillis());
- mInboundSmsTrackerCVSub1.put("message_body", mMessageBody);
- mInboundSmsTrackerCVSub1.put("display_originating_addr", "1234567890");
- mInboundSmsTrackerCVSub1.put("sub_id", mSubId1);
-
- doReturn(1).when(mMockInboundSmsTrackerSub1).getMessageCount();
- doReturn(1).when(mMockInboundSmsTrackerSub1).getReferenceNumber();
- doReturn("1234567890").when(mMockInboundSmsTrackerSub1).getAddress();
- doReturn(1).when(mMockInboundSmsTrackerSub1).getSequenceNumber();
- doReturn(1).when(mMockInboundSmsTrackerSub1).getIndexOffset();
- doReturn(-1).when(mMockInboundSmsTrackerSub1).getDestPort();
- doReturn(mMessageBody).when(mMockInboundSmsTrackerSub1).getMessageBody();
- doReturn(mSmsPdu).when(mMockInboundSmsTrackerSub1).getPdu();
- doReturn(mInboundSmsTrackerCVSub1.get("date")).when(mMockInboundSmsTrackerSub1)
- .getTimestamp();
- doReturn(SmsConstants.FORMAT_3GPP).when(mMockInboundSmsTrackerSub1).getFormat();
- doReturn(mInboundSmsTrackerCVSub1).when(mMockInboundSmsTrackerSub1).getContentValues();
- doReturn(mSubId1).when(mMockInboundSmsTrackerSub1).getSubId();
-
- doReturn(mMockInboundSmsTracker).doReturn(mMockInboundSmsTrackerSub1)
+ private void createInboundSmsTrackerMultiSim() {
+ mInboundSmsTrackerSub1 = new InboundSmsTracker(
+ mContext,
+ mSmsPdu, /* pdu */
+ System.currentTimeMillis(), /* timestamp */
+ -1, /* destPort */
+ false, /* is3gpp2 */
+ false, /* is3gpp2WapPdu */
+ "1234567890", /* address */
+ "1234567890", /* displayAddress */
+ mMessageBody, /* messageBody */
+ false, /* isClass0 */
+ mSubId1,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
+
+ doReturn(mInboundSmsTracker).doReturn(mInboundSmsTrackerSub1)
.when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(Cursor.class),
anyBoolean());
@@ -220,14 +187,15 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
- createMockInboundSmsTracker();
+ createInboundSmsTrackerMultiSim();
mContentProvider = new FakeSmsContentProvider();
((MockContentResolver)mContext.getContentResolver()).addProvider(
@@ -235,8 +203,17 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext,
mSmsStorageMonitor, mPhone);
+ mSmsFilters = new ArrayList<>();
+ mSmsFilters.add(mSmsFilter);
+ mSmsFilters.add(mSmsFilter2);
+ mGsmInboundSmsHandler.setSmsFiltersForTesting(mSmsFilters);
monitorTestableLooper(new TestableLooper(mGsmInboundSmsHandler.getHandler().getLooper()));
+
+ doReturn(mGsmInboundSmsHandler).when(mPhone).getInboundSmsHandler(false);
+ doReturn(mCdmaInboundSmsHandler).when(mPhone).getInboundSmsHandler(true);
+
processAllMessages();
+ logd("setUp: complete");
}
@After
@@ -342,8 +319,11 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
verify(mContext, times(2)).sendBroadcast(any(Intent.class));
assertEquals("IdleState", getCurrentState().getName());
+
+ verifySmsFiltersInvoked(times(1));
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
@@ -356,6 +336,126 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
verify(mContext, never()).sendBroadcast(any(Intent.class));
assertEquals("IdleState", getCurrentState().getName());
+
+ // Filter should still be invoked.
+ verifySmsFiltersInvoked(times(1));
+ }
+
+ @FlakyTest // temporarily disabled, see b/182498318
+ @Test
+ @MediumTest
+ public void testNewSmsWithUserLocked_notificationShown() {
+ // user locked
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ doReturn(false).when(userManager).isUserUnlocked();
+
+ transitionFromStartupToIdle();
+
+ sendNewSms();
+
+ verify(mContext, never()).sendBroadcast(any(Intent.class));
+ assertEquals("IdleState", getCurrentState().getName());
+
+ // Filter should be invoked.
+ verifySmsFiltersInvoked(times(1));
+
+ // New message notification should be shown.
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ verify(notificationManager).notify(
+ eq(InboundSmsHandler.NOTIFICATION_TAG),
+ eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+ any(Notification.class));
+ }
+
+ @FlakyTest // temporarily disabled, see b/182498318
+ @Test
+ @MediumTest
+ public void testNewSmsFromBlockedNumberWithUserLocked_noNotificationShown() {
+ String blockedNumber = "1234567890";
+ mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
+
+ // user locked
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ doReturn(false).when(userManager).isUserUnlocked();
+
+ transitionFromStartupToIdle();
+
+ sendNewSms();
+
+ verify(mContext, never()).sendBroadcast(any(Intent.class));
+ assertEquals("IdleState", getCurrentState().getName());
+
+ // Filter should be invoked.
+ verifySmsFiltersInvoked(times(1));
+
+ // No new message notification should be shown.
+ NotificationManager notificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ verify(notificationManager, never()).notify(
+ eq(InboundSmsHandler.NOTIFICATION_TAG),
+ eq(InboundSmsHandler.NOTIFICATION_ID_NEW_MESSAGE),
+ any(Notification.class));
+ }
+
+ @FlakyTest // temporarily disabled, see b/182498318
+ @Test
+ @MediumTest
+ public void testNewSms_filterInvoked_noBroadcastsSent() {
+ // Configure the first filter to drop the SMS.
+ when(mSmsFilter.filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any()))
+ .thenAnswer((Answer<Boolean>) invocation -> {
+ mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_COMPLETE);
+ return true;
+ });
+
+ transitionFromStartupToIdle();
+
+ sendNewSms();
+
+ verify(mContext, never()).sendBroadcast(any(Intent.class));
+ assertEquals("IdleState", getCurrentState().getName());
+
+ // verify second filter was never invoked.
+ verify(mSmsFilter2, never()).filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
+ }
+
+ @FlakyTest // temporarily disabled, see b/182498318
+ @Test
+ @MediumTest
+ public void testNewSms_filterChaining_noBroadcastsSent() {
+ // Have the first filter indicate it matched without completing the flow.
+ when(mSmsFilter.filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any()))
+ .thenReturn(true);
+
+ transitionFromStartupToIdle();
+
+ sendNewSms();
+
+ verify(mContext, never()).sendBroadcast(any(Intent.class));
+ // Now waiting for the first filter to complete.
+ assertEquals("WaitingState", getCurrentState().getName());
+
+ // Verify the first filter was invoked with the right set of remaining filters.
+ verify(mSmsFilter).filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), eq(Collections.singletonList(mSmsFilter2)));
+
+ // Verify second filter was never invoked.
+ verify(mSmsFilter2, never()).filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
+
+ // Clean up by completing the broadcast, as an asynchronous filter must do.
+ mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_COMPLETE);
+ processAllMessages();
+ assertEquals("IdleState", getCurrentState().getName());
}
private void verifyDataSmsIntentBroadcasts(int numPastBroadcasts) {
@@ -364,9 +464,10 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
intentArgumentCaptor.capture());
assertEquals(Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION,
intentArgumentCaptor.getAllValues().get(numPastBroadcasts).getAction());
- assertNotEquals(0L,
+ // TODO mock messageId correctly in InboundSmsTracker
+ /* assertNotEquals(0L,
intentArgumentCaptor.getAllValues().get(numPastBroadcasts)
- .getLongExtra("messageId", 0L));
+ .getLongExtra("messageId", 0L)); */
assertEquals("WaitingState", getCurrentState().getName());
mContextFixture.sendBroadcastToOrderedBroadcastReceivers();
@@ -375,6 +476,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
assertEquals("IdleState", getCurrentState().getName());
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testClass0Sms() {
@@ -391,19 +493,22 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
true, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS,
mInboundSmsTracker);
processAllMessages();
verifySmsIntentBroadcasts(0, true /* allowBgActivityStarts */);
+ verifySmsFiltersInvoked(times(1));
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastSms() {
@@ -420,12 +525,13 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
false, /* isClass0 */
- mSubId0));
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED));
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
doReturn(2131L).when(mInboundSmsTracker).getMessageId();
mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS,
mInboundSmsTracker);
@@ -439,6 +545,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
processAllMessages();
verifyDataSmsIntentBroadcasts(1);
+
+ verifySmsFiltersInvoked(times(2));
}
@FlakyTest
@@ -460,6 +568,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
verify(mContext, times(2)).sendBroadcast(any(Intent.class));
assertEquals("IdleState", getCurrentState().getName());
+
+ verifySmsFiltersInvoked(times(1));
}
private void prepareMultiPartSms(boolean is3gpp2WapPush) {
@@ -478,7 +588,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
is3gpp2WapPush, /* is3gpp2WapPdu */
mMessageBodyPart1, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
// Part 2
mInboundSmsTrackerPart2 = new InboundSmsTracker(
@@ -495,9 +606,11 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
is3gpp2WapPush, /* is3gpp2WapPdu */
mMessageBodyPart2, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultiPartSmsWithIncompleteWAP() {
@@ -519,7 +632,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -531,7 +645,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -539,6 +654,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
// verify no broadcast sent.
verify(mContext, times(0)).sendBroadcast(any(Intent.class));
+ verifySmsFiltersInvoked(never());
// additional copy of part 1 of non-3gpp2wap
prepareMultiPartSms(false);
@@ -546,7 +662,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify broadcast intents
@@ -555,6 +672,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
// verify there are three segments in the db and only one of them is not marked as deleted.
assertEquals(3, mContentProvider.getNumRows());
assertEquals(1, mContentProvider.query(sRawUri, null, "deleted=0", null, null).getCount());
+
+ verifySmsFiltersInvoked(times(1));
}
@FlakyTest
@@ -573,7 +692,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -583,11 +703,13 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify broadcast intents
verifySmsIntentBroadcasts(0);
+ verifySmsFiltersInvoked(times(1));
// if an additional copy of one of the segments above is received, it should not be kept in
// the db and should not be combined with any subsequent messages received from the same
@@ -598,11 +720,13 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no additional broadcasts sent
verify(mContext, times(2)).sendBroadcast(any(Intent.class));
+ verifySmsFiltersInvoked(times(1));
// part 1 of new sms recieved from same sender with same parameters, just different
// timestamps, should not be combined with the additional part 2 received above
@@ -615,15 +739,18 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no additional broadcasts sent
verify(mContext, times(2)).sendBroadcast(any(Intent.class));
+ verifySmsFiltersInvoked(times(1));
assertEquals("IdleState", getCurrentState().getName());
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultiPartIncompleteSms() {
@@ -650,7 +777,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
false, /* is3gpp2WapPdu */
mMessageBodyPart2, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
mSmsHeader.concatRef = new SmsHeader.ConcatRef();
doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
@@ -659,7 +787,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -669,7 +798,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no broadcasts sent
@@ -682,8 +812,10 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
assertEquals(mMessageBodyPart2, c.getString(c.getColumnIndex("message_body")));
// State machine should go back to idle
assertEquals("IdleState", getCurrentState().getName());
+ verifySmsFiltersInvoked(never());
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultiPartSmsWithInvalidSeqNumber() {
@@ -699,7 +831,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify the message is stored in the raw table
@@ -724,21 +857,25 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
false, /* is3gpp2WapPdu */
mMessageBodyPart2, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no broadcasts sent
verify(mContext, never()).sendBroadcast(any(Intent.class));
// State machine should go back to idle
assertEquals("IdleState", getCurrentState().getName());
+ verifySmsFiltersInvoked(never());
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultipartSmsFromBlockedNumber_noBroadcastsSent() {
@@ -755,7 +892,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
@@ -766,13 +904,17 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
verify(mContext, never()).sendBroadcast(any(Intent.class));
assertEquals("IdleState", getCurrentState().getName());
+ // Filter should still be invoked.
+ verifySmsFiltersInvoked(times(1));
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testMultipartSmsFromBlockedEmail_noBroadcastsSent() {
@@ -797,7 +939,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
false, /* is3gpp2WapPdu */
mMessageBodyPart1, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
mSmsHeader.concatRef = new SmsHeader.ConcatRef();
doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
@@ -805,7 +948,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
@@ -816,22 +960,93 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
verify(mContext, never()).sendBroadcast(any(Intent.class));
assertEquals("IdleState", getCurrentState().getName());
+ // Filter should still be invoked.
+ verifySmsFiltersInvoked(times(1));
}
+ @FlakyTest // temporarily disabled, see b/182498318
+ @Test
+ @MediumTest
+ public void testMultipartSms_filterInvoked_noBroadcastsSent() {
+ // Configure the first filter to drop the SMS.
+ when(mSmsFilter.filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any()))
+ .thenAnswer((Answer<Boolean>) invocation -> {
+ mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_COMPLETE);
+ return true;
+ });
+
+ transitionFromStartupToIdle();
+
+ // prepare SMS part 1 and part 2
+ prepareMultiPartSms(false);
+
+ mSmsHeader.concatRef = new SmsHeader.ConcatRef();
+ doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
+
+ doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
+ .makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
+ anyInt(), anyBoolean(),
+ nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
+ sendNewSms();
+
+ // State machine should go back to idle and wait for second part
+ assertEquals("IdleState", getCurrentState().getName());
+
+ doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
+ .makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
+ anyInt(), anyBoolean(),
+ nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
+ sendNewSms();
+
+ // verify no broadcasts sent
+ verify(mContext, never()).sendBroadcast(any(Intent.class));
+ assertEquals("IdleState", getCurrentState().getName());
+
+ // verify second filter was never invoked.
+ verify(mSmsFilter2, never()).filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
+ }
+
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredUserLocked() throws Exception {
replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
- doReturn(0).when(mMockInboundSmsTracker).getDestPort();
- doReturn(2131L).when(mMockInboundSmsTracker).getMessageId();
+
+ mInboundSmsTracker = new InboundSmsTracker(
+ mContext,
+ mSmsPdu, /* pdu */
+ System.currentTimeMillis(), /* timestamp */
+ 0, /* destPort */
+ false, /* is3gpp2 */
+ false, /* is3gpp2WapPdu */
+ "1234567890", /* address */
+ "1234567890", /* displayAddress */
+ mMessageBody, /* messageBody */
+ false, /* isClass0 */
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
+
+ doReturn(mInboundSmsTracker)
+ .when(mTelephonyComponentFactory)
+ .makeInboundSmsTracker(any(Context.class), nullable(Cursor.class),
+ anyBoolean());
// add a fake entry to db
- mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
+ mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());
// user locked
UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE);
@@ -860,17 +1075,35 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
processAllMessages();
verifyDataSmsIntentBroadcasts(1);
+ verifySmsFiltersInvoked(times(1));
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredUserUnlocked() throws Exception {
replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
- doReturn(0).when(mMockInboundSmsTracker).getDestPort();
- doReturn(2131L).when(mMockInboundSmsTracker).getMessageId();
+ mInboundSmsTracker = new InboundSmsTracker(
+ mContext,
+ mSmsPdu, /* pdu */
+ System.currentTimeMillis(), /* timestamp */
+ 0, /* destPort */
+ false, /* is3gpp2 */
+ false, /* is3gpp2WapPdu */
+ "1234567890", /* address */
+ "1234567890", /* displayAddress */
+ mMessageBody, /* messageBody */
+ false, /* isClass0 */
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
+
+ doReturn(mInboundSmsTracker)
+ .when(mTelephonyComponentFactory)
+ .makeInboundSmsTracker(any(Context.class), nullable(Cursor.class),
+ anyBoolean());
// add a fake entry to db
- mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
+ mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());
SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
@@ -880,8 +1113,10 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
// user is unlocked; intent should be broadcast right away
verifyDataSmsIntentBroadcasts(0);
+ verifySmsFiltersInvoked(times(1));
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredDeleted() throws Exception {
@@ -898,12 +1133,13 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
//add a fake entry to db
ContentValues rawSms = new ContentValues();
@@ -918,7 +1154,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
verify(mContext, times(1)).sendBroadcast(any(Intent.class));
assertEquals("IdleState", getCurrentState().getName());
-
+ verifySmsFiltersInvoked(never());
}
@FlakyTest
@@ -945,16 +1181,18 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
processAllMessages();
verifySmsIntentBroadcasts(0);
+ verifySmsFiltersInvoked(times(1));
}
+ @FlakyTest // temporarily disabled, see b/182498318
@Test
@MediumTest
public void testBroadcastUndeliveredMultiSim() throws Exception {
replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
// add SMSs from different subs to db
- mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
- mContentProvider.insert(sRawUri, mMockInboundSmsTrackerSub1.getContentValues());
+ mContentProvider.insert(sRawUri, mInboundSmsTracker.getContentValues());
+ mContentProvider.insert(sRawUri, mInboundSmsTrackerSub1.getContentValues());
SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
// wait for ScanRawTableThread
@@ -963,5 +1201,47 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
verifySmsIntentBroadcasts(0, mSubId0, true);
verifySmsIntentBroadcasts(2, mSubId1, false);
+ verifySmsFiltersInvoked(times(2));
+ }
+
+ private void verifySmsFiltersInvoked(VerificationMode verificationMode) {
+ verify(mSmsFilter, verificationMode).filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
+ verify(mSmsFilter2, verificationMode).filterSms(any(byte[][].class), anyInt(),
+ any(InboundSmsTracker.class), any(InboundSmsHandler.SmsBroadcastReceiver.class),
+ anyBoolean(), anyBoolean(), Mockito.<List<InboundSmsHandler.SmsFilter>>any());
+ }
+
+ @Test
+ @MediumTest
+ public void testBroadcastTimeout() {
+ InboundSmsHandler.sTimeoutDurationMillis = 100;
+ transitionFromStartupToIdle();
+
+ // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION
+ sendNewSms();
+
+ ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcast(intentArgumentCaptor.capture());
+ Intent intent = intentArgumentCaptor.getAllValues().get(0);
+ assertEquals(Telephony.Sms.Intents.SMS_DELIVER_ACTION, intent.getAction());
+ assertEquals("WaitingState", getCurrentState().getName());
+
+ // don't send broadcast back to InboundSmsHandler, instead wait for timeout
+ waitForMs(300);
+ processAllMessages();
+
+ intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(2)).sendBroadcast(intentArgumentCaptor.capture());
+ intent = intentArgumentCaptor.getAllValues().get(1);
+ assertEquals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION, intent.getAction());
+
+ // don't send broadcast back to InboundSmsHandler, instead wait for timeout
+ waitForMs(300);
+ processAllMessages();
+
+ assertEquals("IdleState", getCurrentState().getName());
+
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
new file mode 100644
index 0000000000..ee4f6e5a26
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.gsm;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for the {@link GsmMmiCodeTest}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class GsmMmiCodeTest extends TelephonyTest {
+ private static final String TEST_DIAL_STRING_SERVICE_CODE = "*67911";
+ private static final String TEST_DIAL_STRING_NO_SERVICE_CODE = "*767911";
+ private static final String TEST_DIAL_STRING_NON_EMERGENCY_NUMBER = "11976";
+ private GsmMmiCode mGsmMmiCode;
+ private GsmCdmaPhone mGsmCdmaPhoneUT;
+
+ private Executor mExecutor = new Executor() {
+ @Override
+ public void execute(Runnable r) {
+ r.run();
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ doReturn(mExecutor).when(mContext).getMainExecutor();
+ mGsmCdmaPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
+ PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager);
+ setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testIsTemporaryModeCLIRFromCarrierConfig() {
+ // Test *67911 is treated as temporary mode CLIR
+ doReturn(true).when(mTelephonyManager).isEmergencyNumber(TEST_DIAL_STRING_SERVICE_CODE);
+ mGsmMmiCode = GsmMmiCode.newFromDialString(TEST_DIAL_STRING_SERVICE_CODE, mGsmCdmaPhoneUT,
+ null, null);
+ assertTrue(mGsmMmiCode.isTemporaryModeCLIR());
+ }
+
+ @Test
+ public void testIsTemporaryModeCLIRForNonServiceCode() {
+ // Test if prefix isn't *67 or *82
+ doReturn(true).when(mTelephonyManager).isEmergencyNumber(TEST_DIAL_STRING_NO_SERVICE_CODE);
+ mGsmMmiCode = GsmMmiCode.newFromDialString(TEST_DIAL_STRING_NO_SERVICE_CODE,
+ mGsmCdmaPhoneUT, null, null);
+ assertTrue(mGsmMmiCode == null);
+ }
+
+ @Test
+ public void testIsTemporaryModeCLIRForNonEmergencyNumber() {
+ // Test if dialing string isn't an emergency number
+ mGsmMmiCode = GsmMmiCode.newFromDialString(TEST_DIAL_STRING_NON_EMERGENCY_NUMBER,
+ mGsmCdmaPhoneUT, null, null);
+ assertTrue(mGsmMmiCode == null);
+ }
+
+ @Test
+ public void testNoCrashOnEmptyMessage() {
+ GsmMmiCode mmi = GsmMmiCode.newNetworkInitiatedUssd(null, true, mGsmCdmaPhoneUT, null);
+ try {
+ mmi.onUssdFinishedError();
+ } catch (Exception e) {
+ fail("Shouldn't crash!!!");
+ }
+ }
+
+ private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager
+ .KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, true);
+ doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 94b8987a70..3fdbe7935d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -23,33 +23,44 @@ import static com.android.internal.telephony.SmsUsageMonitor.PREMIUM_SMS_PERMISS
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Activity;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.location.Country;
import android.location.CountryDetector;
import android.os.HandlerThread;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.provider.Telephony;
+import android.service.carrier.CarrierMessagingService;
+import android.service.carrier.ICarrierMessagingCallback;
+import android.service.carrier.ICarrierMessagingService;
import android.telephony.SmsManager;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.util.Singleton;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
import com.android.internal.telephony.ContextFixture;
import com.android.internal.telephony.ISub;
@@ -63,15 +74,22 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class GsmSmsDispatcherTest extends TelephonyTest {
private static final long TIMEOUT_MS = 500;
+ private static final String CARRIER_APP_PACKAGE_NAME = "com.android.carrier";
@Mock
private android.telephony.SmsMessage mSmsMessage;
@@ -87,6 +105,9 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
private SMSDispatcher.SmsTracker mSmsTracker;
@Mock
private ISub.Stub mISubStub;
+ @Mock
+ private ICarrierMessagingService.Stub mICarrierAppMessagingService;
+
private Object mLock = new Object();
private boolean mReceivedTestIntent;
private static final String TEST_INTENT = "com.android.internal.telephony.TEST_INTENT";
@@ -96,6 +117,7 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
logd("onReceive");
synchronized (mLock) {
mReceivedTestIntent = true;
+ mLock.notifyAll();
}
}
};
@@ -130,6 +152,9 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
mGsmSmsDispatcherTestHandler = new GsmSmsDispatcherTestHandler(getClass().getSimpleName());
mGsmSmsDispatcherTestHandler.start();
waitUntilReady();
+ mGsmSmsDispatcher = new GsmSMSDispatcher(mPhone, mSmsDispatchersController,
+ mGsmInboundSmsHandler);
+ processAllMessages();
}
@After
@@ -143,9 +168,8 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
@Test @SmallTest
public void testSmsStatus() {
mSimulatedCommands.notifySmsStatus(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF});
- TelephonyTestUtils.waitForMs(50);
- verify(mSimulatedCommandsVerifier).acknowledgeLastIncomingGsmSms(true,
- Telephony.Sms.Intents.RESULT_SMS_HANDLED, null);
+ processAllMessages();
+ verify(mSimulatedCommandsVerifier).acknowledgeLastIncomingGsmSms(true, 0, null);
}
@Test @MediumTest
@@ -204,18 +228,22 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
}
}
- @Test
- @SmallTest
- @FlakyTest
- @Ignore
- public void testSendTextWithInvalidDestAddr() throws Exception {
+ private void registerTestIntentReceiver() throws Exception {
// unmock ActivityManager to be able to register receiver, create real PendingIntent and
// receive TEST_INTENT
restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
Context realContext = TestApplication.getAppContext();
realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
- PendingIntent pendingIntent = PendingIntent.getBroadcast(realContext, 0,
+ }
+
+ @Test
+ @SmallTest
+ @FlakyTest
+ @Ignore
+ public void testSendTextWithInvalidDestAddr() throws Exception {
+ registerTestIntentReceiver();
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
new Intent(TEST_INTENT), 0);
// send invalid dest address: +
mReceivedTestIntent = false;
@@ -252,7 +280,8 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
Settings.Global.DEVICE_PROVISIONED, 1);
mGsmSmsDispatcher.sendRawPdu(new SMSDispatcher.SmsTracker[] {mSmsTracker});
- waitForHandlerAction(mGsmSmsDispatcher, TIMEOUT_MS);
+ //waitForHandlerAction(mGsmSmsDispatcher, TIMEOUT_MS);
+ processAllMessages();
verify(mSmsUsageMonitor, times(1)).checkDestination(any(), any());
verify(mSmsUsageMonitor, times(1)).getPremiumSmsPermission(any());
@@ -263,14 +292,10 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
}
@Test @SmallTest
+ @FlakyTest
+ @Ignore
public void testSendMultipartTextWithInvalidText() throws Exception {
- // unmock ActivityManager to be able to register receiver, create real PendingIntent and
- // receive TEST_INTENT
- restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
- restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
-
- Context realContext = TestApplication.getAppContext();
- realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
+ registerTestIntentReceiver();
// initiate parameters for an invalid text MO SMS (the 2nd segmeant has 161 characters)
ArrayList<String> parts = new ArrayList<>();
@@ -280,8 +305,8 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
+ "8");
ArrayList<PendingIntent> sentIntents = new ArrayList<>();
- PendingIntent sentIntent = PendingIntent.getBroadcast(realContext, 0,
- new Intent(TEST_INTENT), 0);
+ PendingIntent sentIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
+ new Intent(TEST_INTENT), PendingIntent.FLAG_IMMUTABLE);
sentIntents.add(sentIntent);
sentIntents.add(sentIntent);
@@ -296,4 +321,193 @@ public class GsmSmsDispatcherTest extends TelephonyTest {
assertEquals(SmsManager.RESULT_ERROR_GENERIC_FAILURE, mTestReceiver.getResultCode());
}
}
+
+ private void mockCarrierApp()
+ throws RemoteException {
+ mContextFixture.addService(
+ CarrierMessagingService.SERVICE_INTERFACE,
+ new ComponentName(CARRIER_APP_PACKAGE_NAME, "CarrierAppFilterClass"),
+ CARRIER_APP_PACKAGE_NAME,
+ mICarrierAppMessagingService,
+ new ServiceInfo());
+ when(mICarrierAppMessagingService.asBinder()).thenReturn(mICarrierAppMessagingService);
+ mockUiccWithCarrierApp();
+ }
+
+ private void mockUiccWithCarrierApp() {
+ when(mUiccController.getUiccCard(mPhone.getPhoneId())).thenReturn(mUiccCard);
+ List<String> carrierPackages = new ArrayList<>();
+ carrierPackages.add(CARRIER_APP_PACKAGE_NAME);
+ when(mUiccCard.getCarrierPackageNamesForIntent(
+ any(PackageManager.class), any(Intent.class))).thenReturn(carrierPackages);
+ }
+
+ private void mockCarrierAppStubResults(final int result, ICarrierMessagingService.Stub stub,
+ boolean callOnFilterComplete)
+ throws RemoteException {
+ when(stub.queryLocalInterface(anyString())).thenReturn(stub);
+ when(stub.asBinder()).thenReturn(stub);
+ // for single part
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ ICarrierMessagingCallback callback = (ICarrierMessagingCallback) args[4];
+ if (callOnFilterComplete) {
+ callback.onSendSmsComplete(result, 0);
+ }
+ return null;
+ }
+ }).when(stub).sendTextSms(
+ anyString(), anyInt(), anyString(), anyInt(),
+ any(ICarrierMessagingCallback.class));
+
+ // for multi part
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ ICarrierMessagingCallback callback = (ICarrierMessagingCallback) args[4];
+ if (callOnFilterComplete) {
+ callback.onSendMultipartSmsComplete(result, null);
+ }
+ return null;
+ }
+ }).when(stub).sendMultipartTextSms(
+ any(), anyInt(), anyString(), anyInt(),
+ any(ICarrierMessagingCallback.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testSendSmsByCarrierApp() throws Exception {
+ mockCarrierApp();
+ mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
+ mICarrierAppMessagingService, true);
+ registerTestIntentReceiver();
+
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
+ new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
+ mReceivedTestIntent = false;
+
+ mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
+ pendingIntent, null, null, null, false, -1, false, -1, false, 0L);
+ processAllMessages();
+ synchronized (mLock) {
+ if (!mReceivedTestIntent) {
+ // long wait since sometimes broadcasts can take a long time if the system is loaded
+ mLock.wait(60000);
+ }
+ assertEquals(true, mReceivedTestIntent);
+ int resultCode = mTestReceiver.getResultCode();
+ assertTrue("Unexpected result code: " + resultCode,
+ resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK);
+ verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
+ any(Message.class));
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testSendSmsByCarrierAppNoResponse() throws Exception {
+ mockCarrierApp();
+ // do not mock result, instead reduce the timeout for test
+ mGsmSmsDispatcher.mCarrierMessagingTimeout = 100;
+
+ mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
+ null, null, null, null, false, -1, false, -1, false, 0L);
+ // wait for timeout
+ waitForMs(150);
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testSendSmsByCarrierAppBindingFailed() throws Exception {
+ mContextFixture.mockBindingFailureForPackage(CARRIER_APP_PACKAGE_NAME);
+ // mock presence of carrier app, but do not create a mock service to make binding fail
+ mockUiccWithCarrierApp();
+
+ mGsmSmsDispatcher.sendText("6501002000", "121" /*scAddr*/, "test sms",
+ null, null, null, null, false, -1, false, -1, false, 0L);
+ processAllMessages();
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(), any(Message.class));
+ }
+
+ private void sendMultipartTextSms(boolean withSentIntents) {
+ // initiate parameters for a multipart sms
+ ArrayList<String> parts = new ArrayList<>();
+ parts.add("segment1");
+ parts.add("segment2");
+
+ ArrayList<PendingIntent> sentIntents = new ArrayList<>();
+ PendingIntent sentIntent1 = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
+ new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
+ PendingIntent sentIntent2 = PendingIntent.getBroadcast(TestApplication.getAppContext(), 0,
+ new Intent(TEST_INTENT), PendingIntent.FLAG_MUTABLE);
+ sentIntents.add(sentIntent1);
+ sentIntents.add(sentIntent2);
+
+ mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
+ withSentIntents ? sentIntents : null, null, null, null, false, -1, false, -1, 0L);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendMultipartSmsByCarrierApp() throws Exception {
+ mockCarrierApp();
+ mockCarrierAppStubResults(CarrierMessagingService.SEND_STATUS_OK,
+ mICarrierAppMessagingService, true);
+ registerTestIntentReceiver();
+
+ // send SMS and check sentIntent
+ mReceivedTestIntent = false;
+ sendMultipartTextSms(true);
+ processAllMessages();
+ synchronized (mLock) {
+ if (!mReceivedTestIntent) {
+ // long wait since sometimes broadcasts can take a long time if the system is loaded
+ mLock.wait(60000);
+ }
+ assertEquals(true, mReceivedTestIntent);
+ int resultCode = mTestReceiver.getResultCode();
+ assertTrue("Unexpected result code: " + resultCode,
+ resultCode == SmsManager.RESULT_ERROR_NONE || resultCode == Activity.RESULT_OK);
+ verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
+ any(Message.class));
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testSendMultipartSmsByCarrierAppNoResponse() throws Exception {
+ mockCarrierApp();
+ // do not mock result, instead reduce the timeout for test
+ mGsmSmsDispatcher.mCarrierMessagingTimeout = 100;
+
+ sendMultipartTextSms(false);
+
+ // wait for timeout
+ waitForMs(150);
+ verify(mSimulatedCommandsVerifier).sendSMSExpectMore(anyString(), anyString(),
+ any(Message.class));
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(),
+ any(Message.class));
+ }
+
+ @Test
+ @SmallTest
+ public void testSendMultipartSmsByCarrierAppBindingFailed() throws Exception {
+ mContextFixture.mockBindingFailureForPackage(CARRIER_APP_PACKAGE_NAME);
+ // mock presence of carrier app, but do not create a mock service to make binding fail
+ mockUiccWithCarrierApp();
+
+ sendMultipartTextSms(false);
+
+ processAllMessages();
+ verify(mSimulatedCommandsVerifier).sendSMSExpectMore(anyString(), anyString(),
+ any(Message.class));
+ verify(mSimulatedCommandsVerifier).sendSMS(anyString(), anyString(),
+ any(Message.class));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java
deleted file mode 100644
index bac45efdb1..0000000000
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectionTest.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.ims;
-
-import junit.framework.AssertionFailedError;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.ims.aidl.IImsRegistration;
-import android.telephony.ims.feature.ImsFeature;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.ims.FeatureConnection;
-import com.android.ims.ImsManager;
-import com.android.ims.internal.IImsServiceFeatureCallback;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.util.concurrent.Executor;
-
-public class FeatureConnectionTest extends TelephonyTest {
-
- private Executor mSimpleExecutor = new Executor() {
- @Override
- public void execute(Runnable r) {
- r.run();
- }
- };
-
- private class TestFeatureConnection extends FeatureConnection {
- private Integer mFeatureState = ImsFeature.STATE_READY;
-
- public boolean isFeatureCreatedCalled = false;
- public boolean isFeatureRemovedCalled = false;
- public int mNewStatus = ImsFeature.STATE_UNAVAILABLE;
-
- TestFeatureConnection(Context context, int slotId) {
- super(context, slotId);
- if (!ImsManager.isImsSupportedOnDevice(context)) {
- sImsSupportedOnDevice = false;
- }
- }
-
- @Override
- public void checkServiceIsReady() throws RemoteException {
- super.checkServiceIsReady();
- }
-
- @Override
- protected void handleImsFeatureCreatedCallback(int slotId, int feature) {
- isFeatureCreatedCalled = true;
- }
-
- @Override
- protected void handleImsFeatureRemovedCallback(int slotId, int feature) {
- isFeatureRemovedCalled = true;
- }
-
- @Override
- protected void handleImsStatusChangedCallback(int slotId, int feature, int status) {
- mNewStatus = status;
- }
-
- @Override
- protected Integer retrieveFeatureState() {
- return mFeatureState;
- }
-
- @Override
- protected IImsRegistration getRegistrationBinder() {
- return getTestRegistrationBinder();
- }
-
- public void setFeatureState(int state) {
- mFeatureState = state;
- }
- };
-
- private int mPhoneId;
- private TestFeatureConnection mTestFeatureConnection;
- @Mock IBinder mBinder;
- @Mock IImsRegistration mRegistrationBinder;
-
- @Before
- public void setUp() throws Exception {
- super.setUp("FeatureConnectionTest");
- mPhoneId = mPhone.getPhoneId();
-
- doReturn(null).when(mContext).getMainLooper();
- doReturn(true).when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS);
-
- mTestFeatureConnection = new TestFeatureConnection(mContext, mPhoneId);
- mTestFeatureConnection.mExecutor = mSimpleExecutor;
- mTestFeatureConnection.setBinder(mBinder);
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- /**
- * Test service is ready when binder is alive and IMS status is ready.
- */
- @Test
- @SmallTest
- public void testServiceIsReady() {
- when(mBinder.isBinderAlive()).thenReturn(true);
- mTestFeatureConnection.setFeatureState(ImsFeature.STATE_READY);
-
- try {
- mTestFeatureConnection.checkServiceIsReady();
- } catch (RemoteException e) {
- throw new AssertionFailedError("Exception in testServiceIsReady: " + e);
- }
- }
-
- /**
- * Test service is not ready when binder is not alive or status is not ready.
- */
- @Test
- @SmallTest
- public void testServiceIsNotReady() {
- // Binder is not alive
- when(mBinder.isBinderAlive()).thenReturn(false);
-
- try {
- mTestFeatureConnection.checkServiceIsReady();
- throw new AssertionFailedError("testServiceIsNotReady: binder isn't alive");
- } catch (RemoteException e) {
- // expected result
- }
-
- // IMS feature status is unavailable
- when(mBinder.isBinderAlive()).thenReturn(true);
- mTestFeatureConnection.setFeatureState(ImsFeature.STATE_UNAVAILABLE);
-
- try {
- mTestFeatureConnection.checkServiceIsReady();
- throw new AssertionFailedError("testServiceIsNotReady: status unavailable");
- } catch (RemoteException e) {
- // expected result
- }
- }
-
- /**
- * Test registration tech callbacks.
- */
- @Test
- @SmallTest
- public void testRegistrationTech() throws Exception {
- when(mRegistrationBinder.getRegistrationTechnology()).thenReturn(
- ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
-
- assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
- mTestFeatureConnection.getRegistrationTech());
-
- }
-
- /**
- * Test callback is called when IMS feature created/removed/changed.
- */
- @Test
- @SmallTest
- public void testListenerCallback() {
- IImsServiceFeatureCallback featureCallback = mTestFeatureConnection.getListener();
-
- try {
- featureCallback.imsFeatureCreated(anyInt(), anyInt());
- assertTrue(mTestFeatureConnection.isFeatureCreatedCalled);
- } catch (RemoteException e) {
- throw new AssertionFailedError("testListenerCallback(Created): " + e);
- }
-
- try {
- featureCallback.imsFeatureRemoved(anyInt(), anyInt());
- assertTrue(mTestFeatureConnection.isFeatureRemovedCalled);
- } catch (RemoteException e) {
- throw new AssertionFailedError("testListenerCallback(Removed): " + e);
- }
-
- try {
- featureCallback.imsStatusChanged(anyInt(), anyInt(), ImsFeature.STATE_READY);
- assertEquals(mTestFeatureConnection.mNewStatus, ImsFeature.STATE_READY);
- } catch (RemoteException e) {
- throw new AssertionFailedError("testListenerCallback(Changed): " + e);
- }
- }
-
- private IImsRegistration getTestRegistrationBinder() {
- return mRegistrationBinder;
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectorTest.java
deleted file mode 100644
index a2b4de84bb..0000000000
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/FeatureConnectorTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.ims;
-
-import junit.framework.AssertionFailedError;
-
-import static org.mockito.ArgumentMatchers.anyObject;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.PackageManager;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.telephony.ims.feature.ImsFeature;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.ims.FeatureConnector;
-import com.android.ims.ImsException;
-import com.android.ims.ImsManager;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.util.concurrent.Executor;
-
-public class FeatureConnectorTest extends TelephonyTest {
-
- private Executor mExecutor = new Executor() {
- @Override
- public void execute(Runnable r) {
- r.run();
- }
- };
-
- private HandlerThread mHandlerThread;
- private FeatureConnector<ImsManager> mFeatureConnector;
- @Mock
- ImsManager mImsManager;
- @Mock
- FeatureConnector.Listener<ImsManager> mListener;
- @Mock
- FeatureConnector.RetryTimeout mRetryTimeout;
-
- @Before
- public void setUp() throws Exception {
- super.setUp("FeatureConnectorTest");
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
- int phoneId = mPhone.getPhoneId();
- mHandlerThread = new HandlerThread("ConnectorHandlerThread");
- mHandlerThread.start();
-
- mFeatureConnector = new FeatureConnector<>(mContext, phoneId,
- mListener, mExecutor, mHandlerThread.getLooper());
- mFeatureConnector.mListener = mListener;
- }
-
- @After
- public void tearDown() throws Exception {
- mHandlerThread.quit();
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testConnect() {
- // ImsManager is supported on device
- setImsSupportedFeature(true);
- when(mListener.getFeatureManager()).thenReturn(mImsManager);
-
- mFeatureConnector.connect();
- waitForHandlerAction(mFeatureConnector, 1000);
-
- // Verify that mListener will retrieve feature manager
- verify(mListener).getFeatureManager();
-
- reset(mListener);
-
- // ImsManager is NOT supported on device
- setImsSupportedFeature(false);
- when(mListener.getFeatureManager()).thenReturn(mImsManager);
-
- mFeatureConnector.connect();
- waitForHandlerAction(mFeatureConnector, 1000);
-
- // Verify that mListener won't retrieve feature manager
- verify(mListener, never()).getFeatureManager();
- }
-
- @Test
- @SmallTest
- public void testDisconnect() {
- // Verify mListener will call connectionUnavailable if disconnect() is called.
- mFeatureConnector.disconnect();
- verify(mListener).connectionUnavailable();
- }
-
- @Test
- @SmallTest
- public void testNotifyStateChanged() {
- try {
- mFeatureConnector.mManager = mImsManager;
- when(mImsManager.getImsServiceState()).thenReturn(ImsFeature.STATE_READY);
- // Trigger status changed
- mFeatureConnector.mNotifyStatusChangedCallback.notifyStateChanged();
- // Verify NotifyReady is called
- verify(mListener).connectionReady(anyObject());
- } catch (ImsException e) {
- throw new AssertionFailedError("Exception in testNotifyStateChanged: " + e);
- }
- }
-
- @Test
- @SmallTest
- public void testRetryGetImsService() {
- mFeatureConnector.mManager = mImsManager;
- mFeatureConnector.mRetryTimeout = mRetryTimeout;
-
- when(mRetryTimeout.get()).thenReturn(1);
- when(mListener.getFeatureManager()).thenReturn(mImsManager);
-
- mFeatureConnector.retryGetImsService();
- waitForHandlerAction(mFeatureConnector, 2000);
-
- // Verify removeNotifyStatusChangedCallback will be called if ImsManager is not null.
- verify(mImsManager).removeNotifyStatusChangedCallback(anyObject());
- }
-
- private void setImsSupportedFeature(boolean isSupported) {
- doReturn(isSupported).when(mPackageManager).hasSystemFeature(
- PackageManager.FEATURE_TELEPHONY_IMS);
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
index 751db91af0..48148ba624 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsCallProfileTest.java
@@ -22,6 +22,7 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
+import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.DisconnectCause;
@@ -74,6 +75,71 @@ public class ImsCallProfileTest {
}
}
+ @Test
+ @SmallTest
+ public void testCallComposerExtras() {
+ ImsCallProfile data = new ImsCallProfile();
+
+ // EXTRA_PRIORITY
+ data.setCallExtraInt(ImsCallProfile.EXTRA_PRIORITY,
+ ImsCallProfile.PRIORITY_URGENT);
+ assertEquals(ImsCallProfile.PRIORITY_URGENT,
+ data.getCallExtraInt(ImsCallProfile.EXTRA_PRIORITY));
+ data.setCallExtraInt(ImsCallProfile.EXTRA_PRIORITY,
+ ImsCallProfile.PRIORITY_NORMAL);
+ assertEquals(ImsCallProfile.PRIORITY_NORMAL,
+ data.getCallExtraInt(ImsCallProfile.EXTRA_PRIORITY));
+
+ // EXTRA_CALL_SUBJECT
+ String testCallSubject = "TEST_CALL_SUBJECT";
+ data.setCallExtra(ImsCallProfile.EXTRA_CALL_SUBJECT, testCallSubject);
+ assertEquals(testCallSubject, data.getCallExtra(ImsCallProfile.EXTRA_CALL_SUBJECT));
+
+ // EXTRA_CALL_LOCATION
+ Location testLocation = new Location("ImsCallProfileTest");
+ double latitude = 123;
+ double longitude = 456;
+ testLocation.setLatitude(latitude);
+ testLocation.setLongitude(longitude);
+ data.setCallExtraParcelable(ImsCallProfile.EXTRA_LOCATION, testLocation);
+ Location testGetLocation = (Location) data.getCallExtraParcelable(
+ ImsCallProfile.EXTRA_LOCATION);
+ assertEquals(latitude, testGetLocation.getLatitude(), 0);
+ assertEquals(longitude, testGetLocation.getLongitude(), 0);
+
+ // EXTRA_PICTURE_URL
+ String testPictureUrl = "TEST_PICTURE_URL";
+ data.setCallExtra(ImsCallProfile.EXTRA_PICTURE_URL, testPictureUrl);
+ assertEquals(testPictureUrl, data.getCallExtra(ImsCallProfile.EXTRA_PICTURE_URL));
+
+ // Test the whole Parcel ImsCallProfile
+ Parcel dataParceled = Parcel.obtain();
+ data.writeToParcel(dataParceled, 0);
+ dataParceled.setDataPosition(0);
+ ImsCallProfile unparceledData = ImsCallProfile.CREATOR.createFromParcel(dataParceled);
+ dataParceled.recycle();
+
+ assertEquals("unparceled data for EXTRA_PRIORITY is not valid!",
+ data.getCallExtraInt(ImsCallProfile.EXTRA_PRIORITY),
+ unparceledData.getCallExtraInt(ImsCallProfile.EXTRA_PRIORITY));
+
+ assertEquals("unparceled data for EXTRA_CALL_SUBJECT is not valid!",
+ data.getCallExtra(ImsCallProfile.EXTRA_CALL_SUBJECT),
+ unparceledData.getCallExtra(ImsCallProfile.EXTRA_CALL_SUBJECT));
+
+ Location locationFromData = data.getCallExtraParcelable(ImsCallProfile.EXTRA_LOCATION);
+ Location locationFromUnparceledData = unparceledData.getCallExtraParcelable(
+ ImsCallProfile.EXTRA_LOCATION);
+ assertEquals("unparceled data for EXTRA_LOCATION latitude is not valid!",
+ locationFromData.getLatitude(), locationFromUnparceledData.getLatitude(), 0);
+ assertEquals("unparceled data for EXTRA_LOCATION Longitude is not valid!",
+ locationFromData.getLongitude(), locationFromUnparceledData.getLongitude(), 0);
+
+ assertEquals("unparceled data for EXTRA_PICTURE_URL is not valid!",
+ data.getCallExtra(ImsCallProfile.EXTRA_PICTURE_URL),
+ unparceledData.getCallExtra(ImsCallProfile.EXTRA_PICTURE_URL));
+ }
+
/**
* Ensures that the {@link ImsCallProfile} will discard invalid extras when it is parceled.
*/
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
deleted file mode 100644
index 7088024e2b..0000000000
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
+++ /dev/null
@@ -1,899 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.ims;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.PackageManager;
-import android.os.IBinder;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ProvisioningManager;
-import android.telephony.ims.stub.ImsConfigImplBase;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.ims.ImsConfig;
-import com.android.ims.ImsManager;
-import com.android.ims.MmTelFeatureConnection;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-import java.util.Hashtable;
-
-@RunWith(AndroidJUnit4.class)
-public class ImsManagerTest extends TelephonyTest {
- private static final String UNSET_PROVISIONED_STRING = "unset";
- private static final boolean ENHANCED_4G_MODE_DEFAULT_VAL = true;
- private static final boolean ENHANCED_4G_MODE_EDITABLE = true;
- private static final boolean WFC_IMS_ENABLE_DEFAULT_VAL = false;
- private static final boolean WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL = true;
- private static final boolean VT_IMS_ENABLE_DEFAULT_VAL = true;
- private static final boolean WFC_IMS_EDITABLE_VAL = true;
- private static final boolean WFC_IMS_NOT_EDITABLE_VAL = false;
- private static final boolean WFC_IMS_ROAMING_EDITABLE_VAL = true;
- private static final boolean WFC_IMS_ROAMING_NOT_EDITABLE_VAL = false;
- private static final int WFC_IMS_MODE_DEFAULT_VAL =
- ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED;
- private static final int WFC_IMS_ROAMING_MODE_DEFAULT_VAL =
- ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED;
- private static final boolean WFC_USE_HOME_MODE_FOR_ROAMING_VAL = true;
- private static final boolean WFC_NOT_USE_HOME_MODE_FOR_ROAMING_VAL = false;
-
- PersistableBundle mBundle;
- @Mock IBinder mBinder;
- @Mock ImsConfigImplBase mImsConfigImplBaseMock;
- Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
- Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
- ImsConfigImplBase.ImsConfigStub mImsConfigStub;
- @Mock MmTelFeatureConnection mMmTelFeatureConnection;
-
- private final int[] mSubId = {0};
- private int mPhoneId;
-
- @Before
- public void setUp() throws Exception {
- super.setUp("ImsManagerTest");
- mPhoneId = mPhone.getPhoneId();
- mBundle = mContextFixture.getCarrierConfigBundle();
- // Force MmTelFeatureConnection to create an executor using Looper.myLooper().
- doReturn(null).when(mContext).getMainLooper();
-
- doReturn(mSubId).when(mSubscriptionController).getSubId(mPhoneId);
-
- doReturn(mSubscriptionController).when(mBinder).queryLocalInterface(anyString());
- mServiceManagerMockedServices.put("isub", mBinder);
- // Stick to the CarrierConfig defaults unless explicitly overwritten.
- doReturn("-1").when(mSubscriptionController).getSubscriptionProperty(anyInt(), anyString(),
- anyString(), nullable(String.class));
-
-
- doReturn(true).when(mMmTelFeatureConnection).isBinderAlive();
- mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS);
-
- mImsManagerInstances.remove(mPhoneId);
-
- setDefaultValues();
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- private void setDefaultValues() {
- mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
- ENHANCED_4G_MODE_EDITABLE);
- mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
- WFC_IMS_EDITABLE_VAL);
- mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL,
- WFC_IMS_ROAMING_EDITABLE_VAL);
- mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL,
- WFC_IMS_ENABLE_DEFAULT_VAL);
- mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL,
- WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL);
- mBundle.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT,
- WFC_IMS_MODE_DEFAULT_VAL);
- mBundle.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT,
- WFC_IMS_ROAMING_MODE_DEFAULT_VAL);
- mBundle.putBoolean(CarrierConfigManager.KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL,
- ENHANCED_4G_MODE_DEFAULT_VAL);
- mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, true);
- mBundle.putBoolean(
- CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL,
- WFC_NOT_USE_HOME_MODE_FOR_ROAMING_VAL);
- mBundle.putBoolean(CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, true);
-
- }
-
- @Test @SmallTest
- public void testGetDefaultValues() {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- assertEquals(WFC_IMS_ENABLE_DEFAULT_VAL, imsManager.isWfcEnabledByUser());
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ENABLED),
- anyString(),
- nullable(String.class));
-
- assertEquals(WFC_IMS_ROAMING_ENABLE_DEFAULT_VAL, imsManager.isWfcRoamingEnabledByUser());
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_ENABLED),
- anyString(),
- nullable(String.class));
-
- assertEquals(ENHANCED_4G_MODE_DEFAULT_VAL,
- imsManager.isEnhanced4gLteModeSettingEnabledByUser());
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.ENHANCED_4G_MODE_ENABLED),
- anyString(),
- nullable(String.class));
-
- assertEquals(WFC_IMS_MODE_DEFAULT_VAL, imsManager.getWfcMode(false));
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_MODE),
- anyString(),
- nullable(String.class));
-
- assertEquals(WFC_IMS_ROAMING_MODE_DEFAULT_VAL, imsManager.getWfcMode(true));
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- anyString(),
- nullable(String.class));
-
- assertEquals(VT_IMS_ENABLE_DEFAULT_VAL, imsManager.isVtEnabledByUser());
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.VT_IMS_ENABLED),
- anyString(),
- nullable(String.class));
- }
-
- @Test @SmallTest
- public void testSetValues() {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- imsManager.setWfcMode(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED);
- verify(mSubscriptionController, times(1)).setSubscriptionProperty(
- eq(mSubId[0]),
- eq(SubscriptionManager.WFC_IMS_MODE),
- eq("1"));
-
- imsManager.setWfcMode(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED, true);
- verify(mSubscriptionController, times(1)).setSubscriptionProperty(
- eq(mSubId[0]),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- eq("1"));
-
- imsManager.setVtSetting(false);
- verify(mSubscriptionController, times(1)).setSubscriptionProperty(
- eq(mSubId[0]),
- eq(SubscriptionManager.VT_IMS_ENABLED),
- eq("0"));
-
- // enhanced 4g mode must be editable to use setEnhanced4gLteModeSetting
- mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL,
- ENHANCED_4G_MODE_EDITABLE);
- imsManager.setEnhanced4gLteModeSetting(true);
- verify(mSubscriptionController, times(1)).setSubscriptionProperty(
- eq(mSubId[0]),
- eq(SubscriptionManager.ENHANCED_4G_MODE_ENABLED),
- eq("1"));
-
- imsManager.setWfcSetting(true);
- verify(mSubscriptionController, times(1)).setSubscriptionProperty(
- eq(mSubId[0]),
- eq(SubscriptionManager.WFC_IMS_ENABLED),
- eq("1"));
- }
- @Test
- public void testGetProvisionedValues() throws Exception {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- assertEquals(true, imsManager.isWfcProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
-
- assertEquals(true, imsManager.isVtProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
-
- assertEquals(true, imsManager.isVolteProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
-
- // If we call get again, times should still be one because the value should be fetched
- // from cache.
- assertEquals(true, imsManager.isWfcProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
-
- assertEquals(true, imsManager.isVtProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.LVC_SETTING_ENABLED));
-
- assertEquals(true, imsManager.isVolteProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.VLT_SETTING_ENABLED));
-
- assertEquals(true, imsManager.isEabProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.EAB_SETTING_ENABLED));
- }
-
- @Test
- public void testSetProvisionedValues() throws Exception {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- assertEquals(true, imsManager.isWfcProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
-
- imsManager.getConfigInterface().setProvisionedValue(
- ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED,
- ImsConfig.FeatureValueConstants.OFF);
-
- assertEquals(0, (int) mProvisionedIntVals.get(
- ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
-
- assertEquals(false, imsManager.isWfcProvisionedOnDevice());
-
- verify(mImsConfigImplBaseMock, times(1)).setConfig(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED),
- eq(0));
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED));
- }
-
- @Test
- public void testEabSetProvisionedValues() throws Exception {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- assertEquals(true, imsManager.isEabProvisionedOnDevice());
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.EAB_SETTING_ENABLED));
-
- imsManager.getConfigInterface().setProvisionedValue(
- ImsConfig.ConfigConstants.EAB_SETTING_ENABLED,
- ImsConfig.FeatureValueConstants.OFF);
-
- assertEquals(0, (int) mProvisionedIntVals.get(
- ImsConfig.ConfigConstants.EAB_SETTING_ENABLED));
-
- assertEquals(false, imsManager.isEabProvisionedOnDevice());
-
- verify(mImsConfigImplBaseMock, times(1)).setConfig(
- eq(ImsConfig.ConfigConstants.EAB_SETTING_ENABLED),
- eq(0));
- verify(mImsConfigImplBaseMock, times(1)).getConfigInt(
- eq(ImsConfig.ConfigConstants.EAB_SETTING_ENABLED));
- }
-
- /**
- * Tests that when WFC is enabled/disabled for home/roaming, that setting is sent to the
- * ImsService correctly.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcSetting_true_shouldSetWfcModeWrtRoamingState() throws Exception {
- // First, Set WFC home/roaming mode that is not the Carrier Config default.
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_MODE),
- anyString(),
- nullable(String.class));
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- anyString(),
- nullable(String.class));
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // Roaming
- doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
- // Turn on WFC
- imsManager.setWfcSetting(true);
- // Roaming mode (CELLULAR_PREFERRED) should be set.
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
- eq(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED));
- // WFC is enabled, so we should set user roaming setting
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- eq(ProvisioningManager.PROVISIONING_VALUE_ENABLED));
-
- // Not roaming
- doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
- // Turn on WFC
- imsManager.setWfcSetting(true);
- // Home mode (WIFI_PREFERRED) should be set.
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
- eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED));
- // WFC is enabled, so we should set user roaming setting
- verify(mImsConfigImplBaseMock, times(2)).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- eq(ProvisioningManager.PROVISIONING_VALUE_ENABLED));
-
-
- // Turn off WFC and ensure that roaming setting is disabled.
- doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
- imsManager.setWfcSetting(false);
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- eq(ProvisioningManager.PROVISIONING_VALUE_DISABLED));
- }
-
-
- /**
- * Tests that when user changed WFC setting while NOT roaming, the home WFC mode is sent to the
- * modem and the roaming enabled configuration is pushed.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcSetting_shouldSetWfcModeRoamingDisabledUserEnabled() throws Exception {
- // The user has previously enabled "WFC while roaming" setting in UI and then turned WFC
- // off.
- doReturn(String.valueOf(1 /*true*/))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_ENABLED),
- anyString(),
- nullable(String.class));
-
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // We are currently on the home network, not roaming.
- doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
-
- // User enables WFC from UI
- imsManager.setWfcSetting(true /*enabled*/);
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- eq(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED));
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- // Should be enabled because the user enabled the "WFC while roaming" setting
- // independent of whether or not we are roaming.
- eq(ProvisioningManager.PROVISIONING_VALUE_ENABLED));
- }
-
- /**
- * Tests that when user changed WFC setting while roaming, that the correct user setting
- * is sent to the ImsService when changing the roaming mode.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcSetting_shouldSetWfcModeRoamingEnabledUserEnabled() throws Exception {
- // The user has previously enabled "WFC while roaming" setting in UI and then turned WFC
- // off.
- doReturn(String.valueOf(1 /*true*/))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_ENABLED),
- anyString(),
- nullable(String.class));
-
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // The device is currently roaming
- doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
-
- // The user has enabled WFC in the UI while the device is roaming.
- imsManager.setWfcSetting(true /*enabled*/);
-
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- // Default for roaming is WFC_IMS_ROAMING_MODE_DEFAULT_VAL
- eq(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED));
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- // Should be enabled because user enabled the setting in the UI previously.
- eq(ProvisioningManager.PROVISIONING_VALUE_ENABLED));
- }
-
- /**
- * Tests that when a WFC mode is updated for home, that setting is sent to the
- * ImsService correctly or ignored if the roaming mode is changed.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcMode_shouldSetWfcModeRoamingDisabled() throws Exception {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // the device is not currently roaming
- doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
-
- // set the WFC roaming mode while the device is not roaming, so any changes to roaming mode
- // should be ignored
- imsManager.setWfcMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, true /*IsRoaming*/);
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- anyInt());
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- anyInt());
-
- // set home WFC mode setting while not roaming, the configuration should be set correctly.
- imsManager.setWfcMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, false /*IsRoaming*/);
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- eq(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED));
- // WiFi Roaming enabled setting is not related to WFC mode
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- anyInt());
- }
-
- /**
- * Tests that when a WFC mode is updated for roaming while WFC is enabled, that setting is sent
- * to the ImsService correctly when changing the roaming mode or ignored if the home setting is
- * changed.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcMode_wfcEnabledShouldSetWfcModeRoamingEnabled() throws Exception {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // The user has previously enabled WFC in the settings UI.
- doReturn(String.valueOf(1 /*true*/))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ENABLED),
- anyString(),
- nullable(String.class));
-
- // The device is roaming
- doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
-
- // The carrier app has changed the WFC mode for roaming while the device is home. The
- // result of this operation is that the neither the WFC mode or the roaming enabled
- // configuration should change.
- imsManager.setWfcMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, false /*IsRoaming*/);
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- anyInt());
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- anyInt());
-
- // The carrier app has set the WFC mode for roaming while the device is roaming. The
- // WFC mode should be updated to reflect the roaming setting and the roaming enabled
- // configuration should be changed to enabled.
- imsManager.setWfcMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, true /*IsRoaming*/);
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- eq(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED));
- // WiFi Roaming enabled setting is not related to WFC mode
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- anyInt());
- }
-
- /**
- * Tests that when a WFC mode is updated for roaming while WFC is disabled, the WFC roaming
- * setting is always set to disabled.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcMode_WfcDisabledShouldNotSetWfcModeRoamingEnabled() throws Exception {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // The user has previously disabled WFC in the settings UI.
- doReturn(String.valueOf(0 /*false*/))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ENABLED),
- anyString(),
- nullable(String.class));
-
- // The device is roaming
- doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
-
- // WFC is disabled and the carrier app has set the WFC mode for roaming while the device is
- // roaming. The WFC mode should be updated to reflect the roaming setting and the roaming
- // enabled configuration should be disabled because WFC is disabled.
- imsManager.setWfcMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, true /*IsRoaming*/);
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- eq(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED));
- // WiFi Roaming enabled setting is not related to WFC mode
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- anyInt());
- }
-
- /**
- * Tests that when user changed WFC mode while not roaming, the new mode is sent to the modem
- * and roaming enabled indication is sent to the ImsService correctly when changing the roaming
- * mode.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcMode_shouldSetWfcModeRoamingDisabledUserEnabled() throws Exception {
- // The user has enabled the WFC setting in the UI.
- doReturn(String.valueOf(1 /*true*/))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ENABLED),
- anyString(),
- nullable(String.class));
- // The user has enabled the "WFC while roaming" setting in the UI while WFC was enabled
- doReturn(String.valueOf(1 /*true*/))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_ENABLED),
- anyString(),
- nullable(String.class));
-
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // The device is currently on the home network
- doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
-
- // The user has changed the WFC mode in the UI for the non-roaming configuration
- imsManager.setWfcMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, false /*IsRoaming*/);
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- // ensure that the correct cellular preferred config change is sent
- eq(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED));
- // WiFi Roaming enabled setting is not related to WFC mode
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- anyInt());
- }
-
- /**
- * Tests that when user changed WFC mode while roaming, that setting is sent to the
- * ImsService correctly when changing the roaming mode.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL = true
- */
- @Test @SmallTest
- public void testSetWfcMode_shouldSetWfcModeRoamingEnabledUserDisabled() throws Exception {
- // The user disabled "WFC while roaming" setting in the UI
- doReturn(String.valueOf(0 /*false*/))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_ENABLED),
- anyString(),
- nullable(String.class));
-
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // the device is currently roaming
- doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
-
- // The carrier app has changed the WFC mode while roaming, so we must set the WFC mode
- // to the new configuration.
- imsManager.setWfcMode(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED, true /*IsRoaming*/);
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE),
- eq(ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED));
- // WiFi Roaming enabled setting is not related to WFC mode
- verify(mImsConfigImplBaseMock, never()).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- anyInt());
- }
-
- /**
- * Tests that the settings for WFC mode are ignored if the Carrier sets the settings to not
- * editable.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = false
- */
- @Test @SmallTest
- public void testSetWfcSetting_wfcNotEditable() throws Exception {
- mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
- WFC_IMS_NOT_EDITABLE_VAL);
- // Set some values that are different than the defaults for WFC mode.
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_MODE),
- anyString(),
- nullable(String.class));
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- anyString(),
- nullable(String.class));
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // Roaming
- doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
- // Turn on WFC
- imsManager.setWfcSetting(true);
- // User defined setting for Roaming mode (WIFI_ONLY) should be set independent of whether or
- // not WFC mode is editable. With 1000 ms timeout.
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
- eq(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
-
- // Not roaming
- doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
- // Turn on WFC
- imsManager.setWfcSetting(true);
- // Default Home mode (CELLULAR_PREFERRED) should be set. With 1000 ms timeout.
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
- eq(WFC_IMS_MODE_DEFAULT_VAL));
- }
-
- /**
- * Tests that the CarrierConfig defaults will be used if no setting is set in the Subscription
- * Manager.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL = true
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = Carrier preferred
- * - CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = WiFi preferred
- */
- @Test @SmallTest
- public void testSetWfcSetting_noUserSettingSet() throws Exception {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // Roaming
- doReturn(true).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
- // Turn on WFC
- imsManager.setWfcSetting(true);
-
- // Default Roaming mode (WIFI_PREFERRED) for carrier should be set. With 1000 ms timeout.
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
- eq(WFC_IMS_ROAMING_MODE_DEFAULT_VAL));
-
- // Not roaming
- doReturn(false).when(mTelephonyManager).isNetworkRoaming(eq(mSubId[0]));
- // Turn on WFC
- imsManager.setWfcSetting(true);
-
- // Default Home mode (CELLULAR_PREFERRED) for carrier should be set. With 1000 ms timeout.
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ImsConfig.ConfigConstants.VOICE_OVER_WIFI_MODE),
- eq(WFC_IMS_MODE_DEFAULT_VAL));
- }
-
- /**
- * Tests the operation of getWfcMode when the configuration to use the home network mode when
- * roaming for WFC is false. First, it checks that the user setting for WFC_IMS_ROAMING_MODE is
- * returned when WFC roaming is set to editable. Then, it switches the WFC roaming mode to not
- * editable and ensures that the default WFC roaming mode is returned.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = false
- */
- @Test @SmallTest
- public void getWfcMode_useWfcHomeModeConfigFalse_shouldUseWfcRoamingMode() {
- // Set some values that are different than the defaults for WFC mode.
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_MODE),
- anyString(),
- nullable(String.class));
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- anyString(),
- nullable(String.class));
-
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // Check that use the WFC roaming network mode.
- assertEquals(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED,
- imsManager.getWfcMode(true));
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- anyString(),
- nullable(String.class));
-
- // Set WFC roaming network mode to not editable.
- mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL,
- WFC_IMS_ROAMING_NOT_EDITABLE_VAL);
-
- // Check that use the default WFC roaming network mode.
- assertEquals(WFC_IMS_ROAMING_MODE_DEFAULT_VAL, imsManager.getWfcMode(true));
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- anyString(),
- nullable(String.class));
- }
-
- /**
- * Tests the operation of getWfcMode when the configuration to use the home network mode when
- * roaming for WFC is true independent of whether or not the WFC roaming mode is editable.
- *
- * Preconditions:
- * - CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = true
- */
- @Test @SmallTest
- public void getWfcMode_useWfcHomeModeConfigTrue_shouldUseWfcHomeMode() {
- // Set some values that are different than the defaults for WFC mode.
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_MODE),
- anyString(),
- nullable(String.class));
- doReturn(String.valueOf(ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED))
- .when(mSubscriptionController).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_MODE),
- anyString(),
- nullable(String.class));
-
- // Set to use WFC home network mode in roaming network.
- mBundle.putBoolean(
- CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL,
- WFC_USE_HOME_MODE_FOR_ROAMING_VAL);
-
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- // Check that use the WFC home network mode.
- assertEquals(ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY, imsManager.getWfcMode(true));
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_MODE),
- anyString(),
- nullable(String.class));
-
- // Set WFC home network mode to not editable.
- mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL,
- WFC_IMS_NOT_EDITABLE_VAL);
-
- // Check that use the default WFC home network mode.
- assertEquals(WFC_IMS_MODE_DEFAULT_VAL, imsManager.getWfcMode(true));
- verify(mSubscriptionController, times(1)).getSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_MODE),
- anyString(),
- nullable(String.class));
- }
-
- /**
- * Tests the operation of setWfcRoamingSetting and ensures that the user setting for WFC roaming
- * and the ImsConfig setting are both called properly.
- */
- @Test @SmallTest
- public void setWfcRoamingSettingTest() {
- ImsManager imsManager = getImsManagerAndInitProvisionedValues();
-
- imsManager.setWfcRoamingSetting(true);
- verify(mSubscriptionController, times(1)).setSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_ENABLED),
- eq("1"));
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- eq(ProvisioningManager.PROVISIONING_VALUE_ENABLED));
-
- imsManager.setWfcRoamingSetting(false);
- verify(mSubscriptionController, times(1)).setSubscriptionProperty(
- anyInt(),
- eq(SubscriptionManager.WFC_IMS_ROAMING_ENABLED),
- eq("0"));
- verify(mImsConfigImplBaseMock).setConfig(
- eq(ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE),
- eq(ProvisioningManager.PROVISIONING_VALUE_DISABLED));
-
- }
-
- private ImsManager getImsManagerAndInitProvisionedValues() {
- when(mImsConfigImplBaseMock.getConfigInt(anyInt()))
- .thenAnswer(invocation -> {
- return getProvisionedInt((Integer) (invocation.getArguments()[0]));
- });
-
- when(mImsConfigImplBaseMock.setConfig(anyInt(), anyInt()))
- .thenAnswer(invocation -> {
- mProvisionedIntVals.put((Integer) (invocation.getArguments()[0]),
- (Integer) (invocation.getArguments()[1]));
- return ImsConfig.OperationStatusConstants.SUCCESS;
- });
-
-
- // Configure ImsConfigStub
- mImsConfigStub = new ImsConfigImplBase.ImsConfigStub(mImsConfigImplBaseMock);
- doReturn(mImsConfigStub).when(mMmTelFeatureConnection).getConfigInterface();
-
- // Configure ImsManager
- ImsManager imsManager = ImsManager.getInstance(mContext, mPhoneId);
- try {
- replaceInstance(ImsManager.class, "mMmTelFeatureConnection", imsManager,
- mMmTelFeatureConnection);
- } catch (Exception ex) {
- fail("failed with " + ex);
- }
-
- ((ImsManager.ImsExecutorFactory) imsManager.mExecutorFactory).destroy();
- imsManager.mExecutorFactory = Runnable::run;
-
- return imsManager;
- }
-
- // If the value is ever set, return the set value. If not, return a constant value 1000.
- private int getProvisionedInt(int item) {
- if (mProvisionedIntVals.containsKey(item)) {
- return mProvisionedIntVals.get(item);
- } else {
- return ImsConfig.FeatureValueConstants.ON;
- }
- }
-
- // If the value is ever set, return the set value. If not, return a constant value "unset".
- private String getProvisionedString(int item) {
- if (mProvisionedStringVals.containsKey(item)) {
- return mProvisionedStringVals.get(item);
- } else {
- return UNSET_PROVISIONED_STRING;
- }
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index 1770764dfd..d5c88e3efc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -58,6 +58,7 @@ import android.testing.TestableLooper;
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.ims.ImsFeatureBinderRepository;
import com.android.internal.telephony.PhoneConfigurationManager;
import org.junit.After;
@@ -110,6 +111,8 @@ public class ImsResolverTest extends ImsTestBase {
ImsResolver.ImsDynamicQueryManagerFactory mMockQueryManagerFactory;
@Mock
ImsServiceFeatureQueryManager mMockQueryManager;
+ @Mock
+ ImsFeatureBinderRepository mMockRepo;
private ImsResolver mTestImsResolver;
private BroadcastReceiver mTestPackageBroadcastReceiver;
private BroadcastReceiver mTestCarrierConfigReceiver;
@@ -129,8 +132,6 @@ public class ImsResolverTest extends ImsTestBase {
public void tearDown() throws Exception {
mTestImsResolver.destroy();
mTestImsResolver = null;
- mLooper.destroy();
- mLooper = null;
super.tearDown();
}
@@ -191,6 +192,215 @@ public class ImsResolverTest extends ImsTestBase {
}
/**
+ * Add a device ImsService and ensure that querying ImsResolver to see if an ImsService is
+ * configured succeeds.
+ */
+ @Test
+ @SmallTest
+ public void testIsDeviceImsServiceConfigured() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ setupPackageQuery(TEST_DEVICE_DEFAULT_NAME, features, true);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ // device package name should be returned for both features.
+ final Boolean[] isConfigured = new Boolean[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ isConfigured[0] = mTestImsResolver.isImsServiceConfiguredForFeature(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertTrue(isConfigured[0]);
+ mLooper.runWithLooper(() ->
+ isConfigured[0] = mTestImsResolver.isImsServiceConfiguredForFeature(0,
+ ImsFeature.FEATURE_RCS));
+ assertTrue(isConfigured[0]);
+ }
+
+ /**
+ * Add a device ImsService and ensure that querying the configured ImsService for all features
+ * reports the device ImsService.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceDevice() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ setupPackageQuery(TEST_DEVICE_DEFAULT_NAME, features, true);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ // device package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ }
+
+ /**
+ * In the case that there is no device or carrier ImsService found, we return null for
+ * configuration queries.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceNoDeviceOrCarrier() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ // package query returns null
+ setupController();
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ // device package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertNull(packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertNull(packageName[0]);
+ }
+
+ /**
+ * In the case that there is no device or carrier ImsService configured, we return null for
+ * configuration queries.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceNoDeviceConfig() throws Exception {
+ // device configuration for MMTEL and RCS is null
+ setupResolver(1 /*numSlots*/, null, null);
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ // ImsService query does report a device ImsService
+ setupPackageQuery(TEST_DEVICE_DEFAULT_NAME, features, true);
+ setupController();
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ // device package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertNull(packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertNull(packageName[0]);
+ }
+
+ /**
+ * Add a device and carrier ImsService and ensure that querying the configured ImsService for
+ * all features reports the carrier ImsService and not device.
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceCarrier() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ List<ResolveInfo> info = new ArrayList<>();
+ info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ setupPackageQuery(info);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+ // Setup the carrier features and response.
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_MMTEL));
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+ ImsFeature.FEATURE_RCS));
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
+
+ // carrier package name should be returned for both features.
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertEquals(TEST_CARRIER_DEFAULT_NAME.getPackageName(), packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertEquals(TEST_CARRIER_DEFAULT_NAME.getPackageName(), packageName[0]);
+ }
+
+ /**
+ * Add a device ImsService and ensure that querying the configured ImsService for all features
+ * reports the device ImsService though there is a configuration for carrier (but no cached
+ * ImsService).
+ */
+ @Test
+ @SmallTest
+ public void testGetConfiguredImsServiceCarrierDevice() throws Exception {
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ HashSet<String> features = new HashSet<>();
+ features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ features.add(ImsResolver.METADATA_RCS_FEATURE);
+ // Carrier service is configured
+ setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ List<ResolveInfo> info = new ArrayList<>();
+ // Carrier ImsService is not found during package query.
+ info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+ setupPackageQuery(info);
+ setupController();
+
+ // Complete package manager lookup and cache.
+ startBindCarrierConfigAlreadySet();
+
+ final String[] packageName = new String[1];
+ // Calling this method will block us until the looper processes the command, so use
+ // runWithLooper to allow the message to be processed.
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_MMTEL));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ mLooper.runWithLooper(() ->
+ packageName[0] = mTestImsResolver.getConfiguredImsServicePackageName(0,
+ ImsFeature.FEATURE_RCS));
+ assertEquals(TEST_DEVICE_DEFAULT_NAME.getPackageName(), packageName[0]);
+ }
+
+ /**
* Set the carrier config override value and ensure that ImsResolver calls .bind on that
* package name with the correct ImsFeatures.
*/
@@ -434,7 +644,7 @@ public class ImsResolverTest extends ImsTestBase {
setConfigCarrierStringMmTelRcs(0, null);
startBindCarrierConfigAlreadySet();
- mLooper.processAllMessages();
+ processAllMessages();
verify(mMockQueryManager, never()).startQuery(any(), any());
verify(controller, never()).bind(any());
verify(controller, never()).unbind();
@@ -461,7 +671,7 @@ public class ImsResolverTest extends ImsTestBase {
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
@@ -494,7 +704,7 @@ public class ImsResolverTest extends ImsTestBase {
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
@@ -508,7 +718,7 @@ public class ImsResolverTest extends ImsTestBase {
// Change number of SIMs and verify the features in the ImsServiceController are changed
// as well
PhoneConfigurationManager.notifyMultiSimConfigChange(1);
- mLooper.processAllMessages();
+ processAllMessages();
featureSet = convertToHashSet(features, 0);
verify(controller).changeImsServiceFeatures(featureSet);
verify(controller, never()).unbind();
@@ -536,7 +746,7 @@ public class ImsResolverTest extends ImsTestBase {
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
@@ -595,7 +805,7 @@ public class ImsResolverTest extends ImsTestBase {
convertToFeatureSlotPairs(0, ImsResolver.METADATA_RCS_FEATURE);
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
// ensure that startQuery was called
verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE_DEFAULT_NAME),
any(String.class));
@@ -605,7 +815,7 @@ public class ImsResolverTest extends ImsTestBase {
mDynamicQueryListener.onComplete(TEST_DEVICE_DEFAULT_NAME, deviceFeatures1);
mDynamicQueryListener.onComplete(TEST_DEVICE2_DEFAULT_NAME, deviceFeatures2);
- mLooper.processAllMessages();
+ processAllMessages();
verify(deviceController, times(2)).bind(eq(deviceFeatures1));
verify(deviceController2, times(1)).bind(eq(deviceFeatures2));
@@ -723,7 +933,7 @@ public class ImsResolverTest extends ImsTestBase {
// Move to single SIM and verify the features in the ImsServiceController are changed as
// well.
PhoneConfigurationManager.notifyMultiSimConfigChange(1);
- mLooper.processAllMessages();
+ processAllMessages();
carrierFeatures = new HashSet<>();
carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
verify(carrierController).changeImsServiceFeatures(carrierFeatures);
@@ -754,7 +964,7 @@ public class ImsResolverTest extends ImsTestBase {
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
@@ -829,15 +1039,6 @@ public class ImsResolverTest extends ImsTestBase {
mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController);
// The carrier override contains this feature
mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, carrierController);
- // Get the IImsServiceControllers for each feature on each slot and verify they are correct.
- assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
- 1 /*Slot id*/, ImsFeature.FEATURE_MMTEL, null));
- assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
- 1 /*Slot id*/, ImsFeature.FEATURE_RCS, null));
- assertEquals(deviceController, mTestImsResolver.getImsServiceControllerAndListen(
- 0 /*Slot id*/, ImsFeature.FEATURE_MMTEL, null));
- assertEquals(carrierController, mTestImsResolver.getImsServiceControllerAndListen(
- 0 /*Slot id*/, ImsFeature.FEATURE_RCS, null));
}
/**
@@ -1380,7 +1581,7 @@ public class ImsResolverTest extends ImsTestBase {
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
// Boot complete has happened and the carrier ImsService is now available.
mTestBootCompleteReceiver.onReceive(null, new Intent(Intent.ACTION_BOOT_COMPLETED));
- mLooper.processAllMessages();
+ processAllMessages();
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
@@ -1477,7 +1678,7 @@ public class ImsResolverTest extends ImsTestBase {
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME);
- mLooper.processAllMessages();
+ processAllMessages();
verify(carrierController).unbind();
// Verify that the device ImsService features are changed to include the ones previously
// taken by the carrier app.
@@ -1508,7 +1709,7 @@ public class ImsResolverTest extends ImsTestBase {
setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
Set<String> featureResult = new HashSet<>();
featureResult.add(ImsResolver.METADATA_MMTEL_FEATURE);
@@ -1545,7 +1746,7 @@ public class ImsResolverTest extends ImsTestBase {
setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
Set<String> featureResult = new HashSet<>();
featureResult.add(ImsResolver.METADATA_RCS_FEATURE);
@@ -1586,7 +1787,7 @@ public class ImsResolverTest extends ImsTestBase {
setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet1 =
convertToHashSet(features1, 0);
@@ -1627,7 +1828,7 @@ public class ImsResolverTest extends ImsTestBase {
setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
startBindNoCarrierConfig(1);
- mLooper.processAllMessages();
+ processAllMessages();
verify(deviceController1, never()).bind(any());
verify(deviceController1, never()).unbind();
@@ -1662,25 +1863,20 @@ public class ImsResolverTest extends ImsTestBase {
}
mTestImsResolver = new ImsResolver(mMockContext, deviceMmTelPkgName, deviceRcsPkgName,
- numSlots);
+ numSlots, mMockRepo);
try {
mLooper = new TestableLooper(mTestImsResolver.getHandler().getLooper());
+ monitorTestableLooper(mLooper);
} catch (Exception e) {
fail("Unable to create looper from handler.");
}
- ArgumentCaptor<BroadcastReceiver> receiversCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
- mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
- mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
- mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
mTestImsResolver.setTelephonyManagerProxy(mTestTelephonyManagerProxy);
when(mMockQueryManagerFactory.create(any(Context.class),
any(ImsServiceFeatureQueryManager.Listener.class))).thenReturn(mMockQueryManager);
mTestImsResolver.setImsDynamicQueryManagerFactory(mMockQueryManagerFactory);
- mLooper.processAllMessages();
+ processAllMessages();
}
private void setupPackageQuery(List<ResolveInfo> infos) {
@@ -1711,7 +1907,8 @@ public class ImsResolverTest extends ImsTestBase {
@Override
public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository r) {
when(controller.getComponentName()).thenReturn(componentName);
return controller;
}
@@ -1725,13 +1922,19 @@ public class ImsResolverTest extends ImsTestBase {
*/
private void startBindCarrierConfigAlreadySet() {
mTestImsResolver.initialize();
+ ArgumentCaptor<BroadcastReceiver> receiversCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
+ mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+ mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
+ mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
mDynamicQueryListener = queryManagerCaptor.getValue();
when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
.thenReturn(true);
- mLooper.processAllMessages();
+ processAllMessages();
}
/**
@@ -1740,11 +1943,17 @@ public class ImsResolverTest extends ImsTestBase {
*/
private void startBindNoCarrierConfig(int numSlots) {
mTestImsResolver.initialize();
+ ArgumentCaptor<BroadcastReceiver> receiversCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
+ mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+ mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
+ mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
mDynamicQueryListener = queryManagerCaptor.getValue();
- mLooper.processAllMessages();
+ processAllMessages();
// For ease of testing, slotId = subId
for (int i = 0; i < numSlots; i++) {
sendCarrierConfigChanged(i, i);
@@ -1753,19 +1962,19 @@ public class ImsResolverTest extends ImsTestBase {
private void setupDynamicQueryFeatures(ComponentName name,
HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
- mLooper.processAllMessages();
+ processAllMessages();
// ensure that startQuery was called
verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
mDynamicQueryListener.onComplete(name, features);
- mLooper.processAllMessages();
+ processAllMessages();
}
private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) {
- mLooper.processAllMessages();
+ processAllMessages();
// ensure that startQuery was called
verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
mDynamicQueryListener.onPermanentError(name);
- mLooper.processAllMessages();
+ processAllMessages();
}
public void packageChanged(String packageName) {
@@ -1775,7 +1984,7 @@ public class ImsResolverTest extends ImsTestBase {
addPackageIntent.setData(new Uri.Builder().scheme("package").opaquePart(packageName)
.build());
mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
- mLooper.processAllMessages();
+ processAllMessages();
}
public void packageRemoved(String packageName) {
@@ -1784,7 +1993,7 @@ public class ImsResolverTest extends ImsTestBase {
removePackageIntent.setData(new Uri.Builder().scheme("package")
.opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
mTestPackageBroadcastReceiver.onReceive(null, removePackageIntent);
- mLooper.processAllMessages();
+ processAllMessages();
}
private void setImsServiceControllerFactory(Map<String, ImsServiceController> controllerMap) {
@@ -1797,7 +2006,8 @@ public class ImsResolverTest extends ImsTestBase {
@Override
public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository r) {
return controllerMap.get(componentName.getPackageName());
}
});
@@ -1814,7 +2024,8 @@ public class ImsResolverTest extends ImsTestBase {
@Override
public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository r) {
if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
componentName.getPackageName())) {
when(deviceController.getComponentName()).thenReturn(componentName);
@@ -1840,7 +2051,8 @@ public class ImsResolverTest extends ImsTestBase {
@Override
public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository r) {
if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
componentName.getPackageName())) {
when(deviceController.getComponentName()).thenReturn(componentName);
@@ -1871,7 +2083,8 @@ public class ImsResolverTest extends ImsTestBase {
@Override
public ImsServiceController create(Context context, ComponentName componentName,
- ImsServiceController.ImsServiceControllerCallbacks callbacks) {
+ ImsServiceController.ImsServiceControllerCallbacks callbacks,
+ ImsFeatureBinderRepository r) {
if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(
componentName.getPackageName())) {
when(deviceController1.getComponentName()).thenReturn(componentName);
@@ -1900,7 +2113,7 @@ public class ImsResolverTest extends ImsTestBase {
carrierConfigIntent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId);
carrierConfigIntent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, slotId);
mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
- mLooper.processAllMessages();
+ processAllMessages();
}
private void setConfigCarrierStringMmTelRcs(int subId, String packageName) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
index ff17216e3f..0d72364c6f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -20,6 +20,8 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -34,9 +36,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsServiceController;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.ImsFeatureConfiguration;
@@ -44,10 +51,14 @@ import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.ims.ImsFeatureBinderRepository;
+import com.android.ims.ImsFeatureContainer;
+import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -77,23 +88,64 @@ public class ImsServiceControllerTest extends ImsTestBase {
}
};
+ private static class TestCallback extends IImsServiceFeatureCallback.Stub {
+ public ImsFeatureContainer container;
+
+ @Override
+ public void imsFeatureCreated(ImsFeatureContainer c) {
+ container = c;
+ }
+
+ @Override
+ public void imsFeatureRemoved(int reason) {
+ container = null;
+ }
+
+ @Override
+ public void imsStatusChanged(int stat) {
+ container.setState(stat);
+ }
+
+ @Override
+ public void updateCapabilities(long caps) {
+ container.setCapabilities(caps);
+ }
+ }
+
+ @Mock IImsMmTelFeature mMockMmTelFeature;
+ @Mock IBinder mMockMmTelBinder;
+ @Mock IImsRcsFeature mMockRcsFeature;
+ @Mock IBinder mMockRcsBinder;
+ @Mock IImsConfig mMockImsConfig;
+ @Mock IImsRegistration mMockRcsRegistration;
+
@Mock IImsServiceController mMockServiceControllerBinder;
@Mock ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
- @Mock IImsServiceFeatureCallback mMockProxyCallbacks;
@Mock Context mMockContext;
private final ComponentName mTestComponentName = new ComponentName("TestPkg",
"ImsServiceControllerTest");
private final Handler mHandler = new Handler(Looper.getMainLooper());
private ImsServiceController mTestImsServiceController;
+ private ImsFeatureBinderRepository mRepo;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
+ mRepo = new ImsFeatureBinderRepository();
mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
- mMockCallbacks, mHandler, REBIND_RETRY);
- mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
+ mMockCallbacks, mHandler, REBIND_RETRY, mRepo);
when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+ when(mMockServiceControllerBinder.createMmTelFeature(anyInt()))
+ .thenReturn(mMockMmTelFeature);
+ when(mMockServiceControllerBinder.createRcsFeature(anyInt()))
+ .thenReturn(mMockRcsFeature);
+ when(mMockServiceControllerBinder.getConfig(anyInt()))
+ .thenReturn(mMockImsConfig);
+ when(mMockServiceControllerBinder.getRegistration(anyInt()))
+ .thenReturn(mMockRcsRegistration);
+ when(mMockMmTelFeature.asBinder()).thenReturn(mMockMmTelBinder);
+ when(mMockRcsFeature.asBinder()).thenReturn(mMockRcsBinder);
}
@@ -163,14 +215,73 @@ public class ImsServiceControllerTest extends ImsTestBase {
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
- verify(mMockServiceControllerBinder).createRcsFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
+ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_RCS), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ validateRcsFeatureContainerExists(SLOT_0);
+ }
+
+ /**
+ * Tests ImsServiceController keeps SIP delegate creation flags if MMTEL and RCS are supported.
+ */
+ @SmallTest
+ @Test
+ public void testBindServiceSipDelegateCapability() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_MMTEL));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_RCS));
+ when(mMockServiceControllerBinder.getImsServiceCapabilities()).thenReturn(
+ ImsService.CAPABILITY_SIP_DELEGATE_CREATION);
+
+ bindAndConnectService(testFeatures);
+
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
+ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_RCS), any());
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
+ eq(mTestImsServiceController));
+ validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0);
+ validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_RCS, SLOT_0);
+ }
+
+ /**
+ * Tests ImsServiceController loses SIP delegate creation flag if MMTEL and RCS are not both
+ * supported.
+ */
+ @Ignore("Disabling for integration b/175766573")
+ @SmallTest
+ @Test
+ public void testBindServiceSipDelegateCapabilityLost() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_MMTEL));
+ when(mMockServiceControllerBinder.getImsServiceCapabilities()).thenReturn(
+ ImsService.CAPABILITY_SIP_DELEGATE_CREATION);
+
+ bindAndConnectService(testFeatures);
+
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ eq(mTestImsServiceController));
+ // verify CAPABILITY_SIP_DELEGATE_CREATION is not set because MMTEL and RCS are not set.
+ validateFeatureContainerDoesNotHaveSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0);
}
/**
@@ -188,16 +299,16 @@ public class ImsServiceControllerTest extends ImsTestBase {
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
// Make sure this callback happens, which will notify the framework of emergency calling
// availability.
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_EMERGENCY_MMTEL));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
}
/**
@@ -217,20 +328,21 @@ public class ImsServiceControllerTest extends ImsTestBase {
bindAndConnectService(testFeatures);
// Verify no MMTEL or EMERGENCY_MMTEL features are created
- verify(mMockServiceControllerBinder, never()).createMmTelFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
- verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_EMERGENCY_MMTEL));
- verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
// verify RCS feature is created
- verify(mMockServiceControllerBinder).createRcsFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_RCS), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
+ validateRcsFeatureContainerExists(SLOT_0);
}
/**
@@ -245,16 +357,11 @@ public class ImsServiceControllerTest extends ImsTestBase {
ImsFeature.FEATURE_EMERGENCY_MMTEL));
testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
ImsFeature.FEATURE_MMTEL));
- mTestImsServiceController.removeImsServiceFeatureCallbacks();
bindAndConnectService(testFeatures);
- // add the callback after bind
- mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
- // Make sure this callback happens for Emergency MMTEL and MMTEL
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_EMERGENCY_MMTEL));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
+ validateMmTelFeatureExistsInCallback(SLOT_0, ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL);
}
/**
@@ -277,8 +384,72 @@ public class ImsServiceControllerTest extends ImsTestBase {
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ validateRcsFeatureContainerDoesntExist(SLOT_0);
+ }
+
+ /**
+ * Tests that when unbind is called while the ImsService is disconnected, we still handle
+ * unbinding to the service correctly.
+ */
+ @SmallTest
+ @Test
+ public void testBindServiceAndConnectedDisconnectedUnbind() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_MMTEL));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_RCS));
+ ServiceConnection conn = bindAndConnectService(testFeatures);
+
+ conn.onServiceDisconnected(mTestComponentName);
+
+ verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
+ eq(mTestImsServiceController));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ validateRcsFeatureContainerDoesntExist(SLOT_0);
+
+ mTestImsServiceController.unbind();
+ verify(mMockContext).unbindService(eq(conn));
+
+ // Even though we unbound, this was already sent after service disconnected, so we shouldn't
+ // see it again
+ verify(mMockCallbacks, times(1)).imsServiceFeatureRemoved(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+ verify(mMockCallbacks, times(1)).imsServiceFeatureRemoved(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
+ }
+
+ /**
+ * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
+ * subsequently unbound.
+ */
+ @SmallTest
+ @Test
+ public void testBindMoveToReady() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_MMTEL));
+ TestCallback cb = new TestCallback();
+ mRepo.registerForConnectionUpdates(SLOT_0, ImsFeature.FEATURE_MMTEL, cb, Runnable::run);
+
+ bindAndConnectService(testFeatures);
+
+ verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0));
+ ArgumentCaptor<IImsFeatureStatusCallback> captor =
+ ArgumentCaptor.forClass(IImsFeatureStatusCallback.class);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), captor.capture());
+ IImsFeatureStatusCallback mmtelStatusCb = captor.getValue();
+ assertNotNull(mmtelStatusCb);
+ validateMmTelFeatureContainerExists(SLOT_0);
+ assertEquals(mMockMmTelBinder, cb.container.imsFeature);
+ assertEquals(ImsFeature.STATE_UNAVAILABLE, cb.container.getState());
+
+ mmtelStatusCb.notifyImsFeatureStatus(ImsFeature.STATE_READY);
+ assertEquals(ImsFeature.STATE_READY, cb.container.getState());
}
/**
@@ -299,15 +470,19 @@ public class ImsServiceControllerTest extends ImsTestBase {
verify(mMockContext).unbindService(eq(conn));
verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL));
+ verify(mMockServiceControllerBinder).removeFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_RCS));
+ verify(mMockServiceControllerBinder).removeFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ validateRcsFeatureContainerDoesntExist(SLOT_0);
}
/**
@@ -325,12 +500,13 @@ public class ImsServiceControllerTest extends ImsTestBase {
conn.onBindingDied(null /*null*/);
+ verify(mMockContext).unbindService(eq(conn));
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ validateRcsFeatureContainerDoesntExist(SLOT_0);
}
/**
@@ -349,8 +525,29 @@ public class ImsServiceControllerTest extends ImsTestBase {
verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks, never()).imsFeatureCreated(anyInt(), anyInt());
verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ validateRcsFeatureContainerDoesntExist(SLOT_0);
+ }
+
+ /**
+ * Ensures that ImsServiceController handles a null ImsFeature instance properly.
+ */
+ @SmallTest
+ @Test
+ public void testBindServiceAndImsFeatureReturnedNull() throws RemoteException {
+ when(mMockServiceControllerBinder.createRcsFeature(anyInt()))
+ .thenReturn(null);
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_RCS));
+
+ bindAndConnectService(testFeatures);
+
+ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0);
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
+ eq(mTestImsServiceController));
+ validateRcsFeatureContainerDoesntExist(SLOT_0);
}
/**
@@ -363,10 +560,11 @@ public class ImsServiceControllerTest extends ImsTestBase {
testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
ImsFeature.FEATURE_MMTEL));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
// Create a new list with an additional item
HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
testFeatures);
@@ -375,10 +573,64 @@ public class ImsServiceControllerTest extends ImsTestBase {
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_1), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ validateMmTelFeatureContainerExists(SLOT_1);
+ }
+
+ /**
+ * Ensure changes in emergency calling status are tracked
+ */
+ @SmallTest
+ @Test
+ public void testBindServiceAndAddRemoveEmergency() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_MMTEL));
+ TestCallback cb = new TestCallback();
+ mRepo.registerForConnectionUpdates(SLOT_0, ImsFeature.FEATURE_MMTEL, cb, Runnable::run);
+ bindAndConnectService(testFeatures);
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ eq(mTestImsServiceController));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ assertEquals(mMockMmTelBinder, cb.container.imsFeature);
+ assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL
+ & cb.container.getCapabilities()) == 0);
+
+ // Add Emergency calling
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
+ testFeatures);
+ testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_EMERGENCY_MMTEL));
+
+ mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
+
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_EMERGENCY_MMTEL),
+ eq(mTestImsServiceController));
+ validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
+ assertEquals(mMockMmTelBinder, cb.container.imsFeature);
+
+ assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL
+ | cb.container.getCapabilities()) > 0);
+
+ // Remove Emergency calling
+ mTestImsServiceController.changeImsServiceFeatures(testFeatures);
+
+ verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_EMERGENCY_MMTEL),
+ eq(mTestImsServiceController));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ assertEquals(mMockMmTelBinder, cb.container.imsFeature);
+ assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL
+ & cb.container.getCapabilities()) == 0);
}
/**
@@ -392,10 +644,12 @@ public class ImsServiceControllerTest extends ImsTestBase {
testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
ImsFeature.FEATURE_RCS));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createRcsFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_RCS), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
+ validateRcsFeatureContainerExists(SLOT_0);
// Add FEATURE_EMERGENCY_MMTEL and ensure it doesn't cause MMTEL bind
HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
testFeatures);
@@ -404,12 +658,13 @@ public class ImsServiceControllerTest extends ImsTestBase {
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
- verify(mMockServiceControllerBinder, never()).createMmTelFeature(eq(SLOT_1), any());
+ verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_1);
+ verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_1),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_1),
- eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerDoesntExist(SLOT_1);
}
/**
@@ -423,22 +678,28 @@ public class ImsServiceControllerTest extends ImsTestBase {
testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
ImsFeature.FEATURE_MMTEL));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerExists(SLOT_0);
// Call change with the same features and make sure it is disregarded
mTestImsServiceController.changeImsServiceFeatures(testFeatures);
- verify(mMockServiceControllerBinder, times(1)).createMmTelFeature(eq(SLOT_0), any());
- verify(mMockServiceControllerBinder, never()).removeImsFeature(anyInt(), anyInt(), any());
+ verify(mMockServiceControllerBinder, times(1)).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder, times(1)).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
+ verify(mMockServiceControllerBinder, never()).removeImsFeature(anyInt(), anyInt());
+
+
+ verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(anyInt(),
+ anyInt(), any());
verify(mMockCallbacks, times(1)).imsServiceFeatureCreated(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockCallbacks, never()).imsServiceFeatureRemoved(anyInt(), anyInt(), any());
- verify(mMockProxyCallbacks, times(1)).imsFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_MMTEL));
- verify(mMockProxyCallbacks, never()).imsFeatureRemoved(anyInt(), anyInt());
+ validateMmTelFeatureContainerExists(SLOT_0);
}
/**
@@ -453,14 +714,18 @@ public class ImsServiceControllerTest extends ImsTestBase {
testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_1,
ImsFeature.FEATURE_MMTEL));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_1), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ validateMmTelFeatureContainerExists(SLOT_1);
// Create a new list with one less item
HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithSubtraction =
new HashSet<>(testFeatures);
@@ -470,10 +735,13 @@ public class ImsServiceControllerTest extends ImsTestBase {
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithSubtraction);
verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_1),
+ eq(ImsFeature.FEATURE_MMTEL));
+ verify(mMockServiceControllerBinder).removeFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ validateMmTelFeatureContainerDoesntExist(SLOT_1);
}
/**
@@ -488,28 +756,36 @@ public class ImsServiceControllerTest extends ImsTestBase {
testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_1,
ImsFeature.FEATURE_MMTEL));
bindAndConnectService(testFeatures);
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
- verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_1), any());
+ verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
+ eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ validateMmTelFeatureContainerExists(SLOT_1);
// Create a new empty list
mTestImsServiceController.changeImsServiceFeatures(new HashSet<>());
verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL));
+ verify(mMockServiceControllerBinder).removeFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_1),
+ eq(ImsFeature.FEATURE_MMTEL));
+ verify(mMockServiceControllerBinder).removeFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
- verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ validateMmTelFeatureContainerDoesntExist(SLOT_1);
}
/**
@@ -532,11 +808,13 @@ public class ImsServiceControllerTest extends ImsTestBase {
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
- verify(mMockServiceControllerBinder, never()).createRcsFeature(eq(SLOT_0), any());
+ verify(mMockServiceControllerBinder, never()).createRcsFeature(eq(SLOT_0));
+ verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_RCS), any());
verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
- verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_RCS));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ validateRcsFeatureContainerDoesntExist(SLOT_0);
}
/**
@@ -562,6 +840,30 @@ public class ImsServiceControllerTest extends ImsTestBase {
}
/**
+ * Due to a bug in ServiceConnection, we will sometimes receive a null binding after the binding
+ * dies. Ignore null binding in this case.
+ */
+ @SmallTest
+ @Test
+ public void testAutoBindAfterBinderDiedIgnoreNullBinding() throws RemoteException {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_MMTEL));
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_RCS));
+ ServiceConnection conn = bindAndConnectService(testFeatures);
+
+ conn.onBindingDied(null);
+ // null binding should be ignored in this case.
+ conn.onNullBinding(null);
+
+ long delay = mTestImsServiceController.getRebindDelay();
+ waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
+ // The service should autobind after rebind event occurs
+ verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+ }
+
+ /**
* Ensure that bindService has only been called once before automatic rebind occurs.
*/
@SmallTest
@@ -625,6 +927,67 @@ public class ImsServiceControllerTest extends ImsTestBase {
verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
}
+ private void validateMmTelFeatureContainerExists(int slotId) {
+
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, ImsFeature.FEATURE_MMTEL).orElse(null);
+ assertNotNull("MMTEL FeatureContainer should not be null", fc);
+ assertEquals("ImsServiceController did not report MmTelFeature to service repo correctly",
+ mMockMmTelBinder, fc.imsFeature);
+ assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL & fc.getCapabilities()) == 0);
+ }
+
+ private void validateMmTelFeatureContainerExistsWithEmergency(int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, ImsFeature.FEATURE_MMTEL).orElse(null);
+ assertNotNull("MMTEL FeatureContainer should not be null", fc);
+ assertEquals("ImsServiceController did not report MmTelFeature to service repo correctly",
+ mMockMmTelBinder, fc.imsFeature);
+ assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL | fc.getCapabilities()) > 0);
+ }
+
+ private void validateFeatureContainerExistsWithSipDelegate(int featureType, int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, featureType).orElse(null);
+ assertNotNull("FeatureContainer should not be null", fc);
+ assertTrue((ImsService.CAPABILITY_SIP_DELEGATE_CREATION | fc.getCapabilities()) > 0);
+ }
+
+ private void validateFeatureContainerDoesNotHaveSipDelegate(int featureType, int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, featureType).orElse(null);
+ assertNotNull("FeatureContainer should not be null", fc);
+ assertEquals(0, (ImsService.CAPABILITY_SIP_DELEGATE_CREATION & fc.getCapabilities()));
+ }
+
+
+ private void validateMmTelFeatureExistsInCallback(int slotId, long expectedCaps) {
+ TestCallback cb = new TestCallback();
+ mRepo.registerForConnectionUpdates(slotId, ImsFeature.FEATURE_MMTEL, cb, Runnable::run);
+ assertEquals(mMockMmTelBinder, cb.container.imsFeature);
+ assertEquals(expectedCaps, cb.container.getCapabilities());
+ }
+
+ private void validateRcsFeatureContainerExists(int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, ImsFeature.FEATURE_RCS).orElse(null);
+ assertNotNull("RCS FeatureContainer should not be null", fc);
+ assertEquals("ImsServiceController did not report RcsFeature to service repo correctly",
+ mMockRcsBinder, fc.imsFeature);
+ }
+
+ private void validateMmTelFeatureContainerDoesntExist(int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, ImsFeature.FEATURE_MMTEL).orElse(null);
+ assertNull("FeatureContainer should be null", fc);
+ }
+
+ private void validateRcsFeatureContainerDoesntExist(int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, ImsFeature.FEATURE_RCS).orElse(null);
+ assertNull("FeatureContainer should be null", fc);
+ }
+
private void bindAndNullServiceError(
HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
ServiceConnection connection = bindService(testFeatures);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
index ad8cd47469..086390ad98 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
@@ -16,14 +16,19 @@
package com.android.internal.telephony.ims;
+import static org.junit.Assert.fail;
+
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.testing.TestableLooper;
import androidx.test.InstrumentationRegistry;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -33,6 +38,8 @@ import java.util.concurrent.TimeUnit;
public class ImsTestBase {
protected Context mContext;
+ protected List<TestableLooper> mTestableLoopers = new ArrayList<>();
+ protected TestableLooper mTestableLooper;
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
@@ -41,9 +48,16 @@ public class ImsTestBase {
if (Looper.myLooper() == null) {
Looper.prepare();
}
+ mTestableLooper = TestableLooper.get(ImsTestBase.this);
+ monitorTestableLooper(mTestableLooper);
}
public void tearDown() throws Exception {
+ unmonitorTestableLooper(mTestableLooper);
+ for (TestableLooper looper : mTestableLoopers) {
+ looper.destroy();
+ }
+ TestableLooper.remove(ImsTestBase.this);
}
protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
@@ -61,4 +75,49 @@ public class ImsTestBase {
}
}
}
+
+ /**
+ * Add a TestableLooper to the list of monitored loopers
+ * @param looper looper to be added if it doesn't already exist
+ */
+ public void monitorTestableLooper(TestableLooper looper) {
+ if (looper != null && !mTestableLoopers.contains(looper)) {
+ mTestableLoopers.add(looper);
+ }
+ }
+
+ /**
+ * Remove a TestableLooper from the list of monitored loopers
+ * @param looper looper to be removed if it exists
+ */
+ public void unmonitorTestableLooper(TestableLooper looper) {
+ if (looper != null && mTestableLoopers.contains(looper)) {
+ mTestableLoopers.remove(looper);
+ }
+ }
+
+ /**
+ * Process all messages at the current time for all monitored TestableLoopers
+ */
+ public void processAllMessages() {
+ if (mTestableLoopers.isEmpty()) {
+ fail("mTestableLoopers is empty. Please make sure to add @RunWithLooper annotation");
+ }
+ while (!areAllTestableLoopersIdle()) {
+ for (TestableLooper looper : mTestableLoopers) looper.processAllMessages();
+ }
+ }
+
+ /**
+ * Check if there are any messages to be processed in any monitored TestableLooper
+ * Delayed messages to be handled at a later time will be ignored
+ * @return true if there are no messages that can be handled at the current time
+ * across all monitored TestableLoopers
+ */
+ private boolean areAllTestableLoopersIdle() {
+ for (TestableLooper looper : mTestableLoopers) {
+ if (!looper.getLooper().getQueue().isIdle()) return false;
+ }
+ return true;
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsUtTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsUtTest.java
deleted file mode 100644
index 4948a67a33..0000000000
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsUtTest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.ims;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.TestCase.fail;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.telephony.ims.ImsSsInfo;
-import android.telephony.ims.ImsUtListener;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.ims.ImsUt;
-import com.android.ims.internal.IImsUt;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class ImsUtTest extends TelephonyTest {
-
- private static final int MSG_QUERY = 1;
- private static final int TEST_TIMEOUT_MS = 5000;
-
- private class TestHandler extends Handler {
-
- TestHandler(Looper looper) {
- super(looper);
- }
-
- private final LinkedBlockingQueue<ImsSsInfo> mPendingSsInfos = new LinkedBlockingQueue<>(1);
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_QUERY) {
- AsyncResult ar = (AsyncResult) msg.obj;
- mPendingSsInfos.offer((ImsSsInfo) ar.result);
- }
- }
- public ImsSsInfo getPendingImsSsInfo() {
- try {
- return mPendingSsInfos.poll(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- fail("test interrupted!");
- }
- return null;
- }
- }
-
- @Mock IImsUt mImsUtBinder;
-
- private TestHandler mHandler;
-
- @Before
- public void setUp() throws Exception {
- super.setUp("ImsUtTest");
- mHandler = new TestHandler(Looper.myLooper());
- processAllMessages();
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- @Test
- @SmallTest
- public void testClirConversionCompat() throws Exception {
- ArgumentCaptor<ImsUt.IImsUtListenerProxy> captor =
- ArgumentCaptor.forClass(ImsUt.IImsUtListenerProxy.class);
- ImsUt mImsUt = new ImsUt(mImsUtBinder);
- verify(mImsUtBinder).setListener(captor.capture());
- ImsUt.IImsUtListenerProxy proxy = captor.getValue();
- assertNotNull(proxy);
-
- doReturn(2).when(mImsUtBinder).queryCLIR();
- mImsUt.queryCLIR(Message.obtain(mHandler, MSG_QUERY));
-
- Bundle result = new Bundle();
- result.putIntArray(ImsUtListener.BUNDLE_KEY_CLIR, new int[] {
- ImsSsInfo.CLIR_OUTGOING_INVOCATION, ImsSsInfo.CLIR_STATUS_PROVISIONED_PERMANENT});
- // This is deprecated, will be converted from Bundle -> ImsSsInfo
- proxy.utConfigurationQueried(null, 2 /*id*/, result);
- processAllMessages();
-
-
- ImsSsInfo info = mHandler.getPendingImsSsInfo();
- assertNotNull(info);
- assertEquals(ImsSsInfo.CLIR_OUTGOING_INVOCATION, info.getClirOutgoingState());
- assertEquals(ImsSsInfo.CLIR_STATUS_PROVISIONED_PERMANENT,
- info.getClirInterrogationStatus());
- }
-
- @Test
- @SmallTest
- public void testClipConversionCompat() throws Exception {
- ArgumentCaptor<ImsUt.IImsUtListenerProxy> captor =
- ArgumentCaptor.forClass(ImsUt.IImsUtListenerProxy.class);
- ImsUt mImsUt = new ImsUt(mImsUtBinder);
- verify(mImsUtBinder).setListener(captor.capture());
- ImsUt.IImsUtListenerProxy proxy = captor.getValue();
- assertNotNull(proxy);
-
- doReturn(2).when(mImsUtBinder).queryCLIP();
- mImsUt.queryCLIP(Message.obtain(mHandler, MSG_QUERY));
-
- ImsSsInfo info = new ImsSsInfo.Builder(ImsSsInfo.ENABLED).setProvisionStatus(
- ImsSsInfo.CLIR_STATUS_PROVISIONED_PERMANENT).build();
- Bundle result = new Bundle();
- result.putParcelable(ImsUtListener.BUNDLE_KEY_SSINFO, info);
- // This is deprecated, will be converted from Bundle -> ImsSsInfo
- proxy.utConfigurationQueried(null, 2 /*id*/, result);
- processAllMessages();
-
- ImsSsInfo resultInfo = mHandler.getPendingImsSsInfo();
- assertNotNull(resultInfo);
- assertEquals(info.getStatus(), resultInfo.getStatus());
- assertEquals(info.getProvisionStatus(), resultInfo.getProvisionStatus());
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java
deleted file mode 100644
index 87b8a56896..0000000000
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.ims;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.Looper;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.ims.ImsCallbackAdapterManager;
-import com.android.internal.telephony.TelephonyTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class MmTelFeatureConnectionTest extends TelephonyTest {
-
- private class TestCallback extends Binder implements IInterface {
-
- @Override
- public IBinder asBinder() {
- return this;
- }
- }
-
- private class CallbackManagerTest extends
- ImsCallbackAdapterManager<TestCallback> {
-
- List<TestCallback> mCallbacks = new ArrayList<>();
-
- CallbackManagerTest(Context context, Object lock) {
- super(context, lock, 0 /*slotId*/);
- }
-
- // A callback has been registered. Register that callback with the MmTelFeature.
- @Override
- public void registerCallback(TestCallback localCallback) {
- mCallbacks.add(localCallback);
- }
-
- // A callback has been removed, unregister that callback with the MmTelFeature.
- @Override
- public void unregisterCallback(TestCallback localCallback) {
- mCallbacks.remove(localCallback);
- }
-
- public boolean doesCallbackExist(TestCallback callback) {
- return mCallbacks.contains(callback);
- }
- }
- private CallbackManagerTest mCallbackManagerUT;
-
- @Before
- public void setUp() throws Exception {
- super.setUp("MmTelFeatureConnectionTest");
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- mCallbackManagerUT = new CallbackManagerTest(mContext, this);
- }
-
- @After
- public void tearDown() throws Exception {
- mCallbackManagerUT = null;
- super.tearDown();
- }
-
- /**
- * Basic test of deprecated functionality, ensure that adding the callback directly triggers the
- * appropriate registerCallback and unregisterCallback calls.
- */
- @Test
- @SmallTest
- public void testCallbackAdapter_addAndRemoveCallback() throws Exception {
- TestCallback testCallback = new TestCallback();
- mCallbackManagerUT.addCallback(testCallback);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback));
- // The subscriptions changed listener should only be added for callbacks that are being
- // linked to a subscription.
- verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- mCallbackManagerUT.removeCallback(testCallback);
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback));
- // The subscriptions changed listener should only be removed for callbacks that are
- // linked to a subscription.
- verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
- }
-
- /**
- * Ensure that adding the callback and linking subId triggers the appropriate registerCallback
- * and unregisterCallback calls as well as the subscriptionChanged listener.
- */
- @Test
- @SmallTest
- public void testCallbackAdapter_addAndRemoveCallbackForSub() throws Exception {
- TestCallback testCallback = new TestCallback();
- int testSub = 1;
- mCallbackManagerUT.addCallbackForSubscription(testCallback, testSub);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback));
- verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- mCallbackManagerUT.removeCallbackForSubscription(testCallback, testSub);
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback));
- verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
- }
-
- /**
- * Ensure that adding the callback and linking multiple subIds trigger the appropriate
- * registerCallback and unregisterCallback calls as well as the subscriptionChanged listener.
- * When removing the callbacks, the subscriptionChanged listener shoud only be removed when all
- * callbacks have been removed.
- */
- @Test
- @SmallTest
- public void testCallbackAdapter_addAndRemoveCallbackForMultipleSubs() throws Exception {
- TestCallback testCallback1 = new TestCallback();
- TestCallback testCallback2 = new TestCallback();
- int testSub1 = 1;
- int testSub2 = 2;
- mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
- mCallbackManagerUT.addCallbackForSubscription(testCallback2, testSub2);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
- // This should only happen once.
- verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- mCallbackManagerUT.removeCallbackForSubscription(testCallback1, testSub1);
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
- // removing the listener should not happen until the second callback is removed.
- verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- mCallbackManagerUT.removeCallbackForSubscription(testCallback2, testSub2);
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback2));
- verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
- }
-
- /**
- * The subscriptions have changed, ensure that the callbacks registered to the original
- * subscription testSub1 are removed, while keeping the callbacks for testSub2, since it was not
- * removed.
- */
- @Test
- @SmallTest
- public void testCallbackAdapter_onSubscriptionsChangedMultipleSubs() throws Exception {
- TestCallback testCallback1 = new TestCallback();
- TestCallback testCallback2 = new TestCallback();
- int testSub1 = 1;
- int testSub2 = 2;
- int testSub3 = 3;
- mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
- mCallbackManagerUT.addCallbackForSubscription(testCallback2, testSub2);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
- verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- // Simulate subscriptions changed, where testSub1 is no longer active
- doReturn(createSubscriptionInfoList(new int[] {testSub2, testSub3}))
- .when(mSubscriptionManager).getActiveSubscriptionInfoList(anyBoolean());
- mCallbackManagerUT.mSubChangedListener.onSubscriptionsChanged();
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
- // verify that the subscription changed listener is not removed, since we still have a
- // callback on testSub2
- verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
- }
-
- /**
- * The active subscription has changed, ensure that the callback registered to the original
- * subscription testSub1 are removed as well as the subscription changed listener, since
- * there are mo more active callbacks.
- */
- @Test
- @SmallTest
- public void testCallbackAdapter_onSubscriptionsChangedOneSub() throws Exception {
- TestCallback testCallback1 = new TestCallback();
- int testSub1 = 1;
- int testSub2 = 2;
- mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
- verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- // Simulate subscriptions changed, where testSub1 is no longer active
- doReturn(createSubscriptionInfoList(new int[] {testSub2}))
- .when(mSubscriptionManager).getActiveSubscriptionInfoList(anyBoolean());
- mCallbackManagerUT.mSubChangedListener.onSubscriptionsChanged();
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
- // verify that the subscription listener is removed, since the only active callback has been
- // removed.
- verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
- }
-
- /**
- * The close() method has been called, so al callbacks should be cleaned up and notified
- * that they have been removed. The subscriptions changed listener should also be removed.
- */
- @Test
- @SmallTest
- public void testCallbackAdapter_closeMultipleSubs() throws Exception {
- TestCallback testCallback1 = new TestCallback();
- TestCallback testCallback2 = new TestCallback();
- int testSub1 = 1;
- int testSub2 = 2;
- mCallbackManagerUT.addCallbackForSubscription(testCallback1, testSub1);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
- mCallbackManagerUT.addCallbackForSubscription(testCallback2, testSub2);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
- verify(mSubscriptionManager, times(1)).addOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- // Close the manager, ensure all subscription callbacks are removed
- mCallbackManagerUT.close();
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback2));
- // verify that the subscription changed listener is removed.
- verify(mSubscriptionManager, times(1)).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
- }
-
- /**
- * The close() method has been called, so all callbacks should be cleaned up. Since they are
- * not associated with any subscriptions, no subscription based logic should be called.
- */
- @Test
- @SmallTest
- public void testCallbackAdapter_closeSlotBasedCallbacks() throws Exception {
- TestCallback testCallback1 = new TestCallback();
- TestCallback testCallback2 = new TestCallback();
- mCallbackManagerUT.addCallback(testCallback1);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback1));
- mCallbackManagerUT.addCallback(testCallback2);
- assertTrue(mCallbackManagerUT.doesCallbackExist(testCallback2));
- // verify that the subscription changed listener is never called for these callbacks
- // because they are not associated with any subscriptions.
- verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
-
- // Close the manager, ensure all subscription callbacks are removed
- mCallbackManagerUT.close();
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback1));
- assertFalse(mCallbackManagerUT.doesCallbackExist(testCallback2));
- // verify that the subscription changed removed method is never called
- verify(mSubscriptionManager, never()).removeOnSubscriptionsChangedListener(
- any(SubscriptionManager.OnSubscriptionsChangedListener.class));
- }
-
- private List<SubscriptionInfo> createSubscriptionInfoList(int[] subIds) {
- List<SubscriptionInfo> infos = new ArrayList<>();
- for (int i = 0; i < subIds.length; i++) {
- SubscriptionInfo info = new SubscriptionInfo(subIds[i], null, -1, null, null, -1, -1,
- null, -1, null, null, null, null, false, null, null);
- infos.add(info);
- }
- return infos;
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
deleted file mode 100644
index 38b3203ddf..0000000000
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.ims;
-
-import android.os.RemoteException;
-import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
-import android.telephony.ims.aidl.IImsRegistration;
-import android.telephony.ims.aidl.IImsServiceController;
-import android.telephony.ims.aidl.IImsServiceControllerListener;
-import android.telephony.ims.stub.ImsConfigImplBase;
-import android.telephony.ims.stub.ImsFeatureConfiguration;
-import android.telephony.ims.stub.ImsRegistrationImplBase;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-
-import static org.mockito.Mockito.spy;
-
-/**
- * Test base implementation of the ImsServiceController, which is used as a mockito spy.
- */
-
-public class TestImsServiceControllerAdapter {
-
- public IImsFeatureStatusCallback mStatusCallback;
-
- public class ImsServiceControllerBinder extends IImsServiceController.Stub {
-
- @Override
- public void setListener(IImsServiceControllerListener l) {
- }
-
- @Override
- public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
- return TestImsServiceControllerAdapter.this.createMMTelFeature(slotId);
- }
-
- @Override
- public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
- return TestImsServiceControllerAdapter.this.createRcsFeature(slotId);
- }
-
- @Override
- public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
- throws RemoteException {
- TestImsServiceControllerAdapter.this.removeImsFeature(slotId, featureType);
- }
-
- @Override
- public ImsFeatureConfiguration querySupportedImsFeatures() {
- return null;
- }
-
- @Override
- public void notifyImsServiceReadyForFeatureCreation() {
- }
-
- @Override
- public IImsConfig getConfig(int slotId) throws RemoteException {
- return new ImsConfigImplBase().getIImsConfig();
- }
-
- @Override
- public IImsRegistration getRegistration(int slotId) throws RemoteException {
- return new ImsRegistrationImplBase().getBinder();
- }
-
- @Override
- public void enableIms(int slotId) {
- }
-
- @Override
- public void disableIms(int slotId) {
-
- }
-
- }
-
- private ImsServiceControllerBinder mBinder;
-
- public IImsServiceController getBinder() {
- if (mBinder == null) {
- mBinder = spy(new ImsServiceControllerBinder());
- }
-
- return mBinder;
- }
-
- // Used by Mockito for verification that this method is being called in spy
- public IImsMmTelFeature createMMTelFeature(int slotId) {
- return null;
- }
-
- // Used by Mockito for verification that this method is being called in spy
- public IImsRcsFeature createRcsFeature(int slotId) {
- return null;
- }
-
- // Used by Mockito for verification that this method is being called in spy
- public void removeImsFeature(int subId, int feature) throws RemoteException {
- }
-}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
index 9327414c88..306a503596 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsCallTest.java
@@ -22,11 +22,16 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.os.Bundle;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.ims.ImsCall;
@@ -35,6 +40,7 @@ import com.android.internal.telephony.TelephonyTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
public class ImsCallTest extends TelephonyTest {
@@ -55,6 +61,36 @@ public class ImsCallTest extends TelephonyTest {
@Test
@SmallTest
+ public void testCallSessionProgressingAppliedMediaCaps() throws Exception {
+ ImsCallSession mockSession = mock(ImsCallSession.class);
+ ImsCall testImsCall = new ImsCall(mContext, mTestCallProfile);
+ ImsCallProfile profile = new ImsCallProfile();
+ when(mockSession.getCallProfile()).thenReturn(profile);
+ testImsCall.attachSession(mockSession);
+
+ ArgumentCaptor<ImsCallSession.Listener> listenerCaptor =
+ ArgumentCaptor.forClass(ImsCallSession.Listener.class);
+ verify(mockSession).setListener(listenerCaptor.capture());
+ ImsCallSession.Listener listener = listenerCaptor.getValue();
+ assertNotNull(listener);
+
+ // Set new profile with direction of none
+ ImsStreamMediaProfile newProfile = new ImsStreamMediaProfile(
+ ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB,
+ ImsStreamMediaProfile.DIRECTION_INACTIVE,
+ ImsStreamMediaProfile.VIDEO_QUALITY_NONE,
+ ImsStreamMediaProfile.DIRECTION_INACTIVE,
+ ImsStreamMediaProfile.RTT_MODE_DISABLED);
+ listener.callSessionProgressing(mockSession, newProfile);
+
+ ImsStreamMediaProfile testProfile = testImsCall.getCallProfile().getMediaProfile();
+ assertNotNull(testProfile);
+ // Assert that the new direction was applied to the profile
+ assertEquals(ImsStreamMediaProfile.DIRECTION_INACTIVE, testProfile.getAudioDirection());
+ }
+
+ @Test
+ @SmallTest
public void testSetWifiDeprecated() {
ImsCall mTestImsCall = new ImsCall(mContext, mTestCallProfile);
assertFalse(mTestImsCall.isWifiCall());
@@ -87,6 +123,20 @@ public class ImsCallTest extends TelephonyTest {
@Test
@SmallTest
+ public void testNullCallProfileAfterNonNull() {
+ ImsCallProfile profile = new ImsCallProfile();
+ profile.mCallType = ImsCallProfile.CALL_TYPE_VT_TX;
+
+ ImsCall imsCall = new ImsCall(mContext, profile);
+ assertNotNull(imsCall);
+ assertTrue(imsCall.wasVideoCall());
+
+ imsCall.setCallProfile(null);
+ assertTrue(imsCall.wasVideoCall());
+ }
+
+ @Test
+ @SmallTest
public void testSetWifi() {
ImsCall mTestImsCall = new ImsCall(mContext, mTestCallProfile);
assertFalse(mTestImsCall.isWifiCall());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
index 31fbfad319..68ba403ae2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -139,6 +140,63 @@ public class ImsPhoneCallTest extends TelephonyTest {
assertEquals(Call.State.ACTIVE, mImsCallUT.getState());
}
+ /**
+ * Verifies we can handle starting ringback between call state changes.
+ */
+ @Test
+ @SmallTest
+ public void testUpdateRingBackToneBetweenStateChange() {
+ mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+ mImsCallProfile.mMediaProfile = mMediaProfile;
+
+ // This call state change should NOT start ringback since it the direction is wrong.
+ mImsCallUT.update(null, mImsCall, Call.State.ALERTING);
+ verify(mImsPhone, never()).startRingbackTone();
+ assertEquals(Call.State.ALERTING, mImsCallUT.getState());
+
+ // Simulate a change to the profile without a state change.
+ mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_INACTIVE;
+ mImsCallUT.maybeChangeRingbackState(mImsCall);
+ verify(mImsPhone, times(1)).startRingbackTone();
+
+ // And then assume the call goes active, which would stop the ringback.
+ mImsCallUT.update(null, mImsCall, Call.State.ACTIVE);
+ verify(mImsPhone, times(1)).stopRingbackTone();
+ assertEquals(Call.State.ACTIVE, mImsCallUT.getState());
+ }
+
+ /**
+ * Verifies we can handle ringback start/stop entirely between call state changes.
+ */
+ @Test
+ @SmallTest
+ public void testUpdateRingBackToneBetweenStateChangeTwo() {
+ mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+ mImsCallProfile.mMediaProfile = mMediaProfile;
+
+ // This call state change should NOT start ringback since it the direction is wrong.
+ mImsCallUT.update(null, mImsCall, Call.State.ALERTING);
+ verify(mImsPhone, never()).startRingbackTone();
+ assertEquals(Call.State.ALERTING, mImsCallUT.getState());
+
+ // Simulate a change to the profile without a state change.
+ mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_INACTIVE;
+ mImsCallUT.maybeChangeRingbackState(mImsCall);
+ verify(mImsPhone, times(1)).startRingbackTone();
+
+ // Simulate another change to the profile without a state change.
+ mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
+ mImsCallUT.maybeChangeRingbackState(mImsCall);
+ verify(mImsPhone, times(1)).stopRingbackTone();
+
+ // And then assume the call goes active, which should not impact ringback state.
+ mImsCallUT.update(null, mImsCall, Call.State.ACTIVE);
+ assertEquals(Call.State.ACTIVE, mImsCallUT.getState());
+ // Should still have only started and stopped once
+ verify(mImsPhone, times(1)).startRingbackTone();
+ verify(mImsPhone, times(1)).stopRingbackTone();
+ }
+
@Test
@SmallTest
public void testStopRingingOnHandover() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 0a972ab7f0..d1826d51e6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -25,11 +25,11 @@ import static android.net.NetworkStats.UID_ALL;
import static com.android.testutils.NetworkStatsUtilsKt.assertNetworkStatsEquals;
import static junit.framework.Assert.assertNotNull;
-import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
@@ -71,6 +71,7 @@ import android.telephony.ims.ImsConferenceState;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -80,9 +81,11 @@ import android.testing.TestableLooper;
import androidx.test.filters.FlakyTest;
+import com.android.ims.FeatureConnector;
import com.android.ims.ImsCall;
import com.android.ims.ImsConfig;
import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
import com.android.ims.internal.IImsCallSession;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
@@ -90,6 +93,7 @@ import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.d2d.RtpTransport;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker.VtDataUsageProvider;
import org.junit.After;
@@ -99,15 +103,19 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.Set;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class ImsPhoneCallTrackerTest extends TelephonyTest {
private ImsPhoneCallTracker mCTUT;
private MmTelFeature.Listener mMmTelListener;
+ private FeatureConnector.Listener<ImsManager> mConnectorListener;
private ImsMmTelManager.CapabilityCallback mCapabilityCallback;
private ImsCall.Listener mImsCallListener;
private ImsCall mImsCall;
@@ -126,6 +134,12 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
private ImsPhoneConnection mImsPhoneConnection;
@Mock
private INetworkStatsProviderCallback mVtDataUsageProviderCb;
+ @Mock
+ private ImsPhoneCallTracker.ConnectorFactory mConnectorFactory;
+ @Mock
+ private FeatureConnector<ImsManager> mMockConnector;
+ @Captor
+ private ArgumentCaptor<Set<RtpHeaderExtensionType>> mRtpHeaderExtensionTypeCaptor;
private void imsCallMocking(final ImsCall imsCall) throws Exception {
@@ -177,13 +191,13 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
imsCall.attachSession(mImsCallSession);
doReturn("1").when(mImsCallSession).getCallId();
+ doReturn(mImsCallProfile).when(mImsCallSession).getCallProfile();
}
@Before
public void setUp() throws Exception {
super.setUp(this.getClass().getSimpleName());
mImsCallProfile.mCallExtras = mBundle;
- mImsManagerInstances.put(mImsPhone.getPhoneId(), mImsManager);
mImsCall = spy(new ImsCall(mContext, mImsCallProfile));
mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
mImsPhoneConnectionListener = mock(ImsPhoneConnection.Listener.class);
@@ -197,8 +211,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
doAnswer(invocation -> {
mMmTelListener = (MmTelFeature.Listener) invocation.getArguments()[0];
return null;
- }).when(mImsManager).open(any(MmTelFeature.Listener.class));
-
+ }).when(mImsManager).open(any(), any(), any());
doAnswer(new Answer<ImsCall>() {
@Override
@@ -225,13 +238,18 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
mCapabilityCallback = (ImsMmTelManager.CapabilityCallback) invocation.getArguments()[0];
return mCapabilityCallback;
- }).when(mImsManager).addCapabilitiesCallback(any(ImsMmTelManager.CapabilityCallback.class));
+ }).when(mImsManager).addCapabilitiesCallback(
+ any(ImsMmTelManager.CapabilityCallback.class), any());
doReturn(mImsConfig).when(mImsManager).getConfigInterface();
- doNothing().when(mImsManager).addNotifyStatusChangedCallbackIfAvailable(any());
+ doAnswer((Answer<FeatureConnector<ImsManager>>) invocation -> {
+ mConnectorListener =
+ (FeatureConnector.Listener<ImsManager>) invocation.getArguments()[3];
+ return mMockConnector;
+ }).when(mConnectorFactory).create(any(), anyInt(), anyString(), any(), any());
- mCTUT = new ImsPhoneCallTracker(mImsPhone, Runnable::run);
+ mCTUT = new ImsPhoneCallTracker(mImsPhone, mConnectorFactory, Runnable::run);
mCTUT.addReasonCodeRemapping(null, "Wifi signal lost.", ImsReasonInfo.CODE_WIFI_LOST);
mCTUT.addReasonCodeRemapping(501, "Call answered elsewhere.",
ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
@@ -257,6 +275,9 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
logd("ImsPhoneCallTracker initiated");
processAllMessages();
+
+ verify(mMockConnector).connect();
+ mConnectorListener.connectionReady(mImsManager);
}
@After
@@ -306,7 +327,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
public void testImsFeatureCapabilityChange() {
doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(
mImsManager).getRegistrationTech();
- assertFalse(mCTUT.isVolteEnabled());
+ assertFalse(mCTUT.isVoiceOverCellularImsEnabled());
assertFalse(mCTUT.isVideoCallEnabled());
// enable only Voice
@@ -315,7 +336,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
mCapabilityCallback.onCapabilitiesStatusChanged(caps);
processAllMessages();
- assertTrue(mCTUT.isVolteEnabled());
+ assertTrue(mCTUT.isVoiceOverCellularImsEnabled());
assertFalse(mCTUT.isVideoCallEnabled());
// video call not enabled
verify(mImsPhone, times(0)).notifyForVideoCapabilityChanged(anyBoolean());
@@ -509,6 +530,54 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState());
}
+ @Test
+ @SmallTest
+ public void testImsMTActiveHoldServiceDisconnect() {
+ testImsMTCallAccept();
+
+ assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
+ assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
+ // mock a new MT
+ try {
+ doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
+ any(ImsCall.Listener.class));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("unexpected exception thrown" + ex.getMessage());
+ }
+ mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
+
+ verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
+ verify(mImsPhone, times(2)).notifyIncomingRing();
+ assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
+ assertEquals(ImsPhoneCall.State.WAITING, mCTUT.mRingingCall.getState());
+ assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
+
+ //hold the foreground active call, accept the new ringing call
+ try {
+ mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
+ verify(mImsCall, times(1)).hold();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("unexpected exception thrown" + ex.getMessage());
+ }
+
+ processAllMessages();
+ assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
+ assertFalse(mCTUT.mRingingCall.isRinging());
+ assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState());
+
+ // Now fake the ImsService crashing
+ mCTUT.hangupAllOrphanedConnections(DisconnectCause.LOST_SIGNAL);
+ assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
+ try {
+ // ensure new calls are not blocked by any lingering state after crash.
+ mCTUT.checkForDialIssues();
+ } catch (CallStateException e) {
+ fail("checkForDialIssues should not generate a CallStateException: " + e.getMessage());
+ }
+ }
+
/**
* Ensures that the dial method will perform a shared preferences lookup using the correct
* shared preference key to determine the CLIR mode.
@@ -541,25 +610,25 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
@Test
@SmallTest
public void testEmergencyDialSuppressClir() {
+ String dialString = "+17005554141";
mCTUT.setSharedPreferenceProxy((Context context) -> {
return mSharedPreferences;
});
- // Mock implementation of phone number utils treats everything as an emergency.
- mCTUT.setPhoneNumberUtilsProxy((String string) -> {
- return true;
- });
+
+ doReturn(true).when(mTelephonyManager).isEmergencyNumber(dialString);
+
// Set preference to hide caller ID.
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
doReturn(CommandsInterface.CLIR_INVOCATION).when(mSharedPreferences).getInt(
stringCaptor.capture(), anyInt());
try {
- mCTUT.dial("+17005554141", VideoProfile.STATE_AUDIO_ONLY, null);
+ mCTUT.dial(dialString, new ImsPhone.ImsDialArgs.Builder().setIsEmergency(true).build());
ArgumentCaptor<ImsCallProfile> profileCaptor = ArgumentCaptor.forClass(
ImsCallProfile.class);
verify(mImsManager, times(1)).makeCall(eq(mImsCallProfile),
- eq(new String[]{"+17005554141"}), any());
+ eq(new String[]{dialString}), any());
// Because this is an emergency call, we expect caller id to be visible now.
assertEquals(mImsCallProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR),
@@ -573,17 +642,32 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
}
}
- @FlakyTest
- @Ignore
@Test
@SmallTest
public void testImsMOCallDial() {
startOutgoingCall();
//call established
mImsCallListener.onCallProgressing(mSecondImsCall);
+ processAllMessages();
assertEquals(Call.State.ALERTING, mCTUT.mForegroundCall.getState());
}
+ @Test
+ @SmallTest
+ public void testImsMoCallCrash() {
+ startOutgoingCall();
+ // Now fake the ImsService crashing
+ mCTUT.hangupAllOrphanedConnections(DisconnectCause.LOST_SIGNAL);
+ processAllMessages();
+ assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
+ try {
+ // ensure new calls are not blocked by any lingering state after crash.
+ mCTUT.checkForDialIssues();
+ } catch (CallStateException e) {
+ fail("checkForDialIssues should not generate a CallStateException: " + e.getMessage());
+ }
+ }
+
private void startOutgoingCall() {
assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
@@ -596,6 +680,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
ex.printStackTrace();
Assert.fail("unexpected exception thrown" + ex.getMessage());
}
+ processAllMessages();
assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
assertEquals(Call.State.DIALING, mCTUT.mForegroundCall.getState());
}
@@ -691,7 +776,6 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
public void testDialImsServiceUnavailable() throws ImsException {
doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
mImsManager).createCallProfile(anyInt(), anyInt());
- mCTUT.setRetryTimeout(() -> 0);
assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
@@ -702,12 +786,14 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
}
processAllMessages();
+
+ // Simulate ImsManager getting reconnected.
+ mConnectorListener.connectionReady(mImsManager);
verify(mImsManager, never()).makeCall(nullable(ImsCallProfile.class),
eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class));
// Make sure that open is called in ImsPhoneCallTracker when it was first connected and
// again after retry.
- verify(mImsManager, times(2)).open(
- nullable(MmTelFeature.Listener.class));
+ verify(mImsManager, times(2)).open(any(), any(), any());
}
@FlakyTest
@@ -718,16 +804,13 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
mImsManager).setUiTTYMode(nullable(Context.class), anyInt(),
nullable(Message.class));
- // Remove retry timeout delay
- mCTUT.setRetryTimeout(() -> 0); //ms
mCTUT.setUiTTYMode(0, new Message());
processAllMessages();
// Make sure that open is called in ImsPhoneCallTracker to re-establish connection to
// ImsService
- verify(mImsManager, times(2)).open(
- nullable(MmTelFeature.Listener.class));
+ verify(mImsManager, times(2)).open(any(), any(), any());
}
@Test
@@ -1066,19 +1149,13 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
@SmallTest
public void testNoHoldErrorMessageWhenCallDisconnected() {
when(mImsPhoneConnection.getImsCall()).thenReturn(mImsCall);
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocationOnMock) {
- fail("Error message showed when the call has already been disconnected!");
- return null;
- }
- }).when(mImsPhoneConnection)
- .onConnectionEvent(eq(android.telecom.Connection.EVENT_CALL_HOLD_FAILED), any());
mCTUT.getConnections().add(mImsPhoneConnection);
when(mImsPhoneConnection.getState()).thenReturn(ImsPhoneCall.State.DISCONNECTED);
- ImsReasonInfo info = new ImsReasonInfo(ImsReasonInfo.CODE_UNSPECIFIED,
+ final ImsReasonInfo info = new ImsReasonInfo(ImsReasonInfo.CODE_UNSPECIFIED,
ImsReasonInfo.CODE_UNSPECIFIED, null);
mCTUT.getImsCallListener().onCallHoldFailed(mImsPhoneConnection.getImsCall(), info);
+ verify(mImsPhoneConnection, never()).onConnectionEvent(
+ eq(android.telecom.Connection.EVENT_CALL_HOLD_FAILED), any());
}
@Test
@@ -1116,6 +1193,55 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
@Test
@SmallTest
+ public void testEndRingbackOnSrvcc() throws RemoteException {
+ mSecondImsCall.getCallProfile().mMediaProfile = new ImsStreamMediaProfile();
+ mSecondImsCall.getCallProfile().mMediaProfile.mAudioDirection =
+ ImsStreamMediaProfile.DIRECTION_INACTIVE;
+
+ startOutgoingCall();
+ mImsCallListener.onCallProgressing(mSecondImsCall);
+
+ assertTrue(mCTUT.mForegroundCall.isRingbackTonePlaying());
+
+ // Move the connection to the handover state.
+ mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+
+ assertFalse(mCTUT.mForegroundCall.isRingbackTonePlaying());
+ }
+
+ @Test
+ @SmallTest
+ public void testClearHoldSwapStateOnSrvcc() throws Exception {
+ // Answer an incoming call
+ testImsMTCall();
+ assertTrue(mCTUT.mRingingCall.isRinging());
+ try {
+ mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
+ verify(mImsCall, times(1)).accept(eq(ImsCallProfile
+ .getCallTypeFromVideoState(ImsCallProfile.CALL_TYPE_VOICE)));
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("set active, unexpected exception thrown" + ex.getMessage());
+ }
+ assertEquals(Call.State.ACTIVE, mCTUT.mForegroundCall.getState());
+ // Hold the call
+ doNothing().when(mImsCall).hold();
+ try {
+ mCTUT.holdActiveCall();
+ assertTrue(mCTUT.isHoldOrSwapInProgress());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ Assert.fail("hold, unexpected exception thrown" + ex.getMessage());
+ }
+
+ // Move the connection to the handover state.
+ mCTUT.notifySrvccState(Call.SrvccState.COMPLETED);
+ // Ensure we are no longer tracking hold.
+ assertFalse(mCTUT.isHoldOrSwapInProgress());
+ }
+
+ @Test
+ @SmallTest
public void testHangupHandoverCall() throws RemoteException {
doReturn("1").when(mImsCallSession).getCallId();
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
@@ -1177,6 +1303,84 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
}
+ /**
+ * Ensures when both RTP and SDP is supported that we register the expected header extension
+ * types.
+ * @throws Exception
+ */
+ @Test
+ @SmallTest
+ public void testConfigureRtpHeaderExtensionTypes() throws Exception {
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+ true);
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL,
+ true);
+ // Hacky but ImsPhoneCallTracker caches carrier config, so necessary.
+ mCTUT.updateCarrierConfigCache(mContextFixture.getCarrierConfigBundle());
+
+ ImsPhoneCallTracker.Config config = new ImsPhoneCallTracker.Config();
+ config.isD2DCommunicationSupported = true;
+ mCTUT.setConfig(config);
+ mConnectorListener.connectionReady(mImsManager);
+
+ // Expect to get offered header extensions since d2d is supported.
+ verify(mImsManager).setOfferedRtpHeaderExtensionTypes(
+ mRtpHeaderExtensionTypeCaptor.capture());
+ Set<RtpHeaderExtensionType> types = mRtpHeaderExtensionTypeCaptor.getValue();
+ assertEquals(2, types.size());
+ assertTrue(types.contains(RtpTransport.CALL_STATE_RTP_HEADER_EXTENSION_TYPE));
+ assertTrue(types.contains(RtpTransport.DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE));
+ }
+
+ /**
+ * Ensures when SDP is not supported (by RTP is) we don't register any extensions.
+ * @throws Exception
+ */
+ @Test
+ @SmallTest
+ public void testRtpButNoSdp() throws Exception {
+
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+ true);
+ mContextFixture.getCarrierConfigBundle().putBoolean(
+ CarrierConfigManager.KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL,
+ false);
+ // Hacky but ImsPhoneCallTracker caches carrier config, so necessary.
+ mCTUT.updateCarrierConfigCache(mContextFixture.getCarrierConfigBundle());
+
+ ImsPhoneCallTracker.Config config = new ImsPhoneCallTracker.Config();
+ config.isD2DCommunicationSupported = true;
+ mCTUT.setConfig(config);
+ mConnectorListener.connectionReady(mImsManager);
+
+ // Expect to get offered header extensions since d2d is supported.
+ verify(mImsManager).setOfferedRtpHeaderExtensionTypes(
+ mRtpHeaderExtensionTypeCaptor.capture());
+ Set<RtpHeaderExtensionType> types = mRtpHeaderExtensionTypeCaptor.getValue();
+ assertEquals(0, types.size());
+ }
+
+ /**
+ * Ensures when D2D communication is not supported that we don't register the D2D RTP header
+ * extension types.
+ * @throws Exception
+ */
+ @Test
+ @SmallTest
+ public void testDontConfigureRtpHeaderExtensionTypes() throws Exception {
+ ImsPhoneCallTracker.Config config = new ImsPhoneCallTracker.Config();
+ config.isD2DCommunicationSupported = false;
+ mCTUT.setConfig(config);
+ mConnectorListener.connectionReady(mImsManager);
+
+ // Expect no offered header extensions since d2d is not supported.
+ verify(mImsManager, never()).setOfferedRtpHeaderExtensionTypes(any());
+ }
+
private void assertVtDataUsageUpdated(int expectedToken, long rxBytes, long txBytes)
throws RemoteException {
final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass(
@@ -1195,7 +1399,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
if (rxBytes != 0 || txBytes != 0) {
expectedStats = expectedStats.addEntry(
- new Entry(NetworkStats.IFACE_VT, UID_ALL, SET_FOREGROUND,
+ new Entry(mCTUT.getVtInterface(), UID_ALL, SET_FOREGROUND,
TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, rxBytes, 0L,
txBytes, 0L, 0L));
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
index 205b92dc1e..5e98e5054f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.telephony.imsphone;
+import static android.telephony.ims.ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB;
+import static android.telephony.ims.ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB;
+
import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static org.junit.Assert.assertEquals;
@@ -27,8 +30,10 @@ import static org.mockito.Mockito.anyChar;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.os.AsyncResult;
import android.os.Bundle;
@@ -40,16 +45,21 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.ims.ImsCall;
+import com.android.ims.ImsException;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.GsmCdmaCall;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import org.junit.After;
import org.junit.Assert;
@@ -61,10 +71,15 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class ImsPhoneConnectionTest extends TelephonyTest {
+ private static final int TIMEOUT_MILLIS = 5000;
+
private ImsPhoneConnection mConnectionUT;
private Bundle mBundle = new Bundle();
@Mock
@@ -94,6 +109,18 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
@Test
@SmallTest
+ public void testNullExtras() {
+ mImsCallProfile.mCallExtras = null;
+ try {
+ mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall,
+ false);
+ } catch (NullPointerException npe) {
+ Assert.fail("Should not get NPE updating extras.");
+ }
+ }
+
+ @Test
+ @SmallTest
public void testImsIncomingConnectionCorrectness() {
logd("Testing initial state of MT ImsPhoneConnection");
mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
@@ -114,7 +141,7 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
logd("Testing initial state of MO ImsPhoneConnection");
mConnectionUT = new ImsPhoneConnection(mImsPhone, String.format("+1 (700).555-41NN%c1234",
- PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false);
+ PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false, false);
assertEquals(PhoneConstants.PRESENTATION_ALLOWED, mConnectionUT.getNumberPresentation());
assertEquals(PhoneConstants.PRESENTATION_ALLOWED, mConnectionUT.getCnapNamePresentation());
assertEquals("+1 (700).555-41NN,1234", mConnectionUT.getOrigDialString());
@@ -127,7 +154,7 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
public void testImsUpdateStateForeGround() {
// MO Foreground Connection dailing -> active
mConnectionUT = new ImsPhoneConnection(mImsPhone, "+1 (700).555-41NN1234", mImsCT,
- mForeGroundCall, false);
+ mForeGroundCall, false, false);
// initially in dialing state
doReturn(Call.State.DIALING).when(mForeGroundCall).getState();
assertTrue(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
@@ -139,6 +166,16 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
@Test
@SmallTest
+ public void testUpdateCodec() {
+ // MO Foreground Connection dailing -> active
+ mConnectionUT = new ImsPhoneConnection(mImsPhone, "+1 (700).555-41NN1234", mImsCT,
+ mForeGroundCall, false, false);
+ doReturn(Call.State.ACTIVE).when(mForeGroundCall).getState();
+ assertTrue(mConnectionUT.updateMediaCapabilities(mImsCall));
+ }
+
+ @Test
+ @SmallTest
public void testImsUpdateStateBackGround() {
// MT background Connection dialing -> active
mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mBackGroundCall, false);
@@ -155,7 +192,7 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
@SmallTest
public void testImsUpdateStatePendingHold() {
mConnectionUT = new ImsPhoneConnection(mImsPhone, "+1 (700).555-41NN1234", mImsCT,
- mForeGroundCall, false);
+ mForeGroundCall, false, false);
doReturn(true).when(mImsCall).isPendingHold();
assertFalse(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
verify(mForeGroundCall, times(0)).update(eq(mConnectionUT), eq(mImsCall),
@@ -200,7 +237,7 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
@SmallTest
public void testPostDialWait() {
mConnectionUT = new ImsPhoneConnection(mImsPhone, String.format("+1 (700).555-41NN%c1234",
- PhoneNumberUtils.WAIT), mImsCT, mForeGroundCall, false);
+ PhoneNumberUtils.WAIT), mImsCT, mForeGroundCall, false, false);
doReturn(Call.State.DIALING).when(mForeGroundCall).getState();
doAnswer(new Answer() {
@Override
@@ -223,7 +260,7 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
@MediumTest
public void testPostDialPause() {
mConnectionUT = new ImsPhoneConnection(mImsPhone, String.format("+1 (700).555-41NN%c1234",
- PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false);
+ PhoneNumberUtils.PAUSE), mImsCT, mForeGroundCall, false, false);
doReturn(Call.State.DIALING).when(mForeGroundCall).getState();
doAnswer(new Answer() {
@Override
@@ -351,7 +388,7 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
{"12345*00000", "12346", "12346"}};
for (String[] testAddress : testAddressMappingSet) {
mConnectionUT = new ImsPhoneConnection(mImsPhone, testAddress[0], mImsCT,
- mForeGroundCall, false);
+ mForeGroundCall, false, false);
mConnectionUT.setIsIncoming(true);
mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, testAddress[1]);
mConnectionUT.updateAddressDisplay(mImsCall);
@@ -369,7 +406,7 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
String updateAddress = "6789";
mConnectionUT = new ImsPhoneConnection(mImsPhone, inputAddress, mImsCT, mForeGroundCall,
- false);
+ false, false);
mConnectionUT.setIsIncoming(false);
mImsCallProfile.setCallExtra(ImsCallProfile.EXTRA_OI, updateAddress);
mConnectionUT.updateAddressDisplay(mImsCall);
@@ -391,4 +428,56 @@ public class ImsPhoneConnectionTest extends TelephonyTest {
assertEquals(android.telecom.Connection.VERIFICATION_STATUS_NOT_VERIFIED,
ImsPhoneConnection.toTelecomVerificationStatus(90210));
}
+
+ @Test
+ @SmallTest
+ public void testSetRedirectingAddress() {
+ mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mForeGroundCall, false);
+ ArrayList<String> forwardedNumber = new ArrayList<String>();
+ forwardedNumber.add("11111");
+ forwardedNumber.add("22222");
+ forwardedNumber.add("33333");
+
+ assertEquals(mConnectionUT.getForwardedNumber(), null);
+ mBundle.putStringArrayList(ImsCallProfile.EXTRA_FORWARDED_NUMBER, forwardedNumber);
+ assertTrue(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
+ assertEquals(forwardedNumber, mConnectionUT.getForwardedNumber());
+ }
+
+ @Test
+ @SmallTest
+ public void testReportMediaCodecChange() throws InterruptedException, ImsException {
+ ImsCall imsCall = mock(ImsCall.class);
+ ImsStreamMediaProfile mediaProfile = new ImsStreamMediaProfile();
+ ImsCallProfile profile = new ImsCallProfile();
+ profile.mMediaProfile = mediaProfile;
+ mediaProfile.mAudioQuality = AUDIO_QUALITY_AMR_WB;
+ when(imsCall.getLocalCallProfile()).thenReturn(profile);
+
+ // Blech; mocks required which are unrelated to this test
+ when(mImsCT.getPhone()).thenReturn(mImsPhone);
+ VoiceCallSessionStats stats = mock(VoiceCallSessionStats.class);
+ when(mImsPhone.getVoiceCallSessionStats()).thenReturn(stats);
+
+ mConnectionUT = new ImsPhoneConnection(mImsPhone, imsCall, mImsCT, mForeGroundCall, false);
+ mConnectionUT.setTelephonyMetrics(mock(TelephonyMetrics.class));
+ CountDownLatch latch = new CountDownLatch(1);
+ boolean[] receivedCountCallback = new boolean[1];
+ mConnectionUT.addListener(new Connection.ListenerBase() {
+ @Override
+ public void onMediaAttributesChanged() {
+ receivedCountCallback[0] = true;
+ latch.countDown();
+ }
+ });
+
+ mConnectionUT.updateMediaCapabilities(imsCall);
+
+ // Make an update to the media caps
+ mediaProfile.mAudioQuality = AUDIO_QUALITY_EVS_SWB;
+ mConnectionUT.updateMediaCapabilities(imsCall);
+
+ latch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ assertTrue(receivedCountCallback[0]);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
new file mode 100644
index 0000000000..aee46b728c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneMmiCodeTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.imsphone;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for the {@link ImsPhoneMmiCodeTest}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ImsPhoneMmiCodeTest extends TelephonyTest {
+ private static final String TEST_DIAL_STRING_SERVICE_CODE = "*67911";
+ private static final String TEST_DIAL_STRING_NO_SERVICE_CODE = "*767911";
+ private static final String TEST_DIAL_STRING_NON_EMERGENCY_NUMBER = "11976";
+ private ImsPhoneMmiCode mImsPhoneMmiCode;
+ private ImsPhone mImsPhoneUT;
+ @Mock private ServiceState mServiceState;
+
+ private Executor mExecutor = new Executor() {
+ @Override
+ public void execute(Runnable r) {
+ r.run();
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ MockitoAnnotations.initMocks(this);
+ doReturn(mExecutor).when(mContext).getMainExecutor();
+ doReturn(mPhone).when(mPhone).getDefaultPhone();
+ doReturn(mServiceState).when(mPhone).getServiceState();
+ doReturn(false).when(mServiceState).getVoiceRoaming();
+ doReturn(false).when(mPhone).supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming();
+ mImsPhoneUT = new ImsPhone(mContext, mNotifier, mPhone);
+ setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testIsTemporaryModeCLIRFromCarrierConfig() {
+ // Test *67911 is treated as temporary mode CLIR
+ doReturn(true).when(mTelephonyManager).isEmergencyNumber(TEST_DIAL_STRING_SERVICE_CODE);
+ mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(TEST_DIAL_STRING_SERVICE_CODE,
+ mImsPhoneUT);
+ assertTrue(mImsPhoneMmiCode.isTemporaryModeCLIR());
+ }
+
+ @Test
+ public void testIsTemporaryModeCLIRForNonServiceCode() {
+ // Test if prefix isn't *67 or *82
+ doReturn(true).when(mTelephonyManager).isEmergencyNumber(TEST_DIAL_STRING_NO_SERVICE_CODE);
+ mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(TEST_DIAL_STRING_NO_SERVICE_CODE,
+ mImsPhoneUT);
+ assertTrue(mImsPhoneMmiCode == null);
+ }
+
+ @Test
+ public void testIsTemporaryModeCLIRForNonEmergencyNumber() {
+ // Test if dialing string isn't an emergency number
+ mImsPhoneMmiCode = ImsPhoneMmiCode.newFromDialString(TEST_DIAL_STRING_NON_EMERGENCY_NUMBER,
+ mImsPhoneUT);
+ assertTrue(mImsPhoneMmiCode == null);
+ }
+
+ @Test
+ public void testNoCrashOnEmptyMessage() {
+ ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(null, true, mImsPhoneUT);
+ try {
+ mmi.onUssdFinishedError();
+ } catch (Exception e) {
+ fail("Shouldn't crash!!!");
+ }
+ }
+
+ private void setCarrierSupportsCallerIdVerticalServiceCodesCarrierConfig() {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager
+ .KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, true);
+ doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 0531d721b9..be037d9ef5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -16,6 +16,11 @@
package com.android.internal.telephony.imsphone;
+import static android.telephony.CarrierConfigManager.USSD_OVER_CS_ONLY;
+import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED;
+import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
+import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_PREFERRED;
+
import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
@@ -75,6 +80,7 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.imsphone.ImsPhone.SS;
import org.junit.After;
import org.junit.Before;
@@ -120,6 +126,7 @@ public class ImsPhoneTest extends TelephonyTest {
private static final int EVENT_SUPP_SERVICE_FAILED = 2;
private static final int EVENT_INCOMING_RING = 3;
private static final int EVENT_EMERGENCY_CALLBACK_MODE_EXIT = 4;
+ private static final int EVENT_CALL_RING_CONTINUE = 15;
private boolean mIsPhoneUtInEcm = false;
@@ -137,7 +144,7 @@ public class ImsPhoneTest extends TelephonyTest {
doReturn(true).when(mTelephonyManager).isVoiceCapable();
- mImsPhoneUT = new ImsPhone(mContext, mNotifier, mPhone, true);
+ mImsPhoneUT = new ImsPhone(mContext, mNotifier, mPhone, (c, p) -> mImsManager, true);
mDoesRilSendMultipleCallRing = TelephonyProperties.ril_sends_multiple_call_ring()
.orElse(true);
@@ -150,6 +157,7 @@ public class ImsPhoneTest extends TelephonyTest {
mImsPhoneUT.registerForIncomingRing(mTestHandler,
EVENT_INCOMING_RING, null);
mImsPhoneUT.setVoiceCallSessionStats(mVoiceCallSessionStats);
+ mImsPhoneUT.setImsStats(mImsStats);
doReturn(mImsUtInterface).when(mImsCT).getUtInterface();
// When the mock GsmCdmaPhone gets setIsInEcbm called, ensure isInEcm matches.
doAnswer(invocation -> {
@@ -475,11 +483,15 @@ public class ImsPhoneTest extends TelephonyTest {
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
verify(mImsUtInterface).queryCLIR(messageArgumentCaptor.capture());
- assertEquals(msg, messageArgumentCaptor.getValue().obj);
+ assertNotNull(messageArgumentCaptor.getValue().obj);
+ SS ss = (SS) messageArgumentCaptor.getValue().obj;
+ assertEquals(msg, ss.mOnComplete);
mImsPhoneUT.setOutgoingCallerIdDisplay(1234, msg);
verify(mImsUtInterface).updateCLIR(eq(1234), messageArgumentCaptor.capture());
- assertEquals(msg, messageArgumentCaptor.getValue().obj);
+ assertNotNull(messageArgumentCaptor.getValue().obj);
+ ss = (SS) messageArgumentCaptor.getValue().obj;
+ assertEquals(msg, ss.mOnComplete);
}
@FlakyTest
@@ -509,12 +521,16 @@ public class ImsPhoneTest extends TelephonyTest {
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
verify(mImsUtInterface).queryCallWaiting(messageArgumentCaptor.capture());
- assertEquals(msg, messageArgumentCaptor.getValue().obj);
+ assertNotNull(messageArgumentCaptor.getValue().obj);
+ SS ss = (SS) messageArgumentCaptor.getValue().obj;
+ assertEquals(msg, ss.mOnComplete);
mImsPhoneUT.setCallWaiting(true, msg);
verify(mImsUtInterface).updateCallWaiting(eq(true),
eq(CommandsInterface.SERVICE_CLASS_VOICE), messageArgumentCaptor.capture());
- assertEquals(msg, messageArgumentCaptor.getValue().obj);
+ assertNotNull(messageArgumentCaptor.getValue().obj);
+ ss = (SS) messageArgumentCaptor.getValue().obj;
+ assertEquals(msg, ss.mOnComplete);
}
@Test
@@ -545,14 +561,18 @@ public class ImsPhoneTest extends TelephonyTest {
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
verify(mImsUtInterface).queryCallBarring(eq(ImsUtImplBase.CALL_BARRING_ALL_OUTGOING),
messageArgumentCaptor.capture(), eq(CommandsInterface.SERVICE_CLASS_NONE));
- assertEquals(msg, messageArgumentCaptor.getValue().obj);
+ assertNotNull(messageArgumentCaptor.getValue().obj);
+ SS ss = (SS) messageArgumentCaptor.getValue().obj;
+ assertEquals(msg, ss.mOnComplete);
mImsPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOIC, true, "abc", msg,
CommandsInterface.SERVICE_CLASS_NONE);
verify(mImsUtInterface).updateCallBarring(eq(ImsUtImplBase.CALL_BARRING_OUTGOING_INTL),
eq(CommandsInterface.CF_ACTION_ENABLE), messageArgumentCaptor.capture(),
(String[]) eq(null), eq(CommandsInterface.SERVICE_CLASS_NONE), eq("abc"));
- assertEquals(msg, messageArgumentCaptor.getValue().obj);
+ assertNotNull(messageArgumentCaptor.getValue().obj);
+ ss = (SS) messageArgumentCaptor.getValue().obj;
+ assertEquals(msg, ss.mOnComplete);
mImsPhoneUT.setCallBarring(CommandsInterface.CB_FACILITY_BAOICxH, false, "abc", msg,
CommandsInterface.SERVICE_CLASS_NONE);
@@ -560,7 +580,9 @@ public class ImsPhoneTest extends TelephonyTest {
eq(ImsUtImplBase.CALL_BARRING_OUTGOING_INTL_EXCL_HOME),
eq(CommandsInterface.CF_ACTION_DISABLE), messageArgumentCaptor.capture(),
(String[]) eq(null), eq(CommandsInterface.SERVICE_CLASS_NONE), eq("abc"));
- assertEquals(msg, messageArgumentCaptor.getValue().obj);
+ assertNotNull(messageArgumentCaptor.getValue().obj);
+ ss = (SS) messageArgumentCaptor.getValue().obj;
+ assertEquals(msg, ss.mOnComplete);
}
@Test
@@ -646,7 +668,7 @@ public class ImsPhoneTest extends TelephonyTest {
@SmallTest
public void testImsRegistered() throws Exception {
mImsPhoneUT.setServiceState(ServiceState.STATE_IN_SERVICE);
- mImsPhoneUT.setImsRegistrationState(RegistrationManager.REGISTRATION_STATE_REGISTERED);
+ mImsPhoneUT.setImsRegistered(true);
assertTrue(mImsPhoneUT.isImsRegistered());
LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
@@ -658,23 +680,9 @@ public class ImsPhoneTest extends TelephonyTest {
@Test
@SmallTest
- public void testImsRegistering() throws Exception {
- mImsPhoneUT.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
- mImsPhoneUT.setImsRegistrationState(RegistrationManager.REGISTRATION_STATE_REGISTERING);
- assertFalse(mImsPhoneUT.isImsRegistered());
-
- LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
- mImsPhoneUT.getImsRegistrationState(result::offer);
- Integer regResult = result.poll(1000, TimeUnit.MILLISECONDS);
- assertNotNull(regResult);
- assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERING, regResult.intValue());
- }
-
- @Test
- @SmallTest
public void testImsDeregistered() throws Exception {
mImsPhoneUT.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
- mImsPhoneUT.setImsRegistrationState(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
+ mImsPhoneUT.setImsRegistered(false);
assertFalse(mImsPhoneUT.isImsRegistered());
LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
@@ -704,6 +712,7 @@ public class ImsPhoneTest extends TelephonyTest {
@SmallTest
public void testRoamingDuplicateMessages() throws Exception {
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
+ doReturn(true).when(mPhone).isRadioOn();
//roaming - data registration only on LTE
Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
@@ -721,12 +730,40 @@ public class ImsPhoneTest extends TelephonyTest {
verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
}
+ @Test
+ @SmallTest
+ public void testRoamingToAirplanModeIwlanInService() throws Exception {
+ doReturn(true).when(mTransportManager).isInLegacyMode();
+ doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
+ doReturn(true).when(mPhone).isRadioOn();
+
+ //roaming - data registration only on LTE
+ Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.STATE_IN_SERVICE, true));
+ // Inject the message synchronously instead of waiting for the thread to do it.
+ mImsPhoneUT.handleMessage(m);
+ m.recycle();
+
+ verify(mImsManager, times(1)).setWfcMode(anyInt(), eq(true));
+
+ // move to airplane mode + IWLAN in service
+ doReturn(false).when(mPhone).isRadioOn();
+ m = getServiceStateChangedMessage(getServiceStateDataOnly(
+ ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.STATE_IN_SERVICE, false));
+ mImsPhoneUT.handleMessage(m);
+ m.recycle();
+
+ // setWfcMode should not be called again, airplane mode should not trigger move out of
+ // roaming.
+ verify(mImsManager, times(1)).setWfcMode(anyInt(), anyBoolean());
+ }
@Test
@SmallTest
public void testRoamingToOutOfService() throws Exception {
doReturn(true).when(mTransportManager).isInLegacyMode();
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
+ doReturn(true).when(mPhone).isRadioOn();
//roaming - data registration only on LTE
Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
@@ -752,6 +789,7 @@ public class ImsPhoneTest extends TelephonyTest {
public void testRoamingChangeForLteInLegacyMode() throws Exception {
doReturn(true).when(mTransportManager).isInLegacyMode();
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
+ doReturn(true).when(mPhone).isRadioOn();
//roaming - data registration only on LTE
Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
@@ -776,6 +814,7 @@ public class ImsPhoneTest extends TelephonyTest {
public void testDataOnlyRoamingCellToIWlanInLegacyMode() throws Exception {
doReturn(true).when(mTransportManager).isInLegacyMode();
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
+ doReturn(true).when(mPhone).isRadioOn();
//roaming - data registration only on LTE
Message m = getServiceStateChangedMessage(getServiceStateDataOnly(
@@ -801,6 +840,7 @@ public class ImsPhoneTest extends TelephonyTest {
public void testCellVoiceDataChangeToWlanInLegacyMode() throws Exception {
doReturn(true).when(mTransportManager).isInLegacyMode();
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
+ doReturn(true).when(mPhone).isRadioOn();
//roaming - voice/data registration on LTE
ServiceState ss = getServiceStateDataAndVoice(
@@ -832,6 +872,8 @@ public class ImsPhoneTest extends TelephonyTest {
public void testSendUssdAllowUssdOverImsInOutOfService() throws Exception {
Resources resources = mContext.getResources();
+ mBundle.putInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT,
+ USSD_OVER_CS_PREFERRED);
doReturn(true).when(resources).getBoolean(
com.android.internal.R.bool.config_allow_ussd_over_ims);
doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getState();
@@ -846,6 +888,8 @@ public class ImsPhoneTest extends TelephonyTest {
String errorCode = "";
Resources resources = mContext.getResources();
+ mBundle.putInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT,
+ USSD_OVER_CS_PREFERRED);
doReturn(true).when(resources).getBoolean(
com.android.internal.R.bool.config_allow_ussd_over_ims);
doReturn(ServiceState.STATE_IN_SERVICE).when(mSST.mSS).getState();
@@ -875,6 +919,63 @@ public class ImsPhoneTest extends TelephonyTest {
assertEquals(Phone.CS_FALLBACK, errorCode);
}
+ @Test
+ @SmallTest
+ public void testSendUssdAllowUssdOverImswithIMSPreferred() throws Exception {
+ Resources resources = mContext.getResources();
+
+ mBundle.putInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT,
+ USSD_OVER_IMS_PREFERRED);
+ doReturn(true).when(resources).getBoolean(
+ com.android.internal.R.bool.config_allow_ussd_over_ims);
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mSST.mSS).getState();
+
+ mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
+ verify(mImsCT).sendUSSD(eq("*135#"), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendUssdAllowUssdOverImswithCSOnly() throws Exception {
+ String errorCode = "";
+ Resources resources = mContext.getResources();
+
+ mBundle.putInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT,
+ USSD_OVER_CS_ONLY);
+ doReturn(true).when(resources).getBoolean(
+ com.android.internal.R.bool.config_allow_ussd_over_ims);
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mSST.mSS).getState();
+
+ try {
+ mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
+ } catch (CallStateException e) {
+ errorCode = e.getMessage();
+ }
+ assertEquals(Phone.CS_FALLBACK, errorCode);
+ }
+
+ @Test
+ @SmallTest
+ public void testSendUssdAllowUssdOverImswithIMSOnly() throws Exception {
+ Resources resources = mContext.getResources();
+
+ mBundle.putInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT,
+ USSD_OVER_IMS_ONLY);
+ doReturn(true).when(resources).getBoolean(
+ com.android.internal.R.bool.config_allow_ussd_over_ims);
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mSST.mSS).getState();
+
+ mImsPhoneUT.dial("*135#", new ImsPhone.ImsDialArgs.Builder().build());
+ verify(mImsCT).sendUSSD(eq("*135#"), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testHandleMessageCallRingContinue() throws Exception {
+ Message m = Message.obtain(mImsPhoneUT.getHandler(), EVENT_CALL_RING_CONTINUE);
+ mImsPhoneUT.handleMessage(m);
+ }
+
private ServiceState getServiceStateDataAndVoice(int rat, int regState, boolean isRoaming) {
ServiceState ss = new ServiceState();
ss.setStateOutOfService();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
new file mode 100644
index 0000000000..c2b690f92c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.UiccSlot;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.Map;
+
+public class ImsStatsTest extends TelephonyTest {
+ private static final long START_TIME_MILLIS = 2000L;
+ private static final int CARRIER1_ID = 1;
+ private static final int CARRIER2_ID = 1187;
+
+ @MmTelCapability
+ private static final int CAPABILITY_TYPE_ALL =
+ MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ | MmTelCapabilities.CAPABILITY_TYPE_VIDEO
+ | MmTelCapabilities.CAPABILITY_TYPE_SMS
+ | MmTelCapabilities.CAPABILITY_TYPE_UT;
+
+ @Mock private UiccSlot mPhysicalSlot0;
+ @Mock private UiccSlot mPhysicalSlot1;
+ @Mock private Phone mSecondPhone;
+ @Mock private ImsPhone mSecondImsPhone;
+
+ private TestableImsStats mImsStats;
+
+ private static class TestableImsStats extends ImsStats {
+ private long mTimeMillis = START_TIME_MILLIS;
+
+ TestableImsStats(ImsPhone imsPhone) {
+ super(imsPhone);
+ }
+
+ @Override
+ protected long getTimeMillis() {
+ // NOTE: super class constructor will be executed before private field is set, which
+ // gives the wrong start time (mTimeMillis will have its default value of 0L)
+ return mTimeMillis == 0L ? START_TIME_MILLIS : mTimeMillis;
+ }
+
+ private void setTimeMillis(long timeMillis) {
+ mTimeMillis = timeMillis;
+ }
+
+ private void incTimeMillis(long timeMillis) {
+ mTimeMillis += timeMillis;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ doReturn(CARRIER1_ID).when(mPhone).getCarrierId();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(mSST).when(mImsPhone).getServiceStateTracker();
+
+ // WWAN PS RAT is LTE
+ doReturn(
+ new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build())
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+
+ // Single physical SIM
+ doReturn(true).when(mPhysicalSlot0).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot0).getCardState();
+ doReturn(false).when(mPhysicalSlot0).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0}).when(mUiccController).getUiccSlots();
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlot(0);
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlotForPhone(0);
+
+ mImsStats = new TestableImsStats(mImsPhone);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_registered() throws Exception {
+ // IMS over LTE
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_SMS,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_ALL));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // Duration should be counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(2000L, stats.voiceAvailableMillis);
+ assertEquals(2000L, stats.videoCapableMillis);
+ assertEquals(2000L, stats.videoAvailableMillis);
+ assertEquals(2000L, stats.utCapableMillis);
+ assertEquals(2000L, stats.utAvailableMillis);
+ assertEquals(2000L, stats.smsCapableMillis);
+ assertEquals(2000L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_registeredPartialFeatures() throws Exception {
+ // IMS over LTE
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_SMS,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // Duration should be counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(2000L, stats.voiceAvailableMillis);
+ assertEquals(2000L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(2000L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(2000L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_registeredVoiceOnly() throws Exception {
+ // Wifi calling
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_IWLAN,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WLAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_IWLAN, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // Duration should be counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(2000L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_notRegistered() throws Exception {
+ // IMS over LTE
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_SMS,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_ALL));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // No atom should be generated
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsCapabilitiesChanged_sameTech() throws Exception {
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+ // Atom with previous feature availability should be generated
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onSetFeatureResponse_sameTech() throws Exception {
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+
+ // Atom with previous capability should be generated
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsRegistered_differentTech() throws Exception {
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WLAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+
+ // At this point, the first 2 registrations should have their durations counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage, times(2)).addImsRegistrationStats(captor.capture());
+ assertEquals(2, captor.getAllValues().size());
+ ImsRegistrationStats statsLte = captor.getAllValues().get(0);
+ assertEquals(CARRIER1_ID, statsLte.carrierId);
+ assertEquals(0, statsLte.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, statsLte.rat);
+ assertEquals(2000L, statsLte.registeredMillis);
+ assertEquals(0L, statsLte.voiceCapableMillis);
+ assertEquals(0L, statsLte.voiceAvailableMillis);
+ assertEquals(0L, statsLte.videoCapableMillis);
+ assertEquals(0L, statsLte.videoAvailableMillis);
+ assertEquals(0L, statsLte.utCapableMillis);
+ assertEquals(0L, statsLte.utAvailableMillis);
+ assertEquals(0L, statsLte.smsCapableMillis);
+ assertEquals(0L, statsLte.smsAvailableMillis);
+ ImsRegistrationStats statsWifi = captor.getAllValues().get(1);
+ assertEquals(CARRIER1_ID, statsWifi.carrierId);
+ assertEquals(0, statsWifi.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, statsWifi.rat);
+ assertEquals(2000L, statsWifi.registeredMillis);
+ assertEquals(0L, statsWifi.voiceCapableMillis);
+ assertEquals(0L, statsWifi.voiceAvailableMillis);
+ assertEquals(0L, statsWifi.videoCapableMillis);
+ assertEquals(0L, statsWifi.videoAvailableMillis);
+ assertEquals(0L, statsWifi.utCapableMillis);
+ assertEquals(0L, statsWifi.utAvailableMillis);
+ assertEquals(0L, statsWifi.smsCapableMillis);
+ assertEquals(0L, statsWifi.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_setupFailure() throws Exception {
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info should be generated
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_setupFailureWithProgress() throws Exception {
+ mImsStats.onImsRegistering(REGISTRATION_TECH_LTE);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info should be generated
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_afterRegistered() throws Exception {
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info and durations should be generated
+ ArgumentCaptor<ImsRegistrationStats> statsCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(statsCaptor.capture());
+ ImsRegistrationStats stats = statsCaptor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(0L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ ArgumentCaptor<ImsRegistrationTermination> terminationCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(terminationCaptor.capture());
+ ImsRegistrationTermination termination = terminationCaptor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+ assertFalse(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_nullMessage() throws Exception {
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0, null));
+
+ // Atom with termination info should be generated, null string should be sanitized
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(0, termination.extraCode);
+ assertEquals("", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_longMessage() throws Exception {
+ String longExtraMessage =
+ "This message is too long -- it has more than 128 characters: "
+ + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+ + " This is the end of the message.";
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0, longExtraMessage));
+
+ // Atom with termination info should be generated, null string should be sanitized
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(0, termination.extraCode);
+ assertEquals(128, termination.extraMessage.length());
+ assertTrue(longExtraMessage.startsWith(termination.extraMessage));
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void filterExtraMessage_noNeedToFilter() throws Exception {
+ final String[] messages = {
+ "Q.850;cause=16",
+ "SIP;cause=200",
+ "q.850",
+ "600",
+ "Call ended during conference merge process.",
+ "cc_term_noreply_tmr_expired",
+ "[e] user triggered",
+ "normal end of call",
+ "cc_q850_017_user_busy",
+ "service unavailable (1:223)",
+ "IP Change",
+ "rtp-rtcp timeout",
+ "0x0000030b",
+ "CD-021: ISP Problem",
+ "IP;cause=487;text=\"Originator canceled;Canceled(t),iCode=CC_SIP_REQUEST_TERMINATED\"",
+ "pt: asr: insufficient_bearer_resources",
+ "po: aaa: result_code=0 exp_result_code=5065",
+ "a peer released.total external peer:1. allowed:2. clear the remain parties(o),"
+ + "icode=cc_sip_request_timeout"
+ };
+
+ for (String message : messages) {
+ assertEquals(message, ImsStats.filterExtraMessage(message));
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void filterExtraMessage_needToFilter() throws Exception {
+ Map<String, String> originalAndExpectedMessages = ImmutableMap.<String, String>builder()
+ // UUIDs
+ .put(
+ "Q.850;cause=34;text=\"12345678-abcd-ef12-34ab-000000000012;"
+ + "User is busy and currently active on another call.\"",
+ "Q.850;cause=34;text=\"<UUID_REDACTED>;"
+ + "User is busy and currently active on another call.\"")
+ .put(
+ "Q.850;cause=34;text=\"12345678-ABCD-EF12-34AB-000000000012;"
+ + "User is busy and currently active on another call.\"",
+ "Q.850;cause=34;text=\"<UUID_REDACTED>;"
+ + "User is busy and currently active on another call.\"")
+ // URIs
+ .put(
+ "SIP;cause=500;text=\"sip:+1234567890@irps.voip.telefonica.de;user=phone"
+ + " clear the call.;Canceled(t)\"",
+ "SIP;cause=500;text=\"sip:<REDACTED>;user=phone"
+ + " clear the call.;Canceled(t)\"")
+ .put(
+ "SIP;cause=500;text=\"SIP:+1234567890@irps.voip.telefonica.de;user=phone"
+ + " clear the call.;Canceled(t)\"",
+ "SIP;cause=500;text=\"SIP:<REDACTED>;user=phone"
+ + " clear the call.;Canceled(t)\"")
+ // IP addresses
+ .put(
+ "dtls handshake error[timeout][2607:F8B0::1] and client disconnected",
+ "dtls handshake error[timeout][<IPV6_REDACTED>] and client disconnected")
+ .put(
+ "dtls handshake error[timeout][2607:f8b0::1] and client disconnected",
+ "dtls handshake error[timeout][<IPV6_REDACTED>] and client disconnected")
+ .put(
+ "dtls handshake error 2607:f8b0:1:2:3:4:56:789",
+ "dtls handshake error <IPV6_REDACTED>")
+ .put(
+ "dtls handshake error[timeout][8.8.8.8] and client disconnected",
+ "dtls handshake error[timeout][<IPV4_REDACTED>] and client disconnected")
+ .put("8.8.8.8 client disconnected", "<IPV4_REDACTED> client disconnected")
+ // IMEIs/IMSIs
+ .put(
+ "call completed elsewhere by instance 313460000000001",
+ "call completed elsewhere by instance <IMEI_IMSI_REDACTED>")
+ .put(
+ "call completed elsewhere by instance 31346000-000000-1",
+ "call completed elsewhere by instance <IMEI_REDACTED>")
+ .put(
+ "call completed elsewhere by instance 31-346000-000000-1",
+ "call completed elsewhere by instance <IMEI_REDACTED>")
+ .put(
+ "call completed elsewhere by instance 31-346000-000000-12",
+ "call completed elsewhere by instance <IMEI_REDACTED>")
+ .put(
+ "399 123.4567.89.ATS.blah.ims.mnc123.mcc456.3gppnetwork.org"
+ + " \"Failure cause code is sip status code.\"",
+ "399 <HOSTNAME_REDACTED> \"Failure cause code is sip status code.\"")
+ // Unknown IDs
+ .put(
+ "01200.30004.a.560.789.123.0.0.00045.00000006"
+ + " released the session because of netfail by no media",
+ // "123.0.0.0" looks like IPv4
+ "<ID_REDACTED><IPV4_REDACTED><ID_REDACTED>"
+ + " released the session because of netfail by no media")
+ .put(
+ "example.cpp,1234,12-300-450-67-89123456:-12345678,"
+ + "tyringtimeout:timer b expired(t)",
+ "example.cpp,1234,<ID_REDACTED>:-<ID_REDACTED>,"
+ + "tyringtimeout:timer b expired(t)")
+ .put(
+ "ss120000f123l1234 invite 2xx after cancel rsp has been received",
+ "ss<ID_REDACTED>l1234 invite 2xx after cancel rsp has been received")
+ .put(
+ "X.int;reasoncode=0x00000123;add-info=0123.00AB.0001",
+ "X.int;reasoncode=0x00000123;add-info=<ID_REDACTED>")
+ .put(
+ "X.int;reasoncode=0x00123abc;add-info=0123.00AB.0001",
+ "X.int;reasoncode=0x<ID_REDACTED>;add-info=<ID_REDACTED>")
+ .put(
+ "Cx Unable To Comply 1203045067D8009",
+ "Cx Unable To Comply <ID_REDACTED>")
+ .build();
+
+ for (Map.Entry<String, String> entry : originalAndExpectedMessages.entrySet()) {
+ assertEquals(entry.getValue(), ImsStats.filterExtraMessage(entry.getKey()));
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_multiSim() throws Exception {
+ doReturn(mSecondImsPhone).when(mSecondPhone).getImsPhone();
+ doReturn(mSecondPhone).when(mSecondImsPhone).getDefaultPhone();
+ doReturn(1).when(mSecondPhone).getPhoneId();
+ doReturn(1).when(mSecondImsPhone).getPhoneId();
+ doReturn(CARRIER2_ID).when(mSecondPhone).getCarrierId();
+ doReturn(true).when(mPhysicalSlot1).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot1).getCardState();
+ doReturn(false).when(mPhysicalSlot1).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0, mPhysicalSlot1})
+ .when(mUiccController)
+ .getUiccSlots();
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlot(1);
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlotForPhone(1);
+ // Reusing service state tracker from phone 0 for simplicity
+ doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
+ doReturn(mSST).when(mSecondImsPhone).getServiceStateTracker();
+ mImsStats = new TestableImsStats(mSecondImsPhone);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info and durations should be generated
+ ArgumentCaptor<ImsRegistrationStats> statsCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(statsCaptor.capture());
+ ImsRegistrationStats stats = statsCaptor.getValue();
+ assertEquals(CARRIER2_ID, stats.carrierId);
+ assertEquals(1, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(0L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ ArgumentCaptor<ImsRegistrationTermination> terminationCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(terminationCaptor.capture());
+ ImsRegistrationTermination termination = terminationCaptor.getValue();
+ assertEquals(CARRIER2_ID, termination.carrierId);
+ assertTrue(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+ assertFalse(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index 103cae1dce..cd156867bc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.metrics;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
+import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
@@ -38,7 +40,9 @@ import android.util.StatsEvent;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.UiccCard;
@@ -82,6 +86,8 @@ public class MetricsCollectorTest extends TelephonyTest {
@Mock private UiccSlot mEsimSlot;
@Mock private UiccCard mActiveCard;
+ @Mock private ServiceStateStats mServiceStateStats;
+
private MetricsCollector mMetricsCollector;
@Before
@@ -89,6 +95,8 @@ public class MetricsCollectorTest extends TelephonyTest {
super.setUp(getClass().getSimpleName());
mMetricsCollector = new MetricsCollector(mContext);
mMetricsCollector.setPersistAtomsStorage(mPersistAtomsStorage);
+ doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
+ doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
}
@After
@@ -216,7 +224,7 @@ public class MetricsCollectorTest extends TelephonyTest {
@Test
@SmallTest
public void onPullAtom_voiceCallRatUsage_empty() throws Exception {
- doReturn(new RawVoiceCallRatUsage[0])
+ doReturn(new VoiceCallRatUsage[0])
.when(mPersistAtomsStorage)
.getVoiceCallRatUsages(anyLong());
List<StatsEvent> actualAtoms = new ArrayList<>();
@@ -244,11 +252,11 @@ public class MetricsCollectorTest extends TelephonyTest {
@Test
@SmallTest
public void onPullAtom_voiceCallRatUsage_bucketWithTooFewCalls() throws Exception {
- RawVoiceCallRatUsage usage1 = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage1 = new VoiceCallRatUsage();
usage1.callCount = MIN_CALLS_PER_BUCKET;
- RawVoiceCallRatUsage usage2 = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage2 = new VoiceCallRatUsage();
usage2.callCount = MIN_CALLS_PER_BUCKET - 1L;
- doReturn(new RawVoiceCallRatUsage[] {usage1, usage1, usage1, usage2})
+ doReturn(new VoiceCallRatUsage[] {usage1, usage1, usage1, usage2})
.when(mPersistAtomsStorage)
.getVoiceCallRatUsages(anyLong());
List<StatsEvent> actualAtoms = new ArrayList<>();
@@ -303,4 +311,95 @@ public class MetricsCollectorTest extends TelephonyTest {
assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
// TODO(b/153196254): verify atom contents
}
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularDataServiceSwitch_empty() throws Exception {
+ doReturn(new CellularDataServiceSwitch[0])
+ .when(mPersistAtomsStorage)
+ .getCellularDataServiceSwitches(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularDataServiceSwitch_tooFrequent() throws Exception {
+ doReturn(null).when(mPersistAtomsStorage).getCellularDataServiceSwitches(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+ verify(mPersistAtomsStorage, times(1))
+ .getCellularDataServiceSwitches(eq(MIN_COOLDOWN_MILLIS));
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularDataServiceSwitch_multipleSwitches() throws Exception {
+ CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
+ doReturn(new CellularDataServiceSwitch[] {serviceSwitch, serviceSwitch, serviceSwitch})
+ .when(mPersistAtomsStorage)
+ .getCellularDataServiceSwitches(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(3);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularServiceState_empty() throws Exception {
+ doReturn(new CellularServiceState[0])
+ .when(mPersistAtomsStorage)
+ .getCellularServiceStates(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularServiceState_tooFrequent() throws Exception {
+ doReturn(null).when(mPersistAtomsStorage).getCellularServiceStates(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+ verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(eq(MIN_COOLDOWN_MILLIS));
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularServiceState_multipleStates() throws Exception {
+ CellularServiceState state = new CellularServiceState();
+ doReturn(new CellularServiceState[] {state, state, state})
+ .when(mPersistAtomsStorage)
+ .getCellularServiceStates(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(3);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index ac1d73f886..df158c1551 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -37,14 +37,20 @@ import static org.mockito.Mockito.times;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Build;
import android.telephony.DisconnectCause;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec;
import com.android.internal.telephony.protobuf.nano.MessageNano;
@@ -63,6 +69,8 @@ import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.Queue;
public class PersistAtomsStorageTest extends TelephonyTest {
private static final String TEST_FILE = "PersistAtomsStorageTest.pb";
@@ -88,14 +96,39 @@ public class PersistAtomsStorageTest extends TelephonyTest {
// failed call
private VoiceCallSession mCall4Proto;
- private RawVoiceCallRatUsage mCarrier1LteUsageProto;
- private RawVoiceCallRatUsage mCarrier1UmtsUsageProto;
- private RawVoiceCallRatUsage mCarrier2LteUsageProto;
- private RawVoiceCallRatUsage mCarrier3LteUsageProto;
- private RawVoiceCallRatUsage mCarrier3GsmUsageProto;
+ private VoiceCallRatUsage mCarrier1LteUsageProto;
+ private VoiceCallRatUsage mCarrier1UmtsUsageProto;
+ private VoiceCallRatUsage mCarrier2LteUsageProto;
+ private VoiceCallRatUsage mCarrier3LteUsageProto;
+ private VoiceCallRatUsage mCarrier3GsmUsageProto;
private VoiceCallSession[] mVoiceCallSessions;
- private RawVoiceCallRatUsage[] mVoiceCallRatUsages;
+ private VoiceCallRatUsage[] mVoiceCallRatUsages;
+
+ // data service state switch for slot 0 and 1
+ private CellularDataServiceSwitch mServiceSwitch1Proto;
+ private CellularDataServiceSwitch mServiceSwitch2Proto;
+
+ // service states for slot 0 and 1
+ private CellularServiceState mServiceState1Proto;
+ private CellularServiceState mServiceState2Proto;
+ private CellularServiceState mServiceState3Proto;
+ private CellularServiceState mServiceState4Proto;
+
+ private CellularDataServiceSwitch[] mServiceSwitches;
+ private CellularServiceState[] mServiceStates;
+
+ // IMS registrations for slot 0 and 1
+ private ImsRegistrationStats mImsRegistrationStatsLte0;
+ private ImsRegistrationStats mImsRegistrationStatsWifi0;
+ private ImsRegistrationStats mImsRegistrationStatsLte1;
+
+ // IMS registration terminations for slot 0 and 1
+ private ImsRegistrationTermination mImsRegistrationTerminationLte;
+ private ImsRegistrationTermination mImsRegistrationTerminationWifi;
+
+ private ImsRegistrationStats[] mImsRegistrationStats;
+ private ImsRegistrationTermination[] mImsRegistrationTerminations;
private void makeTestData() {
// MO call with SRVCC (LTE to UMTS)
@@ -213,38 +246,38 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mCall4Proto.isEmergency = false;
mCall4Proto.isRoaming = true;
- mCarrier1LteUsageProto = new RawVoiceCallRatUsage();
+ mCarrier1LteUsageProto = new VoiceCallRatUsage();
mCarrier1LteUsageProto.carrierId = CARRIER1_ID;
mCarrier1LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE;
mCarrier1LteUsageProto.callCount = 1L;
mCarrier1LteUsageProto.totalDurationMillis = 8000L;
- mCarrier1UmtsUsageProto = new RawVoiceCallRatUsage();
+ mCarrier1UmtsUsageProto = new VoiceCallRatUsage();
mCarrier1UmtsUsageProto.carrierId = CARRIER1_ID;
mCarrier1UmtsUsageProto.rat = TelephonyManager.NETWORK_TYPE_UMTS;
mCarrier1UmtsUsageProto.callCount = 1L;
mCarrier1UmtsUsageProto.totalDurationMillis = 6000L;
- mCarrier2LteUsageProto = new RawVoiceCallRatUsage();
+ mCarrier2LteUsageProto = new VoiceCallRatUsage();
mCarrier2LteUsageProto.carrierId = CARRIER2_ID;
mCarrier2LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE;
mCarrier2LteUsageProto.callCount = 2L;
mCarrier2LteUsageProto.totalDurationMillis = 20000L;
- mCarrier3LteUsageProto = new RawVoiceCallRatUsage();
+ mCarrier3LteUsageProto = new VoiceCallRatUsage();
mCarrier3LteUsageProto.carrierId = CARRIER3_ID;
mCarrier3LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE;
mCarrier3LteUsageProto.callCount = 1L;
mCarrier3LteUsageProto.totalDurationMillis = 1000L;
- mCarrier3GsmUsageProto = new RawVoiceCallRatUsage();
+ mCarrier3GsmUsageProto = new VoiceCallRatUsage();
mCarrier3GsmUsageProto.carrierId = CARRIER3_ID;
mCarrier3GsmUsageProto.rat = TelephonyManager.NETWORK_TYPE_GSM;
mCarrier3GsmUsageProto.callCount = 1L;
mCarrier3GsmUsageProto.totalDurationMillis = 100000L;
mVoiceCallRatUsages =
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
mCarrier1UmtsUsageProto,
mCarrier1LteUsageProto,
mCarrier2LteUsageProto,
@@ -253,6 +286,152 @@ public class PersistAtomsStorageTest extends TelephonyTest {
};
mVoiceCallSessions =
new VoiceCallSession[] {mCall1Proto, mCall2Proto, mCall3Proto, mCall4Proto};
+
+ // OOS to LTE on slot 0
+ mServiceSwitch1Proto = new CellularDataServiceSwitch();
+ mServiceSwitch1Proto.ratFrom = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ mServiceSwitch1Proto.ratTo = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceSwitch1Proto.simSlotIndex = 0;
+ mServiceSwitch1Proto.isMultiSim = true;
+ mServiceSwitch1Proto.carrierId = CARRIER1_ID;
+ mServiceSwitch1Proto.switchCount = 1;
+
+ // LTE to UMTS on slot 1
+ mServiceSwitch2Proto = new CellularDataServiceSwitch();
+ mServiceSwitch2Proto.ratFrom = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceSwitch2Proto.ratTo = TelephonyManager.NETWORK_TYPE_UMTS;
+ mServiceSwitch2Proto.simSlotIndex = 0;
+ mServiceSwitch2Proto.isMultiSim = true;
+ mServiceSwitch2Proto.carrierId = CARRIER2_ID;
+ mServiceSwitch2Proto.switchCount = 2;
+
+ // OOS on slot 0
+ mServiceState1Proto = new CellularServiceState();
+ mServiceState1Proto.voiceRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ mServiceState1Proto.dataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ mServiceState1Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState1Proto.dataRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState1Proto.isEndc = false;
+ mServiceState1Proto.simSlotIndex = 0;
+ mServiceState1Proto.isMultiSim = true;
+ mServiceState1Proto.carrierId = CARRIER1_ID;
+ mServiceState1Proto.totalTimeMillis = 5000L;
+
+ // LTE with ENDC on slot 0
+ mServiceState2Proto = new CellularServiceState();
+ mServiceState2Proto.voiceRat = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceState2Proto.dataRat = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceState2Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState2Proto.dataRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState2Proto.isEndc = true;
+ mServiceState2Proto.simSlotIndex = 0;
+ mServiceState2Proto.isMultiSim = true;
+ mServiceState2Proto.carrierId = CARRIER1_ID;
+ mServiceState2Proto.totalTimeMillis = 15000L;
+
+ // LTE with WFC and roaming on slot 1
+ mServiceState3Proto = new CellularServiceState();
+ mServiceState3Proto.voiceRat = TelephonyManager.NETWORK_TYPE_IWLAN;
+ mServiceState3Proto.dataRat = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceState3Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState3Proto.dataRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState3Proto.isEndc = false;
+ mServiceState3Proto.simSlotIndex = 1;
+ mServiceState3Proto.isMultiSim = true;
+ mServiceState3Proto.carrierId = CARRIER2_ID;
+ mServiceState3Proto.totalTimeMillis = 10000L;
+
+ // UMTS with roaming on slot 1
+ mServiceState4Proto = new CellularServiceState();
+ mServiceState4Proto.voiceRat = TelephonyManager.NETWORK_TYPE_UMTS;
+ mServiceState4Proto.dataRat = TelephonyManager.NETWORK_TYPE_UMTS;
+ mServiceState4Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState4Proto.dataRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState4Proto.isEndc = false;
+ mServiceState4Proto.simSlotIndex = 1;
+ mServiceState4Proto.isMultiSim = true;
+ mServiceState4Proto.carrierId = CARRIER2_ID;
+ mServiceState4Proto.totalTimeMillis = 10000L;
+
+ mServiceSwitches =
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto, mServiceSwitch2Proto};
+ mServiceStates =
+ new CellularServiceState[] {
+ mServiceState1Proto,
+ mServiceState2Proto,
+ mServiceState3Proto,
+ mServiceState4Proto
+ };
+
+ // IMS over LTE on slot 0, registered for 5 seconds
+ mImsRegistrationStatsLte0 = new ImsRegistrationStats();
+ mImsRegistrationStatsLte0.carrierId = CARRIER1_ID;
+ mImsRegistrationStatsLte0.simSlotIndex = 0;
+ mImsRegistrationStatsLte0.rat = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationStatsLte0.registeredMillis = 5000L;
+ mImsRegistrationStatsLte0.voiceCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.voiceAvailableMillis = 5000L;
+ mImsRegistrationStatsLte0.smsCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.smsAvailableMillis = 5000L;
+ mImsRegistrationStatsLte0.videoCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.videoAvailableMillis = 5000L;
+ mImsRegistrationStatsLte0.utCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.utAvailableMillis = 5000L;
+
+ // IMS over WiFi on slot 0, registered for 10 seconds (voice only)
+ mImsRegistrationStatsWifi0 = new ImsRegistrationStats();
+ mImsRegistrationStatsWifi0.carrierId = CARRIER2_ID;
+ mImsRegistrationStatsWifi0.simSlotIndex = 0;
+ mImsRegistrationStatsWifi0.rat = TelephonyManager.NETWORK_TYPE_IWLAN;
+ mImsRegistrationStatsWifi0.registeredMillis = 10000L;
+ mImsRegistrationStatsWifi0.voiceCapableMillis = 10000L;
+ mImsRegistrationStatsWifi0.voiceAvailableMillis = 10000L;
+
+ // IMS over LTE on slot 1, registered for 20 seconds
+ mImsRegistrationStatsLte1 = new ImsRegistrationStats();
+ mImsRegistrationStatsLte1.carrierId = CARRIER1_ID;
+ mImsRegistrationStatsLte1.simSlotIndex = 0;
+ mImsRegistrationStatsLte1.rat = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationStatsLte1.registeredMillis = 20000L;
+ mImsRegistrationStatsLte1.voiceCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.voiceAvailableMillis = 20000L;
+ mImsRegistrationStatsLte1.smsCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.smsAvailableMillis = 20000L;
+ mImsRegistrationStatsLte1.videoCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.videoAvailableMillis = 20000L;
+ mImsRegistrationStatsLte1.utCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.utAvailableMillis = 20000L;
+
+ // IMS terminations on LTE
+ mImsRegistrationTerminationLte = new ImsRegistrationTermination();
+ mImsRegistrationTerminationLte.carrierId = CARRIER1_ID;
+ mImsRegistrationTerminationLte.isMultiSim = true;
+ mImsRegistrationTerminationLte.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationTerminationLte.setupFailed = false;
+ mImsRegistrationTerminationLte.reasonCode = ImsReasonInfo.CODE_REGISTRATION_ERROR;
+ mImsRegistrationTerminationLte.extraCode = 999;
+ mImsRegistrationTerminationLte.extraMessage = "Request Timeout";
+ mImsRegistrationTerminationLte.count = 2;
+
+ // IMS terminations on WiFi
+ mImsRegistrationTerminationWifi = new ImsRegistrationTermination();
+ mImsRegistrationTerminationWifi.carrierId = CARRIER2_ID;
+ mImsRegistrationTerminationWifi.isMultiSim = true;
+ mImsRegistrationTerminationWifi.ratAtEnd = TelephonyManager.NETWORK_TYPE_IWLAN;
+ mImsRegistrationTerminationWifi.setupFailed = false;
+ mImsRegistrationTerminationWifi.reasonCode = ImsReasonInfo.CODE_REGISTRATION_ERROR;
+ mImsRegistrationTerminationWifi.extraCode = 0;
+ mImsRegistrationTerminationWifi.extraMessage = "";
+ mImsRegistrationTerminationWifi.count = 1;
+
+ mImsRegistrationStats =
+ new ImsRegistrationStats[] {
+ mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0, mImsRegistrationStatsLte1
+ };
+ mImsRegistrationTerminations =
+ new ImsRegistrationTermination[] {
+ mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi
+ };
}
private static class TestablePersistAtomsStorage extends PersistAtomsStorage {
@@ -260,6 +439,8 @@ public class PersistAtomsStorageTest extends TelephonyTest {
TestablePersistAtomsStorage(Context context) {
super(context);
+ // Remove delay for saving to persistent storage during tests.
+ mSaveImmediately = true;
}
@Override
@@ -278,7 +459,8 @@ public class PersistAtomsStorageTest extends TelephonyTest {
}
private PersistAtoms getAtomsProto() {
- // NOTE: not guarded by mLock as usual, should be fine since the test is single-threaded
+ // NOTE: unlike other methods in PersistAtomsStorage, this is not synchronized, but
+ // should be fine since the test is single-threaded
return mAtoms;
}
}
@@ -329,18 +511,8 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@@ -353,18 +525,8 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@@ -376,18 +538,8 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@@ -401,57 +553,44 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@SmallTest
public void loadAtoms_pullTimeMissing() throws Exception {
+ // create test file with lastPullTimeMillis = 0L, i.e. default/unknown
createTestFile(0L);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be match, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage);
- assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertProtoArrayEquals(mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayEquals(mVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceStates, mPersistAtomsStorage.getCellularServiceStates(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceSwitches, mPersistAtomsStorage.getCellularDataServiceSwitches(0L));
}
@Test
@SmallTest
public void loadAtoms_validContents() throws Exception {
- createTestFile(100L);
+ createTestFile(/* lastPullTimeMillis= */ 100L);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
// no exception should be thrown, storage and pull time should match
- assertEquals(
- 100L, mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- 100L, mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage);
- assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession);
+ assertAllPullTimestampEquals(100L);
+ assertProtoArrayEquals(mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayEquals(mVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceStates, mPersistAtomsStorage.getCellularServiceStates(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceSwitches, mPersistAtomsStorage.getCellularDataServiceSwitches(0L));
}
@Test
@@ -464,46 +603,31 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// call should be added successfully, there should be no RAT usage, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
+ verifyCurrentStateSavedToFileOnce();
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallRatUsages(0L));
VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
assertProtoArrayEquals(new VoiceCallSession[] {mCall1Proto}, voiceCallSession);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
}
@Test
@SmallTest
public void addVoiceCallSession_withExistingCalls() throws Exception {
- createTestFile(100L);
+ createTestFile(START_TIME_MILLIS);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.addVoiceCallSession(mCall1Proto);
mPersistAtomsStorage.incTimeMillis(100L);
// call should be added successfully, RAT usages should not change, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(mVoiceCallRatUsages.length, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- // call lists are randomized, but sorted version should be identical
+ assertProtoArrayEquals(mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
VoiceCallSession[] expectedVoiceCallSessions =
new VoiceCallSession[] {
mCall1Proto, mCall1Proto, mCall2Proto, mCall3Proto, mCall4Proto
};
- Arrays.sort(expectedVoiceCallSessions, sProtoComparator);
- Arrays.sort(voiceCallSession, sProtoComparator);
- assertProtoArrayEquals(expectedVoiceCallSessions, voiceCallSession);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
+ // call list is randomized at this point
+ verifyCurrentStateSavedToFileOnce();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@@ -517,9 +641,10 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// one previous call should be evicted, the new call should be added
+ verifyCurrentStateSavedToFileOnce();
VoiceCallSession[] calls = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertHasCall(calls, mCall1Proto, 49);
- assertHasCall(calls, mCall2Proto, 1);
+ assertHasCall(calls, mCall1Proto, /* expectedCount= */ 49);
+ assertHasCall(calls, mCall2Proto, /* expectedCount= */ 1);
}
@Test
@@ -533,25 +658,16 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// RAT should be added successfully, calls should not change, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- RawVoiceCallRatUsage[] expectedVoiceCallRatUsage = mVoiceCallRatUsages.clone();
- Arrays.sort(expectedVoiceCallRatUsage, sProtoComparator);
- Arrays.sort(voiceCallRatUsage, sProtoComparator);
- assertProtoArrayEquals(expectedVoiceCallRatUsage, voiceCallRatUsage);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
+ verifyCurrentStateSavedToFileOnce();
+ assertProtoArrayEqualsIgnoringOrder(
+ mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@SmallTest
public void addVoiceCallRatUsage_withExistingUsages() throws Exception {
- createTestFile(100L);
+ createTestFile(START_TIME_MILLIS);
VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(mVoiceCallRatUsages);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
@@ -559,42 +675,30 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
// RAT should be added successfully, calls should not change, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
// call count and duration should become doubled since mVoiceCallRatUsages applied through
// both file and addVoiceCallRatUsage()
- RawVoiceCallRatUsage[] expectedVoiceCallRatUsage =
+ verifyCurrentStateSavedToFileOnce();
+ VoiceCallRatUsage[] expectedVoiceCallRatUsage =
multiplyVoiceCallRatUsage(mVoiceCallRatUsages, 2);
- Arrays.sort(expectedVoiceCallRatUsage, sProtoComparator);
- Arrays.sort(voiceCallRatUsage, sProtoComparator);
- assertProtoArrayEquals(expectedVoiceCallRatUsage, voiceCallRatUsage);
- assertNotNull(voiceCallSession);
- assertEquals(mVoiceCallSessions.length, voiceCallSession.length);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedVoiceCallRatUsage, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayEquals(mVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@SmallTest
public void addVoiceCallRatUsage_empty() throws Exception {
createEmptyTestFile();
- VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(new RawVoiceCallRatUsage[0]);
+ VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(new VoiceCallRatUsage[0]);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.addVoiceCallRatUsage(ratTracker);
mPersistAtomsStorage.incTimeMillis(100L);
// RAT should be added successfully, calls should not change
- // in this case it does not necessarily need to save
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ // in this case saving is unnecessarily
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@@ -604,7 +708,7 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(100L);
+ VoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(100L);
// should be denied
assertNull(voiceCallRatUsage);
@@ -617,9 +721,9 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.incTimeMillis(100L);
- RawVoiceCallRatUsage[] voiceCallRatUsage1 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
+ VoiceCallRatUsage[] voiceCallRatUsage1 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
mPersistAtomsStorage.incTimeMillis(100L);
- RawVoiceCallRatUsage[] voiceCallRatUsage2 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
+ VoiceCallRatUsage[] voiceCallRatUsage2 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
long voiceCallSessionPullTimestampMillis =
mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis;
VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(50L);
@@ -627,19 +731,19 @@ public class PersistAtomsStorageTest extends TelephonyTest {
// first set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved, other fields should be unaffected
assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage1);
- assertProtoArrayEquals(new RawVoiceCallRatUsage[0], voiceCallRatUsage2);
+ assertProtoArrayIsEmpty(voiceCallRatUsage2);
assertEquals(
START_TIME_MILLIS + 200L,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
+ mPersistAtomsStorage.getAtomsProto().voiceCallRatUsagePullTimestampMillis);
assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession);
assertEquals(START_TIME_MILLIS, voiceCallSessionPullTimestampMillis);
InOrder inOrder = inOrder(mTestFileOutputStream);
assertEquals(
START_TIME_MILLIS + 100L,
- getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis);
+ getAtomsWritten(inOrder).voiceCallRatUsagePullTimestampMillis);
assertEquals(
START_TIME_MILLIS + 200L,
- getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis);
+ getAtomsWritten(inOrder).voiceCallRatUsagePullTimestampMillis);
assertEquals(
START_TIME_MILLIS + 200L,
getAtomsWritten(inOrder).voiceCallSessionPullTimestampMillis);
@@ -670,13 +774,13 @@ public class PersistAtomsStorageTest extends TelephonyTest {
mPersistAtomsStorage.incTimeMillis(100L);
VoiceCallSession[] voiceCallSession2 = mPersistAtomsStorage.getVoiceCallSessions(50L);
long voiceCallRatUsagePullTimestampMillis =
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis;
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
+ mPersistAtomsStorage.getAtomsProto().voiceCallRatUsagePullTimestampMillis;
+ VoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
// first set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved, other fields should be unaffected
assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession1);
- assertProtoArrayEquals(new VoiceCallSession[0], voiceCallSession2);
+ assertProtoArrayIsEmpty(voiceCallSession2);
assertEquals(
START_TIME_MILLIS + 200L,
mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
@@ -691,10 +795,486 @@ public class PersistAtomsStorageTest extends TelephonyTest {
getAtomsWritten(inOrder).voiceCallSessionPullTimestampMillis);
assertEquals(
START_TIME_MILLIS + 200L,
- getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis);
+ getAtomsWritten(inOrder).voiceCallRatUsagePullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ mServiceState1Proto, mServiceSwitch1Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ assertProtoArrayEquals(new CellularServiceState[] {mServiceState1Proto}, serviceStates);
+ assertProtoArrayEquals(
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto}, serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_withExistingEntries()
+ throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ mServiceState1Proto, mServiceSwitch1Proto);
+
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ mServiceState2Proto, mServiceSwitch2Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularServiceState[] {mServiceState1Proto, mServiceState2Proto},
+ serviceStates);
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto, mServiceSwitch2Proto},
+ serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_updateExistingEntries()
+ throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ CellularServiceState newServiceState1Proto = copyOf(mServiceState1Proto);
+ CellularDataServiceSwitch newServiceSwitch1Proto = copyOf(mServiceSwitch1Proto);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ copyOf(mServiceState1Proto), copyOf(mServiceSwitch1Proto));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mServiceState1Proto's duration and mServiceSwitch1Proto's switch should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ newServiceState1Proto.totalTimeMillis *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularServiceState[] {
+ newServiceState1Proto,
+ mServiceState2Proto,
+ mServiceState3Proto,
+ mServiceState4Proto
+ },
+ serviceStates);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ newServiceSwitch1Proto.switchCount *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularDataServiceSwitch[] {newServiceSwitch1Proto, mServiceSwitch2Proto},
+ serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_tooManyServiceStates()
+ throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ Queue<CellularServiceState> expectedServiceStates = new LinkedList<>();
+ Queue<CellularDataServiceSwitch> expectedServiceSwitches = new LinkedList<>();
+
+ // Add 51 service states, with the first being least recent
+ for (int i = 0; i < 51; i++) {
+ CellularServiceState state = new CellularServiceState();
+ state.voiceRat = i / 10;
+ state.dataRat = i % 10;
+ expectedServiceStates.add(state);
+ CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
+ serviceSwitch.ratFrom = i / 10;
+ serviceSwitch.ratTo = i % 10;
+ expectedServiceSwitches.add(serviceSwitch);
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ copyOf(state), copyOf(serviceSwitch));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // The least recent (the first) service state should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ expectedServiceStates.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedServiceStates.toArray(new CellularServiceState[0]), serviceStates);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ expectedServiceSwitches.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedServiceSwitches.toArray(new CellularDataServiceSwitch[0]), serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularDataServiceSwitches_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(100L);
+
+ // should be denied
+ assertNull(serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularDataServiceSwitches_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularDataServiceSwitch[] serviceSwitches1 =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularDataServiceSwitch[] serviceSwitches2 =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto, mServiceSwitch2Proto},
+ serviceSwitches1);
+ assertProtoArrayEquals(new CellularDataServiceSwitch[0], serviceSwitches2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().cellularDataServiceSwitchPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).cellularDataServiceSwitchPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).cellularDataServiceSwitchPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularServiceStates_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(100L);
+
+ // should be denied
+ assertNull(serviceStates);
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularServiceStates_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularServiceState[] serviceStates1 = mPersistAtomsStorage.getCellularServiceStates(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularServiceState[] serviceStates2 = mPersistAtomsStorage.getCellularServiceStates(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularServiceState[] {
+ mServiceState1Proto,
+ mServiceState2Proto,
+ mServiceState3Proto,
+ mServiceState4Proto
+ },
+ serviceStates1);
+ assertProtoArrayEquals(new CellularServiceState[0], serviceStates2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().cellularServiceStatePullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).cellularServiceStatePullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).cellularServiceStatePullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationStats(mImsRegistrationStatsLte0);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ assertProtoArrayEquals(new ImsRegistrationStats[] {mImsRegistrationStatsLte0}, regStats);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationStats(mImsRegistrationStatsLte0);
+
+ mPersistAtomsStorage.addImsRegistrationStats(mImsRegistrationStatsWifi0);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0},
+ regStats);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ ImsRegistrationStats newImsRegistrationStatsLte0 = copyOf(mImsRegistrationStatsLte0);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsLte0));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mImsRegistrationStatsLte0's durations should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] serviceStates = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ newImsRegistrationStatsLte0.registeredMillis *= 2;
+ newImsRegistrationStatsLte0.voiceCapableMillis *= 2;
+ newImsRegistrationStatsLte0.voiceAvailableMillis *= 2;
+ newImsRegistrationStatsLte0.smsCapableMillis *= 2;
+ newImsRegistrationStatsLte0.smsAvailableMillis *= 2;
+ newImsRegistrationStatsLte0.videoCapableMillis *= 2;
+ newImsRegistrationStatsLte0.videoAvailableMillis *= 2;
+ newImsRegistrationStatsLte0.utCapableMillis *= 2;
+ newImsRegistrationStatsLte0.utAvailableMillis *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {
+ newImsRegistrationStatsLte0,
+ mImsRegistrationStatsWifi0,
+ mImsRegistrationStatsLte1
+ },
+ serviceStates);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_tooManyRegistrationStats() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ Queue<ImsRegistrationStats> expectedRegistrationStats = new LinkedList<>();
+
+ // Add 11 registration stats
+ for (int i = 0; i < 11; i++) {
+ ImsRegistrationStats stats = copyOf(mImsRegistrationStatsLte0);
+ stats.rat = i;
+ expectedRegistrationStats.add(stats);
+ mPersistAtomsStorage.addImsRegistrationStats(stats);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // The least recent (the first) registration stats should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] stats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ expectedRegistrationStats.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedRegistrationStats.toArray(new ImsRegistrationStats[0]), stats);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationLte);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ assertProtoArrayEquals(
+ new ImsRegistrationTermination[] {mImsRegistrationTerminationLte}, terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationLte);
+
+ mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationWifi);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationTermination[] {
+ mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi
+ },
+ terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ ImsRegistrationTermination newTermination = copyOf(mImsRegistrationTerminationWifi);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsRegistrationTermination(copyOf(mImsRegistrationTerminationWifi));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mImsRegistrationTerminationWifi's count should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ newTermination.count *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationTermination[] {mImsRegistrationTerminationLte, newTermination},
+ terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_tooManyTerminations() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ Queue<ImsRegistrationTermination> expectedTerminations = new LinkedList<>();
+
+ // Add 11 registration terminations
+ for (int i = 0; i < 11; i++) {
+ ImsRegistrationTermination termination = copyOf(mImsRegistrationTerminationLte);
+ termination.reasonCode = i;
+ expectedTerminations.add(termination);
+ mPersistAtomsStorage.addImsRegistrationTermination(termination);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // The least recent (the first) registration termination should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ expectedTerminations.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedTerminations.toArray(new ImsRegistrationTermination[0]), terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ ImsRegistrationStats[] stats = mPersistAtomsStorage.getImsRegistrationStats(100L);
+
+ // should be denied
+ assertNull(stats);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationStats_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationStats[] stats1 = mPersistAtomsStorage.getImsRegistrationStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationStats[] stats2 = mPersistAtomsStorage.getImsRegistrationStats(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {
+ mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0, mImsRegistrationStatsLte1
+ },
+ stats1);
+ assertProtoArrayEquals(new ImsRegistrationStats[0], stats2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().imsRegistrationStatsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).imsRegistrationStatsPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).imsRegistrationStatsPullTimestampMillis);
inOrder.verifyNoMoreInteractions();
}
+ @Test
+ @SmallTest
+ public void getImsRegistrationTerminations_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(100L);
+
+ // should be denied
+ assertNull(terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationTerminations_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationTermination[] terminations1 =
+ mPersistAtomsStorage.getImsRegistrationTerminations(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationTermination[] terminations2 =
+ mPersistAtomsStorage.getImsRegistrationTerminations(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationTermination[] {
+ mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi
+ },
+ terminations1);
+ assertProtoArrayEquals(new ImsRegistrationTermination[0], terminations2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().imsRegistrationTerminationPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).imsRegistrationTerminationPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).imsRegistrationTerminationPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ /* Utilities */
+
private void createEmptyTestFile() throws Exception {
PersistAtoms atoms = new PersistAtoms();
FileOutputStream stream = new FileOutputStream(mTestFile);
@@ -704,10 +1284,19 @@ public class PersistAtomsStorageTest extends TelephonyTest {
private void createTestFile(long lastPullTimeMillis) throws Exception {
PersistAtoms atoms = new PersistAtoms();
- atoms.rawVoiceCallRatUsagePullTimestampMillis = lastPullTimeMillis;
+ atoms.buildFingerprint = Build.FINGERPRINT;
+ atoms.voiceCallRatUsagePullTimestampMillis = lastPullTimeMillis;
+ atoms.voiceCallRatUsage = mVoiceCallRatUsages;
atoms.voiceCallSessionPullTimestampMillis = lastPullTimeMillis;
- atoms.rawVoiceCallRatUsage = mVoiceCallRatUsages;
atoms.voiceCallSession = mVoiceCallSessions;
+ atoms.cellularServiceStatePullTimestampMillis = lastPullTimeMillis;
+ atoms.cellularServiceState = mServiceStates;
+ atoms.cellularDataServiceSwitchPullTimestampMillis = lastPullTimeMillis;
+ atoms.cellularDataServiceSwitch = mServiceSwitches;
+ atoms.imsRegistrationStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsRegistrationStats = mImsRegistrationStats;
+ atoms.imsRegistrationTerminationPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsRegistrationTermination = mImsRegistrationTerminations;
FileOutputStream stream = new FileOutputStream(mTestFile);
stream.write(PersistAtoms.toByteArray(atoms));
stream.close();
@@ -731,11 +1320,11 @@ public class PersistAtomsStorageTest extends TelephonyTest {
}
}
- private static RawVoiceCallRatUsage[] multiplyVoiceCallRatUsage(
- RawVoiceCallRatUsage[] usages, int times) {
- RawVoiceCallRatUsage[] multipliedUsages = new RawVoiceCallRatUsage[usages.length];
+ private static VoiceCallRatUsage[] multiplyVoiceCallRatUsage(
+ VoiceCallRatUsage[] usages, int times) {
+ VoiceCallRatUsage[] multipliedUsages = new VoiceCallRatUsage[usages.length];
for (int i = 0; i < usages.length; i++) {
- multipliedUsages[i] = new RawVoiceCallRatUsage();
+ multipliedUsages[i] = new VoiceCallRatUsage();
multipliedUsages[i].carrierId = usages[i].carrierId;
multipliedUsages[i].rat = usages[i].rat;
multipliedUsages[i].callCount = usages[i].callCount * 2;
@@ -744,19 +1333,73 @@ public class PersistAtomsStorageTest extends TelephonyTest {
return multipliedUsages;
}
+ private static CellularServiceState copyOf(CellularServiceState source) throws Exception {
+ return CellularServiceState.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static CellularDataServiceSwitch copyOf(CellularDataServiceSwitch source)
+ throws Exception {
+ return CellularDataServiceSwitch.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static ImsRegistrationStats copyOf(ImsRegistrationStats source) throws Exception {
+ return ImsRegistrationStats.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static ImsRegistrationTermination copyOf(ImsRegistrationTermination source)
+ throws Exception {
+ return ImsRegistrationTermination.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private void assertAllPullTimestampEquals(long timestamp) {
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().voiceCallRatUsagePullTimestampMillis);
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().cellularServiceStatePullTimestampMillis);
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().cellularDataServiceSwitchPullTimestampMillis);
+ }
+
+ private void assertStorageIsEmptyForAllAtoms() {
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallSessions(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getCellularServiceStates(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getCellularDataServiceSwitches(0L));
+ }
+
+ private static <T extends MessageNano> void assertProtoArrayIsEmpty(T[] array) {
+ assertNotNull(array);
+ assertEquals(0, array.length);
+ }
+
private static void assertProtoArrayEquals(MessageNano[] expected, MessageNano[] actual) {
assertNotNull(expected);
assertNotNull(actual);
- assertEquals(expected.length, actual.length);
+ String message =
+ "Expected:\n" + Arrays.toString(expected) + "\nGot:\n" + Arrays.toString(actual);
+ assertEquals(message, expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
- assertTrue(
- String.format(
- "Message %d of %d differs:\n=== expected ===\n%s=== got ===\n%s",
- i + 1, expected.length, expected[i].toString(), actual[i].toString()),
- MessageNano.messageNanoEquals(expected[i], actual[i]));
+ assertTrue(message, MessageNano.messageNanoEquals(expected[i], actual[i]));
}
}
+ private static void assertProtoArrayEqualsIgnoringOrder(
+ MessageNano[] expected, MessageNano[] actual) {
+ assertNotNull(expected);
+ assertNotNull(actual);
+ expected = expected.clone();
+ actual = actual.clone();
+ Arrays.sort(expected, sProtoComparator);
+ Arrays.sort(actual, sProtoComparator);
+ assertProtoArrayEquals(expected, actual);
+ }
+
private static void assertHasCall(
VoiceCallSession[] calls, @Nullable VoiceCallSession expectedCall, int expectedCount) {
assertNotNull(calls);
@@ -770,4 +1413,12 @@ public class PersistAtomsStorageTest extends TelephonyTest {
}
assertEquals(expectedCount, actualCount);
}
+
+ private void verifyCurrentStateSavedToFileOnce() throws Exception {
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
new file mode 100644
index 0000000000..8c6752c6e4
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.UiccSlot;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class ServiceStateStatsTest extends TelephonyTest {
+ private static final long START_TIME_MILLIS = 2000L;
+ private static final int CARRIER1_ID = 1;
+ private static final int CARRIER2_ID = 1187;
+
+ @Mock private UiccSlot mPhysicalSlot0;
+ @Mock private UiccSlot mPhysicalSlot1;
+ @Mock private Phone mSecondPhone;
+
+ private TestableServiceStateStats mServiceStateStats;
+
+ private static class TestableServiceStateStats extends ServiceStateStats {
+ private long mTimeMillis = START_TIME_MILLIS;
+
+ TestableServiceStateStats(Phone phone) {
+ super(phone);
+ }
+
+ @Override
+ protected long getTimeMillis() {
+ // NOTE: super class constructor will be executed before private field is set, which
+ // gives the wrong start time (mTimeMillis will have its default value of 0L)
+ return mTimeMillis == 0L ? START_TIME_MILLIS : mTimeMillis;
+ }
+
+ private void setTimeMillis(long timeMillis) {
+ mTimeMillis = timeMillis;
+ }
+
+ private void incTimeMillis(long timeMillis) {
+ mTimeMillis += timeMillis;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ doReturn(CARRIER1_ID).when(mPhone).getCarrierId();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+
+ // Single physical SIM
+ doReturn(true).when(mPhysicalSlot0).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot0).getCardState();
+ doReturn(false).when(mPhysicalSlot0).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0}).when(mUiccController).getUiccSlots();
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlot(0);
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlotForPhone(0);
+
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+
+ mServiceStateStats = new TestableServiceStateStats(mPhone);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_inService() throws Exception {
+ // Using default service state for LTE
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_outOfService() throws Exception {
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_airplaneMode() throws Exception {
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // There should be no new switches, service states, or added durations
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_airplaneModeWithWifiCalling() throws Exception {
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(true).when(mImsPhone).isWifiCallingEnabled();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // There should be no new switches, service states, or added durations
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_noSimCardEmergencyOnly() throws Exception {
+ // Using default service state for LTE
+ doReturn(CardState.CARDSTATE_ABSENT).when(mPhysicalSlot0).getCardState();
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getDataRegState();
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(-1, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_noSimCardOutOfService() throws Exception {
+ doReturn(CardState.CARDSTATE_ABSENT).when(mPhysicalSlot0).getCardState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(-1, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_longOnGoingServiceState() throws Exception {
+ // Using default service state for LTE
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // There should be 2 separate service state updates, which should be different objects
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ assertNotSame(captor.getAllValues().get(0), captor.getAllValues().get(1));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_sameRats() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ // Should produce 1 service state atom with LTE and no data service switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_differentDataRats() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+
+ // There should be 2 service states and a data service switch
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> serviceStateCaptor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ ArgumentCaptor<CellularDataServiceSwitch> serviceSwitchCaptor =
+ ArgumentCaptor.forClass(CellularDataServiceSwitch.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(
+ serviceStateCaptor.capture(), serviceSwitchCaptor.capture());
+ CellularServiceState state = serviceStateCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratTo);
+ assertEquals(0, serviceSwitch.simSlotIndex);
+ assertFalse(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ assertNull(serviceSwitchCaptor.getAllValues().get(1)); // produced by conclude()
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_differentVoiceRats() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice RAT changes to IWLAN and data RAT stays in LTE according to WWAN PS RAT
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ doReturn(true).when(mImsPhone).isWifiCallingEnabled();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+
+ // There should be 2 service states but no data service switch
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_iwlanButNotWifiCalling() throws Exception {
+ // Using default service state for LTE as WWAN PS RAT
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ doReturn(false).when(mImsPhone).isWifiCallingEnabled();
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ // Should produce 1 service state atom with voice and data (WWAN PS) RAT as LTE
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(0L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_endc() throws Exception {
+ // Using default service state for LTE without ENDC
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // ENDC should stay false
+ doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+ // ENDC should become true
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(400L);
+ // ENDC should stay true
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(800L);
+
+ // There should be 4 service state updates (2 distinct service states) but no service switch
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(4))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ state = captor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertTrue(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(400L, state.totalTimeMillis);
+ state = captor.getAllValues().get(3);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertTrue(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(800L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_simSwapSameRat() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // SIM removed, emergency call only
+ doReturn(CardState.CARDSTATE_ABSENT).when(mPhysicalSlot0).getCardState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(5000L);
+ // New SIM inserted
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot0).getCardState();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(CARRIER2_ID).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+
+ // There should be 3 service states, but there should be no switches due to carrier change
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(3))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(-1, state.carrierId);
+ assertEquals(5000L, state.totalTimeMillis);
+ state = captor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER2_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_roaming() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice roaming
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(ServiceState.ROAMING_TYPE_INTERNATIONAL).when(mServiceState).getVoiceRoamingType();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+ // Voice and data roaming
+ doReturn(ServiceState.ROAMING_TYPE_INTERNATIONAL).when(mServiceState).getDataRoamingType();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(400L);
+
+ // There should be 3 service states and 1 data service switch (LTE to UMTS)
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> serviceStateCaptor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ ArgumentCaptor<CellularDataServiceSwitch> serviceSwitchCaptor =
+ ArgumentCaptor.forClass(CellularDataServiceSwitch.class);
+ verify(mPersistAtomsStorage, times(3))
+ .addCellularServiceStateAndCellularDataServiceSwitch(
+ serviceStateCaptor.capture(), serviceSwitchCaptor.capture());
+ CellularServiceState state = serviceStateCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_INTERNATIONAL, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_INTERNATIONAL, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_INTERNATIONAL, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(400L, state.totalTimeMillis);
+ CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
+ assertEquals(0, serviceSwitch.simSlotIndex);
+ assertFalse(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ assertNull(serviceSwitchCaptor.getAllValues().get(1));
+ assertNull(serviceSwitchCaptor.getAllValues().get(2)); // produced by conclude()
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_dualSim() throws Exception {
+ // Using default service state for LTE
+ // Only difference between the 2 slots is slot index
+ mockDualSim(CARRIER1_ID);
+ TestableServiceStateStats mSecondServiceStateStats =
+ new TestableServiceStateStats(mSecondPhone);
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ mSecondServiceStateStats.onServiceStateChanged(mServiceState);
+ mSecondServiceStateStats.incTimeMillis(100L);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UMTS);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+ mSecondServiceStateStats.onServiceStateChanged(mServiceState);
+ mSecondServiceStateStats.incTimeMillis(200L);
+
+ // There should be 4 service states and 2 data service switches
+ mServiceStateStats.conclude();
+ mSecondServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> serviceStateCaptor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ ArgumentCaptor<CellularDataServiceSwitch> serviceSwitchCaptor =
+ ArgumentCaptor.forClass(CellularDataServiceSwitch.class);
+ verify(mPersistAtomsStorage, times(4))
+ .addCellularServiceStateAndCellularDataServiceSwitch(
+ serviceStateCaptor.capture(), serviceSwitchCaptor.capture());
+ CellularServiceState state = serviceStateCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(1, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(3);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(1, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
+ assertEquals(0, serviceSwitch.simSlotIndex);
+ assertTrue(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ serviceSwitch = serviceSwitchCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
+ assertEquals(1, serviceSwitch.simSlotIndex);
+ assertTrue(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ assertNull(serviceSwitchCaptor.getAllValues().get(2)); // produced by conclude()
+ assertNull(serviceSwitchCaptor.getAllValues().get(3)); // produced by conclude()
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_airplaneMode() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(5000L);
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(CARRIER1_ID).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+
+ // There should be 2 service state updates (1 distinct service state) and no switches
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ private void mockWwanPsRat(@NetworkType int rat) {
+ doReturn(new NetworkRegistrationInfo.Builder().setAccessNetworkTechnology(rat).build())
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ }
+
+ private void mockDualSim(int carrierId) {
+ doReturn(1).when(mSecondPhone).getPhoneId();
+ doReturn(1).when(mUiccController).getSlotIdFromPhoneId(1);
+ doReturn(carrierId).when(mSecondPhone).getCarrierId();
+
+ doReturn(true).when(mPhysicalSlot1).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot1).getCardState();
+ doReturn(false).when(mPhysicalSlot1).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0, mPhysicalSlot1})
+ .when(mUiccController)
+ .getUiccSlots();
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlot(1);
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlotForPhone(1);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
index 0f1196f27b..e05faecc53 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
@@ -17,6 +17,8 @@
package com.android.internal.telephony.metrics;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -73,10 +75,12 @@ public class SimSlotStateTest extends TelephonyTest {
doReturn(new UiccSlot[] {}).when(mUiccController).getUiccSlots();
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(0, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -85,10 +89,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(null);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(0, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -97,10 +103,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mInactiveSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(0, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -109,10 +117,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mEmptySlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -122,10 +132,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -135,10 +147,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -148,11 +162,13 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
// the metrics should not count restricted cards since they cannot be used
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -162,10 +178,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -175,10 +193,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -188,10 +208,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupSingleSim(mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -200,10 +222,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mEmptySlot, mInactiveSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -212,10 +236,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mPhysicalSlot, mInactiveSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -225,10 +251,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mInactiveSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -238,10 +266,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mInactiveSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -251,10 +281,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mEmptySlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -264,10 +296,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mPhysicalSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -277,10 +311,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mEmptySlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -290,10 +326,12 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mPhysicalSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(2, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertTrue(isMultiSim);
}
@Test
@@ -302,10 +340,45 @@ public class SimSlotStateTest extends TelephonyTest {
setupDualSim(mPhysicalSlot, mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(2, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertTrue(isMultiSim);
+ }
+
+ @Test
+ @SmallTest
+ public void isEsim_singlePhysicalSim() {
+ doReturn(mPhysicalSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
+
+ boolean isEsim = SimSlotState.isEsim(0);
+
+ assertFalse(isEsim);
+ }
+
+ @Test
+ @SmallTest
+ public void isEsim_singleEsim() {
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
+
+ boolean isEsim = SimSlotState.isEsim(0);
+
+ assertTrue(isEsim);
+ }
+
+ @Test
+ @SmallTest
+ public void isEsim_dualSim() {
+ doReturn(mPhysicalSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
+
+ boolean isEsim0 = SimSlotState.isEsim(0);
+ boolean isEsim1 = SimSlotState.isEsim(1);
+
+ assertFalse(isEsim0);
+ assertTrue(isEsim1);
}
private void setupSingleSim(UiccSlot slot0) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 44c9e8418a..c4e6e27d4e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -29,6 +29,7 @@ import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_D
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
+import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.NUM_SIGNAL_LEVEL;
import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
import static org.junit.Assert.assertArrayEquals;
@@ -59,8 +60,11 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsResponse;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
import com.android.internal.telephony.nano.TelephonyProto;
+import com.android.internal.telephony.nano.TelephonyProto.BandwidthEstimatorStats;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.NrMode;
import com.android.internal.telephony.nano.TelephonyProto.RadioAccessTechnology;
import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
@@ -73,6 +77,7 @@ import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState.FrequencyRange;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState.NrState;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState.RoamingType;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings.RilNetworkMode;
import org.junit.After;
import org.junit.Before;
@@ -419,6 +424,36 @@ public class TelephonyMetricsTest extends TelephonyTest {
assertTrue(log.callSessions[0].events[1].settings.isEnhanced4GLteModeEnabled);
}
+ // Test multiple events impacting TelephonySettings.
+ @Test
+ @SmallTest
+ public void testTelephonySettingsEvents() throws Exception {
+ mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE, 1);
+ mMetrics.writeSetPreferredNetworkType(mPhone.getPhoneId(),
+ TelephonyManager.NETWORK_MODE_LTE_ONLY);
+ mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, 1);
+
+ TelephonyLog log = buildProto();
+
+ assertEquals(3, log.events.length);
+ assertTrue(log.events[0].settings.isEnhanced4GLteModeEnabled);
+ assertFalse(log.events[0].settings.isWifiCallingEnabled);
+ assertEquals(log.events[0].settings.preferredNetworkMode,
+ RilNetworkMode.NETWORK_MODE_UNKNOWN);
+ assertTrue(log.events[1].settings.isEnhanced4GLteModeEnabled);
+ assertFalse(log.events[1].settings.isWifiCallingEnabled);
+ assertEquals(log.events[1].settings.preferredNetworkMode,
+ RilNetworkMode.NETWORK_MODE_LTE_ONLY);
+ assertTrue(log.events[2].settings.isEnhanced4GLteModeEnabled);
+ assertTrue(log.events[2].settings.isWifiCallingEnabled);
+ assertEquals(log.events[2].settings.preferredNetworkMode,
+ RilNetworkMode.NETWORK_MODE_LTE_ONLY);
+ }
+
// Test write on ims call handover event
@Test
@SmallTest
@@ -506,7 +541,7 @@ public class TelephonyMetricsTest extends TelephonyTest {
public void testWriteOnSetupDataCallResponse() throws Exception {
DataCallResponse response = new DataCallResponse.Builder()
.setCause(5)
- .setSuggestedRetryTime(6)
+ .setRetryDurationMillis(6)
.setId(7)
.setLinkStatus(8)
.setProtocolType(ApnSetting.PROTOCOL_IPV4V6)
@@ -536,7 +571,7 @@ public class TelephonyMetricsTest extends TelephonyTest {
assertEquals(6, respProto.suggestedRetryTimeMillis);
assertEquals(7, respProto.call.cid);
assertEquals(PDP_TYPE_IPV4V6, respProto.call.type);
- assertEquals(FAKE_IFNAME, respProto.call.iframe);
+ assertEquals(FAKE_IFNAME, respProto.call.ifname);
}
// Test write on deactivate data call response
@@ -560,14 +595,17 @@ public class TelephonyMetricsTest extends TelephonyTest {
@Test
@SmallTest
public void testWriteRilSendSms() throws Exception {
- mMetrics.writeRilSendSms(mPhone.getPhoneId(), 1, 2, 1);
- mMetrics.writeRilSendSms(mPhone.getPhoneId(), 4, 5, 2);
+ long fakeMessageId1 = 123123L;
+ long fakeMessageId2 = -987L;
- SmsResponse response = new SmsResponse(0, null, 123);
+ mMetrics.writeRilSendSms(mPhone.getPhoneId(), 1, 2, 1, fakeMessageId1);
+ mMetrics.writeRilSendSms(mPhone.getPhoneId(), 4, 5, 2, fakeMessageId2);
+
+ SmsResponse response = new SmsResponse(0, null, 123, fakeMessageId1);
mMetrics.writeOnRilSolicitedResponse(mPhone.getPhoneId(), 1, 0, RIL_REQUEST_SEND_SMS,
response);
- response = new SmsResponse(0, null, 456);
+ response = new SmsResponse(0, null, 456, fakeMessageId2);
mMetrics.writeOnRilSolicitedResponse(mPhone.getPhoneId(), 4, 0, RIL_REQUEST_SEND_SMS,
response);
TelephonyLog log = buildProto();
@@ -583,21 +621,25 @@ public class TelephonyMetricsTest extends TelephonyTest {
assertEquals(1, events[0].rilRequestId);
assertEquals(2, events[0].tech);
assertEquals(1, events[0].format);
+ assertEquals(fakeMessageId1, events[0].messageId);
assertEquals(SmsSession.Event.Type.SMS_SEND, events[1].type);
assertEquals(4, events[1].rilRequestId);
assertEquals(5, events[1].tech);
assertEquals(2, events[1].format);
+ assertEquals(fakeMessageId2, events[1].messageId);
assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[2].type);
assertEquals(1, events[2].rilRequestId);
assertEquals(1, events[2].error);
assertEquals(123, events[2].errorCode);
+ assertEquals(fakeMessageId1, events[2].messageId);
assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[3].type);
assertEquals(4, events[3].rilRequestId);
assertEquals(1, events[3].error);
assertEquals(456, events[3].errorCode);
+ assertEquals(fakeMessageId2, events[3].messageId);
}
// Test write phone state
@@ -834,4 +876,39 @@ public class TelephonyMetricsTest extends TelephonyTest {
assertEquals(false, event.imsCapabilities.videoOverLte);
assertEquals(false, event.imsCapabilities.utOverLte);
}
+
+
+ // Test write Bandwidth Stats
+ @Test
+ @SmallTest
+ public void testWriteBandwidthStats() throws Exception {
+ addBandwidthStats(LinkBandwidthEstimator.LINK_TX, TelephonyManager.NETWORK_TYPE_LTE,
+ NrMode.NR_NSA_MMWAVE);
+ addBandwidthStats(LinkBandwidthEstimator.LINK_RX, TelephonyManager.NETWORK_TYPE_LTE,
+ NrMode.NR_NSA_MMWAVE);
+ addBandwidthStats(LinkBandwidthEstimator.LINK_RX, TelephonyManager.NETWORK_TYPE_NR,
+ NrMode.NR_SA_MMWAVE);
+ TelephonyLog log = buildProto();
+
+ BandwidthEstimatorStats stats = log.bandwidthEstimatorStats;
+ assertEquals(1, stats.perRatTx.length);
+ assertEquals(2, stats.perRatRx.length);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.perRatTx[0].rat);
+ assertEquals(NrMode.NR_NSA_MMWAVE, stats.perRatTx[0].nrMode);
+ assertEquals(NUM_SIGNAL_LEVEL - 1, stats.perRatTx[0].perLevel.length);
+ assertEquals(2, stats.perRatTx[0].perLevel[0].count);
+ assertEquals(0, stats.perRatTx[0].perLevel[0].signalLevel);
+ assertEquals(400_000, stats.perRatTx[0].perLevel[0].avgBwKbps);
+ assertEquals(40, stats.perRatTx[0].perLevel[0].staticBwErrorPercent);
+ assertEquals(30, stats.perRatTx[0].perLevel[0].bwEstErrorPercent);
+ }
+
+ private void addBandwidthStats(int link, int dataRat, int nrMode) {
+ for (int i = 0; i < NUM_SIGNAL_LEVEL - 1; i++) {
+ mMetrics.writeBandwidthStats(link, dataRat, nrMode,
+ i, 20, 30, 300_000);
+ mMetrics.writeBandwidthStats(link, dataRat, nrMode,
+ i, 40, 50, 500_000);
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index d8db445183..f64bc6ea16 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -20,11 +20,12 @@ import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSIO
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
import static com.google.common.truth.Truth.assertThat;
@@ -41,7 +42,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.telephony.DisconnectCause;
import android.telephony.PreciseDisconnectCause;
import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
@@ -58,7 +58,7 @@ import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec;
import com.android.internal.telephony.protobuf.nano.MessageNano;
@@ -102,10 +102,6 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
@Mock private ImsPhoneCall mImsCall0;
@Mock private ImsPhoneCall mImsCall1;
- @Mock private SubscriptionInfo mSubInfo0;
- @Mock private SubscriptionInfo mSubInfo1;
- private List<SubscriptionInfo> mSubInfos;
-
private static class TestableVoiceCallSessionStats extends VoiceCallSessionStats {
private long mTimeMillis = 0L;
@@ -135,7 +131,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
super.setUp(getClass().getSimpleName());
replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mSecondPhone});
+ doReturn(CARRIER_ID_SLOT_0).when(mPhone).getCarrierId();
// mPhone's mSST/mServiceState has been set up by TelephonyTest
+ doReturn(CARRIER_ID_SLOT_1).when(mSecondPhone).getCarrierId();
+ doReturn(mSignalStrength).when(mSecondPhone).getSignalStrength();
doReturn(mSecondServiceStateTracker).when(mSecondPhone).getServiceStateTracker();
doReturn(mSecondServiceState).when(mSecondServiceStateTracker).getServiceState();
@@ -163,6 +162,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
doReturn(new UiccSlot[] {mPhysicalSlot}).when(mUiccController).getUiccSlots();
doReturn(mPhysicalSlot).when(mUiccController).getUiccSlot(eq(0));
+ doReturn(mPhysicalSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
doReturn(0).when(mUiccController).getSlotIdFromPhoneId(0);
doReturn(1).when(mUiccController).getSlotIdFromPhoneId(1);
@@ -176,17 +176,9 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
doReturn(PhoneConstants.PHONE_TYPE_GSM).when(mGsmConnection1).getPhoneType();
doReturn(false).when(mGsmConnection1).isEmergencyCall();
- mSubInfos = List.of(mSubInfo0, mSubInfo1);
- doReturn(0).when(mSubInfo0).getSimSlotIndex();
- doReturn(CARRIER_ID_SLOT_0).when(mSubInfo0).getCarrierId();
- doReturn(1).when(mSubInfo1).getSimSlotIndex();
- doReturn(CARRIER_ID_SLOT_1).when(mSubInfo1).getCarrierId();
-
mVoiceCallSessionStats0 = new TestableVoiceCallSessionStats(0, mPhone);
- mVoiceCallSessionStats0.onActiveSubscriptionInfoChanged(mSubInfos);
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(1, mSecondPhone);
- mVoiceCallSessionStats1.onActiveSubscriptionInfoChanged(mSubInfos);
mVoiceCallSessionStats1.onServiceStateChanged(mSecondServiceState);
}
@@ -210,13 +202,18 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_REMOTE_CALL_DECLINE);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 200;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.DIALING).when(mImsCall0).getState();
@@ -258,10 +255,11 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_SIP_FORBIDDEN);
expectedCall.setupFailed = true;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 2200L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.DIALING).when(mImsCall0).getState();
@@ -296,14 +294,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 200;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
expectedCall.disconnectExtraMessage = "normal call clearing";
- RawVoiceCallRatUsage expectedRatUsage =
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 100000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.DIALING).when(mImsCall0).getState();
@@ -351,11 +352,14 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -393,13 +397,16 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -434,6 +441,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
doReturn(mInactiveCard).when(mEsimSlot).getUiccCard();
doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -446,7 +454,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isMultiSim = false; // DSDS with one active SIM profile should not count
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -474,6 +485,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -486,7 +498,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isMultiSim = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -514,6 +529,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mSecondServiceState).getVoiceNetworkType();
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(2000L).when(mImsConnection1).getCreateTime();
@@ -526,7 +542,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
mVoiceCallSessionStats1.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall1).getState();
@@ -563,7 +582,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isEmergency = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -601,7 +623,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isRoaming = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -638,10 +663,13 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask =
1L << AudioCodec.AUDIO_CODEC_AMR | 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -686,21 +714,25 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.ratSwitchCount = 2L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 4000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 4000L, 6000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 6000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -732,7 +764,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
expectedRatUsageLte, expectedRatUsageHspa, expectedRatUsageUmts
},
ratUsage.get());
@@ -754,7 +786,10 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.rttEnabled = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -791,9 +826,12 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.rttEnabled = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -839,13 +877,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 1L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
+ expectedCall0.bandAtEnd = 0;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -859,26 +901,30 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 2L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall1.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 80000L, 100000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0,
TelephonyManager.NETWORK_TYPE_UMTS,
100000L,
120000L,
1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -937,7 +983,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
expectedRatUsageLte, expectedRatUsageHspa, expectedRatUsageUmts
},
ratUsage.get());
@@ -959,13 +1005,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 0;
expectedCall0.ratSwitchCount = 2L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall0.bandAtEnd = 0;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -979,26 +1029,30 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 1;
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall1.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 80000L, 100000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0,
TelephonyManager.NETWORK_TYPE_UMTS,
100000L,
120000L,
1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1057,7 +1111,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
expectedRatUsageLte, expectedRatUsageHspa, expectedRatUsageUmts
},
ratUsage.get());
@@ -1079,9 +1133,12 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 0L;
@@ -1099,19 +1156,23 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall1.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 80000L, 90000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1166,7 +1227,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageHspa},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageHspa},
ratUsage.get());
}
@@ -1183,19 +1244,24 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.NORMAL);
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
+ expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.CALL_REJECTED;
expectedCall.ratSwitchCount = 1L;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 3000L, 15000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
@@ -1226,7 +1292,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1243,16 +1309,20 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.LOST_SIGNAL);
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.ratSwitchCount = 1L;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 3000L, 15000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
@@ -1276,7 +1346,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1292,20 +1362,25 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.NORMAL);
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
+ expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.ratSwitchCount = 1L;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 3000L, 100000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
@@ -1338,7 +1413,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1357,13 +1432,18 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
DisconnectCause.NORMAL);
expectedCall.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
+ expectedCall.setupDurationMillis = 0;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.CALL_REJECTED;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 2500L, 15000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
@@ -1404,14 +1484,19 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_UMTS,
DisconnectCause.NORMAL);
- expectedCall.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST;
+ expectedCall.setupDuration =
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ expectedCall.setupDurationMillis = 500;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 2500L, 100000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
@@ -1460,19 +1545,23 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccFailureCount = 2L;
expectedCall.ratSwitchCount = 1L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 10000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 10000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1513,7 +1602,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1534,14 +1623,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccCancellationCount = 2L;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1602,7 +1694,8 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
DisconnectCause.NORMAL);
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccCancellationCount = 1L;
expectedCall.srvccFailureCount = 1L;
@@ -1610,14 +1703,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
expectedCall.ratSwitchCount = 1L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 7000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 7000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// IMS call created
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1667,7 +1763,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1693,13 +1789,17 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
DisconnectCause.NORMAL);
expectedCall0.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 1L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall0.bandAtEnd = 0;
expectedCall0.srvccCompleted = true;
expectedCall0.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
// call 1 starts later, MT
@@ -1718,21 +1818,25 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
expectedCall1.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall1.bandAtEnd = 0;
expectedCall1.srvccCompleted = true;
expectedCall1.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 80000L, 120000L, 2L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1795,7 +1899,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1817,11 +1921,15 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_IWLAN,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1862,11 +1970,15 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
TelephonyManager.NETWORK_TYPE_IWLAN,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1889,8 +2001,8 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
}
- private AtomicReference<RawVoiceCallRatUsage[]> setupRatUsageCapture() {
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = new AtomicReference<>(null);
+ private AtomicReference<VoiceCallRatUsage[]> setupRatUsageCapture() {
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = new AtomicReference<>(null);
doAnswer(invocation -> {
VoiceCallRatTracker tracker = (VoiceCallRatTracker) invocation.getArguments()[0];
ratUsage.set(tracker.toProto());
@@ -1906,14 +2018,18 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
call.bearerAtEnd = bearer;
call.direction = direction;
call.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
+ call.setupDurationMillis = 0;
call.setupFailed = true;
call.disconnectReasonCode = reason;
call.disconnectExtraCode = 0;
call.disconnectExtraMessage = "";
call.ratAtStart = rat;
+ call.ratAtConnected = rat;
call.ratAtEnd = rat;
+ call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
call.codecBitmask = 0L;
+ call.mainCodecQuality = VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
call.simSlotIndex = 0;
call.isMultiSim = false;
call.isEsim = false;
@@ -1925,6 +2041,7 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
call.isEmergency = false;
call.isRoaming = false;
call.setupBeginMillis = 0L;
+ call.signalStrengthAtEnd = 2;
return call;
}
@@ -1935,14 +2052,18 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
call.bearerAtEnd = bearer;
call.direction = direction;
call.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
+ call.setupDurationMillis = 0;
call.setupFailed = true;
call.disconnectReasonCode = reason;
call.disconnectExtraCode = 0;
call.disconnectExtraMessage = "";
call.ratAtStart = rat;
+ call.ratAtConnected = rat;
call.ratAtEnd = rat;
+ call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
call.codecBitmask = 0L;
+ call.mainCodecQuality = VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
call.simSlotIndex = 1;
call.isMultiSim = true;
call.isEsim = true;
@@ -1954,12 +2075,13 @@ public class VoiceCallSessionStatsTest extends TelephonyTest {
call.isEmergency = false;
call.isRoaming = false;
call.setupBeginMillis = 0L;
+ call.signalStrengthAtEnd = 2;
return call;
}
- private static RawVoiceCallRatUsage makeRatUsageProto(
+ private static VoiceCallRatUsage makeRatUsageProto(
int carrierId, int rat, long beginMillis, long endMillis, long callCount) {
- RawVoiceCallRatUsage usage = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage = new VoiceCallRatUsage();
usage.carrierId = carrierId;
usage.rat = rat;
usage.totalDurationMillis = endMillis - beginMillis;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
index 19d57f92a3..c433bd0dcb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzStateMachineImplTest.java
@@ -621,7 +621,7 @@ public class NitzStateMachineImplTest extends TelephonyTest {
void commitLatest() {
if (hasBeenSet()) {
- mInitialValue = mValues.getLast();
+ mInitialValue = mValues.getFirst();
mValues.clear();
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneLookupHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneLookupHelperTest.java
index af1be4cafc..990f568695 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneLookupHelperTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneLookupHelperTest.java
@@ -24,6 +24,7 @@ import static com.android.internal.telephony.nitz.TimeZoneLookupHelper.CountryRe
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -37,8 +38,10 @@ import com.android.internal.telephony.nitz.TimeZoneLookupHelper.CountryResult;
import org.junit.Before;
import org.junit.Test;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
+import java.util.List;
import java.util.concurrent.TimeUnit;
public class TimeZoneLookupHelperTest {
@@ -447,6 +450,18 @@ public class TimeZoneLookupHelperTest {
assertTrue(mTimeZoneLookupHelper.countryUsesUtc("gb", NH_WINTER_TIME_MILLIS));
}
+ @Test
+ public void regressionTest_Bug167653885() {
+ // This NITZ caused an error in Android R because lookupByNitz was returning a time zone
+ // known to android.icu.util.TimeZone but not java.util.TimeZone.
+ NitzData nitzData = NitzData.parse("20/05/08,04:15:48+08,00");
+ OffsetResult offsetResult = mTimeZoneLookupHelper.lookupByNitz(nitzData);
+ assertNotNull(offsetResult);
+
+ List<String> knownIds = Arrays.asList(java.util.TimeZone.getAvailableIDs());
+ assertTrue(knownIds.contains(offsetResult.getTimeZone().getID()));
+ }
+
/**
* Assert the time zone in the OffsetResult has the expected properties at the specified time.
*/
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
index 72ff321057..47ebc67ee0 100644..100755
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
@@ -15,18 +15,25 @@
*/
package com.android.internal.telephony.uicc;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.ContentValues;
import android.os.HandlerThread;
import android.os.Message;
import android.os.AsyncResult;
+import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.telephony.IccPhoneBookInterfaceManager;
+import com.android.internal.telephony.IccProvider;
import com.android.internal.telephony.TelephonyTest;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-
-import android.test.suitebuilder.annotation.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -34,10 +41,6 @@ import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.anyInt;
-
import java.util.Arrays;
import java.util.List;
@@ -47,6 +50,8 @@ public class IccPhoneBookInterfaceManagerTest extends TelephonyTest {
private AdnRecordCache mAdnRecordCache;
@Mock
private AdnRecord mAdnRecord;
+ @Mock
+ private SimPhonebookRecordCache mSimPhonebookRecordCache;
private IccPhoneBookInterfaceManager mIccPhoneBookInterfaceMgr;
private IccPhoneBookInterfaceManagerHandler mIccPhoneBookInterfaceManagerHandler;
private List<AdnRecord> mAdnList = Arrays.asList(mAdnRecord);
@@ -82,9 +87,19 @@ public class IccPhoneBookInterfaceManagerTest extends TelephonyTest {
}
}).when(mAdnRecordCache).requestLoadAllAdnLike(anyInt(), anyInt(), (Message) anyObject());
+ doAnswer(invocation -> {
+ Message response = (Message) invocation.getArguments()[0];
+ //set result for load ADN EF
+ AsyncResult.forMessage(response).result = mAdnList;
+ response.sendToTarget();
+ return null;
+ }).when(mSimPhonebookRecordCache).requestLoadAllPbRecords((Message)anyObject());
mIccPhoneBookInterfaceManagerHandler = new IccPhoneBookInterfaceManagerHandler(TAG);
mIccPhoneBookInterfaceManagerHandler.start();
+
waitUntilReady();
+ replaceInstance(IccPhoneBookInterfaceManager.class,
+ "mSimPbRecordCache", mIccPhoneBookInterfaceMgr, mSimPhonebookRecordCache);
}
@After
@@ -93,9 +108,11 @@ public class IccPhoneBookInterfaceManagerTest extends TelephonyTest {
mIccPhoneBookInterfaceManagerHandler.join();
super.tearDown();
}
+
@Test
@SmallTest
public void testAdnEFLoadWithFailure() {
+ doReturn(false).when(mSimPhonebookRecordCache).isEnabled();
List<AdnRecord> adnListResult = mIccPhoneBookInterfaceMgr.getAdnRecordsInEf(
IccConstants.EF_ADN);
assertEquals(mAdnList, adnListResult);
@@ -116,4 +133,88 @@ public class IccPhoneBookInterfaceManagerTest extends TelephonyTest {
//verify the previous read is not got affected
assertEquals(mAdnList, adnListResult);
}
+
+ @Test
+ @SmallTest
+ public void testAdnEFLoadByPbCacheWithFailure() {
+ doReturn(true).when(mSimPhonebookRecordCache).isEnabled();
+ List<AdnRecord> adnListResult = mIccPhoneBookInterfaceMgr.getAdnRecordsInEf(
+ IccConstants.EF_ADN);
+ assertEquals(mAdnList, adnListResult);
+ //mock a ADN Ef load failure
+ doAnswer(invocation -> {
+ Message response = (Message) invocation.getArguments()[0];
+ AsyncResult.forMessage(response).exception = new RuntimeException();
+ response.sendToTarget();
+ return null;
+ }).when(mSimPhonebookRecordCache).requestLoadAllPbRecords((Message) anyObject());
+ List<AdnRecord> adnListResultNew = mIccPhoneBookInterfaceMgr.getAdnRecordsInEf(
+ IccConstants.EF_ADN);
+ //the later read return null due to exception
+ assertNull(adnListResultNew);
+ //verify the previous read is not got affected
+ assertEquals(mAdnList, adnListResult);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateAdnRecord() {
+ doReturn(false).when(mSimPhonebookRecordCache).isEnabled();
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message response = (Message) invocation.getArguments()[4];
+ //set result for update ADN EF
+ AsyncResult.forMessage(response).exception = null;
+ response.sendToTarget();
+ return null;
+ }
+ }).when(mAdnRecordCache).updateAdnBySearch(
+ anyInt(), any(), any(),
+ any(), (Message) anyObject());
+
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_TAG, "");
+ values.put(IccProvider.STR_NUMBER, "");
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, "test");
+ values.put(IccProvider.STR_NEW_NUMBER, "123456");
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+
+ boolean result = mIccPhoneBookInterfaceMgr.updateAdnRecordsInEfBySearchForSubscriber(
+ IccConstants.EF_ADN, values , null);
+
+ assertTrue(result);
+ }
+
+ @Test
+ @SmallTest
+ public void testUpdateAdnRecordByPbCache() {
+ doReturn(true).when(mSimPhonebookRecordCache).isEnabled();
+ doAnswer(invocation -> {
+ Message response = (Message) invocation.getArguments()[2];
+ //set result for update ADN EF
+ AsyncResult.forMessage(response).exception = null;
+ response.sendToTarget();
+ return null;
+ }).when(mSimPhonebookRecordCache).updateSimPbAdnBySearch(any(),
+ any(), (Message) anyObject());
+
+ ContentValues values = new ContentValues();
+ values.put(IccProvider.STR_TAG, "");
+ values.put(IccProvider.STR_NUMBER, "");
+ values.put(IccProvider.STR_EMAILS, "");
+ values.put(IccProvider.STR_ANRS, "");
+ values.put(IccProvider.STR_NEW_TAG, "test");
+ values.put(IccProvider.STR_NEW_NUMBER, "123456");
+ values.put(IccProvider.STR_NEW_EMAILS, "");
+ values.put(IccProvider.STR_NEW_ANRS, "");
+
+ boolean result = mIccPhoneBookInterfaceMgr.updateAdnRecordsInEfBySearchForSubscriber(
+ IccConstants.EF_ADN, values , "12");
+
+ assertTrue(result);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
index 417c532f53..500821b4ff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
@@ -48,11 +48,16 @@ import android.util.Log;
import android.util.Pair;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo;
+import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+
public class IccRecordsTest extends TelephonyTest {
private IccRecords mIccRecords;
@@ -244,4 +249,102 @@ public class IccRecordsTest extends TelephonyTest {
assertTrue("Two responses must be different.", !response.equals(response2));
}
+ @Test
+ public void testOperatorPlmnInfo() {
+ String plmn = "123456";
+ int lacTacStart = 0x0000;
+ int lacTacEnd = 0xFFFE;
+ int pnnIndex = 2;
+
+ OperatorPlmnInfo opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex);
+ assertEquals(opi.getPnnIdx(plmn, lacTacStart), pnnIndex - 1);
+ assertEquals(opi.getPnnIdx(plmn, lacTacEnd), pnnIndex - 1);
+ assertEquals(opi.getPnnIdx(plmn, 0xFFFF), pnnIndex - 1);
+ assertEquals(opi.getPnnIdx("654321", 0xFFFF), -1);
+ assertEquals(opi.getPnnIdx("12345", 0xFFFF), -1);
+
+ lacTacStart = 0x0001;
+ lacTacEnd = 0x1FFF;
+ opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex);
+ assertEquals(opi.getPnnIdx(plmn, 2), pnnIndex - 1);
+ assertEquals(opi.getPnnIdx(plmn, 0x2FFF), -1);
+ assertEquals(opi.getPnnIdx(plmn, 0xFFFF), -1);
+
+ plmn = "123DDD";
+ opi = new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex);
+ assertEquals(opi.getPnnIdx("123123", lacTacStart), pnnIndex - 1);
+ assertEquals(opi.getPnnIdx("12345", lacTacStart), -1);
+ }
+
+ @Test
+ public void testGetNetworkNameForPlmn() {
+ // Set up PNN
+ String fullName1 = "Name full 1";
+ String shortName1 = "Name short 1";
+ String fullName2 = "Name full 2";
+ String shortName2 = "Name short 2";
+ List<PlmnNetworkName> pnns = new ArrayList<PlmnNetworkName>();
+ pnns.add(new PlmnNetworkName(fullName1, shortName1));
+ pnns.add(new PlmnNetworkName(fullName2, shortName2));
+ pnns.add(new PlmnNetworkName(null, shortName2));
+ PlmnNetworkName[] pnnsArray = pnns.toArray(new PlmnNetworkName[0]);
+
+ // Set up OPL
+ String plmn1 = "345678";
+ String plmn2 = "456789";
+ String plmn3 = "567890";
+ int lacTacStart = 0x0000;
+ int lacTacEnd = 0xFFFE;
+ int pnnIndex = 1;
+ List<OperatorPlmnInfo> opl = new ArrayList<OperatorPlmnInfo>();
+ opl.add(new OperatorPlmnInfo(plmn1, lacTacStart, lacTacEnd, pnnIndex));
+ opl.add(new OperatorPlmnInfo(plmn2, lacTacStart, lacTacEnd, pnnIndex + 1));
+ opl.add(new OperatorPlmnInfo(plmn3, lacTacStart, lacTacEnd, pnnIndex + 2));
+ OperatorPlmnInfo[] oplArray = opl.toArray(new OperatorPlmnInfo[0]);
+
+ // Test
+ assertNull(IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, null, 0));
+ assertEquals(fullName1, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray,
+ plmn1, 0));
+ assertEquals(fullName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray,
+ plmn2, 0));
+ assertEquals(shortName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray,
+ plmn3, 0));
+ }
+
+ @Test
+ public void testGetNetworkNameForPlmnFromPnnOpl() {
+ // Set up PNN
+ String fullName1 = "Name full 1";
+ String shortName1 = "Name short 1";
+ String fullName2 = "Name full 2";
+ String shortName2 = "Name short 2";
+ List<PlmnNetworkName> pnns = new ArrayList<PlmnNetworkName>();
+ pnns.add(new PlmnNetworkName(fullName1, shortName1));
+ pnns.add(new PlmnNetworkName(fullName2, shortName2));
+ pnns.add(new PlmnNetworkName(null, shortName2));
+ PlmnNetworkName[] pnnsArray = pnns.toArray(new PlmnNetworkName[0]);
+
+ // Set up OPL
+ String plmn1 = "345678";
+ String plmn2 = "456789";
+ String plmn3 = "567890";
+ int lacTacStart = 0x0000;
+ int lacTacEnd = 0xFFFE;
+ int pnnIndex = 1;
+ List<OperatorPlmnInfo> opl = new ArrayList<OperatorPlmnInfo>();
+ opl.add(new OperatorPlmnInfo(plmn1, lacTacStart, lacTacEnd, pnnIndex));
+ opl.add(new OperatorPlmnInfo(plmn2, lacTacStart, lacTacEnd, pnnIndex + 1));
+ opl.add(new OperatorPlmnInfo(plmn3, lacTacStart, lacTacEnd, pnnIndex + 2));
+ OperatorPlmnInfo[] oplArray = opl.toArray(new OperatorPlmnInfo[0]);
+
+ // Test
+ assertNull(IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray, null, 0));
+ assertEquals(fullName1, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray,
+ plmn1, 0));
+ assertEquals(fullName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray,
+ plmn2, 0));
+ assertEquals(shortName2, IccRecords.getNetworkNameForPlmnFromPnnOpl(pnnsArray, oplArray,
+ plmn3, 0));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccUtilsTest.java
index d2cebb330d..02603828a1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccUtilsTest.java
@@ -16,20 +16,29 @@
package com.android.internal.telephony.uicc;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
import android.text.TextUtils;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Arrays;
import java.util.List;
-public class IccUtilsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class IccUtilsTest {
private static final int NUM_FPLMN = 3;
private static final List<String> FPLMNS_SAMPLE = Arrays.asList("123456", "12345", "54321");
private static final int DATA_LENGTH = 12;
+ private static final String EMOJI = new String(Character.toChars(0x1F642));
- @SmallTest
- public void testEncodeFplmns() {
+ @Test
+ public void encodeFplmns() {
byte[] encodedFplmns = IccUtils.encodeFplmns(FPLMNS_SAMPLE, DATA_LENGTH);
int numValidPlmns = 0;
for (int i = 0; i < NUM_FPLMN; i++) {
@@ -40,4 +49,58 @@ public class IccUtilsTest extends AndroidTestCase {
}
assertEquals(NUM_FPLMN, numValidPlmns);
}
+
+ @Test
+ public void stringToAdnStringField_gsmBasic() {
+ String alphaTag = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 1234567890"
+ + "!@#$%&*()_+,.?;:<>";
+
+ byte[] result = IccUtils.stringToAdnStringField(alphaTag);
+ assertThat(result.length).isEqualTo(alphaTag.length());
+ assertThat(IccUtils.adnStringFieldToString(result, 0, result.length))
+ .isEqualTo(alphaTag);
+ }
+
+ @Test
+ public void stringToAdnStringField_nonGsm() {
+ String alphaTag = "日本";
+
+ byte[] result = IccUtils.stringToAdnStringField(alphaTag);
+ assertThat(result.length).isEqualTo(alphaTag.length() * 2 + 1);
+ assertThat(result[0]).isEqualTo((byte) 0x80);
+ assertThat(IccUtils.adnStringFieldToString(result, 0, result.length))
+ .isEqualTo(alphaTag);
+ }
+
+ @Test
+ public void stringToAdnStringField_mixed() {
+ String alphaTag = "ni=日;hon=本;";
+
+ byte[] result = IccUtils.stringToAdnStringField(alphaTag);
+ assertThat(result.length).isEqualTo(alphaTag.length() * 2 + 1);
+ assertThat(result[0]).isEqualTo((byte) 0x80);
+ assertThat(IccUtils.adnStringFieldToString(result, 0, result.length))
+ .isEqualTo(alphaTag);
+ }
+
+ @Test
+ public void stringToAdnStringField_gsmWithEmoji() {
+ String alphaTag = ":)=" + EMOJI + ";";
+
+ byte[] result = IccUtils.stringToAdnStringField(alphaTag);
+ assertThat(result.length).isEqualTo(alphaTag.length() * 2 + 1);
+ assertThat(IccUtils.adnStringFieldToString(result, 0, result.length))
+ .isEqualTo(alphaTag);
+ }
+
+ @Test
+ public void stringToAdnStringField_mixedWithEmoji() {
+ String alphaTag = "ni=日;hon=本;:)=" + EMOJI + ";";
+
+ byte[] result = IccUtils.stringToAdnStringField(alphaTag);
+ assertThat(result.length).isEqualTo(alphaTag.length() * 2 + 1);
+ assertThat(result[0]).isEqualTo((byte) 0x80);
+ assertThat(IccUtils.adnStringFieldToString(result, 0, result.length))
+ .isEqualTo(alphaTag);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java
new file mode 100644
index 0000000000..f165a9ef4d
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/PinStorageTest.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.uicc;
+
+import static com.android.internal.telephony.uicc.IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.os.PersistableBundle;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PinStorageTest extends TelephonyTest {
+ private static final String ICCID_1 = "89010003006562472370";
+ private static final String ICCID_2 = "89010003006562472399";
+ private static final String ICCID_INVALID = "1234";
+
+ private int mBootCount;
+ private int mSimulatedRebootsCount;
+ private PinStorage mPinStorage;
+
+ private void simulateReboot() {
+ mSimulatedRebootsCount++;
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.BOOT_COUNT, mBootCount + mSimulatedRebootsCount);
+
+ mPinStorage = new PinStorage(mContext);
+ mPinStorage.mShortTermSecretKeyDurationMinutes = 0;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+
+ // Store boot count, so that correct value can be restored at the end.
+ mBootCount = Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.BOOT_COUNT, -1);
+ mSimulatedRebootsCount = 0;
+
+ // Clear shared preferences.
+ PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
+ .edit().clear().commit();
+ // Enable PIN storage in resources
+ mContextFixture.putBooleanResource(
+ R.bool.config_allow_pin_storage_for_unattended_reboot, true);
+ // Remaining setup
+ doReturn(ICCID_1).when(mPhone).getFullIccSerialNumber();
+ // Simulate the device is not secure by default
+ when(mKeyguardManager.isDeviceSecure()).thenReturn(false);
+ when(mKeyguardManager.isDeviceLocked()).thenReturn(false);
+
+ mPinStorage = new PinStorage(mContext);
+ mPinStorage.mShortTermSecretKeyDurationMinutes = 0;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ // Restore boot count
+ if (mBootCount == -1) {
+ Settings.Global.resetToDefaults(
+ mContext.getContentResolver(), Settings.Global.BOOT_COUNT);
+ } else {
+ Settings.Global.putInt(
+ mContext.getContentResolver(), Settings.Global.BOOT_COUNT, mBootCount);
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_withoutReboot_pinCannotBeRetrieved() {
+ mPinStorage.storePin("1234", 0);
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_normalReboot_pinCannotBeRetrieved() {
+ mPinStorage.storePin("1234", 0);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_crash_pinCannotBeRetrieved() {
+ mPinStorage.storePin("1234", 0);
+
+ // Simulate crash
+ mPinStorage = new PinStorage(mContext);
+ mPinStorage.mShortTermSecretKeyDurationMinutes = 0;
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_unattendedReboot_pinCanBeRetrievedOnce() {
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ // PIN can be retrieved only once after unattended reboot
+ assertThat(mPinStorage.getPin(0)).isEqualTo("1234");
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_unattendedReboot_deviceIsLocked() {
+ // Simulate the device is still locked
+ when(mKeyguardManager.isDeviceSecure()).thenReturn(true);
+ when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+ simulateReboot();
+
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_ERROR);
+
+ simulateReboot();
+
+ // PIN cannot be retrieved
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_unattendedReboot_pinIsRemovedAfterDelay() {
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ // Move time forward by 60 seconds
+ moveTimeForward(60000);
+ processAllMessages();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+
+ // Simulate a second unattended reboot to make sure that PIN was deleted.
+ result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_unattendedRebootNotDone_pinCannotBeRetrieved() {
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ // Move time forward by 60 seconds before simulating reboot
+ moveTimeForward(60000);
+ processAllMessages();
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_unattendedReboot_iccidChange() {
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ // Switch to a different ICCID in the device after the reboot
+ doReturn(ICCID_2).when(mPhone).getFullIccSerialNumber();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+
+ // Switch back to the initial ICCID to make sure that PIN was deleted.
+ doReturn(ICCID_1).when(mPhone).getFullIccSerialNumber();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void clearPin_pinCannotBeRetrieved() {
+ mPinStorage.storePin("1234", 0);
+ mPinStorage.clearPin(0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_pinChanged_pinIsUpdated() {
+ mPinStorage.storePin("1234", 0);
+ mPinStorage.storePin("5678", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("5678");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_pinTooShort_pinIsNotStored() {
+ mPinStorage.storePin("12", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_pinTooLong_pinIsNotStored() {
+ mPinStorage.storePin("123456789", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_invalidIccid_pinIsNotStored() {
+ doReturn(ICCID_INVALID).when(mPhone).getFullIccSerialNumber();
+
+ mPinStorage.storePin("1234", 0);
+ int result = mPinStorage.prepareUnattendedReboot();
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_disabledInResources_pinIsNotStored() {
+ mContextFixture.putBooleanResource(
+ R.bool.config_allow_pin_storage_for_unattended_reboot, false);
+
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_disabledInResources_containsSimWithPinEnabledAndVerified() {
+ mContextFixture.putBooleanResource(
+ R.bool.config_allow_pin_storage_for_unattended_reboot, false);
+
+ when(mUiccController.getUiccProfileForPhone(anyInt())).thenReturn(mUiccProfile);
+ when(mUiccCardApplication3gpp.getPin1State()).thenReturn(PINSTATE_ENABLED_VERIFIED);
+
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_disabledInCarrierConfig_pinIsNotStored() {
+ PersistableBundle carrierConfigs = new PersistableBundle();
+ carrierConfigs.putBoolean(
+ CarrierConfigManager.KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, false);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(carrierConfigs);
+
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_changeToDisabledInCarrierConfig_pinIsRemoved() {
+ mPinStorage.storePin("1234", 0);
+
+ // Simulate change in the carrier configuration
+ PersistableBundle carrierConfigs = new PersistableBundle();
+ carrierConfigs.putBoolean(
+ CarrierConfigManager.KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, false);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(carrierConfigs);
+ final Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 0);
+ mContext.sendBroadcast(intent);
+ processAllMessages();
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_simIsRemoved_pinIsRemoved() {
+ mPinStorage.storePin("1234", 0);
+
+ // SIM is removed
+ final Intent intent = new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+ intent.putExtra(PhoneConstants.PHONE_KEY, 0);
+ intent.putExtra(TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_ABSENT);
+ mContext.sendBroadcast(intent);
+ processAllMessages();
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+
+ @Test
+ @SmallTest
+ public void storePin_simReadyAfterUnattendedReboot_pinIsRemoved() {
+ mPinStorage.storePin("1234", 0);
+
+ int result = mPinStorage.prepareUnattendedReboot();
+ assertThat(result).isEqualTo(TelephonyManager.PREPARE_UNATTENDED_REBOOT_SUCCESS);
+
+ simulateReboot();
+
+ // SIM is fully loaded before cached PIN is used.
+ final Intent intent = new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
+ intent.putExtra(PhoneConstants.PHONE_KEY, 0);
+ intent.putExtra(TelephonyManager.EXTRA_SIM_STATE, TelephonyManager.SIM_STATE_LOADED);
+ mContext.sendBroadcast(intent);
+ processAllMessages();
+
+ assertThat(mPinStorage.getPin(0)).isEqualTo("");
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/RuimRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/RuimRecordsTest.java
new file mode 100755
index 0000000000..70a1577a49
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/RuimRecordsTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import com.android.internal.telephony.TelephonyTest;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+
+public class RuimRecordsTest extends TelephonyTest {
+
+ private RuimRecords mRuimRecords;
+
+ private class RuimRecordsTestHandler extends HandlerThread {
+ private RuimRecordsTestHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mRuimRecords = new RuimRecords(mUiccCardApplication3gpp2, mContext, mSimulatedCommands);
+ setReady(true);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ new RuimRecordsTestHandler(TAG).start();
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testCsimImsiLoaded() {
+ RuimRecords.EfCsimImsimLoaded mImsiLoaded = mRuimRecords.new EfCsimImsimLoaded();
+ AsyncResult ar = new AsyncResult(null, null, null);
+ mImsiLoaded.onRecordLoaded(ar);
+ String mccmnc = mRuimRecords.getRUIMOperatorNumeric();
+ assertNull(mccmnc);
+
+ byte[] byteArray = new byte[]{0, 19, 3, 75, 68, 88, 99, (byte)128, (byte)209, 0};
+ AsyncResult ar2 = new AsyncResult(null, byteArray, null);
+ mImsiLoaded.onRecordLoaded(ar2);
+ mccmnc = mRuimRecords.getRUIMOperatorNumeric();
+ assertNotNull(mccmnc);
+ assertEquals("310008", mccmnc);
+ }
+
+ @Test
+ public void testCsimImsiDecode() {
+ RuimRecords.EfCsimImsimLoaded efCsimImsimLoaded = mRuimRecords.new EfCsimImsimLoaded();
+
+ // mcc + mnc + min
+ byte[] byteArray = new byte[]{0, 19, 3, 75, 68, 88, 99, (byte)128, (byte)209, 0};
+ String imsi = efCsimImsimLoaded.decodeImsi(byteArray);
+
+ assertEquals("310008984641186", imsi);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
index 80f88670ec..2e375755f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
@@ -19,6 +19,7 @@ package com.android.internal.telephony.uicc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -35,7 +36,10 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.uicc.IccRecords.OperatorPlmnInfo;
+import com.android.internal.telephony.uicc.IccRecords.PlmnNetworkName;
import org.junit.After;
import org.junit.Before;
@@ -286,4 +290,113 @@ public class SIMRecordsTest extends TelephonyTest {
assertNull(ar.exception);
assertNull(ar.result);
}
+
+ @Test
+ public void testGetEfPnn() {
+ ArrayList<byte[]> rawPnns = new ArrayList<byte[]>();
+ List<PlmnNetworkName> targetPnns = new ArrayList<PlmnNetworkName>();
+
+ String name = "Test 1";
+ rawPnns.add(encodePnn(name));
+ targetPnns.add(new PlmnNetworkName(name, null));
+ name = "Test 2";
+ rawPnns.add(encodePnn(name));
+ targetPnns.add(new PlmnNetworkName(name, null));
+
+ Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_PNN_DONE);
+ AsyncResult ar = AsyncResult.forMessage(message, rawPnns, null);
+ mSIMRecordsUT.handleMessage(message);
+ List<PlmnNetworkName> parsedPnns = Arrays.asList(mSIMRecordsUT.getPnns());
+
+ assertEquals(parsedPnns, targetPnns);
+ }
+
+ private static byte[] encodePnn(String name) {
+ byte[] gsm7BitName = new byte[] {};
+ try {
+ gsm7BitName = GsmAlphabet.stringToGsm7BitPacked(name);
+ gsm7BitName[0] = (byte) (name.length() % 8 | 0x80);
+ } catch (Exception ex) {
+ fail("SimRecordsTest: GsmAlphabet.stringToGsm7BitPacked() exception:" + ex);
+ }
+
+ byte[] encodedName = new byte[gsm7BitName.length + 2];
+ encodedName[0] = 0x43;
+ encodedName[1] = (byte) gsm7BitName.length;
+ System.arraycopy(gsm7BitName, 0, encodedName, 2, gsm7BitName.length);
+
+ return encodedName;
+ }
+
+ @Test
+ public void testGetEfOpl() {
+ ArrayList<byte[]> rawOpl = new ArrayList<byte[]>();
+ List<OperatorPlmnInfo> targetOpl = new ArrayList<OperatorPlmnInfo>();
+
+ // OperatorPlmnInfo 1
+ String plmn = "123456";
+ int lacTacStart = 0x0000;
+ int lacTacEnd = 0xFFFE;
+ int pnnIndex = 0;
+
+ rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex));
+ targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex));
+
+ Message message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
+ AsyncResult ar = AsyncResult.forMessage(message, rawOpl, null);
+ mSIMRecordsUT.handleMessage(message);
+ List<OperatorPlmnInfo> parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
+
+ assertEquals(targetOpl, parsedOpl);
+
+ // OperatorPlmnInfo 2
+ plmn = "123DDD";
+ lacTacStart = 0x0000;
+ lacTacEnd = 0xFFFE;
+ pnnIndex = 123;
+
+ rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex));
+ targetOpl.add(new OperatorPlmnInfo(plmn, lacTacStart, lacTacEnd, pnnIndex));
+
+ message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
+ ar = AsyncResult.forMessage(message, rawOpl, null);
+ mSIMRecordsUT.handleMessage(message);
+ parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
+
+ assertEquals(targetOpl, parsedOpl);
+
+ // OperatorPlmnInfo 3
+ plmn = "123";
+ lacTacStart = 0x0000;
+ lacTacEnd = 0xFFFE;
+ pnnIndex = 123;
+
+ rawOpl.add(encodeOpl(plmn, lacTacStart, lacTacEnd, pnnIndex));
+
+ message = mSIMRecordsUT.obtainMessage(SIMRecords.EVENT_GET_OPL_DONE);
+ ar = AsyncResult.forMessage(message, rawOpl, null);
+ mSIMRecordsUT.handleMessage(message);
+ parsedOpl = Arrays.asList(mSIMRecordsUT.getOpl());
+
+ assertEquals(targetOpl, parsedOpl);
+ }
+
+ private byte[] encodeOpl(String plmn, int lacTacStart, int lacTacEnd, int pnnIndex) {
+ byte[] data = new byte[8];
+
+ if (plmn.length() == 5 || plmn.length() == 6) {
+ IccUtils.stringToBcdPlmn(plmn, data, 0);
+ } else {
+ data[0] = (byte) 0xFF;
+ data[1] = (byte) 0xFF;
+ data[2] = (byte) 0xFF;
+ }
+ data[3] = (byte) (lacTacStart >>> 8);
+ data[4] = (byte) lacTacStart;
+ data[5] = (byte) (lacTacEnd >>> 8);
+ data[6] = (byte) lacTacEnd;
+ data[7] = (byte) pnnIndex;
+
+ return data;
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/SimPhonebookRecordCacheTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/SimPhonebookRecordCacheTest.java
new file mode 100644
index 0000000000..24db77daa2
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/SimPhonebookRecordCacheTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.AsyncResult;
+import android.os.HandlerThread;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SimPhonebookRecordCacheTest extends TelephonyTest {
+ private static final int EVENT_PHONEBOOK_RECORDS_RECEIVED = 2;
+
+ private SimPhonebookRecordCache mSimPhonebookRecordCacheUt;
+ private SimPhonebookRecordHandler mSimPhonebookRecordHandler;
+
+ private class SimPhonebookRecordHandler extends HandlerThread {
+ SimPhonebookRecordHandler(String name) {
+ super(name);
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mSimPhonebookRecordCacheUt =
+ new SimPhonebookRecordCache(mContext, 0, mSimulatedCommands);
+ setReady(true);
+ }
+
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(this.getClass().getSimpleName());
+ mSimPhonebookRecordHandler = new SimPhonebookRecordHandler(this.getClass().getSimpleName());
+ mSimPhonebookRecordHandler.start();
+ waitUntilReady();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSimPhonebookRecordHandler.quit();
+ mSimPhonebookRecordHandler.join();
+ super.tearDown();
+ }
+
+ @Test
+ public void testSimPhonebookChangedOnBootup() {
+ mSimulatedCommands.notifySimPhonebookChanged();
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ AdnCapacity capacity = mSimPhonebookRecordCacheUt.getAdnCapacity();
+ AdnCapacity capVerifer = new AdnCapacity(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ assertNotNull(capacity);
+ assertTrue(capVerifer.equals(capacity));
+ mSimulatedCommands.notifySimPhonebookChanged();
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ assertTrue(capacity != mSimPhonebookRecordCacheUt.getAdnCapacity());
+ assertTrue(capVerifer.equals(capacity));
+ }
+
+ @Test
+ public void testGetPhonebookRecords() {
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ mSimulatedCommands.notifySimPhonebookChanged();
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ mSimPhonebookRecordCacheUt.requestLoadAllPbRecords(null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+
+ List<SimPhonebookRecord> records = new ArrayList<SimPhonebookRecord>();
+ records.add(new SimPhonebookRecord(10, "ABC", "12345", null, null));
+ AsyncResult ar = new AsyncResult(null, new ReceivedPhonebookRecords(4, records), null);
+ Message msg = Message.obtain(mSimPhonebookRecordCacheUt,
+ EVENT_PHONEBOOK_RECORDS_RECEIVED, ar);
+ mSimPhonebookRecordCacheUt.handleMessage(msg);
+
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ List<AdnRecord> adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 1);
+ assertEquals(adnRecords.get(0).getRecId(), 10);
+ }
+
+ @Test
+ public void testGetPhonebookRecordsWithoutInitization() {
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ mSimPhonebookRecordCacheUt.requestLoadAllPbRecords(null);
+ assertTrue(mSimPhonebookRecordCacheUt.isLoading());
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ assertFalse(mSimPhonebookRecordCacheUt.isLoading());
+ }
+
+ @Test
+ public void testUpdatePhonebookRecord() {
+ List<AdnRecord> adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 0);
+
+ AdnRecord newAdn = new AdnRecord(0, 20, "AB", "123", null, null);
+ // add
+ mSimPhonebookRecordCacheUt.updateSimPbAdnBySearch(null, newAdn, null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 1);
+ AdnRecord oldAdn = adnRecords.get(0);
+ assertEquals(oldAdn.getRecId(), 20);
+ assertEquals(oldAdn.getAlphaTag(), "AB");
+ assertEquals(oldAdn.getNumber(), "123");
+ // update
+ newAdn = new AdnRecord(0, 20, "ABCD", "123456789", null, null);
+ mSimPhonebookRecordCacheUt.updateSimPbAdnBySearch(oldAdn, newAdn, null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 1);
+ oldAdn = adnRecords.get(0);
+ assertEquals(oldAdn.getRecId(), 20);
+ assertEquals(oldAdn.getAlphaTag(), "ABCD");
+ assertEquals(oldAdn.getNumber(), "123456789");
+ // Delete
+ newAdn = new AdnRecord(0, 20, null, null, null, null);
+ mSimPhonebookRecordCacheUt.updateSimPbAdnBySearch(oldAdn, newAdn, null);
+ waitForLastHandlerAction(mSimPhonebookRecordCacheUt);
+ adnRecords = mSimPhonebookRecordCacheUt.getAdnRecords();
+ assertEquals(adnRecords.size(), 0);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index 23a7ec980d..5ff4aed36e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -78,8 +78,8 @@ public class UiccCardTest extends TelephonyTest {
@Test
@SmallTest
- public void tesUiccCartdInfoSanity() {
- /* before update sanity test */
+ public void testUiccCartdInfoCorrectness() {
+ /* before update correctness test */
assertEquals(0, mUiccCard.getNumApplications());
assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccCard.getCardState());
assertNull(mUiccCard.getUniversalPinState());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
index b383bfa8e2..39ec59dc63 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.doAnswer;
import android.content.pm.Signature;
import android.os.AsyncResult;
import android.os.Message;
+import android.telephony.UiccAccessRule;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -41,11 +42,17 @@ import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.List;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
private UiccCarrierPrivilegeRules mUiccCarrierPrivilegeRules;
+ private static final String ARAM = "A00000015141434C00";
+ private static final String ARAD = "A00000015144414300";
+ private static final String PKCS15_AID = "A000000063504B43532D3135";
+
@Mock
private UiccProfile mUiccProfile;
@@ -289,10 +296,6 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
assertTrue(!mUiccCarrierPrivilegeRules.shouldRetry(ar, 0));
}
- private static final String ARAM = "A00000015141434C00";
- private static final String ARAD = "A00000015144414300";
- private static final String PKCS15_AID = "A000000063504B43532D3135";
-
@Test
@SmallTest
public void testAID_OnlyARAM() {
@@ -476,7 +479,7 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
@Test
@SmallTest
- public void testAID_NeitherARAMorARAD() {
+ public void testAID_ARFFailed() {
final String hexString =
"FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+ "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
@@ -515,8 +518,285 @@ public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
}
+ @Test
+ @SmallTest
+ public void testAID_ARFSucceed() {
+ /**
+ * PKCS#15 application (AID: A0 00 00 00 63 50 4B 43 53 2D 31 35)
+ * -ODF (5031)
+ * A7 06 30 04 04 02 52 07
+ * -DODF (5207)
+ * A1 29 30 00 30 0F 0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C A1 14 30 12
+ * 06 0A 2A 86 48 86 FC 6B 81 48 01 01 30 04 04 02 42 00
+ * -EF ACMain (4200)
+ * 30 10 04 08 01 02 03 04 05 06 07 08 30 04 04 02 43 00
+ * -EF ACRules (4300)
+ * 30 10 A0 08 04 06 A0 00 00 01 51 01 30 04 04 02 43 10
+ * -EF ACConditions1 (4310)
+ * 30 22
+ * 04 20
+ * B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350
+ * 30 22
+ * 04 20
+ * 4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859
+ */
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ String aid = (String) invocation.getArguments()[0];
+ Message message = (Message) invocation.getArguments()[2];
+ AsyncResult ar = new AsyncResult(null, null, null);
+ if (aid.equals(ARAM)) {
+ message.arg2 = 1;
+ } else if (aid.equals(ARAD)) {
+ message.arg2 = 0;
+ } else {
+ // PKCS15
+ ar = new AsyncResult(null, new int[]{2}, null);
+ }
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+ // Select files
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00),
+ eq(0x04), eq(0x02), anyString(), any(Message.class));
+
+ // Read binary - ODF
+ String odf = "A706300404025207";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(odf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("5031"), any(Message.class));
+
+ // Read binary - DODF
+ String dodf =
+ "A1293000300F0C0D4750205345204163632043746CA11"
+ + "43012060A2A864886FC6B81480101300404024200";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(dodf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("5207"), any(Message.class));
+
+ // Read binary - ACMF
+ String acmf = "301004080102030405060708300404024300";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acmf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4200"), any(Message.class));
+
+ // Read binary - ACRF
+ String acrf = "3010A0080406FFFFFFFFFFFF300404024310";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acrf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4300"), any(Message.class));
+
+ // Read binary - ACCF
+ String accf =
+ "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB514653503022042"
+ + "04C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(accf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4310"), any(Message.class));
+
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[1];
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+ mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
+ processAllMessages();
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(2, mUiccCarrierPrivilegeRules.getAccessRules().size());
+ List<UiccAccessRule> accessRules = mUiccCarrierPrivilegeRules.getAccessRules();
+ UiccAccessRule accessRule1 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule1));
+ UiccAccessRule accessRule2 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule2));
+ }
+
+ @Test
+ @SmallTest
+ public void testAID_ARFFallbackToACRF() {
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ String aid = (String) invocation.getArguments()[0];
+ Message message = (Message) invocation.getArguments()[2];
+ AsyncResult ar = new AsyncResult(null, null, null);
+ if (aid.equals(ARAM)) {
+ message.arg2 = 1;
+ } else if (aid.equals(ARAD)) {
+ message.arg2 = 0;
+ } else {
+ // PKCS15
+ ar = new AsyncResult(null, new int[]{2}, null);
+ }
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+ // Select files
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00),
+ eq(0x04), eq(0x02), anyString(), any(Message.class));
+
+ // Read binary ODF failed
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, new byte[]{});
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("5031"), any(Message.class));
+
+ // Read binary - ACRF
+ String acrf = "3010A0080406FFFFFFFFFFFF300404024310";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acrf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4300"), any(Message.class));
+
+ // Read binary - ACCF
+ String accf =
+ "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB514653503022042"
+ + "04C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(accf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4310"), any(Message.class));
+
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[1];
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+ mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
+ processAllMessages();
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(2, mUiccCarrierPrivilegeRules.getAccessRules().size());
+ List<UiccAccessRule> accessRules = mUiccCarrierPrivilegeRules.getAccessRules();
+ UiccAccessRule accessRule1 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule1));
+ UiccAccessRule accessRule2 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule2));
+ }
+
private static final int P2 = 0x40;
private static final int P2_EXTENDED_DATA = 0x60;
+
@Test
@SmallTest
public void testAID_RetransmitLogicalChannel() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index 61a5e13bb9..199ff344c6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -19,6 +19,7 @@ import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -37,6 +38,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -135,21 +137,21 @@ public class UiccProfileTest extends TelephonyTest {
@Test
@SmallTest
- public void testParseWhitelistMapFromString() {
- String whitelist = "";
- Map<String, String> parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+ public void testParseAllowListMapFromString() {
+ String allowList = "";
+ Map<String, String> parsedMap = UiccProfile.parseToCertificateToPackageMap(allowList);
assertTrue(parsedMap.isEmpty());
- whitelist = "nokey;value;separation";
- parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+ allowList = "nokey;value;separation";
+ parsedMap = UiccProfile.parseToCertificateToPackageMap(allowList);
assertTrue(parsedMap.isEmpty());
- whitelist = "KEY1:value1";
- parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+ allowList = "KEY1:value1";
+ parsedMap = UiccProfile.parseToCertificateToPackageMap(allowList);
assertEquals("value1", parsedMap.get("KEY1"));
- whitelist = "KEY1:value1; KEY2:value2 ;KEY3:value3";
- parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+ allowList = "KEY1:value1; KEY2:value2 ;KEY3:value3";
+ parsedMap = UiccProfile.parseToCertificateToPackageMap(allowList);
assertEquals("value1", parsedMap.get("KEY1"));
assertEquals("value2", parsedMap.get("KEY2"));
assertEquals("value3", parsedMap.get("KEY3"));
@@ -560,13 +562,19 @@ public class UiccProfileTest extends TelephonyTest {
String fakeBrand = "operator";
mUiccProfile.getApplicationIndex(0).getIccRecords().mIccId = fakeIccId;
- doReturn(fakeIccId).when(mSubscriptionInfo).getIccId();
- doReturn(mSubscriptionInfo).when(mSubscriptionController)
- .getActiveSubscriptionInfoForSimSlotIndex(eq(0), any(), any());
+ doReturn(false).when(mSubscriptionController)
+ .checkPhoneIdAndIccIdMatch(anyInt(), anyString());
mUiccProfile.setOperatorBrandOverride(fakeBrand);
String brandInSharedPreference = mContext.getSharedPreferences("file name", 0)
.getString("operator_branding_" + fakeIccId, null);
+ assertNotEquals(fakeBrand, brandInSharedPreference);
+
+ doReturn(true).when(mSubscriptionController)
+ .checkPhoneIdAndIccIdMatch(anyInt(), anyString());
+ mUiccProfile.setOperatorBrandOverride(fakeBrand);
+ brandInSharedPreference = mContext.getSharedPreferences("file name", 0)
+ .getString("operator_branding_" + fakeIccId, null);
assertEquals(fakeBrand, brandInSharedPreference);
}
@@ -606,4 +614,44 @@ public class UiccProfileTest extends TelephonyTest {
assertTrue(mUiccProfile.isEmptyProfile());
}
+
+ private void testUpdateUiccProfileApplicationNoCsim() {
+ /* update app status and index */
+ IccCardApplicationStatus imsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_ISIM,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA1");
+ IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+ IccCardApplicationStatus.AppType.APPTYPE_USIM,
+ IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+ mIccCardStatus.mApplications = new IccCardApplicationStatus[]{imsApp, umtsApp};
+ mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+ mIccCardStatus.mImsSubscriptionAppIndex = 0;
+ mIccCardStatus.mGsmUmtsSubscriptionAppIndex = 1;
+ logd("Update UICC Profile Applications");
+ mUiccProfile.update(mContext, mSimulatedCommands, mIccCardStatus);
+ processAllMessages();
+
+ assertEquals(2, mUiccProfile.getNumApplications());
+ assertFalse(mUiccProfile.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_CSIM));
+ assertTrue(mUiccProfile.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_ISIM));
+ assertTrue(mUiccProfile.isApplicationOnIcc(IccCardApplicationStatus.AppType.APPTYPE_USIM));
+ }
+
+ @Test
+ @SmallTest
+ public void testSetVoiceRadioTech() {
+ // if voice rat is GSM, mCurrentAppType should be 3gpp
+ mUiccProfile.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_GSM);
+ assertEquals(UiccController.APP_FAM_3GPP, mUiccProfile.mCurrentAppType);
+
+ // if voice rat is CDMA, mCurrentAppType should be 3gpp2
+ mUiccProfile.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_IS95A);
+ assertEquals(UiccController.APP_FAM_3GPP2, mUiccProfile.mCurrentAppType);
+
+ // if voice rat is CDMA, there is no CSIM app, and there is a SIM/USIM app, then
+ // mCurrentAppType should be 3gpp
+ testUpdateUiccProfileApplicationNoCsim();
+ mUiccProfile.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_IS95A);
+ assertEquals(UiccController.APP_FAM_3GPP, mUiccProfile.mCurrentAppType);
+ }
}