aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-11-11 21:14:39 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-11-11 21:14:39 +0000
commit77744d732721665c8663822515f344739f313f5f (patch)
treedac40bae52f480d44cc79f4309f323947a0e4eaa
parent09a320c9085992637fdbae638d89f479f852ab01 (diff)
parent3386e7bd94c533837bd434c23757aa8aa1176b3e (diff)
downloadtelephony-android10-mainline-resolv-release.tar.gz
Snap for 6001391 from 3386e7bd94c533837bd434c23757aa8aa1176b3e to qt-aml-resolv-releaseandroid-mainline-10.0.0_r8android10-mainline-resolv-release
Change-Id: I9608399e8ea70a4af95168e197c450ec64049360
-rw-r--r--CleanSpec.mk2
-rw-r--r--src/java/android/telephony/CarrierMessagingServiceManager.java99
-rw-r--r--src/java/android/telephony/CellBroadcastMessage.java452
-rw-r--r--src/java/com/android/internal/telephony/BaseCommands.java144
-rw-r--r--src/java/com/android/internal/telephony/BtSmsInterfaceManager.java6
-rw-r--r--src/java/com/android/internal/telephony/Call.java28
-rw-r--r--src/java/com/android/internal/telephony/CallForwardInfo.java4
-rw-r--r--src/java/com/android/internal/telephony/CallTracker.java4
-rw-r--r--src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java33
-rw-r--r--src/java/com/android/internal/telephony/CarrierServiceBindHelper.java79
-rw-r--r--src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java29
-rw-r--r--src/java/com/android/internal/telephony/CellBroadcastServiceManager.java88
-rw-r--r--src/java/com/android/internal/telephony/CellularNetworkValidator.java4
-rw-r--r--src/java/com/android/internal/telephony/CommandException.java3
-rw-r--r--src/java/com/android/internal/telephony/DefaultPhoneNotifier.java273
-rw-r--r--src/java/com/android/internal/telephony/DriverCall.java5
-rwxr-xr-xsrc/java/com/android/internal/telephony/GsmCdmaCallTracker.java2
-rw-r--r--src/java/com/android/internal/telephony/HbpcdLookup.java124
-rw-r--r--src/java/com/android/internal/telephony/HbpcdUtils.java163
-rw-r--r--src/java/com/android/internal/telephony/IIccPhoneBook.aidl5
-rw-r--r--src/java/com/android/internal/telephony/IccProvider.java4
-rw-r--r--src/java/com/android/internal/telephony/IccSmsInterfaceManager.java73
-rw-r--r--src/java/com/android/internal/telephony/ImsSmsDispatcher.java69
-rw-r--r--src/java/com/android/internal/telephony/InboundSmsHandler.java87
-rwxr-xr-xsrc/java/com/android/internal/telephony/LocaleTracker.java90
-rw-r--r--src/java/com/android/internal/telephony/MultiSimSettingController.java23
-rw-r--r--src/java/com/android/internal/telephony/NetworkRegistrationManager.java66
-rw-r--r--src/java/com/android/internal/telephony/NetworkScanRequestTracker.java6
-rw-r--r--src/java/com/android/internal/telephony/NitzStateMachine.java2
-rw-r--r--src/java/com/android/internal/telephony/Phone.java115
-rw-r--r--src/java/com/android/internal/telephony/PhoneConfigurationManager.java141
-rw-r--r--src/java/com/android/internal/telephony/PhoneFactory.java6
-rw-r--r--src/java/com/android/internal/telephony/PhoneNotifier.java11
-rw-r--r--src/java/com/android/internal/telephony/PhoneSwitcher.java15
-rw-r--r--src/java/com/android/internal/telephony/ProxyController.java7
-rw-r--r--src/java/com/android/internal/telephony/RIL.java4
-rw-r--r--src/java/com/android/internal/telephony/RadioIndication.java5
-rw-r--r--src/java/com/android/internal/telephony/RatRatcheter.java2
-rw-r--r--src/java/com/android/internal/telephony/SMSDispatcher.java101
-rwxr-xr-xsrc/java/com/android/internal/telephony/ServiceStateTracker.java5
-rw-r--r--src/java/com/android/internal/telephony/SmsController.java25
-rw-r--r--src/java/com/android/internal/telephony/SmsDispatchersController.java2
-rw-r--r--src/java/com/android/internal/telephony/SmsNumberUtils.java644
-rw-r--r--src/java/com/android/internal/telephony/SmsPermissions.java64
-rw-r--r--src/java/com/android/internal/telephony/SubscriptionController.java16
-rw-r--r--src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java148
-rw-r--r--src/java/com/android/internal/telephony/TelephonyComponentFactory.java9
-rwxr-xr-xsrc/java/com/android/internal/telephony/WapPushOverSms.java5
-rwxr-xr-xsrc/java/com/android/internal/telephony/cat/AppInterface.java9
-rw-r--r--src/java/com/android/internal/telephony/cat/CatService.java15
-rw-r--r--src/java/com/android/internal/telephony/cat/ResponseData.java15
-rw-r--r--src/java/com/android/internal/telephony/cat/ResultCode.java1
-rwxr-xr-xsrc/java/com/android/internal/telephony/cat/RilMessageDecoder.java6
-rw-r--r--src/java/com/android/internal/telephony/cat/ValueParser.java8
-rw-r--r--src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java308
-rwxr-xr-xsrc/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java5
-rw-r--r--src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java10
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java2
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/ApnContext.java2
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java2
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataConnection.java17
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java2
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java3
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java76
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java5
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DcRequest.java2
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/DcTracker.java36
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java2
-rw-r--r--src/java/com/android/internal/telephony/dataconnection/TransportManager.java2
-rw-r--r--src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java135
-rw-r--r--src/java/com/android/internal/telephony/euicc/EuiccConnector.java69
-rw-r--r--src/java/com/android/internal/telephony/euicc/EuiccController.java48
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java8
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java147
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmMmiCode.java34
-rw-r--r--src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java41
-rw-r--r--src/java/com/android/internal/telephony/gsm/SimTlv.java8
-rwxr-xr-xsrc/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java9
-rw-r--r--src/java/com/android/internal/telephony/ims/ImsResolver.java14
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhone.java49
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java9
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java62
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java12
-rw-r--r--src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java53
-rw-r--r--src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java17
-rw-r--r--src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java357
-rw-r--r--src/java/com/android/internal/telephony/nitz/NewTimeServiceHelper.java60
-rw-r--r--src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java122
-rw-r--r--src/java/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactory.java273
-rw-r--r--src/java/com/android/internal/telephony/nitz/TimeZoneSuggesterImpl.java288
-rw-r--r--src/java/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestion.java254
-rw-r--r--src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionService.java489
-rw-r--r--src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceHelperImpl.java150
-rw-r--r--src/java/com/android/internal/telephony/sip/SipPhone.java13
-rw-r--r--src/java/com/android/internal/telephony/test/ModelInterpreter.java3
-rw-r--r--src/java/com/android/internal/telephony/test/SimulatedCommands.java8
-rw-r--r--src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java4
-rw-r--r--src/java/com/android/internal/telephony/test/SimulatedGsmCallState.java17
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java11
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccFileHandler.java14
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccRecords.java58
-rw-r--r--src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java4
-rw-r--r--src/java/com/android/internal/telephony/uicc/SIMRecords.java11
-rw-r--r--src/java/com/android/internal/telephony/uicc/UiccController.java14
-rw-r--r--src/java/com/android/internal/telephony/util/TelephonyUtils.java42
-rw-r--r--src/java/com/google/android/mms/ContentType.java233
-rw-r--r--src/java/com/google/android/mms/InvalidHeaderValueException.java41
-rw-r--r--src/java/com/google/android/mms/MmsException.java60
-rwxr-xr-xsrc/java/com/google/android/mms/package.html5
-rw-r--r--src/java/com/google/android/mms/pdu/AcknowledgeInd.java89
-rw-r--r--src/java/com/google/android/mms/pdu/Base64.java167
-rw-r--r--src/java/com/google/android/mms/pdu/CharacterSets.java172
-rw-r--r--src/java/com/google/android/mms/pdu/DeliveryInd.java138
-rw-r--r--src/java/com/google/android/mms/pdu/EncodedStringValue.java283
-rw-r--r--src/java/com/google/android/mms/pdu/GenericPdu.java113
-rw-r--r--src/java/com/google/android/mms/pdu/MultimediaMessagePdu.java150
-rw-r--r--src/java/com/google/android/mms/pdu/NotificationInd.java285
-rw-r--r--src/java/com/google/android/mms/pdu/NotifyRespInd.java114
-rw-r--r--src/java/com/google/android/mms/pdu/PduBody.java191
-rw-r--r--src/java/com/google/android/mms/pdu/PduComposer.java1202
-rw-r--r--src/java/com/google/android/mms/pdu/PduContentTypes.java110
-rw-r--r--src/java/com/google/android/mms/pdu/PduHeaders.java721
-rwxr-xr-xsrc/java/com/google/android/mms/pdu/PduParser.java2011
-rw-r--r--src/java/com/google/android/mms/pdu/PduPart.java414
-rwxr-xr-xsrc/java/com/google/android/mms/pdu/PduPersister.java1543
-rw-r--r--src/java/com/google/android/mms/pdu/QuotedPrintable.java68
-rw-r--r--src/java/com/google/android/mms/pdu/ReadOrigInd.java153
-rw-r--r--src/java/com/google/android/mms/pdu/ReadRecInd.java144
-rw-r--r--src/java/com/google/android/mms/pdu/RetrieveConf.java300
-rw-r--r--src/java/com/google/android/mms/pdu/SendConf.java117
-rw-r--r--src/java/com/google/android/mms/pdu/SendReq.java345
-rwxr-xr-xsrc/java/com/google/android/mms/pdu/package.html5
-rw-r--r--src/java/com/google/android/mms/util/AbstractCache.java112
-rw-r--r--src/java/com/google/android/mms/util/DownloadDrmHelper.java111
-rw-r--r--src/java/com/google/android/mms/util/DrmConvertSession.java172
-rw-r--r--src/java/com/google/android/mms/util/PduCache.java261
-rw-r--r--src/java/com/google/android/mms/util/PduCacheEntry.java44
-rw-r--r--src/java/com/google/android/mms/util/SqliteWrapper.java120
-rwxr-xr-xsrc/java/com/google/android/mms/util/package.html5
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java31
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CarrierServiceBindHelperTest.java84
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java10
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java5
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java68
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java7
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java34
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java1
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java15
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupportTest.java33
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java189
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java1
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java15
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java62
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java60
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java19
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java122
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java11
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java20
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java53
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java3
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java6
-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.java2
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java24
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java64
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java16
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java7
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java628
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactoryTest.java333
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneSuggesterImplTest.java644
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestionTest.java94
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceTest.java587
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java115
175 files changed, 7608 insertions, 12638 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 9caa0ad621..1ca7248068 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -49,6 +49,8 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/teleph
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/src/java/android/telephony/CarrierMessagingServiceManager.java b/src/java/android/telephony/CarrierMessagingServiceManager.java
deleted file mode 100644
index 768d97bcfd..0000000000
--- a/src/java/android/telephony/CarrierMessagingServiceManager.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.service.carrier.CarrierMessagingService;
-import android.service.carrier.ICarrierMessagingService;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * Provides basic structure for platform to connect to the carrier messaging service.
- * <p>
- * <code>
- * CarrierMessagingServiceManager carrierMessagingServiceManager =
- * new CarrierMessagingServiceManagerImpl();
- * if (carrierMessagingServiceManager.bindToCarrierMessagingService(context, carrierPackageName)) {
- * // wait for onServiceReady callback
- * } else {
- * // Unable to bind: handle error.
- * }
- * </code>
- * <p> Upon completion {@link #disposeConnection} should be called to unbind the
- * CarrierMessagingService.
- * @hide
- */
-public abstract class CarrierMessagingServiceManager {
- // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
- // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
- private volatile CarrierMessagingServiceConnection mCarrierMessagingServiceConnection;
-
- /**
- * Binds to the carrier messaging service under package {@code carrierPackageName}. This method
- * should be called exactly once.
- *
- * @param context the context
- * @param carrierPackageName the carrier package name
- * @return true upon successfully binding to a carrier messaging service, false otherwise
- */
- public boolean bindToCarrierMessagingService(Context context, String carrierPackageName) {
- Preconditions.checkState(mCarrierMessagingServiceConnection == null);
-
- Intent intent = new Intent(CarrierMessagingService.SERVICE_INTERFACE);
- intent.setPackage(carrierPackageName);
- mCarrierMessagingServiceConnection = new CarrierMessagingServiceConnection();
- return context.bindService(intent, mCarrierMessagingServiceConnection,
- Context.BIND_AUTO_CREATE);
- }
-
- /**
- * Unbinds the carrier messaging service. This method should be called exactly once.
- *
- * @param context the context
- */
- public void disposeConnection(Context context) {
- Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
- context.unbindService(mCarrierMessagingServiceConnection);
- mCarrierMessagingServiceConnection = null;
- }
-
- /**
- * Implemented by subclasses to use the carrier messaging service once it is ready.
- *
- * @param carrierMessagingService the carrier messaing service interface
- */
- protected abstract void onServiceReady(ICarrierMessagingService carrierMessagingService);
-
- /**
- * A basic {@link ServiceConnection}.
- */
- private final class CarrierMessagingServiceConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- onServiceReady(ICarrierMessagingService.Stub.asInterface(service));
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- }
-}
diff --git a/src/java/android/telephony/CellBroadcastMessage.java b/src/java/android/telephony/CellBroadcastMessage.java
deleted file mode 100644
index 3cb364d16f..0000000000
--- a/src/java/android/telephony/CellBroadcastMessage.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright (C) 2011 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 android.annotation.UnsupportedAppUsage;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.Telephony;
-import android.text.format.DateUtils;
-
-/**
- * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that
- * decoded broadcast message objects can be passed between running Services.
- * New broadcasts are received by the CellBroadcastReceiver app, which exports
- * the database of previously received broadcasts at "content://cellbroadcasts/".
- * The "android.permission.READ_CELL_BROADCASTS" permission is required to read
- * from the ContentProvider, and writes to the database are not allowed.<p>
- *
- * Use {@link #createFromCursor} to create CellBroadcastMessage objects from rows
- * in the database cursor returned by the ContentProvider.
- *
- * {@hide}
- */
-public class CellBroadcastMessage implements Parcelable {
-
- /** Identifier for getExtra() when adding this object to an Intent. */
- public static final String SMS_CB_MESSAGE_EXTRA =
- "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE";
-
- /** SmsCbMessage. */
- private final SmsCbMessage mSmsCbMessage;
-
- private final long mDeliveryTime;
- private boolean mIsRead;
-
- /**
- * Indicates the subId
- *
- * @hide
- */
- private int mSubId = 0;
-
- /**
- * set Subscription information
- *
- * @hide
- */
- public void setSubId(int subId) {
- mSubId = subId;
- }
-
- /**
- * get Subscription information
- *
- * @hide
- */
- public int getSubId() {
- return mSubId;
- }
-
- @UnsupportedAppUsage
- public CellBroadcastMessage(SmsCbMessage message) {
- mSmsCbMessage = message;
- mDeliveryTime = System.currentTimeMillis();
- mIsRead = false;
- }
-
- private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) {
- mSmsCbMessage = message;
- mDeliveryTime = deliveryTime;
- mIsRead = isRead;
- }
-
- private CellBroadcastMessage(Parcel in) {
- mSmsCbMessage = new SmsCbMessage(in);
- mDeliveryTime = in.readLong();
- mIsRead = (in.readInt() != 0);
- mSubId = in.readInt();
- }
-
- /** Parcelable: no special flags. */
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- mSmsCbMessage.writeToParcel(out, flags);
- out.writeLong(mDeliveryTime);
- out.writeInt(mIsRead ? 1 : 0);
- out.writeInt(mSubId);
- }
-
- public static final Parcelable.Creator<CellBroadcastMessage> CREATOR
- = new Parcelable.Creator<CellBroadcastMessage>() {
- @Override
- public CellBroadcastMessage createFromParcel(Parcel in) {
- return new CellBroadcastMessage(in);
- }
-
- @Override
- public CellBroadcastMessage[] newArray(int size) {
- return new CellBroadcastMessage[size];
- }
- };
-
- /**
- * Create a CellBroadcastMessage from a row in the database.
- * @param cursor an open SQLite cursor pointing to the row to read
- * @return the new CellBroadcastMessage
- * @throws IllegalArgumentException if one of the required columns is missing
- */
- @UnsupportedAppUsage
- public static CellBroadcastMessage createFromCursor(Cursor cursor) {
- int geoScope = cursor.getInt(
- cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE));
- int serialNum = cursor.getInt(
- cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER));
- int category = cursor.getInt(
- cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY));
- String language = cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE));
- String body = cursor.getString(
- cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY));
- int format = cursor.getInt(
- cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT));
- int priority = cursor.getInt(
- cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY));
-
- String plmn;
- int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN);
- if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
- plmn = cursor.getString(plmnColumn);
- } else {
- plmn = null;
- }
-
- int lac;
- int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC);
- if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
- lac = cursor.getInt(lacColumn);
- } else {
- lac = -1;
- }
-
- int cid;
- int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID);
- if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
- cid = cursor.getInt(cidColumn);
- } else {
- cid = -1;
- }
-
- SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
-
- SmsCbEtwsInfo etwsInfo;
- int etwsWarningTypeColumn = cursor.getColumnIndex(
- Telephony.CellBroadcasts.ETWS_WARNING_TYPE);
- if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
- int warningType = cursor.getInt(etwsWarningTypeColumn);
- etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null);
- } else {
- etwsInfo = null;
- }
-
- SmsCbCmasInfo cmasInfo;
- int cmasMessageClassColumn = cursor.getColumnIndex(
- Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS);
- if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
- int messageClass = cursor.getInt(cmasMessageClassColumn);
-
- int cmasCategory;
- int cmasCategoryColumn = cursor.getColumnIndex(
- Telephony.CellBroadcasts.CMAS_CATEGORY);
- if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
- cmasCategory = cursor.getInt(cmasCategoryColumn);
- } else {
- cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
- }
-
- int responseType;
- int cmasResponseTypeColumn = cursor.getColumnIndex(
- Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE);
- if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
- responseType = cursor.getInt(cmasResponseTypeColumn);
- } else {
- responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
- }
-
- int severity;
- int cmasSeverityColumn = cursor.getColumnIndex(
- Telephony.CellBroadcasts.CMAS_SEVERITY);
- if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
- severity = cursor.getInt(cmasSeverityColumn);
- } else {
- severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
- }
-
- int urgency;
- int cmasUrgencyColumn = cursor.getColumnIndex(
- Telephony.CellBroadcasts.CMAS_URGENCY);
- if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
- urgency = cursor.getInt(cmasUrgencyColumn);
- } else {
- urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
- }
-
- int certainty;
- int cmasCertaintyColumn = cursor.getColumnIndex(
- Telephony.CellBroadcasts.CMAS_CERTAINTY);
- if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
- certainty = cursor.getInt(cmasCertaintyColumn);
- } else {
- certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
- }
-
- cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
- urgency, certainty);
- } else {
- cmasInfo = null;
- }
-
- SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category,
- language, body, priority, etwsInfo, cmasInfo);
-
- long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow(
- Telephony.CellBroadcasts.DELIVERY_TIME));
- boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow(
- Telephony.CellBroadcasts.MESSAGE_READ)) != 0);
-
- return new CellBroadcastMessage(msg, deliveryTime, isRead);
- }
-
- /**
- * Return a ContentValues object for insertion into the database.
- * @return a new ContentValues object containing this object's data
- */
- @UnsupportedAppUsage
- public ContentValues getContentValues() {
- ContentValues cv = new ContentValues(16);
- SmsCbMessage msg = mSmsCbMessage;
- cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope());
- SmsCbLocation location = msg.getLocation();
- if (location.getPlmn() != null) {
- cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn());
- }
- if (location.getLac() != -1) {
- cv.put(Telephony.CellBroadcasts.LAC, location.getLac());
- }
- if (location.getCid() != -1) {
- cv.put(Telephony.CellBroadcasts.CID, location.getCid());
- }
- cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber());
- cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory());
- cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode());
- cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody());
- cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime);
- cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead);
- cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat());
- cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority());
-
- SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
- if (etwsInfo != null) {
- cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
- }
-
- SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo();
- if (cmasInfo != null) {
- cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
- cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
- cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
- cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
- cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
- cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
- }
-
- return cv;
- }
-
- /**
- * Set or clear the "read message" flag.
- * @param isRead true if the message has been read; false if not
- */
- public void setIsRead(boolean isRead) {
- mIsRead = isRead;
- }
-
- @UnsupportedAppUsage
- public String getLanguageCode() {
- return mSmsCbMessage.getLanguageCode();
- }
-
- @UnsupportedAppUsage
- public int getServiceCategory() {
- return mSmsCbMessage.getServiceCategory();
- }
-
- @UnsupportedAppUsage
- public long getDeliveryTime() {
- return mDeliveryTime;
- }
-
- @UnsupportedAppUsage
- public String getMessageBody() {
- return mSmsCbMessage.getMessageBody();
- }
-
- @UnsupportedAppUsage
- public boolean isRead() {
- return mIsRead;
- }
-
- @UnsupportedAppUsage
- public int getSerialNumber() {
- return mSmsCbMessage.getSerialNumber();
- }
-
- public SmsCbCmasInfo getCmasWarningInfo() {
- return mSmsCbMessage.getCmasWarningInfo();
- }
-
- @UnsupportedAppUsage
- public SmsCbEtwsInfo getEtwsWarningInfo() {
- return mSmsCbMessage.getEtwsWarningInfo();
- }
-
- /**
- * Return whether the broadcast is an emergency (PWS) message type.
- * This includes lower priority test messages and Amber alerts.
- *
- * All public alerts show the flashing warning icon in the dialog,
- * but only emergency alerts play the alert sound and speak the message.
- *
- * @return true if the message is PWS type; false otherwise
- */
- public boolean isPublicAlertMessage() {
- return mSmsCbMessage.isEmergencyMessage();
- }
-
- /**
- * Returns whether the broadcast is an emergency (PWS) message type,
- * including test messages and AMBER alerts.
- *
- * @return true if the message is PWS type (ETWS or CMAS)
- */
- @UnsupportedAppUsage
- public boolean isEmergencyAlertMessage() {
- return mSmsCbMessage.isEmergencyMessage();
- }
-
- /**
- * Return whether the broadcast is an ETWS emergency message type.
- * @return true if the message is ETWS emergency type; false otherwise
- */
- @UnsupportedAppUsage
- public boolean isEtwsMessage() {
- return mSmsCbMessage.isEtwsMessage();
- }
-
- /**
- * Return whether the broadcast is a CMAS emergency message type.
- * @return true if the message is CMAS emergency type; false otherwise
- */
- @UnsupportedAppUsage
- public boolean isCmasMessage() {
- return mSmsCbMessage.isCmasMessage();
- }
-
- /**
- * Return the CMAS message class.
- * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or
- * {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert
- */
- public int getCmasMessageClass() {
- if (mSmsCbMessage.isCmasMessage()) {
- return mSmsCbMessage.getCmasWarningInfo().getMessageClass();
- } else {
- return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
- }
- }
-
- /**
- * Return whether the broadcast is an ETWS popup alert.
- * This method checks the message ID and the message code.
- * @return true if the message indicates an ETWS popup alert
- */
- public boolean isEtwsPopupAlert() {
- SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
- return etwsInfo != null && etwsInfo.isPopupAlert();
- }
-
- /**
- * Return whether the broadcast is an ETWS emergency user alert.
- * This method checks the message ID and the message code.
- * @return true if the message indicates an ETWS emergency user alert
- */
- public boolean isEtwsEmergencyUserAlert() {
- SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
- return etwsInfo != null && etwsInfo.isEmergencyUserAlert();
- }
-
- /**
- * Return whether the broadcast is an ETWS test message.
- * @return true if the message is an ETWS test message; false otherwise
- */
- public boolean isEtwsTestMessage() {
- SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
- return etwsInfo != null &&
- etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE;
- }
-
- /**
- * Return the abbreviated date string for the message delivery time.
- * @param context the context object
- * @return a String to use in the broadcast list UI
- */
- public String getDateString(Context context) {
- int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME |
- DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE |
- DateUtils.FORMAT_CAP_AMPM;
- return DateUtils.formatDateTime(context, mDeliveryTime, flags);
- }
-
- /**
- * Return the date string for the message delivery time, suitable for text-to-speech.
- * @param context the context object
- * @return a String for populating the list item AccessibilityEvent for TTS
- */
- @UnsupportedAppUsage
- public String getSpokenDateString(Context context) {
- int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE;
- return DateUtils.formatDateTime(context, mDeliveryTime, flags);
- }
-}
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index f72fdcd629..f0c62620d1 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -24,6 +24,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
+import android.telephony.Annotation.RadioPowerState;
import android.telephony.TelephonyManager;
/**
@@ -160,17 +161,15 @@ public abstract class BaseCommands implements CommandsInterface {
//***** CommandsInterface implementation
@Override
- public @TelephonyManager.RadioPowerState int getRadioState() {
+ public @RadioPowerState int getRadioState() {
return mState;
}
@Override
public void registerForRadioStateChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
synchronized (mStateMonitor) {
- mRadioStateChangedRegistrants.add(r);
- r.notifyRegistrant();
+ mRadioStateChangedRegistrants.addUnique(h, what, obj);
+ Message.obtain(h, what, new AsyncResult(obj, null, null)).sendToTarget();
}
}
@@ -182,8 +181,7 @@ public abstract class BaseCommands implements CommandsInterface {
}
public void registerForImsNetworkStateChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mImsNetworkStateChangedRegistrants.add(r);
+ mImsNetworkStateChangedRegistrants.addUnique(h, what, obj);
}
public void unregisterForImsNetworkStateChanged(Handler h) {
@@ -192,13 +190,11 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForOn(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
synchronized (mStateMonitor) {
- mOnRegistrants.add(r);
+ mOnRegistrants.addUnique(h, what, obj);
if (mState == TelephonyManager.RADIO_POWER_ON) {
- r.notifyRegistrant(new AsyncResult(null, null, null));
+ Message.obtain(h, what, new AsyncResult(obj, null, null)).sendToTarget();
}
}
}
@@ -212,13 +208,11 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForAvailable(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
synchronized (mStateMonitor) {
- mAvailRegistrants.add(r);
+ mAvailRegistrants.addUnique(h, what, obj);
if (mState != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
- r.notifyRegistrant(new AsyncResult(null, null, null));
+ Message.obtain(h, what, new AsyncResult(obj, null, null)).sendToTarget();
}
}
}
@@ -232,13 +226,11 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForNotAvailable(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
synchronized (mStateMonitor) {
- mNotAvailRegistrants.add(r);
+ mNotAvailRegistrants.addUnique(h, what, obj);
if (mState == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
- r.notifyRegistrant(new AsyncResult(null, null, null));
+ Message.obtain(h, what, new AsyncResult(obj, null, null)).sendToTarget();
}
}
}
@@ -252,14 +244,12 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForOffOrNotAvailable(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
synchronized (mStateMonitor) {
- mOffOrNotAvailRegistrants.add(r);
+ mOffOrNotAvailRegistrants.addUnique(h, what, obj);
if (mState == TelephonyManager.RADIO_POWER_OFF
|| mState == TelephonyManager.RADIO_POWER_UNAVAILABLE) {
- r.notifyRegistrant(new AsyncResult(null, null, null));
+ Message.obtain(h, what, new AsyncResult(obj, null, null)).sendToTarget();
}
}
}
@@ -272,9 +262,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForCallStateChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- mCallStateRegistrants.add(r);
+ mCallStateRegistrants.addUnique(h, what, obj);
}
@Override
@@ -284,9 +272,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForNetworkStateChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- mNetworkStateRegistrants.add(r);
+ mNetworkStateRegistrants.addUnique(h, what, obj);
}
@Override
@@ -296,9 +282,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForDataCallListChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- mDataCallListChangedRegistrants.add(r);
+ mDataCallListChangedRegistrants.addUnique(h, what, obj);
}
@Override
@@ -308,8 +292,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForVoiceRadioTechChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mVoiceRadioTechChangedRegistrants.add(r);
+ mVoiceRadioTechChangedRegistrants.addUnique(h, what, obj);
}
@Override
@@ -319,8 +302,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForIccStatusChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mIccStatusChangedRegistrants.add(r);
+ mIccStatusChangedRegistrants.addUnique(h, what, obj);
}
@Override
@@ -330,8 +312,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForIccSlotStatusChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant(h, what, obj);
- mIccSlotStatusChangedRegistrants.add(r);
+ mIccSlotStatusChangedRegistrants.addUnique(h, what, obj);
}
@Override
@@ -523,8 +504,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForIccRefresh(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mIccRefreshRegistrants.add(r);
+ mIccRefreshRegistrants.addUnique(h, what, obj);
}
@Override
public void setOnIccRefresh(Handler h, int what, Object obj) {
@@ -580,8 +560,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mVoicePrivacyOnRegistrants.add(r);
+ mVoicePrivacyOnRegistrants.addUnique(h, what, obj);
}
@Override
@@ -591,8 +570,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mVoicePrivacyOffRegistrants.add(r);
+ mVoicePrivacyOffRegistrants.addUnique(h, what, obj);
}
@Override
@@ -615,8 +593,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForDisplayInfo(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mDisplayInfoRegistrants.add(r);
+ mDisplayInfoRegistrants.addUnique(h, what, obj);
}
@Override
@@ -626,8 +603,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForCallWaitingInfo(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mCallWaitingInfoRegistrants.add(r);
+ mCallWaitingInfoRegistrants.addUnique(h, what, obj);
}
@Override
@@ -637,8 +613,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForSignalInfo(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mSignalInfoRegistrants.add(r);
+ mSignalInfoRegistrants.addUnique(h, what, obj);
}
public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) {
@@ -659,8 +634,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForCdmaOtaProvision(Handler h,int what, Object obj){
- Registrant r = new Registrant (h, what, obj);
- mOtaProvisionRegistrants.add(r);
+ mOtaProvisionRegistrants.addUnique(h, what, obj);
}
@Override
@@ -670,8 +644,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForNumberInfo(Handler h,int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mNumberInfoRegistrants.add(r);
+ mNumberInfoRegistrants.addUnique(h, what, obj);
}
@Override
@@ -681,8 +654,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForRedirectedNumberInfo(Handler h,int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mRedirNumInfoRegistrants.add(r);
+ mRedirNumInfoRegistrants.addUnique(h, what, obj);
}
@Override
@@ -692,8 +664,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForLineControlInfo(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mLineControlInfoRegistrants.add(r);
+ mLineControlInfoRegistrants.addUnique(h, what, obj);
}
@Override
@@ -703,8 +674,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerFoT53ClirlInfo(Handler h,int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mT53ClirInfoRegistrants.add(r);
+ mT53ClirInfoRegistrants.addUnique(h, what, obj);
}
@Override
@@ -714,8 +684,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForT53AudioControlInfo(Handler h,int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mT53AudCntrlInfoRegistrants.add(r);
+ mT53AudCntrlInfoRegistrants.addUnique(h, what, obj);
}
@Override
@@ -725,8 +694,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForRingbackTone(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mRingbackToneRegistrants.add(r);
+ mRingbackToneRegistrants.addUnique(h, what, obj);
}
@Override
@@ -736,8 +704,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForResendIncallMute(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mResendIncallMuteRegistrants.add(r);
+ mResendIncallMuteRegistrants.addUnique(h, what, obj);
}
@Override
@@ -747,8 +714,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForCdmaSubscriptionChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mCdmaSubscriptionChangedRegistrants.add(r);
+ mCdmaSubscriptionChangedRegistrants.addUnique(h, what, obj);
}
@Override
@@ -758,8 +724,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForCdmaPrlChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mCdmaPrlChangedRegistrants.add(r);
+ mCdmaPrlChangedRegistrants.addUnique(h, what, obj);
}
@Override
@@ -769,8 +734,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForExitEmergencyCallbackMode(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mExitEmergencyCallbackModeRegistrants.add(r);
+ mExitEmergencyCallbackModeRegistrants.addUnique(h, what, obj);
}
@Override
@@ -780,8 +744,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForHardwareConfigChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mHardwareConfigChangeRegistrants.add(r);
+ mHardwareConfigChangeRegistrants.addUnique(h, what, obj);
}
@Override
@@ -792,7 +755,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForNetworkScanResult(Handler h, int what, Object obj) {
Registrant r = new Registrant(h, what, obj);
- mRilNetworkScanResultRegistrants.add(r);
+ mRilNetworkScanResultRegistrants.addUnique(h, what, obj);
}
@Override
@@ -805,10 +768,10 @@ public abstract class BaseCommands implements CommandsInterface {
*/
@Override
public void registerForRilConnected(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mRilConnectedRegistrants.add(r);
+ mRilConnectedRegistrants.addUnique(h, what, obj);
if (mRilVersion != -1) {
- r.notifyRegistrant(new AsyncResult(null, new Integer(mRilVersion), null));
+ Message.obtain(h, what, new AsyncResult(obj, new Integer(mRilVersion), null))
+ .sendToTarget();
}
}
@@ -818,8 +781,7 @@ public abstract class BaseCommands implements CommandsInterface {
}
public void registerForSubscriptionStatusChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mSubscriptionStatusRegistrants.add(r);
+ mSubscriptionStatusRegistrants.addUnique(h, what, obj);
}
public void unregisterForSubscriptionStatusChanged(Handler h) {
@@ -828,8 +790,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForEmergencyNumberList(Handler h, int what, Object obj) {
- Registrant r = new Registrant(h, what, obj);
- mEmergencyNumberListRegistrants.add(r);
+ mEmergencyNumberListRegistrants.addUnique(h, what, obj);
}
@Override
@@ -900,8 +861,7 @@ public abstract class BaseCommands implements CommandsInterface {
*/
@Override
public void registerForCellInfoList(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
- mRilCellInfoListRegistrants.add(r);
+ mRilCellInfoListRegistrants.addUnique(h, what, obj);
}
@Override
public void unregisterForCellInfoList(Handler h) {
@@ -910,8 +870,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForPhysicalChannelConfiguration(Handler h, int what, Object obj) {
- Registrant r = new Registrant(h, what, obj);
- mPhysicalChannelConfigurationRegistrants.add(r);
+ mPhysicalChannelConfigurationRegistrants.addUnique(h, what, obj);
}
@Override
@@ -921,9 +880,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForSrvccStateChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- mSrvccStateRegistrants.add(r);
+ mSrvccStateRegistrants.addUnique(h, what, obj);
}
@Override
@@ -960,8 +917,7 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForRadioCapabilityChanged(Handler h, int what, Object obj) {
- Registrant r = new Registrant(h, what, obj);
- mPhoneRadioCapabilityChangedRegistrants.add(r);
+ mPhoneRadioCapabilityChangedRegistrants.addUnique(h, what, obj);
}
@Override
@@ -983,10 +939,8 @@ public abstract class BaseCommands implements CommandsInterface {
@Override
public void registerForLceInfo(Handler h, int what, Object obj) {
- Registrant r = new Registrant(h, what, obj);
-
synchronized (mStateMonitor) {
- mLceInfoRegistrants.add(r);
+ mLceInfoRegistrants.addUnique(h, what, obj);
}
}
@@ -1032,7 +986,7 @@ public abstract class BaseCommands implements CommandsInterface {
Registrant r = new Registrant(h, what, obj);
synchronized (mStateMonitor) {
- mNattKeepaliveStatusRegistrants.add(r);
+ mNattKeepaliveStatusRegistrants.addUnique(h, what, obj);
}
}
diff --git a/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java b/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
index 408d7ab73d..67e07bcc3b 100644
--- a/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/BtSmsInterfaceManager.java
@@ -45,13 +45,13 @@ public class BtSmsInterfaceManager {
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter == null) {
// No bluetooth service on this platform?
- sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_NO_SERVICE);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_NO_BLUETOOTH_SERVICE);
return;
}
BluetoothDevice device = btAdapter.getRemoteDevice(info.getIccId());
if (device == null) {
Log.d(LOG_TAG, "Bluetooth device addr invalid: " + info.getIccId());
- sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_NO_SERVICE);
+ sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS);
return;
}
btAdapter.getProfileProxy(ActivityThread.currentApplication().getApplicationContext(),
@@ -113,7 +113,7 @@ public class BtSmsInterfaceManager {
public void onServiceDisconnected(int profile) {
if (mMessage != null) {
Log.d(LOG_TAG, "Bluetooth disconnected before sending the message");
- sendErrorInPendingIntent(mSentIntent, SmsManager.RESULT_ERROR_NO_SERVICE);
+ sendErrorInPendingIntent(mSentIntent, SmsManager.RESULT_BLUETOOTH_DISCONNECTED);
mMessage = null;
}
}
diff --git a/src/java/com/android/internal/telephony/Call.java b/src/java/com/android/internal/telephony/Call.java
index 37fefd9e03..cce5ee75f0 100644
--- a/src/java/com/android/internal/telephony/Call.java
+++ b/src/java/com/android/internal/telephony/Call.java
@@ -16,25 +16,43 @@
package com.android.internal.telephony;
+import android.annotation.UnsupportedAppUsage;
import android.telecom.ConferenceParticipant;
+import android.telephony.Rlog;
import java.util.ArrayList;
import java.util.List;
-import android.annotation.UnsupportedAppUsage;
-import android.telephony.Rlog;
-
/**
* {@hide}
*/
public abstract class Call {
protected final String LOG_TAG = "Call";
- /* Enums */
+ @UnsupportedAppUsage
+ public Call() {
+ }
+ /* Enums */
+ @UnsupportedAppUsage(implicitMember = "values()[Lcom/android/internal/telephony/Call$State;")
public enum State {
@UnsupportedAppUsage
- IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
+ IDLE,
+ ACTIVE,
+ @UnsupportedAppUsage
+ HOLDING,
+ @UnsupportedAppUsage
+ DIALING,
+ @UnsupportedAppUsage
+ ALERTING,
+ @UnsupportedAppUsage
+ INCOMING,
+ @UnsupportedAppUsage
+ WAITING,
+ @UnsupportedAppUsage
+ DISCONNECTED,
+ @UnsupportedAppUsage
+ DISCONNECTING;
@UnsupportedAppUsage
public boolean isAlive() {
diff --git a/src/java/com/android/internal/telephony/CallForwardInfo.java b/src/java/com/android/internal/telephony/CallForwardInfo.java
index ea3ba270aa..d9967adca0 100644
--- a/src/java/com/android/internal/telephony/CallForwardInfo.java
+++ b/src/java/com/android/internal/telephony/CallForwardInfo.java
@@ -28,6 +28,10 @@ public class CallForwardInfo {
private static final String TAG = "CallForwardInfo";
@UnsupportedAppUsage
+ public CallForwardInfo() {
+ }
+
+ @UnsupportedAppUsage
public int status; /*1 = active, 0 = not active */
@UnsupportedAppUsage
public int reason; /* from TS 27.007 7.11 "reason" */
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index 002e082abf..f24b3c809e 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -75,6 +75,10 @@ public abstract class CallTracker extends Handler {
protected static final int EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA = 16;
protected static final int EVENT_THREE_WAY_DIAL_BLANK_FLASH = 20;
+ @UnsupportedAppUsage
+ public CallTracker() {
+ }
+
protected void pollCallsWhenSafe() {
mNeedsPoll = true;
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 4241d26b01..a73b1eaf8b 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -43,7 +43,6 @@ import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.org.bouncycastle.util.io.pem.PemReader;
import org.json.JSONArray;
import org.json.JSONException;
@@ -55,7 +54,6 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.Reader;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -72,6 +70,10 @@ public class CarrierKeyDownloadManager extends Handler {
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-----";
+
private static final int DAY_IN_MILLIS = 24 * 3600 * 1000;
// Create a window prior to the key expiration, during which the cert will be
@@ -436,7 +438,6 @@ public class CarrierKeyDownloadManager extends Handler {
Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty");
return;
}
- PemReader reader = null;
try {
String mcc = "";
String mnc = "";
@@ -470,26 +471,14 @@ public class CarrierKeyDownloadManager extends Handler {
}
}
String identifier = key.getString(JSON_IDENTIFIER);
- ByteArrayInputStream inStream = new ByteArrayInputStream(cert.getBytes());
- Reader fReader = new BufferedReader(new InputStreamReader(inStream));
- reader = new PemReader(fReader);
Pair<PublicKey, Long> keyInfo =
- getKeyInformation(reader.readPemObject().getContent());
- reader.close();
+ getKeyInformation(cleanCertString(cert).getBytes());
savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc);
}
} catch (final JSONException e) {
Log.e(LOG_TAG, "Json parsing error: " + e.getMessage());
} catch (final Exception e) {
Log.e(LOG_TAG, "Exception getting certificate: " + e);
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (final Exception e) {
- Log.e(LOG_TAG, "Exception getting certificate: " + e);
- }
}
}
@@ -597,4 +586,16 @@ public class CarrierKeyDownloadManager extends Handler {
publicKey, new Date(expirationDate));
mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
}
+
+ /**
+ * Remove potential extraneous text in a certificate string
+ * @param cert certificate string
+ * @return Cleaned up version of the certificate string
+ */
+ @VisibleForTesting
+ public static String cleanCertString(String cert) {
+ return cert.substring(
+ cert.indexOf(CERT_BEGIN_STRING),
+ cert.indexOf(CERT_END_STRING) + CERT_END_STRING.length());
+ }
}
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index ba7e4ec73c..0393ffa245 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
@@ -37,9 +38,12 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
@@ -59,8 +63,10 @@ public class CarrierServiceBindHelper {
@UnsupportedAppUsage
private Context mContext;
- private AppBinding[] mBindings;
- private String[] mLastSimState;
+ @VisibleForTesting
+ public SparseArray<AppBinding> mBindings = new SparseArray();
+ @VisibleForTesting
+ public SparseArray<String> mLastSimState = new SparseArray<>();
private final PackageMonitor mPackageMonitor = new CarrierServicePackageMonitor();
private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
@@ -72,8 +78,8 @@ public class CarrierServiceBindHelper {
if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
// On user unlock, new components might become available, so reevaluate all
// bindings.
- for (int phoneId = 0; phoneId < mBindings.length; phoneId++) {
- mBindings[phoneId].rebind();
+ for (int phoneId = 0; phoneId < mBindings.size(); phoneId++) {
+ mBindings.get(phoneId).rebind();
}
}
}
@@ -81,24 +87,34 @@ public class CarrierServiceBindHelper {
private static final int EVENT_REBIND = 0;
private static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1;
+ @VisibleForTesting
+ public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 2;
@UnsupportedAppUsage
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
+ int phoneId;
AppBinding binding;
log("mHandler: " + msg.what);
switch (msg.what) {
case EVENT_REBIND:
- binding = (AppBinding) msg.obj;
+ phoneId = (int) msg.obj;
+ binding = mBindings.get(phoneId);
+ if (binding == null) return;
log("Rebinding if necessary for phoneId: " + binding.getPhoneId());
binding.rebind();
break;
case EVENT_PERFORM_IMMEDIATE_UNBIND:
- binding = (AppBinding) msg.obj;
+ phoneId = (int) msg.obj;
+ binding = mBindings.get(phoneId);
+ if (binding == null) return;
binding.performImmediateUnbind();
break;
+ case EVENT_MULTI_SIM_CONFIG_CHANGED:
+ updateBindingsAndSimStates();
+ break;
}
}
};
@@ -106,13 +122,10 @@ public class CarrierServiceBindHelper {
public CarrierServiceBindHelper(Context context) {
mContext = context;
- int numPhones = TelephonyManager.from(context).getMaxPhoneCount();
- mBindings = new AppBinding[numPhones];
- mLastSimState = new String[numPhones];
+ updateBindingsAndSimStates();
- for (int phoneId = 0; phoneId < numPhones; phoneId++) {
- mBindings[phoneId] = new AppBinding(phoneId);
- }
+ PhoneConfigurationManager.getInstance().registerForMultiSimConfigChange(
+ mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
mPackageMonitor.register(
context, mHandler.getLooper(), UserHandle.ALL, false /* externalStorage */);
@@ -121,19 +134,39 @@ public class CarrierServiceBindHelper {
mHandler);
}
+ // Create or dispose mBindings and mLastSimState objects.
+ private void updateBindingsAndSimStates() {
+ int prevLen = mBindings.size();
+ int newLen = ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .getActiveModemCount();
+
+ // If prevLen < newLen, allocate AppBinding and simState objects.
+ for (int phoneId = prevLen; phoneId < newLen; phoneId++) {
+ mBindings.put(phoneId, new AppBinding(phoneId));
+ mLastSimState.put(phoneId, new String());
+ }
+
+ // If prevLen > newLen, dispose AppBinding and simState objects.
+ for (int phoneId = newLen; phoneId < prevLen; phoneId++) {
+ mBindings.get(phoneId).unbind(true);
+ mBindings.delete(phoneId);
+ mLastSimState.delete(phoneId);
+ }
+ }
+
void updateForPhoneId(int phoneId, String simState) {
log("update binding for phoneId: " + phoneId + " simState: " + simState);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
return;
}
- if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.length) return;
- if (simState.equals(mLastSimState[phoneId])) {
+ if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.size()) return;
+ if (simState.equals(mLastSimState.get(phoneId))) {
// ignore consecutive duplicated events
return;
} else {
- mLastSimState[phoneId] = simState;
+ mLastSimState.put(phoneId, simState);
}
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, mBindings[phoneId]));
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, phoneId));
}
private class AppBinding {
@@ -197,8 +230,9 @@ public class CarrierServiceBindHelper {
String candidateServiceClass = null;
if (carrierResolveInfo != null) {
metadata = carrierResolveInfo.serviceInfo.metaData;
- candidateServiceClass =
- carrierResolveInfo.getComponentInfo().getComponentName().getClassName();
+ ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(carrierResolveInfo);
+ candidateServiceClass = new ComponentName(componentInfo.packageName,
+ componentInfo.name).getClassName();
}
// Only bind if the service wants it
@@ -274,7 +308,7 @@ public class CarrierServiceBindHelper {
mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS;
log("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis");
mHandler.sendMessageAtTime(
- mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, this),
+ mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, phoneId),
mUnbindScheduledUptimeMillis);
}
}
@@ -365,7 +399,8 @@ public class CarrierServiceBindHelper {
}
private void evaluateBinding(String carrierPackageName, boolean forceUnbind) {
- for (AppBinding appBinding : mBindings) {
+ for (int i = 0; i < mBindings.size(); i++) {
+ AppBinding appBinding = mBindings.get(i);
String appBindingPackage = appBinding.getPackage();
boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage);
// Only log if this package was a carrier package to avoid log spam in the common
@@ -391,8 +426,8 @@ public class CarrierServiceBindHelper {
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("CarrierServiceBindHelper:");
- for (AppBinding binding : mBindings) {
- binding.dump(fd, pw, args);
+ for (int i = 0; i < mBindings.size(); i++) {
+ mBindings.get(i).dump(fd, pw, args);
}
}
}
diff --git a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
index 2c4a728a69..24e50593fc 100644
--- a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
+++ b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
@@ -23,12 +23,10 @@ import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.service.carrier.CarrierMessagingService;
-import android.service.carrier.ICarrierMessagingCallback;
-import android.service.carrier.ICarrierMessagingService;
+import android.service.carrier.CarrierMessagingServiceWrapper;
+import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper;
import android.service.carrier.MessagePdu;
-import android.telephony.CarrierMessagingServiceManager;
import android.telephony.Rlog;
import android.util.LocalLog;
@@ -209,7 +207,7 @@ 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 CarrierMessagingServiceManager {
+ private final class CarrierSmsFilter extends CarrierMessagingServiceWrapper {
private final byte[][] mPdus;
private final int mDestPort;
private final String mSmsFormat;
@@ -223,8 +221,8 @@ public class CarrierServicesSmsFilter {
}
/**
- * Attempts to bind to a {@link ICarrierMessagingService}. Filtering is initiated
- * asynchronously once the service is ready using {@link #onServiceReady}.
+ * Attempts to bind to a {@link CarrierMessagingService}. Filtering is initiated
+ * asynchronously once the service is ready using {@link #onServiceReady()}.
*/
void filterSms(String carrierPackageName, CarrierSmsFilterCallback smsFilterCallback) {
mSmsFilterCallback = smsFilterCallback;
@@ -241,13 +239,12 @@ public class CarrierServicesSmsFilter {
* delivered to {@code smsFilterCallback}.
*/
@Override
- protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
+ public void onServiceReady() {
try {
log("onServiceReady: calling filterSms");
- carrierMessagingService.filterSms(
- new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
+ filterSms(new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
mPhone.getSubId(), mSmsFilterCallback);
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
loge("Exception filtering the SMS: " + e);
mSmsFilterCallback.onFilterComplete(
CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
@@ -259,15 +256,15 @@ 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 ICarrierMessagingCallback.Stub {
+ private final class CarrierSmsFilterCallback extends CarrierMessagingCallbackWrapper {
private final FilterAggregator mFilterAggregator;
- private final CarrierMessagingServiceManager mCarrierMessagingServiceManager;
+ private final CarrierMessagingServiceWrapper mCarrierMessagingServiceWrapper;
private boolean mIsOnFilterCompleteCalled;
CarrierSmsFilterCallback(FilterAggregator filterAggregator,
- CarrierMessagingServiceManager carrierMessagingServiceManager) {
+ CarrierMessagingServiceWrapper carrierMessagingServiceWrapper) {
mFilterAggregator = filterAggregator;
- mCarrierMessagingServiceManager = carrierMessagingServiceManager;
+ mCarrierMessagingServiceWrapper = carrierMessagingServiceWrapper;
mIsOnFilterCompleteCalled = false;
}
@@ -281,7 +278,7 @@ public class CarrierServicesSmsFilter {
// is run afterwards, we should not follow through
if (!mIsOnFilterCompleteCalled) {
mIsOnFilterCompleteCalled = true;
- mCarrierMessagingServiceManager.disposeConnection(mContext);
+ mCarrierMessagingServiceWrapper.disposeConnection(mContext);
mFilterAggregator.onFilterComplete(result);
}
}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java b/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
index 02ba64e89b..132ea3b021 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java
@@ -25,11 +25,15 @@ import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.telephony.CellBroadcastService;
import android.telephony.ICellBroadcastService;
import android.util.LocalLog;
import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.telephony.cdma.SmsMessage;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -53,7 +57,9 @@ public class CellBroadcastServiceManager {
private final LocalLog mLocalLog = new LocalLog(100);
/** New SMS cell broadcast received as an AsyncResult. */
- private static final int EVENT_NEW_SMS_CB = 0;
+ private static final int EVENT_NEW_GSM_SMS_CB = 0;
+ private static final int EVENT_NEW_CDMA_SMS_CB = 1;
+ private static final int EVENT_NEW_CDMA_SCP_MESSAGE = 2;
private boolean mEnabled;
public CellBroadcastServiceManager(Context context, Phone phone) {
@@ -63,10 +69,33 @@ public class CellBroadcastServiceManager {
}
/**
- * Send a CB message to the CellBroadcastServieManager's handler.
+ * Send a GSM CB message to the CellBroadcastServiceManager's handler.
* @param m the message
*/
- public void sendMessageToHandler(Message m) {
+ public void sendGsmMessageToHandler(Message m) {
+ m.what = EVENT_NEW_GSM_SMS_CB;
+ mModuleCellBroadcastHandler.sendMessage(m);
+ }
+
+ /**
+ * Send a CDMA CB message to the CellBroadcastServiceManager's handler.
+ * @param sms the SmsMessage to forward
+ */
+ public void sendCdmaMessageToHandler(SmsMessage sms) {
+ Message m = Message.obtain();
+ m.what = EVENT_NEW_CDMA_SMS_CB;
+ m.obj = sms;
+ mModuleCellBroadcastHandler.sendMessage(m);
+ }
+
+ /**
+ * Send a CDMA Service Category Program message to the CellBroadcastServiceManager's handler.
+ * @param sms the SCP message
+ */
+ public void sendCdmaScpMessageToHandler(SmsMessage sms, RemoteCallback callback) {
+ Message m = Message.obtain();
+ m.what = EVENT_NEW_CDMA_SCP_MESSAGE;
+ m.obj = Pair.create(sms, callback);
mModuleCellBroadcastHandler.sendMessage(m);
}
@@ -84,7 +113,9 @@ public class CellBroadcastServiceManager {
public void disable() {
mEnabled = false;
mPhone.mCi.unSetOnNewGsmBroadcastSms(mModuleCellBroadcastHandler);
- mContext.unbindService(sServiceConnection);
+ if (sServiceConnection.mService != null) {
+ mContext.unbindService(sServiceConnection);
+ }
}
/**
@@ -106,12 +137,34 @@ public class CellBroadcastServiceManager {
Log.d(TAG, "CB module is disabled.");
return;
}
+ if (sServiceConnection.mService == null) {
+ Log.d(TAG, "No connection to CB module, ignoring message.");
+ return;
+ }
try {
ICellBroadcastService cellBroadcastService =
ICellBroadcastService.Stub.asInterface(
sServiceConnection.mService);
- cellBroadcastService.handleGsmCellBroadcastSms(mPhone.getPhoneId(),
- (byte[]) ((AsyncResult) msg.obj).result);
+ if (msg.what == EVENT_NEW_GSM_SMS_CB) {
+ mLocalLog.log("GSM SMS CB for phone " + mPhone.getPhoneId());
+ cellBroadcastService.handleGsmCellBroadcastSms(mPhone.getPhoneId(),
+ (byte[]) ((AsyncResult) msg.obj).result);
+ } else if (msg.what == EVENT_NEW_CDMA_SMS_CB) {
+ mLocalLog.log("CDMA SMS CB for phone " + mPhone.getPhoneId());
+ SmsMessage sms = (SmsMessage) msg.obj;
+ cellBroadcastService.handleCdmaCellBroadcastSms(mPhone.getPhoneId(),
+ sms.getEnvelopeBearerData(), sms.getEnvelopeServiceCategory());
+ } else if (msg.what == EVENT_NEW_CDMA_SCP_MESSAGE) {
+ mLocalLog.log("CDMA SCP message for phone " + mPhone.getPhoneId());
+ Pair<SmsMessage, RemoteCallback> smsAndCallback =
+ (Pair<SmsMessage, RemoteCallback>) msg.obj;
+ SmsMessage sms = smsAndCallback.first;
+ RemoteCallback callback = smsAndCallback.second;
+ cellBroadcastService.handleCdmaScpMessage(mPhone.getPhoneId(),
+ sms.getSmsCbProgramData(),
+ sms.getOriginatingAddress(),
+ callback);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Failed to connect to default app: "
+ mCellBroadcastServicePackage + " err: " + e.toString());
@@ -126,9 +179,18 @@ public class CellBroadcastServiceManager {
Intent intent = new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE);
intent.setPackage(mCellBroadcastServicePackage);
if (sServiceConnection.mService == null) {
- mContext.bindService(intent, sServiceConnection, Context.BIND_AUTO_CREATE);
+ boolean serviceWasBound = mContext.bindService(intent, sServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ Log.d(TAG, "serviceWasBound=" + serviceWasBound);
+ if (!serviceWasBound) {
+ Log.e(TAG, "Unable to bind to service");
+ mLocalLog.log("Unable to bind to service");
+ return;
+ }
+ } else {
+ Log.d(TAG, "skipping bindService because connection already exists");
}
- mPhone.mCi.setOnNewGsmBroadcastSms(mModuleCellBroadcastHandler, EVENT_NEW_SMS_CB,
+ mPhone.mCi.setOnNewGsmBroadcastSms(mModuleCellBroadcastHandler, EVENT_NEW_GSM_SMS_CB,
null);
} else {
Log.e(TAG, "Unable to bind service; no cell broadcast service found");
@@ -156,6 +218,16 @@ public class CellBroadcastServiceManager {
Log.d(TAG, "mICellBroadcastService has disconnected unexpectedly");
this.mService = null;
}
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ Log.d(TAG, "Binding died");
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ Log.d(TAG, "Null binding");
+ }
}
/**
diff --git a/src/java/com/android/internal/telephony/CellularNetworkValidator.java b/src/java/com/android/internal/telephony/CellularNetworkValidator.java
index c1b990c025..48a90ea127 100644
--- a/src/java/com/android/internal/telephony/CellularNetworkValidator.java
+++ b/src/java/com/android/internal/telephony/CellularNetworkValidator.java
@@ -52,7 +52,7 @@ public class CellularNetworkValidator {
private int mState = STATE_IDLE;
private int mSubId;
- private int mTimeoutInMs;
+ private long mTimeoutInMs;
private boolean mReleaseAfterValidation;
private NetworkRequest mNetworkRequest;
@@ -114,7 +114,7 @@ public class CellularNetworkValidator {
/**
* API to start a validation
*/
- public synchronized void validate(int subId, int timeoutInMs,
+ public synchronized void validate(int subId, long timeoutInMs,
boolean releaseAfterValidation, ValidationCallback callback) {
// If it's already validating the same subscription, do nothing.
if (subId == mSubId) return;
diff --git a/src/java/com/android/internal/telephony/CommandException.java b/src/java/com/android/internal/telephony/CommandException.java
index 4642d90cf7..31ffcd23c2 100644
--- a/src/java/com/android/internal/telephony/CommandException.java
+++ b/src/java/com/android/internal/telephony/CommandException.java
@@ -123,6 +123,7 @@ public class CommandException extends RuntimeException {
OEM_ERROR_23,
OEM_ERROR_24,
OEM_ERROR_25,
+ REQUEST_CANCELLED,
}
@UnsupportedAppUsage
@@ -321,6 +322,8 @@ public class CommandException extends RuntimeException {
return new CommandException(Error.OEM_ERROR_24);
case RILConstants.OEM_ERROR_25:
return new CommandException(Error.OEM_ERROR_25);
+ case RILConstants.REQUEST_CANCELLED:
+ return new CommandException(Error.REQUEST_CANCELLED);
default:
Rlog.e("GSM", "Unrecognized RIL errno " + ril_errno);
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 006a2321eb..9cce7bc7e7 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -18,15 +18,19 @@ package com.android.internal.telephony;
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.telephony.TelephonyRegistryManager;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SrvccState;
import android.telephony.CallQuality;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
-import android.telephony.DataFailCause;
import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
@@ -38,21 +42,27 @@ import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
+import com.android.internal.telephony.PhoneInternalInterface.DataActivityState;
import java.util.List;
/**
* broadcast intents
*/
public class DefaultPhoneNotifier implements PhoneNotifier {
+
private static final String LOG_TAG = "DefaultPhoneNotifier";
private static final boolean DBG = false; // STOPSHIP if true
@UnsupportedAppUsage
protected ITelephonyRegistry mRegistry;
+ private TelephonyRegistryManager mTelephonyRegistryMgr;
+
- public DefaultPhoneNotifier() {
+ public DefaultPhoneNotifier(Context context) {
mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
- "telephony.registry"));
+ "telephony.registry"));
+ mTelephonyRegistryMgr = (TelephonyRegistryManager) context.getSystemService(
+ Context.TELEPHONY_REGISTRY_SERVICE);
}
@Override
@@ -64,15 +74,8 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
if (ringingCall != null && ringingCall.getEarliestConnection() != null) {
incomingNumber = ringingCall.getEarliestConnection().getAddress();
}
- try {
- if (mRegistry != null) {
- mRegistry.notifyCallStateForPhoneId(phoneId, subId,
- PhoneConstantConversions.convertCallState(
- sender.getState()), incomingNumber);
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyCallStateChanged(subId, phoneId,
+ PhoneConstantConversions.convertCallState(sender.getState()), incomingNumber);
}
@Override
@@ -82,18 +85,12 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
int subId = sender.getSubId();
Rlog.d(LOG_TAG, "nofityServiceState: mRegistry=" + mRegistry + " ss=" + ss
- + " sender=" + sender + " phondId=" + phoneId + " subId=" + subId);
+ + " sender=" + sender + " phondId=" + phoneId + " subId=" + subId);
if (ss == null) {
ss = new ServiceState();
ss.setStateOutOfService();
}
- try {
- if (mRegistry != null) {
- mRegistry.notifyServiceStateForPhoneId(phoneId, subId, ss);
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyServiceStateChanged(subId, phoneId, ss);
}
@Override
@@ -103,74 +100,53 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
if (DBG) {
// too chatty to log constantly
Rlog.d(LOG_TAG, "notifySignalStrength: mRegistry=" + mRegistry
- + " ss=" + sender.getSignalStrength() + " sender=" + sender);
- }
- try {
- if (mRegistry != null) {
- mRegistry.notifySignalStrengthForPhoneId(phoneId, subId,
- sender.getSignalStrength());
- }
- } catch (RemoteException ex) {
- // system process is dead
+ + " ss=" + sender.getSignalStrength() + " sender=" + sender);
}
+ mTelephonyRegistryMgr.notifySignalStrengthChanged(subId, phoneId,
+ sender.getSignalStrength());
}
@Override
public void notifyMessageWaitingChanged(Phone sender) {
int phoneId = sender.getPhoneId();
int subId = sender.getSubId();
-
- try {
- if (mRegistry != null) {
- mRegistry.notifyMessageWaitingChangedForPhoneId(phoneId, subId,
- sender.getMessageWaitingIndicator());
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyMessageWaitingChanged(subId, phoneId,
+ sender.getMessageWaitingIndicator());
}
@Override
public void notifyCallForwardingChanged(Phone sender) {
int subId = sender.getSubId();
- try {
- if (mRegistry != null) {
- Rlog.d(LOG_TAG, "notifyCallForwardingChanged: subId=" + subId + ", isCFActive="
- + sender.getCallForwardingIndicator());
+ Rlog.d(LOG_TAG, "notifyCallForwardingChanged: subId=" + subId + ", isCFActive="
+ + sender.getCallForwardingIndicator());
- mRegistry.notifyCallForwardingChangedForSubscriber(subId,
- sender.getCallForwardingIndicator());
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyCallForwardingChanged(subId,
+ sender.getCallForwardingIndicator());
}
@Override
public void notifyDataActivity(Phone sender) {
int subId = sender.getSubId();
- try {
- if (mRegistry != null) {
- mRegistry.notifyDataActivityForSubscriber(subId,
- convertDataActivityState(sender.getDataActivityState()));
- }
- } catch (RemoteException ex) {
- // system process is dead
+ if (mRegistry != null) {
+ mTelephonyRegistryMgr.notifyDataActivityChanged(subId,
+ convertDataActivityState(sender.getDataActivityState()));
}
}
@Override
public void notifyDataConnection(Phone sender, String apnType,
- PhoneConstants.DataState state) {
+ PhoneConstants.DataState state) {
doNotifyDataConnection(sender, apnType, state);
}
private void doNotifyDataConnection(Phone sender, String apnType,
- PhoneConstants.DataState state) {
+ PhoneConstants.DataState state) {
int subId = sender.getSubId();
int phoneId = sender.getPhoneId();
long dds = SubscriptionManager.getDefaultDataSubscriptionId();
- if (DBG) log("subId = " + subId + ", DDS = " + dds);
+ if (DBG) {
+ log("subId = " + subId + ", DDS = " + dds);
+ }
// TODO
// use apnType as the key to which connection we're talking about.
@@ -185,35 +161,24 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
networkCapabilities = sender.getNetworkCapabilities(apnType);
}
ServiceState ss = sender.getServiceState();
- if (ss != null) roaming = ss.getDataRoaming();
-
- try {
- if (mRegistry != null) {
- mRegistry.notifyDataConnectionForSubscriber(phoneId, subId,
- PhoneConstantConversions.convertDataState(state),
- sender.isDataAllowed(ApnSetting.getApnTypesBitmaskFromString(apnType)),
- sender.getActiveApnHost(apnType),
- apnType,
- linkProperties,
- networkCapabilities,
- ((telephony != null) ? telephony.getDataNetworkType(subId) :
- TelephonyManager.NETWORK_TYPE_UNKNOWN), roaming);
- }
- } catch (RemoteException ex) {
- // system process is dead
+ if (ss != null) {
+ roaming = ss.getDataRoaming();
}
+ mTelephonyRegistryMgr.notifyDataConnectionForSubscriber(phoneId, subId,
+ PhoneConstantConversions.convertDataState(state),
+ sender.isDataAllowed(ApnSetting.getApnTypesBitmaskFromString(apnType)),
+ sender.getActiveApnHost(apnType),
+ apnType,
+ linkProperties,
+ networkCapabilities,
+ ((telephony != null) ? telephony.getDataNetworkType(subId) :
+ TelephonyManager.NETWORK_TYPE_UNKNOWN), roaming);
}
@Override
public void notifyDataConnectionFailed(Phone sender, String apnType) {
- try {
- if (mRegistry != null) {
- mRegistry.notifyDataConnectionFailedForSubscriber(sender.getPhoneId(),
- sender.getSubId(), apnType);
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyDataConnectionFailed(sender.getSubId(), sender.getPhoneId(),
+ apnType);
}
@Override
@@ -221,30 +186,18 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
int subId = sender.getSubId();
Bundle data = new Bundle();
cl.fillInNotifierBundle(data);
- try {
- if (mRegistry != null) {
- mRegistry.notifyCellLocationForSubscriber(subId, data);
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyCellLocation(subId, data);
}
@Override
public void notifyCellInfo(Phone sender, List<CellInfo> cellInfo) {
int subId = sender.getSubId();
- try {
- if (mRegistry != null) {
- mRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
- }
- } catch (RemoteException ex) {
-
- }
+ mTelephonyRegistryMgr.notifyCellInfoChanged(subId, cellInfo);
}
@Override
public void notifyPhysicalChannelConfiguration(Phone sender,
- List<PhysicalChannelConfig> configs) {
+ List<PhysicalChannelConfig> configs) {
int subId = sender.getSubId();
int phoneId = sender.getPhoneId();
try {
@@ -259,13 +212,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
@Override
public void notifyOtaspChanged(Phone sender, int otaspMode) {
int subId = sender.getSubId();
- try {
- if (mRegistry != null) {
- mRegistry.notifyOtaspChanged(subId, otaspMode);
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyOtaspChanged(subId, otaspMode);
}
public void notifyPreciseCallState(Phone sender) {
@@ -273,122 +220,72 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
Call foregroundCall = sender.getForegroundCall();
Call backgroundCall = sender.getBackgroundCall();
if (ringingCall != null && foregroundCall != null && backgroundCall != null) {
- try {
- mRegistry.notifyPreciseCallState(sender.getPhoneId(), sender.getSubId(),
- convertPreciseCallState(ringingCall.getState()),
- convertPreciseCallState(foregroundCall.getState()),
- convertPreciseCallState(backgroundCall.getState()));
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyPreciseCallState(sender.getSubId(), sender.getPhoneId(),
+ convertPreciseCallState(ringingCall.getState()),
+ convertPreciseCallState(foregroundCall.getState()),
+ convertPreciseCallState(backgroundCall.getState()));
}
}
public void notifyDisconnectCause(Phone sender, int cause, int preciseCause) {
- try {
- mRegistry.notifyDisconnectCause(sender.getPhoneId(), sender.getSubId(), cause,
- preciseCause);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyDisconnectCause(sender.getSubId(), sender.getPhoneId(), cause,
+ preciseCause);
}
@Override
public void notifyImsDisconnectCause(@NonNull Phone sender, ImsReasonInfo imsReasonInfo) {
- try {
- mRegistry.notifyImsDisconnectCause(sender.getSubId(), imsReasonInfo);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyImsDisconnectCause(sender.getSubId(), imsReasonInfo);
}
public void notifyPreciseDataConnectionFailed(Phone sender, String apnType,
- String apn, @DataFailCause.FailCause int failCause) {
- try {
- mRegistry.notifyPreciseDataConnectionFailed(sender.getPhoneId(), sender.getSubId(),
- apnType, apn, failCause);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ String apn, @DataFailureCause int failCause) {
+ mTelephonyRegistryMgr.notifyPreciseDataConnectionFailed(sender.getSubId(),
+ sender.getPhoneId(), apnType, apn, failCause);
}
@Override
- public void notifySrvccStateChanged(Phone sender, @TelephonyManager.SrvccState int state) {
- try {
- mRegistry.notifySrvccStateChanged(sender.getSubId(), state);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ public void notifySrvccStateChanged(Phone sender, @SrvccState int state) {
+ mTelephonyRegistryMgr.notifySrvccStateChanged(sender.getSubId(), state);
}
@Override
public void notifyDataActivationStateChanged(Phone sender, int activationState) {
- try {
- mRegistry.notifySimActivationStateChangedForPhoneId(sender.getPhoneId(),
- sender.getSubId(), PhoneConstants.SIM_ACTIVATION_TYPE_DATA, activationState);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyDataActivationStateChanged(sender.getSubId(),
+ sender.getPhoneId(), activationState);
}
@Override
public void notifyVoiceActivationStateChanged(Phone sender, int activationState) {
- try {
- mRegistry.notifySimActivationStateChangedForPhoneId(sender.getPhoneId(),
- sender.getSubId(), PhoneConstants.SIM_ACTIVATION_TYPE_VOICE, activationState);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyVoiceActivationStateChanged(sender.getSubId(),
+ sender.getPhoneId(), activationState);
}
@Override
public void notifyUserMobileDataStateChanged(Phone sender, boolean state) {
- try {
- mRegistry.notifyUserMobileDataStateChangedForPhoneId(
- sender.getPhoneId(), sender.getSubId(), state);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyUserMobileDataStateChanged(
+ sender.getSubId(), sender.getPhoneId(), state);
}
@Override
public void notifyOemHookRawEventForSubscriber(Phone sender, byte[] rawData) {
- try {
- mRegistry.notifyOemHookRawEventForSubscriber(sender.getPhoneId(),
- sender.getSubId(), rawData);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyOemHookRawEventForSubscriber(sender.getSubId(),
+ sender.getPhoneId(), rawData);
}
@Override
public void notifyPhoneCapabilityChanged(PhoneCapability capability) {
- try {
- mRegistry.notifyPhoneCapabilityChanged(capability);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyPhoneCapabilityChanged(capability);
}
@Override
- public void notifyRadioPowerStateChanged(Phone sender,
- @TelephonyManager.RadioPowerState int state) {
- try {
- mRegistry.notifyRadioPowerStateChanged(sender.getPhoneId(), sender.getSubId(), state);
- } catch (RemoteException ex) {
- // system process is dead
- }
+ public void notifyRadioPowerStateChanged(Phone sender, @RadioPowerState int state) {
+ mTelephonyRegistryMgr.notifyRadioPowerStateChanged(sender.getSubId(), sender.getPhoneId(),
+ state);
}
@Override
public void notifyEmergencyNumberList(Phone sender) {
- try {
- if (mRegistry != null) {
- mRegistry.notifyEmergencyNumberList(sender.getPhoneId(), sender.getSubId());
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ mTelephonyRegistryMgr.notifyEmergencyNumberList(sender.getSubId(), sender.getPhoneId());
}
@Override
@@ -417,22 +314,16 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
@Override
public void notifyCallQualityChanged(Phone sender, CallQuality callQuality,
- int callNetworkType) {
- try {
- if (mRegistry != null) {
- mRegistry.notifyCallQualityChanged(callQuality, sender.getPhoneId(),
- sender.getSubId(), callNetworkType);
- }
- } catch (RemoteException ex) {
- // system process is dead
- }
+ int callNetworkType) {
+ mTelephonyRegistryMgr.notifyCallQualityChanged(sender.getSubId(), sender.getPhoneId(),
+ callQuality, callNetworkType);
}
/**
- * Convert the {@link Phone.DataActivityState} enum into the TelephonyManager.DATA_* constants
- * for the public API.
+ * Convert the {@link DataActivityState} enum into the TelephonyManager.DATA_* constants for the
+ * public API.
*/
- public static int convertDataActivityState(Phone.DataActivityState state) {
+ public static int convertDataActivityState(DataActivityState state) {
switch (state) {
case DATAIN:
return TelephonyManager.DATA_ACTIVITY_IN;
diff --git a/src/java/com/android/internal/telephony/DriverCall.java b/src/java/com/android/internal/telephony/DriverCall.java
index 8c2c3db3dc..3a7947d0f8 100644
--- a/src/java/com/android/internal/telephony/DriverCall.java
+++ b/src/java/com/android/internal/telephony/DriverCall.java
@@ -17,9 +17,8 @@
package com.android.internal.telephony;
import android.annotation.UnsupportedAppUsage;
-import android.telephony.Rlog;
-import java.lang.Comparable;
import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
/**
* {@hide}
@@ -27,6 +26,8 @@ import android.telephony.PhoneNumberUtils;
public class DriverCall implements Comparable<DriverCall> {
static final String LOG_TAG = "DriverCall";
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/DriverCall$State;")
public enum State {
@UnsupportedAppUsage
ACTIVE,
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index cc768415d2..1bbcd5e587 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -414,7 +414,7 @@ public class GsmCdmaCallTracker extends CallTracker {
TelephonyManager tm =
(TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
String origNumber = dialString;
- String operatorIsoContry = tm.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
+ String operatorIsoContry = tm.getNetworkCountryIso(mPhone.getPhoneId());
String simIsoContry = tm.getSimCountryIsoForPhone(mPhone.getPhoneId());
boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry)
&& !TextUtils.isEmpty(simIsoContry)
diff --git a/src/java/com/android/internal/telephony/HbpcdLookup.java b/src/java/com/android/internal/telephony/HbpcdLookup.java
deleted file mode 100644
index d9a3e725b6..0000000000
--- a/src/java/com/android/internal/telephony/HbpcdLookup.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-**
-** Copyright 2014, 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.Uri;
-import android.provider.BaseColumns;
-
-/**
- * @hide
- */
-public class HbpcdLookup {
- public static final String AUTHORITY = "hbpcd_lookup";
-
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY);
-
- public static final String PATH_MCC_IDD = "idd";
- public static final String PATH_MCC_LOOKUP_TABLE = "lookup";
- public static final String PATH_MCC_SID_CONFLICT = "conflict";
- public static final String PATH_MCC_SID_RANGE = "range";
- public static final String PATH_NANP_AREA_CODE = "nanp";
- public static final String PATH_ARBITRARY_MCC_SID_MATCH = "arbitrary";
- public static final String PATH_USERADD_COUNTRY = "useradd";
-
- public static final String ID = "_id";
- public static final int IDINDEX = 0;
-
- /**
- * @hide
- */
- public static class MccIdd implements BaseColumns {
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_IDD);
- public static final String DEFAULT_SORT_ORDER = "MCC ASC";
-
- public static final String MCC = "MCC";
- public static final String IDD = "IDD";
-
- }
-
- /**
- * @hide
- */
- public static class MccLookup implements BaseColumns {
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_LOOKUP_TABLE);
- public static final String DEFAULT_SORT_ORDER = "MCC ASC";
-
- public static final String MCC = "MCC";
- public static final String COUNTRY_CODE = "Country_Code";
- public static final String COUNTRY_NAME = "Country_Name";
- public static final String NDD = "NDD";
- public static final String NANPS = "NANPS";
- public static final String GMT_OFFSET_LOW = "GMT_Offset_Low";
- public static final String GMT_OFFSET_HIGH = "GMT_Offset_High";
- public static final String GMT_DST_LOW = "GMT_DST_Low";
- public static final String GMT_DST_HIGH = "GMT_DST_High";
-
- }
-
- /**
- * @hide
- */
- public static class MccSidConflicts implements BaseColumns {
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_CONFLICT);
- public static final String DEFAULT_SORT_ORDER = "MCC ASC";
-
- public static final String MCC = "MCC";
- public static final String SID_CONFLICT = "SID_Conflict";
-
- }
-
- /**
- * @hide
- */
- public static class MccSidRange implements BaseColumns {
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_RANGE);
- public static final String DEFAULT_SORT_ORDER = "MCC ASC";
-
- public static final String MCC = "MCC";
- public static final String RANGE_LOW = "SID_Range_Low";
- public static final String RANGE_HIGH = "SID_Range_High";
- }
-
- /**
- * @hide
- */
- public static class ArbitraryMccSidMatch implements BaseColumns {
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/" + PATH_ARBITRARY_MCC_SID_MATCH);
- public static final String DEFAULT_SORT_ORDER = "MCC ASC";
-
- public static final String MCC = "MCC";
- public static final String SID = "SID";
-
- }
-
- /**
- * @hide
- */
- public static class NanpAreaCode implements BaseColumns {
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/" + PATH_NANP_AREA_CODE);
- public static final String DEFAULT_SORT_ORDER = "Area_Code ASC";
-
- public static final String AREA_CODE = "Area_Code";
- }
-}
diff --git a/src/java/com/android/internal/telephony/HbpcdUtils.java b/src/java/com/android/internal/telephony/HbpcdUtils.java
deleted file mode 100644
index 2f3194214b..0000000000
--- a/src/java/com/android/internal/telephony/HbpcdUtils.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2014 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.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.telephony.Rlog;
-
-import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
-import com.android.internal.telephony.HbpcdLookup.MccIdd;
-import com.android.internal.telephony.HbpcdLookup.MccLookup;
-import com.android.internal.telephony.HbpcdLookup.MccSidConflicts;
-import com.android.internal.telephony.HbpcdLookup.MccSidRange;
-
-public final class HbpcdUtils {
- private static final String LOG_TAG = "HbpcdUtils";
- private static final boolean DBG = false;
- private ContentResolver resolver = null;
-
- public HbpcdUtils(Context context) {
- resolver = context.getContentResolver();
- }
-
- /**
- * Resolves the unknown MCC with SID and Timezone information.
- */
- public int getMcc(int sid, int tz, int DSTflag, boolean isNitzTimeZone) {
- int tmpMcc = 0;
-
- // check if SID exists in arbitrary_mcc_sid_match table.
- // these SIDs are assigned to more than 1 operators, but they are known to
- // be used by a specific operator, other operators having the same SID are
- // not using it currently, if that SID is in this table, we don't need to
- // check other tables.
- String projection2[] = {ArbitraryMccSidMatch.MCC};
- Cursor c2 = resolver.query(ArbitraryMccSidMatch.CONTENT_URI, projection2,
- ArbitraryMccSidMatch.SID + "=" + sid, null, null);
-
- if (c2 != null) {
- int c2Counter = c2.getCount();
- if (DBG) {
- Rlog.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
- }
- if (c2Counter == 1) {
- if (DBG) {
- Rlog.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2);
- }
- c2.moveToFirst();
- tmpMcc = c2.getInt(0);
- if (DBG) {
- Rlog.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
- }
- c2.close();
- return tmpMcc;
- }
- c2.close();
- }
-
- // Then check if SID exists in mcc_sid_conflict table.
- // and use the timezone in mcc_lookup table to check which MCC matches.
- String projection3[] = {MccSidConflicts.MCC};
- Cursor c3 = resolver.query(MccSidConflicts.CONTENT_URI, projection3,
- MccSidConflicts.SID_CONFLICT + "=" + sid + " and (((" +
- MccLookup.GMT_OFFSET_LOW + "<=" + tz + ") and (" + tz + "<=" +
- MccLookup.GMT_OFFSET_HIGH + ") and (" + "0=" + DSTflag + ")) or ((" +
- MccLookup.GMT_DST_LOW + "<=" + tz + ") and (" + tz + "<=" +
- MccLookup.GMT_DST_HIGH + ") and (" + "1=" + DSTflag + ")))",
- null, null);
- if (c3 != null) {
- int c3Counter = c3.getCount();
- if (c3Counter > 0) {
- if (c3Counter > 1) {
- Rlog.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
- }
- if (DBG) Rlog.d(LOG_TAG, "Query conflict sid returned the cursor " + c3);
- c3.moveToFirst();
- tmpMcc = c3.getInt(0);
- if (DBG) {
- Rlog.d(LOG_TAG, "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
- }
- if (!isNitzTimeZone) {
- // time zone is not accurate, it may get wrong mcc, ignore it.
- if (DBG) {
- Rlog.d(LOG_TAG, "time zone is not accurate, mcc may be " + tmpMcc);
- }
- tmpMcc = 0;
- }
- c3.close();
- return tmpMcc;
- } else {
- c3.close();
- }
- }
-
- // if there is no conflict, then check if SID is in mcc_sid_range.
- String projection5[] = {MccSidRange.MCC};
- Cursor c5 = resolver.query(MccSidRange.CONTENT_URI, projection5,
- MccSidRange.RANGE_LOW + "<=" + sid + " and " +
- MccSidRange.RANGE_HIGH + ">=" + sid,
- null, null);
- if (c5 != null) {
- if (c5.getCount() > 0) {
- if (DBG) Rlog.d(LOG_TAG, "Query Range returned the cursor " + c5);
- c5.moveToFirst();
- tmpMcc = c5.getInt(0);
- if (DBG) Rlog.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
- c5.close();
- return tmpMcc;
- }
- c5.close();
- }
- if (DBG) Rlog.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
-
- if (DBG) Rlog.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc = " + tmpMcc);
- // If unknown MCC still could not be resolved,
- return tmpMcc;
- }
-
- /**
- * Gets country information with given MCC.
- */
- public String getIddByMcc(int mcc) {
- if (DBG) Rlog.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
- String idd = "";
-
- Cursor c = null;
-
- String projection[] = {MccIdd.IDD};
- Cursor cur = resolver.query(MccIdd.CONTENT_URI, projection,
- MccIdd.MCC + "=" + mcc, null, null);
- if (cur != null) {
- if (cur.getCount() > 0) {
- if (DBG) Rlog.d(LOG_TAG, "Query Idd returned the cursor " + cur);
- // TODO: for those country having more than 1 IDDs, need more information
- // to decide which IDD would be used. currently just use the first 1.
- cur.moveToFirst();
- idd = cur.getString(0);
- if (DBG) Rlog.d(LOG_TAG, "IDD = " + idd);
-
- }
- cur.close();
- }
- if (c != null) c.close();
-
- if (DBG) Rlog.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
- return idd;
- }
-}
diff --git a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
index fbbdee215b..dc990de650 100644
--- a/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
+++ b/src/java/com/android/internal/telephony/IIccPhoneBook.aidl
@@ -30,6 +30,7 @@ interface IIccPhoneBook {
* @param efid the EF id of a ADN-like SIM
* @return List of AdnRecord
*/
+ @UnsupportedAppUsage
List<AdnRecord> getAdnRecordsInEf(int efid);
/**
@@ -40,6 +41,7 @@ interface IIccPhoneBook {
* @param subId user preferred subId
* @return List of AdnRecord
*/
+ @UnsupportedAppUsage
List<AdnRecord> getAdnRecordsInEfForSubscriber(int subId, int efid);
/**
@@ -60,6 +62,7 @@ interface IIccPhoneBook {
* @param pin2 required to update EF_FDN, otherwise must be null
* @return true for success
*/
+ @UnsupportedAppUsage
boolean updateAdnRecordsInEfBySearch(int efid,
String oldTag, String oldPhoneNumber,
String newTag, String newPhoneNumber,
@@ -138,6 +141,7 @@ interface IIccPhoneBook {
* recordSizes[1] is the total length of the EF file
* recordSizes[2] is the number of records in the EF file
*/
+ @UnsupportedAppUsage
int[] getAdnRecordsSize(int efid);
/**
@@ -150,6 +154,7 @@ interface IIccPhoneBook {
* recordSizes[1] is the total length of the EF file
* recordSizes[2] is the number of records in the EF file
*/
+ @UnsupportedAppUsage
int[] getAdnRecordsSizeForSubscriber(int subId, int efid);
}
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
index 3ac4027f99..ae5cd7b6d9 100644
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -81,6 +81,10 @@ public class IccProvider extends ContentProvider {
private SubscriptionManager mSubscriptionManager;
+ @UnsupportedAppUsage
+ public IccProvider() {
+ }
+
@Override
public boolean onCreate() {
mSubscriptionManager = SubscriptionManager.from(getContext());
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 6771a2f615..c98d2659e2 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -72,6 +72,8 @@ public class IccSmsInterfaceManager {
@UnsupportedAppUsage
private List<SmsRawData> mSms;
+ private String mSmsc;
+
@UnsupportedAppUsage
private CellBroadcastRangeManager mCellBroadcastRangeManager =
new CellBroadcastRangeManager();
@@ -82,6 +84,8 @@ public class IccSmsInterfaceManager {
private static final int EVENT_UPDATE_DONE = 2;
protected static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
protected static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
+ private static final int EVENT_GET_SMSC_DONE = 5;
+ private static final int EVENT_SET_SMSC_DONE = 6;
private static final int SMS_CB_CODE_SCHEME_MIN = 0;
private static final int SMS_CB_CODE_SCHEME_MAX = 255;
public static final int SMS_MESSAGE_PRIORITY_NOT_SPECIFIED = -1;
@@ -137,6 +141,25 @@ public class IccSmsInterfaceManager {
mLock.notifyAll();
}
break;
+ case EVENT_GET_SMSC_DONE:
+ ar = (AsyncResult) msg.obj;
+ synchronized (mLock) {
+ if (ar.exception == null) {
+ mSmsc = (String) ar.result;
+ } else {
+ log("Cannot read SMSC");
+ mSmsc = null;
+ }
+ mLock.notifyAll();
+ }
+ break;
+ case EVENT_SET_SMSC_DONE:
+ ar = (AsyncResult) msg.obj;
+ synchronized (mLock) {
+ mSuccess = (ar.exception == null);
+ mLock.notifyAll();
+ }
+ break;
}
}
};
@@ -799,6 +822,53 @@ public class IccSmsInterfaceManager {
return data;
}
+ /**
+ * Gets the SMSC address from (U)SIM.
+ *
+ * @return the SMSC address string, null if failed.
+ */
+ public String getSmscAddressFromIccEf(String callingPackage) {
+ if (!mSmsPermissions.checkCallingOrSelfCanGetSmscAddress(
+ callingPackage, "getSmscAddressFromIccEf")) {
+ return null;
+ }
+ synchronized (mLock) {
+ mSmsc = null;
+ Message response = mHandler.obtainMessage(EVENT_GET_SMSC_DONE);
+ mPhone.mCi.getSmscAddress(response);
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to read SMSC");
+ }
+ }
+ return mSmsc;
+ }
+
+ /**
+ * Sets the SMSC address on (U)SIM.
+ *
+ * @param smsc the SMSC address string.
+ * @return true for success, false otherwise.
+ */
+ public boolean setSmscAddressOnIccEf(String callingPackage, String smsc) {
+ if (!mSmsPermissions.checkCallingOrSelfCanSetSmscAddress(
+ callingPackage, "setSmscAddressOnIccEf")) {
+ return false;
+ }
+ synchronized (mLock) {
+ mSuccess = false;
+ Message response = mHandler.obtainMessage(EVENT_SET_SMSC_DONE);
+ mPhone.mCi.setSmscAddress(smsc, response);
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to write SMSC");
+ }
+ }
+ return mSuccess;
+ }
+
public boolean enableCellBroadcast(int messageIdentifier, int ranType) {
return enableCellBroadcastRange(messageIdentifier, messageIdentifier, ranType);
}
@@ -1327,8 +1397,7 @@ public class IccSmsInterfaceManager {
@UnsupportedAppUsage
private String filterDestAddress(String destAddr) {
- String result = null;
- result = SmsNumberUtils.filterDestAddr(mPhone, destAddr);
+ String result = SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(), destAddr);
return result != null ? result : destAddr;
}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index cbf502c972..4be189de6e 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -26,6 +26,7 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsSmsListener;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -71,12 +72,12 @@ public class ImsSmsDispatcher extends SMSDispatcher {
* Listen to the IMS service state change
*
*/
- private android.telephony.ims.ImsMmTelManager.RegistrationCallback mRegistrationCallback =
- new android.telephony.ims.ImsMmTelManager.RegistrationCallback() {
+ private RegistrationManager.RegistrationCallback mRegistrationCallback =
+ new RegistrationManager.RegistrationCallback() {
@Override
public void onRegistered(
@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
- Rlog.d(TAG, "onImsConnected imsRadioTech=" + imsRadioTech);
+ logd("onImsConnected imsRadioTech=" + imsRadioTech);
synchronized (mLock) {
mIsRegistered = true;
}
@@ -85,7 +86,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void onRegistering(
@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
- Rlog.d(TAG, "onImsProgressing imsRadioTech=" + imsRadioTech);
+ logd("onImsProgressing imsRadioTech=" + imsRadioTech);
synchronized (mLock) {
mIsRegistered = false;
}
@@ -93,7 +94,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void onUnregistered(ImsReasonInfo info) {
- Rlog.d(TAG, "onImsDisconnected imsReasonInfo=" + info);
+ logd("onImsDisconnected imsReasonInfo=" + info);
synchronized (mLock) {
mIsRegistered = false;
}
@@ -116,7 +117,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
int reason, int networkReasonCode) {
- Rlog.d(TAG, "onSendSmsResult token=" + token + " messageRef=" + messageRef
+ logd("onSendSmsResult token=" + token + " messageRef=" + messageRef
+ " status=" + status + " reason=" + reason + " networkReasonCode="
+ networkReasonCode);
// TODO integrate networkReasonCode into IMS SMS metrics.
@@ -154,7 +155,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void onSmsStatusReportReceived(int token, String format, byte[] pdu)
throws RemoteException {
- Rlog.d(TAG, "Status report received.");
+ logd("Status report received.");
android.telephony.SmsMessage message =
android.telephony.SmsMessage.createFromPdu(pdu, format);
if (message == null || message.mWrappedSmsMessage == null) {
@@ -177,8 +178,8 @@ public class ImsSmsDispatcher extends SMSDispatcher {
}
Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
tracker, format, pdu);
- Rlog.d(TAG, "Status report handle result, success: " + result.first +
- "complete: " + result.second);
+ logd("Status report handle result, success: " + result.first
+ + " complete: " + result.second);
try {
getImsManager().acknowledgeSmsReport(
token,
@@ -186,7 +187,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
result.first ? ImsSmsImplBase.STATUS_REPORT_STATUS_OK
: ImsSmsImplBase.STATUS_REPORT_STATUS_ERROR);
} catch (ImsException e) {
- Rlog.e(TAG, "Failed to acknowledgeSmsReport(). Error: "
+ loge("Failed to acknowledgeSmsReport(). Error: "
+ e.getMessage());
}
if (result.second) {
@@ -196,11 +197,11 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void onSmsReceived(int token, String format, byte[] pdu) {
- Rlog.d(TAG, "SMS received.");
+ logd("SMS received.");
android.telephony.SmsMessage message =
android.telephony.SmsMessage.createFromPdu(pdu, format);
mSmsDispatchersController.injectSmsPdu(message, format, result -> {
- Rlog.d(TAG, "SMS handled result: " + result);
+ logd("SMS handled result: " + result);
int mappedResult;
switch (result) {
case Intents.RESULT_SMS_HANDLED:
@@ -221,11 +222,11 @@ public class ImsSmsDispatcher extends SMSDispatcher {
getImsManager().acknowledgeSms(token,
message.mWrappedSmsMessage.mMessageRef, mappedResult);
} else {
- Rlog.w(TAG, "SMS Received with a PDU that could not be parsed.");
+ logw("SMS Received with a PDU that could not be parsed.");
getImsManager().acknowledgeSms(token, 0, mappedResult);
}
} catch (ImsException e) {
- Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
+ loge("Failed to acknowledgeSms(). Error: " + e.getMessage());
}
}, true);
}
@@ -248,7 +249,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void connectionReady(ImsManager manager) throws ImsException {
- Rlog.d(TAG, "ImsManager: connection ready.");
+ logd("ImsManager: connection ready.");
synchronized (mLock) {
setListeners();
mIsImsServiceUp = true;
@@ -257,7 +258,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void connectionUnavailable() {
- Rlog.d(TAG, "ImsManager: connection unavailable.");
+ logd("ImsManager: connection unavailable.");
synchronized (mLock) {
mIsImsServiceUp = false;
}
@@ -292,7 +293,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
PersistableBundle b;
boolean eSmsCarrierSupport = false;
if (!PhoneNumberUtils.isLocalEmergencyNumber(mContext, mPhone.getSubId(), destAddr)) {
- Rlog.e(TAG, "Emergency Sms is not supported for: " + Rlog.pii(TAG, destAddr));
+ loge("Emergency Sms is not supported for: " + Rlog.pii(TAG, destAddr));
return false;
}
@@ -301,18 +302,18 @@ public class ImsSmsDispatcher extends SMSDispatcher {
CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager == null) {
- Rlog.e(TAG, "configManager is null");
+ loge("configManager is null");
return false;
}
b = configManager.getConfigForSubId(getSubId());
if (b == null) {
- Rlog.e(TAG, "PersistableBundle is null");
+ loge("PersistableBundle is null");
return false;
}
eSmsCarrierSupport = b.getBoolean(
CarrierConfigManager.KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL);
boolean lteOrLimitedLte = isEmergencySmsPossible();
- Rlog.i(TAG, "isEmergencySmsSupport emergencySmsCarrierSupport: "
+ logi("isEmergencySmsSupport emergencySmsCarrierSupport: "
+ eSmsCarrierSupport + " destAddr: " + Rlog.pii(TAG, destAddr)
+ " mIsImsServiceUp: " + mIsImsServiceUp + " lteOrLimitedLte: "
+ lteOrLimitedLte);
@@ -325,7 +326,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
public boolean isAvailable() {
synchronized (mLock) {
- Rlog.d(TAG, "isAvailable: up=" + mIsImsServiceUp + ", reg= " + mIsRegistered
+ logd("isAvailable: up=" + mIsImsServiceUp + ", reg= " + mIsRegistered
+ ", cap= " + mIsSmsCapable);
return mIsImsServiceUp && mIsRegistered && mIsSmsCapable;
}
@@ -336,7 +337,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
try {
return getImsManager().getSmsFormat();
} catch (ImsException e) {
- Rlog.e(TAG, "Failed to get sms format. Error: " + e.getMessage());
+ loge("Failed to get sms format. Error: " + e.getMessage());
return SmsConstants.FORMAT_UNKNOWN;
}
}
@@ -370,7 +371,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
@Override
public void sendSms(SmsTracker tracker) {
- Rlog.d(TAG, "sendSms: "
+ logd("sendSms: "
+ " mRetryCount=" + tracker.mRetryCount
+ " mMessageRef=" + tracker.mMessageRef
+ " SS=" + mPhone.getServiceState().getState());
@@ -409,7 +410,7 @@ public class ImsSmsDispatcher extends SMSDispatcher {
mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format,
ImsSmsImplBase.SEND_STATUS_OK);
} catch (ImsException e) {
- Rlog.e(TAG, "sendSms failed. Falling back to PSTN. Error: " + e.getMessage());
+ loge("sendSms failed. Falling back to PSTN. Error: " + e.getMessage());
fallbackToPstn(token, tracker);
mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format,
ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK);
@@ -435,4 +436,24 @@ public class ImsSmsDispatcher extends SMSDispatcher {
public IImsSmsListener getSmsListener() {
return mImsSmsListener;
}
+
+ private void logd(String s) {
+ Rlog.d(TAG + " [" + getPhoneId(mPhone) + "]", s);
+ }
+
+ private void logi(String s) {
+ Rlog.i(TAG + " [" + getPhoneId(mPhone) + "]", s);
+ }
+
+ private void logw(String s) {
+ Rlog.w(TAG + " [" + getPhoneId(mPhone) + "]", s);
+ }
+
+ private void loge(String s) {
+ Rlog.e(TAG + " [" + getPhoneId(mPhone) + "]", s);
+ }
+
+ private static String getPhoneId(Phone phone) {
+ return (phone != null) ? Integer.toString(phone.getPhoneId()) : "?";
+ }
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 449e320fe9..dc7d19c693 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -56,7 +56,6 @@ import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.service.carrier.CarrierMessagingService;
import android.telephony.Rlog;
-import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -241,6 +240,10 @@ public abstract class InboundSmsHandler extends StateMachine {
@UnsupportedAppUsage
IDeviceIdleController mDeviceIdleController;
+ protected static boolean sEnableCbModule = false;
+
+ protected CellBroadcastServiceManager mCellBroadcastServiceManager;
+
// Delete permanently from raw table
private final int DELETE_PERMANENTLY = 1;
// Only mark deleted, but keep in db for message de-duping
@@ -284,6 +287,7 @@ public abstract class InboundSmsHandler extends StateMachine {
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mDeviceIdleController = TelephonyComponentFactory.getInstance()
.inject(IDeviceIdleController.class.getName()).getIDeviceIdleController();
+ mCellBroadcastServiceManager = new CellBroadcastServiceManager(context, phone);
addState(mDefaultState);
addState(mStartupState, mDefaultState);
@@ -308,6 +312,7 @@ public abstract class InboundSmsHandler extends StateMachine {
@Override
protected void onQuitting() {
mWapPush.dispose();
+ mCellBroadcastServiceManager.disable();
while (mWakeLock.isHeld()) {
mWakeLock.release();
@@ -1258,15 +1263,6 @@ public abstract class InboundSmsHandler extends StateMachine {
intent.setComponent(null);
}
- // TODO: Validate that this is the right place to store the SMS.
- if (SmsManager.getDefault().getAutoPersisting()) {
- final Uri uri = writeInboxMessage(intent);
- if (uri != null) {
- // Pass this to SMS apps so that they know where it is stored
- intent.putExtra("uri", uri.toString());
- }
- }
-
// Handle app specific sms messages.
AppSmsManager appManager = mPhone.getAppSmsManager();
if (appManager.handleSmsReceivedIntent(intent)) {
@@ -1712,6 +1708,9 @@ public abstract class InboundSmsHandler extends StateMachine {
if (mCellBroadcastHandler != null) {
mCellBroadcastHandler.dump(fd, pw, args);
}
+ if (mCellBroadcastServiceManager != null) {
+ mCellBroadcastServiceManager.dump(fd, pw, args);
+ }
mLocalLog.dump(fd, pw, args);
}
@@ -1756,6 +1755,32 @@ public abstract class InboundSmsHandler extends StateMachine {
}
}
+ protected byte[] decodeHexString(String hexString) {
+ if (hexString == null || hexString.length() % 2 == 1) {
+ return null;
+ }
+ byte[] bytes = new byte[hexString.length() / 2];
+ for (int i = 0; i < hexString.length(); i += 2) {
+ bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
+ }
+ return bytes;
+ }
+
+ private byte hexToByte(String hexString) {
+ int firstDigit = toDigit(hexString.charAt(0));
+ int secondDigit = toDigit(hexString.charAt(1));
+ return (byte) ((firstDigit << 4) + secondDigit);
+ }
+
+ private int toDigit(char hexChar) {
+ int digit = Character.digit(hexChar, 16);
+ if (digit == -1) {
+ return 0;
+ }
+ return digit;
+ }
+
+
/**
* Registers the broadcast receiver to launch the default SMS app when the user clicks the
* new message notification.
@@ -1765,4 +1790,46 @@ public abstract class InboundSmsHandler extends StateMachine {
userFilter.addAction(ACTION_OPEN_SMS_APP);
context.registerReceiver(new NewMessageNotificationActionReceiver(), userFilter);
}
+
+ protected abstract class CbTestBroadcastReceiver extends BroadcastReceiver {
+
+ protected abstract void handleTestAction(Intent intent);
+ protected abstract void handleToggleEnable();
+ protected abstract void handleToggleDisable(Context context);
+
+ protected final String mTestAction;
+ protected final String mToggleAction;
+
+ public CbTestBroadcastReceiver(String testAction, String toggleAction) {
+ mTestAction = testAction;
+ mToggleAction = toggleAction;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ logd("Received test intent action=" + intent.getAction());
+ if (intent.getAction().equals(mTestAction)) {
+ // 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;
+ }
+ handleTestAction(intent);
+ } else if (intent.getAction().equals(mToggleAction)) {
+ if (intent.hasExtra("enable")) {
+ sEnableCbModule = intent.getBooleanExtra("enable", false);
+ } else {
+ sEnableCbModule = !sEnableCbModule;
+ }
+ if (sEnableCbModule) {
+ log("enabling CB module");
+ handleToggleEnable();
+ } else {
+ log("enabling legacy platform CB handling");
+ handleToggleDisable(context);
+ }
+ }
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index 13d7dfb909..b284799e4b 100755
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -25,8 +25,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.wifi.WifiManager;
import android.os.AsyncResult;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -48,6 +48,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@@ -76,6 +77,31 @@ public class LocaleTracker extends Handler {
/** Event to fire if the operator from ServiceState is considered truly lost */
private static final int EVENT_OPERATOR_LOST = 6;
+ /** Event to override the current locale */
+ private static final int EVENT_OVERRIDE_LOCALE = 7;
+
+ /**
+ * The broadcast intent action to override the current country for testing purposes
+ *
+ * <p> This broadcast is not effective on user build.
+ *
+ * <p>Example: To override the current country <code>
+ * adb shell am broadcast -a com.android.internal.telephony.action.COUNTRY_OVERRIDE
+ * --es country us </code>
+ *
+ * <p> To remove the override <code>
+ * adb shell am broadcast -a com.android.internal.telephony.action.COUNTRY_OVERRIDE
+ * --ez reset true</code>
+ */
+ private static final String ACTION_COUNTRY_OVERRIDE =
+ "com.android.internal.telephony.action.COUNTRY_OVERRIDE";
+
+ /** The extra for country override */
+ private static final String EXTRA_COUNTRY = "country";
+
+ /** The extra for country override reset */
+ private static final String EXTRA_RESET = "reset";
+
// Todo: Read this from Settings.
/** The minimum delay to get cell info from the modem */
private static final long CELL_INFO_MIN_DELAY_MS = 2 * SECOND_IN_MILLIS;
@@ -120,6 +146,10 @@ public class LocaleTracker extends Handler {
@Nullable
private String mCurrentCountryIso;
+ /** The country override for testing purposes */
+ @Nullable
+ private String mCountryOverride;
+
/** Current service state. Must be one of ServiceState.STATE_XXX. */
private int mLastServiceState = ServiceState.STATE_POWER_OFF;
@@ -138,6 +168,13 @@ public class LocaleTracker extends Handler {
intent.getIntExtra(TelephonyManager.EXTRA_SIM_STATE,
TelephonyManager.SIM_STATE_UNKNOWN), 0).sendToTarget();
}
+ } else if (ACTION_COUNTRY_OVERRIDE.equals(intent.getAction())) {
+ String countryOverride = intent.getStringExtra(EXTRA_COUNTRY);
+ boolean reset = intent.getBooleanExtra(EXTRA_RESET, false);
+ if (reset) countryOverride = null;
+ log("Received country override: " + countryOverride);
+ // countryOverride null to reset the override.
+ obtainMessage(EVENT_OVERRIDE_LOCALE, countryOverride).sendToTarget();
}
}
};
@@ -181,6 +218,11 @@ public class LocaleTracker extends Handler {
updateTrackingStatus();
break;
+ case EVENT_OVERRIDE_LOCALE:
+ mCountryOverride = (String) msg.obj;
+ updateLocale();
+ break;
+
default:
throw new IllegalStateException("Unexpected message arrives. msg = " + msg.what);
}
@@ -201,12 +243,26 @@ public class LocaleTracker extends Handler {
final IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
+ if (Build.IS_DEBUGGABLE) {
+ filter.addAction(ACTION_COUNTRY_OVERRIDE);
+ }
mPhone.getContext().registerReceiver(mBroadcastReceiver, filter);
mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
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.
*
@@ -399,11 +455,16 @@ public class LocaleTracker extends Handler {
private synchronized void updateLocale() {
// If MCC is available from network service state, use it first.
String mcc = null;
- String countryIso = "";
+ String countryIso = getCarrierCountry();
+ boolean isBogusMcc = false;
+
if (!TextUtils.isEmpty(mOperatorNumeric)) {
try {
mcc = mOperatorNumeric.substring(0, 3);
countryIso = MccTable.countryCodeForMcc(mcc);
+ if (!TextUtils.isEmpty(mcc) && TextUtils.isEmpty(countryIso)) {
+ isBogusMcc = true;
+ }
} catch (StringIndexOutOfBoundsException ex) {
loge("updateLocale: Can't get country from operator numeric. mcc = "
+ mcc + ". ex=" + ex);
@@ -417,7 +478,13 @@ public class LocaleTracker extends Handler {
countryIso = MccTable.countryCodeForMcc(mcc);
}
- log("updateLocale: mcc = " + mcc + ", country = " + countryIso);
+ if (mCountryOverride != null) {
+ countryIso = mCountryOverride;
+ log("Override current country to " + mCountryOverride);
+ }
+
+ log("updateLocale: mcc = " + mcc + ", country = " + countryIso
+ + ", isBogusMcc = " + isBogusMcc);
boolean countryChanged = false;
if (!Objects.equals(countryIso, mCurrentCountryIso)) {
String msg = "updateLocale: Change the current country to \"" + countryIso
@@ -429,20 +496,6 @@ public class LocaleTracker extends Handler {
TelephonyManager.setTelephonyProperty(mPhone.getPhoneId(),
TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, mCurrentCountryIso);
- // Set the country code for wifi. This sets allowed wifi channels based on the
- // country of the carrier we see. If we can't see any, reset to 0 so we don't
- // broadcast on forbidden channels.
- WifiManager wifiManager = (WifiManager) mPhone.getContext()
- .getSystemService(Context.WIFI_SERVICE);
- if (wifiManager != null) {
- wifiManager.setCountryCode(countryIso);
- } else {
- msg = "Wifi manager is not available.";
- log(msg);
- mLocalLog.log(msg);
- }
-
-
Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
intent.putExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY, countryIso);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
@@ -451,7 +504,8 @@ public class LocaleTracker extends Handler {
countryChanged = true;
}
- if (TextUtils.isEmpty(countryIso)) {
+ // For bogus mcc, the countryIso is always empty, it should be marked as available.
+ if (TextUtils.isEmpty(countryIso) && !isBogusMcc) {
mNitzStateMachine.handleNetworkCountryCodeUnavailable();
} else {
mNitzStateMachine.handleNetworkCountryCodeSet(countryChanged);
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index 5ab29f3078..1ce360f527 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -27,6 +27,7 @@ import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_T
import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA;
import static android.telephony.TelephonyManager.EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE;
import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
+import static android.telephony.TelephonyManager.MODEM_COUNT_SINGLE_MODEM;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -35,6 +36,7 @@ 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.ParcelUuid;
@@ -78,6 +80,7 @@ public class MultiSimSettingController extends Handler {
private static final int EVENT_SUBSCRIPTION_GROUP_CHANGED = 5;
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;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"PRIMARY_SUB_"},
@@ -183,10 +186,13 @@ public class MultiSimSettingController extends Handler {
// Initialize mCarrierConfigLoadedSubIds and register to listen to carrier config change.
final int phoneCount = ((TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE)).getMaxPhoneCount();
+ Context.TELEPHONY_SERVICE)).getSupportedModemCount();
mCarrierConfigLoadedSubIds = new int[phoneCount];
Arrays.fill(mCarrierConfigLoadedSubIds, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ PhoneConfigurationManager.registerForMultiSimConfigChange(
+ this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
+
context.registerReceiver(mIntentReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
}
@@ -267,6 +273,9 @@ public class MultiSimSettingController extends Handler {
int subId = msg.arg2;
onCarrierConfigChanged(phoneId, subId);
break;
+ case EVENT_MULTI_SIM_CONFIG_CHANGED:
+ int activeModems = (int) ((AsyncResult) msg.obj).result;
+ onMultiSimConfigChanged(activeModems);
}
}
@@ -357,6 +366,14 @@ public class MultiSimSettingController extends Handler {
return true;
}
+ private void onMultiSimConfigChanged(int activeModems) {
+ // Clear mCarrierConfigLoadedSubIds. Other actions will responds to active
+ // subscription change.
+ for (int phoneId = activeModems; phoneId < mCarrierConfigLoadedSubIds.length; phoneId++) {
+ mCarrierConfigLoadedSubIds[phoneId] = INVALID_SUBSCRIPTION_ID;
+ }
+ }
+
/**
* Wait for subInfo initialization (after boot up) and carrier config load for all active
* subscriptions before re-evaluate multi SIM settings.
@@ -473,7 +490,9 @@ public class MultiSimSettingController extends Handler {
// Otherwise, if user just inserted their first SIM, or there's one primary and one
// opportunistic subscription active (activeSubInfos.size() > 1), we automatically
// set the primary to be default SIM and return.
- if (mPrimarySubList.size() == 1 && change != PRIMARY_SUB_REMOVED) {
+ if (mPrimarySubList.size() == 1 && (change != PRIMARY_SUB_REMOVED
+ || ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .getActiveModemCount() == MODEM_COUNT_SINGLE_MODEM)) {
int subId = mPrimarySubList.get(0);
if (DBG) log("[updateDefaultValues] to only primary sub " + subId);
mSubController.setDefaultDataSubId(subId);
diff --git a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
index 4b2b690e0d..1b28be4b74 100644
--- a/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
+++ b/src/java/com/android/internal/telephony/NetworkRegistrationManager.java
@@ -38,6 +38,7 @@ import android.telephony.INetworkServiceCallback;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import java.util.Hashtable;
@@ -100,6 +101,9 @@ public class NetworkRegistrationManager extends Handler {
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
phone.getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
intentFilter, null, null);
+ PhoneConfigurationManager.registerForMultiSimConfigChange(
+ this, EVENT_BIND_NETWORK_SERVICE, null);
+
sendEmptyMessage(EVENT_BIND_NETWORK_SERVICE);
}
@@ -112,7 +116,7 @@ public class NetworkRegistrationManager extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_BIND_NETWORK_SERVICE:
- bindService();
+ rebindService();
break;
default:
loge("Unhandled event " + msg.what);
@@ -231,15 +235,38 @@ public class NetworkRegistrationManager extends Handler {
}
}
- private void bindService() {
- Intent intent = null;
- String packageName = getPackageName();
- String className = getClassName();
+ private void unbindService() {
+ if (mINetworkService != null && mINetworkService.asBinder().isBinderAlive()) {
+ logd("unbinding service");
+ // Remove the network availability updater and then unbind the service.
+ try {
+ mINetworkService.removeNetworkServiceProvider(mPhone.getPhoneId());
+ } catch (RemoteException e) {
+ loge("Cannot remove data service provider. " + e);
+ }
+ }
+
+ if (mServiceConnection != null) {
+ mPhone.getContext().unbindService(mServiceConnection);
+ }
+ mINetworkService = null;
+ mServiceConnection = null;
+ mTargetBindingPackageName = null;
+ }
+
+ private void bindService(String packageName) {
+ if (mPhone == null || !SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())) {
+ loge("can't bindService with invalid phone or phoneId.");
+ return;
+ }
+
if (TextUtils.isEmpty(packageName)) {
loge("Can't find the binding package");
return;
}
+ Intent intent = null;
+ String className = getClassName();
if (TextUtils.isEmpty(className)) {
intent = new Intent(NetworkService.SERVICE_INTERFACE);
intent.setPackage(packageName);
@@ -248,22 +275,6 @@ public class NetworkRegistrationManager extends Handler {
intent = new Intent(NetworkService.SERVICE_INTERFACE).setComponent(cm);
}
- if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
- logd("Service " + packageName + " already bound or being bound.");
- return;
- }
-
- if (mINetworkService != null && mINetworkService.asBinder().isBinderAlive()) {
- // Remove the network availability updater and then unbind the service.
- try {
- mINetworkService.removeNetworkServiceProvider(mPhone.getPhoneId());
- } catch (RemoteException e) {
- loge("Cannot remove data service provider. " + e);
- }
-
- mPhone.getContext().unbindService(mServiceConnection);
- }
-
try {
// We bind this as a foreground service because it is operating directly on the SIM,
// and we do not want it subjected to power-savings restrictions while doing so.
@@ -281,6 +292,19 @@ public class NetworkRegistrationManager extends Handler {
}
}
+ private void rebindService() {
+ String packageName = getPackageName();
+ // Do nothing if no need to rebind.
+ if (SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())
+ && TextUtils.equals(packageName, mTargetBindingPackageName)) {
+ logd("Service " + packageName + " already bound or being bound.");
+ return;
+ }
+
+ unbindService();
+ bindService(packageName);
+ }
+
private String getPackageName() {
String packageName;
int resourceId;
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index ba1081b2fe..c848358a55 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -525,12 +525,12 @@ public final class NetworkScanRequestTracker {
// stopped, a new scan will automatically start with nsri.
// The new scan can interrupt the live scan only when all the below requirements are met:
// 1. There is 1 live scan and no other pending scan
- // 2. The new scan is requested by mobile network setting menu (owned by PHONE process)
+ // 2. The new scan is requested by mobile network setting menu (owned by SYSTEM process)
// 3. The live scan is not requested by mobile network setting menu
private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
if (mLiveRequestInfo != null && mPendingRequestInfo == null
- && nsri.mUid == Process.PHONE_UID
- && mLiveRequestInfo.mUid != Process.PHONE_UID) {
+ && nsri.mUid == Process.SYSTEM_UID
+ && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
doInterruptScan(mLiveRequestInfo.mScanId);
mPendingRequestInfo = nsri;
notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
diff --git a/src/java/com/android/internal/telephony/NitzStateMachine.java b/src/java/com/android/internal/telephony/NitzStateMachine.java
index c6965289b4..1ab306e627 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachine.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachine.java
@@ -165,7 +165,7 @@ public interface NitzStateMachine {
@Override
@Nullable
public String getNetworkCountryIsoForPhone() {
- return mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
+ return mTelephonyManager.getNetworkCountryIso(mPhone.getPhoneId());
}
@Override
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index c39cce9f12..fd22e7717d 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -28,7 +28,6 @@ import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.net.Uri;
-import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Build;
import android.os.Handler;
@@ -41,15 +40,15 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.WorkSource;
import android.preference.PreferenceManager;
-import android.provider.Settings;
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.CellInfo;
import android.telephony.CellLocation;
import android.telephony.ClientRequestStats;
-import android.telephony.DataFailCause;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.PhoneStateListener;
import android.telephony.PhysicalChannelConfig;
@@ -60,8 +59,8 @@ import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -96,6 +95,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
/**
* (<em>Not for SDK use</em>)
@@ -380,6 +380,8 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
private final RegistrantList mCellInfoRegistrants = new RegistrantList();
+ private final RegistrantList mRedialRegistrants = new RegistrantList();
+
protected Registrant mPostDialHandler;
protected final LocalLog mLocalLog;
@@ -565,23 +567,6 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
return;
}
- // 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 = getLocaleFromCarrierProperties(mContext);
- if (carrierLocale != null && !TextUtils.isEmpty(carrierLocale.getCountry())) {
- final String country = carrierLocale.getCountry();
- try {
- Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_COUNTRY_CODE);
- } catch (Settings.SettingNotFoundException e) {
- // note this is not persisting
- WifiManager wM = (WifiManager)
- mContext.getSystemService(Context.WIFI_SERVICE);
- wM.setCountryCode(country);
- }
- }
-
// Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
mTelephonyComponentFactory = telephonyComponentFactory;
mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName())
@@ -596,7 +581,6 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) {
mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
}
- mCi.setOnUnsolOemHookRaw(this, EVENT_UNSOL_OEM_HOOK_RAW, null);
mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
obtainMessage(EVENT_CONFIG_LCE));
}
@@ -659,6 +643,24 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
/**
+ * Check if sending CLIR activation("*31#") and deactivation("#31#") code only without dialing
+ * number is prevented.
+ *
+ * @return {@code true} when carrier config
+ * "KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL" is set to {@code true}
+ */
+ public boolean isClirActivationAndDeactivationPrevented() {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle b = configManager.getConfigForSubId(getSubId());
+ if (b == null) {
+ b = CarrierConfigManager.getDefaultConfig();
+ }
+ return b.getBoolean(
+ CarrierConfigManager.KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL);
+ }
+
+ /**
* When overridden the derived class needs to call
* super.handleMessage(msg) so this method has a
* a chance to process the message.
@@ -715,9 +717,17 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
String dialString = (String) ar.result;
if (TextUtils.isEmpty(dialString)) return;
try {
- dialInternal(dialString, new DialArgs.Builder().build());
+ 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);
+ }
} catch (CallStateException e) {
Rlog.e(LOG_TAG, "silent redial failed: " + e);
+ if (mImsPhone != null) {
+ mImsPhone.notifyRedialConnectionChanged(null);
+ }
}
}
break;
@@ -912,6 +922,30 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
mHandoverRegistrants.notifyRegistrants(ar);
}
+ /**
+ * Notifies when a Handover happens due to Silent Redial
+ */
+ public void registerForRedialConnectionChanged(Handler h, int what, Object obj) {
+ checkCorrectThread(h);
+ mRedialRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for redial connection notifications
+ */
+ public void unregisterForRedialConnectionChanged(Handler h) {
+ mRedialRegistrants.remove(h);
+ }
+
+ /**
+ * Subclasses of Phone probably want to replace this with a
+ * version scoped to their packages
+ */
+ public void notifyRedialConnectionChanged(Connection cn) {
+ AsyncResult ar = new AsyncResult(null, cn, null);
+ mRedialRegistrants.notifyRegistrants(ar);
+ }
+
protected void setIsInEmergencyCall() {
}
@@ -983,6 +1017,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
migrate(mUnknownConnectionRegistrants, from.mUnknownConnectionRegistrants);
migrate(mSuppServiceFailedRegistrants, from.mSuppServiceFailedRegistrants);
migrate(mCellInfoRegistrants, from.mCellInfoRegistrants);
+ migrate(mRedialRegistrants, from.mRedialRegistrants);
// The emergency state of IMS phone will be cleared in ImsPhone#notifySrvccState after
// receive SRVCC completed
if (from.isInEmergencyCall()) {
@@ -1681,14 +1716,15 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
* Set the properties by matching the carrier string in
* a string-array resource
*/
- private static Locale getLocaleFromCarrierProperties(Context ctx) {
+ @Nullable Locale getLocaleFromCarrierProperties() {
String carrier = SystemProperties.get("ro.carrier");
if (null == carrier || 0 == carrier.length() || "unknown".equals(carrier)) {
return null;
}
- CharSequence[] carrierLocales = ctx.getResources().getTextArray(R.array.carrier_properties);
+ CharSequence[] carrierLocales = mContext.getResources().getTextArray(
+ R.array.carrier_properties);
for (int i = 0; i < carrierLocales.length; i+=3) {
String c = carrierLocales[i].toString();
@@ -3263,7 +3299,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
}
public void notifyPreciseDataConnectionFailed(String apnType, String apn,
- @DataFailCause.FailCause int failCause) {
+ @DataFailureCause int failCause) {
mNotifier.notifyPreciseDataConnectionFailed(this, apnType, apn, failCause);
}
@@ -3620,6 +3656,31 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
return regTech;
}
+ /**
+ * Get the IMS MmTel Registration technology for this Phone, defined in
+ * {@link ImsRegistrationImplBase}.
+ */
+ public void getImsRegistrationTech(Consumer<Integer> callback) {
+ Phone imsPhone = mImsPhone;
+ if (imsPhone != null) {
+ imsPhone.getImsRegistrationTech(callback);
+ } else {
+ callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ }
+ }
+
+ /**
+ * Asynchronously get the IMS MmTel Registration state for this Phone.
+ */
+ public void getImsRegistrationState(Consumer<Integer> callback) {
+ Phone imsPhone = mImsPhone;
+ if (imsPhone != null) {
+ imsPhone.getImsRegistrationState(callback);
+ }
+ callback.accept(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
+ }
+
+
private boolean getRoamingOverrideHelper(String prefix, String key) {
String iccId = getIccSerialNumber();
if (TextUtils.isEmpty(iccId) || TextUtils.isEmpty(key)) {
@@ -3865,7 +3926,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
return new Locale(records.getSimLanguage());
}
- return getLocaleFromCarrierProperties(mContext);
+ return getLocaleFromCarrierProperties();
}
public void updateDataConnectionTracker() {
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index cae6dc40d4..df73ef84d2 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -16,11 +16,16 @@
package com.android.internal.telephony;
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
+import static android.telephony.TelephonyManager.EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED;
+
import android.content.Context;
+import android.content.Intent;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RegistrantList;
import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.telephony.PhoneCapability;
@@ -28,6 +33,8 @@ import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -53,9 +60,12 @@ public class PhoneConfigurationManager {
private final Context mContext;
private PhoneCapability mStaticCapability;
private final RadioConfig mRadioConfig;
- private final MainThreadHandler mHandler;
+ private final Handler mHandler;
private final Phone[] mPhones;
private final Map<Integer, Boolean> mPhoneStatusMap;
+ private MockableInterface mMi = new MockableInterface();
+ private TelephonyManager mTelephonyManager;
+ private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList();
/**
* Init method to instantiate the object
@@ -79,11 +89,11 @@ public class PhoneConfigurationManager {
private PhoneConfigurationManager(Context context) {
mContext = context;
// TODO: send commands to modem once interface is ready.
- TelephonyManager telephonyManager = new TelephonyManager(context);
+ 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);
- mHandler = new MainThreadHandler();
+ mHandler = new ConfigManagerHandler();
mPhoneStatusMap = new HashMap<>();
notifyCapabilityChanged();
@@ -135,7 +145,7 @@ public class PhoneConfigurationManager {
/**
* Handler class to handle callbacks
*/
- private final class MainThreadHandler extends Handler {
+ private final class ConfigManagerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
AsyncResult ar;
@@ -159,7 +169,7 @@ public class PhoneConfigurationManager {
ar = (AsyncResult) msg.obj;
if (ar != null && ar.exception == null) {
int numOfLiveModems = msg.arg1;
- setMultiSimProperties(numOfLiveModems);
+ onMultiSimConfigChanged(numOfLiveModems);
} else {
log(msg.what + " failure. Not switching multi-sim config." + ar.exception);
}
@@ -169,7 +179,7 @@ public class PhoneConfigurationManager {
if (ar != null && ar.exception == null) {
int phoneId = msg.arg1;
boolean enabled = (boolean) ar.result;
- //update the cache each time getModemStatus is requested
+ // update the cache each time getModemStatus is requested
addToPhoneStatusCache(phoneId, enabled);
} else {
log(msg.what + " failure. Not updating modem status." + ar.exception);
@@ -283,8 +293,7 @@ public class PhoneConfigurationManager {
* Returns how many phone objects the device supports.
*/
public int getPhoneCount() {
- TelephonyManager tm = new TelephonyManager(mContext);
- return tm.getPhoneCount();
+ return mTelephonyManager.getActiveModemCount();
}
/**
@@ -297,6 +306,7 @@ public class PhoneConfigurationManager {
mHandler, EVENT_GET_PHONE_CAPABILITY_DONE);
mRadioConfig.getPhoneCapability(callback);
}
+ log("getStaticPhoneCapability: mStaticCapability " + mStaticCapability);
return mStaticCapability;
}
@@ -312,7 +322,7 @@ public class PhoneConfigurationManager {
}
private void notifyCapabilityChanged() {
- PhoneNotifier notifier = new DefaultPhoneNotifier();
+ PhoneNotifier notifier = new DefaultPhoneNotifier(mContext);
notifier.notifyPhoneCapabilityChanged(mStaticCapability);
}
@@ -344,40 +354,19 @@ public class PhoneConfigurationManager {
* Return value defaults to true
*/
public boolean isRebootRequiredForModemConfigChange() {
- String rebootRequired = SystemProperties.get(
- TelephonyProperties.PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE);
- log("isRebootRequiredForModemConfigChange: isRebootRequired = " + rebootRequired);
- return !rebootRequired.equals("false");
+ return mMi.isRebootRequiredForModemConfigChange();
}
- /**
- * Helper method to set system properties for setting multi sim configs,
- * as well as doing the phone reboot
- * NOTE: In order to support more than 3 sims, we need to change this method.
- * @param numOfSims number of active sims
- */
- private void setMultiSimProperties(int numOfSims) {
- String finalMultiSimConfig;
- switch(numOfSims) {
- case 3:
- finalMultiSimConfig = TSTS;
- break;
- case 2:
- finalMultiSimConfig = DSDS;
- break;
- default:
- finalMultiSimConfig = SSSS;
- }
+ private void onMultiSimConfigChanged(int numOfActiveModems) {
+ setMultiSimProperties(numOfActiveModems);
- SystemProperties.set(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG, finalMultiSimConfig);
if (isRebootRequiredForModemConfigChange()) {
- log("setMultiSimProperties: Rebooting due to switching multi-sim config to "
- + finalMultiSimConfig);
+ log("onMultiSimConfigChanged: Rebooting.");
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- pm.reboot("Switching to " + finalMultiSimConfig);
+ pm.reboot("Multi-SIM config changed.");
} else {
- log("setMultiSimProperties: Rebooting is not required to switch multi-sim config to "
- + finalMultiSimConfig);
+ log("onMultiSimConfigChanged: Rebooting is not required.");
+ broadcastMultiSimConfigChange(numOfActiveModems);
// Register to RIL service if needed.
for (int i = 0; i < mPhones.length; i++) {
Phone phone = mPhones[i];
@@ -386,6 +375,84 @@ public class PhoneConfigurationManager {
}
}
+ /**
+ * Helper method to set system properties for setting multi sim configs,
+ * as well as doing the phone reboot
+ * NOTE: In order to support more than 3 sims, we need to change this method.
+ * @param numOfActiveModems number of active sims
+ */
+ private void setMultiSimProperties(int numOfActiveModems) {
+ mMi.setMultiSimProperties(numOfActiveModems);
+ }
+
+ @VisibleForTesting
+ public static void notifyMultiSimConfigChange(int numOfActiveModems) {
+ sMultiSimConfigChangeRegistrants.notifyResult(numOfActiveModems);
+ }
+
+ /**
+ * Register for multi-SIM configuration change, for example if the devices switched from single
+ * SIM to dual-SIM mode.
+ *
+ * It doesn't trigger callback upon registration as multi-SIM config change is in-frequent.
+ */
+ public static void registerForMultiSimConfigChange(Handler h, int what, Object obj) {
+ sMultiSimConfigChangeRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregister for multi-SIM configuration change.
+ */
+ public static void unregisterForMultiSimConfigChange(Handler h) {
+ sMultiSimConfigChangeRegistrants.remove(h);
+ }
+
+ private void broadcastMultiSimConfigChange(int numOfActiveModems) {
+ log("broadcastSimSlotNumChange numOfActiveModems" + numOfActiveModems);
+ // Notify internal registrants first.
+ notifyMultiSimConfigChange(numOfActiveModems);
+
+ Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
+ intent.putExtra(EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED, numOfActiveModems);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * A wrapper class that wraps some methods so that they can be replaced or mocked in unit-tests.
+ *
+ * For example, setting or reading system property are static native methods that can't be
+ * directly mocked. We can mock it by replacing MockableInterface object with a mock instance
+ * in unittest.
+ */
+ @VisibleForTesting
+ public static class MockableInterface {
+ @VisibleForTesting
+ public boolean isRebootRequiredForModemConfigChange() {
+ String rebootRequired = SystemProperties.get(
+ TelephonyProperties.PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE);
+ log("isRebootRequiredForModemConfigChange: isRebootRequired = " + rebootRequired);
+ return !rebootRequired.equals("false");
+ }
+
+ @VisibleForTesting
+ public void setMultiSimProperties(int numOfActiveModems) {
+ String multiSimConfig;
+ switch(numOfActiveModems) {
+ case 3:
+ multiSimConfig = TSTS;
+ break;
+ case 2:
+ multiSimConfig = DSDS;
+ break;
+ default:
+ multiSimConfig = SSSS;
+ }
+
+ log("setMultiSimProperties to " + multiSimConfig);
+ SystemProperties.set(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG, multiSimConfig);
+ }
+ }
+
private static void log(String s) {
Rlog.d(LOG_TAG, s);
}
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index b6fdffe288..b92c68babc 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -138,7 +138,7 @@ public class PhoneFactory {
}
}
- sPhoneNotifier = new DefaultPhoneNotifier();
+ sPhoneNotifier = new DefaultPhoneNotifier(context);
int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
@@ -146,7 +146,7 @@ public class PhoneFactory {
/* In case of multi SIM mode two instances of Phone, RIL are created,
where as in single SIM mode only instance. isMultiSimEnabled() function checks
whether it is single SIM or multi SIM mode */
- int numPhones = TelephonyManager.getDefault().getMaxPhoneCount();
+ int numPhones = TelephonyManager.getDefault().getSupportedModemCount();
int[] networkModes = new int[numPhones];
sPhones = new Phone[numPhones];
@@ -218,7 +218,7 @@ public class PhoneFactory {
Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
sSubInfoRecordUpdater = new SubscriptionInfoUpdater(
- BackgroundThread.get().getLooper(), context, sPhones, sCommandsInterfaces);
+ BackgroundThread.get().getLooper(), context, sCommandsInterfaces);
sc.updatePhonesAvailability(sPhones);
// Only bring up IMS if the device supports having an IMS stack.
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 9009d30331..31cd71fae8 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -17,13 +17,14 @@
package com.android.internal.telephony;
import android.annotation.UnsupportedAppUsage;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SrvccState;
import android.telephony.CallQuality;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
-import android.telephony.DataFailCause;
import android.telephony.PhoneCapability;
import android.telephony.PhysicalChannelConfig;
-import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
@@ -69,10 +70,10 @@ public interface PhoneNotifier {
void notifyImsDisconnectCause(Phone sender, ImsReasonInfo imsReasonInfo);
public void notifyPreciseDataConnectionFailed(Phone sender, String apnType, String apn,
- @DataFailCause.FailCause int failCause);
+ @DataFailureCause int failCause);
/** send a notification that the SRVCC state has changed.*/
- void notifySrvccStateChanged(Phone sender, @TelephonyManager.SrvccState int state);
+ void notifySrvccStateChanged(Phone sender, @SrvccState int state);
public void notifyVoiceActivationStateChanged(Phone sender, int activationState);
@@ -84,7 +85,7 @@ public interface PhoneNotifier {
public void notifyPhoneCapabilityChanged(PhoneCapability capability);
- void notifyRadioPowerStateChanged(Phone sender, @TelephonyManager.RadioPowerState int state);
+ void notifyRadioPowerStateChanged(Phone sender, @RadioPowerState int state);
/** Notify of change to EmergencyNumberList. */
void notifyEmergencyNumberList(Phone sender);
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index bf8fd7a47b..cf64a8bb6f 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG;
import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
import static android.telephony.SubscriptionManager.INVALID_PHONE_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -41,10 +42,12 @@ import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
import android.telephony.Rlog;
@@ -1167,8 +1170,16 @@ public class PhoneSwitcher extends Handler {
// start validation on the subscription first.
if (mValidator.isValidationFeatureSupported() && needValidation) {
mSetOpptSubCallback = callback;
- mValidator.validate(subIdToValidate, DEFAULT_VALIDATION_EXPIRATION_TIME,
- false, mValidationCallback);
+ long validationTimeout = DEFAULT_VALIDATION_EXPIRATION_TIME;
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle b = configManager.getConfigForSubId(subIdToValidate);
+ if (b != null) {
+ validationTimeout = b.getLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG);
+ }
+ }
+ mValidator.validate(subIdToValidate, validationTimeout, false, mValidationCallback);
} else {
setOpportunisticSubscriptionInternal(subId);
sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index fd7bc248ae..54fcac95b9 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -26,6 +26,7 @@ import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -151,7 +152,7 @@ public class ProxyController {
public void registerForAllDataDisconnected(int subId, Handler h, int what) {
int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
- if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getMaxPhoneCount()) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
mPhones[phoneId].registerForAllDataDisconnected(h, what);
}
}
@@ -159,7 +160,7 @@ public class ProxyController {
public void unregisterForAllDataDisconnected(int subId, Handler h) {
int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
- if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getMaxPhoneCount()) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
mPhones[phoneId].unregisterForAllDataDisconnected(h);
}
}
@@ -168,7 +169,7 @@ public class ProxyController {
public boolean areAllDataDisconnected(int subId) {
int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
- if (phoneId >= 0 && phoneId < TelephonyManager.getDefault().getMaxPhoneCount()) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
return mPhones[phoneId].areAllDataDisconnected();
} else {
// if we can't find a phone for the given subId, it is disconnected.
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index a47e802cf1..4d7a6879dd 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -393,7 +393,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
}
}
- private void resetProxyAndRequestList() {
+ private synchronized void resetProxyAndRequestList() {
mRadioProxy = null;
mOemHookProxy = null;
@@ -2641,7 +2641,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
/**
* convert RAF from {@link android.hardware.radio.V1_0.RadioAccessFamily} to
* {@link TelephonyManager.NetworkTypeBitMask}, the bitmask represented by
- * {@link TelephonyManager.NetworkType}.
+ * {@link android.telephony.Annotation.NetworkType}.
*
* @param raf {@link android.hardware.radio.V1_0.RadioAccessFamily}
* @return {@link TelephonyManager.NetworkTypeBitMask}
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index 6beab09c21..5d00690c1d 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -87,6 +87,7 @@ import android.hardware.radio.V1_4.IRadioIndication;
import android.hardware.radio.V1_4.RadioFrequencyInfo.hidl_discriminator;
import android.os.AsyncResult;
import android.os.SystemProperties;
+import android.telephony.Annotation.RadioPowerState;
import android.telephony.CellInfo;
import android.telephony.PcoData;
import android.telephony.PhysicalChannelConfig;
@@ -962,9 +963,9 @@ public class RadioIndication extends IRadioIndication.Stub {
/**
* @param stateInt
- * @return {@link TelephonyManager.RadioPowerState RadioPowerState}
+ * @return {@link RadioPowerState RadioPowerState}
*/
- private @TelephonyManager.RadioPowerState int getRadioStateFromInt(int stateInt) {
+ private @RadioPowerState int getRadioStateFromInt(int stateInt) {
int state;
switch(stateInt) {
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index 07d36fe134..7b45c537e3 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -23,11 +23,11 @@ import android.content.IntentFilter;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.NetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.Rlog;
import android.telephony.ServiceState;
-import android.telephony.TelephonyManager.NetworkType;
import android.util.SparseArray;
import android.util.SparseIntArray;
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 28f23e39f4..0f3d8aae3f 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -17,7 +17,6 @@
package com.android.internal.telephony;
import static android.Manifest.permission.SEND_SMS_NO_CONFIRMATION;
-import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
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;
@@ -56,16 +55,14 @@ import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.Process;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Telephony;
import android.provider.Telephony.Sms;
import android.service.carrier.CarrierMessagingService;
-import android.service.carrier.ICarrierMessagingCallback;
-import android.service.carrier.ICarrierMessagingService;
+import android.service.carrier.CarrierMessagingServiceWrapper;
+import android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper;
import android.telephony.CarrierConfigManager;
-import android.telephony.CarrierMessagingServiceManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.ServiceState;
@@ -342,8 +339,8 @@ public abstract class SMSDispatcher extends Handler {
Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+ "sending LIMIT_EXCEEDED error code.");
} else {
- error = RESULT_ERROR_GENERIC_FAILURE;
- 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);
@@ -363,7 +360,7 @@ public abstract class SMSDispatcher extends Handler {
/**
* Use the carrier messaging service to send a data or text SMS.
*/
- protected abstract class SmsSender extends CarrierMessagingServiceManager {
+ protected abstract class SmsSender extends CarrierMessagingServiceWrapper {
protected final SmsTracker mTracker;
// Initialized in sendSmsByCarrierApp
protected volatile SmsSenderCallback mSenderCallback;
@@ -402,16 +399,16 @@ public abstract class SMSDispatcher extends Handler {
}
@Override
- protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
+ public void onServiceReady() {
HashMap<String, Object> map = mTracker.getData();
String text = (String) map.get(MAP_KEY_TEXT);
if (text != null) {
try {
- carrierMessagingService.sendTextSms(text, getSubId(),
+ sendTextSms(text, getSubId(),
mTracker.mDestAddress, getSendSmsFlag(mTracker.mDeliveryIntent),
mSenderCallback);
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
Rlog.e(TAG, "Exception sending the SMS: " + e);
mSenderCallback.onSendSmsComplete(
CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
@@ -434,17 +431,17 @@ public abstract class SMSDispatcher extends Handler {
}
@Override
- protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
+ public void 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 {
- carrierMessagingService.sendDataSms(data, getSubId(),
+ sendDataSms(data, getSubId(),
mTracker.mDestAddress, destPort,
getSendSmsFlag(mTracker.mDeliveryIntent), mSenderCallback);
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
Rlog.e(TAG, "Exception sending the SMS: " + e);
mSenderCallback.onSendSmsComplete(
CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
@@ -462,7 +459,8 @@ public abstract class SMSDispatcher extends Handler {
* 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 ICarrierMessagingCallback.Stub {
+ protected final class SmsSenderCallback extends
+ CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper {
private final SmsSender mSmsSender;
public SmsSenderCallback(SmsSender smsSender) {
@@ -541,7 +539,7 @@ public abstract class SMSDispatcher extends Handler {
/**
* Use the carrier messaging service to send a multipart text SMS.
*/
- private final class MultipartSmsSender extends CarrierMessagingServiceManager {
+ private final class MultipartSmsSender extends CarrierMessagingServiceWrapper {
private final List<String> mParts;
public final SmsTracker[] mTrackers;
// Initialized in sendSmsByCarrierApp
@@ -567,12 +565,12 @@ public abstract class SMSDispatcher extends Handler {
}
@Override
- protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
+ public void onServiceReady() {
try {
- carrierMessagingService.sendMultipartTextSms(
+ sendMultipartTextSms(
mParts, getSubId(), mTrackers[0].mDestAddress,
getSendSmsFlag(mTrackers[0].mDeliveryIntent), mSenderCallback);
- } catch (RemoteException e) {
+ } catch (RuntimeException e) {
Rlog.e(TAG, "Exception sending the SMS: " + e);
mSenderCallback.onSendMultipartSmsComplete(
CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK,
@@ -585,7 +583,7 @@ public abstract class SMSDispatcher extends Handler {
* 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 ICarrierMessagingCallback.Stub {
+ private final class MultipartSmsSenderCallback extends CarrierMessagingCallbackWrapper {
private final MultipartSmsSender mSmsSender;
MultipartSmsSenderCallback(MultipartSmsSender smsSender) {
@@ -650,7 +648,8 @@ public abstract class SMSDispatcher extends Handler {
private void sendSubmitPdu(SmsTracker[] trackers) {
if (shouldBlockSmsForEcbm()) {
Rlog.d(TAG, "Block SMS in Emergency Callback mode");
- handleSmsTrackersFailure(trackers, RESULT_ERROR_NO_SERVICE, NO_ERROR_CODE);
+ handleSmsTrackersFailure(trackers, SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY,
+ NO_ERROR_CODE);
} else {
sendRawPdu(trackers);
}
@@ -730,16 +729,62 @@ public abstract class SMSDispatcher extends Handler {
if (ar.result != null) {
errorCode = ((SmsResponse)ar.result).mErrorCode;
}
- int error = RESULT_ERROR_GENERIC_FAILURE;
- if (((CommandException)(ar.exception)).getCommandError()
- == CommandException.Error.FDN_CHECK_FAILURE) {
- error = RESULT_ERROR_FDN_CHECK_FAILURE;
- }
+ int error = rilErrorToSmsManagerResult(((CommandException) (ar.exception))
+ .getCommandError());
tracker.onFailed(mContext, error, errorCode);
}
}
}
+ private static int rilErrorToSmsManagerResult(CommandException.Error rilError) {
+ switch (rilError) {
+ case RADIO_NOT_AVAILABLE:
+ return SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE;
+ case SMS_FAIL_RETRY:
+ return SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY;
+ case NETWORK_REJECT:
+ return SmsManager.RESULT_RIL_NETWORK_REJECT;
+ case INVALID_STATE:
+ return SmsManager.RESULT_RIL_INVALID_STATE;
+ case INVALID_ARGUMENTS:
+ return SmsManager.RESULT_RIL_INVALID_ARGUMENTS;
+ case NO_MEMORY:
+ return SmsManager.RESULT_RIL_NO_MEMORY;
+ case REQUEST_RATE_LIMITED:
+ return SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED;
+ case INVALID_SMS_FORMAT:
+ return SmsManager.RESULT_RIL_INVALID_SMS_FORMAT;
+ case SYSTEM_ERR:
+ return SmsManager.RESULT_RIL_SYSTEM_ERR;
+ case ENCODING_ERR:
+ return SmsManager.RESULT_RIL_ENCODING_ERR;
+ case MODEM_ERR:
+ return SmsManager.RESULT_RIL_MODEM_ERR;
+ case NETWORK_ERR:
+ return SmsManager.RESULT_RIL_NETWORK_ERR;
+ case INTERNAL_ERR:
+ return SmsManager.RESULT_RIL_INTERNAL_ERR;
+ case REQUEST_NOT_SUPPORTED:
+ return SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED;
+ case INVALID_MODEM_STATE:
+ return SmsManager.RESULT_RIL_INVALID_MODEM_STATE;
+ case NETWORK_NOT_READY:
+ return SmsManager.RESULT_RIL_NETWORK_NOT_READY;
+ case OPERATION_NOT_ALLOWED:
+ return SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED;
+ case NO_RESOURCES:
+ return SmsManager.RESULT_RIL_NO_RESOURCES;
+ case REQUEST_CANCELLED:
+ return SmsManager.RESULT_RIL_CANCELLED;
+ case SIM_ABSENT:
+ return SmsManager.RESULT_RIL_SIM_ABSENT;
+ case FDN_CHECK_FAILURE:
+ return SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
+ default:
+ return RESULT_ERROR_GENERIC_FAILURE;
+ }
+ }
+
/**
* Handles outbound message when the phone is not in service.
*
@@ -1241,7 +1286,7 @@ public abstract class SMSDispatcher extends Handler {
if (simCountryIso == null || simCountryIso.length() != 2) {
Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso");
simCountryIso =
- mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
+ mTelephonyManager.getNetworkCountryIso(mPhone.getPhoneId());
}
smsCategory =
@@ -1251,7 +1296,7 @@ public abstract class SMSDispatcher extends Handler {
}
if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) {
String networkCountryIso =
- mTelephonyManager.getNetworkCountryIsoForPhone(mPhone.getPhoneId());
+ mTelephonyManager.getNetworkCountryIso(mPhone.getPhoneId());
if (networkCountryIso == null || networkCountryIso.length() != 2) {
Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso");
networkCountryIso =
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 32b0cff5fe..561a051061 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -1570,10 +1570,11 @@ public class ServiceStateTracker extends Handler {
mNrStateChangedRegistrants.notifyRegistrants();
hasChanged = true;
}
+ hasChanged |= RatRatcheter
+ .updateBandwidths(getBandwidthsFromConfigs(list), mSS);
// Notify NR frequency, NR connection status or bandwidths changed.
- if (hasChanged
- || RatRatcheter.updateBandwidths(getBandwidthsFromConfigs(list), mSS)) {
+ if (hasChanged) {
mPhone.notifyServiceStateChanged(mSS);
}
}
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 8292fb0154..0fce4d2d42 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -555,6 +555,31 @@ public class SmsController extends ISmsImplBase {
* Triggered by `adb shell dumpsys isms`
*/
@Override
+ public String getSmscAddressFromIccEfForSubscriber(int subId, String callingPackage) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ return iccSmsIntMgr.getSmscAddressFromIccEf(callingPackage);
+ } else {
+ Rlog.e(LOG_TAG, "getSmscAddressFromIccEfForSubscriber iccSmsIntMgr is null"
+ + " for Subscription: " + subId);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean setSmscAddressOnIccEfForSubscriber(
+ String smsc, int subId, String callingPackage) {
+ IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+ if (iccSmsIntMgr != null) {
+ return iccSmsIntMgr.setSmscAddressOnIccEf(callingPackage, smsc);
+ } else {
+ Rlog.e(LOG_TAG, "setSmscAddressOnIccEfForSubscriber iccSmsIntMgr is null"
+ + " for Subscription: " + subId);
+ return false;
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!checkDumpPermission(mContext, LOG_TAG, pw)) {
return;
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index c75b478594..6b7c8a34e3 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -462,7 +462,7 @@ public class SmsDispatchersController extends Handler {
|| (map.containsKey("data") && map.containsKey("destPort"))))) {
// should never come here...
Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
- tracker.onFailed(mContext, SmsManager.RESULT_ERROR_GENERIC_FAILURE, NO_ERROR_CODE);
+ tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
return;
}
String scAddr = (String) map.get("scAddr");
diff --git a/src/java/com/android/internal/telephony/SmsNumberUtils.java b/src/java/com/android/internal/telephony/SmsNumberUtils.java
deleted file mode 100644
index af7d871716..0000000000
--- a/src/java/com/android/internal/telephony/SmsNumberUtils.java
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * Copyright (C) 2014 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.Context;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.os.Binder;
-import android.os.Build;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.HbpcdLookup.MccIdd;
-import com.android.internal.telephony.HbpcdLookup.MccLookup;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-
- /**
- * This class implements handle the MO SMS target address before sending.
- * This is special for VZW requirement. Follow the specifications of assisted dialing
- * of MO SMS while traveling on VZW CDMA, international CDMA or GSM markets.
- * {@hide}
- */
-public class SmsNumberUtils {
- private static final String TAG = "SmsNumberUtils";
- private static final boolean DBG = Build.IS_DEBUGGABLE;
-
- private static final String PLUS_SIGN = "+";
-
- private static final int NANP_SHORT_LENGTH = 7;
- private static final int NANP_MEDIUM_LENGTH = 10;
- private static final int NANP_LONG_LENGTH = 11;
-
- private static final int NANP_CC = 1;
- private static final String NANP_NDD = "1";
- private static final String NANP_IDD = "011";
-
- private static final int MIN_COUNTRY_AREA_LOCAL_LENGTH = 10;
-
- private static final int GSM_UMTS_NETWORK = 0;
- private static final int CDMA_HOME_NETWORK = 1;
- private static final int CDMA_ROAMING_NETWORK = 2;
-
- private static final int NP_NONE = 0;
- private static final int NP_NANP_BEGIN = 1;
-
- /* <Phone Number>, <NXX>-<XXXX> N[2-9] */
- private static final int NP_NANP_LOCAL = NP_NANP_BEGIN;
-
- /* <Area_code>-<Phone Number>, <NXX>-<NXX>-<XXXX> N[2-9] */
- private static final int NP_NANP_AREA_LOCAL = NP_NANP_BEGIN + 1;
-
- /* <1>-<Area_code>-<Phone Number>, 1-<NXX>-<NXX>-<XXXX> N[2-9] */
- private static final int NP_NANP_NDD_AREA_LOCAL = NP_NANP_BEGIN + 2;
-
- /* <+><U.S.Country_code><Area_code><Phone Number>, +1-<NXX>-<NXX>-<XXXX> N[2-9] */
- private static final int NP_NANP_NBPCD_CC_AREA_LOCAL = NP_NANP_BEGIN + 3;
-
- /* <Local_IDD><Country_code><Area_code><Phone Number>, 001-1-<NXX>-<NXX>-<XXXX> N[2-9] */
- private static final int NP_NANP_LOCALIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 4;
-
- /* <+><Home_IDD><Country_code><Area_code><Phone Number>, +011-1-<NXX>-<NXX>-<XXXX> N[2-9] */
- private static final int NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 5;
-
- private static final int NP_INTERNATIONAL_BEGIN = 100;
- /* <+>-<Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, +011-86-25-86281234 */
- private static final int NP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN;
-
- /* <Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, 011-86-25-86281234 */
- private static final int NP_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 1;
-
- /* <NBPCD>-<Country_code>-<Area_code>-<Phone Number>, +1-86-25-86281234 */
- private static final int NP_NBPCD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 2;
-
- /* <Local_IDD>-<Country_code>-<Area_code>-<Phone Number>, 00-86-25-86281234 */
- private static final int NP_LOCALIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 3;
-
- /* <Country_code>-<Area_code>-<Phone Number>, 86-25-86281234*/
- private static final int NP_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 4;
-
- private static int[] ALL_COUNTRY_CODES = null;
- private static int MAX_COUNTRY_CODES_LENGTH;
- private static HashMap<String, ArrayList<String>> IDDS_MAPS =
- new HashMap<String, ArrayList<String>>();
-
- private static class NumberEntry {
- public String number;
- public String IDD;
- public int countryCode;
- public NumberEntry(String number) {
- this.number = number;
- }
- }
-
- /* Breaks the given number down and formats it according to the rules
- * for different number plans and different network.
- *
- * @param number destination number which need to be format
- * @param activeMcc current network's mcc
- * @param networkType current network type
- *
- * @return the number after formatting.
- */
- private static String formatNumber(Context context, String number,
- String activeMcc,
- int networkType) {
- if (number == null ) {
- throw new IllegalArgumentException("number is null");
- }
-
- if (activeMcc == null || activeMcc.trim().length() == 0) {
- throw new IllegalArgumentException("activeMcc is null or empty!");
- }
-
- String networkPortionNumber = PhoneNumberUtils.extractNetworkPortion(number);
- if (networkPortionNumber == null || networkPortionNumber.length() == 0) {
- throw new IllegalArgumentException("Number is invalid!");
- }
-
- NumberEntry numberEntry = new NumberEntry(networkPortionNumber);
- ArrayList<String> allIDDs = getAllIDDs(context, activeMcc);
-
- // First check whether the number is a NANP number.
- int nanpState = checkNANP(numberEntry, allIDDs);
- if (DBG) Rlog.d(TAG, "NANP type: " + getNumberPlanType(nanpState));
-
- if ((nanpState == NP_NANP_LOCAL)
- || (nanpState == NP_NANP_AREA_LOCAL)
- || (nanpState == NP_NANP_NDD_AREA_LOCAL)) {
- return networkPortionNumber;
- } else if (nanpState == NP_NANP_NBPCD_CC_AREA_LOCAL) {
- if (networkType == CDMA_HOME_NETWORK
- || networkType == CDMA_ROAMING_NETWORK) {
- // Remove "+"
- return networkPortionNumber.substring(1);
- } else {
- return networkPortionNumber;
- }
- } else if (nanpState == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
- if (networkType == CDMA_HOME_NETWORK) {
- return networkPortionNumber;
- } else if (networkType == GSM_UMTS_NETWORK) {
- // Remove the local IDD and replace with "+"
- int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
- return PLUS_SIGN + networkPortionNumber.substring(iddLength);
- } else if (networkType == CDMA_ROAMING_NETWORK) {
- // Remove the local IDD
- int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
- return networkPortionNumber.substring(iddLength);
- }
- }
-
- int internationalState = checkInternationalNumberPlan(context, numberEntry, allIDDs,
- NANP_IDD);
- if (DBG) Rlog.d(TAG, "International type: " + getNumberPlanType(internationalState));
- String returnNumber = null;
-
- switch (internationalState) {
- case NP_NBPCD_HOMEIDD_CC_AREA_LOCAL:
- if (networkType == GSM_UMTS_NETWORK) {
- // Remove "+"
- returnNumber = networkPortionNumber.substring(1);
- }
- break;
-
- case NP_NBPCD_CC_AREA_LOCAL:
- // Replace "+" with "011"
- returnNumber = NANP_IDD + networkPortionNumber.substring(1);
- break;
-
- case NP_LOCALIDD_CC_AREA_LOCAL:
- if (networkType == GSM_UMTS_NETWORK || networkType == CDMA_ROAMING_NETWORK) {
- int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
- // Replace <Local IDD> to <Home IDD>("011")
- returnNumber = NANP_IDD + networkPortionNumber.substring(iddLength);
- }
- break;
-
- case NP_CC_AREA_LOCAL:
- int countryCode = numberEntry.countryCode;
-
- if (!inExceptionListForNpCcAreaLocal(numberEntry)
- && networkPortionNumber.length() >= 11 && countryCode != NANP_CC) {
- // Add "011"
- returnNumber = NANP_IDD + networkPortionNumber;
- }
- break;
-
- case NP_HOMEIDD_CC_AREA_LOCAL:
- returnNumber = networkPortionNumber;
- break;
-
- default:
- // Replace "+" with 011 in CDMA network if the number's country
- // code is not in the HbpcdLookup database.
- if (networkPortionNumber.startsWith(PLUS_SIGN)
- && (networkType == CDMA_HOME_NETWORK || networkType == CDMA_ROAMING_NETWORK)) {
- if (networkPortionNumber.startsWith(PLUS_SIGN + NANP_IDD)) {
- // Only remove "+"
- returnNumber = networkPortionNumber.substring(1);
- } else {
- // Replace "+" with "011"
- returnNumber = NANP_IDD + networkPortionNumber.substring(1);
- }
- }
- }
-
- if (returnNumber == null) {
- returnNumber = networkPortionNumber;
- }
- return returnNumber;
- }
-
- /* Query International direct dialing from HbpcdLookup.db
- * for specified country code
- *
- * @param mcc current network's country code
- *
- * @return the IDD array list.
- */
- private static ArrayList<String> getAllIDDs(Context context, String mcc) {
- ArrayList<String> allIDDs = IDDS_MAPS.get(mcc);
- if (allIDDs != null) {
- return allIDDs;
- } else {
- allIDDs = new ArrayList<String>();
- }
-
- String projection[] = {MccIdd.IDD, MccIdd.MCC};
- String where = null;
-
- // if mcc is null : return all rows
- // if mcc is empty-string : return those rows whose mcc is emptry-string
- String[] selectionArgs = null;
- if (mcc != null) {
- where = MccIdd.MCC + "=?";
- selectionArgs = new String[] {mcc};
- }
-
- Cursor cursor = null;
- try {
- cursor = context.getContentResolver().query(MccIdd.CONTENT_URI, projection,
- where, selectionArgs, null);
- if (cursor.getCount() > 0) {
- while (cursor.moveToNext()) {
- String idd = cursor.getString(0);
- if (!allIDDs.contains(idd)) {
- allIDDs.add(idd);
- }
- }
- }
- } catch (SQLException e) {
- Rlog.e(TAG, "Can't access HbpcdLookup database", e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- IDDS_MAPS.put(mcc, allIDDs);
-
- if (DBG) Rlog.d(TAG, "MCC = " + mcc + ", all IDDs = " + allIDDs);
- return allIDDs;
- }
-
-
- /* Verify if the the destination number is a NANP number
- *
- * @param numberEntry including number and IDD array
- * @param allIDDs the IDD array list of the current network's country code
- *
- * @return the number plan type related NANP
- */
- private static int checkNANP(NumberEntry numberEntry, ArrayList<String> allIDDs) {
- boolean isNANP = false;
- String number = numberEntry.number;
-
- if (number.length() == NANP_SHORT_LENGTH) {
- // 7 digits - Seven digit phone numbers
- char firstChar = number.charAt(0);
- if (firstChar >= '2' && firstChar <= '9') {
- isNANP = true;
- for (int i=1; i< NANP_SHORT_LENGTH; i++ ) {
- char c= number.charAt(i);
- if (!PhoneNumberUtils.isISODigit(c)) {
- isNANP = false;
- break;
- }
- }
- }
- if (isNANP) {
- return NP_NANP_LOCAL;
- }
- } else if (number.length() == NANP_MEDIUM_LENGTH) {
- // 10 digits - Three digit area code followed by seven digit phone numbers/
- if (isNANP(number)) {
- return NP_NANP_AREA_LOCAL;
- }
- } else if (number.length() == NANP_LONG_LENGTH) {
- // 11 digits - One digit U.S. NDD(National Direct Dial) prefix '1',
- // followed by three digit area code and seven digit phone numbers
- if (isNANP(number)) {
- return NP_NANP_NDD_AREA_LOCAL;
- }
- } else if (number.startsWith(PLUS_SIGN)) {
- number = number.substring(1);
- if (number.length() == NANP_LONG_LENGTH) {
- // '+' and 11 digits -'+', followed by NANP CC prefix '1' followed by
- // three digit area code and seven digit phone numbers
- if (isNANP(number)) {
- return NP_NANP_NBPCD_CC_AREA_LOCAL;
- }
- } else if (number.startsWith(NANP_IDD) && number.length() == NANP_LONG_LENGTH + 3) {
- // '+' and 14 digits -'+', followed by NANP IDD "011" followed by NANP CC
- // prefix '1' followed by three digit area code and seven digit phone numbers
- number = number.substring(3);
- if (isNANP(number)) {
- return NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
- }
- }
- } else {
- // Check whether it's NP_NANP_LOCALIDD_CC_AREA_LOCAL
- for (String idd : allIDDs) {
- if (number.startsWith(idd)) {
- String number2 = number.substring(idd.length());
- if(number2 !=null && number2.startsWith(String.valueOf(NANP_CC))){
- if (isNANP(number2)) {
- numberEntry.IDD = idd;
- return NP_NANP_LOCALIDD_CC_AREA_LOCAL;
- }
- }
- }
- }
- }
-
- return NP_NONE;
- }
-
- private static boolean isNANP(String number) {
- if (number.length() == NANP_MEDIUM_LENGTH
- || (number.length() == NANP_LONG_LENGTH && number.startsWith(NANP_NDD))) {
- if (number.length() == NANP_LONG_LENGTH) {
- number = number.substring(1);
- }
- return (PhoneNumberUtils.isNanp(number));
- }
- return false;
- }
-
- /* Verify if the the destination number is an internal number
- *
- * @param numberEntry including number and IDD array
- * @param allIDDs the IDD array list of the current network's country code
- *
- * @return the number plan type related international number
- */
- private static int checkInternationalNumberPlan(Context context, NumberEntry numberEntry,
- ArrayList<String> allIDDs,String homeIDD) {
- String number = numberEntry.number;
- int countryCode = -1;
-
- if (number.startsWith(PLUS_SIGN)) {
- // +xxxxxxxxxx
- String numberNoNBPCD = number.substring(1);
- if (numberNoNBPCD.startsWith(homeIDD)) {
- // +011xxxxxxxx
- String numberCountryAreaLocal = numberNoNBPCD.substring(homeIDD.length());
- if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
- numberEntry.countryCode = countryCode;
- return NP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
- }
- } else if ((countryCode = getCountryCode(context, numberNoNBPCD)) > 0) {
- numberEntry.countryCode = countryCode;
- return NP_NBPCD_CC_AREA_LOCAL;
- }
-
- } else if (number.startsWith(homeIDD)) {
- // 011xxxxxxxxx
- String numberCountryAreaLocal = number.substring(homeIDD.length());
- if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
- numberEntry.countryCode = countryCode;
- return NP_HOMEIDD_CC_AREA_LOCAL;
- }
- } else {
- for (String exitCode : allIDDs) {
- if (number.startsWith(exitCode)) {
- String numberNoIDD = number.substring(exitCode.length());
- if ((countryCode = getCountryCode(context, numberNoIDD)) > 0) {
- numberEntry.countryCode = countryCode;
- numberEntry.IDD = exitCode;
- return NP_LOCALIDD_CC_AREA_LOCAL;
- }
- }
- }
-
- if (!number.startsWith("0") && (countryCode = getCountryCode(context, number)) > 0) {
- numberEntry.countryCode = countryCode;
- return NP_CC_AREA_LOCAL;
- }
- }
- return NP_NONE;
- }
-
- /**
- * Returns the country code from the given number.
- */
- private static int getCountryCode(Context context, String number) {
- int countryCode = -1;
- if (number.length() >= MIN_COUNTRY_AREA_LOCAL_LENGTH) {
- // Check Country code
- int[] allCCs = getAllCountryCodes(context);
- if (allCCs == null) {
- return countryCode;
- }
-
- int[] ccArray = new int[MAX_COUNTRY_CODES_LENGTH];
- for (int i = 0; i < MAX_COUNTRY_CODES_LENGTH; i ++) {
- ccArray[i] = Integer.parseInt(number.substring(0, i+1));
- }
-
- for (int i = 0; i < allCCs.length; i ++) {
- int tempCC = allCCs[i];
- for (int j = 0; j < MAX_COUNTRY_CODES_LENGTH; j ++) {
- if (tempCC == ccArray[j]) {
- if (DBG) Rlog.d(TAG, "Country code = " + tempCC);
- return tempCC;
- }
- }
- }
- }
-
- return countryCode;
- }
-
- /**
- * Gets all country Codes information with given MCC.
- */
- private static int[] getAllCountryCodes(Context context) {
- if (ALL_COUNTRY_CODES != null) {
- return ALL_COUNTRY_CODES;
- }
-
- Cursor cursor = null;
- try {
- String projection[] = {MccLookup.COUNTRY_CODE};
- cursor = context.getContentResolver().query(MccLookup.CONTENT_URI,
- projection, null, null, null);
-
- if (cursor.getCount() > 0) {
- ALL_COUNTRY_CODES = new int[cursor.getCount()];
- int i = 0;
- while (cursor.moveToNext()) {
- int countryCode = cursor.getInt(0);
- ALL_COUNTRY_CODES[i++] = countryCode;
- int length = String.valueOf(countryCode).trim().length();
- if (length > MAX_COUNTRY_CODES_LENGTH) {
- MAX_COUNTRY_CODES_LENGTH = length;
- }
- }
- }
- } catch (SQLException e) {
- Rlog.e(TAG, "Can't access HbpcdLookup database", e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return ALL_COUNTRY_CODES;
- }
-
- private static boolean inExceptionListForNpCcAreaLocal(NumberEntry numberEntry) {
- int countryCode = numberEntry.countryCode;
- boolean result = (numberEntry.number.length() == 12
- && (countryCode == 7 || countryCode == 20
- || countryCode == 65 || countryCode == 90));
- return result;
- }
-
- private static String getNumberPlanType(int state) {
- String numberPlanType = "Number Plan type (" + state + "): ";
-
- if (state == NP_NANP_LOCAL) {
- numberPlanType = "NP_NANP_LOCAL";
- } else if (state == NP_NANP_AREA_LOCAL) {
- numberPlanType = "NP_NANP_AREA_LOCAL";
- } else if (state == NP_NANP_NDD_AREA_LOCAL) {
- numberPlanType = "NP_NANP_NDD_AREA_LOCAL";
- } else if (state == NP_NANP_NBPCD_CC_AREA_LOCAL) {
- numberPlanType = "NP_NANP_NBPCD_CC_AREA_LOCAL";
- } else if (state == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
- numberPlanType = "NP_NANP_LOCALIDD_CC_AREA_LOCAL";
- } else if (state == NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
- numberPlanType = "NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL";
- } else if (state == NP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
- numberPlanType = "NP_NBPCD_HOMEIDD_CC_AREA_LOCAL";
- } else if (state == NP_HOMEIDD_CC_AREA_LOCAL) {
- numberPlanType = "NP_HOMEIDD_CC_AREA_LOCAL";
- } else if (state == NP_NBPCD_CC_AREA_LOCAL) {
- numberPlanType = "NP_NBPCD_CC_AREA_LOCAL";
- } else if (state == NP_LOCALIDD_CC_AREA_LOCAL) {
- numberPlanType = "NP_LOCALIDD_CC_AREA_LOCAL";
- } else if (state == NP_CC_AREA_LOCAL) {
- numberPlanType = "NP_CC_AREA_LOCAL";
- } else {
- numberPlanType = "Unknown type";
- }
- return numberPlanType;
- }
-
- /**
- * Filter the destination number if using VZW sim card.
- */
- public static String filterDestAddr(Phone phone, String destAddr) {
- if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + Rlog.pii(TAG, destAddr) + "\"" );
-
- if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
- Rlog.w(TAG, "destAddr" + Rlog.pii(TAG, destAddr) +
- " is not a global phone number! Nothing changed.");
- return destAddr;
- }
-
- final String networkOperator = TelephonyManager.from(phone.getContext()).
- getNetworkOperator(phone.getSubId());
- String result = null;
-
- if (needToConvert(phone)) {
- final int networkType = getNetworkType(phone);
- if (networkType != -1 && !TextUtils.isEmpty(networkOperator)) {
- String networkMcc = networkOperator.substring(0, 3);
- if (networkMcc != null && networkMcc.trim().length() > 0) {
- result = formatNumber(phone.getContext(), destAddr, networkMcc, networkType);
- }
- }
- }
-
- if (DBG) {
- Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
- Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? Rlog.pii(TAG,
- result) : Rlog.pii(TAG, destAddr)) + "\"");
- }
- return result != null ? result : destAddr;
- }
-
- /**
- * Returns the current network type
- */
- private static int getNetworkType(Phone phone) {
- int networkType = -1;
- int phoneType = phone.getPhoneType();
-
- if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- networkType = GSM_UMTS_NETWORK;
- } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- if (isInternationalRoaming(phone)) {
- networkType = CDMA_ROAMING_NETWORK;
- } else {
- networkType = CDMA_HOME_NETWORK;
- }
- } else {
- if (DBG) Rlog.w(TAG, "warning! unknown mPhoneType value=" + phoneType);
- }
-
- return networkType;
- }
-
- private static boolean isInternationalRoaming(Phone phone) {
- String operatorIsoCountry = TelephonyManager.from(phone.getContext()).
- getNetworkCountryIsoForPhone(phone.getPhoneId());
- String simIsoCountry = TelephonyManager.from(phone.getContext()).getSimCountryIsoForPhone(
- phone.getPhoneId());
- boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoCountry)
- && !TextUtils.isEmpty(simIsoCountry)
- && !simIsoCountry.equals(operatorIsoCountry);
- if (internationalRoaming) {
- if ("us".equals(simIsoCountry)) {
- internationalRoaming = !"vi".equals(operatorIsoCountry);
- } else if ("vi".equals(simIsoCountry)) {
- internationalRoaming = !"us".equals(operatorIsoCountry);
- }
- }
- return internationalRoaming;
- }
-
- private static boolean needToConvert(Phone phone) {
- // Calling package may not have READ_PHONE_STATE which is required for getConfig().
- // Clear the calling identity so that it is called as self.
- final long identity = Binder.clearCallingIdentity();
- try {
- CarrierConfigManager configManager = (CarrierConfigManager)
- phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null) {
- PersistableBundle bundle = configManager.getConfigForSubId(phone.getSubId());
- if (bundle != null) {
- return bundle.getBoolean(CarrierConfigManager
- .KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- // by default this value is false
- return false;
- }
-
- private static boolean compareGid1(Phone phone, String serviceGid1) {
- String gid1 = phone.getGroupIdLevel1();
- boolean ret = true;
-
- if (TextUtils.isEmpty(serviceGid1)) {
- if (DBG) Rlog.d(TAG, "compareGid1 serviceGid is empty, return " + ret);
- return ret;
- }
-
- int gid_length = serviceGid1.length();
- // Check if gid1 match service GID1
- if (!((gid1 != null) && (gid1.length() >= gid_length) &&
- gid1.substring(0, gid_length).equalsIgnoreCase(serviceGid1))) {
- if (DBG) Rlog.d(TAG, " gid1 " + gid1 + " serviceGid1 " + serviceGid1);
- ret = false;
- }
- if (DBG) Rlog.d(TAG, "compareGid1 is " + (ret?"Same":"Different"));
- return ret;
- }
-}
diff --git a/src/java/com/android/internal/telephony/SmsPermissions.java b/src/java/com/android/internal/telephony/SmsPermissions.java
index 5f147f2406..463ffe7dfb 100644
--- a/src/java/com/android/internal/telephony/SmsPermissions.java
+++ b/src/java/com/android/internal/telephony/SmsPermissions.java
@@ -50,7 +50,6 @@ public class SmsPermissions {
* Check that the caller can send text messages.
*
* For persisted messages, the caller just needs the SEND_SMS permission. For unpersisted
- * For persisted messages, the caller just needs the SEND_SMS permission. For unpersisted
* messages, the caller must either be the IMS app or a carrier-privileged app, or they must
* have both the MODIFY_PHONE_STATE and SEND_SMS permissions.
*
@@ -74,7 +73,6 @@ public class SmsPermissions {
return checkCallingCanSendSms(callingPackage, message);
}
-
/**
* Enforces that the caller is one of the following apps:
* <ul>
@@ -101,7 +99,6 @@ public class SmsPermissions {
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mPhone.getSubId(), message);
}
-
/**
* Check that the caller has SEND_SMS permissions. Can only be called during an IPC.
*
@@ -128,6 +125,67 @@ public class SmsPermissions {
== AppOpsManager.MODE_ALLOWED;
}
+ /**
+ * Check that the caller (or self, if this is not an IPC) can get SMSC address from (U)SIM.
+ *
+ * The default SMS application can get SMSC address, otherwise the caller must have
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or carrier privileges.
+ *
+ * @return true if the caller is default SMS app or has the required permission and privileges.
+ * Otherwise, false;
+ */
+ public boolean checkCallingOrSelfCanGetSmscAddress(String callingPackage, String message) {
+ // Allow it to the default SMS app always.
+ if (!isDefaultSmsPackage(callingPackage)) {
+ try {
+ // Allow it with READ_PRIVILEGED_PHONE_STATE or Carrier Privileges
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mContext, mPhone.getSubId(), message);
+ } catch (SecurityException e) { // To avoid crashing applications
+ Log.e(LOG_TAG, message + ": Neither " + callingPackage + " is the default SMS app"
+ + " nor the caller has "
+ + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
+ + ", or carrier privileges", e);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check that the caller (or self, if this is not an IPC) can set SMSC address on (U)SIM.
+ *
+ * The default SMS application can set SMSC address, otherwise the caller must have
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier privileges.
+ *
+ * @return true if the caller is default SMS app or has the required permission and privileges.
+ * Otherwise, false.
+ */
+ public boolean checkCallingOrSelfCanSetSmscAddress(String callingPackage, String message) {
+ // Allow it to the default SMS app always.
+ if (!isDefaultSmsPackage(callingPackage)) {
+ try {
+ // Allow it with MODIFY_PHONE_STATE or Carrier Privileges
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mContext, mPhone.getSubId(), message);
+ } catch (SecurityException e) { // To avoid crashing applications
+ Log.e(LOG_TAG, message + ": Neither " + callingPackage + " is the default SMS app"
+ + " nor the caller has " + android.Manifest.permission.MODIFY_PHONE_STATE
+ + ", or carrier privileges", e);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /** Check if a package is default SMS app. */
+ public boolean isDefaultSmsPackage(String packageName) {
+ return SmsApplication.isDefaultSmsApplication(mContext, packageName);
+ }
+
@UnsupportedAppUsage
protected void log(String msg) {
Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg);
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 34ea10d6ad..24bb42de4f 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -61,6 +61,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.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils;
import java.io.FileDescriptor;
@@ -1332,17 +1333,12 @@ public class SubscriptionController extends ISub.Stub {
if (DBG) logdl("[clearSubInfoRecord]+ iccId:" + " slotIndex:" + slotIndex);
// update simInfo db with invalid slot index
- List<SubscriptionInfo> oldSubInfo = getSubInfoUsingSlotIndexPrivileged(slotIndex);
ContentResolver resolver = mContext.getContentResolver();
ContentValues value = new ContentValues(1);
- value.put(SubscriptionManager.SIM_SLOT_INDEX,
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
- if (oldSubInfo != null) {
- for (int i = 0; i < oldSubInfo.size(); i++) {
- resolver.update(SubscriptionManager.getUriForSubscriptionId(
- oldSubInfo.get(i).getSubscriptionId()), value, null, null);
- }
- }
+ value.put(SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ String where = "(" + SubscriptionManager.SIM_SLOT_INDEX + "=" + slotIndex + ")";
+ resolver.update(SubscriptionManager.CONTENT_URI, value, where, null);
+
// Refresh the Cache of Active Subscription Info List
refreshCachedActiveSubscriptionInfoList();
@@ -3743,7 +3739,7 @@ public class SubscriptionController extends ISub.Stub {
*/
@NonNull
public String getDataEnabledOverrideRules(int subId) {
- return TextUtils.emptyIfNull(getSubscriptionProperty(subId,
+ return TelephonyUtils.emptyIfNull(getSubscriptionProperty(subId,
SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES));
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 9afa754609..f20870a4a6 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -75,7 +75,8 @@ import java.util.List;
public class SubscriptionInfoUpdater extends Handler {
private static final String LOG_TAG = "SubscriptionInfoUpdater";
@UnsupportedAppUsage
- private static final int PROJECT_SIM_NUM = TelephonyManager.getDefault().getMaxPhoneCount();
+ private static final int SUPPORTED_MODEM_COUNT = TelephonyManager.getDefault()
+ .getSupportedModemCount();
private static final boolean DBG = true;
@@ -91,6 +92,7 @@ public class SubscriptionInfoUpdater extends Handler {
private static final int EVENT_SIM_READY = 10;
private static final int EVENT_SIM_IMSI = 11;
private static final int EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS = 12;
+ private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 13;
private static final String ICCID_STRING_FOR_NO_SIM = "";
@@ -101,13 +103,12 @@ public class SubscriptionInfoUpdater extends Handler {
public static final String CURR_SUBID = "curr_subid";
@UnsupportedAppUsage
- private static Phone[] mPhone;
+ private static Context sContext = null;
@UnsupportedAppUsage
- private static Context mContext = null;
- @UnsupportedAppUsage
- private static String mIccId[] = new String[PROJECT_SIM_NUM];
- private static int[] sSimCardState = new int[PROJECT_SIM_NUM];
- private static int[] sSimApplicationState = new int[PROJECT_SIM_NUM];
+
+ private static String[] sIccId = new String[SUPPORTED_MODEM_COUNT];
+ private static int[] sSimCardState = new int[SUPPORTED_MODEM_COUNT];
+ private static int[] sSimApplicationState = new int[SUPPORTED_MODEM_COUNT];
private static boolean sIsSubInfoInitialized = false;
private SubscriptionManager mSubscriptionManager = null;
private EuiccManager mEuiccManager;
@@ -136,25 +137,26 @@ public class SubscriptionInfoUpdater extends Handler {
// TODO: The SubscriptionController instance should be passed in here from PhoneFactory
// rather than invoking the static getter all over the place.
public SubscriptionInfoUpdater(
- Looper looper, Context context, Phone[] phone, CommandsInterface[] ci) {
- this(looper, context, phone, ci,
- IPackageManager.Stub.asInterface(ServiceManager.getService("package")));
+ Looper looper, Context context, CommandsInterface[] ci) {
+ this(looper, context, ci, IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package")));
}
- @VisibleForTesting public SubscriptionInfoUpdater(
- Looper looper, Context context, Phone[] phone,
+ @VisibleForTesting public SubscriptionInfoUpdater(Looper looper, Context context,
CommandsInterface[] ci, IPackageManager packageMgr) {
logd("Constructor invoked");
mBackgroundHandler = new Handler(looper);
- mContext = context;
- mPhone = phone;
- mSubscriptionManager = SubscriptionManager.from(mContext);
- mEuiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
+ sContext = context;
+ mSubscriptionManager = SubscriptionManager.from(sContext);
+ mEuiccManager = (EuiccManager) sContext.getSystemService(Context.EUICC_SERVICE);
mPackageManager = packageMgr;
- mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
+ mCarrierServiceBindHelper = new CarrierServiceBindHelper(sContext);
initializeCarrierApps();
+
+ PhoneConfigurationManager.registerForMultiSimConfigChange(
+ this, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
}
private void initializeCarrierApps() {
@@ -169,9 +171,9 @@ public class SubscriptionInfoUpdater extends Handler {
public void onUserSwitching(int newUserId, IRemoteCallback reply)
throws RemoteException {
mCurrentlyActiveUserId = newUserId;
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(),
mPackageManager, TelephonyManager.getDefault(),
- mContext.getContentResolver(), mCurrentlyActiveUserId);
+ sContext.getContentResolver(), mCurrentlyActiveUserId);
if (reply != null) {
try {
@@ -181,14 +183,14 @@ public class SubscriptionInfoUpdater extends Handler {
}
}
}, LOG_TAG);
- ActivityManager am = (ActivityManager) mContext.getSystemService(
+ ActivityManager am = (ActivityManager) sContext.getSystemService(
Context.ACTIVITY_SERVICE);
mCurrentlyActiveUserId = am.getCurrentUser();
} catch (RemoteException e) {
logd("Couldn't get current user ID; guessing it's 0: " + e.getMessage());
}
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
- mPackageManager, TelephonyManager.getDefault(), mContext.getContentResolver(),
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(),
+ mPackageManager, TelephonyManager.getDefault(), sContext.getContentResolver(),
mCurrentlyActiveUserId);
}
@@ -224,11 +226,11 @@ public class SubscriptionInfoUpdater extends Handler {
@UnsupportedAppUsage
private boolean isAllIccIdQueryDone() {
- for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+ for (int i = 0; i < TelephonyManager.getDefault().getActiveModemCount(); i++) {
UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(i);
int slotId = UiccController.getInstance().getSlotIdFromPhoneId(i);
- if (mIccId[i] == null || slot == null || !slot.isActive()) {
- if (mIccId[i] == null) {
+ if (sIccId[i] == null || slot == null || !slot.isActive()) {
+ if (sIccId[i] == null) {
logd("Wait for SIM " + i + " Iccid");
} else {
logd(String.format("Wait for slot corresponding to phone %d to be active, "
@@ -252,7 +254,7 @@ public class SubscriptionInfoUpdater extends Handler {
if (ar.exception == null && ar.result != null) {
int[] modes = (int[])ar.result;
if (modes[0] == 1) { // Manual mode.
- mPhone[slotId].setNetworkSelectionModeAutomatic(null);
+ PhoneFactory.getPhone(slotId).setNetworkSelectionModeAutomatic(null);
}
} else {
logd("EVENT_GET_NETWORK_SELECTION_MODE_DONE: error getting network mode.");
@@ -336,11 +338,25 @@ public class SubscriptionInfoUpdater extends Handler {
});
break;
+ case EVENT_MULTI_SIM_CONFIG_CHANGED:
+ onMultiSimConfigChanged();
default:
logd("Unknown msg:" + msg.what);
}
}
+ private void onMultiSimConfigChanged() {
+ int activeModemCount = ((TelephonyManager) sContext.getSystemService(
+ 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;
+ }
+ }
+
private int getCardIdFromPhoneId(int phoneId) {
UiccController uiccController = UiccController.getInstance();
UiccCard card = uiccController.getUiccCardForPhone(phoneId);
@@ -356,14 +372,14 @@ public class SubscriptionInfoUpdater extends Handler {
}
private void handleSimLocked(int slotId, String reason) {
- if (mIccId[slotId] != null && mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
+ if (sIccId[slotId] != null && sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd("SIM" + (slotId + 1) + " hot plug in");
- mIccId[slotId] = null;
+ sIccId[slotId] = null;
}
- String iccId = mIccId[slotId];
+ String iccId = sIccId[slotId];
if (iccId == null) {
- IccCard iccCard = mPhone[slotId].getIccCard();
+ IccCard iccCard = PhoneFactory.getPhone(slotId).getIccCard();
if (iccCard == null) {
logd("handleSimLocked: IccCard null");
return;
@@ -377,7 +393,7 @@ public class SubscriptionInfoUpdater extends Handler {
logd("handleSimLocked: IccID null");
return;
}
- mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
+ sIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
} else {
logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
}
@@ -410,12 +426,12 @@ public class SubscriptionInfoUpdater extends Handler {
private void handleSimNotReady(int slotId) {
logd("handleSimNotReady: slotId: " + slotId);
- IccCard iccCard = mPhone[slotId].getIccCard();
+ IccCard iccCard = PhoneFactory.getPhone(slotId).getIccCard();
if (iccCard.isEmptyProfile()) {
// ICC_NOT_READY is a terminal state for an eSIM on the boot profile. At this
// phase, the subscription list is accessible. Treating NOT_READY
// as equivalent to ABSENT, once the rest of the system can handle it.
- mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+ sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
updateSubscriptionInfoByIccId(slotId, false /* updateEmbeddedSubs */);
}
@@ -432,7 +448,7 @@ public class SubscriptionInfoUpdater extends Handler {
// removed or a refresh RESET that the IccRecords could be null. The right behavior is to
// not broadcast the SIM loaded.
int loadedSlotId = slotId;
- IccCard iccCard = mPhone[slotId].getIccCard();
+ IccCard iccCard = PhoneFactory.getPhone(slotId).getIccCard();
if (iccCard == null) { // Possibly a race condition.
logd("handleSimLoaded: IccCard null");
return;
@@ -446,7 +462,7 @@ public class SubscriptionInfoUpdater extends Handler {
logd("handleSimLoaded: IccID null");
return;
}
- mIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
+ sIccId[slotId] = IccUtils.stripTrailingFs(records.getFullIccId());
updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
List<SubscriptionInfo> subscriptionInfos = SubscriptionController.getInstance()
@@ -457,12 +473,12 @@ public class SubscriptionInfoUpdater extends Handler {
for (SubscriptionInfo sub : subscriptionInfos) {
int subId = sub.getSubscriptionId();
TelephonyManager tm = (TelephonyManager)
- mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ sContext.getSystemService(Context.TELEPHONY_SERVICE);
String operator = tm.getSimOperatorNumeric(subId);
if (!TextUtils.isEmpty(operator)) {
if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
- MccTable.updateMccMncConfiguration(mContext, operator);
+ MccTable.updateMccMncConfiguration(sContext, operator);
}
SubscriptionController.getInstance().setMccMnc(operator, subId);
} else {
@@ -497,12 +513,12 @@ public class SubscriptionInfoUpdater extends Handler {
* Storing last subId in SharedPreference for now to detect SIM change.
*/
SharedPreferences sp =
- PreferenceManager.getDefaultSharedPreferences(mContext);
+ PreferenceManager.getDefaultSharedPreferences(sContext);
int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
if (storedSubId != subId) {
int networkType = Settings.Global.getInt(
- mPhone[slotId].getContext().getContentResolver(),
+ PhoneFactory.getPhone(slotId).getContext().getContentResolver(),
Settings.Global.PREFERRED_NETWORK_MODE + subId,
-1 /* invalid network mode */);
@@ -510,23 +526,23 @@ public class SubscriptionInfoUpdater extends Handler {
networkType = RILConstants.PREFERRED_NETWORK_MODE;
try {
networkType = TelephonyManager.getIntAtIndex(
- mContext.getContentResolver(),
+ sContext.getContentResolver(),
Settings.Global.PREFERRED_NETWORK_MODE, slotId);
} catch (SettingNotFoundException retrySnfe) {
Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for "
+ "Settings.Global.PREFERRED_NETWORK_MODE");
}
Settings.Global.putInt(
- mPhone[slotId].getContext().getContentResolver(),
+ PhoneFactory.getPhone(slotId).getContext().getContentResolver(),
Global.PREFERRED_NETWORK_MODE + subId,
networkType);
}
// Set the modem network mode
- mPhone[slotId].setPreferredNetworkType(networkType, null);
+ PhoneFactory.getPhone(slotId).setPreferredNetworkType(networkType, null);
// Only support automatic selection mode on SIM change.
- mPhone[slotId].getNetworkSelectionMode(
+ PhoneFactory.getPhone(slotId).getNetworkSelectionMode(
obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE,
new Integer(slotId)));
@@ -539,9 +555,9 @@ public class SubscriptionInfoUpdater extends Handler {
}
// Update set of enabled carrier apps now that the privilege rules may have changed.
- CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+ CarrierAppUtils.disableCarrierAppsUntilPrivileged(sContext.getOpPackageName(),
mPackageManager, TelephonyManager.getDefault(),
- mContext.getContentResolver(), mCurrentlyActiveUserId);
+ sContext.getContentResolver(), mCurrentlyActiveUserId);
/**
* The sim loading sequence will be
@@ -560,22 +576,22 @@ public class SubscriptionInfoUpdater extends Handler {
private void updateCarrierServices(int slotId, String simState) {
CarrierConfigManager configManager =
- (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
configManager.updateConfigForPhoneId(slotId, simState);
mCarrierServiceBindHelper.updateForPhoneId(slotId, simState);
}
private void updateSubscriptionCarrierId(int slotId, String simState) {
- if (mPhone != null && mPhone[slotId] != null) {
- mPhone[slotId].resolveSubscriptionCarrierId(simState);
+ if (PhoneFactory.getPhone(slotId) != null) {
+ PhoneFactory.getPhone(slotId).resolveSubscriptionCarrierId(simState);
}
}
private void handleSimAbsent(int slotId, int absentAndInactive) {
- if (mIccId[slotId] != null && !mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
+ if (sIccId[slotId] != null && !sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd("SIM" + (slotId + 1) + " hot plug out, absentAndInactive=" + absentAndInactive);
}
- mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+ sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
// Do not broadcast if the SIM is absent and inactive, because the logical slotId here is
// no longer correct
@@ -589,10 +605,10 @@ public class SubscriptionInfoUpdater extends Handler {
}
private void handleSimError(int slotId) {
- if (mIccId[slotId] != null && !mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
+ if (sIccId[slotId] != null && !sIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
logd("SIM" + (slotId + 1) + " Error ");
}
- mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+ sIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
updateSubscriptionInfoByIccId(slotId, true /* updateEmbeddedSubs */);
broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR,
IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
@@ -617,10 +633,10 @@ public class SubscriptionInfoUpdater extends Handler {
SubscriptionController.getInstance().clearSubInfoRecord(slotIndex);
// If SIM is not absent, insert new record or update existing record.
- if (!ICCID_STRING_FOR_NO_SIM.equals(mIccId[slotIndex])) {
- logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
- + mIccId[slotIndex] + "slot: " + slotIndex);
- mSubscriptionManager.addSubscriptionInfoRecord(mIccId[slotIndex], slotIndex);
+ if (!ICCID_STRING_FOR_NO_SIM.equals(sIccId[slotIndex])) {
+ logd("updateSubscriptionInfoByIccId: adding subscription info record: iccid: "
+ + sIccId[slotIndex] + "slot: " + slotIndex);
+ mSubscriptionManager.addSubscriptionInfoRecord(sIccId[slotIndex], slotIndex);
}
List<SubscriptionInfo> subInfos = SubscriptionController.getInstance()
@@ -636,8 +652,8 @@ public class SubscriptionInfoUpdater extends Handler {
if (!TextUtils.equals(msisdn, temp.getNumber())) {
value.put(SubscriptionManager.NUMBER, msisdn);
- mContext.getContentResolver().update(SubscriptionManager.getUriForSubscriptionId(
- temp.getSubscriptionId()), value, null, null);
+ sContext.getContentResolver().update(SubscriptionManager
+ .getUriForSubscriptionId(temp.getSubscriptionId()), value, null, null);
changed = true;
}
}
@@ -800,7 +816,7 @@ public class SubscriptionInfoUpdater extends Handler {
// returned by the eUICC controller).
List<SubscriptionInfo> existingSubscriptions = SubscriptionController.getInstance()
.getSubscriptionInfoListForEmbeddedSubscriptionUpdate(embeddedIccids, isRemovable);
- ContentResolver contentResolver = mContext.getContentResolver();
+ ContentResolver contentResolver = sContext.getContentResolver();
for (EuiccProfileInfo embeddedProfile : embeddedProfiles) {
int index =
findSubscriptionInfoForIccid(existingSubscriptions, embeddedProfile.getIccid());
@@ -848,7 +864,7 @@ public class SubscriptionInfoUpdater extends Handler {
// is no valid carrier id present.
if (prevCarrierId == TelephonyManager.UNKNOWN_CARRIER_ID) {
values.put(SubscriptionManager.CARRIER_ID,
- CarrierResolver.getCarrierIdFromIdentifier(mContext, cid));
+ CarrierResolver.getCarrierIdFromIdentifier(sContext, cid));
}
String mcc = cid.getMcc();
String mnc = cid.getMnc();
@@ -918,14 +934,14 @@ public class SubscriptionInfoUpdater extends Handler {
private String getDefaultCarrierServicePackageName() {
CarrierConfigManager configManager =
- (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ (CarrierConfigManager) sContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
return configManager.getDefaultCarrierServicePackageName();
}
private boolean isCarrierServicePackage(int phoneId, String pkgName) {
if (pkgName.equals(getDefaultCarrierServicePackageName())) return false;
- List<String> carrierPackageNames = TelephonyManager.from(mContext)
+ List<String> carrierPackageNames = TelephonyManager.from(sContext)
.getCarrierPackageNamesForIntentAndPhone(
new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId);
if (DBG) logd("Carrier Packages For Subscription = " + carrierPackageNames);
@@ -1018,7 +1034,7 @@ public class SubscriptionInfoUpdater extends Handler {
}
}
}
- if (cv.size() > 0 && mContext.getContentResolver().update(SubscriptionManager
+ if (cv.size() > 0 && sContext.getContentResolver().update(SubscriptionManager
.getUriForSubscriptionId(currentSubId), cv, null, null) > 0) {
sc.refreshCachedActiveSubscriptionInfoList();
sc.notifySubscriptionInfoChanged();
@@ -1083,7 +1099,7 @@ public class SubscriptionInfoUpdater extends Handler {
i.putExtra(PhoneConstants.SLOT_KEY, slotId);
logd("Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED " + simStateString(state)
+ " for phone: " + phoneId + " slot: " + slotId);
- mContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ sContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
TelephonyMetrics.getInstance().updateSimState(phoneId, state);
}
}
@@ -1096,7 +1112,7 @@ public class SubscriptionInfoUpdater extends Handler {
boolean isUnknownToNotReady =
(sSimApplicationState[phoneId] == TelephonyManager.SIM_STATE_UNKNOWN
&& state == TelephonyManager.SIM_STATE_NOT_READY);
- IccCard iccCard = mPhone[phoneId].getIccCard();
+ IccCard iccCard = PhoneFactory.getPhone(phoneId).getIccCard();
boolean emptyProfile = iccCard != null && iccCard.isEmptyProfile();
if (state != sSimApplicationState[phoneId] && (!isUnknownToNotReady || emptyProfile)) {
sSimApplicationState[phoneId] = state;
@@ -1111,7 +1127,7 @@ public class SubscriptionInfoUpdater extends Handler {
i.putExtra(PhoneConstants.SLOT_KEY, slotId);
logd("Broadcasting intent ACTION_SIM_APPLICATION_STATE_CHANGED " + simStateString(state)
+ " for phone: " + phoneId + " slot: " + slotId);
- mContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ sContext.sendBroadcast(i, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
TelephonyMetrics.getInstance().updateSimState(phoneId, state);
}
}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index a461324bdd..d89ee942d8 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -42,6 +42,7 @@ 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.nitz.NewNitzStateMachineImpl;
import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccProfile;
@@ -292,11 +293,17 @@ public class TelephonyComponentFactory {
return new EmergencyNumberTracker(phone, ci);
}
+ private static final boolean USE_NEW_NITZ_STATE_MACHINE = false;
+
/**
* Returns a new {@link NitzStateMachine} instance.
*/
public NitzStateMachine makeNitzStateMachine(GsmCdmaPhone phone) {
- return new NitzStateMachineImpl(phone);
+ if (USE_NEW_NITZ_STATE_MACHINE) {
+ return NewNitzStateMachineImpl.createInstance(phone);
+ } else {
+ return new NitzStateMachineImpl(phone);
+ }
}
public SimActivationTracker makeSimActivationTracker(Phone phone) {
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index 94b020a073..776bbb67db 100755
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -337,11 +337,6 @@ public class WapPushOverSms implements ServiceConnection {
return result.statusCode;
}
- if (SmsManager.getDefault().getAutoPersisting()) {
- // Store the wap push data in telephony
- writeInboxMessage(result.subId, result.parsedPdu);
- }
-
/**
* If the pdu has application ID, WapPushManager substitute the message
* processing. Since WapPushManager is optional module, if WapPushManager
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
index ed7c822210..4f6ca8624d 100755
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -62,13 +62,20 @@ public interface AppInterface {
*/
void onCmdResponse(CatResponseMessage resMsg);
+ /**
+ * Dispose when the service is not longer needed.
+ */
+ void dispose();
+
/*
* Enumeration for representing "Type of Command" of proactive commands.
* Those are the only commands which are supported by the Telephony. Any app
* implementation should support those.
* Refer to ETSI TS 102.223 section 9.4
*/
- public static enum CommandType {
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/cat/AppInterface$CommandType;")
+ enum CommandType {
@UnsupportedAppUsage
DISPLAY_TEXT(0x21),
@UnsupportedAppUsage
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 71718081b7..f149c6faa9 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -16,12 +16,9 @@
package com.android.internal.telephony.cat;
-import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants
- .IDLE_SCREEN_AVAILABLE_EVENT;
-import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants
- .LANGUAGE_SELECTION_EVENT;
-import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants
- .USER_ACTIVITY_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
+import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.USER_ACTIVITY_EVENT;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManagerNative;
@@ -38,7 +35,6 @@ import android.os.Handler;
import android.os.LocaleList;
import android.os.Message;
import android.os.RemoteException;
-import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.CommandsInterface;
@@ -221,7 +217,7 @@ public class CatService extends Handler implements AppInterface {
synchronized (sInstanceLock) {
if (sInstance == null) {
- int simCount = TelephonyManager.getDefault().getMaxPhoneCount();
+ int simCount = TelephonyManager.getDefault().getSupportedModemCount();
sInstance = new CatService[simCount];
for (int i = 0; i < simCount; i++) {
sInstance[i] = null;
@@ -252,6 +248,7 @@ public class CatService extends Handler implements AppInterface {
}
@UnsupportedAppUsage
+ @Override
public void dispose() {
synchronized (sInstanceLock) {
CatLog.d(this, "Disposing CatService object");
@@ -275,7 +272,7 @@ public class CatService extends Handler implements AppInterface {
mMsgDecoder = null;
removeCallbacksAndMessages(null);
if (sInstance != null) {
- if (SubscriptionManager.isValidSlotIndex(mSlotId)) {
+ if (mSlotId >= 0 && mSlotId < sInstance.length) {
sInstance[mSlotId] = null;
} else {
CatLog.d(this, "error: invaild slot id: " + mSlotId);
diff --git a/src/java/com/android/internal/telephony/cat/ResponseData.java b/src/java/com/android/internal/telephony/cat/ResponseData.java
index 600514ff20..4d0a2d2d12 100644
--- a/src/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/src/java/com/android/internal/telephony/cat/ResponseData.java
@@ -16,20 +16,25 @@
package com.android.internal.telephony.cat;
-import com.android.internal.telephony.EncodeException;
-import com.android.internal.telephony.GsmAlphabet;
-import java.util.Calendar;
-import java.util.TimeZone;
+import android.annotation.UnsupportedAppUsage;
import android.os.SystemProperties;
import android.text.TextUtils;
+import com.android.internal.telephony.EncodeException;
+import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.cat.AppInterface.CommandType;
-import android.annotation.UnsupportedAppUsage;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.TimeZone;
abstract class ResponseData {
+
+ @UnsupportedAppUsage
+ ResponseData() {
+ }
+
/**
* Format the data appropriate for TERMINAL RESPONSE and write it into
* the ByteArrayOutputStream object.
diff --git a/src/java/com/android/internal/telephony/cat/ResultCode.java b/src/java/com/android/internal/telephony/cat/ResultCode.java
index 346d74adba..adcf53e094 100644
--- a/src/java/com/android/internal/telephony/cat/ResultCode.java
+++ b/src/java/com/android/internal/telephony/cat/ResultCode.java
@@ -26,6 +26,7 @@ import android.annotation.UnsupportedAppUsage;
*
* {@hide}
*/
+@UnsupportedAppUsage(implicitMember = "values()[Lcom/android/internal/telephony/cat/ResultCode;")
public enum ResultCode {
/*
diff --git a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
index ebaf08a859..eeb9dd42fb 100755
--- a/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
+++ b/src/java/com/android/internal/telephony/cat/RilMessageDecoder.java
@@ -22,7 +22,6 @@ import android.os.Message;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.State;
@@ -41,6 +40,7 @@ class RilMessageDecoder extends StateMachine {
// members
@UnsupportedAppUsage
private CommandParamsFactory mCmdParamsFactory = null;
+ @UnsupportedAppUsage
private RilMessage mCurrentRilMessage = null;
private Handler mCaller = null;
private static int mSimCount = 0;
@@ -63,7 +63,7 @@ class RilMessageDecoder extends StateMachine {
public static synchronized RilMessageDecoder getInstance(Handler caller, IccFileHandler fh,
int slotId) {
if (null == mInstance) {
- mSimCount = TelephonyManager.getDefault().getMaxPhoneCount();
+ mSimCount = TelephonyManager.getDefault().getSupportedModemCount();
mInstance = new RilMessageDecoder[mSimCount];
for (int i = 0; i < mSimCount; i++) {
mInstance[i] = null;
@@ -88,6 +88,7 @@ class RilMessageDecoder extends StateMachine {
*
* @param rilMsg
*/
+ @UnsupportedAppUsage
public void sendStartDecodingMessageParams(RilMessage rilMsg) {
Message msg = obtainMessage(CMD_START);
msg.obj = rilMsg;
@@ -107,6 +108,7 @@ class RilMessageDecoder extends StateMachine {
sendMessage(msg);
}
+ @UnsupportedAppUsage
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/ValueParser.java b/src/java/com/android/internal/telephony/cat/ValueParser.java
index 4e528b6f66..03d7f67482 100644
--- a/src/java/com/android/internal/telephony/cat/ValueParser.java
+++ b/src/java/com/android/internal/telephony/cat/ValueParser.java
@@ -16,13 +16,14 @@
package com.android.internal.telephony.cat;
+import android.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.cat.Duration.TimeUnit;
import com.android.internal.telephony.uicc.IccUtils;
-import android.annotation.UnsupportedAppUsage;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
@@ -62,6 +63,7 @@ abstract class ValueParser {
* Command Details object is found, ResultException is thrown.
* @throws ResultException
*/
+ @UnsupportedAppUsage
static DeviceIdentities retrieveDeviceIdentities(ComprehensionTlv ctlv)
throws ResultException {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 5eadd863ef..0608f88db0 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -18,11 +18,17 @@ package com.android.internal.telephony.cdma;
import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Message;
+import android.os.RemoteCallback;
+import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
+import android.telephony.PhoneNumberUtils;
import android.telephony.SmsCbMessage;
import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaSmsCbProgramResults;
import com.android.internal.telephony.CellBroadcastHandler;
import com.android.internal.telephony.CommandsInterface;
@@ -34,9 +40,15 @@ import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.TelephonyComponentFactory;
import com.android.internal.telephony.WspTypeDecoder;
+import com.android.internal.telephony.cdma.sms.BearerData;
+import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.util.HexDump;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -46,13 +58,27 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
private final CdmaSMSDispatcher mSmsDispatcher;
private final CdmaServiceCategoryProgramHandler mServiceCategoryProgramHandler;
+ private static CdmaCbTestBroadcastReceiver sTestBroadcastReceiver;
+ private static CdmaScpTestBroadcastReceiver sTestScpBroadcastReceiver;
private byte[] mLastDispatchedSmsFingerprint;
private byte[] mLastAcknowledgedSmsFingerprint;
+ // Callback used to process the result of an SCP message
+ private RemoteCallback mScpCallback;
+
private final boolean mCheckForDuplicatePortsInOmadmWapPush = Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_duplicate_port_omadm_wappush);
+ // When TEST_MODE is on we allow the test intent to trigger an SMS CB alert
+ private static final boolean TEST_MODE = SystemProperties.getInt("ro.debuggable", 0) == 1;
+ private static final String TEST_ACTION = "com.android.internal.telephony.cdma"
+ + ".TEST_TRIGGER_CELL_BROADCAST";
+ private static final String SCP_TEST_ACTION = "com.android.internal.telephony.cdma"
+ + ".TEST_TRIGGER_SCP_MESSAGE";
+ private static final String TOGGLE_CB_MODULE = "com.android.internal.telephony.cdma"
+ + ".TOGGLE_CB_MODULE";
+
/**
* Create a new inbound SMS handler for CDMA.
*/
@@ -62,8 +88,78 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
CellBroadcastHandler.makeCellBroadcastHandler(context, phone));
mSmsDispatcher = smsDispatcher;
mServiceCategoryProgramHandler = CdmaServiceCategoryProgramHandler.makeScpHandler(context,
- phone.mCi);
+ phone.mCi, phone);
phone.mCi.setOnNewCdmaSms(getHandler(), EVENT_NEW_SMS, null);
+
+ mCellBroadcastServiceManager.enable();
+ mScpCallback = new RemoteCallback(result -> {
+ if (result == null) {
+ loge("SCP results error: missing extras");
+ return;
+ }
+ String sender = result.getString("sender");
+ if (sender == null) {
+ loge("SCP results error: missing sender extra.");
+ return;
+ }
+ ArrayList<CdmaSmsCbProgramResults> results = result.getParcelableArrayList("results");
+ if (results == null) {
+ loge("SCP results error: missing results extra.");
+ return;
+ }
+
+ BearerData bData = new BearerData();
+ bData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
+ bData.messageId = SmsMessage.getNextMessageId();
+ bData.serviceCategoryProgramResults = results;
+ byte[] encodedBearerData = BearerData.encode(bData);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
+ DataOutputStream dos = new DataOutputStream(baos);
+ try {
+ dos.writeInt(SmsEnvelope.TELESERVICE_SCPT);
+ dos.writeInt(0); //servicePresent
+ dos.writeInt(0); //serviceCategory
+ CdmaSmsAddress destAddr = CdmaSmsAddress.parse(
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCodeForSms(sender));
+ dos.write(destAddr.digitMode);
+ dos.write(destAddr.numberMode);
+ dos.write(destAddr.ton); // number_type
+ dos.write(destAddr.numberPlan);
+ dos.write(destAddr.numberOfDigits);
+ dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
+ // Subaddress is not supported.
+ dos.write(0); //subaddressType
+ dos.write(0); //subaddr_odd
+ dos.write(0); //subaddr_nbr_of_digits
+ dos.write(encodedBearerData.length);
+ dos.write(encodedBearerData, 0, encodedBearerData.length);
+ // Ignore the RIL response. TODO: implement retry if SMS send fails.
+ mPhone.mCi.sendCdmaSms(baos.toByteArray(), null);
+ } catch (IOException e) {
+ loge("exception creating SCP results PDU", e);
+ } finally {
+ try {
+ dos.close();
+ } catch (IOException ignored) {
+ }
+ }
+ });
+ if (TEST_MODE) {
+ if (sTestBroadcastReceiver == null) {
+ sTestBroadcastReceiver = new CdmaCbTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TEST_ACTION);
+ filter.addAction(TOGGLE_CB_MODULE);
+ context.registerReceiver(sTestBroadcastReceiver, filter);
+ }
+ if (sTestScpBroadcastReceiver == null) {
+ sTestScpBroadcastReceiver = new CdmaScpTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(SCP_TEST_ACTION);
+ context.registerReceiver(sTestScpBroadcastReceiver, filter);
+ }
+ }
}
/**
@@ -91,6 +187,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
/**
* Return true if this handler is for 3GPP2 messages; false for 3GPP format.
+ *
* @return true (3GPP2)
*/
@Override
@@ -100,6 +197,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
/**
* Process Cell Broadcast, Voicemail Notification, and other 3GPP/3GPP2-specific messages.
+ *
* @param smsb the SmsMessageBase object from the RIL
* @return true if the message was handled here; false to continue processing
*/
@@ -111,13 +209,10 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
// Handle CMAS emergency broadcast messages.
if (isBroadcastType) {
log("Broadcast type message");
- String plmn =
- TelephonyManager.from(mContext).getNetworkOperatorForPhone(mPhone.getPhoneId());
- SmsCbMessage cbMessage = sms.parseBroadcastSms(plmn);
- if (cbMessage != null) {
- mCellBroadcastHandler.dispatchSmsMessage(cbMessage);
+ if (sEnableCbModule) {
+ mCellBroadcastServiceManager.sendCdmaMessageToHandler(sms);
} else {
- loge("error trying to parse broadcast SMS");
+ legacyDispatchSmsCbMessage(sms);
}
return Intents.RESULT_SMS_HANDLED;
}
@@ -149,7 +244,11 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
break;
case SmsEnvelope.TELESERVICE_SCPT:
- mServiceCategoryProgramHandler.dispatchSmsMessage(sms);
+ if (sEnableCbModule) {
+ mCellBroadcastServiceManager.sendCdmaScpMessageToHandler(sms, mScpCallback);
+ } else {
+ mServiceCategoryProgramHandler.dispatchSmsMessage(sms);
+ }
return Intents.RESULT_SMS_HANDLED;
case SmsEnvelope.TELESERVICE_FDEA_WAP:
@@ -184,10 +283,24 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
return dispatchNormalMessage(smsb);
}
+ // dispatch an SMS message through the platform
+ private void legacyDispatchSmsCbMessage(SmsMessage sms) {
+ String plmn =
+ TelephonyManager.from(mContext).getNetworkOperatorForPhone(
+ mPhone.getPhoneId());
+ SmsCbMessage cbMessage = sms.parseBroadcastSms(plmn, mPhone.getPhoneId());
+ if (cbMessage != null) {
+ mCellBroadcastHandler.dispatchSmsMessage(cbMessage);
+ } else {
+ loge("error trying to parse broadcast SMS");
+ }
+ }
+
/**
* Send an acknowledge message.
- * @param success indicates that last message was successfully received.
- * @param result result code indicating any error
+ *
+ * @param success indicates that last message was successfully received.
+ * @param result result code indicating any error
* @param response callback message sent when operation completes.
*/
@Override
@@ -203,27 +316,29 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
/**
* Convert Android result code to CDMA SMS failure cause.
+ *
* @param rc the Android SMS intent result value
* @return 0 for success, or a CDMA SMS failure cause value
*/
private static int resultToCause(int rc) {
switch (rc) {
- case Activity.RESULT_OK:
- case Intents.RESULT_SMS_HANDLED:
- // Cause code is ignored on success.
- return 0;
- case Intents.RESULT_SMS_OUT_OF_MEMORY:
- return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE;
- case Intents.RESULT_SMS_UNSUPPORTED:
- return CommandsInterface.CDMA_SMS_FAIL_CAUSE_INVALID_TELESERVICE_ID;
- case Intents.RESULT_SMS_GENERIC_ERROR:
- default:
- return CommandsInterface.CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM;
+ case Activity.RESULT_OK:
+ case Intents.RESULT_SMS_HANDLED:
+ // Cause code is ignored on success.
+ return 0;
+ case Intents.RESULT_SMS_OUT_OF_MEMORY:
+ return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE;
+ case Intents.RESULT_SMS_UNSUPPORTED:
+ return CommandsInterface.CDMA_SMS_FAIL_CAUSE_INVALID_TELESERVICE_ID;
+ case Intents.RESULT_SMS_GENERIC_ERROR:
+ default:
+ return CommandsInterface.CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM;
}
}
/**
* Handle {@link SmsEnvelope#TELESERVICE_VMN} and {@link SmsEnvelope#TELESERVICE_MWI}.
+ *
* @param sms the message to process
*/
private void handleVoicemailTeleservice(SmsMessage sms) {
@@ -251,8 +366,8 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
*
* @param pdu The WAP-WDP PDU segment
* @return a result code from {@link android.provider.Telephony.Sms.Intents}, or
- * {@link Activity#RESULT_OK} if the message has been broadcast
- * to applications
+ * {@link Activity#RESULT_OK} if the message has been broadcast
+ * to applications
*/
private int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address, String dispAddr,
long timestamp) {
@@ -299,8 +414,10 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
System.arraycopy(pdu, index, userData, 0, pdu.length - index);
InboundSmsTracker tracker = TelephonyComponentFactory.getInstance()
.inject(InboundSmsTracker.class.getName()).makeInboundSmsTracker(
- userData, timestamp, destinationPort, true, address, dispAddr, referenceNumber,
- segment, totalSegments, true, HexDump.toHexString(userData), false /* isClass0 */,
+ userData, timestamp, destinationPort, true, address, dispAddr,
+ referenceNumber,
+ segment, totalSegments, true, HexDump.toHexString(userData),
+ false /* isClass0 */,
mPhone.getSubId());
// de-duping is done only for text messages
@@ -312,11 +429,12 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
* extra port fields.
* - Some carriers make this mistake.
* ex: MSGTYPE-TotalSegments-CurrentSegment
- * -SourcePortDestPort-SourcePortDestPort-OMADM PDU
+ * -SourcePortDestPort-SourcePortDestPort-OMADM PDU
+ *
* @param origPdu The WAP-WDP PDU segment
- * @param index Current Index while parsing the PDU.
+ * @param index Current Index while parsing the PDU.
* @return True if OrigPdu is OmaDM Push Message which has duplicate ports.
- * False if OrigPdu is NOT OmaDM Push Message which has duplicate ports.
+ * False if OrigPdu is NOT OmaDM Push Message which has duplicate ports.
*/
private static boolean checkDuplicatePortOmadmWapPush(byte[] origPdu, int index) {
index += 4;
@@ -349,4 +467,138 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler {
mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP2);
}
+
+ /**
+ * A broadcast receiver used for testing emergency cell broadcasts. To trigger test CDMA cell
+ * broadcasts with adb run e.g:
+ *
+ * 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
+ *
+ * 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 \
+ *
+ * adb shell am broadcast -a com.android.internal.telephony.cdma.TOGGLE_CB_MODULE
+ *
+ * adb shell am broadcast -a com.android.internal.telephony.cdma.TOGGLE_CB_MODULE \
+ * --ez enable true
+ */
+ private class CdmaCbTestBroadcastReceiver extends CbTestBroadcastReceiver {
+
+ CdmaCbTestBroadcastReceiver() {
+ super(TEST_ACTION, TOGGLE_CB_MODULE);
+ }
+
+ @Override
+ protected void handleTestAction(Intent intent) {
+ SmsEnvelope envelope = new SmsEnvelope();
+ // the CdmaSmsAddress is not used for a test cell broadcast message, but needs to be
+ // supplied to avoid a null pointer exception in the platform
+ CdmaSmsAddress nonNullAddress = new CdmaSmsAddress();
+ nonNullAddress.origBytes = new byte[]{(byte) 0xFF};
+ envelope.origAddress = nonNullAddress;
+
+ // parse service category from intent
+ envelope.serviceCategory = intent.getIntExtra("service_category", -1);
+ if (envelope.serviceCategory == -1) {
+ log("No service category, ignoring CB test intent");
+ return;
+ }
+
+ // parse bearer data from intent
+ String bearerDataString = intent.getStringExtra("bearer_data_string");
+ envelope.bearerData = decodeHexString(bearerDataString);
+ if (envelope.bearerData == null) {
+ log("No bearer data, ignoring CB test intent");
+ return;
+ }
+
+ SmsMessage sms = new SmsMessage(new CdmaSmsAddress(), envelope);
+ if (sEnableCbModule) {
+ mCellBroadcastServiceManager.sendCdmaMessageToHandler(sms);
+ } else {
+ legacyDispatchSmsCbMessage(sms);
+ }
+ }
+
+ @Override
+ protected void handleToggleEnable() {
+ // sEnableCbModule is already toggled in super class
+ mCellBroadcastServiceManager.enable();
+ }
+
+ @Override
+ protected void handleToggleDisable(Context context) {
+ // sEnableCbModule is already toggled in super class
+ mCellBroadcastServiceManager.disable();
+ }
+ }
+
+ /**
+ * A broadcast receiver used for testing CDMA SCP messages. To trigger test CDMA SCP messages
+ * with adb run e.g:
+ *
+ * adb shell am broadcast -a com.android.internal.telephony.cdma.TEST_TRIGGER_SCP_MESSAGE \
+ * --es originating_address_string 1234567890 \
+ * --es bearer_data_string 00031007B0122610880080B2091C5F1D3965DB95054D1CB2E1E883A6F41334E \
+ * 6CA830EEC882872DFC32F2E9E40
+ *
+ * To toggle use the CDMA CB test broadcast receiver.
+ */
+ private class CdmaScpTestBroadcastReceiver extends CbTestBroadcastReceiver {
+
+ CdmaScpTestBroadcastReceiver() {
+ super(SCP_TEST_ACTION, null);
+ }
+
+ @Override
+ protected void handleTestAction(Intent intent) {
+ SmsEnvelope envelope = new SmsEnvelope();
+ // the CdmaSmsAddress is not used for a test SCP message, but needs to be supplied to
+ // avoid a null pointer exception in the platform
+ CdmaSmsAddress nonNullAddress = new CdmaSmsAddress();
+ nonNullAddress.origBytes = new byte[]{(byte) 0xFF};
+ envelope.origAddress = nonNullAddress;
+
+ // parse bearer data from intent
+ String bearerDataString = intent.getStringExtra("bearer_data_string");
+ envelope.bearerData = decodeHexString(bearerDataString);
+ if (envelope.bearerData == null) {
+ log("No bearer data, ignoring SCP test intent");
+ return;
+ }
+
+ CdmaSmsAddress origAddr = new CdmaSmsAddress();
+ String addressString = intent.getStringExtra("originating_address_string");
+ origAddr.origBytes = decodeHexString(addressString);
+ if (origAddr.origBytes == null) {
+ log("No address data, ignoring SCP test intent");
+ return;
+ }
+ SmsMessage sms = new SmsMessage(origAddr, envelope);
+ sms.parseSms();
+ if (sEnableCbModule) {
+ mCellBroadcastServiceManager.sendCdmaScpMessageToHandler(sms, mScpCallback);
+ } else {
+ mServiceCategoryProgramHandler.dispatchSmsMessage(sms);
+ }
+ }
+
+ @Override
+ protected void handleToggleEnable() {
+ // noop
+ }
+
+ @Override
+ protected void handleToggleDisable(Context context) {
+ // noop
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 0ca6edf0c7..61aa089516 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -126,15 +126,16 @@ public class CdmaSMSDispatcher extends SMSDispatcher {
/** {@inheritDoc} */
@Override
public void sendSms(SmsTracker tracker) {
+ int ss = mPhone.getServiceState().getState();
+
Rlog.d(TAG, "sendSms: "
+ " isIms()=" + isIms()
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
- + " SS=" + mPhone.getServiceState().getState());
+ + " SS=" + ss);
- int ss = mPhone.getServiceState().getState();
// 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);
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
index 54129119d3..3dc728cab5 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
@@ -31,6 +31,7 @@ import android.telephony.cdma.CdmaSmsCbProgramData;
import android.telephony.cdma.CdmaSmsCbProgramResults;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
import com.android.internal.telephony.WakeLockStateMachine;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
@@ -51,8 +52,9 @@ public final class CdmaServiceCategoryProgramHandler extends WakeLockStateMachin
/**
* Create a new CDMA inbound SMS handler.
*/
- CdmaServiceCategoryProgramHandler(Context context, CommandsInterface commandsInterface) {
- super("CdmaServiceCategoryProgramHandler", context, null);
+ CdmaServiceCategoryProgramHandler(Context context, CommandsInterface commandsInterface,
+ Phone phone) {
+ super("CdmaServiceCategoryProgramHandler", context, phone);
mContext = context;
mCi = commandsInterface;
}
@@ -64,9 +66,9 @@ public final class CdmaServiceCategoryProgramHandler extends WakeLockStateMachin
* @return the new SCPD handler
*/
static CdmaServiceCategoryProgramHandler makeScpHandler(Context context,
- CommandsInterface commandsInterface) {
+ CommandsInterface commandsInterface, Phone phone) {
CdmaServiceCategoryProgramHandler handler = new CdmaServiceCategoryProgramHandler(
- context, commandsInterface);
+ context, commandsInterface, phone);
handler.start();
return handler;
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
index 8b99bfb482..78e7138971 100644
--- a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
@@ -32,10 +32,10 @@ import android.os.RegistrantList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.telephony.data.IQualifiedNetworksService;
import android.telephony.data.IQualifiedNetworksServiceCallback;
import android.telephony.data.QualifiedNetworksService;
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index f86230c786..d73c900204 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -22,9 +22,9 @@ import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkRequest;
import android.os.Message;
+import android.telephony.Annotation.ApnType;
import android.telephony.Rlog;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.SparseIntArray;
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
index 9a8d3ab9c5..8403efae44 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSettingUtils.java
@@ -18,10 +18,10 @@ package com.android.internal.telephony.dataconnection;
import android.content.Context;
import android.os.PersistableBundle;
+import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.util.Log;
import com.android.internal.telephony.Phone;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 551ab5f0d2..53337fd78b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -45,6 +45,8 @@ import android.os.SystemProperties;
import android.provider.Telephony;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.DataFailCause;
import android.telephony.NetworkRegistrationInfo;
@@ -53,7 +55,6 @@ import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
@@ -251,7 +252,7 @@ public class DataConnection extends StateMachine {
private ApnSetting mApnSetting;
private ConnectionParams mConnectionParams;
private DisconnectParams mDisconnectParams;
- @DataFailCause.FailCause
+ @DataFailureCause
private int mDcFailCause;
private Phone mPhone;
@@ -260,7 +261,7 @@ public class DataConnection extends StateMachine {
private LinkProperties mLinkProperties = new LinkProperties();
private long mCreateTime;
private long mLastFailTime;
- @DataFailCause.FailCause
+ @DataFailureCause
private int mLastFailCause;
private static final String NULL_IP = "0.0.0.0";
private Object mUserData;
@@ -632,7 +633,7 @@ public class DataConnection extends StateMachine {
*
* @return Fail cause if failed to setup data connection. {@link DataFailCause#NONE} if success.
*/
- private @DataFailCause.FailCause int connect(ConnectionParams cp) {
+ private @DataFailureCause int connect(ConnectionParams cp) {
log("connect: carrier='" + mApnSetting.getEntryName()
+ "' APN='" + mApnSetting.getApnName()
+ "' proxy='" + mApnSetting.getProxyAddressAsString()
@@ -787,7 +788,7 @@ public class DataConnection extends StateMachine {
* @param cause and if no error the cause is DataFailCause.NONE
* @param sendAll is true if all contexts are to be notified
*/
- private void notifyConnectCompleted(ConnectionParams cp, @DataFailCause.FailCause int cause,
+ private void notifyConnectCompleted(ConnectionParams cp, @DataFailureCause int cause,
boolean sendAll) {
ApnContext alreadySent = null;
@@ -1385,7 +1386,7 @@ public class DataConnection extends StateMachine {
if (!la.getAddress().isAnyLocalAddress()) {
if (DBG) {
log("addr/pl=" + la.getAddress() + "/"
- + la.getNetworkPrefixLength());
+ + la.getPrefixLength());
}
linkProperties.addLinkAddress(la);
}
@@ -1688,7 +1689,7 @@ public class DataConnection extends StateMachine {
private class DcInactiveState extends State {
// Inform all contexts we've failed connecting
public void setEnterNotificationParams(ConnectionParams cp,
- @DataFailCause.FailCause int cause) {
+ @DataFailureCause int cause) {
if (VDBG) log("DcInactiveState: setEnterNotificationParams cp,cause");
mConnectionParams = cp;
mDisconnectParams = null;
@@ -1704,7 +1705,7 @@ public class DataConnection extends StateMachine {
}
// Inform all contexts of the failure cause
- public void setEnterNotificationParams(@DataFailCause.FailCause int cause) {
+ public void setEnterNotificationParams(@DataFailureCause int cause) {
mConnectionParams = null;
mDisconnectParams = null;
mDcFailCause = cause;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
index f639b36334..ef5b0ca8f3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledOverride.java
@@ -18,10 +18,10 @@ package com.android.internal.telephony.dataconnection;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.telephony.Annotation.ApnType;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index 1b05487946..2b67796df3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -27,6 +27,7 @@ import android.os.Handler;
import android.os.RegistrantList;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.telephony.Annotation.CallState;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.Rlog;
@@ -157,7 +158,7 @@ public class DataEnabledSettings {
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
- public void onCallStateChanged(@TelephonyManager.CallState int state, String phoneNumber) {
+ public void onCallStateChanged(@CallState int state, String phoneNumber) {
updateDataEnabledAndNotify(REASON_OVERRIDE_CONDITION_CHANGED);
}
};
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
index 79bd475859..66d30649f3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -44,6 +44,7 @@ import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
@@ -53,6 +54,7 @@ import android.telephony.data.IDataServiceCallback;
import android.text.TextUtils;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConfigurationManager;
import java.util.HashSet;
import java.util.List;
@@ -285,8 +287,10 @@ public class DataServiceManager extends Handler {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- phone.getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
- intentFilter, null, null);
+
+ PhoneConfigurationManager.registerForMultiSimConfigChange(
+ this, EVENT_BIND_DATA_SERVICE, null);
+
sendEmptyMessage(EVENT_BIND_DATA_SERVICE);
}
@@ -299,7 +303,7 @@ public class DataServiceManager extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_BIND_DATA_SERVICE:
- bindDataService();
+ rebindDataService();
break;
case EVENT_WATCHDOG_TIMEOUT:
handleRequestUnresponded((CellularDataServiceCallback) msg.obj);
@@ -320,41 +324,48 @@ public class DataServiceManager extends Handler {
message);
}
- private void bindDataService() {
- Intent intent = null;
- String packageName = getDataServicePackageName();
- String className = getDataServiceClassName();
- if (TextUtils.isEmpty(packageName)) {
- loge("Can't find the binding package");
- return;
- }
-
- if (TextUtils.isEmpty(className)) {
- intent = new Intent(DataService.SERVICE_INTERFACE);
- intent.setPackage(packageName);
- } else {
- ComponentName cm = new ComponentName(packageName, className);
- intent = new Intent(DataService.SERVICE_INTERFACE).setComponent(cm);
- }
-
- if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
- if (DBG) log("Service " + packageName + " already bound or being bound.");
- return;
- }
-
+ private void unbindDataService() {
// Start by cleaning up all packages that *shouldn't* have permissions.
revokePermissionsFromUnusedDataServices();
-
if (mIDataService != null && mIDataService.asBinder().isBinderAlive()) {
+ log("unbinding service");
// Remove the network availability updater and then unbind the service.
try {
mIDataService.removeDataServiceProvider(mPhone.getPhoneId());
} catch (RemoteException e) {
loge("Cannot remove data service provider. " + e);
}
+ }
+ if (mServiceConnection != null) {
mPhone.getContext().unbindService(mServiceConnection);
}
+ mIDataService = null;
+ mServiceConnection = null;
+ mTargetBindingPackageName = null;
+ mBound = false;
+ }
+
+ private void bindDataService(String packageName) {
+ if (mPhone == null || !SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())) {
+ loge("can't bindDataService with invalid phone or phoneId.");
+ return;
+ }
+
+ if (TextUtils.isEmpty(packageName)) {
+ loge("Can't find the binding package");
+ return;
+ }
+
+ Intent intent = null;
+ String className = getDataServiceClassName();
+ if (TextUtils.isEmpty(className)) {
+ intent = new Intent(DataService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+ } else {
+ ComponentName cm = new ComponentName(packageName, className);
+ intent = new Intent(DataService.SERVICE_INTERFACE).setComponent(cm);
+ }
// Then pre-emptively grant the permissions to the package we will bind.
grantPermissionsToService(packageName);
@@ -372,6 +383,19 @@ public class DataServiceManager extends Handler {
}
}
+ private void rebindDataService() {
+ String packageName = getDataServicePackageName();
+ // Do nothing if no need to rebind.
+ if (SubscriptionManager.isValidPhoneId(mPhone.getPhoneId())
+ && TextUtils.equals(packageName, mTargetBindingPackageName)) {
+ if (DBG) log("Service " + packageName + " already bound or being bound.");
+ return;
+ }
+
+ unbindDataService();
+ bindDataService(packageName);
+ }
+
@NonNull
private Set<String> getAllDataServicePackageNames() {
// Cowardly using the public PackageManager interface here.
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java b/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
index 20e13ff8a5..a68c7b4895 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcFailBringUp.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.dataconnection;
import android.content.Intent;
+import android.telephony.Annotation.DataFailureCause;
import android.telephony.DataFailCause;
import android.telephony.Rlog;
@@ -41,7 +42,7 @@ public class DcFailBringUp {
// failCause with its --ei option name and default value
static final String FAIL_CAUSE = "fail_cause";
static final int DEFAULT_FAIL_CAUSE = DataFailCause.ERROR_UNSPECIFIED;
- @DataFailCause.FailCause
+ @DataFailureCause
int mFailCause;
// suggestedRetryTime with its --ei option name and default value
@@ -62,7 +63,7 @@ public class DcFailBringUp {
}
}
- public void saveParameters(int counter, @DataFailCause.FailCause int failCause,
+ public void saveParameters(int counter, @DataFailureCause int failCause,
int suggestedRetryTime) {
mCounter = counter;
mFailCause = DataFailCause.getFailCause(failCause);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
index bf2a6af3f9..099fc5a80f 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcRequest.java
@@ -18,7 +18,7 @@ package com.android.internal.telephony.dataconnection;
import android.content.Context;
import android.net.NetworkConfig;
import android.net.NetworkRequest;
-import android.telephony.data.ApnSetting.ApnType;
+import android.telephony.Annotation.ApnType;
import java.util.HashMap;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 8575a44aa7..d2354e1077 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -66,10 +66,12 @@ import android.provider.Settings.SettingNotFoundException;
import android.provider.Telephony;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetworkType;
import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
import android.telephony.DataFailCause;
-import android.telephony.DataFailCause.FailCause;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PcoData;
import android.telephony.Rlog;
@@ -80,7 +82,6 @@ import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.telephony.data.DataProfile;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
@@ -963,7 +964,9 @@ public class DcTracker extends Handler {
private void enableMobileProvisioning() {
final Message msg = obtainMessage(DctConstants.CMD_ENABLE_MOBILE_PROVISIONING);
- msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl));
+ Bundle bundle = new Bundle(1);
+ bundle.putString(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl);
+ msg.setData(bundle);
sendMessage(msg);
}
@@ -1399,11 +1402,8 @@ public class DcTracker extends Handler {
// At this point, if data is not allowed, it must be because of the soft reasons. We
// should start to check some special conditions that data will be allowed.
if (!reasons.allowed()) {
- // If the device is on IWLAN, then all data should be unmetered. Check if the transport
- // is WLAN (for AP-assisted mode devices), or RAT equals IWLAN (for legacy mode devices)
- if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN
- || (mPhone.getTransportManager().isInLegacyMode()
- && dataRat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
+ // Check if the transport is WLAN ie wifi (for AP-assisted mode devices)
+ if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
reasons.add(DataAllowedReasonType.UNMETERED_APN);
// Or if the data is on cellular, and the APN type is determined unmetered by the
// configuration.
@@ -1874,7 +1874,7 @@ public class DcTracker extends Handler {
}
}
- boolean isPermanentFailure(@FailCause int dcFailCause) {
+ boolean isPermanentFailure(@DataFailureCause int dcFailCause) {
return (DataFailCause.isPermanentFailure(mPhone.getContext(), dcFailCause,
mPhone.getSubId())
&& (mAttached.get() == false || dcFailCause != DataFailCause.SIGNAL_LOST));
@@ -2178,13 +2178,6 @@ public class DcTracker extends Handler {
* desired power state has changed in the interim, we don't want to
* override it with an unconditional power on.
*/
-
- int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
- try {
- SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset + 1));
- } catch (RuntimeException ex) {
- log("Failed to set net.ppp.reset-by-timeout");
- }
}
/**
@@ -2232,7 +2225,7 @@ public class DcTracker extends Handler {
SystemClock.elapsedRealtime() + delay, alarmIntent);
}
- private void notifyNoData(@FailCause int lastFailCauseCode,
+ private void notifyNoData(@DataFailureCause int lastFailCauseCode,
ApnContext apnContext) {
if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
if (isPermanentFailure(lastFailCauseCode)
@@ -2334,7 +2327,7 @@ public class DcTracker extends Handler {
private void sendRequestNetworkCompleteMsg(Message message, boolean success,
@TransportType int transport,
@RequestNetworkType int requestType,
- @FailCause int cause) {
+ @DataFailureCause int cause) {
if (message == null) return;
Bundle b = message.getData();
@@ -4138,6 +4131,9 @@ public class DcTracker extends Handler {
mEmergencyApn = new ApnSetting.Builder()
.setEntryName("Emergency")
.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)
.build();
@@ -4799,8 +4795,8 @@ public class DcTracker extends Handler {
.setApn(apn.getApnName())
.setProtocolType(apn.getProtocol())
.setAuthType(apn.getAuthType())
- .setUserName(apn.getUser())
- .setPassword(apn.getPassword())
+ .setUserName(apn.getUser() == null ? "" : apn.getUser())
+ .setPassword(apn.getPassword() == null ? "" : apn.getPassword())
.setType(profileType)
.setMaxConnectionsTime(apn.getMaxConnsTime())
.setMaxConnections(apn.getMaxConns())
diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
index 382c1d61b6..fa9723a02c 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
@@ -28,9 +28,9 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.ApnType;
import android.telephony.Rlog;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.util.LocalLog;
import com.android.internal.telephony.Phone;
diff --git a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
index ff72919538..c61bbe217f 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TransportManager.java
@@ -26,10 +26,10 @@ import android.os.RegistrantList;
import android.os.SystemProperties;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.util.LocalLog;
import android.util.SparseArray;
import android.util.SparseIntArray;
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
index 69ac0b1f44..af2c61bb1c 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyNumberTracker.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
+import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
@@ -49,11 +50,11 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.ecc.nano.ProtobufEccData;
import com.android.phone.ecc.nano.ProtobufEccData.EccInfo;
-import libcore.io.IoUtils;
-
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -70,6 +71,12 @@ import java.util.zip.GZIPInputStream;
public class EmergencyNumberTracker extends Handler {
private static final String TAG = EmergencyNumberTracker.class.getSimpleName();
+ private static final int INVALID_DATABASE_VERSION = -1;
+ 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;
+
+
/** @hide */
public static boolean DBG = false;
/** @hide */
@@ -82,6 +89,7 @@ public class EmergencyNumberTracker extends Handler {
private final CommandsInterface mCi;
private final Phone mPhone;
private String mCountryIso;
+ private int mCurrentDatabaseVersion = INVALID_DATABASE_VERSION;
/**
* Indicates if the country iso is set by another subscription.
* @hide
@@ -114,6 +122,8 @@ public class EmergencyNumberTracker extends Handler {
private static final int EVENT_UPDATE_EMERGENCY_NUMBER_TEST_MODE = 3;
/** 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;
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -133,7 +143,7 @@ public class EmergencyNumberTracker extends Handler {
// 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. So we do need to update
+ // because they think they are still in that country. We don't need to update
// country change in this case.
if (TextUtils.isEmpty(countryIso)) {
return;
@@ -143,6 +153,11 @@ public class EmergencyNumberTracker extends Handler {
updateEmergencyCountryIsoAllPhones(countryIso);
}
return;
+ } else if (intent.getAction().equals(
+ TelephonyManager.ACTION_OTA_EMERGENCY_NUMBER_DB_INSTALLED)) {
+ logd("ACTION_OTA_EMERGENCY_NUMBER_DB_INSTALLED: triggered");
+ updateOtaEmergencyNumberDatabase();
+ return;
}
}
};
@@ -168,6 +183,8 @@ public class EmergencyNumberTracker extends Handler {
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
// Receive Telephony Network Country Changes
filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
+ // Receive Emergency Number OTA Update Changes
+ filter.addAction(TelephonyManager.ACTION_OTA_EMERGENCY_NUMBER_DB_INSTALLED);
mPhone.getContext().registerReceiver(mIntentReceiver, filter);
} else {
@@ -223,7 +240,10 @@ public class EmergencyNumberTracker extends Handler {
} else {
updateEmergencyNumberPrefixAndNotify((String[]) msg.obj);
}
-
+ break;
+ case EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB:
+ updateOtaEmergencyNumberListDatabaseAndNotify();
+ break;
}
}
@@ -312,6 +332,15 @@ public class EmergencyNumberTracker extends Handler {
this.obtainMessage(EVENT_UPDATE_DB_COUNTRY_ISO_CHANGED, countryIso).sendToTarget();
}
+ /**
+ * Update changed OTA Emergency Number database.
+ *
+ * @hide
+ */
+ public void updateOtaEmergencyNumberDatabase() {
+ this.obtainMessage(EVENT_UPDATE_OTA_EMERGENCY_NUMBER_DB).sendToTarget();
+ }
+
private EmergencyNumber convertEmergencyNumberFromEccInfo(EccInfo eccInfo, String countryIso) {
String phoneNumber = eccInfo.phoneNumber.trim();
if (phoneNumber.isEmpty()) {
@@ -351,25 +380,84 @@ public class EmergencyNumberTracker extends Handler {
private void cacheEmergencyDatabaseByCountry(String countryIso) {
BufferedInputStream inputStream = null;
ProtobufEccData.AllInfo allEccMessages = null;
- List<EmergencyNumber> updatedEmergencyNumberList = new ArrayList<>();
+ int assetsDatabaseVersion = INVALID_DATABASE_VERSION;
+
+ // Read the Asset emergency number database
+ List<EmergencyNumber> updatedAssetEmergencyNumberList = new ArrayList<>();
try {
inputStream = new BufferedInputStream(
mPhone.getContext().getAssets().open(EMERGENCY_NUMBER_DB_ASSETS_FILE));
allEccMessages = ProtobufEccData.AllInfo.parseFrom(readInputStreamToByteArray(
new GZIPInputStream(inputStream)));
- logd(countryIso + " emergency database is loaded. ");
+ assetsDatabaseVersion = allEccMessages.revision;
+ logd(countryIso + " asset emergency database is loaded. Ver: " + assetsDatabaseVersion
+ + " Phone Id: " + mPhone.getPhoneId());
for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) {
if (countryEccInfo.isoCode.equals(countryIso.toUpperCase())) {
for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) {
- updatedEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
+ updatedAssetEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
eccInfo, countryIso));
}
}
}
- EmergencyNumber.mergeSameNumbersInEmergencyNumberList(updatedEmergencyNumberList);
- mEmergencyNumberListFromDatabase = updatedEmergencyNumberList;
+ EmergencyNumber.mergeSameNumbersInEmergencyNumberList(updatedAssetEmergencyNumberList);
+ } catch (IOException ex) {
+ loge("Cache asset emergency database failure: " + ex);
+ } finally {
+ // close quietly by catching non-runtime exceptions.
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ // Cache OTA emergency number database
+ int otaDatabaseVersion = cacheOtaEmergencyNumberDatabase();
+
+ // Use a valid database that has higher version.
+ if (otaDatabaseVersion == INVALID_DATABASE_VERSION
+ && assetsDatabaseVersion == INVALID_DATABASE_VERSION) {
+ loge("No database available. Phone Id: " + mPhone.getPhoneId());
+ return;
+ } else if (assetsDatabaseVersion > otaDatabaseVersion) {
+ logd("Using Asset Emergency database. Version: " + assetsDatabaseVersion);
+ mCurrentDatabaseVersion = assetsDatabaseVersion;
+ mEmergencyNumberListFromDatabase = updatedAssetEmergencyNumberList;
+ } else {
+ logd("Using Ota Emergency database. Version: " + otaDatabaseVersion);
+ }
+ }
+
+ private int cacheOtaEmergencyNumberDatabase() {
+ BufferedInputStream inputStream = null;
+ ProtobufEccData.AllInfo allEccMessages = null;
+ int otaDatabaseVersion = INVALID_DATABASE_VERSION;
+
+ // Read the OTA emergency number database
+ List<EmergencyNumber> updatedOtaEmergencyNumberList = new ArrayList<>();
+ try {
+ inputStream = new BufferedInputStream(
+ new FileInputStream(new File(Environment.getDataDirectory(),
+ EMERGENCY_NUMBER_DB_OTA_FILE_PATH)));
+ allEccMessages = ProtobufEccData.AllInfo.parseFrom(readInputStreamToByteArray(
+ new GZIPInputStream(inputStream)));
+ logd(mCountryIso + " ota emergency database is loaded. Ver: " + otaDatabaseVersion);
+ otaDatabaseVersion = allEccMessages.revision;
+ for (ProtobufEccData.CountryInfo countryEccInfo : allEccMessages.countries) {
+ if (countryEccInfo.isoCode.equals(mCountryIso.toUpperCase())) {
+ for (ProtobufEccData.EccInfo eccInfo : countryEccInfo.eccs) {
+ updatedOtaEmergencyNumberList.add(convertEmergencyNumberFromEccInfo(
+ eccInfo, mCountryIso));
+ }
+ }
+ }
+ EmergencyNumber.mergeSameNumbersInEmergencyNumberList(updatedOtaEmergencyNumberList);
} catch (IOException ex) {
- loge("Cache emergency database failure: " + ex);
+ loge("Cache ota emergency database failure: " + ex);
} finally {
// close quietly by catching non-runtime exceptions.
if (inputStream != null) {
@@ -381,6 +469,14 @@ public class EmergencyNumberTracker extends Handler {
}
}
}
+
+ // Use a valid database that has higher version.
+ if (otaDatabaseVersion != INVALID_DATABASE_VERSION
+ && mCurrentDatabaseVersion < otaDatabaseVersion) {
+ mCurrentDatabaseVersion = otaDatabaseVersion;
+ mEmergencyNumberListFromDatabase = updatedOtaEmergencyNumberList;
+ }
+ return otaDatabaseVersion;
}
/**
@@ -443,6 +539,25 @@ public class EmergencyNumberTracker extends Handler {
notifyEmergencyNumberList();
}
+ private void updateOtaEmergencyNumberListDatabaseAndNotify() {
+ logd("updateOtaEmergencyNumberListDatabaseAndNotify():"
+ + " receiving Emegency Number database OTA update");
+ if (cacheOtaEmergencyNumberDatabase() != INVALID_DATABASE_VERSION) {
+ writeUpdatedEmergencyNumberListMetrics(mEmergencyNumberListFromDatabase);
+ if (!DBG) {
+ mEmergencyNumberListDatabaseLocalLog.log(
+ "updateOtaEmergencyNumberListDatabaseAndNotify:"
+ + mEmergencyNumberListFromDatabase);
+ }
+ updateEmergencyNumberList();
+ if (!DBG) {
+ mEmergencyNumberListLocalLog.log("updateOtaEmergencyNumberListDatabaseAndNotify:"
+ + mEmergencyNumberList);
+ }
+ notifyEmergencyNumberList();
+ }
+ }
+
private void updateEmergencyNumberPrefixAndNotify(String[] emergencyNumberPrefix) {
logd("updateEmergencyNumberPrefixAndNotify(): receiving emergencyNumberPrefix: "
+ Arrays.toString(emergencyNumberPrefix));
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 24f56de5ab..874bd512b7 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.telephony.euicc;
+import static android.telephony.euicc.EuiccCardManager.ResetOption;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import android.Manifest;
@@ -67,6 +69,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -143,6 +146,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
private static final int CMD_RETAIN_SUBSCRIPTIONS = 110;
private static final int CMD_GET_OTA_STATUS = 111;
private static final int CMD_START_OTA_IF_NECESSARY = 112;
+ private static final int CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS = 113;
private static boolean isEuiccCommand(int what) {
return what >= CMD_GET_EID;
@@ -302,7 +306,9 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
void onUpdateNicknameComplete(int result);
}
- /** Callback class for {@link #eraseSubscriptions}. */
+ /**
+ * Callback class for {@link #eraseSubscriptions} and {@link #eraseSubscriptionsWithOptions}.
+ */
@VisibleForTesting(visibility = PACKAGE)
public interface EraseCommandCallback extends BaseEuiccCommandCallback {
/** Called when the erase has completed (though it may have failed). */
@@ -318,6 +324,8 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
private Context mContext;
private PackageManager mPm;
+ private TelephonyManager mTm;
+ private SubscriptionManager mSm;
private final PackageMonitor mPackageMonitor = new EuiccPackageMonitor();
private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
@@ -361,6 +369,9 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
private void init(Context context) {
mContext = context;
mPm = context.getPackageManager();
+ mTm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ mSm = (SubscriptionManager)
+ context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
// Unavailable/Available both monitor for package changes and update mSelectedComponent but
// do not need to adjust the binding.
@@ -491,12 +502,19 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
sendMessage(CMD_UPDATE_SUBSCRIPTION_NICKNAME, cardId, 0 /* arg2 */, request);
}
- /** Asynchronously erase all profiles on the eUICC. */
+ /** Asynchronously erase operational profiles on the eUICC. */
@VisibleForTesting(visibility = PACKAGE)
public void eraseSubscriptions(int cardId, EraseCommandCallback callback) {
sendMessage(CMD_ERASE_SUBSCRIPTIONS, cardId, 0 /* arg2 */, callback);
}
+ /** Asynchronously erase specific profiles on the eUICC. */
+ @VisibleForTesting(visibility = PACKAGE)
+ public void eraseSubscriptionsWithOptions(
+ int cardId, @ResetOption int options, EraseCommandCallback callback) {
+ sendMessage(CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS, cardId, options, callback);
+ }
+
/** Asynchronously ensure that all profiles will be retained on the next factory reset. */
@VisibleForTesting(visibility = PACKAGE)
public void retainSubscriptions(int cardId, RetainSubscriptionsCommandCallback callback) {
@@ -523,6 +541,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
} else if (getCurrentState() != mUnavailableState) {
transitionTo(mUnavailableState);
}
+ updateSubscriptionInfoListForAllAccessibleEuiccs();
return HANDLED;
} else if (isEuiccCommand(message.what)) {
BaseEuiccCommandCallback callback = getCallback(message);
@@ -616,9 +635,9 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
isSameComponent = mSelectedComponent != null;
} else {
isSameComponent = mSelectedComponent == null
- || Objects.equals(
- bestComponent.getComponentName(),
- mSelectedComponent.getComponentName());
+ || Objects.equals(new ComponentName(bestComponent.packageName,
+ bestComponent.name),
+ new ComponentName(mSelectedComponent.packageName, mSelectedComponent.name));
}
boolean forceRebind = bestComponent != null
&& Objects.equals(bestComponent.packageName, affectedPackage);
@@ -631,6 +650,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
transitionTo(mBindingState);
}
}
+ updateSubscriptionInfoListForAllAccessibleEuiccs();
return HANDLED;
} else if (message.what == CMD_CONNECT_TIMEOUT) {
transitionTo(mAvailableState);
@@ -838,6 +858,21 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
});
break;
}
+ case CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS: {
+ mEuiccService.eraseSubscriptionsWithOptions(slotId,
+ message.arg2 /* options */,
+ new IEraseSubscriptionsCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ sendMessage(CMD_COMMAND_COMPLETE, (Runnable) () -> {
+ ((EraseCommandCallback) callback)
+ .onEraseComplete(result);
+ onCommandEnd(callback);
+ });
+ }
+ });
+ break;
+ }
case CMD_RETAIN_SUBSCRIPTIONS: {
mEuiccService.retainSubscriptionsForFactoryReset(slotId,
new IRetainSubscriptionsForFactoryResetCallback.Stub() {
@@ -929,6 +964,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
case CMD_GET_EUICC_PROFILE_INFO_LIST:
case CMD_GET_EUICC_INFO:
case CMD_ERASE_SUBSCRIPTIONS:
+ case CMD_ERASE_SUBSCRIPTIONS_WITH_OPTIONS:
case CMD_RETAIN_SUBSCRIPTIONS:
case CMD_GET_OTA_STATUS:
case CMD_START_OTA_IF_NECESSARY:
@@ -1006,7 +1042,8 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
return false;
}
Intent intent = new Intent(EuiccService.EUICC_SERVICE_INTERFACE);
- intent.setComponent(mSelectedComponent.getComponentName());
+ intent.setComponent(new ComponentName(mSelectedComponent.packageName,
+ mSelectedComponent.name));
// We bind this as a foreground service because it is operating directly on the SIM, and we
// do not want it subjected to power-savings restrictions while doing so.
return mContext.bindService(intent, this,
@@ -1030,7 +1067,7 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
if (resolveInfo.filter.getPriority() > bestPriority) {
bestPriority = resolveInfo.filter.getPriority();
- bestComponent = resolveInfo.getComponentInfo();
+ bestComponent = TelephonyUtils.getComponentInfo(resolveInfo);
}
}
}
@@ -1040,8 +1077,9 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
private static boolean isValidEuiccComponent(
PackageManager packageManager, ResolveInfo resolveInfo) {
- ComponentInfo componentInfo = resolveInfo.getComponentInfo();
- String packageName = componentInfo.getComponentName().getPackageName();
+ ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(resolveInfo);
+ String packageName = new ComponentName(componentInfo.packageName, componentInfo.name)
+ .getPackageName();
// Verify that the app is privileged (via granting of a privileged permission).
if (packageManager.checkPermission(
@@ -1136,4 +1174,17 @@ public class EuiccConnector extends StateMachine implements ServiceConnection {
pw.println("mEuiccService=" + mEuiccService);
pw.println("mActiveCommandCount=" + mActiveCommandCallbacks.size());
}
+
+ private void updateSubscriptionInfoListForAllAccessibleEuiccs() {
+ if (mTm.getCardIdForDefaultEuicc() == TelephonyManager.UNSUPPORTED_CARD_ID) {
+ // Device does not support card ID
+ mSm.requestEmbeddedSubscriptionInfoListRefresh();
+ } else {
+ for (UiccCardInfo cardInfo : mTm.getUiccCardsInfo()) {
+ if (cardInfo.isEuicc()) {
+ mSm.requestEmbeddedSubscriptionInfoListRefresh(cardInfo.getCardId());
+ }
+ }
+ }
+ }
}
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index a79c1c68dd..694db1278d 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -40,6 +40,7 @@ import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.UiccCardInfo;
import android.telephony.euicc.DownloadableSubscription;
+import android.telephony.euicc.EuiccCardManager.ResetOption;
import android.telephony.euicc.EuiccInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.euicc.EuiccManager.OtaStatus;
@@ -957,7 +958,50 @@ public class EuiccController extends IEuiccController.Stub {
}
long token = Binder.clearCallingIdentity();
try {
- mConnector.eraseSubscriptions(cardId, new EuiccConnector.EraseCommandCallback() {
+ mConnector.eraseSubscriptions(
+ cardId, new EuiccConnector.EraseCommandCallback() {
+ @Override
+ public void onEraseComplete(int result) {
+ Intent extrasIntent = new Intent();
+ final int resultCode;
+ switch (result) {
+ case EuiccService.RESULT_OK:
+ resultCode = OK;
+ refreshSubscriptionsAndSendResult(
+ callbackIntent, resultCode, extrasIntent);
+ return;
+ default:
+ resultCode = ERROR;
+ extrasIntent.putExtra(
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
+ result);
+ break;
+ }
+
+ sendResult(callbackIntent, resultCode, extrasIntent);
+ }
+
+ @Override
+ public void onEuiccServiceUnavailable() {
+ sendResult(callbackIntent, ERROR, null /* extrasIntent */);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void eraseSubscriptionsWithOptions(
+ int cardId, @ResetOption int options, PendingIntent callbackIntent) {
+ if (!callerCanWriteEmbeddedSubscriptions()) {
+ throw new SecurityException(
+ "Must have WRITE_EMBEDDED_SUBSCRIPTIONS to erase subscriptions");
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mConnector.eraseSubscriptionsWithOptions(
+ cardId, options, new EuiccConnector.EraseCommandCallback() {
@Override
public void onEraseComplete(int result) {
Intent extrasIntent = new Intent();
@@ -1203,7 +1247,7 @@ public class EuiccController extends IEuiccController.Stub {
}
private boolean supportMultiActiveSlots() {
- return mTelephonyManager.getMaxPhoneCount() > 1;
+ return mTelephonyManager.getSupportedModemCount() > 1;
}
// Checks whether the caller can manage the active embedded subscription on the SIM with the
diff --git a/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java b/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
index 51e12d89cb..052d89c245 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
@@ -39,6 +39,8 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.gsm.GsmSmsCbMessage.GeoFencingTriggerMessage;
import com.android.internal.telephony.gsm.GsmSmsCbMessage.GeoFencingTriggerMessage.CellBroadcastIdentity;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -54,6 +56,7 @@ public class GsmCellBroadcastHandler extends CellBroadcastHandler {
private static final String MESSAGE_NOT_BROADCASTED = "0";
/** This map holds incomplete concatenated messages waiting for assembly. */
+ @UnsupportedAppUsage
private final HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
new HashMap<SmsCbConcatInfo, byte[][]>(4);
@@ -308,7 +311,8 @@ public class GsmCellBroadcastHandler extends CellBroadcastHandler {
}
}
- return GsmSmsCbMessage.createSmsCbMessage(mContext, header, location, pdus);
+ return GsmSmsCbMessage.createSmsCbMessage(mContext, header, location, pdus,
+ mPhone.getPhoneId());
} catch (RuntimeException e) {
loge("Error in decoding SMS CB pdu", e);
@@ -333,6 +337,7 @@ public class GsmCellBroadcastHandler extends CellBroadcastHandler {
private final SmsCbHeader mHeader;
private final SmsCbLocation mLocation;
+ @UnsupportedAppUsage
SmsCbConcatInfo(SmsCbHeader header, SmsCbLocation location) {
mHeader = header;
mLocation = location;
@@ -368,6 +373,7 @@ public class GsmCellBroadcastHandler extends CellBroadcastHandler {
* @param cid the current Cell ID
* @return true if this message is valid for the current location; false otherwise
*/
+ @UnsupportedAppUsage
public boolean matchesLocation(String plmn, int lac, int cid) {
return mLocation.isInLocationArea(plmn, lac, cid);
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index ee7a8ea667..7b5dd0c8cd 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -23,9 +23,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Message;
+import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
-import com.android.internal.telephony.CellBroadcastServiceManager;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.Phone;
@@ -36,8 +36,7 @@ import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.VisualVoicemailSmsFilter;
import com.android.internal.telephony.uicc.UsimServiceTable;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
+import dalvik.annotation.compat.UnsupportedAppUsage;
/**
* This class broadcasts incoming SMS messages to interested apps after storing them in
@@ -50,13 +49,11 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
private final UsimDataDownloadHandler mDataDownloadHandler;
// When TEST_MODE is on we allow the test intent to trigger an SMS CB alert
- private static boolean sEnableCbModule = false;
- private static final boolean TEST_MODE = true; //STOPSHIP if true
+ private static final boolean TEST_MODE = SystemProperties.getInt("ro.debuggable", 0) == 1;
private static final String TEST_ACTION = "com.android.internal.telephony.gsm"
+ ".TEST_TRIGGER_CELL_BROADCAST";
private static final String TOGGLE_CB_MODULE = "com.android.internal.telephony.gsm"
+ ".TOGGLE_CB_MODULE";
- private CellBroadcastServiceManager mCellBroadcastServiceManager;
/**
* Create a new GSM inbound SMS handler.
@@ -66,7 +63,6 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
super("GsmInboundSmsHandler", context, storageMonitor, phone, null);
phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId());
- mCellBroadcastServiceManager = new CellBroadcastServiceManager(context, phone);
if (sEnableCbModule) {
mCellBroadcastServiceManager.enable();
} else {
@@ -75,11 +71,13 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
}
if (TEST_MODE) {
- sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction(TEST_ACTION);
- filter.addAction(TOGGLE_CB_MODULE);
- context.registerReceiver(sTestBroadcastReceiver, filter);
+ if (sTestBroadcastReceiver == null) {
+ sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TEST_ACTION);
+ filter.addAction(TOGGLE_CB_MODULE);
+ context.registerReceiver(sTestBroadcastReceiver, filter);
+ }
}
}
@@ -100,85 +98,60 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
*
* adb shell am broadcast -a com.android.internal.telephony.gsm.TOGGLE_CB_MODULE
*
- * adb shell am broadcast -a com.android.internal.telephony.gsm.TOGGLE_CB_MODULE -ez enable true
+ * adb shell am broadcast -a com.android.internal.telephony.gsm.TOGGLE_CB_MODULE \
+ * --ez enable true
*/
- private class GsmCbTestBroadcastReceiver extends BroadcastReceiver {
+ private class GsmCbTestBroadcastReceiver extends CbTestBroadcastReceiver {
+
+ GsmCbTestBroadcastReceiver() {
+ super(TEST_ACTION, TOGGLE_CB_MODULE);
+ }
+
@Override
- public void onReceive(Context context, Intent intent) {
- logd("Received test intent action=" + intent.getAction());
- if (intent.getAction() == TEST_ACTION) {
- byte[] smsPdu = intent.getByteArrayExtra("pdu");
- if (smsPdu == null) {
- String pduString = intent.getStringExtra("pdu_string");
- smsPdu = decodeHexString(pduString);
- }
- if (smsPdu == null) {
- log("No pdu or pdu_string extra, ignoring CB test intent");
- return;
- }
+ protected void handleTestAction(Intent intent) {
+ byte[] smsPdu = intent.getByteArrayExtra("pdu");
+ if (smsPdu == null) {
+ String pduString = intent.getStringExtra("pdu_string");
+ smsPdu = decodeHexString(pduString);
+ }
+ if (smsPdu == null) {
+ log("No pdu or pdu_string extra, ignoring CB test intent");
+ 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);
- if (sEnableCbModule) {
- mCellBroadcastServiceManager.sendMessageToHandler(m);
- } else {
- m.setWhat(GsmCellBroadcastHandler.EVENT_NEW_SMS_MESSAGE);
- mCellBroadcastHandler.sendMessage(m);
- }
- } else if (intent.getAction() == TOGGLE_CB_MODULE) {
- if (intent.hasExtra("enable")) {
- sEnableCbModule = intent.getBooleanExtra("enable", false);
- } else {
- sEnableCbModule = !sEnableCbModule;
- }
- if (sEnableCbModule) {
- log("enabling CB module");
- mPhone.mCi.unSetOnNewGsmBroadcastSms(mCellBroadcastHandler.getHandler());
- mCellBroadcastServiceManager.enable();
- } else {
- log("enabling legacy platform CB handling");
- mCellBroadcastServiceManager.disable();
- if (mCellBroadcastHandler == null) {
- mCellBroadcastHandler =
- GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context,
- mPhone);
- }
- mPhone.mCi.setOnNewGsmBroadcastSms(mCellBroadcastHandler.getHandler(),
- GsmCellBroadcastHandler.EVENT_NEW_SMS_MESSAGE, null);
- }
+ // 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);
+ if (sEnableCbModule) {
+ mCellBroadcastServiceManager.sendGsmMessageToHandler(m);
+ } else {
+ m.setWhat(GsmCellBroadcastHandler.EVENT_NEW_SMS_MESSAGE);
+ mCellBroadcastHandler.sendMessage(m);
}
}
- }
- private byte[] decodeHexString(String hexString) {
- if (hexString == null || hexString.length() % 2 == 1) {
- return null;
- }
- byte[] bytes = new byte[hexString.length() / 2];
- for (int i = 0; i < hexString.length(); i += 2) {
- bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
+ @Override
+ protected void handleToggleEnable() {
+ mPhone.mCi.unSetOnNewGsmBroadcastSms(mCellBroadcastHandler.getHandler());
+ mCellBroadcastServiceManager.enable();
}
- return bytes;
- }
- private byte hexToByte(String hexString) {
- int firstDigit = toDigit(hexString.charAt(0));
- int secondDigit = toDigit(hexString.charAt(1));
- return (byte) ((firstDigit << 4) + secondDigit);
- }
-
- private int toDigit(char hexChar) {
- int digit = Character.digit(hexChar, 16);
- if (digit == -1) {
- return 0;
+ @Override
+ protected void handleToggleDisable(Context context) {
+ mCellBroadcastServiceManager.disable();
+ if (mCellBroadcastHandler == null) {
+ mCellBroadcastHandler =
+ GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context,
+ mPhone);
+ }
+ mPhone.mCi.setOnNewGsmBroadcastSms(mCellBroadcastHandler.getHandler(),
+ GsmCellBroadcastHandler.EVENT_NEW_SMS_MESSAGE, null);
}
- return digit;
}
/**
@@ -187,7 +160,6 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
@Override
protected void onQuitting() {
mPhone.mCi.unSetOnNewGsmSms(getHandler());
- mCellBroadcastServiceManager.disable();
mCellBroadcastHandler.dispose();
if (DBG) log("unregistered for 3GPP SMS");
@@ -295,6 +267,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
* @param result result code indicating any error
* @param response callback message sent when operation completes.
*/
+ @UnsupportedAppUsage
@Override
protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) {
mPhone.mCi.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response);
@@ -335,12 +308,4 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP);
}
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
- if (mCellBroadcastServiceManager != null) {
- mCellBroadcastServiceManager.dump(fd, pw, args);
- }
- }
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 2a6c84806e..01ba6ad12e 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -55,6 +55,8 @@ import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.util.ArrayUtils;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -136,15 +138,25 @@ public final class GsmMmiCode extends Handler implements MmiCode {
//***** Instance Variables
+ @UnsupportedAppUsage
GsmCdmaPhone mPhone;
+ @UnsupportedAppUsage
Context mContext;
UiccCardApplication mUiccApplication;
+ @UnsupportedAppUsage
IccRecords mIccRecords;
String mAction; // One of ACTION_*
+ @UnsupportedAppUsage
String mSc; // Service Code
- String mSia, mSib, mSic; // Service Info a,b,c
+ @UnsupportedAppUsage
+ String mSia; // Service Info a
+ @UnsupportedAppUsage
+ String mSib; // Service Info b
+ @UnsupportedAppUsage
+ String mSic; // Service Info c
String mPoundString; // Entire MMI string up to and including #
+ @UnsupportedAppUsage
public String mDialingNumber;
String mPwd; // For password registration
@@ -165,6 +177,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
// See TS 22.030 6.5.2 "Structure of the MMI"
+ @UnsupportedAppUsage
static Pattern sPatternSuppService = Pattern.compile(
"((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
/* 1 2 3 4 5 6 7 8 9 10 11 12
@@ -205,6 +218,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
*
* Please see flow chart in TS 22.030 6.5.3.2
*/
+ @UnsupportedAppUsage
public static GsmMmiCode newFromDialString(String dialString, GsmCdmaPhone phone,
UiccCardApplication app) {
return newFromDialString(dialString, phone, app, null);
@@ -461,6 +475,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
/** make empty strings be null.
* Regexp returns empty strings for empty groups
*/
+ @UnsupportedAppUsage
private static String
makeEmptyNull (String s) {
if (s != null && s.length() == 0) return null;
@@ -498,6 +513,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
}
}
+ @UnsupportedAppUsage
private static int
siToServiceClass(String si) {
if (si == null || si.length() == 0) {
@@ -547,6 +563,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
}
}
+ @UnsupportedAppUsage
static boolean
isServiceCodeCallForwarding(String sc) {
return sc != null &&
@@ -556,6 +573,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
|| sc.equals(SC_CF_All_Conditional));
}
+ @UnsupportedAppUsage
static boolean
isServiceCodeCallBarring(String sc) {
Resources resource = Resources.getSystem();
@@ -600,6 +618,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
//***** Constructor
+ @UnsupportedAppUsage
public GsmMmiCode(GsmCdmaPhone phone, UiccCardApplication app) {
// The telephony unit-test cases may create GsmMmiCode's
// in secondary threads
@@ -782,6 +801,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
* In temporary mode, to invoke CLIR for a single call enter:
* " # 31 # [called number] SEND "
*/
+ @UnsupportedAppUsage
public boolean
isTemporaryModeCLIR() {
return mSc != null && mSc.equals(SC_CLIR) && mDialingNumber != null
@@ -792,6 +812,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
* returns CommandsInterface.CLIR_*
* See also isTemporaryModeCLIR()
*/
+ @UnsupportedAppUsage
public int
getCLIRMode() {
if (mSc != null && mSc.equals(SC_CLIR)) {
@@ -829,22 +850,27 @@ public final class GsmMmiCode extends Handler implements MmiCode {
return false;
}
+ @UnsupportedAppUsage
boolean isActivate() {
return mAction != null && mAction.equals(ACTION_ACTIVATE);
}
+ @UnsupportedAppUsage
boolean isDeactivate() {
return mAction != null && mAction.equals(ACTION_DEACTIVATE);
}
+ @UnsupportedAppUsage
boolean isInterrogate() {
return mAction != null && mAction.equals(ACTION_INTERROGATE);
}
+ @UnsupportedAppUsage
boolean isRegister() {
return mAction != null && mAction.equals(ACTION_REGISTER);
}
+ @UnsupportedAppUsage
boolean isErasure() {
return mAction != null && mAction.equals(ACTION_ERASURE);
}
@@ -874,6 +900,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
}
/** Process a MMI code or short code...anything that isn't a dialing number */
+ @UnsupportedAppUsage
public void
processCode() throws CallStateException {
try {
@@ -894,10 +921,10 @@ public final class GsmMmiCode extends Handler implements MmiCode {
}
} else if (mSc != null && mSc.equals(SC_CLIR)) {
Rlog.d(LOG_TAG, "processCode: is CLIR");
- if (isActivate()) {
+ if (isActivate() && !mPhone.isClirActivationAndDeactivationPrevented()) {
mPhone.mCi.setCLIR(CommandsInterface.CLIR_INVOCATION,
obtainMessage(EVENT_SET_COMPLETE, this));
- } else if (isDeactivate()) {
+ } else if (isDeactivate() && !mPhone.isClirActivationAndDeactivationPrevented()) {
mPhone.mCi.setCLIR(CommandsInterface.CLIR_SUPPRESSION,
obtainMessage(EVENT_SET_COMPLETE, this));
} else if (isInterrogate()) {
@@ -1264,6 +1291,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
return mContext.getText(com.android.internal.R.string.mmiError);
}
+ @UnsupportedAppUsage
private CharSequence getScString() {
if (mSc != null) {
if (isServiceCodeCallBarring(mSc)) {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index ea0e06a25b..0e5d83f5bc 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -28,9 +28,9 @@ 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.SmsDispatchersController;
-import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.uicc.IccRecords;
@@ -39,6 +39,8 @@ import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.util.SMSDispatcherUtil;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
@@ -48,6 +50,7 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
private AtomicReference<UiccCardApplication> mUiccApplication =
new AtomicReference<UiccCardApplication>();
+ @UnsupportedAppUsage
private GsmInboundSmsHandler mGsmInboundSmsHandler;
/** Status report received */
@@ -70,6 +73,7 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
mUiccController.unregisterForIccChanged(this);
}
+ @UnsupportedAppUsage
@Override
protected String getFormat() {
return SmsConstants.FORMAT_3GPP;
@@ -166,43 +170,38 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
}
/** {@inheritDoc} */
+ @UnsupportedAppUsage
@Override
protected void sendSms(SmsTracker tracker) {
- HashMap<String, Object> map = tracker.getData();
-
- byte pdu[] = (byte[]) map.get("pdu");
-
- if (tracker.mRetryCount > 0) {
- Rlog.d(TAG, "sendSms: "
- + " mRetryCount=" + tracker.mRetryCount
- + " mMessageRef=" + tracker.mMessageRef
- + " SS=" + mPhone.getServiceState().getState());
+ int ss = mPhone.getServiceState().getState();
- // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type
- // TP-RD (bit 2) is 1 for retry
- // and TP-MR is set to previously failed sms TP-MR
- if (((0x01 & pdu[0]) == 0x01)) {
- pdu[0] |= 0x04; // TP-RD
- pdu[1] = (byte) tracker.mMessageRef; // TP-MR
- }
- }
Rlog.d(TAG, "sendSms: "
+ " isIms()=" + isIms()
+ " mRetryCount=" + tracker.mRetryCount
+ " mImsRetry=" + tracker.mImsRetry
+ " mMessageRef=" + tracker.mMessageRef
+ " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
- + " SS=" + mPhone.getServiceState().getState());
+ + " SS=" + ss);
- int ss = mPhone.getServiceState().getState();
// 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);
return;
}
- byte smsc[] = (byte[]) map.get("smsc");
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
+ HashMap<String, Object> map = tracker.getData();
+ byte pdu[] = (byte[]) map.get("pdu");
+ byte smsc[] = (byte[]) map.get("smsc");
+ if (tracker.mRetryCount > 0) {
+ // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type
+ // TP-RD (bit 2) is 1 for retry
+ // and TP-MR is set to previously failed sms TP-MR
+ if (((0x01 & pdu[0]) == 0x01)) {
+ pdu[0] |= 0x04; // TP-RD
+ pdu[1] = (byte) tracker.mMessageRef; // TP-MR
+ }
+ }
// sms over gsm is used:
// if sms over IMS is not supported AND
diff --git a/src/java/com/android/internal/telephony/gsm/SimTlv.java b/src/java/com/android/internal/telephony/gsm/SimTlv.java
index c98b9a101a..7df1f960f6 100644
--- a/src/java/com/android/internal/telephony/gsm/SimTlv.java
+++ b/src/java/com/android/internal/telephony/gsm/SimTlv.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.gsm;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
/**
* SIM Tag-Length-Value record
* TS 102 223 Annex C
@@ -33,8 +35,10 @@ public class SimTlv
int mCurOffset;
int mCurDataOffset;
int mCurDataLength;
+ @UnsupportedAppUsage
boolean mHasValidTlvObject;
+ @UnsupportedAppUsage
public SimTlv(byte[] record, int offset, int length) {
mRecord = record;
@@ -45,6 +49,7 @@ public class SimTlv
mHasValidTlvObject = parseCurrentTlvObject();
}
+ @UnsupportedAppUsage
public boolean nextObject() {
if (!mHasValidTlvObject) return false;
mCurOffset = mCurDataOffset + mCurDataLength;
@@ -52,6 +57,7 @@ public class SimTlv
return mHasValidTlvObject;
}
+ @UnsupportedAppUsage
public boolean isValidObject() {
return mHasValidTlvObject;
}
@@ -62,6 +68,7 @@ public class SimTlv
* 0 and 0xff are invalid tag values
* valid tags range from 1 - 0xfe
*/
+ @UnsupportedAppUsage
public int getTag() {
if (!mHasValidTlvObject) return 0;
return mRecord[mCurOffset] & 0xff;
@@ -72,6 +79,7 @@ public class SimTlv
* returns null if !isValidObject()
*/
+ @UnsupportedAppUsage
public byte[] getData() {
if (!mHasValidTlvObject) return null;
diff --git a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index 6489014972..a54145938d 100755
--- a/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -28,6 +28,9 @@ import com.android.internal.telephony.uicc.AdnRecordCache;
import com.android.internal.telephony.uicc.IccConstants;
import com.android.internal.telephony.uicc.IccFileHandler;
import com.android.internal.telephony.uicc.IccUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.util.ArrayList;
/**
@@ -41,9 +44,12 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
private static final boolean DBG = true;
private ArrayList<PbrRecord> mPbrRecords;
private Boolean mIsPbrPresent;
+ @UnsupportedAppUsage
private IccFileHandler mFh;
private AdnRecordCache mAdnCache;
+ @UnsupportedAppUsage
private Object mLock = new Object();
+ @UnsupportedAppUsage
private ArrayList<AdnRecord> mPhoneBookRecords;
private ArrayList<byte[]> mIapFileRecord;
private ArrayList<byte[]> mEmailFileRecord;
@@ -119,6 +125,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
mSfiEfidTable = new SparseIntArray();
}
+ @UnsupportedAppUsage
public void reset() {
mPhoneBookRecords.clear();
mIapFileRecord = null;
@@ -131,6 +138,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
}
// Load all phonebook related EFs from the SIM.
+ @UnsupportedAppUsage
public ArrayList<AdnRecord> loadEfFilesFromUsim() {
synchronized (mLock) {
if (!mPhoneBookRecords.isEmpty()) {
@@ -660,6 +668,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants {
}
}
+ @UnsupportedAppUsage
private void log(String msg) {
if(DBG) Rlog.d(LOG_TAG, msg);
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index 8a2cf49b7c..2c3ed6d1c1 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -495,6 +495,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
}
};
+ // Used during testing, overrides the carrier services while non-empty.
+ // Array index corresponds to slot Id associated with the service package name.
+ private String[] mOverrideServices;
// Array index corresponds to slot Id associated with the service package name.
private String[] mCarrierServices;
// List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
@@ -522,6 +525,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
Context.CARRIER_CONFIG_SERVICE);
mCarrierServices = new String[numSlots];
+ mOverrideServices = new String[numSlots];
mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new)
.limit(mNumSlots).collect(Collectors.toList());
@@ -1127,9 +1131,11 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
// not specified, replace package on all slots.
for (int i = 0; i < mNumSlots; i++) {
+ mOverrideServices[i] = newPackageName;
updateBoundCarrierServices(i, newPackageName);
}
} else {
+ mOverrideServices[slotId] = newPackageName;
updateBoundCarrierServices(slotId, newPackageName);
}
@@ -1160,7 +1166,15 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
*/
private void updateBoundCarrierServices(int slotId, String newPackageName) {
if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
+ String overridePackageName = mOverrideServices[slotId];
String oldPackageName = mCarrierServices[slotId];
+ if (!TextUtils.isEmpty(overridePackageName)) {
+ // Do not allow carrier config changes to change the override package while it is
+ // in effect.
+ Log.i(TAG, "CarrierConfig change ignored for " + newPackageName + " while override"
+ + " is in effect for " + overridePackageName);
+ newPackageName = overridePackageName;
+ }
mCarrierServices[slotId] = newPackageName;
if (!TextUtils.equals(newPackageName, oldPackageName)) {
Log.i(TAG, "Carrier Config updated, binding new ImsService");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 448d8f03ba..7a863d0277 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -72,7 +72,9 @@ import android.telephony.UssdResponse;
import android.telephony.ims.ImsCallForwardInfo;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
import android.telephony.ims.ImsSsInfo;
+import android.telephony.ims.RegistrationManager;
import android.text.TextUtils;
import com.android.ims.FeatureConnector;
@@ -110,6 +112,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
/**
* {@hide}
@@ -213,7 +216,7 @@ public class ImsPhone extends ImsPhoneBase {
private final RegistrantList mSilentRedialRegistrants = new RegistrantList();
- private boolean mImsRegistered = false;
+ private int mImsRegistrationState = RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
private boolean mRoaming = false;
@@ -234,6 +237,18 @@ public class ImsPhone extends ImsPhoneBase {
this.mCurrentSubscriberUris = currentSubscriberUris;
}
+ @UnsupportedAppUsage
+ @Override
+ public void notifyCallForwardingIndicator() {
+ super.notifyCallForwardingIndicator();
+ }
+
+ @UnsupportedAppUsage
+ @Override
+ public void notifyPreciseCallStateChanged() {
+ super.notifyPreciseCallStateChanged();
+ }
+
@Override
public Uri[] getCurrentSubscriberUris() {
return mCurrentSubscriberUris;
@@ -758,6 +773,8 @@ public class ImsPhone extends ImsPhoneBase {
ResultReceiver wrappedCallback)
throws CallStateException {
+ mLastDialString = dialString;
+
// Need to make sure dialString gets parsed properly
String newDialString = PhoneNumberUtils.stripSeparators(dialString);
@@ -1530,10 +1547,12 @@ public class ImsPhone extends ImsPhoneBase {
break;
case EVENT_GET_CLIR_DONE:
- Bundle ssInfo = (Bundle) ar.result;
+ ImsSsInfo ssInfo = (ImsSsInfo) ar.result;
int[] clirInfo = null;
if (ssInfo != null) {
- clirInfo = ssInfo.getIntArray(ImsPhoneMmiCode.UT_BUNDLE_KEY_CLIR);
+ // Unfortunately callers still use the old {n,m} format of ImsSsInfo, so return
+ // that for compatibility
+ clirInfo = ssInfo.getCompatArray(ImsSsData.SS_CLIR);
}
sendResponse((Message) ar.userObj, clirInfo, ar.exception);
break;
@@ -1740,18 +1759,34 @@ public class ImsPhone extends ImsPhoneBase {
}
@Override
+ public void getImsRegistrationTech(Consumer<Integer> callback) {
+ mCT.getImsRegistrationTech(callback);
+ }
+
+ @Override
+ public void getImsRegistrationState(Consumer<Integer> callback) {
+ callback.accept(mImsRegistrationState);
+ }
+
+ @Override
public Phone getDefaultPhone() {
return mDefaultPhone;
}
@Override
public boolean isImsRegistered() {
- return mImsRegistered;
+ return mImsRegistrationState == RegistrationManager.REGISTRATION_STATE_REGISTERED;
}
+ // Not used, but not removed due to UnsupportedAppUsage tag.
@UnsupportedAppUsage
- public void setImsRegistered(boolean value) {
- mImsRegistered = value;
+ public void setImsRegistered(boolean isRegistered) {
+ mImsRegistrationState = isRegistered ? RegistrationManager.REGISTRATION_STATE_REGISTERED :
+ RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
+ }
+
+ public void setImsRegistrationState(@RegistrationManager.ImsRegistrationState int value) {
+ mImsRegistrationState = value;
}
@Override
@@ -2023,7 +2058,7 @@ public class ImsPhone extends ImsPhoneBase {
pw.println(" mIsPhoneInEcmState = " + isInEcm());
pw.println(" mEcmExitRespRegistrant = " + mEcmExitRespRegistrant);
pw.println(" mSilentRedialRegistrants = " + mSilentRedialRegistrants);
- pw.println(" mImsRegistered = " + mImsRegistered);
+ pw.println(" mImsRegistrationState = " + mImsRegistrationState);
pw.println(" mRoaming = " + mRoaming);
pw.println(" mSsnRegistrants = " + mSsnRegistrants);
pw.flush();
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index eb01a34065..f2fb7793dd 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -18,18 +18,18 @@ package com.android.internal.telephony.imsphone;
import android.annotation.UnsupportedAppUsage;
import android.telecom.ConferenceParticipant;
-import android.telephony.Rlog;
import android.telephony.DisconnectCause;
+import android.telephony.Rlog;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.util.Log;
+import com.android.ims.ImsCall;
+import com.android.ims.ImsException;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
-import com.android.ims.ImsCall;
-import com.android.ims.ImsException;
-import android.telephony.ims.ImsStreamMediaProfile;
import java.util.List;
@@ -300,6 +300,7 @@ public class ImsPhoneCall extends Call {
* @return The {@link ImsCall}.
*/
@VisibleForTesting
+ @UnsupportedAppUsage
public ImsCall
getImsCall() {
return (getFirstConnection() == null) ? null : getFirstConnection().getImsCall();
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 9199c44138..ec3f324ccf 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -65,6 +65,7 @@ import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.ImsSuppServiceNotification;
import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -125,6 +126,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
@@ -171,21 +173,26 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (DBG) log("onReceive : incoming call intent");
mOperationLocalLog.log("onIncomingCall Received");
+ if (extras == null) extras = new Bundle();
if (mImsManager == null) return;
try {
// Network initiated USSD will be treated by mImsUssdListener
- boolean isUssd = extras.getBoolean(ImsManager.EXTRA_USSD, false);
+ boolean isUssd = extras.getBoolean(MmTelFeature.EXTRA_IS_USSD, false);
+ // For compatibility purposes with older vendor implmentations.
+ isUssd |= extras.getBoolean(ImsManager.EXTRA_USSD, false);
if (isUssd) {
if (DBG) log("onReceive : USSD");
- mUssdSession = mImsManager.takeCall(c, extras, mImsUssdListener);
+ mUssdSession = mImsManager.takeCall(c, mImsUssdListener);
if (mUssdSession != null) {
mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
}
return;
}
- boolean isUnknown = extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false);
+ boolean isUnknown = extras.getBoolean(MmTelFeature.EXTRA_IS_UNKNOWN_CALL, false);
+ // For compatibility purposes with older vendor implmentations.
+ isUnknown |= extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false);
if (DBG) {
log("onReceive : isUnknown = " + isUnknown
+ " fg = " + mForegroundCall.getState()
@@ -193,7 +200,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
// Normal MT/Unknown call
- ImsCall imsCall = mImsManager.takeCall(c, extras, mImsCallListener);
+ ImsCall imsCall = mImsManager.takeCall(c, mImsCallListener);
ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
ImsPhoneCallTracker.this,
(isUnknown ? mForegroundCall : mRingingCall), isUnknown);
@@ -392,7 +399,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private int mClirMode = CommandsInterface.CLIR_DEFAULT;
@UnsupportedAppUsage
private Object mSyncHold = new Object();
-
+ @UnsupportedAppUsage
private ImsCall mUssdSession = null;
@UnsupportedAppUsage
private Message mPendingUssd = null;
@@ -408,6 +415,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private PhoneConstants.State mState = PhoneConstants.State.IDLE;
+ @UnsupportedAppUsage
private ImsManager mImsManager;
private ImsUtInterface mUtInterface;
@@ -422,6 +430,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
private boolean pendingCallInEcm = false;
@UnsupportedAppUsage
private boolean mSwitchingFgAndBgCalls = false;
+ @UnsupportedAppUsage
private ImsCall mCallExpectedToResume = null;
@UnsupportedAppUsage
private boolean mAllowEmergencyVideoCalls = false;
@@ -1891,6 +1900,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
}
+ @UnsupportedAppUsage
private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) {
for (ImsPhoneConnection conn : mConnections) {
if (conn.getImsCall() == imsCall) {
@@ -1930,6 +1940,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
}
+ @UnsupportedAppUsage
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
@@ -1939,6 +1950,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
processCallStateChange(imsCall, state, cause, false /* do not ignore state update */);
}
+ @UnsupportedAppUsage
private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause,
boolean ignoreState) {
if (DBG) {
@@ -2253,6 +2265,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
/**
* Listen to the IMS call state change
*/
+ @UnsupportedAppUsage
private ImsCall.Listener mImsCallListener = new ImsCall.Listener() {
@Override
public void onCallProgressing(ImsCall imsCall) {
@@ -2462,6 +2475,10 @@ 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 " +
@@ -3151,8 +3168,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
};
- private final ImsMmTelManager.RegistrationCallback mImsRegistrationCallback =
- new ImsMmTelManager.RegistrationCallback() {
+ private final RegistrationManager.RegistrationCallback mImsRegistrationCallback =
+ new RegistrationManager.RegistrationCallback() {
@Override
public void onRegistered(int imsRadioTech) {
@@ -3163,7 +3180,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mRegLocalLog.log("onImsConnected imsRadioTech="
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
- mPhone.setImsRegistered(true);
+ mPhone.setImsRegistrationState(
+ RegistrationManager.REGISTRATION_STATE_REGISTERED);
mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
ImsConnectionState.State.CONNECTED, null);
}
@@ -3177,7 +3195,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mRegLocalLog.log("onImsProgressing imsRadioTech="
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
- mPhone.setImsRegistered(false);
+ mPhone.setImsRegistrationState(
+ RegistrationManager.REGISTRATION_STATE_REGISTERING);
mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
ImsConnectionState.State.PROGRESSING, null);
}
@@ -3187,7 +3206,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
mRegLocalLog.log("onImsDisconnected imsRadioTech=" + imsReasonInfo);
mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
- mPhone.setImsRegistered(false);
+ mPhone.setImsRegistrationState(
+ RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
mPhone.processDisconnectReason(imsReasonInfo);
mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
ImsConnectionState.State.DISCONNECTED, imsReasonInfo);
@@ -3271,6 +3291,9 @@ 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);
mPhone.notifyImsReason(reasonInfo);
@@ -3711,6 +3734,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
}
/* package */
+ @UnsupportedAppUsage
ImsEcbm getEcbmInterface() throws ImsException {
if (mImsManager == null) {
throw getImsManagerIsNullException();
@@ -3777,6 +3801,17 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
}
+ /**
+ * Asynchronously gets the IMS registration technology for MMTEL.
+ */
+ public void getImsRegistrationTech(Consumer<Integer> callback) {
+ if (mImsManager != null) {
+ mImsManager.getRegistrationTech(callback);
+ } else {
+ callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ }
+ }
+
private void retryGetImsService() {
// The binder connection is already up. Do not try to get it again.
if (mImsManager.isServiceAvailable()) {
@@ -3786,6 +3821,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
mImsManagerConnector.connect();
}
+ @UnsupportedAppUsage
private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
throws RemoteException {
IImsVideoCallProvider imsVideoCallProvider =
@@ -4168,7 +4204,11 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
log("Resetting Capabilities...");
boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
mMmTelCapabilities = new MmTelFeature.MmTelCapabilities();
-
+ mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
+ mPhone.setImsRegistrationState(
+ RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
+ mPhone.processDisconnectReason(new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN,
+ ImsReasonInfo.CODE_UNSPECIFIED));
boolean isVideoEnabled = isVideoCallEnabled();
if (tmpIsVideoCallEnabled != isVideoEnabled) {
mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index e33bd6d98a..f98fc4076d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -66,6 +66,7 @@ public class ImsPhoneConnection extends Connection implements
private ImsPhoneCallTracker mOwner;
@UnsupportedAppUsage
private ImsPhoneCall mParent;
+ @UnsupportedAppUsage
private ImsCall mImsCall;
private Bundle mExtras = new Bundle();
private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
@@ -121,6 +122,8 @@ 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
@@ -345,7 +348,7 @@ public class ImsPhoneConnection extends Connection implements
@Override
public String getVendorDisconnectCause() {
- return null;
+ return mVendorCause;
}
@UnsupportedAppUsage
@@ -442,6 +445,7 @@ public class ImsPhoneConnection extends Connection implements
void
onHangupLocal() {
mCause = DisconnectCause.LOCAL;
+ mVendorCause = null;
}
/** Called when the connection has been disconnected */
@@ -485,6 +489,11 @@ public class ImsPhoneConnection extends Connection implements
return changed;
}
+ void
+ onRemoteDisconnect(String vendorCause) {
+ this.mVendorCause = vendorCause;
+ }
+
/**
* An incoming or outgoing call has connected
*/
@@ -719,6 +728,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
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
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 420ac82fa2..403b535ef1 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -31,7 +31,6 @@ import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncResult;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ResultReceiver;
@@ -41,6 +40,7 @@ import android.telephony.ims.ImsCallForwardInfo;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsSsData;
import android.telephony.ims.ImsSsInfo;
+import android.telephony.ims.ImsUtListener;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -155,7 +155,19 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
//***** Supplementary Service Query Bundle Keys
// Used by IMS Service layer to put supp. serv. query
// responses into the ssInfo Bundle.
+ /**
+ * @deprecated Use {@link ImsUtListener#onLineIdentificationSupplementaryServiceResponse(int,
+ * ImsSsInfo)} API instead.
+ */
+ @Deprecated
+ // Not used, only kept around to not break vendors using this key.
public static final String UT_BUNDLE_KEY_CLIR = "queryClir";
+ /**
+ * @deprecated Use {@link ImsUtListener#onLineIdentificationSupplementaryServiceResponse(int,
+ * ImsSsInfo)} API instead.
+ */
+ @Deprecated
+ // Not used, only kept around to not break vendors using this key.
public static final String UT_BUNDLE_KEY_SSINFO = "imsSsInfo";
//***** Instance Variables
@@ -870,14 +882,16 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
// NOTE: Since these supplementary services are accessed only
// via MMI codes, methods have not been added to ImsPhone.
// Only the UT interface handle is used.
- if (isActivate()) {
+ if (isActivate()
+ && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) {
try {
mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_INVOCATION,
obtainMessage(EVENT_SET_COMPLETE, this));
} catch (ImsException e) {
Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
}
- } else if (isDeactivate()) {
+ } else if (isDeactivate()
+ && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) {
try {
mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_SUPPRESSION,
obtainMessage(EVENT_SET_COMPLETE, this));
@@ -1483,12 +1497,10 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
sb.append(getErrorMessage(ar));
}
} else {
- ImsSsInfo ssInfo = null;
- if (ar.result instanceof Bundle) {
+ if (ar.result instanceof ImsSsInfo) {
Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response.");
// Response for CLIP, COLP and COLR queries.
- Bundle ssInfoResp = (Bundle) ar.result;
- ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
+ ImsSsInfo ssInfo = (ImsSsInfo) ar.result;
if (ssInfo != null) {
Rlog.d(LOG_TAG,
"onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.getStatus());
@@ -1583,15 +1595,14 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
sb.append(getImsErrorMessage(ar));
}
} else {
- Bundle ssInfo = (Bundle) ar.result;
- int[] clirInfo = ssInfo.getIntArray(UT_BUNDLE_KEY_CLIR);
- // clirInfo[0] = The 'n' parameter from TS 27.007 7.7
- // clirInfo[1] = The 'm' parameter from TS 27.007 7.7
- Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + clirInfo[0]
- + " m=" + clirInfo[1]);
+ ImsSsInfo ssInfo = (ImsSsInfo) 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());
// 'm' parameter.
- switch (clirInfo[1]) {
+ switch (ssInfo.getClirInterrogationStatus()) {
case ImsSsInfo.CLIR_STATUS_NOT_PROVISIONED:
sb.append(mContext.getText(
com.android.internal.R.string.serviceNotProvisioned));
@@ -1604,7 +1615,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
break;
case ImsSsInfo.CLIR_STATUS_TEMPORARILY_RESTRICTED:
// 'n' parameter.
- switch (clirInfo[0]) {
+ switch (ssInfo.getClirOutgoingState()) {
case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
sb.append(mContext.getText(
com.android.internal.R.string.CLIRDefaultOnNextCallOn));
@@ -1628,7 +1639,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
break;
case ImsSsInfo.CLIR_STATUS_TEMPORARILY_ALLOWED:
// 'n' parameter.
- switch (clirInfo[0]) {
+ switch (ssInfo.getClirOutgoingState()) {
case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
sb.append(mContext.getText(
com.android.internal.R.string.CLIRDefaultOffNextCallOff));
@@ -1786,8 +1797,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
case ImsSsData.SS_INTERROGATION:
if (ssData.isTypeClir()) {
Rlog.d(LOG_TAG, "CLIR INTERROGATION");
- Bundle clirInfo = new Bundle();
- clirInfo.putIntArray(UT_BUNDLE_KEY_CLIR, ssData.getSuppServiceInfoCompat());
+ ImsSsInfo clirInfo = ssData.getSuppServiceInfo().get(0);
onQueryClirComplete(new AsyncResult(null, clirInfo, ex));
} else if (ssData.isTypeCF()) {
Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
@@ -1805,11 +1815,8 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode {
onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(),
ex));
} else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) {
- int[] suppServiceInfo = ssData.getSuppServiceInfoCompat();
- ImsSsInfo ssInfo = new ImsSsInfo.Builder(suppServiceInfo[0]).build();
- Bundle clInfo = new Bundle();
- clInfo.putParcelable(UT_BUNDLE_KEY_SSINFO, ssInfo);
- onSuppSvcQueryComplete(new AsyncResult(null, clInfo, ex));
+ onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo().get(0),
+ ex));
} else if (ssData.isTypeIcb()) {
onIcbQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
} else {
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index b8dbcf03ff..26742909e8 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -72,6 +72,7 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SmsResponse;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.nano.TelephonyProto;
@@ -2469,14 +2470,14 @@ public class TelephonyMetrics {
}
// fill in complete matching information from the SIM.
- carrierIdMatchingResult.mccmnc = TextUtils.emptyIfNull(simInfo.mccMnc);
- carrierIdMatchingResult.spn = TextUtils.emptyIfNull(simInfo.spn);
- carrierIdMatchingResult.pnn = TextUtils.emptyIfNull(simInfo.plmn);
- carrierIdMatchingResult.gid1 = TextUtils.emptyIfNull(simInfo.gid1);
- carrierIdMatchingResult.gid2 = TextUtils.emptyIfNull(simInfo.gid2);
- carrierIdMatchingResult.imsiPrefix = TextUtils.emptyIfNull(simInfo.imsiPrefixPattern);
- carrierIdMatchingResult.iccidPrefix = TextUtils.emptyIfNull(simInfo.iccidPrefix);
- carrierIdMatchingResult.preferApn = TextUtils.emptyIfNull(simInfo.apn);
+ carrierIdMatchingResult.mccmnc = TelephonyUtils.emptyIfNull(simInfo.mccMnc);
+ carrierIdMatchingResult.spn = TelephonyUtils.emptyIfNull(simInfo.spn);
+ carrierIdMatchingResult.pnn = TelephonyUtils.emptyIfNull(simInfo.plmn);
+ carrierIdMatchingResult.gid1 = TelephonyUtils.emptyIfNull(simInfo.gid1);
+ carrierIdMatchingResult.gid2 = TelephonyUtils.emptyIfNull(simInfo.gid2);
+ carrierIdMatchingResult.imsiPrefix = TelephonyUtils.emptyIfNull(simInfo.imsiPrefixPattern);
+ carrierIdMatchingResult.iccidPrefix = TelephonyUtils.emptyIfNull(simInfo.iccidPrefix);
+ carrierIdMatchingResult.preferApn = TelephonyUtils.emptyIfNull(simInfo.apn);
if (simInfo.privilegeAccessRule != null) {
carrierIdMatchingResult.privilegeAccessRule =
simInfo.privilegeAccessRule.stream().toArray(String[]::new);
diff --git a/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java b/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
new file mode 100644
index 0000000000..edc3e67496
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
@@ -0,0 +1,357 @@
+/*
+ * 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.nitz;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.content.Context;
+import android.telephony.Rlog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.NitzData;
+import com.android.internal.telephony.NitzStateMachine;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TimeZoneLookupHelper;
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+
+// TODO Update this comment when NitzStateMachineImpl is deleted - it will no longer be appropriate
+// to contrast the behavior of the two implementations.
+/**
+ * A new and more testable implementation of {@link NitzStateMachine}. It is intended to replace
+ * {@link com.android.internal.telephony.NitzStateMachineImpl}.
+ *
+ * <p>This implementation differs in a number of ways:
+ * <ul>
+ * <li>It is decomposed into multiple classes that perform specific, well-defined, usually
+ * stateless, testable behaviors.
+ * </li>
+ * <li>It splits responsibility for setting the device time zone with a "time zone detection
+ * service". The time zone detection service is stateful, recording the latest suggestion from
+ * possibly multiple sources. The {@link NewNitzStateMachineImpl} must now actively signal when
+ * it has no answer for the current time zone, allowing the service to arbitrate between
+ * multiple sources without polling each of them.
+ * </li>
+ * <li>Rate limiting of NITZ signals is performed for time zone as well as time detection.</li>
+ * </ul>
+ */
+public final class NewNitzStateMachineImpl implements NitzStateMachine {
+
+ /**
+ * An interface for predicates applied to incoming NITZ signals to determine whether they must
+ * be processed. See {@link NitzSignalInputFilterPredicateFactory#create(Context, DeviceState)}
+ * for the real implementation. The use of an interface means the behavior can be tested
+ * independently and easily replaced for tests.
+ */
+ @VisibleForTesting
+ @FunctionalInterface
+ public interface NitzSignalInputFilterPredicate {
+
+ /**
+ * See {@link NitzSignalInputFilterPredicate}.
+ */
+ boolean mustProcessNitzSignal(
+ @Nullable TimestampedValue<NitzData> oldSignal,
+ @NonNull TimestampedValue<NitzData> newSignal);
+ }
+
+ /**
+ * An interface for the stateless component that generates suggestions using country and/or NITZ
+ * information. The use of an interface means the behavior can be tested independently.
+ */
+ @VisibleForTesting
+ public interface TimeZoneSuggester {
+
+ /**
+ * Generates a {@link PhoneTimeZoneSuggestion} given the information available. This method
+ * must always return a non-null {@link PhoneTimeZoneSuggestion} but that object does not
+ * have to contain a time zone if the available information is not sufficient to determine
+ * one. {@link PhoneTimeZoneSuggestion#getDebugInfo()} provides debugging / logging
+ * information explaining the choice.
+ */
+ @NonNull
+ PhoneTimeZoneSuggestion getTimeZoneSuggestion(
+ int phoneId, @Nullable String countryIsoCode,
+ @Nullable TimestampedValue<NitzData> nitzSignal);
+ }
+
+ static final String LOG_TAG = "NewNitzStateMachineImpl";
+ static final boolean DBG = true;
+
+ // Miscellaneous dependencies and helpers not related to detection state.
+ private final int mPhoneId;
+ /** Accesses global information about the device. */
+ private final DeviceState mDeviceState;
+ /** Applied to NITZ signals during input filtering. */
+ private final NitzSignalInputFilterPredicate mNitzSignalInputFilter;
+ /** Creates {@link PhoneTimeZoneSuggestion} for passing to the time zone detection service. */
+ private final TimeZoneSuggester mTimeZoneSuggester;
+ /** A facade to the time / time zone detection services. */
+ private final NewTimeServiceHelper mNewTimeServiceHelper;
+
+ // Shared detection state.
+
+ /**
+ * The last / latest NITZ signal <em>processed</em> (i.e. after input filtering). It is used for
+ * input filtering (e.g. rate limiting) and provides the NITZ information when time / time zone
+ * needs to be recalculated when something else has changed.
+ */
+ @Nullable
+ private TimestampedValue<NitzData> mLatestNitzSignal;
+
+ // Time Zone detection state.
+
+ /**
+ * Records whether the device should have a country code available via
+ * {@link DeviceState#getNetworkCountryIsoForPhone()}. Before this an NITZ signal
+ * received is (almost always) not enough to determine time zone. On test networks the country
+ * code should be available but can still be an empty string but this flag indicates that the
+ * information available is unlikely to improve.
+ */
+ private boolean mGotCountryCode = false;
+
+ /**
+ * Creates an instance for the supplied {@link Phone}.
+ */
+ public static NewNitzStateMachineImpl createInstance(@NonNull Phone phone) {
+ Objects.requireNonNull(phone);
+
+ int phoneId = phone.getPhoneId();
+ DeviceState deviceState = new DeviceStateImpl(phone);
+ TimeZoneLookupHelper timeZoneLookupHelper = new TimeZoneLookupHelper();
+ TimeZoneSuggester timeZoneSuggester =
+ new TimeZoneSuggesterImpl(deviceState, timeZoneLookupHelper);
+ NewTimeServiceHelper newTimeServiceHelper = new NewTimeServiceHelperImpl(phone);
+ NitzSignalInputFilterPredicate nitzSignalFilter =
+ NitzSignalInputFilterPredicateFactory.create(phone.getContext(), deviceState);
+ return new NewNitzStateMachineImpl(
+ phoneId, nitzSignalFilter, timeZoneSuggester, newTimeServiceHelper, deviceState);
+ }
+
+ /**
+ * Creates an instance using the supplied components. Used during tests to supply fakes.
+ * See {@link #createInstance(Phone)}
+ */
+ @VisibleForTesting
+ public NewNitzStateMachineImpl(int phoneId,
+ @NonNull NitzSignalInputFilterPredicate nitzSignalInputFilter,
+ @NonNull TimeZoneSuggester timeZoneSuggester,
+ @NonNull NewTimeServiceHelper newTimeServiceHelper, @NonNull DeviceState deviceState) {
+ mPhoneId = phoneId;
+ mTimeZoneSuggester = Objects.requireNonNull(timeZoneSuggester);
+ mNewTimeServiceHelper = Objects.requireNonNull(newTimeServiceHelper);
+ mDeviceState = Objects.requireNonNull(deviceState);
+ mNitzSignalInputFilter = Objects.requireNonNull(nitzSignalInputFilter);
+ }
+
+ @Override
+ public void handleNetworkAvailable() {
+ // Assume any previous NITZ signals received are now invalid.
+ mLatestNitzSignal = null;
+
+ String countryIsoCode =
+ mGotCountryCode ? mDeviceState.getNetworkCountryIsoForPhone() : null;
+
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkAvailable: countryIsoCode=" + countryIsoCode
+ + ", mLatestNitzSignal=" + mLatestNitzSignal);
+ }
+
+ String reason = "handleNetworkAvailable()";
+
+ // Generate a new time zone suggestion and update the service as needed.
+ doTimeZoneDetection(countryIsoCode, null /* nitzSignal */, reason);
+
+ // Generate a new time suggestion and update the service as needed.
+ doTimeDetection(null /* nitzSignal */, reason);
+ }
+
+ @Override
+ public void handleNetworkCountryCodeSet(boolean countryChanged) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkCountryCodeSet: countryChanged=" + countryChanged
+ + ", mLatestNitzSignal=" + mLatestNitzSignal);
+ }
+
+ mGotCountryCode = true;
+
+ // Generate a new time zone suggestion and update the service as needed.
+ String countryIsoCode = mDeviceState.getNetworkCountryIsoForPhone();
+ doTimeZoneDetection(countryIsoCode, mLatestNitzSignal,
+ "handleNetworkCountryCodeSet(" + countryChanged + ")");
+ }
+
+ @Override
+ public void handleNetworkCountryCodeUnavailable() {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNetworkCountryCodeUnavailable:"
+ + " mLatestNitzSignal=" + mLatestNitzSignal);
+ }
+ mGotCountryCode = false;
+
+ // Generate a new time zone suggestion and update the service as needed.
+ doTimeZoneDetection(null /* countryIsoCode */, mLatestNitzSignal,
+ "handleNetworkCountryCodeUnavailable()");
+ }
+
+ @Override
+ public void handleNitzReceived(@NonNull TimestampedValue<NitzData> nitzSignal) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleNitzReceived: nitzSignal=" + nitzSignal);
+ }
+ Objects.requireNonNull(nitzSignal);
+
+ // Perform input filtering to filter bad data and avoid processing signals too often.
+ TimestampedValue<NitzData> previousNitzSignal = mLatestNitzSignal;
+ if (!mNitzSignalInputFilter.mustProcessNitzSignal(previousNitzSignal, nitzSignal)) {
+ return;
+ }
+
+ // Always store the latest valid NITZ signal to be processed.
+ mLatestNitzSignal = nitzSignal;
+
+ String reason = "handleNitzReceived(" + nitzSignal + ")";
+
+ // Generate a new time zone suggestion and update the service as needed.
+ String countryIsoCode =
+ mGotCountryCode ? mDeviceState.getNetworkCountryIsoForPhone() : null;
+ doTimeZoneDetection(countryIsoCode, nitzSignal, reason);
+
+ // Generate a new time suggestion and update the service as needed.
+ doTimeDetection(nitzSignal, reason);
+ }
+
+ @Override
+ public void handleAirplaneModeChanged(boolean on) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "handleAirplaneModeChanged: on=" + on);
+ }
+
+ // Treat entry / exit from airplane mode as a strong signal that the user wants to clear
+ // cached state. If the user really is boarding a plane they won't want cached state from
+ // before their flight influencing behavior.
+ //
+ // State is cleared on entry AND exit: on entry because the detection code shouldn't be
+ // opinionated while in airplane mode, and on exit to avoid any unexpected signals received
+ // while in airplane mode from influencing behavior afterwards.
+ //
+ // After clearing detection state, the time zone detection should work out from first
+ // principles what the time / time zone is. This assumes calls like handleNetworkAvailable()
+ // will be made after airplane mode is re-enabled as the device re-establishes network
+ // connectivity.
+
+ // Clear shared state.
+ mLatestNitzSignal = null;
+
+ // Clear time zone detection state.
+ mGotCountryCode = false;
+
+ String reason = "handleAirplaneModeChanged(" + on + ")";
+
+ // Generate a new time zone suggestion and update the service as needed.
+ doTimeZoneDetection(null /* countryIsoCode */, null /* nitzSignal */,
+ reason);
+
+ // Generate a new time suggestion and update the service as needed.
+ doTimeDetection(null /* nitzSignal */, reason);
+ }
+
+ /**
+ * Perform a round of time zone detection and notify the time zone detection service as needed.
+ */
+ private void doTimeZoneDetection(
+ @Nullable String countryIsoCode, @Nullable TimestampedValue<NitzData> nitzSignal,
+ @NonNull String reason) {
+ try {
+ Objects.requireNonNull(reason);
+
+ PhoneTimeZoneSuggestion suggestion =
+ mTimeZoneSuggester.getTimeZoneSuggestion(mPhoneId, countryIsoCode, nitzSignal);
+ suggestion.addDebugInfo("Detection reason=" + reason);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "doTimeZoneDetection: countryIsoCode=" + countryIsoCode
+ + ", nitzSignal=" + nitzSignal + ", suggestion=" + suggestion
+ + ", reason=" + reason);
+ }
+ mNewTimeServiceHelper.maybeSuggestDeviceTimeZone(suggestion);
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "doTimeZoneDetection: Exception thrown"
+ + " mPhoneId=" + mPhoneId
+ + ", countryIsoCode=" + countryIsoCode
+ + ", nitzSignal=" + nitzSignal
+ + ", reason=" + reason
+ + ", ex=" + ex, ex);
+ }
+ }
+
+ /**
+ * Perform a round of time detection and notify the time detection service as needed.
+ */
+ private void doTimeDetection(@Nullable TimestampedValue<NitzData> nitzSignal,
+ @NonNull String reason) {
+ try {
+ Objects.requireNonNull(reason);
+ if (nitzSignal == null) {
+ // Do nothing to withdraw previous suggestions: the service currently does not
+ // support withdrawing suggestions.
+ return;
+ }
+
+ Objects.requireNonNull(nitzSignal.getValue());
+
+ TimestampedValue<Long> newNitzTime = new TimestampedValue<>(
+ nitzSignal.getReferenceTimeMillis(),
+ nitzSignal.getValue().getCurrentTimeInMillis());
+ PhoneTimeSuggestion timeSuggestion = new PhoneTimeSuggestion(mPhoneId, newNitzTime);
+ timeSuggestion.addDebugInfo("doTimeDetection: NITZ signal used"
+ + " nitzSignal=" + nitzSignal
+ + ", newNitzTime=" + newNitzTime
+ + ", reason=" + reason);
+ mNewTimeServiceHelper.suggestDeviceTime(timeSuggestion);
+ } catch (RuntimeException ex) {
+ Rlog.e(LOG_TAG, "doTimeDetection: Exception thrown"
+ + " mPhoneId=" + mPhoneId
+ + ", nitzSignal=" + nitzSignal
+ + ", reason=" + reason
+ + ", ex=" + ex, ex);
+ }
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ pw.println(" NewNitzStateMachineImpl.mLatestNitzSignal=" + mLatestNitzSignal);
+ pw.println(" NewNitzStateMachineImpl.mGotCountryCode=" + mGotCountryCode);
+ mNewTimeServiceHelper.dumpState(pw);
+ pw.flush();
+ }
+
+ @Override
+ public void dumpLogs(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+ mNewTimeServiceHelper.dumpLogs(ipw);
+ }
+
+ @Nullable
+ public NitzData getCachedNitzData() {
+ return mLatestNitzSignal != null ? mLatestNitzSignal.getValue() : null;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelper.java b/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelper.java
new file mode 100644
index 0000000000..7984c9744a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelper.java
@@ -0,0 +1,60 @@
+/*
+ * 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.nitz;
+
+import android.annotation.NonNull;
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TimeDetector;
+
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface to various time / time zone detection behaviors that should be centralized into
+ * new services.
+ */
+public interface NewTimeServiceHelper {
+
+ /**
+ * Suggests the time to the {@link TimeDetector}.
+ *
+ * @param suggestion the time
+ */
+ void suggestDeviceTime(@NonNull PhoneTimeSuggestion suggestion);
+
+ /**
+ * Suggests the time zone to the time zone detector.
+ *
+ * <p>NOTE: The PhoneTimeZoneSuggestion cannot be null. The zoneId it contains can be null to
+ * indicate there is no active suggestion; this can be used to clear a previous suggestion.
+ *
+ * @param suggestion the time zone
+ */
+ void maybeSuggestDeviceTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion);
+
+ /**
+ * Dumps any logs held to the supplied writer.
+ */
+ void dumpLogs(IndentingPrintWriter ipw);
+
+ /**
+ * Dumps internal state such as field values.
+ */
+ void dumpState(PrintWriter pw);
+}
diff --git a/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java b/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java
new file mode 100644
index 0000000000..337423bccc
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java
@@ -0,0 +1,122 @@
+/*
+ * 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.nitz;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.app.timedetector.TimeDetector;
+import android.content.Context;
+import android.util.LocalLog;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
+import com.android.internal.telephony.nitz.service.TimeZoneDetectionService;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link NewTimeServiceHelper}.
+ */
+public final class NewTimeServiceHelperImpl implements NewTimeServiceHelper {
+
+ private final int mPhoneId;
+ private final TimeDetector mTimeDetector;
+ private final TimeZoneDetectionService mTimeZoneDetector;
+
+ private final LocalLog mTimeZoneLog = new LocalLog(30);
+ private final LocalLog mTimeLog = new LocalLog(30);
+
+ /**
+ * Records the last time zone suggestion made. Used to avoid sending duplicate suggestions to
+ * the time zone service. The value can be {@code null} to indicate no previous suggestion has
+ * been made.
+ */
+ @NonNull
+ private PhoneTimeZoneSuggestion mLastSuggestedTimeZone;
+
+ public NewTimeServiceHelperImpl(@NonNull Phone phone) {
+ mPhoneId = phone.getPhoneId();
+ Context context = Objects.requireNonNull(phone.getContext());
+ mTimeDetector = Objects.requireNonNull(context.getSystemService(TimeDetector.class));
+ mTimeZoneDetector = Objects.requireNonNull(TimeZoneDetectionService.getInstance(context));
+ }
+
+ @Override
+ public void suggestDeviceTime(@NonNull PhoneTimeSuggestion phoneTimeSuggestion) {
+ mTimeLog.log("Suggesting system clock update: " + phoneTimeSuggestion);
+
+ // 3 nullness assertions in 1 line
+ Objects.requireNonNull(phoneTimeSuggestion.getUtcTime().getValue());
+
+ TimestampedValue<Long> utcTime = phoneTimeSuggestion.getUtcTime();
+ TelephonyMetrics.getInstance().writeNITZEvent(mPhoneId, utcTime.getValue());
+ mTimeDetector.suggestPhoneTime(phoneTimeSuggestion);
+ }
+
+ @Override
+ public void maybeSuggestDeviceTimeZone(@NonNull PhoneTimeZoneSuggestion newSuggestion) {
+ Objects.requireNonNull(newSuggestion);
+
+ PhoneTimeZoneSuggestion oldSuggestion = mLastSuggestedTimeZone;
+ if (shouldSendNewTimeZoneSuggestion(oldSuggestion, newSuggestion)) {
+ mTimeZoneLog.log("Suggesting time zone update: " + newSuggestion);
+ mTimeZoneDetector.suggestPhoneTimeZone(newSuggestion);
+ mLastSuggestedTimeZone = newSuggestion;
+ }
+ }
+
+ private static boolean shouldSendNewTimeZoneSuggestion(
+ @Nullable PhoneTimeZoneSuggestion oldSuggestion,
+ @NonNull PhoneTimeZoneSuggestion newSuggestion) {
+ if (oldSuggestion == null) {
+ // No previous suggestion.
+ return true;
+ }
+ // This code relies on PhoneTimeZoneSuggestion.equals() to only check meaningful fields.
+ return !Objects.equals(newSuggestion, oldSuggestion);
+ }
+
+ @Override
+ public void dumpLogs(IndentingPrintWriter ipw) {
+ ipw.println("NewTimeServiceHelperImpl:");
+ ipw.increaseIndent();
+ ipw.println("Time Logs:");
+ ipw.increaseIndent();
+ mTimeLog.dump(ipw);
+ ipw.decreaseIndent();
+
+ ipw.println("Time zone Logs:");
+ ipw.increaseIndent();
+ mTimeZoneLog.dump(ipw);
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+
+ // TODO Remove this line when the service moves to the system server.
+ mTimeZoneDetector.dumpLogs(ipw);
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ pw.println(" NewTimeServiceHelperImpl.mLastSuggestedTimeZone=" + mLastSuggestedTimeZone);
+ mTimeZoneDetector.dumpState(pw);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactory.java b/src/java/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactory.java
new file mode 100644
index 0000000000..58cbaaa71d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactory.java
@@ -0,0 +1,273 @@
+/*
+ * 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.nitz;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.telephony.Rlog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.NitzData;
+import com.android.internal.telephony.NitzStateMachine.DeviceState;
+import com.android.internal.telephony.nitz.NewNitzStateMachineImpl.NitzSignalInputFilterPredicate;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A factory class for the {@link NitzSignalInputFilterPredicate} instance used by
+ * {@link NewNitzStateMachineImpl}. This class is exposed for testing and provides access to various
+ * internal components.
+ */
+@VisibleForTesting
+public final class NitzSignalInputFilterPredicateFactory {
+
+ private static final String LOG_TAG = NewNitzStateMachineImpl.LOG_TAG;
+ private static final boolean DBG = NewNitzStateMachineImpl.DBG;
+ private static final String WAKELOCK_TAG = "NitzSignalInputFilterPredicateFactory";
+
+ private NitzSignalInputFilterPredicateFactory() {}
+
+ /**
+ * Returns the real {@link NitzSignalInputFilterPredicate} to use for NITZ signal input
+ * filtering.
+ */
+ @NonNull
+ public static NitzSignalInputFilterPredicate create(
+ @NonNull Context context, @NonNull DeviceState deviceState) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(deviceState);
+
+ TrivalentPredicate[] components = new TrivalentPredicate[] {
+ // Disables NITZ processing entirely: can return false or null.
+ createIgnoreNitzPropertyCheck(deviceState),
+ // Filters bad reference times from new signals: can return false or null.
+ createBogusElapsedRealtimeCheck(context, deviceState),
+ // Ensures oldSignal == null is always processed: can return true or null.
+ createNoOldSignalCheck(),
+ // Adds rate limiting: can return true or false.
+ createRateLimitCheck(deviceState),
+ };
+ return new NitzSignalInputFilterPredicateImpl(components);
+ }
+
+ /**
+ * A filtering function that can give a {@code true} (must process), {@code false} (must not
+ * process) and a {@code null} (no opinion) response given a previous NITZ signal and a new
+ * signal. The previous signal may be {@code null} (unless ruled out by a prior
+ * {@link TrivalentPredicate}).
+ */
+ @VisibleForTesting
+ @FunctionalInterface
+ public interface TrivalentPredicate {
+
+ /**
+ * See {@link TrivalentPredicate}.
+ */
+ @Nullable
+ Boolean mustProcessNitzSignal(
+ @Nullable TimestampedValue<NitzData> previousSignal,
+ @NonNull TimestampedValue<NitzData> newSignal);
+ }
+
+ /**
+ * Returns a {@link TrivalentPredicate} function that implements a check for the
+ * "gsm.ignore-nitz" Android system property. The function can return {@code false} or
+ * {@code null}.
+ */
+ @VisibleForTesting
+ @NonNull
+ public static TrivalentPredicate createIgnoreNitzPropertyCheck(
+ @NonNull DeviceState deviceState) {
+ return (oldSignal, newSignal) -> {
+ boolean ignoreNitz = deviceState.getIgnoreNitz();
+ if (ignoreNitz) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "mustProcessNitzSignal: Not processing NITZ signal because"
+ + " gsm.ignore-nitz is set");
+ }
+ return false;
+ }
+ return null;
+ };
+ }
+
+ /**
+ * Returns a {@link TrivalentPredicate} function that implements a check for a bad reference
+ * time associated with {@code newSignal}. The function can return {@code false} or
+ * {@code null}.
+ */
+ @VisibleForTesting
+ @NonNull
+ public static TrivalentPredicate createBogusElapsedRealtimeCheck(
+ @NonNull Context context, @NonNull DeviceState deviceState) {
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ final WakeLock wakeLock =
+ powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+
+ return (oldSignal, newSignal) -> {
+ Objects.requireNonNull(newSignal);
+
+ // Validate the newSignal to reject obviously bogus elapsedRealtime values.
+ try {
+ // Acquire the wake lock as we are reading the elapsed realtime clock below.
+ wakeLock.acquire();
+
+ long elapsedRealtime = deviceState.elapsedRealtime();
+ long millisSinceNitzReceived = elapsedRealtime - newSignal.getReferenceTimeMillis();
+ if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "mustProcessNitzSignal: Not processing NITZ signal"
+ + " because unexpected elapsedRealtime=" + elapsedRealtime
+ + " nitzSignal=" + newSignal);
+ }
+ return false;
+ }
+ return null;
+ } finally {
+ wakeLock.release();
+ }
+ };
+ }
+
+ /**
+ * Returns a {@link TrivalentPredicate} function that implements a check for a {@code null}
+ * {@code oldSignal} (indicating there's no history). The function can return {@code true}
+ * or {@code null}.
+ */
+ @VisibleForTesting
+ @NonNull
+ public static TrivalentPredicate createNoOldSignalCheck() {
+ // Always process a signal when there was no previous signal.
+ return (oldSignal, newSignal) -> oldSignal == null ? true : null;
+ }
+
+ /**
+ * Returns a {@link TrivalentPredicate} function that implements filtering using
+ * {@code oldSignal} and {@code newSignal}. The function can return {@code true} or
+ * {@code false} and so is intended as the final function in a chain.
+ *
+ * Function detail: if an NITZ signal received that is too similar to a previous one
+ * it should be disregarded if it's received within a configured time period.
+ * The general contract for {@link TrivalentPredicate} allows {@code previousSignal} to be
+ * {@code null}, but previous functions are expected to prevent it in this case.
+ */
+ @VisibleForTesting
+ @NonNull
+ public static TrivalentPredicate createRateLimitCheck(@NonNull DeviceState deviceState) {
+ return new TrivalentPredicate() {
+ @Override
+ @NonNull
+ public Boolean mustProcessNitzSignal(
+ @NonNull TimestampedValue<NitzData> previousSignal,
+ @NonNull TimestampedValue<NitzData> newSignal) {
+ Objects.requireNonNull(newSignal);
+ Objects.requireNonNull(newSignal.getValue());
+ Objects.requireNonNull(previousSignal);
+ Objects.requireNonNull(previousSignal.getValue());
+
+ NitzData newNitzData = newSignal.getValue();
+ NitzData previousNitzData = previousSignal.getValue();
+
+ // Compare the discrete NitzData fields associated with local time offset. Any
+ // difference and we should process the signal regardless of how recent the last one
+ // was.
+ if (!offsetInfoIsTheSame(previousNitzData, newNitzData)) {
+ return true;
+ }
+
+ // Now check the continuous NitzData field (time) to see if it is sufficiently
+ // different.
+ int nitzUpdateSpacing = deviceState.getNitzUpdateSpacingMillis();
+ int nitzUpdateDiff = deviceState.getNitzUpdateDiffMillis();
+
+ // Calculate the elapsed time between the new signal and the last signal.
+ long elapsedRealtimeSinceLastSaved = newSignal.getReferenceTimeMillis()
+ - previousSignal.getReferenceTimeMillis();
+
+ // Calculate the UTC difference between the time the two signals hold.
+ long utcTimeDifferenceMillis = newNitzData.getCurrentTimeInMillis()
+ - previousNitzData.getCurrentTimeInMillis();
+
+ // Ideally the difference between elapsedRealtimeSinceLastSaved and
+ // utcTimeDifferenceMillis would be zero.
+ long millisGainedOrLost = Math
+ .abs(utcTimeDifferenceMillis - elapsedRealtimeSinceLastSaved);
+
+ if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing
+ || millisGainedOrLost > nitzUpdateDiff) {
+ return true;
+ }
+
+ if (DBG) {
+ Rlog.d(LOG_TAG, "mustProcessNitzSignal: NITZ signal filtered"
+ + " previousSignal=" + previousSignal
+ + ", newSignal=" + newSignal
+ + ", nitzUpdateSpacing=" + nitzUpdateSpacing
+ + ", nitzUpdateDiff=" + nitzUpdateDiff);
+ }
+ return false;
+ }
+
+ private boolean offsetInfoIsTheSame(NitzData one, NitzData two) {
+ return Objects.equals(two.getDstAdjustmentMillis(), one.getDstAdjustmentMillis())
+ && Objects.equals(
+ two.getEmulatorHostTimeZone(), one.getEmulatorHostTimeZone())
+ && two.getLocalOffsetMillis() == one.getLocalOffsetMillis();
+ }
+ };
+ }
+
+ /**
+ * An implementation of {@link NitzSignalInputFilterPredicate} that tries a series of
+ * {@link TrivalentPredicate} instances until one provides a {@code true} or {@code false}
+ * response indicating that the {@code newSignal} should be processed or not. If all return
+ * {@code null} then a default of {@code true} is returned.
+ */
+ @VisibleForTesting
+ public static class NitzSignalInputFilterPredicateImpl
+ implements NitzSignalInputFilterPredicate {
+
+ @NonNull
+ private final TrivalentPredicate[] mComponents;
+
+ @VisibleForTesting
+ public NitzSignalInputFilterPredicateImpl(@NonNull TrivalentPredicate[] components) {
+ this.mComponents = Arrays.copyOf(components, components.length);
+ }
+
+ @Override
+ public boolean mustProcessNitzSignal(@Nullable TimestampedValue<NitzData> oldSignal,
+ @NonNull TimestampedValue<NitzData> newSignal) {
+ Objects.requireNonNull(newSignal);
+
+ for (TrivalentPredicate component : mComponents) {
+ Boolean result = component.mustProcessNitzSignal(oldSignal, newSignal);
+ if (result != null) {
+ return result;
+ }
+ }
+ // The default is to process.
+ return true;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/nitz/TimeZoneSuggesterImpl.java b/src/java/com/android/internal/telephony/nitz/TimeZoneSuggesterImpl.java
new file mode 100644
index 0000000000..c5d9df6bd8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/TimeZoneSuggesterImpl.java
@@ -0,0 +1,288 @@
+/*
+ * 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.nitz;
+
+import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_DEFAULT_BOOSTED;
+import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS;
+import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_MULTIPLE_ZONES_SAME_OFFSET;
+import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_SINGLE_ZONE;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.createEmptySuggestion;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.NitzData;
+import com.android.internal.telephony.NitzStateMachine.DeviceState;
+import com.android.internal.telephony.TimeZoneLookupHelper;
+import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
+import com.android.internal.telephony.nitz.NewNitzStateMachineImpl.TimeZoneSuggester;
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
+
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link TimeZoneSuggester}.
+ */
+@VisibleForTesting
+public class TimeZoneSuggesterImpl implements TimeZoneSuggester {
+
+ private static final String LOG_TAG = NewNitzStateMachineImpl.LOG_TAG;
+
+ private final DeviceState mDeviceState;
+ private final TimeZoneLookupHelper mTimeZoneLookupHelper;
+
+ @VisibleForTesting
+ public TimeZoneSuggesterImpl(
+ @NonNull DeviceState deviceState, @NonNull TimeZoneLookupHelper timeZoneLookupHelper) {
+ mDeviceState = Objects.requireNonNull(deviceState);
+ mTimeZoneLookupHelper = Objects.requireNonNull(timeZoneLookupHelper);
+ }
+
+ @Override
+ @NonNull
+ public PhoneTimeZoneSuggestion getTimeZoneSuggestion(int phoneId,
+ @Nullable String countryIsoCode, @Nullable TimestampedValue<NitzData> nitzSignal) {
+ try {
+ // Check for overriding NITZ-based signals from Android running in an emulator.
+ PhoneTimeZoneSuggestion overridingSuggestion = null;
+ if (nitzSignal != null) {
+ NitzData nitzData = nitzSignal.getValue();
+ if (nitzData.getEmulatorHostTimeZone() != null) {
+ overridingSuggestion = new PhoneTimeZoneSuggestion(phoneId);
+ overridingSuggestion.setZoneId(nitzData.getEmulatorHostTimeZone().getID());
+ overridingSuggestion.setMatchType(PhoneTimeZoneSuggestion.EMULATOR_ZONE_ID);
+ overridingSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ overridingSuggestion.addDebugInfo("Emulator time zone override: " + nitzData);
+ }
+ }
+
+ PhoneTimeZoneSuggestion suggestion;
+ if (overridingSuggestion != null) {
+ suggestion = overridingSuggestion;
+ } else if (countryIsoCode == null) {
+ if (nitzSignal == null) {
+ suggestion = createEmptySuggestion(phoneId,
+ "getTimeZoneSuggestion: nitzSignal=null, countryIsoCode=null");
+ } else {
+ // NITZ only - wait until we have a country.
+ suggestion = createEmptySuggestion(phoneId, "getTimeZoneSuggestion:"
+ + " nitzSignal=" + nitzSignal + ", countryIsoCode=null");
+ }
+ } else { // countryIsoCode != null
+ if (nitzSignal == null) {
+ if (countryIsoCode.isEmpty()) {
+ // This is assumed to be a test network with no NITZ data to go on.
+ suggestion = createEmptySuggestion(phoneId,
+ "getTimeZoneSuggestion: nitzSignal=null, countryIsoCode=\"\"");
+ } else {
+ // Country only
+ suggestion = findTimeZoneFromNetworkCountryCode(
+ phoneId, countryIsoCode, mDeviceState.currentTimeMillis());
+ }
+ } else { // nitzSignal != null
+ if (countryIsoCode.isEmpty()) {
+ // We have been told we have a country code but it's empty. This is most
+ // likely because we're on a test network that's using a bogus MCC
+ // (eg, "001"). Obtain a TimeZone based only on the NITZ parameters: without
+ // a country it will be arbitrary, but it should at least have the correct
+ // offset.
+ suggestion = findTimeZoneForTestNetwork(phoneId, nitzSignal);
+ } else {
+ // We have both NITZ and Country code.
+ suggestion = findTimeZoneFromCountryAndNitz(
+ phoneId, countryIsoCode, nitzSignal);
+ }
+ }
+ }
+
+ // Ensure the return value is never null.
+ Objects.requireNonNull(suggestion);
+
+ return suggestion;
+ } catch (RuntimeException e) {
+ // This would suggest a coding error. Log at a high level and try to avoid leaving the
+ // device in a bad state by making an "empty" suggestion.
+ String message = "getTimeZoneSuggestion: Error during lookup: "
+ + " countryIsoCode=" + countryIsoCode
+ + ", nitzSignal=" + nitzSignal
+ + ", e=" + e.getMessage();
+ PhoneTimeZoneSuggestion errorSuggestion = createEmptySuggestion(phoneId, message);
+ errorSuggestion.addDebugInfo(message);
+ Rlog.w(LOG_TAG, message, e);
+ return errorSuggestion;
+ }
+ }
+
+ /**
+ * Creates a {@link PhoneTimeZoneSuggestion} using only NITZ. This happens when the device
+ * is attached to a test cell with an unrecognized MCC. In these cases we try to return a
+ * suggestion for an arbitrary time zone that matches the NITZ offset information.
+ */
+ @NonNull
+ private PhoneTimeZoneSuggestion findTimeZoneForTestNetwork(
+ int phoneId, @NonNull TimestampedValue<NitzData> nitzSignal) {
+ Objects.requireNonNull(nitzSignal);
+ NitzData nitzData = Objects.requireNonNull(nitzSignal.getValue());
+
+ PhoneTimeZoneSuggestion result = new PhoneTimeZoneSuggestion(phoneId);
+ result.addDebugInfo("findTimeZoneForTestNetwork: nitzSignal=" + nitzSignal);
+ TimeZoneLookupHelper.OffsetResult lookupResult =
+ mTimeZoneLookupHelper.lookupByNitz(nitzData);
+ if (lookupResult == null) {
+ result.addDebugInfo("findTimeZoneForTestNetwork: No zone found");
+ } else {
+ result.setZoneId(lookupResult.getTimeZone().getID());
+ result.setMatchType(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY);
+ int quality = lookupResult.getIsOnlyMatch() ? PhoneTimeZoneSuggestion.SINGLE_ZONE
+ : PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
+ result.setQuality(quality);
+ result.addDebugInfo("findTimeZoneForTestNetwork: lookupResult=" + lookupResult);
+ }
+ return result;
+ }
+
+ /**
+ * Creates a {@link PhoneTimeZoneSuggestion} using network country code and NITZ.
+ */
+ @NonNull
+ private PhoneTimeZoneSuggestion findTimeZoneFromCountryAndNitz(
+ int phoneId, @NonNull String countryIsoCode,
+ @NonNull TimestampedValue<NitzData> nitzSignal) {
+ Objects.requireNonNull(countryIsoCode);
+ Objects.requireNonNull(nitzSignal);
+
+ PhoneTimeZoneSuggestion suggestion = new PhoneTimeZoneSuggestion(phoneId);
+ suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: countryIsoCode=" + countryIsoCode
+ + ", nitzSignal=" + nitzSignal);
+ NitzData nitzData = Objects.requireNonNull(nitzSignal.getValue());
+ if (isNitzSignalOffsetInfoBogus(countryIsoCode, nitzData)) {
+ suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: NITZ signal looks bogus");
+ return suggestion;
+ }
+
+ // Try to find a match using both country + NITZ signal.
+ TimeZoneLookupHelper.OffsetResult lookupResult =
+ mTimeZoneLookupHelper.lookupByNitzCountry(nitzData, countryIsoCode);
+ if (lookupResult != null) {
+ suggestion.setZoneId(lookupResult.getTimeZone().getID());
+ suggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ int quality = lookupResult.getIsOnlyMatch()
+ ? PhoneTimeZoneSuggestion.SINGLE_ZONE
+ : PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
+ suggestion.setQuality(quality);
+ suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: lookupResult=" + lookupResult);
+ return suggestion;
+ }
+
+ // The country + offset provided no match, so see if the country by itself would be enough.
+ CountryResult countryResult = mTimeZoneLookupHelper.lookupByCountry(
+ countryIsoCode, nitzData.getCurrentTimeInMillis());
+ if (countryResult == null) {
+ // Country not recognized.
+ suggestion.addDebugInfo(
+ "findTimeZoneFromCountryAndNitz: lookupByCountry() country not recognized");
+ return suggestion;
+ }
+
+ // If the country has a single zone, or it has multiple zones but the default zone is
+ // "boosted" (i.e. the country default is considered a good suggestion in most cases) then
+ // use it.
+ if (countryResult.quality == QUALITY_SINGLE_ZONE
+ || countryResult.quality == QUALITY_DEFAULT_BOOSTED) {
+ suggestion.setZoneId(countryResult.zoneId);
+ suggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ suggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ suggestion.addDebugInfo(
+ "findTimeZoneFromCountryAndNitz: high quality country-only suggestion:"
+ + " countryResult=" + countryResult);
+ return suggestion;
+ }
+
+ // Quality is not high enough to set the zone using country only.
+ suggestion.addDebugInfo("findTimeZoneFromCountryAndNitz: country-only suggestion quality"
+ + " not high enough. countryResult=" + countryResult);
+ return suggestion;
+ }
+
+ /**
+ * Creates a {@link PhoneTimeZoneSuggestion} using only network country code; works well on
+ * countries which only have one time zone or multiple zones with the same offset.
+ *
+ * @param countryIsoCode country code from network MCC
+ * @param whenMillis the time to use when looking at time zone rules data
+ */
+ @NonNull
+ private PhoneTimeZoneSuggestion findTimeZoneFromNetworkCountryCode(
+ int phoneId, @NonNull String countryIsoCode, long whenMillis) {
+ Objects.requireNonNull(countryIsoCode);
+ if (TextUtils.isEmpty(countryIsoCode)) {
+ throw new IllegalArgumentException("countryIsoCode must not be empty");
+ }
+
+ PhoneTimeZoneSuggestion result = new PhoneTimeZoneSuggestion(phoneId);
+ result.addDebugInfo("findTimeZoneFromNetworkCountryCode:"
+ + " whenMillis=" + whenMillis + ", countryIsoCode=" + countryIsoCode);
+ CountryResult lookupResult = mTimeZoneLookupHelper.lookupByCountry(
+ countryIsoCode, whenMillis);
+ if (lookupResult != null) {
+ result.setZoneId(lookupResult.zoneId);
+ result.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+
+ int quality;
+ if (lookupResult.quality == QUALITY_SINGLE_ZONE
+ || lookupResult.quality == QUALITY_DEFAULT_BOOSTED) {
+ quality = PhoneTimeZoneSuggestion.SINGLE_ZONE;
+ } else if (lookupResult.quality == QUALITY_MULTIPLE_ZONES_SAME_OFFSET) {
+ quality = PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
+ } else if (lookupResult.quality == QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS) {
+ quality = PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+ } else {
+ // This should never happen.
+ throw new IllegalArgumentException(
+ "lookupResult.quality not recognized: countryIsoCode=" + countryIsoCode
+ + ", whenMillis=" + whenMillis + ", lookupResult=" + lookupResult);
+ }
+ result.setQuality(quality);
+ result.addDebugInfo("findTimeZoneFromNetworkCountryCode: lookupResult=" + lookupResult);
+ } else {
+ result.addDebugInfo("findTimeZoneFromNetworkCountryCode: Country not recognized?");
+ }
+ return result;
+ }
+
+ /**
+ * Returns true if the NITZ signal is definitely bogus, assuming that the country is correct.
+ */
+ private boolean isNitzSignalOffsetInfoBogus(String countryIsoCode, NitzData nitzData) {
+ if (TextUtils.isEmpty(countryIsoCode)) {
+ // We cannot say for sure.
+ return false;
+ }
+
+ boolean zeroOffsetNitz = nitzData.getLocalOffsetMillis() == 0;
+ return zeroOffsetNitz && !countryUsesUtc(countryIsoCode, nitzData);
+ }
+
+ private boolean countryUsesUtc(String countryIsoCode, NitzData nitzData) {
+ return mTimeZoneLookupHelper.countryUsesUtc(
+ countryIsoCode, nitzData.getCurrentTimeInMillis());
+ }
+}
diff --git a/src/java/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestion.java b/src/java/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestion.java
new file mode 100644
index 0000000000..a5078a7234
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestion.java
@@ -0,0 +1,254 @@
+/*
+ * 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.nitz.service;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A suggested time zone from a Phone-based signal, e.g. from MCC and NITZ information.
+ */
+public final class PhoneTimeZoneSuggestion implements Parcelable {
+
+ public static final Creator<PhoneTimeZoneSuggestion> CREATOR =
+ new Creator<PhoneTimeZoneSuggestion>() {
+ public PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
+ return PhoneTimeZoneSuggestion.createFromParcel(in);
+ }
+
+ public PhoneTimeZoneSuggestion[] newArray(int size) {
+ return new PhoneTimeZoneSuggestion[size];
+ }
+ };
+
+ /**
+ * Creates an empty time zone suggestion, i.e. one that will cancel previous suggestions with
+ * the same {@code phoneId}.
+ */
+ @NonNull
+ public static PhoneTimeZoneSuggestion createEmptySuggestion(
+ int phoneId, @NonNull String debugInfo) {
+ PhoneTimeZoneSuggestion timeZoneSuggestion = new PhoneTimeZoneSuggestion(phoneId);
+ timeZoneSuggestion.addDebugInfo(debugInfo);
+ return timeZoneSuggestion;
+ }
+
+ @IntDef({ MATCH_TYPE_NA, NETWORK_COUNTRY_ONLY, NETWORK_COUNTRY_AND_OFFSET, EMULATOR_ZONE_ID,
+ TEST_NETWORK_OFFSET_ONLY })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MatchType {}
+
+ /** Used when match type is not applicable. */
+ public static final int MATCH_TYPE_NA = 0;
+
+ /**
+ * Only the network country is known.
+ */
+ public static final int NETWORK_COUNTRY_ONLY = 2;
+
+ /**
+ * Both the network county and offset were known.
+ */
+ public static final int NETWORK_COUNTRY_AND_OFFSET = 3;
+
+ /**
+ * The device is running in an emulator and an NITZ signal was simulated containing an
+ * Android extension with an explicit Olson ID.
+ */
+ public static final int EMULATOR_ZONE_ID = 4;
+
+ /**
+ * The phone is most likely running in a test network not associated with a country (this is
+ * distinct from the country just not being known yet).
+ * Historically, Android has just picked an arbitrary time zone with the correct offset when
+ * on a test network.
+ */
+ public static final int TEST_NETWORK_OFFSET_ONLY = 5;
+
+ @IntDef({ QUALITY_NA, SINGLE_ZONE, MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Quality {}
+
+ /** Used when quality is not applicable. */
+ public static final int QUALITY_NA = 0;
+
+ /** There is only one answer */
+ public static final int SINGLE_ZONE = 1;
+
+ /**
+ * There are multiple answers, but they all shared the same offset / DST state at the time
+ * the suggestion was created. i.e. it might be the wrong zone but the user won't notice
+ * immediately if it is wrong.
+ */
+ public static final int MULTIPLE_ZONES_WITH_SAME_OFFSET = 2;
+
+ /**
+ * There are multiple answers with different offsets. The one given is just one possible.
+ */
+ public static final int MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3;
+
+ /**
+ * The ID of the phone this suggestion is associated with. For multiple-sim devices this
+ * helps to establish origin so filtering / stickiness can be implemented.
+ */
+ private final int mPhoneId;
+
+ /**
+ * The suggestion. {@code null} means there is no current suggestion and any previous suggestion
+ * should be forgotten.
+ */
+ private String mZoneId;
+
+ /**
+ * The type of "match" used to establish the time zone.
+ */
+ @MatchType
+ private int mMatchType;
+
+ /**
+ * A measure of the quality of the time zone suggestion, i.e. how confident one could be in
+ * it.
+ */
+ @Quality
+ private int mQuality;
+
+ /**
+ * Free-form debug information about how the signal was derived. Used for debug only,
+ * intentionally not used in equals(), etc.
+ */
+ private List<String> mDebugInfo;
+
+ public PhoneTimeZoneSuggestion(int phoneId) {
+ this.mPhoneId = phoneId;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
+ int phoneId = in.readInt();
+ PhoneTimeZoneSuggestion phoneTimeZoneSuggestion = new PhoneTimeZoneSuggestion(phoneId);
+ phoneTimeZoneSuggestion.mZoneId = in.readString();
+ phoneTimeZoneSuggestion.mMatchType = in.readInt();
+ phoneTimeZoneSuggestion.mQuality = in.readInt();
+ phoneTimeZoneSuggestion.mDebugInfo =
+ (List<String>) in.readArrayList(PhoneTimeZoneSuggestion.class.getClassLoader());
+ return phoneTimeZoneSuggestion;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPhoneId);
+ dest.writeString(mZoneId);
+ dest.writeInt(mMatchType);
+ dest.writeInt(mQuality);
+ dest.writeList(mDebugInfo);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public int getPhoneId() {
+ return mPhoneId;
+ }
+
+ @Nullable
+ public String getZoneId() {
+ return mZoneId;
+ }
+
+ public void setZoneId(@Nullable String zoneId) {
+ this.mZoneId = zoneId;
+ }
+
+
+ @MatchType
+ public int getMatchType() {
+ return mMatchType;
+ }
+
+ public void setMatchType(@MatchType int matchType) {
+ this.mMatchType = matchType;
+ }
+ @Quality
+ public int getQuality() {
+ return mQuality;
+ }
+
+ public void setQuality(@Quality int quality) {
+ this.mQuality = quality;
+ }
+
+ public List<String> getDebugInfo() {
+ return Collections.unmodifiableList(mDebugInfo);
+ }
+
+ /**
+ * Associates information with the instance that can be useful for debugging / logging. The
+ * information is present in {@link #toString()} but is not considered for
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ public void addDebugInfo(String... debugInfos) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>();
+ }
+ mDebugInfo.addAll(Arrays.asList(debugInfos));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PhoneTimeZoneSuggestion that = (PhoneTimeZoneSuggestion) o;
+ return mPhoneId == that.mPhoneId
+ && mMatchType == that.mMatchType
+ && mQuality == that.mQuality
+ && Objects.equals(mZoneId, that.mZoneId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPhoneId, mZoneId, mMatchType, mQuality);
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneTimeZoneSuggestion{"
+ + "mPhoneId=" + mPhoneId
+ + ", mZoneId='" + mZoneId + '\''
+ + ", mMatchType=" + mMatchType
+ + ", mQuality=" + mQuality
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+}
diff --git a/src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionService.java b/src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionService.java
new file mode 100644
index 0000000000..a766d9a415
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionService.java
@@ -0,0 +1,489 @@
+/*
+ * 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.nitz.service;
+
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.EMULATOR_ZONE_ID;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.MATCH_TYPE_NA;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.QUALITY_NA;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.SINGLE_ZONE;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.Objects;
+
+/**
+ * A singleton, stateful time zone detection service that is aware of multiple phone devices. It
+ * keeps track of the most recent suggestion from each phone and it uses the best based on a scoring
+ * algorithm. If both phones provide the same score then the phone with the lowest numeric ID
+ * "wins". If the situation changes and it is no longer possible to be confident about the time
+ * zone, phones must submit an empty suggestion in order to "withdraw" their previous suggestion.
+ *
+ * <p>Ultimately, this responsibility will be moved to system server and then it will be extended /
+ * rewritten to handle non-telephony time zone signals.
+ */
+public class TimeZoneDetectionService {
+
+ /**
+ * Used by {@link TimeZoneDetectionService} to interact with device settings. It can be faked
+ * for tests.
+ */
+ @VisibleForTesting
+ public interface Helper {
+
+ /**
+ * Callback interface for automatic detection enable/disable changes.
+ */
+ interface Listener {
+ /**
+ * Automatic time zone detection has been enabled or disabled.
+ */
+ void onTimeZoneDetectionChange(boolean enabled);
+ }
+
+ /**
+ * Sets a listener that will be called when the automatic time / time zone detection setting
+ * changes.
+ */
+ void setListener(Listener listener);
+
+ /**
+ * Returns true if automatic time zone detection is enabled in settings.
+ */
+ boolean isTimeZoneDetectionEnabled();
+
+ /**
+ * Returns true if the device has had an explicit time zone set.
+ */
+ boolean isTimeZoneSettingInitialized();
+
+ /**
+ * Set the device time zone from the suggestion as needed.
+ */
+ void setDeviceTimeZoneFromSuggestion(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion);
+
+ /**
+ * Dumps any logs held to the supplied writer.
+ */
+ void dumpLogs(IndentingPrintWriter ipw);
+
+ /**
+ * Dumps internal state such as field values.
+ */
+ void dumpState(PrintWriter pw);
+ }
+
+ static final String LOG_TAG = "TimeZoneDetectionService";
+ static final boolean DBG = true;
+
+ /**
+ * The abstract score for an empty or invalid suggestion.
+ *
+ * Used to score suggestions where there is no zone.
+ */
+ @VisibleForTesting
+ public static final int SCORE_NONE = 0;
+
+ /**
+ * The abstract score for a low quality suggestion.
+ *
+ * Used to score suggestions where:
+ * The suggested zone ID is one of several possibilities, and the possibilities have different
+ * offsets.
+ *
+ * You would have to be quite desperate to want to use this choice.
+ */
+ @VisibleForTesting
+ public static final int SCORE_LOW = 1;
+
+ /**
+ * The abstract score for a medium quality suggestion.
+ *
+ * Used for:
+ * The suggested zone ID is one of several possibilities but at least the possibilities have the
+ * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
+ * switch to DST at the wrong time and (for example) their calendar events.
+ */
+ @VisibleForTesting
+ public static final int SCORE_MEDIUM = 2;
+
+ /**
+ * The abstract score for a high quality suggestion.
+ *
+ * Used for:
+ * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
+ * the info available.
+ */
+ @VisibleForTesting
+ public static final int SCORE_HIGH = 3;
+
+ /**
+ * The abstract score for a highest quality suggestion.
+ *
+ * Used for:
+ * Suggestions that must "win" because they constitute test or emulator zone ID.
+ */
+ @VisibleForTesting
+ public static final int SCORE_HIGHEST = 4;
+
+ /** The threshold at which suggestions are good enough to use to set the device's time zone. */
+ @VisibleForTesting
+ public static final int SCORE_USAGE_THRESHOLD = SCORE_MEDIUM;
+
+ /** The singleton instance. */
+ private static TimeZoneDetectionService sInstance;
+
+ /**
+ * Returns the singleton instance, constructing as needed with the supplied context.
+ */
+ public static synchronized TimeZoneDetectionService getInstance(Context context) {
+ if (sInstance == null) {
+ Helper timeZoneDetectionServiceHelper = new TimeZoneDetectionServiceHelperImpl(context);
+ sInstance = new TimeZoneDetectionService(timeZoneDetectionServiceHelper);
+ }
+ return sInstance;
+ }
+
+ private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+
+ /**
+ * A mapping from phoneId to a linked list of time zone suggestions (the head being the latest).
+ * We typically expect one or two entries in this Map: devices will have a small number
+ * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
+ * the ID will not exceed {@link #KEEP_SUGGESTION_HISTORY_SIZE} in size.
+ */
+ @GuardedBy("this")
+ private ArrayMap<Integer, LinkedList<QualifiedPhoneTimeZoneSuggestion>> mSuggestionByPhoneId =
+ new ArrayMap<>();
+
+ /**
+ * The most recent best guess of time zone from all phones. Can be {@code null} to indicate
+ * there would be no current suggestion.
+ */
+ @GuardedBy("this")
+ @Nullable
+ private QualifiedPhoneTimeZoneSuggestion mCurrentSuggestion;
+
+ // Dependencies and log state.
+ private final Helper mTimeZoneDetectionServiceHelper;
+
+ @VisibleForTesting
+ public TimeZoneDetectionService(Helper timeZoneDetectionServiceHelper) {
+ mTimeZoneDetectionServiceHelper = timeZoneDetectionServiceHelper;
+ mTimeZoneDetectionServiceHelper.setListener(enabled -> {
+ if (enabled) {
+ handleAutoTimeZoneEnabled();
+ }
+ });
+ }
+
+ /**
+ * Suggests a time zone for the device, or withdraws a previous suggestion if
+ * {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a
+ * specific {@link PhoneTimeZoneSuggestion#getPhoneId() phone}.
+ * See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a
+ * suggestion. The service uses suggestions to decide whether to modify the device's time zone
+ * setting and what to set it to.
+ */
+ public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion newSuggestion) {
+ if (DBG) {
+ Log.d(LOG_TAG, "suggestPhoneTimeZone: newSuggestion=" + newSuggestion);
+ }
+ Objects.requireNonNull(newSuggestion);
+
+ int score = scoreSuggestion(newSuggestion);
+ QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(newSuggestion, score);
+
+ // Record the suggestion against the correct phoneId.
+ LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
+ mSuggestionByPhoneId.get(newSuggestion.getPhoneId());
+ if (suggestions == null) {
+ suggestions = new LinkedList<>();
+ mSuggestionByPhoneId.put(newSuggestion.getPhoneId(), suggestions);
+ }
+ suggestions.addFirst(scoredSuggestion);
+ if (suggestions.size() > KEEP_SUGGESTION_HISTORY_SIZE) {
+ suggestions.removeLast();
+ }
+
+ // Now run the competition between the phones' suggestions.
+ doTimeZoneDetection();
+ }
+
+ private static int scoreSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ int score;
+ if (suggestion.getZoneId() == null || !isValid(suggestion)) {
+ score = SCORE_NONE;
+ } else if (suggestion.getMatchType() == TEST_NETWORK_OFFSET_ONLY
+ || suggestion.getMatchType() == EMULATOR_ZONE_ID) {
+ // Handle emulator / test cases : These suggestions should always just be used.
+ score = SCORE_HIGHEST;
+ } else if (suggestion.getQuality() == SINGLE_ZONE) {
+ score = SCORE_HIGH;
+ } else if (suggestion.getQuality() == MULTIPLE_ZONES_WITH_SAME_OFFSET) {
+ // The suggestion may be wrong, but at least the offset should be correct.
+ score = SCORE_MEDIUM;
+ } else if (suggestion.getQuality() == MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
+ // The suggestion has a good chance of being wrong.
+ score = SCORE_LOW;
+ } else {
+ throw new AssertionError();
+ }
+ return score;
+ }
+
+ private static boolean isValid(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ int quality = suggestion.getQuality();
+ int matchType = suggestion.getMatchType();
+ if (suggestion.getZoneId() == null) {
+ return quality == QUALITY_NA && matchType == MATCH_TYPE_NA;
+ } else {
+ boolean qualityValid = quality == SINGLE_ZONE
+ || quality == MULTIPLE_ZONES_WITH_SAME_OFFSET
+ || quality == MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+ boolean matchTypeValid = matchType == NETWORK_COUNTRY_ONLY
+ || matchType == NETWORK_COUNTRY_AND_OFFSET
+ || matchType == EMULATOR_ZONE_ID
+ || matchType == TEST_NETWORK_OFFSET_ONLY;
+ return qualityValid && matchTypeValid;
+ }
+ }
+
+ /**
+ * Finds the best available time zone suggestion from all phones. If it is high-enough quality
+ * and automatic time zone detection is enabled then it will be set on the device. The outcome
+ * can be that this service becomes / remains un-opinionated and nothing is set.
+ */
+ @GuardedBy("this")
+ private void doTimeZoneDetection() {
+ QualifiedPhoneTimeZoneSuggestion bestSuggestion = findBestSuggestion();
+ boolean timeZoneDetectionEnabled =
+ mTimeZoneDetectionServiceHelper.isTimeZoneDetectionEnabled();
+
+ // Work out what to do with the best suggestion.
+ if (bestSuggestion == null) {
+ // There is no suggestion. Become un-opinionated.
+ if (DBG) {
+ Log.d(LOG_TAG, "doTimeZoneDetection: No good suggestion."
+ + " bestSuggestion=null"
+ + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
+ }
+ mCurrentSuggestion = null;
+ return;
+ }
+
+ // Special case handling for uninitialized devices. This should only happen once.
+ String newZoneId = bestSuggestion.suggestion.getZoneId();
+ if (newZoneId != null && !mTimeZoneDetectionServiceHelper.isTimeZoneSettingInitialized()) {
+ Log.i(LOG_TAG, "doTimeZoneDetection: Device has no time zone set so might set the"
+ + " device to the best available suggestion."
+ + " bestSuggestion=" + bestSuggestion
+ + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
+
+ mCurrentSuggestion = bestSuggestion;
+ if (timeZoneDetectionEnabled) {
+ mTimeZoneDetectionServiceHelper.setDeviceTimeZoneFromSuggestion(
+ bestSuggestion.suggestion);
+ }
+ return;
+ }
+
+ boolean suggestionGoodEnough = bestSuggestion.score >= SCORE_USAGE_THRESHOLD;
+ if (!suggestionGoodEnough) {
+ if (DBG) {
+ Log.d(LOG_TAG, "doTimeZoneDetection: Suggestion not good enough."
+ + " bestSuggestion=" + bestSuggestion);
+ }
+ mCurrentSuggestion = null;
+ return;
+ }
+
+ // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
+ // zone ID.
+ if (newZoneId == null) {
+ Log.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
+ + " bestSuggestion=" + bestSuggestion);
+ mCurrentSuggestion = null;
+ return;
+ }
+
+ // There is a good suggestion. Store the suggestion and set the device time zone if
+ // settings allow.
+ mCurrentSuggestion = bestSuggestion;
+
+ // Only set the device time zone if time zone detection is enabled.
+ if (!timeZoneDetectionEnabled) {
+ if (DBG) {
+ Log.d(LOG_TAG, "doTimeZoneDetection: Not setting the time zone because time zone"
+ + " detection is disabled."
+ + " bestSuggestion=" + bestSuggestion);
+ }
+ return;
+ }
+ mTimeZoneDetectionServiceHelper.setDeviceTimeZoneFromSuggestion(bestSuggestion.suggestion);
+ }
+
+ @GuardedBy("this")
+ @Nullable
+ private QualifiedPhoneTimeZoneSuggestion findBestSuggestion() {
+ QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
+
+ // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
+ // and find the best. Note that we deliberately do not look at age: the caller can
+ // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
+ // expected to withdraw suggestions they no longer have confidence in.
+ for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
+ LinkedList<QualifiedPhoneTimeZoneSuggestion> phoneSuggestions =
+ mSuggestionByPhoneId.valueAt(i);
+ if (phoneSuggestions == null) {
+ // Unexpected
+ continue;
+ }
+ QualifiedPhoneTimeZoneSuggestion candidateSuggestion = phoneSuggestions.getFirst();
+ if (candidateSuggestion == null) {
+ // Unexpected
+ continue;
+ }
+
+ if (bestSuggestion == null) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score > bestSuggestion.score) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score == bestSuggestion.score) {
+ // Tie! Use the suggestion with the lowest phoneId.
+ int candidatePhoneId = candidateSuggestion.suggestion.getPhoneId();
+ int bestPhoneId = bestSuggestion.suggestion.getPhoneId();
+ if (candidatePhoneId < bestPhoneId) {
+ bestSuggestion = candidateSuggestion;
+ }
+ }
+ }
+ return bestSuggestion;
+ }
+
+ /**
+ * Returns the current best suggestion. Not intended for general use: it is used during tests
+ * to check service behavior.
+ */
+ @VisibleForTesting
+ @Nullable
+ public synchronized QualifiedPhoneTimeZoneSuggestion findBestSuggestionForTests() {
+ return findBestSuggestion();
+ }
+
+ private synchronized void handleAutoTimeZoneEnabled() {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleAutoTimeEnabled() called");
+ }
+ // When the user enabled time zone detection, run the time zone detection and change the
+ // device time zone if possible.
+ doTimeZoneDetection();
+ }
+
+ /**
+ * Dumps any logs held to the supplied writer.
+ */
+ public void dumpLogs(IndentingPrintWriter ipw) {
+ mTimeZoneDetectionServiceHelper.dumpLogs(ipw);
+ }
+
+ /**
+ * Dumps internal state such as field values.
+ */
+ public void dumpState(PrintWriter pw) {
+ pw.println(" TimeZoneDetectionService.mCurrentSuggestion=" + mCurrentSuggestion);
+ pw.println(" TimeZoneDetectionService.mSuggestionsByPhoneId=" + mSuggestionByPhoneId);
+ mTimeZoneDetectionServiceHelper.dumpState(pw);
+ pw.flush();
+ }
+
+ /**
+ * A method used to inspect service state during tests. Not intended for general use.
+ */
+ @VisibleForTesting
+ public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int phoneId) {
+ LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
+ mSuggestionByPhoneId.get(phoneId);
+ if (suggestions == null) {
+ return null;
+ }
+ return suggestions.getFirst();
+ }
+
+ /**
+ * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
+ */
+ @VisibleForTesting
+ public static class QualifiedPhoneTimeZoneSuggestion {
+
+ @VisibleForTesting
+ public final PhoneTimeZoneSuggestion suggestion;
+
+ /**
+ * The score the suggestion has been given. This can be used to rank against other
+ * suggestions of the same type.
+ */
+ @VisibleForTesting
+ public final int score;
+
+ @VisibleForTesting
+ public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
+ this.suggestion = suggestion;
+ this.score = score;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
+ return score == that.score
+ && suggestion.equals(that.suggestion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(score, suggestion);
+ }
+
+ @Override
+ public String toString() {
+ return "QualifiedPhoneTimeZoneSuggestion{"
+ + "suggestion=" + suggestion
+ + ", score=" + score
+ + '}';
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceHelperImpl.java b/src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceHelperImpl.java
new file mode 100644
index 0000000000..fc857a7695
--- /dev/null
+++ b/src/java/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceHelperImpl.java
@@ -0,0 +1,150 @@
+/*
+ * 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.nitz.service;
+
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+
+/**
+ * The real implementation of {@link TimeZoneDetectionService.Helper}.
+ */
+public final class TimeZoneDetectionServiceHelperImpl implements TimeZoneDetectionService.Helper {
+
+ private static final String LOG_TAG = TimeZoneDetectionService.LOG_TAG;
+ private static final boolean DBG = TimeZoneDetectionService.DBG;
+ private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
+
+ private final Context mContext;
+ private final ContentResolver mCr;
+ private final LocalLog mTimeZoneLog = new LocalLog(30);
+
+ private Listener mListener;
+
+ /** Creates a TimeServiceHelper */
+ public TimeZoneDetectionServiceHelperImpl(Context context) {
+ mContext = context;
+ mCr = context.getContentResolver();
+ }
+
+ @Override
+ public void setListener(Listener listener) {
+ if (listener == null) {
+ throw new NullPointerException("listener==null");
+ }
+ if (mListener != null) {
+ throw new IllegalStateException("listener already set");
+ }
+ this.mListener = listener;
+ mCr.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
+ new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange) {
+ listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled());
+ }
+ });
+ }
+
+ @Override
+ public boolean isTimeZoneDetectionEnabled() {
+ try {
+ return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
+ } catch (Settings.SettingNotFoundException snfe) {
+ return true;
+ }
+ }
+
+ @Override
+ public boolean isTimeZoneSettingInitialized() {
+ // timezone.equals("GMT") will be true and only true if the time zone was
+ // set to a default value by the system server (when starting, system server
+ // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
+ // any code that sets it explicitly (in case where something sets GMT explicitly,
+ // "Etc/GMT" Olson ID would be used).
+
+ String timeZoneId = getTimeZoneSetting();
+ return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
+ }
+
+ @Override
+ public void setDeviceTimeZoneFromSuggestion(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ String currentZoneId = getTimeZoneSetting();
+ String newZoneId = timeZoneSuggestion.getZoneId();
+ if (newZoneId.equals(currentZoneId)) {
+ // No need to set the device time zone - the setting is already what we would be
+ // suggesting.
+ if (DBG) {
+ Log.d(LOG_TAG, "setDeviceTimeZoneAsNeeded: No need to change the time zone;"
+ + " device is already set to the suggested zone."
+ + " timeZoneSuggestion=" + timeZoneSuggestion);
+ }
+ return;
+ }
+
+ String msg = "Changing device time zone. currentZoneId=" + currentZoneId
+ + ", timeZoneSuggestion=" + timeZoneSuggestion;
+ if (DBG) {
+ Log.d(LOG_TAG, msg);
+ }
+ mTimeZoneLog.log(msg);
+
+ AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.setTimeZone(newZoneId);
+ Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time-zone", newZoneId);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ @Nullable
+ private String getTimeZoneSetting() {
+ return SystemProperties.get(TIMEZONE_PROPERTY);
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ pw.println(" TimeZoneDetectionServiceHelperImpl.getTimeZoneSetting()="
+ + getTimeZoneSetting());
+ }
+
+ @Override
+ public void dumpLogs(IndentingPrintWriter ipw) {
+ ipw.println("TimeZoneDetectionServiceHelperImpl:");
+
+ ipw.increaseIndent();
+ ipw.println("Time zone logs:");
+ ipw.increaseIndent();
+ mTimeZoneLog.dump(ipw);
+ ipw.decreaseIndent();
+
+ ipw.decreaseIndent();
+ }
+}
diff --git a/src/java/com/android/internal/telephony/sip/SipPhone.java b/src/java/com/android/internal/telephony/sip/SipPhone.java
index ff9e5a7cc1..7fcaf96099 100644
--- a/src/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhone.java
@@ -29,9 +29,9 @@ import android.os.AsyncResult;
import android.os.Message;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.text.TextUtils;
-import android.telephony.Rlog;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
@@ -40,6 +40,8 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneNotifier;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.text.ParseException;
import java.util.List;
import java.util.regex.Pattern;
@@ -59,7 +61,9 @@ public class SipPhone extends SipPhoneBase {
// A call that is ringing or (call) waiting
private SipCall mRingingCall = new SipCall();
+ @UnsupportedAppUsage
private SipCall mForegroundCall = new SipCall();
+ @UnsupportedAppUsage
private SipCall mBackgroundCall = new SipCall();
private SipManager mSipManager;
@@ -431,6 +435,7 @@ public class SipPhone extends SipPhoneBase {
return false;
}
+ @UnsupportedAppUsage
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
@@ -439,6 +444,7 @@ public class SipPhone extends SipPhoneBase {
Rlog.d(LOG_TAG, s);
}
+ @UnsupportedAppUsage
private void loge(String s) {
Rlog.e(LOG_TAG, s);
}
@@ -458,6 +464,7 @@ public class SipPhone extends SipPhoneBase {
setState(Call.State.IDLE);
}
+ @UnsupportedAppUsage
void switchWith(SipCall that) {
if (SC_DBG) log("switchWith");
synchronized (SipPhone.class) {
@@ -594,6 +601,7 @@ public class SipPhone extends SipPhoneBase {
audioGroup.getMode()));
}
+ @UnsupportedAppUsage
void hold() throws CallStateException {
if (SC_DBG) log("hold:");
setState(State.HOLDING);
@@ -601,10 +609,11 @@ public class SipPhone extends SipPhoneBase {
setAudioGroupMode();
}
+ @UnsupportedAppUsage
void unhold() throws CallStateException {
if (SC_DBG) log("unhold:");
setState(State.ACTIVE);
- AudioGroup audioGroup = new AudioGroup();
+ AudioGroup audioGroup = new AudioGroup(mContext);
for (Connection c : mConnections) {
((SipConnection) c).unhold(audioGroup);
}
diff --git a/src/java/com/android/internal/telephony/test/ModelInterpreter.java b/src/java/com/android/internal/telephony/test/ModelInterpreter.java
index 7930b56c30..2a046d944e 100644
--- a/src/java/com/android/internal/telephony/test/ModelInterpreter.java
+++ b/src/java/com/android/internal/telephony/test/ModelInterpreter.java
@@ -20,6 +20,8 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.telephony.Rlog;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -121,6 +123,7 @@ class LineReader
class InterpreterEx extends Exception
{
+ @UnsupportedAppUsage
public
InterpreterEx (String result)
{
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 965d5592ac..db581b2b99 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -69,6 +69,8 @@ import com.android.internal.telephony.uicc.IccCardStatus;
import com.android.internal.telephony.uicc.IccIoResult;
import com.android.internal.telephony.uicc.IccSlotStatus;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -113,6 +115,7 @@ public class SimulatedCommands extends BaseCommands
//***** Instance Variables
+ @UnsupportedAppUsage
SimulatedGsmCallState simulatedCallState;
HandlerThread mHandlerThread;
SimLockState mSimLockedState;
@@ -153,6 +156,7 @@ public class SimulatedCommands extends BaseCommands
int mNextCallFailCause = CallFailCause.NORMAL_CLEARING;
+ @UnsupportedAppUsage
private boolean mDcSuccess = true;
private SetupDataCallResult mSetupDataCallResult;
private boolean mIsRadioPowerFailResponse = false;
@@ -801,6 +805,7 @@ public class SimulatedCommands extends BaseCommands
* ar.userObject contains the original value of result.obj
* ar.result is null on success and failure
*/
+ @UnsupportedAppUsage
@Override
public void acceptCall (Message result) {
boolean success;
@@ -1654,6 +1659,7 @@ public class SimulatedCommands extends BaseCommands
//***** Private Methods
+ @UnsupportedAppUsage
private void unimplemented(Message result) {
if (result != null) {
AsyncResult.forMessage(result).exception
@@ -1667,6 +1673,7 @@ public class SimulatedCommands extends BaseCommands
}
}
+ @UnsupportedAppUsage
private void resultSuccess(Message result, Object ret) {
if (result != null) {
AsyncResult.forMessage(result).result = ret;
@@ -1678,6 +1685,7 @@ public class SimulatedCommands extends BaseCommands
}
}
+ @UnsupportedAppUsage
private void resultFail(Message result, Object ret, Throwable tr) {
if (result != null) {
AsyncResult.forMessage(result, ret, tr);
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
index 13d8a9b78d..c550f2c4e5 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
@@ -32,6 +32,8 @@ import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
public class SimulatedCommandsVerifier implements CommandsInterface {
private static SimulatedCommandsVerifier sInstance;
@@ -39,6 +41,7 @@ public class SimulatedCommandsVerifier implements CommandsInterface {
}
+ @UnsupportedAppUsage
public static SimulatedCommandsVerifier getInstance() {
if (sInstance == null) {
sInstance = new SimulatedCommandsVerifier();
@@ -926,6 +929,7 @@ public class SimulatedCommandsVerifier implements CommandsInterface {
}
+ @UnsupportedAppUsage
@Override
public void setCallForward(int action, int cfReason, int serviceClass, String number,
int timeSeconds, Message response) {
diff --git a/src/java/com/android/internal/telephony/test/SimulatedGsmCallState.java b/src/java/com/android/internal/telephony/test/SimulatedGsmCallState.java
index 75e84c4642..72e2d93457 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedGsmCallState.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedGsmCallState.java
@@ -16,16 +16,19 @@
package com.android.internal.telephony.test;
+import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.Handler;
import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+
import com.android.internal.telephony.ATParseEx;
import com.android.internal.telephony.DriverCall;
-import java.util.List;
-import java.util.ArrayList;
-import android.telephony.Rlog;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.ArrayList;
+import java.util.List;
class CallInfo {
enum State {
@@ -388,6 +391,7 @@ class SimulatedGsmCallState extends Handler {
return found;
}
+ @UnsupportedAppUsage
public boolean
onChld(char c0, char c1) {
boolean ret;
@@ -444,6 +448,7 @@ class SimulatedGsmCallState extends Handler {
return ret;
}
+ @UnsupportedAppUsage
public boolean
releaseHeldOrUDUB() {
boolean found = false;
@@ -474,6 +479,7 @@ class SimulatedGsmCallState extends Handler {
}
+ @UnsupportedAppUsage
public boolean
releaseActiveAcceptHeldOrWaiting() {
boolean foundHeld = false;
@@ -529,6 +535,7 @@ class SimulatedGsmCallState extends Handler {
return true;
}
+ @UnsupportedAppUsage
public boolean
switchActiveAndHeldOrWaiting() {
boolean hasHeld = false;
@@ -562,6 +569,7 @@ class SimulatedGsmCallState extends Handler {
}
+ @UnsupportedAppUsage
public boolean
separateCall(int index) {
try {
@@ -603,6 +611,7 @@ class SimulatedGsmCallState extends Handler {
+ @UnsupportedAppUsage
public boolean
conference() {
int countCalls = 0;
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
index 76242c48a2..87bbc69b12 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardApplicationStatus.java
@@ -28,7 +28,14 @@ import com.android.internal.telephony.uicc.IccCardStatus.PinState;
* {@hide}
*/
public class IccCardApplicationStatus {
+
+ @UnsupportedAppUsage
+ public IccCardApplicationStatus() {
+ }
+
// TODO: Replace with constants from PhoneConstants.APPTYPE_xxx
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;")
public enum AppType{
@UnsupportedAppUsage
APPTYPE_UNKNOWN,
@@ -44,6 +51,8 @@ public class IccCardApplicationStatus {
APPTYPE_ISIM
}
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;")
public enum AppState{
@UnsupportedAppUsage
APPSTATE_UNKNOWN,
@@ -80,6 +89,8 @@ public class IccCardApplicationStatus {
}
}
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;")
public enum PersoSubState{
@UnsupportedAppUsage
PERSOSUBSTATE_UNKNOWN,
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index b22e2b4a45..6d5232b634 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -228,9 +228,10 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
*
* @param fileid EF id
* @param path Path of the EF on the card
- * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[] int[0] is the
- * record length int[1] is the total length of the EF file int[3] is the number of records
- * in the EF file So int[0] * int[3] = int[1]
+ * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]. recordSize[0] is
+ * the single record length, recordSize[1] is the total length of the EF file
+ * and recordSize[2] is the number of records in the EF file. So recordSize[0]
+ * * recordSize[2] = recordSize[1].
*/
@UnsupportedAppUsage
public void getEFLinearRecordSize(int fileid, String path, Message onLoaded) {
@@ -246,9 +247,10 @@ public abstract class IccFileHandler extends Handler implements IccConstants {
* Get record size for a linear fixed EF
*
* @param fileid EF id
- * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[] int[0] is the
- * record length int[1] is the total length of the EF file int[3] is the number of records
- * in the EF file So int[0] * int[3] = int[1]
+ * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]. recordSize[0] is
+ * the single record length, recordSize[1] is the total length of the EF file
+ * and recordSize[2] is the number of records in the EF file. So recordSize[0]
+ * * recordSize[2] = recordSize[1].
*/
@UnsupportedAppUsage
public void getEFLinearRecordSize(int fileid, Message onLoaded) {
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 884bc751b1..030736365e 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -391,11 +391,13 @@ public abstract class IccRecords extends Handler implements IccConstants {
*/
@UnsupportedAppUsage
public String getIccId() {
- if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIccid() != null) {
- return mCarrierTestOverride.getFakeIccid();
- } else {
- return mIccId;
+ if (mCarrierTestOverride.isInTestMode()) {
+ String fakeIccId = mCarrierTestOverride.getFakeIccid();
+ if (fakeIccId != null) {
+ return fakeIccId;
+ }
}
+ return mIccId;
}
/**
@@ -564,11 +566,13 @@ public abstract class IccRecords extends Handler implements IccConstants {
*/
@UnsupportedAppUsage
public String getIMSI() {
- if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeIMSI() != null) {
- return mCarrierTestOverride.getFakeIMSI();
- } else {
- return mImsi;
+ if (mCarrierTestOverride.isInTestMode()) {
+ String fakeImsi = mCarrierTestOverride.getFakeIMSI();
+ if (fakeImsi != null) {
+ return fakeImsi;
+ }
}
+ return mImsi;
}
/**
@@ -668,11 +672,13 @@ public abstract class IccRecords extends Handler implements IccConstants {
*/
@UnsupportedAppUsage
public String getGid1() {
- if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid1() != null) {
- return mCarrierTestOverride.getFakeGid1();
- } else {
- return mGid1;
+ if (mCarrierTestOverride.isInTestMode()) {
+ String fakeGid1 = mCarrierTestOverride.getFakeGid1();
+ if (fakeGid1 != null) {
+ return fakeGid1;
+ }
}
+ return mGid1;
}
/**
@@ -680,11 +686,13 @@ public abstract class IccRecords extends Handler implements IccConstants {
* @return null if SIM is not yet ready
*/
public String getGid2() {
- if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeGid2() != null) {
- return mCarrierTestOverride.getFakeGid2();
- } else {
- return mGid2;
+ if (mCarrierTestOverride.isInTestMode()) {
+ String fakeGid2 = mCarrierTestOverride.getFakeGid2();
+ if (fakeGid2 != null) {
+ return fakeGid2;
+ }
}
+ return mGid2;
}
/**
@@ -692,12 +700,13 @@ public abstract class IccRecords extends Handler implements IccConstants {
* @return null if SIM is not yet ready
*/
public String getPnnHomeName() {
- if (mCarrierTestOverride.isInTestMode()
- && mCarrierTestOverride.getFakePnnHomeName() != null) {
- return mCarrierTestOverride.getFakePnnHomeName();
- } else {
- return mPnnHomeName;
+ if (mCarrierTestOverride.isInTestMode()) {
+ String fakePnnHomeName = mCarrierTestOverride.getFakePnnHomeName();
+ if (fakePnnHomeName != null) {
+ return fakePnnHomeName;
+ }
}
+ return mPnnHomeName;
}
@UnsupportedAppUsage
@@ -725,8 +734,11 @@ public abstract class IccRecords extends Handler implements IccConstants {
*/
@UnsupportedAppUsage
public String getServiceProviderName() {
- if (mCarrierTestOverride.isInTestMode() && mCarrierTestOverride.getFakeSpn() != null) {
- return mCarrierTestOverride.getFakeSpn();
+ if (mCarrierTestOverride.isInTestMode()) {
+ String fakeSpn = mCarrierTestOverride.getFakeSpn();
+ if (fakeSpn != null) {
+ return fakeSpn;
+ }
}
return mSpn;
}
diff --git a/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java b/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java
index 7d0f845701..ccb6f9846e 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRefreshResponse.java
@@ -40,6 +40,10 @@ public class IccRefreshResponse {
0x30, 0x30, 0x30 */
/* Example: a0000000871002f310ffff89080000ff */
+ @UnsupportedAppUsage
+ public IccRefreshResponse() {
+ }
+
@Override
public String toString() {
return "{" + refreshResult + ", " + aid +", " + efId + "}";
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 8b9bdb185c..663ebd66b4 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -1193,7 +1193,6 @@ public class SIMRecords extends IccRecords {
data = (byte[]) ar.result;
if (ar.exception != null || data == null) {
loge("Failed getting Forbidden PLMNs: " + ar.exception);
- break;
} else {
mFplmns = parseBcdPlmnList(data, "Forbidden");
}
@@ -1203,8 +1202,12 @@ public class SIMRecords extends IccRecords {
int key = msg.arg2;
Message response = retrievePendingTransaction(key).first;
if (response != null) {
- AsyncResult.forMessage(
- response, Arrays.copyOf(mFplmns, mFplmns.length), null);
+ if (ar.exception == null && data != null && mFplmns != null) {
+ AsyncResult.forMessage(response, Arrays.copyOf(mFplmns,
+ mFplmns.length), null);
+ } else {
+ AsyncResult.forMessage(response, null, ar.exception);
+ }
response.sendToTarget();
} else {
loge("Failed to retrieve a response message for FPLMN");
@@ -1687,6 +1690,8 @@ public class SIMRecords extends IccRecords {
/**
* States of Get SPN Finite State Machine which only used by getSpnFsm()
*/
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;")
private enum GetSpnFsmState {
IDLE, // No initialized
@UnsupportedAppUsage
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 0cb8c1caee..6112ddae6b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -830,7 +830,17 @@ public class UiccController extends Handler {
boolean isDefaultEuiccCardIdSet = false;
boolean anyEuiccIsActive = false;
mHasActiveBuiltInEuicc = false;
- for (int i = 0; i < status.size(); i++) {
+
+ int numSlots = status.size();
+ if (mUiccSlots.length < numSlots) {
+ String logStr = "The number of the physical slots reported " + numSlots
+ + " is greater than the expectation " + mUiccSlots.length + ".";
+ Rlog.e(LOG_TAG, logStr);
+ sLocalLog.log(logStr);
+ numSlots = mUiccSlots.length;
+ }
+
+ for (int i = 0; i < numSlots; i++) {
IccSlotStatus iss = status.get(i);
boolean isActive = (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE);
if (isActive) {
@@ -895,7 +905,7 @@ public class UiccController extends Handler {
// Note that on HAL<1.2, it's possible that a built-in eUICC exists, but does not
// correspond to any slot in mUiccSlots. This logic is still safe in that case because
// SlotStatus is only for HAL >= 1.2
- for (int i = 0; i < status.size(); i++) {
+ for (int i = 0; i < numSlots; i++) {
if (mUiccSlots[i].isEuicc()) {
String eid = status.get(i).eid;
if (!TextUtils.isEmpty(eid)) {
diff --git a/src/java/com/android/internal/telephony/util/TelephonyUtils.java b/src/java/com/android/internal/telephony/util/TelephonyUtils.java
new file mode 100644
index 0000000000..1048e5c1af
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/TelephonyUtils.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ResolveInfo;
+
+/**
+ * This class provides various util functions
+ */
+public final class TelephonyUtils {
+ /** {@hide} */
+ public static String emptyIfNull(@Nullable String str) {
+ return str == null ? "" : str;
+ }
+
+ public static boolean IS_DEBUGGABLE =
+ SystemProperties.getInt("ro.debuggable", 0) == 1;
+
+ public static ComponentInfo getComponentInfo(@NonNull ResolveInfo resolveInfo) {
+ if (resolveInfo.activityInfo != null) return resolveInfo.activityInfo;
+ if (resolveInfo.serviceInfo != null) return resolveInfo.serviceInfo;
+ if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo;
+ throw new IllegalStateException("Missing ComponentInfo!");
+ }
+ }
diff --git a/src/java/com/google/android/mms/ContentType.java b/src/java/com/google/android/mms/ContentType.java
deleted file mode 100644
index 36730127cf..0000000000
--- a/src/java/com/google/android/mms/ContentType.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Esmertec AG.
- * Copyright (C) 2007-2008 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.google.android.mms;
-
-import java.util.ArrayList;
-
-public class ContentType {
- public static final String MMS_MESSAGE = "application/vnd.wap.mms-message";
- // The phony content type for generic PDUs (e.g. ReadOrig.ind,
- // Notification.ind, Delivery.ind).
- public static final String MMS_GENERIC = "application/vnd.wap.mms-generic";
- public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed";
- public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
- public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
-
- public static final String TEXT_PLAIN = "text/plain";
- public static final String TEXT_HTML = "text/html";
- public static final String TEXT_VCALENDAR = "text/x-vCalendar";
- public static final String TEXT_VCARD = "text/x-vCard";
-
- public static final String IMAGE_UNSPECIFIED = "image/*";
- public static final String IMAGE_JPEG = "image/jpeg";
- public static final String IMAGE_JPG = "image/jpg";
- public static final String IMAGE_GIF = "image/gif";
- public static final String IMAGE_WBMP = "image/vnd.wap.wbmp";
- public static final String IMAGE_PNG = "image/png";
- public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp";
-
- public static final String AUDIO_UNSPECIFIED = "audio/*";
- public static final String AUDIO_AAC = "audio/aac";
- public static final String AUDIO_AMR = "audio/amr";
- public static final String AUDIO_IMELODY = "audio/imelody";
- public static final String AUDIO_MID = "audio/mid";
- public static final String AUDIO_MIDI = "audio/midi";
- public static final String AUDIO_MP3 = "audio/mp3";
- public static final String AUDIO_MPEG3 = "audio/mpeg3";
- public static final String AUDIO_MPEG = "audio/mpeg";
- public static final String AUDIO_MPG = "audio/mpg";
- public static final String AUDIO_MP4 = "audio/mp4";
- public static final String AUDIO_X_MID = "audio/x-mid";
- public static final String AUDIO_X_MIDI = "audio/x-midi";
- public static final String AUDIO_X_MP3 = "audio/x-mp3";
- public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3";
- public static final String AUDIO_X_MPEG = "audio/x-mpeg";
- public static final String AUDIO_X_MPG = "audio/x-mpg";
- public static final String AUDIO_3GPP = "audio/3gpp";
- public static final String AUDIO_X_WAV = "audio/x-wav";
- public static final String AUDIO_OGG = "application/ogg";
- public static final String AUDIO_OGG2 = "audio/ogg";
-
- public static final String VIDEO_UNSPECIFIED = "video/*";
- public static final String VIDEO_3GPP = "video/3gpp";
- public static final String VIDEO_3G2 = "video/3gpp2";
- public static final String VIDEO_H263 = "video/h263";
- public static final String VIDEO_MP4 = "video/mp4";
-
- public static final String APP_SMIL = "application/smil";
- public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml";
- public static final String APP_XHTML = "application/xhtml+xml";
-
- public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content";
- public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message";
-
- private static final ArrayList<String> sSupportedContentTypes = new ArrayList<String>();
- private static final ArrayList<String> sSupportedImageTypes = new ArrayList<String>();
- private static final ArrayList<String> sSupportedAudioTypes = new ArrayList<String>();
- private static final ArrayList<String> sSupportedVideoTypes = new ArrayList<String>();
-
- static {
- sSupportedContentTypes.add(TEXT_PLAIN);
- sSupportedContentTypes.add(TEXT_HTML);
- sSupportedContentTypes.add(TEXT_VCALENDAR);
- sSupportedContentTypes.add(TEXT_VCARD);
-
- sSupportedContentTypes.add(IMAGE_JPEG);
- sSupportedContentTypes.add(IMAGE_GIF);
- sSupportedContentTypes.add(IMAGE_WBMP);
- sSupportedContentTypes.add(IMAGE_PNG);
- sSupportedContentTypes.add(IMAGE_JPG);
- sSupportedContentTypes.add(IMAGE_X_MS_BMP);
- //supportedContentTypes.add(IMAGE_SVG); not yet supported.
-
- sSupportedContentTypes.add(AUDIO_AAC);
- sSupportedContentTypes.add(AUDIO_AMR);
- sSupportedContentTypes.add(AUDIO_IMELODY);
- sSupportedContentTypes.add(AUDIO_MID);
- sSupportedContentTypes.add(AUDIO_MIDI);
- sSupportedContentTypes.add(AUDIO_MP3);
- sSupportedContentTypes.add(AUDIO_MP4);
- sSupportedContentTypes.add(AUDIO_MPEG3);
- sSupportedContentTypes.add(AUDIO_MPEG);
- sSupportedContentTypes.add(AUDIO_MPG);
- sSupportedContentTypes.add(AUDIO_X_MID);
- sSupportedContentTypes.add(AUDIO_X_MIDI);
- sSupportedContentTypes.add(AUDIO_X_MP3);
- sSupportedContentTypes.add(AUDIO_X_MPEG3);
- sSupportedContentTypes.add(AUDIO_X_MPEG);
- sSupportedContentTypes.add(AUDIO_X_MPG);
- sSupportedContentTypes.add(AUDIO_X_WAV);
- sSupportedContentTypes.add(AUDIO_3GPP);
- sSupportedContentTypes.add(AUDIO_OGG);
- sSupportedContentTypes.add(AUDIO_OGG2);
-
- sSupportedContentTypes.add(VIDEO_3GPP);
- sSupportedContentTypes.add(VIDEO_3G2);
- sSupportedContentTypes.add(VIDEO_H263);
- sSupportedContentTypes.add(VIDEO_MP4);
-
- sSupportedContentTypes.add(APP_SMIL);
- sSupportedContentTypes.add(APP_WAP_XHTML);
- sSupportedContentTypes.add(APP_XHTML);
-
- sSupportedContentTypes.add(APP_DRM_CONTENT);
- sSupportedContentTypes.add(APP_DRM_MESSAGE);
-
- // add supported image types
- sSupportedImageTypes.add(IMAGE_JPEG);
- sSupportedImageTypes.add(IMAGE_GIF);
- sSupportedImageTypes.add(IMAGE_WBMP);
- sSupportedImageTypes.add(IMAGE_PNG);
- sSupportedImageTypes.add(IMAGE_JPG);
- sSupportedImageTypes.add(IMAGE_X_MS_BMP);
-
- // add supported audio types
- sSupportedAudioTypes.add(AUDIO_AAC);
- sSupportedAudioTypes.add(AUDIO_AMR);
- sSupportedAudioTypes.add(AUDIO_IMELODY);
- sSupportedAudioTypes.add(AUDIO_MID);
- sSupportedAudioTypes.add(AUDIO_MIDI);
- sSupportedAudioTypes.add(AUDIO_MP3);
- sSupportedAudioTypes.add(AUDIO_MPEG3);
- sSupportedAudioTypes.add(AUDIO_MPEG);
- sSupportedAudioTypes.add(AUDIO_MPG);
- sSupportedAudioTypes.add(AUDIO_MP4);
- sSupportedAudioTypes.add(AUDIO_X_MID);
- sSupportedAudioTypes.add(AUDIO_X_MIDI);
- sSupportedAudioTypes.add(AUDIO_X_MP3);
- sSupportedAudioTypes.add(AUDIO_X_MPEG3);
- sSupportedAudioTypes.add(AUDIO_X_MPEG);
- sSupportedAudioTypes.add(AUDIO_X_MPG);
- sSupportedAudioTypes.add(AUDIO_X_WAV);
- sSupportedAudioTypes.add(AUDIO_3GPP);
- sSupportedAudioTypes.add(AUDIO_OGG);
- sSupportedAudioTypes.add(AUDIO_OGG2);
-
- // add supported video types
- sSupportedVideoTypes.add(VIDEO_3GPP);
- sSupportedVideoTypes.add(VIDEO_3G2);
- sSupportedVideoTypes.add(VIDEO_H263);
- sSupportedVideoTypes.add(VIDEO_MP4);
- }
-
- // This class should never be instantiated.
- private ContentType() {
- }
-
- public static boolean isSupportedType(String contentType) {
- return (null != contentType) && sSupportedContentTypes.contains(contentType);
- }
-
- public static boolean isSupportedImageType(String contentType) {
- return isImageType(contentType) && isSupportedType(contentType);
- }
-
- public static boolean isSupportedAudioType(String contentType) {
- return isAudioType(contentType) && isSupportedType(contentType);
- }
-
- public static boolean isSupportedVideoType(String contentType) {
- return isVideoType(contentType) && isSupportedType(contentType);
- }
-
- public static boolean isTextType(String contentType) {
- return (null != contentType) && contentType.startsWith("text/");
- }
-
- public static boolean isImageType(String contentType) {
- return (null != contentType) && contentType.startsWith("image/");
- }
-
- public static boolean isAudioType(String contentType) {
- return (null != contentType) && contentType.startsWith("audio/");
- }
-
- public static boolean isVideoType(String contentType) {
- return (null != contentType) && contentType.startsWith("video/");
- }
-
- public static boolean isDrmType(String contentType) {
- return (null != contentType)
- && (contentType.equals(APP_DRM_CONTENT)
- || contentType.equals(APP_DRM_MESSAGE));
- }
-
- public static boolean isUnspecified(String contentType) {
- return (null != contentType) && contentType.endsWith("*");
- }
-
- @SuppressWarnings("unchecked")
- public static ArrayList<String> getImageTypes() {
- return (ArrayList<String>) sSupportedImageTypes.clone();
- }
-
- @SuppressWarnings("unchecked")
- public static ArrayList<String> getAudioTypes() {
- return (ArrayList<String>) sSupportedAudioTypes.clone();
- }
-
- @SuppressWarnings("unchecked")
- public static ArrayList<String> getVideoTypes() {
- return (ArrayList<String>) sSupportedVideoTypes.clone();
- }
-
- @SuppressWarnings("unchecked")
- public static ArrayList<String> getSupportedTypes() {
- return (ArrayList<String>) sSupportedContentTypes.clone();
- }
-}
diff --git a/src/java/com/google/android/mms/InvalidHeaderValueException.java b/src/java/com/google/android/mms/InvalidHeaderValueException.java
deleted file mode 100644
index 73d78328e2..0000000000
--- a/src/java/com/google/android/mms/InvalidHeaderValueException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms;
-
-/**
- * Thrown when an invalid header value was set.
- */
-public class InvalidHeaderValueException extends MmsException {
- private static final long serialVersionUID = -2053384496042052262L;
-
- /**
- * Constructs an InvalidHeaderValueException with no detailed message.
- */
- public InvalidHeaderValueException() {
- super();
- }
-
- /**
- * Constructs an InvalidHeaderValueException with the specified detailed message.
- *
- * @param message the detailed message.
- */
- public InvalidHeaderValueException(String message) {
- super(message);
- }
-}
diff --git a/src/java/com/google/android/mms/MmsException.java b/src/java/com/google/android/mms/MmsException.java
deleted file mode 100644
index 6ca0c7eab9..0000000000
--- a/src/java/com/google/android/mms/MmsException.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms;
-
-/**
- * A generic exception that is thrown by the Mms client.
- */
-public class MmsException extends Exception {
- private static final long serialVersionUID = -7323249827281485390L;
-
- /**
- * Creates a new MmsException.
- */
- public MmsException() {
- super();
- }
-
- /**
- * Creates a new MmsException with the specified detail message.
- *
- * @param message the detail message.
- */
- public MmsException(String message) {
- super(message);
- }
-
- /**
- * Creates a new MmsException with the specified cause.
- *
- * @param cause the cause.
- */
- public MmsException(Throwable cause) {
- super(cause);
- }
-
- /**
- * Creates a new MmsException with the specified detail message and cause.
- *
- * @param message the detail message.
- * @param cause the cause.
- */
- public MmsException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/src/java/com/google/android/mms/package.html b/src/java/com/google/android/mms/package.html
deleted file mode 100755
index c9f96a66ab..0000000000
--- a/src/java/com/google/android/mms/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/src/java/com/google/android/mms/pdu/AcknowledgeInd.java b/src/java/com/google/android/mms/pdu/AcknowledgeInd.java
deleted file mode 100644
index 0e96c60bd0..0000000000
--- a/src/java/com/google/android/mms/pdu/AcknowledgeInd.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-/**
- * M-Acknowledge.ind PDU.
- */
-public class AcknowledgeInd extends GenericPdu {
- /**
- * Constructor, used when composing a M-Acknowledge.ind pdu.
- *
- * @param mmsVersion current viersion of mms
- * @param transactionId the transaction-id value
- * @throws InvalidHeaderValueException if parameters are invalid.
- * NullPointerException if transactionId is null.
- */
- public AcknowledgeInd(int mmsVersion, byte[] transactionId)
- throws InvalidHeaderValueException {
- super();
-
- setMessageType(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
- setMmsVersion(mmsVersion);
- setTransactionId(transactionId);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- AcknowledgeInd(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get X-Mms-Report-Allowed field value.
- *
- * @return the X-Mms-Report-Allowed value
- */
- public int getReportAllowed() {
- return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
- }
-
- /**
- * Set X-Mms-Report-Allowed field value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setReportAllowed(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
- }
-
- /**
- * Get X-Mms-Transaction-Id field value.
- *
- * @return the X-Mms-Report-Allowed value
- */
- public byte[] getTransactionId() {
- return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
- }
-
- /**
- * Set X-Mms-Transaction-Id field value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTransactionId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/Base64.java b/src/java/com/google/android/mms/pdu/Base64.java
deleted file mode 100644
index 604bee0d50..0000000000
--- a/src/java/com/google/android/mms/pdu/Base64.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-public class Base64 {
- /**
- * Used to get the number of Quadruples.
- */
- static final int FOURBYTE = 4;
-
- /**
- * Byte used to pad output.
- */
- static final byte PAD = (byte) '=';
-
- /**
- * The base length.
- */
- static final int BASELENGTH = 255;
-
- // Create arrays to hold the base64 characters
- private static byte[] base64Alphabet = new byte[BASELENGTH];
-
- // Populating the character arrays
- static {
- for (int i = 0; i < BASELENGTH; i++) {
- base64Alphabet[i] = (byte) -1;
- }
- for (int i = 'Z'; i >= 'A'; i--) {
- base64Alphabet[i] = (byte) (i - 'A');
- }
- for (int i = 'z'; i >= 'a'; i--) {
- base64Alphabet[i] = (byte) (i - 'a' + 26);
- }
- for (int i = '9'; i >= '0'; i--) {
- base64Alphabet[i] = (byte) (i - '0' + 52);
- }
-
- base64Alphabet['+'] = 62;
- base64Alphabet['/'] = 63;
- }
-
- /**
- * Decodes Base64 data into octects
- *
- * @param base64Data Byte array containing Base64 data
- * @return Array containing decoded data.
- */
- public static byte[] decodeBase64(byte[] base64Data) {
- // RFC 2045 requires that we discard ALL non-Base64 characters
- base64Data = discardNonBase64(base64Data);
-
- // handle the edge case, so we don't have to worry about it later
- if (base64Data.length == 0) {
- return new byte[0];
- }
-
- int numberQuadruple = base64Data.length / FOURBYTE;
- byte decodedData[] = null;
- byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
-
- // Throw away anything not in base64Data
-
- int encodedIndex = 0;
- int dataIndex = 0;
- {
- // this sizes the output array properly - rlw
- int lastData = base64Data.length;
- // ignore the '=' padding
- while (base64Data[lastData - 1] == PAD) {
- if (--lastData == 0) {
- return new byte[0];
- }
- }
- decodedData = new byte[lastData - numberQuadruple];
- }
-
- for (int i = 0; i < numberQuadruple; i++) {
- dataIndex = i * 4;
- marker0 = base64Data[dataIndex + 2];
- marker1 = base64Data[dataIndex + 3];
-
- b1 = base64Alphabet[base64Data[dataIndex]];
- b2 = base64Alphabet[base64Data[dataIndex + 1]];
-
- if (marker0 != PAD && marker1 != PAD) {
- //No PAD e.g 3cQl
- b3 = base64Alphabet[marker0];
- b4 = base64Alphabet[marker1];
-
- decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
- decodedData[encodedIndex + 1] =
- (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
- decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
- } else if (marker0 == PAD) {
- //Two PAD e.g. 3c[Pad][Pad]
- decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
- } else if (marker1 == PAD) {
- //One PAD e.g. 3cQ[Pad]
- b3 = base64Alphabet[marker0];
-
- decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
- decodedData[encodedIndex + 1] =
- (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
- }
- encodedIndex += 3;
- }
- return decodedData;
- }
-
- /**
- * Check octect wheter it is a base64 encoding.
- *
- * @param octect to be checked byte
- * @return ture if it is base64 encoding, false otherwise.
- */
- private static boolean isBase64(byte octect) {
- if (octect == PAD) {
- return true;
- } else if (base64Alphabet[octect] == -1) {
- return false;
- } else {
- return true;
- }
- }
-
- /**
- * Discards any characters outside of the base64 alphabet, per
- * the requirements on page 25 of RFC 2045 - "Any characters
- * outside of the base64 alphabet are to be ignored in base64
- * encoded data."
- *
- * @param data The base-64 encoded data to groom
- * @return The data, less non-base64 characters (see RFC 2045).
- */
- static byte[] discardNonBase64(byte[] data) {
- byte groomedData[] = new byte[data.length];
- int bytesCopied = 0;
-
- for (int i = 0; i < data.length; i++) {
- if (isBase64(data[i])) {
- groomedData[bytesCopied++] = data[i];
- }
- }
-
- byte packedData[] = new byte[bytesCopied];
-
- System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
-
- return packedData;
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/CharacterSets.java b/src/java/com/google/android/mms/pdu/CharacterSets.java
deleted file mode 100644
index 4e22ca5e2c..0000000000
--- a/src/java/com/google/android/mms/pdu/CharacterSets.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import java.io.UnsupportedEncodingException;
-import java.util.HashMap;
-
-public class CharacterSets {
- /**
- * IANA assigned MIB enum numbers.
- *
- * From wap-230-wsp-20010705-a.pdf
- * Any-charset = <Octet 128>
- * Equivalent to the special RFC2616 charset value "*"
- */
- public static final int ANY_CHARSET = 0x00;
- public static final int US_ASCII = 0x03;
- public static final int ISO_8859_1 = 0x04;
- public static final int ISO_8859_2 = 0x05;
- public static final int ISO_8859_3 = 0x06;
- public static final int ISO_8859_4 = 0x07;
- public static final int ISO_8859_5 = 0x08;
- public static final int ISO_8859_6 = 0x09;
- public static final int ISO_8859_7 = 0x0A;
- public static final int ISO_8859_8 = 0x0B;
- public static final int ISO_8859_9 = 0x0C;
- public static final int SHIFT_JIS = 0x11;
- public static final int UTF_8 = 0x6A;
- public static final int BIG5 = 0x07EA;
- public static final int UCS2 = 0x03E8;
- public static final int UTF_16 = 0x03F7;
-
- /**
- * If the encoding of given data is unsupported, use UTF_8 to decode it.
- */
- public static final int DEFAULT_CHARSET = UTF_8;
-
- /**
- * Array of MIB enum numbers.
- */
- private static final int[] MIBENUM_NUMBERS = {
- ANY_CHARSET,
- US_ASCII,
- ISO_8859_1,
- ISO_8859_2,
- ISO_8859_3,
- ISO_8859_4,
- ISO_8859_5,
- ISO_8859_6,
- ISO_8859_7,
- ISO_8859_8,
- ISO_8859_9,
- SHIFT_JIS,
- UTF_8,
- BIG5,
- UCS2,
- UTF_16,
- };
-
- /**
- * The Well-known-charset Mime name.
- */
- public static final String MIMENAME_ANY_CHARSET = "*";
- public static final String MIMENAME_US_ASCII = "us-ascii";
- public static final String MIMENAME_ISO_8859_1 = "iso-8859-1";
- public static final String MIMENAME_ISO_8859_2 = "iso-8859-2";
- public static final String MIMENAME_ISO_8859_3 = "iso-8859-3";
- public static final String MIMENAME_ISO_8859_4 = "iso-8859-4";
- public static final String MIMENAME_ISO_8859_5 = "iso-8859-5";
- public static final String MIMENAME_ISO_8859_6 = "iso-8859-6";
- public static final String MIMENAME_ISO_8859_7 = "iso-8859-7";
- public static final String MIMENAME_ISO_8859_8 = "iso-8859-8";
- public static final String MIMENAME_ISO_8859_9 = "iso-8859-9";
- public static final String MIMENAME_SHIFT_JIS = "shift_JIS";
- public static final String MIMENAME_UTF_8 = "utf-8";
- public static final String MIMENAME_BIG5 = "big5";
- public static final String MIMENAME_UCS2 = "iso-10646-ucs-2";
- public static final String MIMENAME_UTF_16 = "utf-16";
-
- public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8;
-
- /**
- * Array of the names of character sets.
- */
- private static final String[] MIME_NAMES = {
- MIMENAME_ANY_CHARSET,
- MIMENAME_US_ASCII,
- MIMENAME_ISO_8859_1,
- MIMENAME_ISO_8859_2,
- MIMENAME_ISO_8859_3,
- MIMENAME_ISO_8859_4,
- MIMENAME_ISO_8859_5,
- MIMENAME_ISO_8859_6,
- MIMENAME_ISO_8859_7,
- MIMENAME_ISO_8859_8,
- MIMENAME_ISO_8859_9,
- MIMENAME_SHIFT_JIS,
- MIMENAME_UTF_8,
- MIMENAME_BIG5,
- MIMENAME_UCS2,
- MIMENAME_UTF_16,
- };
-
- private static final HashMap<Integer, String> MIBENUM_TO_NAME_MAP;
- private static final HashMap<String, Integer> NAME_TO_MIBENUM_MAP;
-
- static {
- // Create the HashMaps.
- MIBENUM_TO_NAME_MAP = new HashMap<Integer, String>();
- NAME_TO_MIBENUM_MAP = new HashMap<String, Integer>();
- assert(MIBENUM_NUMBERS.length == MIME_NAMES.length);
- int count = MIBENUM_NUMBERS.length - 1;
- for(int i = 0; i <= count; i++) {
- MIBENUM_TO_NAME_MAP.put(MIBENUM_NUMBERS[i], MIME_NAMES[i]);
- NAME_TO_MIBENUM_MAP.put(MIME_NAMES[i], MIBENUM_NUMBERS[i]);
- }
- }
-
- private CharacterSets() {} // Non-instantiatable
-
- /**
- * Map an MIBEnum number to the name of the charset which this number
- * is assigned to by IANA.
- *
- * @param mibEnumValue An IANA assigned MIBEnum number.
- * @return The name string of the charset.
- * @throws UnsupportedEncodingException
- */
- public static String getMimeName(int mibEnumValue)
- throws UnsupportedEncodingException {
- String name = MIBENUM_TO_NAME_MAP.get(mibEnumValue);
- if (name == null) {
- throw new UnsupportedEncodingException();
- }
- return name;
- }
-
- /**
- * Map a well-known charset name to its assigned MIBEnum number.
- *
- * @param mimeName The charset name.
- * @return The MIBEnum number assigned by IANA for this charset.
- * @throws UnsupportedEncodingException
- */
- public static int getMibEnumValue(String mimeName)
- throws UnsupportedEncodingException {
- if(null == mimeName) {
- return -1;
- }
-
- Integer mibEnumValue = NAME_TO_MIBENUM_MAP.get(mimeName);
- if (mibEnumValue == null) {
- throw new UnsupportedEncodingException();
- }
- return mibEnumValue;
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/DeliveryInd.java b/src/java/com/google/android/mms/pdu/DeliveryInd.java
deleted file mode 100644
index dafa8d11b8..0000000000
--- a/src/java/com/google/android/mms/pdu/DeliveryInd.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-/**
- * M-Delivery.Ind Pdu.
- */
-public class DeliveryInd extends GenericPdu {
- /**
- * Empty constructor.
- * Since the Pdu corresponding to this class is constructed
- * by the Proxy-Relay server, this class is only instantiated
- * by the Pdu Parser.
- *
- * @throws InvalidHeaderValueException if error occurs.
- */
- public DeliveryInd() throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_DELIVERY_IND);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- DeliveryInd(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get Date value.
- *
- * @return the value
- */
- public long getDate() {
- return mPduHeaders.getLongInteger(PduHeaders.DATE);
- }
-
- /**
- * Set Date value.
- *
- * @param value the value
- */
- public void setDate(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.DATE);
- }
-
- /**
- * Get Message-ID value.
- *
- * @return the value
- */
- public byte[] getMessageId() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Set Message-ID value.
- *
- * @param value the value, should not be null
- * @throws NullPointerException if the value is null.
- */
- public void setMessageId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Get Status value.
- *
- * @return the value
- */
- public int getStatus() {
- return mPduHeaders.getOctet(PduHeaders.STATUS);
- }
-
- /**
- * Set Status value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setStatus(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.STATUS);
- }
-
- /**
- * Get To value.
- *
- * @return the value
- */
- public EncodedStringValue[] getTo() {
- return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
- }
-
- /**
- * set To value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTo(EncodedStringValue[] value) {
- mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
- }
-
- /*
- * Optional, not supported header fields:
- *
- * public byte[] getApplicId() {return null;}
- * public void setApplicId(byte[] value) {}
- *
- * public byte[] getAuxApplicId() {return null;}
- * public void getAuxApplicId(byte[] value) {}
- *
- * public byte[] getReplyApplicId() {return 0x00;}
- * public void setReplyApplicId(byte[] value) {}
- *
- * public EncodedStringValue getStatusText() {return null;}
- * public void setStatusText(EncodedStringValue value) {}
- */
-}
diff --git a/src/java/com/google/android/mms/pdu/EncodedStringValue.java b/src/java/com/google/android/mms/pdu/EncodedStringValue.java
deleted file mode 100644
index 14ef7722d5..0000000000
--- a/src/java/com/google/android/mms/pdu/EncodedStringValue.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Esmertec AG.
- * Copyright (C) 2007-2008 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.google.android.mms.pdu;
-
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-
-/**
- * Encoded-string-value = Text-string | Value-length Char-set Text-string
- */
-public class EncodedStringValue implements Cloneable {
- private static final String TAG = "EncodedStringValue";
- private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = false;
-
- /**
- * The Char-set value.
- */
- private int mCharacterSet;
-
- /**
- * The Text-string value.
- */
- private byte[] mData;
-
- /**
- * Constructor.
- *
- * @param charset the Char-set value
- * @param data the Text-string value
- * @throws NullPointerException if Text-string value is null.
- */
- public EncodedStringValue(int charset, byte[] data) {
- // TODO: CharSet needs to be validated against MIBEnum.
- if(null == data) {
- throw new NullPointerException("EncodedStringValue: Text-string is null.");
- }
-
- mCharacterSet = charset;
- mData = new byte[data.length];
- System.arraycopy(data, 0, mData, 0, data.length);
- }
-
- /**
- * Constructor.
- *
- * @param data the Text-string value
- * @throws NullPointerException if Text-string value is null.
- */
- public EncodedStringValue(byte[] data) {
- this(CharacterSets.DEFAULT_CHARSET, data);
- }
-
- public EncodedStringValue(String data) {
- try {
- mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
- mCharacterSet = CharacterSets.DEFAULT_CHARSET;
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Default encoding must be supported.", e);
- }
- }
-
- /**
- * Get Char-set value.
- *
- * @return the value
- */
- public int getCharacterSet() {
- return mCharacterSet;
- }
-
- /**
- * Set Char-set value.
- *
- * @param charset the Char-set value
- */
- public void setCharacterSet(int charset) {
- // TODO: CharSet needs to be validated against MIBEnum.
- mCharacterSet = charset;
- }
-
- /**
- * Get Text-string value.
- *
- * @return the value
- */
- public byte[] getTextString() {
- byte[] byteArray = new byte[mData.length];
-
- System.arraycopy(mData, 0, byteArray, 0, mData.length);
- return byteArray;
- }
-
- /**
- * Set Text-string value.
- *
- * @param textString the Text-string value
- * @throws NullPointerException if Text-string value is null.
- */
- public void setTextString(byte[] textString) {
- if(null == textString) {
- throw new NullPointerException("EncodedStringValue: Text-string is null.");
- }
-
- mData = new byte[textString.length];
- System.arraycopy(textString, 0, mData, 0, textString.length);
- }
-
- /**
- * Convert this object to a {@link java.lang.String}. If the encoding of
- * the EncodedStringValue is null or unsupported, it will be
- * treated as iso-8859-1 encoding.
- *
- * @return The decoded String.
- */
- public String getString() {
- if (CharacterSets.ANY_CHARSET == mCharacterSet) {
- return new String(mData); // system default encoding.
- } else {
- try {
- String name = CharacterSets.getMimeName(mCharacterSet);
- return new String(mData, name);
- } catch (UnsupportedEncodingException e) {
- if (LOCAL_LOGV) {
- Log.v(TAG, e.getMessage(), e);
- }
- try {
- return new String(mData, CharacterSets.MIMENAME_ISO_8859_1);
- } catch (UnsupportedEncodingException e2) {
- return new String(mData); // system default encoding.
- }
- }
- }
- }
-
- /**
- * Append to Text-string.
- *
- * @param textString the textString to append
- * @throws NullPointerException if the text String is null
- * or an IOException occured.
- */
- public void appendTextString(byte[] textString) {
- if(null == textString) {
- throw new NullPointerException("Text-string is null.");
- }
-
- if(null == mData) {
- mData = new byte[textString.length];
- System.arraycopy(textString, 0, mData, 0, textString.length);
- } else {
- ByteArrayOutputStream newTextString = new ByteArrayOutputStream();
- try {
- newTextString.write(mData);
- newTextString.write(textString);
- } catch (IOException e) {
- e.printStackTrace();
- throw new NullPointerException(
- "appendTextString: failed when write a new Text-string");
- }
-
- mData = newTextString.toByteArray();
- }
- }
-
- /*
- * (non-Javadoc)
- * @see java.lang.Object#clone()
- */
- @Override
- public Object clone() throws CloneNotSupportedException {
- super.clone();
- int len = mData.length;
- byte[] dstBytes = new byte[len];
- System.arraycopy(mData, 0, dstBytes, 0, len);
-
- try {
- return new EncodedStringValue(mCharacterSet, dstBytes);
- } catch (Exception e) {
- Log.e(TAG, "failed to clone an EncodedStringValue: " + this);
- e.printStackTrace();
- throw new CloneNotSupportedException(e.getMessage());
- }
- }
-
- /**
- * Split this encoded string around matches of the given pattern.
- *
- * @param pattern the delimiting pattern
- * @return the array of encoded strings computed by splitting this encoded
- * string around matches of the given pattern
- */
- public EncodedStringValue[] split(String pattern) {
- String[] temp = getString().split(pattern);
- EncodedStringValue[] ret = new EncodedStringValue[temp.length];
- for (int i = 0; i < ret.length; ++i) {
- try {
- ret[i] = new EncodedStringValue(mCharacterSet,
- temp[i].getBytes());
- } catch (NullPointerException e) {
- // Can't arrive here
- return null;
- }
- }
- return ret;
- }
-
- /**
- * Extract an EncodedStringValue[] from a given String.
- */
- public static EncodedStringValue[] extract(String src) {
- String[] values = src.split(";");
-
- ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
- for (int i = 0; i < values.length; i++) {
- if (values[i].length() > 0) {
- list.add(new EncodedStringValue(values[i]));
- }
- }
-
- int len = list.size();
- if (len > 0) {
- return list.toArray(new EncodedStringValue[len]);
- } else {
- return null;
- }
- }
-
- /**
- * Concatenate an EncodedStringValue[] into a single String.
- */
- public static String concat(EncodedStringValue[] addr) {
- StringBuilder sb = new StringBuilder();
- int maxIndex = addr.length - 1;
- for (int i = 0; i <= maxIndex; i++) {
- sb.append(addr[i].getString());
- if (i < maxIndex) {
- sb.append(";");
- }
- }
-
- return sb.toString();
- }
-
- public static EncodedStringValue copy(EncodedStringValue value) {
- if (value == null) {
- return null;
- }
-
- return new EncodedStringValue(value.mCharacterSet, value.mData);
- }
-
- public static EncodedStringValue[] encodeStrings(String[] array) {
- int count = array.length;
- if (count > 0) {
- EncodedStringValue[] encodedArray = new EncodedStringValue[count];
- for (int i = 0; i < count; i++) {
- encodedArray[i] = new EncodedStringValue(array[i]);
- }
- return encodedArray;
- }
- return null;
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/GenericPdu.java b/src/java/com/google/android/mms/pdu/GenericPdu.java
deleted file mode 100644
index 705de6afb8..0000000000
--- a/src/java/com/google/android/mms/pdu/GenericPdu.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-public class GenericPdu {
- /**
- * The headers of pdu.
- */
- PduHeaders mPduHeaders = null;
-
- /**
- * Constructor.
- */
- public GenericPdu() {
- mPduHeaders = new PduHeaders();
- }
-
- /**
- * Constructor.
- *
- * @param headers Headers for this PDU.
- */
- GenericPdu(PduHeaders headers) {
- mPduHeaders = headers;
- }
-
- /**
- * Get the headers of this PDU.
- *
- * @return A PduHeaders of this PDU.
- */
- PduHeaders getPduHeaders() {
- return mPduHeaders;
- }
-
- /**
- * Get X-Mms-Message-Type field value.
- *
- * @return the X-Mms-Report-Allowed value
- */
- public int getMessageType() {
- return mPduHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
- }
-
- /**
- * Set X-Mms-Message-Type field value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- * RuntimeException if field's value is not Octet.
- */
- public void setMessageType(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.MESSAGE_TYPE);
- }
-
- /**
- * Get X-Mms-MMS-Version field value.
- *
- * @return the X-Mms-MMS-Version value
- */
- public int getMmsVersion() {
- return mPduHeaders.getOctet(PduHeaders.MMS_VERSION);
- }
-
- /**
- * Set X-Mms-MMS-Version field value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- * RuntimeException if field's value is not Octet.
- */
- public void setMmsVersion(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION);
- }
-
- /**
- * Get From value.
- * From-value = Value-length
- * (Address-present-token Encoded-string-value | Insert-address-token)
- *
- * @return the value
- */
- public EncodedStringValue getFrom() {
- return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
- }
-
- /**
- * Set From value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setFrom(EncodedStringValue value) {
- mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/MultimediaMessagePdu.java b/src/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
deleted file mode 100644
index 5a85e0e79d..0000000000
--- a/src/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-/**
- * Multimedia message PDU.
- */
-public class MultimediaMessagePdu extends GenericPdu{
- /**
- * The body.
- */
- private PduBody mMessageBody;
-
- /**
- * Constructor.
- */
- public MultimediaMessagePdu() {
- super();
- }
-
- /**
- * Constructor.
- *
- * @param header the header of this PDU
- * @param body the body of this PDU
- */
- public MultimediaMessagePdu(PduHeaders header, PduBody body) {
- super(header);
- mMessageBody = body;
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- MultimediaMessagePdu(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get body of the PDU.
- *
- * @return the body
- */
- public PduBody getBody() {
- return mMessageBody;
- }
-
- /**
- * Set body of the PDU.
- *
- * @param body the body
- */
- public void setBody(PduBody body) {
- mMessageBody = body;
- }
-
- /**
- * Get subject.
- *
- * @return the value
- */
- public EncodedStringValue getSubject() {
- return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
- }
-
- /**
- * Set subject.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setSubject(EncodedStringValue value) {
- mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
- }
-
- /**
- * Get To value.
- *
- * @return the value
- */
- public EncodedStringValue[] getTo() {
- return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
- }
-
- /**
- * Add a "To" value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void addTo(EncodedStringValue value) {
- mPduHeaders.appendEncodedStringValue(value, PduHeaders.TO);
- }
-
- /**
- * Get X-Mms-Priority value.
- *
- * @return the value
- */
- public int getPriority() {
- return mPduHeaders.getOctet(PduHeaders.PRIORITY);
- }
-
- /**
- * Set X-Mms-Priority value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setPriority(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.PRIORITY);
- }
-
- /**
- * Get Date value.
- *
- * @return the value
- */
- public long getDate() {
- return mPduHeaders.getLongInteger(PduHeaders.DATE);
- }
-
- /**
- * Set Date value in seconds.
- *
- * @param value the value
- */
- public void setDate(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.DATE);
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/NotificationInd.java b/src/java/com/google/android/mms/pdu/NotificationInd.java
deleted file mode 100644
index c56cba6d14..0000000000
--- a/src/java/com/google/android/mms/pdu/NotificationInd.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-/**
- * M-Notification.ind PDU.
- */
-public class NotificationInd extends GenericPdu {
- /**
- * Empty constructor.
- * Since the Pdu corresponding to this class is constructed
- * by the Proxy-Relay server, this class is only instantiated
- * by the Pdu Parser.
- *
- * @throws InvalidHeaderValueException if error occurs.
- * RuntimeException if an undeclared error occurs.
- */
- public NotificationInd() throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- NotificationInd(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get X-Mms-Content-Class Value.
- *
- * @return the value
- */
- public int getContentClass() {
- return mPduHeaders.getOctet(PduHeaders.CONTENT_CLASS);
- }
-
- /**
- * Set X-Mms-Content-Class Value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- * RuntimeException if an undeclared error occurs.
- */
- public void setContentClass(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.CONTENT_CLASS);
- }
-
- /**
- * Get X-Mms-Content-Location value.
- * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
- * Content-location-value = Uri-value
- *
- * @return the value
- */
- public byte[] getContentLocation() {
- return mPduHeaders.getTextString(PduHeaders.CONTENT_LOCATION);
- }
-
- /**
- * Set X-Mms-Content-Location value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- * RuntimeException if an undeclared error occurs.
- */
- public void setContentLocation(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.CONTENT_LOCATION);
- }
-
- /**
- * Get X-Mms-Expiry value.
- *
- * Expiry-value = Value-length
- * (Absolute-token Date-value | Relative-token Delta-seconds-value)
- *
- * @return the value
- */
- public long getExpiry() {
- return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
- }
-
- /**
- * Set X-Mms-Expiry value.
- *
- * @param value the value
- * @throws RuntimeException if an undeclared error occurs.
- */
- public void setExpiry(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
- }
-
- /**
- * Get From value.
- * From-value = Value-length
- * (Address-present-token Encoded-string-value | Insert-address-token)
- *
- * @return the value
- */
- public EncodedStringValue getFrom() {
- return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
- }
-
- /**
- * Set From value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- * RuntimeException if an undeclared error occurs.
- */
- public void setFrom(EncodedStringValue value) {
- mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
- }
-
- /**
- * Get X-Mms-Message-Class value.
- * Message-class-value = Class-identifier | Token-text
- * Class-identifier = Personal | Advertisement | Informational | Auto
- *
- * @return the value
- */
- public byte[] getMessageClass() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
- }
-
- /**
- * Set X-Mms-Message-Class value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- * RuntimeException if an undeclared error occurs.
- */
- public void setMessageClass(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
- }
-
- /**
- * Get X-Mms-Message-Size value.
- * Message-size-value = Long-integer
- *
- * @return the value
- */
- public long getMessageSize() {
- return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
- }
-
- /**
- * Set X-Mms-Message-Size value.
- *
- * @param value the value
- * @throws RuntimeException if an undeclared error occurs.
- */
- public void setMessageSize(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
- }
-
- /**
- * Get subject.
- *
- * @return the value
- */
- public EncodedStringValue getSubject() {
- return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
- }
-
- /**
- * Set subject.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- * RuntimeException if an undeclared error occurs.
- */
- public void setSubject(EncodedStringValue value) {
- mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
- }
-
- /**
- * Get X-Mms-Transaction-Id.
- *
- * @return the value
- */
- public byte[] getTransactionId() {
- return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
- }
-
- /**
- * Set X-Mms-Transaction-Id.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- * RuntimeException if an undeclared error occurs.
- */
- public void setTransactionId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
- }
-
- /**
- * Get X-Mms-Delivery-Report Value.
- *
- * @return the value
- */
- public int getDeliveryReport() {
- return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
- }
-
- /**
- * Set X-Mms-Delivery-Report Value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- * RuntimeException if an undeclared error occurs.
- */
- public void setDeliveryReport(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
- }
-
- /*
- * Optional, not supported header fields:
- *
- * public byte[] getApplicId() {return null;}
- * public void setApplicId(byte[] value) {}
- *
- * public byte[] getAuxApplicId() {return null;}
- * public void getAuxApplicId(byte[] value) {}
- *
- * public byte getDrmContent() {return 0x00;}
- * public void setDrmContent(byte value) {}
- *
- * public byte getDistributionIndicator() {return 0x00;}
- * public void setDistributionIndicator(byte value) {}
- *
- * public ElementDescriptorValue getElementDescriptor() {return null;}
- * public void getElementDescriptor(ElementDescriptorValue value) {}
- *
- * public byte getPriority() {return 0x00;}
- * public void setPriority(byte value) {}
- *
- * public byte getRecommendedRetrievalMode() {return 0x00;}
- * public void setRecommendedRetrievalMode(byte value) {}
- *
- * public byte getRecommendedRetrievalModeText() {return 0x00;}
- * public void setRecommendedRetrievalModeText(byte value) {}
- *
- * public byte[] getReplaceId() {return 0x00;}
- * public void setReplaceId(byte[] value) {}
- *
- * public byte[] getReplyApplicId() {return 0x00;}
- * public void setReplyApplicId(byte[] value) {}
- *
- * public byte getReplyCharging() {return 0x00;}
- * public void setReplyCharging(byte value) {}
- *
- * public byte getReplyChargingDeadline() {return 0x00;}
- * public void setReplyChargingDeadline(byte value) {}
- *
- * public byte[] getReplyChargingId() {return 0x00;}
- * public void setReplyChargingId(byte[] value) {}
- *
- * public long getReplyChargingSize() {return 0;}
- * public void setReplyChargingSize(long value) {}
- *
- * public byte getStored() {return 0x00;}
- * public void setStored(byte value) {}
- */
-}
diff --git a/src/java/com/google/android/mms/pdu/NotifyRespInd.java b/src/java/com/google/android/mms/pdu/NotifyRespInd.java
deleted file mode 100644
index 2cc2fce081..0000000000
--- a/src/java/com/google/android/mms/pdu/NotifyRespInd.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-/**
- * M-NofifyResp.ind PDU.
- */
-public class NotifyRespInd extends GenericPdu {
- /**
- * Constructor, used when composing a M-NotifyResp.ind pdu.
- *
- * @param mmsVersion current version of mms
- * @param transactionId the transaction-id value
- * @param status the status value
- * @throws InvalidHeaderValueException if parameters are invalid.
- * NullPointerException if transactionId is null.
- * RuntimeException if an undeclared error occurs.
- */
- public NotifyRespInd(int mmsVersion,
- byte[] transactionId,
- int status) throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
- setMmsVersion(mmsVersion);
- setTransactionId(transactionId);
- setStatus(status);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- NotifyRespInd(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get X-Mms-Report-Allowed field value.
- *
- * @return the X-Mms-Report-Allowed value
- */
- public int getReportAllowed() {
- return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
- }
-
- /**
- * Set X-Mms-Report-Allowed field value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- * RuntimeException if an undeclared error occurs.
- */
- public void setReportAllowed(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
- }
-
- /**
- * Set X-Mms-Status field value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- * RuntimeException if an undeclared error occurs.
- */
- public void setStatus(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.STATUS);
- }
-
- /**
- * GetX-Mms-Status field value.
- *
- * @return the X-Mms-Status value
- */
- public int getStatus() {
- return mPduHeaders.getOctet(PduHeaders.STATUS);
- }
-
- /**
- * Get X-Mms-Transaction-Id field value.
- *
- * @return the X-Mms-Report-Allowed value
- */
- public byte[] getTransactionId() {
- return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
- }
-
- /**
- * Set X-Mms-Transaction-Id field value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- * RuntimeException if an undeclared error occurs.
- */
- public void setTransactionId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/PduBody.java b/src/java/com/google/android/mms/pdu/PduBody.java
deleted file mode 100644
index fa0416c6be..0000000000
--- a/src/java/com/google/android/mms/pdu/PduBody.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Vector;
-
-public class PduBody {
- private Vector<PduPart> mParts = null;
-
- private Map<String, PduPart> mPartMapByContentId = null;
- private Map<String, PduPart> mPartMapByContentLocation = null;
- private Map<String, PduPart> mPartMapByName = null;
- private Map<String, PduPart> mPartMapByFileName = null;
-
- /**
- * Constructor.
- */
- public PduBody() {
- mParts = new Vector<PduPart>();
-
- mPartMapByContentId = new HashMap<String, PduPart>();
- mPartMapByContentLocation = new HashMap<String, PduPart>();
- mPartMapByName = new HashMap<String, PduPart>();
- mPartMapByFileName = new HashMap<String, PduPart>();
- }
-
- private void putPartToMaps(PduPart part) {
- // Put part to mPartMapByContentId.
- byte[] contentId = part.getContentId();
- if(null != contentId) {
- mPartMapByContentId.put(new String(contentId), part);
- }
-
- // Put part to mPartMapByContentLocation.
- byte[] contentLocation = part.getContentLocation();
- if(null != contentLocation) {
- String clc = new String(contentLocation);
- mPartMapByContentLocation.put(clc, part);
- }
-
- // Put part to mPartMapByName.
- byte[] name = part.getName();
- if(null != name) {
- String clc = new String(name);
- mPartMapByName.put(clc, part);
- }
-
- // Put part to mPartMapByFileName.
- byte[] fileName = part.getFilename();
- if(null != fileName) {
- String clc = new String(fileName);
- mPartMapByFileName.put(clc, part);
- }
- }
-
- /**
- * Appends the specified part to the end of this body.
- *
- * @param part part to be appended
- * @return true when success, false when fail
- * @throws NullPointerException when part is null
- */
- public boolean addPart(PduPart part) {
- if(null == part) {
- throw new NullPointerException();
- }
-
- putPartToMaps(part);
- return mParts.add(part);
- }
-
- /**
- * Inserts the specified part at the specified position.
- *
- * @param index index at which the specified part is to be inserted
- * @param part part to be inserted
- * @throws NullPointerException when part is null
- */
- public void addPart(int index, PduPart part) {
- if(null == part) {
- throw new NullPointerException();
- }
-
- putPartToMaps(part);
- mParts.add(index, part);
- }
-
- /**
- * Removes the part at the specified position.
- *
- * @param index index of the part to return
- * @return part at the specified index
- */
- public PduPart removePart(int index) {
- return mParts.remove(index);
- }
-
- /**
- * Remove all of the parts.
- */
- public void removeAll() {
- mParts.clear();
- }
-
- /**
- * Get the part at the specified position.
- *
- * @param index index of the part to return
- * @return part at the specified index
- */
- public PduPart getPart(int index) {
- return mParts.get(index);
- }
-
- /**
- * Get the index of the specified part.
- *
- * @param part the part object
- * @return index the index of the first occurrence of the part in this body
- */
- public int getPartIndex(PduPart part) {
- return mParts.indexOf(part);
- }
-
- /**
- * Get the number of parts.
- *
- * @return the number of parts
- */
- public int getPartsNum() {
- return mParts.size();
- }
-
- /**
- * Get pdu part by content id.
- *
- * @param cid the value of content id.
- * @return the pdu part.
- */
- public PduPart getPartByContentId(String cid) {
- return mPartMapByContentId.get(cid);
- }
-
- /**
- * Get pdu part by Content-Location. Content-Location of part is
- * the same as filename and name(param of content-type).
- *
- * @param fileName the value of filename.
- * @return the pdu part.
- */
- public PduPart getPartByContentLocation(String contentLocation) {
- return mPartMapByContentLocation.get(contentLocation);
- }
-
- /**
- * Get pdu part by name.
- *
- * @param fileName the value of filename.
- * @return the pdu part.
- */
- public PduPart getPartByName(String name) {
- return mPartMapByName.get(name);
- }
-
- /**
- * Get pdu part by filename.
- *
- * @param fileName the value of filename.
- * @return the pdu part.
- */
- public PduPart getPartByFileName(String filename) {
- return mPartMapByFileName.get(filename);
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/PduComposer.java b/src/java/com/google/android/mms/pdu/PduComposer.java
deleted file mode 100644
index 582caec401..0000000000
--- a/src/java/com/google/android/mms/pdu/PduComposer.java
+++ /dev/null
@@ -1,1202 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Esmertec AG.
- * Copyright (C) 2007-2008 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.google.android.mms.pdu;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.util.Log;
-import android.text.TextUtils;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.HashMap;
-
-public class PduComposer {
- /**
- * Address type.
- */
- static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1;
- static private final int PDU_EMAIL_ADDRESS_TYPE = 2;
- static private final int PDU_IPV4_ADDRESS_TYPE = 3;
- static private final int PDU_IPV6_ADDRESS_TYPE = 4;
- static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5;
-
- /**
- * Address regular expression string.
- */
- static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+";
- static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" +
- "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}";
- static final String REGEXP_IPV6_ADDRESS_TYPE =
- "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
- "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
- "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}";
- static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" +
- "[0-9]{1,3}\\.{1}[0-9]{1,3}";
-
- /**
- * The postfix strings of address.
- */
- static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN";
- static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4";
- static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6";
-
- /**
- * Error values.
- */
- static private final int PDU_COMPOSE_SUCCESS = 0;
- static private final int PDU_COMPOSE_CONTENT_ERROR = 1;
- static private final int PDU_COMPOSE_FIELD_NOT_SET = 2;
- static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3;
-
- /**
- * WAP values defined in WSP spec.
- */
- static private final int QUOTED_STRING_FLAG = 34;
- static private final int END_STRING_FLAG = 0;
- static private final int LENGTH_QUOTE = 31;
- static private final int TEXT_MAX = 127;
- static private final int SHORT_INTEGER_MAX = 127;
- static private final int LONG_INTEGER_LENGTH_MAX = 8;
-
- /**
- * Block size when read data from InputStream.
- */
- static private final int PDU_COMPOSER_BLOCK_SIZE = 1024;
-
- /**
- * The output message.
- */
- protected ByteArrayOutputStream mMessage = null;
-
- /**
- * The PDU.
- */
- private GenericPdu mPdu = null;
-
- /**
- * Current visiting position of the mMessage.
- */
- protected int mPosition = 0;
-
- /**
- * Message compose buffer stack.
- */
- private BufferStack mStack = null;
-
- /**
- * Content resolver.
- */
- private final ContentResolver mResolver;
-
- /**
- * Header of this pdu.
- */
- private PduHeaders mPduHeader = null;
-
- /**
- * Map of all content type
- */
- private static HashMap<String, Integer> mContentTypeMap = null;
-
- static {
- mContentTypeMap = new HashMap<String, Integer>();
-
- int i;
- for (i = 0; i < PduContentTypes.contentTypes.length; i++) {
- mContentTypeMap.put(PduContentTypes.contentTypes[i], i);
- }
- }
-
- /**
- * Constructor.
- *
- * @param context the context
- * @param pdu the pdu to be composed
- */
- public PduComposer(Context context, GenericPdu pdu) {
- mPdu = pdu;
- mResolver = context.getContentResolver();
- mPduHeader = pdu.getPduHeaders();
- mStack = new BufferStack();
- mMessage = new ByteArrayOutputStream();
- mPosition = 0;
- }
-
- /**
- * Make the message. No need to check whether mandatory fields are set,
- * because the constructors of outgoing pdus are taking care of this.
- *
- * @return OutputStream of maked message. Return null if
- * the PDU is invalid.
- */
- public byte[] make() {
- // Get Message-type.
- int type = mPdu.getMessageType();
-
- /* make the message */
- switch (type) {
- case PduHeaders.MESSAGE_TYPE_SEND_REQ:
- case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
- if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
- return null;
- }
- break;
- case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
- if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) {
- return null;
- }
- break;
- case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
- if (makeAckInd() != PDU_COMPOSE_SUCCESS) {
- return null;
- }
- break;
- case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
- if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) {
- return null;
- }
- break;
- default:
- return null;
- }
-
- return mMessage.toByteArray();
- }
-
- /**
- * Copy buf to mMessage.
- */
- protected void arraycopy(byte[] buf, int pos, int length) {
- mMessage.write(buf, pos, length);
- mPosition = mPosition + length;
- }
-
- /**
- * Append a byte to mMessage.
- */
- protected void append(int value) {
- mMessage.write(value);
- mPosition ++;
- }
-
- /**
- * Append short integer value to mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendShortInteger(int value) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Short-integer = OCTET
- * ; Integers in range 0-127 shall be encoded as a one octet value
- * ; with the most significant bit set to one (1xxx xxxx) and with
- * ; the value in the remaining least significant bits.
- * In our implementation, only low 7 bits are stored and otherwise
- * bits are ignored.
- */
- append((value | 0x80) & 0xff);
- }
-
- /**
- * Append an octet number between 128 and 255 into mMessage.
- * NOTE:
- * A value between 0 and 127 should be appended by using appendShortInteger.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendOctet(int number) {
- append(number);
- }
-
- /**
- * Append a short length into mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendShortLength(int value) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Short-length = <Any octet 0-30>
- */
- append(value);
- }
-
- /**
- * Append long integer into mMessage. it's used for really long integers.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendLongInteger(long longInt) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Long-integer = Short-length Multi-octet-integer
- * ; The Short-length indicates the length of the Multi-octet-integer
- * Multi-octet-integer = 1*30 OCTET
- * ; The content octets shall be an unsigned integer value with the
- * ; most significant octet encoded first (big-endian representation).
- * ; The minimum number of octets must be used to encode the value.
- */
- int size;
- long temp = longInt;
-
- // Count the length of the long integer.
- for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) {
- temp = (temp >>> 8);
- }
-
- // Set Length.
- appendShortLength(size);
-
- // Count and set the long integer.
- int i;
- int shift = (size -1) * 8;
-
- for (i = 0; i < size; i++) {
- append((int)((longInt >>> shift) & 0xff));
- shift = shift - 8;
- }
- }
-
- /**
- * Append text string into mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendTextString(byte[] text) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Text-string = [Quote] *TEXT End-of-string
- * ; If the first character in the TEXT is in the range of 128-255,
- * ; a Quote character must precede it. Otherwise the Quote character
- * ;must be omitted. The Quote is not part of the contents.
- */
- if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255
- append(TEXT_MAX);
- }
-
- arraycopy(text, 0, text.length);
- append(0);
- }
-
- /**
- * Append text string into mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendTextString(String str) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Text-string = [Quote] *TEXT End-of-string
- * ; If the first character in the TEXT is in the range of 128-255,
- * ; a Quote character must precede it. Otherwise the Quote character
- * ;must be omitted. The Quote is not part of the contents.
- */
- appendTextString(str.getBytes());
- }
-
- /**
- * Append encoded string value to mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendEncodedString(EncodedStringValue enStr) {
- /*
- * From OMA-TS-MMS-ENC-V1_3-20050927-C:
- * Encoded-string-value = Text-string | Value-length Char-set Text-string
- */
- assert(enStr != null);
-
- int charset = enStr.getCharacterSet();
- byte[] textString = enStr.getTextString();
- if (null == textString) {
- return;
- }
-
- /*
- * In the implementation of EncodedStringValue, the charset field will
- * never be 0. It will always be composed as
- * Encoded-string-value = Value-length Char-set Text-string
- */
- mStack.newbuf();
- PositionMarker start = mStack.mark();
-
- appendShortInteger(charset);
- appendTextString(textString);
-
- int len = start.getLength();
- mStack.pop();
- appendValueLength(len);
- mStack.copy();
- }
-
- /**
- * Append uintvar integer into mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendUintvarInteger(long value) {
- /*
- * From WAP-230-WSP-20010705-a:
- * To encode a large unsigned integer, split it into 7-bit fragments
- * and place them in the payloads of multiple octets. The most significant
- * bits are placed in the first octets with the least significant bits
- * ending up in the last octet. All octets MUST set the Continue bit to 1
- * except the last octet, which MUST set the Continue bit to 0.
- */
- int i;
- long max = SHORT_INTEGER_MAX;
-
- for (i = 0; i < 5; i++) {
- if (value < max) {
- break;
- }
-
- max = (max << 7) | 0x7fl;
- }
-
- while(i > 0) {
- long temp = value >>> (i * 7);
- temp = temp & 0x7f;
-
- append((int)((temp | 0x80) & 0xff));
-
- i--;
- }
-
- append((int)(value & 0x7f));
- }
-
- /**
- * Append date value into mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendDateValue(long date) {
- /*
- * From OMA-TS-MMS-ENC-V1_3-20050927-C:
- * Date-value = Long-integer
- */
- appendLongInteger(date);
- }
-
- /**
- * Append value length to mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendValueLength(long value) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Value-length = Short-length | (Length-quote Length)
- * ; Value length is used to indicate the length of the value to follow
- * Short-length = <Any octet 0-30>
- * Length-quote = <Octet 31>
- * Length = Uintvar-integer
- */
- if (value < LENGTH_QUOTE) {
- appendShortLength((int) value);
- return;
- }
-
- append(LENGTH_QUOTE);
- appendUintvarInteger(value);
- }
-
- /**
- * Append quoted string to mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendQuotedString(byte[] text) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Quoted-string = <Octet 34> *TEXT End-of-string
- * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
- * ;quotation-marks <"> removed.
- */
- append(QUOTED_STRING_FLAG);
- arraycopy(text, 0, text.length);
- append(END_STRING_FLAG);
- }
-
- /**
- * Append quoted string to mMessage.
- * This implementation doesn't check the validity of parameter, since it
- * assumes that the values are validated in the GenericPdu setter methods.
- */
- protected void appendQuotedString(String str) {
- /*
- * From WAP-230-WSP-20010705-a:
- * Quoted-string = <Octet 34> *TEXT End-of-string
- * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
- * ;quotation-marks <"> removed.
- */
- appendQuotedString(str.getBytes());
- }
-
- private EncodedStringValue appendAddressType(EncodedStringValue address) {
- EncodedStringValue temp = null;
-
- try {
- int addressType = checkAddressType(address.getString());
- temp = EncodedStringValue.copy(address);
- if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) {
- // Phone number.
- temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes());
- } else if (PDU_IPV4_ADDRESS_TYPE == addressType) {
- // Ipv4 address.
- temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes());
- } else if (PDU_IPV6_ADDRESS_TYPE == addressType) {
- // Ipv6 address.
- temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes());
- }
- } catch (NullPointerException e) {
- return null;
- }
-
- return temp;
- }
-
- /**
- * Append header to mMessage.
- */
- private int appendHeader(int field) {
- switch (field) {
- case PduHeaders.MMS_VERSION:
- appendOctet(field);
-
- int version = mPduHeader.getOctet(field);
- if (0 == version) {
- appendShortInteger(PduHeaders.CURRENT_MMS_VERSION);
- } else {
- appendShortInteger(version);
- }
-
- break;
-
- case PduHeaders.MESSAGE_ID:
- case PduHeaders.TRANSACTION_ID:
- byte[] textString = mPduHeader.getTextString(field);
- if (null == textString) {
- return PDU_COMPOSE_FIELD_NOT_SET;
- }
-
- appendOctet(field);
- appendTextString(textString);
- break;
-
- case PduHeaders.TO:
- case PduHeaders.BCC:
- case PduHeaders.CC:
- EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field);
-
- if (null == addr) {
- return PDU_COMPOSE_FIELD_NOT_SET;
- }
-
- EncodedStringValue temp;
- for (int i = 0; i < addr.length; i++) {
- temp = appendAddressType(addr[i]);
- if (temp == null) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- appendOctet(field);
- appendEncodedString(temp);
- }
- break;
-
- case PduHeaders.FROM:
- // Value-length (Address-present-token Encoded-string-value | Insert-address-token)
- appendOctet(field);
-
- EncodedStringValue from = mPduHeader.getEncodedStringValue(field);
- if ((from == null)
- || TextUtils.isEmpty(from.getString())
- || new String(from.getTextString()).equals(
- PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) {
- // Length of from = 1
- append(1);
- // Insert-address-token = <Octet 129>
- append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN);
- } else {
- mStack.newbuf();
- PositionMarker fstart = mStack.mark();
-
- // Address-present-token = <Octet 128>
- append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN);
-
- temp = appendAddressType(from);
- if (temp == null) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- appendEncodedString(temp);
-
- int flen = fstart.getLength();
- mStack.pop();
- appendValueLength(flen);
- mStack.copy();
- }
- break;
-
- case PduHeaders.READ_STATUS:
- case PduHeaders.STATUS:
- case PduHeaders.REPORT_ALLOWED:
- case PduHeaders.PRIORITY:
- case PduHeaders.DELIVERY_REPORT:
- case PduHeaders.READ_REPORT:
- case PduHeaders.RETRIEVE_STATUS:
- int octet = mPduHeader.getOctet(field);
- if (0 == octet) {
- return PDU_COMPOSE_FIELD_NOT_SET;
- }
-
- appendOctet(field);
- appendOctet(octet);
- break;
-
- case PduHeaders.DATE:
- long date = mPduHeader.getLongInteger(field);
- if (-1 == date) {
- return PDU_COMPOSE_FIELD_NOT_SET;
- }
-
- appendOctet(field);
- appendDateValue(date);
- break;
-
- case PduHeaders.SUBJECT:
- case PduHeaders.RETRIEVE_TEXT:
- EncodedStringValue enString =
- mPduHeader.getEncodedStringValue(field);
- if (null == enString) {
- return PDU_COMPOSE_FIELD_NOT_SET;
- }
-
- appendOctet(field);
- appendEncodedString(enString);
- break;
-
- case PduHeaders.MESSAGE_CLASS:
- byte[] messageClass = mPduHeader.getTextString(field);
- if (null == messageClass) {
- return PDU_COMPOSE_FIELD_NOT_SET;
- }
-
- appendOctet(field);
- if (Arrays.equals(messageClass,
- PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) {
- appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT);
- } else if (Arrays.equals(messageClass,
- PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) {
- appendOctet(PduHeaders.MESSAGE_CLASS_AUTO);
- } else if (Arrays.equals(messageClass,
- PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) {
- appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL);
- } else if (Arrays.equals(messageClass,
- PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) {
- appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL);
- } else {
- appendTextString(messageClass);
- }
- break;
-
- case PduHeaders.EXPIRY:
- long expiry = mPduHeader.getLongInteger(field);
- if (-1 == expiry) {
- return PDU_COMPOSE_FIELD_NOT_SET;
- }
-
- appendOctet(field);
-
- mStack.newbuf();
- PositionMarker expiryStart = mStack.mark();
-
- append(PduHeaders.VALUE_RELATIVE_TOKEN);
- appendLongInteger(expiry);
-
- int expiryLength = expiryStart.getLength();
- mStack.pop();
- appendValueLength(expiryLength);
- mStack.copy();
- break;
-
- default:
- return PDU_COMPOSE_FIELD_NOT_SUPPORTED;
- }
-
- return PDU_COMPOSE_SUCCESS;
- }
-
- /**
- * Make ReadRec.Ind.
- */
- private int makeReadRecInd() {
- if (mMessage == null) {
- mMessage = new ByteArrayOutputStream();
- mPosition = 0;
- }
-
- // X-Mms-Message-Type
- appendOctet(PduHeaders.MESSAGE_TYPE);
- appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
-
- // X-Mms-MMS-Version
- if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // Message-ID
- if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // To
- if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // From
- if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // Date Optional
- appendHeader(PduHeaders.DATE);
-
- // X-Mms-Read-Status
- if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // X-Mms-Applic-ID Optional(not support)
- // X-Mms-Reply-Applic-ID Optional(not support)
- // X-Mms-Aux-Applic-Info Optional(not support)
-
- return PDU_COMPOSE_SUCCESS;
- }
-
- /**
- * Make NotifyResp.Ind.
- */
- private int makeNotifyResp() {
- if (mMessage == null) {
- mMessage = new ByteArrayOutputStream();
- mPosition = 0;
- }
-
- // X-Mms-Message-Type
- appendOctet(PduHeaders.MESSAGE_TYPE);
- appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
-
- // X-Mms-Transaction-ID
- if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // X-Mms-MMS-Version
- if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // X-Mms-Status
- if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // X-Mms-Report-Allowed Optional (not support)
- return PDU_COMPOSE_SUCCESS;
- }
-
- /**
- * Make Acknowledge.Ind.
- */
- private int makeAckInd() {
- if (mMessage == null) {
- mMessage = new ByteArrayOutputStream();
- mPosition = 0;
- }
-
- // X-Mms-Message-Type
- appendOctet(PduHeaders.MESSAGE_TYPE);
- appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
-
- // X-Mms-Transaction-ID
- if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // X-Mms-MMS-Version
- if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // X-Mms-Report-Allowed Optional
- appendHeader(PduHeaders.REPORT_ALLOWED);
-
- return PDU_COMPOSE_SUCCESS;
- }
-
- /**
- * Make Send.req.
- */
- private int makeSendRetrievePdu(int type) {
- if (mMessage == null) {
- mMessage = new ByteArrayOutputStream();
- mPosition = 0;
- }
-
- // X-Mms-Message-Type
- appendOctet(PduHeaders.MESSAGE_TYPE);
- appendOctet(type);
-
- // X-Mms-Transaction-ID
- appendOctet(PduHeaders.TRANSACTION_ID);
-
- byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID);
- if (trid == null) {
- // Transaction-ID should be set(by Transaction) before make().
- throw new IllegalArgumentException("Transaction-ID is null.");
- }
- appendTextString(trid);
-
- // X-Mms-MMS-Version
- if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // Date Date-value Optional.
- appendHeader(PduHeaders.DATE);
-
- // From
- if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- boolean recipient = false;
-
- // To
- if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) {
- recipient = true;
- }
-
- // Cc
- if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) {
- recipient = true;
- }
-
- // Bcc
- if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) {
- recipient = true;
- }
-
- // Need at least one of "cc", "bcc" and "to".
- if (false == recipient) {
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // Subject Optional
- appendHeader(PduHeaders.SUBJECT);
-
- // X-Mms-Message-Class Optional
- // Message-class-value = Class-identifier | Token-text
- appendHeader(PduHeaders.MESSAGE_CLASS);
-
- // X-Mms-Expiry Optional
- appendHeader(PduHeaders.EXPIRY);
-
- // X-Mms-Priority Optional
- appendHeader(PduHeaders.PRIORITY);
-
- // X-Mms-Delivery-Report Optional
- appendHeader(PduHeaders.DELIVERY_REPORT);
-
- // X-Mms-Read-Report Optional
- appendHeader(PduHeaders.READ_REPORT);
-
- if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
- // X-Mms-Retrieve-Status Optional
- appendHeader(PduHeaders.RETRIEVE_STATUS);
- // X-Mms-Retrieve-Text Optional
- appendHeader(PduHeaders.RETRIEVE_TEXT);
- }
-
-
- // Content-Type
- appendOctet(PduHeaders.CONTENT_TYPE);
-
- // Message body
- return makeMessageBody(type);
- }
-
- /**
- * Make message body.
- */
- private int makeMessageBody(int type) {
- // 1. add body informations
- mStack.newbuf(); // Switching buffer because we need to
-
- PositionMarker ctStart = mStack.mark();
-
- // This contentTypeIdentifier should be used for type of attachment...
- String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE));
- Integer contentTypeIdentifier = mContentTypeMap.get(contentType);
- if (contentTypeIdentifier == null) {
- // content type is mandatory
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- appendShortInteger(contentTypeIdentifier.intValue());
-
- // content-type parameter: start
- PduBody body;
- if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
- body = ((RetrieveConf) mPdu).getBody();
- } else {
- body = ((SendReq) mPdu).getBody();
- }
- if (null == body || body.getPartsNum() == 0) {
- // empty message
- appendUintvarInteger(0);
- mStack.pop();
- mStack.copy();
- return PDU_COMPOSE_SUCCESS;
- }
-
- PduPart part;
- try {
- part = body.getPart(0);
-
- byte[] start = part.getContentId();
- if (start != null) {
- appendOctet(PduPart.P_DEP_START);
- if (('<' == start[0]) && ('>' == start[start.length - 1])) {
- appendTextString(start);
- } else {
- appendTextString("<" + new String(start) + ">");
- }
- }
-
- // content-type parameter: type
- appendOctet(PduPart.P_CT_MR_TYPE);
- appendTextString(part.getContentType());
- }
- catch (ArrayIndexOutOfBoundsException e){
- e.printStackTrace();
- }
-
- int ctLength = ctStart.getLength();
- mStack.pop();
- appendValueLength(ctLength);
- mStack.copy();
-
- // 3. add content
- int partNum = body.getPartsNum();
- appendUintvarInteger(partNum);
- for (int i = 0; i < partNum; i++) {
- part = body.getPart(i);
- mStack.newbuf(); // Leaving space for header lengh and data length
- PositionMarker attachment = mStack.mark();
-
- mStack.newbuf(); // Leaving space for Content-Type length
- PositionMarker contentTypeBegin = mStack.mark();
-
- byte[] partContentType = part.getContentType();
-
- if (partContentType == null) {
- // content type is mandatory
- return PDU_COMPOSE_CONTENT_ERROR;
- }
-
- // content-type value
- Integer partContentTypeIdentifier =
- mContentTypeMap.get(new String(partContentType));
- if (partContentTypeIdentifier == null) {
- appendTextString(partContentType);
- } else {
- appendShortInteger(partContentTypeIdentifier.intValue());
- }
-
- /* Content-type parameter : name.
- * The value of name, filename, content-location is the same.
- * Just one of them is enough for this PDU.
- */
- byte[] name = part.getName();
-
- if (null == name) {
- name = part.getFilename();
-
- if (null == name) {
- name = part.getContentLocation();
-
- if (null == name) {
- /* at lease one of name, filename, Content-location
- * should be available.
- */
- return PDU_COMPOSE_CONTENT_ERROR;
- }
- }
- }
- appendOctet(PduPart.P_DEP_NAME);
- appendTextString(name);
-
- // content-type parameter : charset
- int charset = part.getCharset();
- if (charset != 0) {
- appendOctet(PduPart.P_CHARSET);
- appendShortInteger(charset);
- }
-
- int contentTypeLength = contentTypeBegin.getLength();
- mStack.pop();
- appendValueLength(contentTypeLength);
- mStack.copy();
-
- // content id
- byte[] contentId = part.getContentId();
-
- if (null != contentId) {
- appendOctet(PduPart.P_CONTENT_ID);
- if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) {
- appendQuotedString(contentId);
- } else {
- appendQuotedString("<" + new String(contentId) + ">");
- }
- }
-
- // content-location
- byte[] contentLocation = part.getContentLocation();
- if (null != contentLocation) {
- appendOctet(PduPart.P_CONTENT_LOCATION);
- appendTextString(contentLocation);
- }
-
- // content
- int headerLength = attachment.getLength();
-
- int dataLength = 0; // Just for safety...
- byte[] partData = part.getData();
-
- if (partData != null) {
- arraycopy(partData, 0, partData.length);
- dataLength = partData.length;
- } else {
- InputStream cr = null;
- try {
- byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE];
- cr = mResolver.openInputStream(part.getDataUri());
- int len = 0;
- while ((len = cr.read(buffer)) != -1) {
- mMessage.write(buffer, 0, len);
- mPosition += len;
- dataLength += len;
- }
- } catch (FileNotFoundException e) {
- return PDU_COMPOSE_CONTENT_ERROR;
- } catch (IOException e) {
- return PDU_COMPOSE_CONTENT_ERROR;
- } catch (RuntimeException e) {
- return PDU_COMPOSE_CONTENT_ERROR;
- } finally {
- if (cr != null) {
- try {
- cr.close();
- } catch (IOException e) {
- }
- }
- }
- }
-
- if (dataLength != (attachment.getLength() - headerLength)) {
- throw new RuntimeException("BUG: Length sanity check failed");
- }
-
- mStack.pop();
- appendUintvarInteger(headerLength);
- appendUintvarInteger(dataLength);
- mStack.copy();
- }
-
- return PDU_COMPOSE_SUCCESS;
- }
-
- /**
- * Record current message informations.
- */
- static private class LengthRecordNode {
- ByteArrayOutputStream currentMessage = null;
- public int currentPosition = 0;
-
- public LengthRecordNode next = null;
- }
-
- /**
- * Mark current message position and stact size.
- */
- private class PositionMarker {
- private int c_pos; // Current position
- private int currentStackSize; // Current stack size
-
- int getLength() {
- // If these assert fails, likely that you are finding the
- // size of buffer that is deep in BufferStack you can only
- // find the length of the buffer that is on top
- if (currentStackSize != mStack.stackSize) {
- throw new RuntimeException("BUG: Invalid call to getLength()");
- }
-
- return mPosition - c_pos;
- }
- }
-
- /**
- * This implementation can be OPTIMIZED to use only
- * 2 buffers. This optimization involves changing BufferStack
- * only... Its usage (interface) will not change.
- */
- private class BufferStack {
- private LengthRecordNode stack = null;
- private LengthRecordNode toCopy = null;
-
- int stackSize = 0;
-
- /**
- * Create a new message buffer and push it into the stack.
- */
- void newbuf() {
- // You can't create a new buff when toCopy != null
- // That is after calling pop() and before calling copy()
- // If you do, it is a bug
- if (toCopy != null) {
- throw new RuntimeException("BUG: Invalid newbuf() before copy()");
- }
-
- LengthRecordNode temp = new LengthRecordNode();
-
- temp.currentMessage = mMessage;
- temp.currentPosition = mPosition;
-
- temp.next = stack;
- stack = temp;
-
- stackSize = stackSize + 1;
-
- mMessage = new ByteArrayOutputStream();
- mPosition = 0;
- }
-
- /**
- * Pop the message before and record current message in the stack.
- */
- void pop() {
- ByteArrayOutputStream currentMessage = mMessage;
- int currentPosition = mPosition;
-
- mMessage = stack.currentMessage;
- mPosition = stack.currentPosition;
-
- toCopy = stack;
- // Re using the top element of the stack to avoid memory allocation
-
- stack = stack.next;
- stackSize = stackSize - 1;
-
- toCopy.currentMessage = currentMessage;
- toCopy.currentPosition = currentPosition;
- }
-
- /**
- * Append current message to the message before.
- */
- void copy() {
- arraycopy(toCopy.currentMessage.toByteArray(), 0,
- toCopy.currentPosition);
-
- toCopy = null;
- }
-
- /**
- * Mark current message position
- */
- PositionMarker mark() {
- PositionMarker m = new PositionMarker();
-
- m.c_pos = mPosition;
- m.currentStackSize = stackSize;
-
- return m;
- }
- }
-
- /**
- * Check address type.
- *
- * @param address address string without the postfix stinng type,
- * such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4"
- * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number,
- * PDU_EMAIL_ADDRESS_TYPE if it is email address,
- * PDU_IPV4_ADDRESS_TYPE if it is ipv4 address,
- * PDU_IPV6_ADDRESS_TYPE if it is ipv6 address,
- * PDU_UNKNOWN_ADDRESS_TYPE if it is unknown.
- */
- protected static int checkAddressType(String address) {
- /**
- * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8.
- * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode)
- * e-mail = mailbox; to the definition of mailbox as described in
- * section 3.4 of [RFC2822], but excluding the
- * obsolete definitions as indicated by the "obs-" prefix.
- * device-address = ( global-phone-number "/TYPE=PLMN" )
- * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" )
- * / ( escaped-value "/TYPE=" address-type )
- *
- * global-phone-number = ["+"] 1*( DIGIT / written-sep )
- * written-sep =("-"/".")
- *
- * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value
- *
- * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373
- */
-
- if (null == address) {
- return PDU_UNKNOWN_ADDRESS_TYPE;
- }
-
- if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) {
- // Ipv4 address.
- return PDU_IPV4_ADDRESS_TYPE;
- }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) {
- // Phone number.
- return PDU_PHONE_NUMBER_ADDRESS_TYPE;
- } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) {
- // Email address.
- return PDU_EMAIL_ADDRESS_TYPE;
- } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) {
- // Ipv6 address.
- return PDU_IPV6_ADDRESS_TYPE;
- } else {
- // Unknown address.
- return PDU_UNKNOWN_ADDRESS_TYPE;
- }
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/PduContentTypes.java b/src/java/com/google/android/mms/pdu/PduContentTypes.java
deleted file mode 100644
index 7799e0e834..0000000000
--- a/src/java/com/google/android/mms/pdu/PduContentTypes.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-public class PduContentTypes {
- /**
- * All content types. From:
- * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.htm
- */
- static final String[] contentTypes = {
- "*/*", /* 0x00 */
- "text/*", /* 0x01 */
- "text/html", /* 0x02 */
- "text/plain", /* 0x03 */
- "text/x-hdml", /* 0x04 */
- "text/x-ttml", /* 0x05 */
- "text/x-vCalendar", /* 0x06 */
- "text/x-vCard", /* 0x07 */
- "text/vnd.wap.wml", /* 0x08 */
- "text/vnd.wap.wmlscript", /* 0x09 */
- "text/vnd.wap.wta-event", /* 0x0A */
- "multipart/*", /* 0x0B */
- "multipart/mixed", /* 0x0C */
- "multipart/form-data", /* 0x0D */
- "multipart/byterantes", /* 0x0E */
- "multipart/alternative", /* 0x0F */
- "application/*", /* 0x10 */
- "application/java-vm", /* 0x11 */
- "application/x-www-form-urlencoded", /* 0x12 */
- "application/x-hdmlc", /* 0x13 */
- "application/vnd.wap.wmlc", /* 0x14 */
- "application/vnd.wap.wmlscriptc", /* 0x15 */
- "application/vnd.wap.wta-eventc", /* 0x16 */
- "application/vnd.wap.uaprof", /* 0x17 */
- "application/vnd.wap.wtls-ca-certificate", /* 0x18 */
- "application/vnd.wap.wtls-user-certificate", /* 0x19 */
- "application/x-x509-ca-cert", /* 0x1A */
- "application/x-x509-user-cert", /* 0x1B */
- "image/*", /* 0x1C */
- "image/gif", /* 0x1D */
- "image/jpeg", /* 0x1E */
- "image/tiff", /* 0x1F */
- "image/png", /* 0x20 */
- "image/vnd.wap.wbmp", /* 0x21 */
- "application/vnd.wap.multipart.*", /* 0x22 */
- "application/vnd.wap.multipart.mixed", /* 0x23 */
- "application/vnd.wap.multipart.form-data", /* 0x24 */
- "application/vnd.wap.multipart.byteranges", /* 0x25 */
- "application/vnd.wap.multipart.alternative", /* 0x26 */
- "application/xml", /* 0x27 */
- "text/xml", /* 0x28 */
- "application/vnd.wap.wbxml", /* 0x29 */
- "application/x-x968-cross-cert", /* 0x2A */
- "application/x-x968-ca-cert", /* 0x2B */
- "application/x-x968-user-cert", /* 0x2C */
- "text/vnd.wap.si", /* 0x2D */
- "application/vnd.wap.sic", /* 0x2E */
- "text/vnd.wap.sl", /* 0x2F */
- "application/vnd.wap.slc", /* 0x30 */
- "text/vnd.wap.co", /* 0x31 */
- "application/vnd.wap.coc", /* 0x32 */
- "application/vnd.wap.multipart.related", /* 0x33 */
- "application/vnd.wap.sia", /* 0x34 */
- "text/vnd.wap.connectivity-xml", /* 0x35 */
- "application/vnd.wap.connectivity-wbxml", /* 0x36 */
- "application/pkcs7-mime", /* 0x37 */
- "application/vnd.wap.hashed-certificate", /* 0x38 */
- "application/vnd.wap.signed-certificate", /* 0x39 */
- "application/vnd.wap.cert-response", /* 0x3A */
- "application/xhtml+xml", /* 0x3B */
- "application/wml+xml", /* 0x3C */
- "text/css", /* 0x3D */
- "application/vnd.wap.mms-message", /* 0x3E */
- "application/vnd.wap.rollover-certificate", /* 0x3F */
- "application/vnd.wap.locc+wbxml", /* 0x40 */
- "application/vnd.wap.loc+xml", /* 0x41 */
- "application/vnd.syncml.dm+wbxml", /* 0x42 */
- "application/vnd.syncml.dm+xml", /* 0x43 */
- "application/vnd.syncml.notification", /* 0x44 */
- "application/vnd.wap.xhtml+xml", /* 0x45 */
- "application/vnd.wv.csp.cir", /* 0x46 */
- "application/vnd.oma.dd+xml", /* 0x47 */
- "application/vnd.oma.drm.message", /* 0x48 */
- "application/vnd.oma.drm.content", /* 0x49 */
- "application/vnd.oma.drm.rights+xml", /* 0x4A */
- "application/vnd.oma.drm.rights+wbxml", /* 0x4B */
- "application/vnd.wv.csp+xml", /* 0x4C */
- "application/vnd.wv.csp+wbxml", /* 0x4D */
- "application/vnd.syncml.ds.notification", /* 0x4E */
- "audio/*", /* 0x4F */
- "video/*", /* 0x50 */
- "application/vnd.oma.dd2+xml", /* 0x51 */
- "application/mikey" /* 0x52 */
- };
-}
diff --git a/src/java/com/google/android/mms/pdu/PduHeaders.java b/src/java/com/google/android/mms/pdu/PduHeaders.java
deleted file mode 100644
index 43138150f8..0000000000
--- a/src/java/com/google/android/mms/pdu/PduHeaders.java
+++ /dev/null
@@ -1,721 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-public class PduHeaders {
- /**
- * All pdu header fields.
- */
- public static final int BCC = 0x81;
- public static final int CC = 0x82;
- public static final int CONTENT_LOCATION = 0x83;
- public static final int CONTENT_TYPE = 0x84;
- public static final int DATE = 0x85;
- public static final int DELIVERY_REPORT = 0x86;
- public static final int DELIVERY_TIME = 0x87;
- public static final int EXPIRY = 0x88;
- public static final int FROM = 0x89;
- public static final int MESSAGE_CLASS = 0x8A;
- public static final int MESSAGE_ID = 0x8B;
- public static final int MESSAGE_TYPE = 0x8C;
- public static final int MMS_VERSION = 0x8D;
- public static final int MESSAGE_SIZE = 0x8E;
- public static final int PRIORITY = 0x8F;
-
- public static final int READ_REPLY = 0x90;
- public static final int READ_REPORT = 0x90;
- public static final int REPORT_ALLOWED = 0x91;
- public static final int RESPONSE_STATUS = 0x92;
- public static final int RESPONSE_TEXT = 0x93;
- public static final int SENDER_VISIBILITY = 0x94;
- public static final int STATUS = 0x95;
- public static final int SUBJECT = 0x96;
- public static final int TO = 0x97;
- public static final int TRANSACTION_ID = 0x98;
- public static final int RETRIEVE_STATUS = 0x99;
- public static final int RETRIEVE_TEXT = 0x9A;
- public static final int READ_STATUS = 0x9B;
- public static final int REPLY_CHARGING = 0x9C;
- public static final int REPLY_CHARGING_DEADLINE = 0x9D;
- public static final int REPLY_CHARGING_ID = 0x9E;
- public static final int REPLY_CHARGING_SIZE = 0x9F;
-
- public static final int PREVIOUSLY_SENT_BY = 0xA0;
- public static final int PREVIOUSLY_SENT_DATE = 0xA1;
- public static final int STORE = 0xA2;
- public static final int MM_STATE = 0xA3;
- public static final int MM_FLAGS = 0xA4;
- public static final int STORE_STATUS = 0xA5;
- public static final int STORE_STATUS_TEXT = 0xA6;
- public static final int STORED = 0xA7;
- public static final int ATTRIBUTES = 0xA8;
- public static final int TOTALS = 0xA9;
- public static final int MBOX_TOTALS = 0xAA;
- public static final int QUOTAS = 0xAB;
- public static final int MBOX_QUOTAS = 0xAC;
- public static final int MESSAGE_COUNT = 0xAD;
- public static final int CONTENT = 0xAE;
- public static final int START = 0xAF;
-
- public static final int ADDITIONAL_HEADERS = 0xB0;
- public static final int DISTRIBUTION_INDICATOR = 0xB1;
- public static final int ELEMENT_DESCRIPTOR = 0xB2;
- public static final int LIMIT = 0xB3;
- public static final int RECOMMENDED_RETRIEVAL_MODE = 0xB4;
- public static final int RECOMMENDED_RETRIEVAL_MODE_TEXT = 0xB5;
- public static final int STATUS_TEXT = 0xB6;
- public static final int APPLIC_ID = 0xB7;
- public static final int REPLY_APPLIC_ID = 0xB8;
- public static final int AUX_APPLIC_ID = 0xB9;
- public static final int CONTENT_CLASS = 0xBA;
- public static final int DRM_CONTENT = 0xBB;
- public static final int ADAPTATION_ALLOWED = 0xBC;
- public static final int REPLACE_ID = 0xBD;
- public static final int CANCEL_ID = 0xBE;
- public static final int CANCEL_STATUS = 0xBF;
-
- /**
- * X-Mms-Message-Type field types.
- */
- public static final int MESSAGE_TYPE_SEND_REQ = 0x80;
- public static final int MESSAGE_TYPE_SEND_CONF = 0x81;
- public static final int MESSAGE_TYPE_NOTIFICATION_IND = 0x82;
- public static final int MESSAGE_TYPE_NOTIFYRESP_IND = 0x83;
- public static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
- public static final int MESSAGE_TYPE_ACKNOWLEDGE_IND = 0x85;
- public static final int MESSAGE_TYPE_DELIVERY_IND = 0x86;
- public static final int MESSAGE_TYPE_READ_REC_IND = 0x87;
- public static final int MESSAGE_TYPE_READ_ORIG_IND = 0x88;
- public static final int MESSAGE_TYPE_FORWARD_REQ = 0x89;
- public static final int MESSAGE_TYPE_FORWARD_CONF = 0x8A;
- public static final int MESSAGE_TYPE_MBOX_STORE_REQ = 0x8B;
- public static final int MESSAGE_TYPE_MBOX_STORE_CONF = 0x8C;
- public static final int MESSAGE_TYPE_MBOX_VIEW_REQ = 0x8D;
- public static final int MESSAGE_TYPE_MBOX_VIEW_CONF = 0x8E;
- public static final int MESSAGE_TYPE_MBOX_UPLOAD_REQ = 0x8F;
- public static final int MESSAGE_TYPE_MBOX_UPLOAD_CONF = 0x90;
- public static final int MESSAGE_TYPE_MBOX_DELETE_REQ = 0x91;
- public static final int MESSAGE_TYPE_MBOX_DELETE_CONF = 0x92;
- public static final int MESSAGE_TYPE_MBOX_DESCR = 0x93;
- public static final int MESSAGE_TYPE_DELETE_REQ = 0x94;
- public static final int MESSAGE_TYPE_DELETE_CONF = 0x95;
- public static final int MESSAGE_TYPE_CANCEL_REQ = 0x96;
- public static final int MESSAGE_TYPE_CANCEL_CONF = 0x97;
-
- /**
- * X-Mms-Delivery-Report |
- * X-Mms-Read-Report |
- * X-Mms-Report-Allowed |
- * X-Mms-Sender-Visibility |
- * X-Mms-Store |
- * X-Mms-Stored |
- * X-Mms-Totals |
- * X-Mms-Quotas |
- * X-Mms-Distribution-Indicator |
- * X-Mms-DRM-Content |
- * X-Mms-Adaptation-Allowed |
- * field types.
- */
- public static final int VALUE_YES = 0x80;
- public static final int VALUE_NO = 0x81;
-
- /**
- * Delivery-Time |
- * Expiry and Reply-Charging-Deadline |
- * field type components.
- */
- public static final int VALUE_ABSOLUTE_TOKEN = 0x80;
- public static final int VALUE_RELATIVE_TOKEN = 0x81;
-
- /**
- * X-Mms-MMS-Version field types.
- */
- public static final int MMS_VERSION_1_3 = ((1 << 4) | 3);
- public static final int MMS_VERSION_1_2 = ((1 << 4) | 2);
- public static final int MMS_VERSION_1_1 = ((1 << 4) | 1);
- public static final int MMS_VERSION_1_0 = ((1 << 4) | 0);
-
- // Current version is 1.2.
- public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2;
-
- /**
- * From field type components.
- */
- public static final int FROM_ADDRESS_PRESENT_TOKEN = 0x80;
- public static final int FROM_INSERT_ADDRESS_TOKEN = 0x81;
-
- public static final String FROM_ADDRESS_PRESENT_TOKEN_STR = "address-present-token";
- public static final String FROM_INSERT_ADDRESS_TOKEN_STR = "insert-address-token";
-
- /**
- * X-Mms-Status Field.
- */
- public static final int STATUS_EXPIRED = 0x80;
- public static final int STATUS_RETRIEVED = 0x81;
- public static final int STATUS_REJECTED = 0x82;
- public static final int STATUS_DEFERRED = 0x83;
- public static final int STATUS_UNRECOGNIZED = 0x84;
- public static final int STATUS_INDETERMINATE = 0x85;
- public static final int STATUS_FORWARDED = 0x86;
- public static final int STATUS_UNREACHABLE = 0x87;
-
- /**
- * MM-Flags field type components.
- */
- public static final int MM_FLAGS_ADD_TOKEN = 0x80;
- public static final int MM_FLAGS_REMOVE_TOKEN = 0x81;
- public static final int MM_FLAGS_FILTER_TOKEN = 0x82;
-
- /**
- * X-Mms-Message-Class field types.
- */
- public static final int MESSAGE_CLASS_PERSONAL = 0x80;
- public static final int MESSAGE_CLASS_ADVERTISEMENT = 0x81;
- public static final int MESSAGE_CLASS_INFORMATIONAL = 0x82;
- public static final int MESSAGE_CLASS_AUTO = 0x83;
-
- public static final String MESSAGE_CLASS_PERSONAL_STR = "personal";
- public static final String MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement";
- public static final String MESSAGE_CLASS_INFORMATIONAL_STR = "informational";
- public static final String MESSAGE_CLASS_AUTO_STR = "auto";
-
- /**
- * X-Mms-Priority field types.
- */
- public static final int PRIORITY_LOW = 0x80;
- public static final int PRIORITY_NORMAL = 0x81;
- public static final int PRIORITY_HIGH = 0x82;
-
- /**
- * X-Mms-Response-Status field types.
- */
- public static final int RESPONSE_STATUS_OK = 0x80;
- public static final int RESPONSE_STATUS_ERROR_UNSPECIFIED = 0x81;
- public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82;
-
- public static final int RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT = 0x83;
- public static final int RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED = 0x84;
-
- public static final int RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND = 0x85;
- public static final int RESPONSE_STATUS_ERROR_NETWORK_PROBLEM = 0x86;
- public static final int RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED = 0x87;
- public static final int RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE = 0x88;
- public static final int RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
-
- public static final int RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED = 0xC1;
- public static final int RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC2;
- public static final int RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC3;
- public static final int RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS = 0xC4;
-
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED = 0xE3;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE4;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED = 0xE5;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET = 0xE6;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED = 0xE6;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED = 0xE8;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED = 0xE9;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED = 0xEA;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID = 0xEB;
- public static final int RESPONSE_STATUS_ERROR_PERMANENT_END = 0xFF;
-
- /**
- * X-Mms-Retrieve-Status field types.
- */
- public static final int RETRIEVE_STATUS_OK = 0x80;
- public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
- public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC1;
- public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC2;
- public static final int RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
- public static final int RETRIEVE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
- public static final int RETRIEVE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE2;
- public static final int RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED = 0xE3;
- public static final int RETRIEVE_STATUS_ERROR_END = 0xFF;
-
- /**
- * X-Mms-Sender-Visibility field types.
- */
- public static final int SENDER_VISIBILITY_HIDE = 0x80;
- public static final int SENDER_VISIBILITY_SHOW = 0x81;
-
- /**
- * X-Mms-Read-Status field types.
- */
- public static final int READ_STATUS_READ = 0x80;
- public static final int READ_STATUS__DELETED_WITHOUT_BEING_READ = 0x81;
-
- /**
- * X-Mms-Cancel-Status field types.
- */
- public static final int CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED = 0x80;
- public static final int CANCEL_STATUS_REQUEST_CORRUPTED = 0x81;
-
- /**
- * X-Mms-Reply-Charging field types.
- */
- public static final int REPLY_CHARGING_REQUESTED = 0x80;
- public static final int REPLY_CHARGING_REQUESTED_TEXT_ONLY = 0x81;
- public static final int REPLY_CHARGING_ACCEPTED = 0x82;
- public static final int REPLY_CHARGING_ACCEPTED_TEXT_ONLY = 0x83;
-
- /**
- * X-Mms-MM-State field types.
- */
- public static final int MM_STATE_DRAFT = 0x80;
- public static final int MM_STATE_SENT = 0x81;
- public static final int MM_STATE_NEW = 0x82;
- public static final int MM_STATE_RETRIEVED = 0x83;
- public static final int MM_STATE_FORWARDED = 0x84;
-
- /**
- * X-Mms-Recommended-Retrieval-Mode field types.
- */
- public static final int RECOMMENDED_RETRIEVAL_MODE_MANUAL = 0x80;
-
- /**
- * X-Mms-Content-Class field types.
- */
- public static final int CONTENT_CLASS_TEXT = 0x80;
- public static final int CONTENT_CLASS_IMAGE_BASIC = 0x81;
- public static final int CONTENT_CLASS_IMAGE_RICH = 0x82;
- public static final int CONTENT_CLASS_VIDEO_BASIC = 0x83;
- public static final int CONTENT_CLASS_VIDEO_RICH = 0x84;
- public static final int CONTENT_CLASS_MEGAPIXEL = 0x85;
- public static final int CONTENT_CLASS_CONTENT_BASIC = 0x86;
- public static final int CONTENT_CLASS_CONTENT_RICH = 0x87;
-
- /**
- * X-Mms-Store-Status field types.
- */
- public static final int STORE_STATUS_SUCCESS = 0x80;
- public static final int STORE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
- public static final int STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC1;
- public static final int STORE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
- public static final int STORE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
- public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
- public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE3;
- public static final int STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL = 0xE4;
- public static final int STORE_STATUS_ERROR_END = 0xFF;
-
- /**
- * The map contains the value of all headers.
- */
- private HashMap<Integer, Object> mHeaderMap = null;
-
- /**
- * Constructor of PduHeaders.
- */
- public PduHeaders() {
- mHeaderMap = new HashMap<Integer, Object>();
- }
-
- /**
- * Get octet value by header field.
- *
- * @param field the field
- * @return the octet value of the pdu header
- * with specified header field. Return 0 if
- * the value is not set.
- */
- protected int getOctet(int field) {
- Integer octet = (Integer) mHeaderMap.get(field);
- if (null == octet) {
- return 0;
- }
-
- return octet;
- }
-
- /**
- * Set octet value to pdu header by header field.
- *
- * @param value the value
- * @param field the field
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- protected void setOctet(int value, int field)
- throws InvalidHeaderValueException{
- /**
- * Check whether this field can be set for specific
- * header and check validity of the field.
- */
- switch (field) {
- case REPORT_ALLOWED:
- case ADAPTATION_ALLOWED:
- case DELIVERY_REPORT:
- case DRM_CONTENT:
- case DISTRIBUTION_INDICATOR:
- case QUOTAS:
- case READ_REPORT:
- case STORE:
- case STORED:
- case TOTALS:
- case SENDER_VISIBILITY:
- if ((VALUE_YES != value) && (VALUE_NO != value)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case READ_STATUS:
- if ((READ_STATUS_READ != value) &&
- (READ_STATUS__DELETED_WITHOUT_BEING_READ != value)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case CANCEL_STATUS:
- if ((CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED != value) &&
- (CANCEL_STATUS_REQUEST_CORRUPTED != value)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case PRIORITY:
- if ((value < PRIORITY_LOW) || (value > PRIORITY_HIGH)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case STATUS:
- if ((value < STATUS_EXPIRED) || (value > STATUS_UNREACHABLE)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case REPLY_CHARGING:
- if ((value < REPLY_CHARGING_REQUESTED)
- || (value > REPLY_CHARGING_ACCEPTED_TEXT_ONLY)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case MM_STATE:
- if ((value < MM_STATE_DRAFT) || (value > MM_STATE_FORWARDED)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case RECOMMENDED_RETRIEVAL_MODE:
- if (RECOMMENDED_RETRIEVAL_MODE_MANUAL != value) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case CONTENT_CLASS:
- if ((value < CONTENT_CLASS_TEXT)
- || (value > CONTENT_CLASS_CONTENT_RICH)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- case RETRIEVE_STATUS:
- // According to oma-ts-mms-enc-v1_3, section 7.3.50, we modify the invalid value.
- if ((value > RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
- (value < RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE)) {
- value = RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE;
- } else if ((value > RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED) &&
- (value <= RETRIEVE_STATUS_ERROR_END)) {
- value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
- } else if ((value < RETRIEVE_STATUS_OK) ||
- ((value > RETRIEVE_STATUS_OK) &&
- (value < RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
- (value > RETRIEVE_STATUS_ERROR_END)) {
- value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
- }
- break;
- case STORE_STATUS:
- // According to oma-ts-mms-enc-v1_3, section 7.3.58, we modify the invalid value.
- if ((value > STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
- (value < STORE_STATUS_ERROR_PERMANENT_FAILURE)) {
- value = STORE_STATUS_ERROR_TRANSIENT_FAILURE;
- } else if ((value > STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL) &&
- (value <= STORE_STATUS_ERROR_END)) {
- value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
- } else if ((value < STORE_STATUS_SUCCESS) ||
- ((value > STORE_STATUS_SUCCESS) &&
- (value < STORE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
- (value > STORE_STATUS_ERROR_END)) {
- value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
- }
- break;
- case RESPONSE_STATUS:
- // According to oma-ts-mms-enc-v1_3, section 7.3.48, we modify the invalid value.
- if ((value > RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) &&
- (value < RESPONSE_STATUS_ERROR_PERMANENT_FAILURE)) {
- value = RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE;
- } else if (((value > RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID) &&
- (value <= RESPONSE_STATUS_ERROR_PERMANENT_END)) ||
- (value < RESPONSE_STATUS_OK) ||
- ((value > RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE) &&
- (value < RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
- (value > RESPONSE_STATUS_ERROR_PERMANENT_END)) {
- value = RESPONSE_STATUS_ERROR_PERMANENT_FAILURE;
- }
- break;
- case MMS_VERSION:
- if ((value < MMS_VERSION_1_0)|| (value > MMS_VERSION_1_3)) {
- value = CURRENT_MMS_VERSION; // Current version is the default value.
- }
- break;
- case MESSAGE_TYPE:
- if ((value < MESSAGE_TYPE_SEND_REQ) || (value > MESSAGE_TYPE_CANCEL_CONF)) {
- // Invalid value.
- throw new InvalidHeaderValueException("Invalid Octet value!");
- }
- break;
- default:
- // This header value should not be Octect.
- throw new RuntimeException("Invalid header field!");
- }
- mHeaderMap.put(field, value);
- }
-
- /**
- * Get TextString value by header field.
- *
- * @param field the field
- * @return the TextString value of the pdu header
- * with specified header field
- */
- protected byte[] getTextString(int field) {
- return (byte[]) mHeaderMap.get(field);
- }
-
- /**
- * Set TextString value to pdu header by header field.
- *
- * @param value the value
- * @param field the field
- * @return the TextString value of the pdu header
- * with specified header field
- * @throws NullPointerException if the value is null.
- */
- protected void setTextString(byte[] value, int field) {
- /**
- * Check whether this field can be set for specific
- * header and check validity of the field.
- */
- if (null == value) {
- throw new NullPointerException();
- }
-
- switch (field) {
- case TRANSACTION_ID:
- case REPLY_CHARGING_ID:
- case AUX_APPLIC_ID:
- case APPLIC_ID:
- case REPLY_APPLIC_ID:
- case MESSAGE_ID:
- case REPLACE_ID:
- case CANCEL_ID:
- case CONTENT_LOCATION:
- case MESSAGE_CLASS:
- case CONTENT_TYPE:
- break;
- default:
- // This header value should not be Text-String.
- throw new RuntimeException("Invalid header field!");
- }
- mHeaderMap.put(field, value);
- }
-
- /**
- * Get EncodedStringValue value by header field.
- *
- * @param field the field
- * @return the EncodedStringValue value of the pdu header
- * with specified header field
- */
- protected EncodedStringValue getEncodedStringValue(int field) {
- return (EncodedStringValue) mHeaderMap.get(field);
- }
-
- /**
- * Get TO, CC or BCC header value.
- *
- * @param field the field
- * @return the EncodeStringValue array of the pdu header
- * with specified header field
- */
- protected EncodedStringValue[] getEncodedStringValues(int field) {
- ArrayList<EncodedStringValue> list =
- (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
- if (null == list) {
- return null;
- }
- EncodedStringValue[] values = new EncodedStringValue[list.size()];
- return list.toArray(values);
- }
-
- /**
- * Set EncodedStringValue value to pdu header by header field.
- *
- * @param value the value
- * @param field the field
- * @return the EncodedStringValue value of the pdu header
- * with specified header field
- * @throws NullPointerException if the value is null.
- */
- protected void setEncodedStringValue(EncodedStringValue value, int field) {
- /**
- * Check whether this field can be set for specific
- * header and check validity of the field.
- */
- if (null == value) {
- throw new NullPointerException();
- }
-
- switch (field) {
- case SUBJECT:
- case RECOMMENDED_RETRIEVAL_MODE_TEXT:
- case RETRIEVE_TEXT:
- case STATUS_TEXT:
- case STORE_STATUS_TEXT:
- case RESPONSE_TEXT:
- case FROM:
- case PREVIOUSLY_SENT_BY:
- case MM_FLAGS:
- break;
- default:
- // This header value should not be Encoded-String-Value.
- throw new RuntimeException("Invalid header field!");
- }
-
- mHeaderMap.put(field, value);
- }
-
- /**
- * Set TO, CC or BCC header value.
- *
- * @param value the value
- * @param field the field
- * @return the EncodedStringValue value array of the pdu header
- * with specified header field
- * @throws NullPointerException if the value is null.
- */
- protected void setEncodedStringValues(EncodedStringValue[] value, int field) {
- /**
- * Check whether this field can be set for specific
- * header and check validity of the field.
- */
- if (null == value) {
- throw new NullPointerException();
- }
-
- switch (field) {
- case BCC:
- case CC:
- case TO:
- break;
- default:
- // This header value should not be Encoded-String-Value.
- throw new RuntimeException("Invalid header field!");
- }
-
- ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
- for (int i = 0; i < value.length; i++) {
- list.add(value[i]);
- }
- mHeaderMap.put(field, list);
- }
-
- /**
- * Append one EncodedStringValue to another.
- *
- * @param value the EncodedStringValue to append
- * @param field the field
- * @throws NullPointerException if the value is null.
- */
- protected void appendEncodedStringValue(EncodedStringValue value,
- int field) {
- if (null == value) {
- throw new NullPointerException();
- }
-
- switch (field) {
- case BCC:
- case CC:
- case TO:
- break;
- default:
- throw new RuntimeException("Invalid header field!");
- }
-
- ArrayList<EncodedStringValue> list =
- (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
- if (null == list) {
- list = new ArrayList<EncodedStringValue>();
- }
- list.add(value);
- mHeaderMap.put(field, list);
- }
-
- /**
- * Get LongInteger value by header field.
- *
- * @param field the field
- * @return the LongInteger value of the pdu header
- * with specified header field. if return -1, the
- * field is not existed in pdu header.
- */
- protected long getLongInteger(int field) {
- Long longInteger = (Long) mHeaderMap.get(field);
- if (null == longInteger) {
- return -1;
- }
-
- return longInteger.longValue();
- }
-
- /**
- * Set LongInteger value to pdu header by header field.
- *
- * @param value the value
- * @param field the field
- */
- protected void setLongInteger(long value, int field) {
- /**
- * Check whether this field can be set for specific
- * header and check validity of the field.
- */
- switch (field) {
- case DATE:
- case REPLY_CHARGING_SIZE:
- case MESSAGE_SIZE:
- case MESSAGE_COUNT:
- case START:
- case LIMIT:
- case DELIVERY_TIME:
- case EXPIRY:
- case REPLY_CHARGING_DEADLINE:
- case PREVIOUSLY_SENT_DATE:
- break;
- default:
- // This header value should not be LongInteger.
- throw new RuntimeException("Invalid header field!");
- }
- mHeaderMap.put(field, value);
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/PduParser.java b/src/java/com/google/android/mms/pdu/PduParser.java
deleted file mode 100755
index 9fbdb667cb..0000000000
--- a/src/java/com/google/android/mms/pdu/PduParser.java
+++ /dev/null
@@ -1,2011 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Esmertec AG.
- * Copyright (C) 2007-2008 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.google.android.mms.pdu;
-
-import android.util.Log;
-
-import com.google.android.mms.ContentType;
-import com.google.android.mms.InvalidHeaderValueException;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-import java.util.HashMap;
-
-public class PduParser {
- /**
- * The next are WAP values defined in WSP specification.
- */
- private static final int QUOTE = 127;
- private static final int LENGTH_QUOTE = 31;
- private static final int TEXT_MIN = 32;
- private static final int TEXT_MAX = 127;
- private static final int SHORT_INTEGER_MAX = 127;
- private static final int SHORT_LENGTH_MAX = 30;
- private static final int LONG_INTEGER_LENGTH_MAX = 8;
- private static final int QUOTED_STRING_FLAG = 34;
- private static final int END_STRING_FLAG = 0x00;
- //The next two are used by the interface "parseWapString" to
- //distinguish Text-String and Quoted-String.
- private static final int TYPE_TEXT_STRING = 0;
- private static final int TYPE_QUOTED_STRING = 1;
- private static final int TYPE_TOKEN_STRING = 2;
-
- /**
- * Specify the part position.
- */
- private static final int THE_FIRST_PART = 0;
- private static final int THE_LAST_PART = 1;
-
- /**
- * The pdu data.
- */
- private ByteArrayInputStream mPduDataStream = null;
-
- /**
- * Store pdu headers
- */
- private PduHeaders mHeaders = null;
-
- /**
- * Store pdu parts.
- */
- private PduBody mBody = null;
-
- /**
- * Store the "type" parameter in "Content-Type" header field.
- */
- private static byte[] mTypeParam = null;
-
- /**
- * Store the "start" parameter in "Content-Type" header field.
- */
- private static byte[] mStartParam = null;
-
- /**
- * The log tag.
- */
- private static final String LOG_TAG = "PduParser";
- private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = false;
-
- /**
- * Whether to parse content-disposition part header
- */
- private final boolean mParseContentDisposition;
-
- /**
- * Constructor.
- *
- * @param pduDataStream pdu data to be parsed
- * @param parseContentDisposition whether to parse the Content-Disposition part header
- */
- public PduParser(byte[] pduDataStream, boolean parseContentDisposition) {
- mPduDataStream = new ByteArrayInputStream(pduDataStream);
- mParseContentDisposition = parseContentDisposition;
- }
-
- /**
- * Parse the pdu.
- *
- * @return the pdu structure if parsing successfully.
- * null if parsing error happened or mandatory fields are not set.
- */
- public GenericPdu parse(){
- if (mPduDataStream == null) {
- return null;
- }
-
- /* parse headers */
- mHeaders = parseHeaders(mPduDataStream);
- if (null == mHeaders) {
- // Parse headers failed.
- return null;
- }
-
- /* get the message type */
- int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
-
- /* check mandatory header fields */
- if (false == checkMandatoryHeader(mHeaders)) {
- log("check mandatory headers failed!");
- return null;
- }
-
- if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) ||
- (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) {
- /* need to parse the parts */
- mBody = parseParts(mPduDataStream);
- if (null == mBody) {
- // Parse parts failed.
- return null;
- }
- }
-
- switch (messageType) {
- case PduHeaders.MESSAGE_TYPE_SEND_REQ:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_REQ");
- }
- SendReq sendReq = new SendReq(mHeaders, mBody);
- return sendReq;
- case PduHeaders.MESSAGE_TYPE_SEND_CONF:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_CONF");
- }
- SendConf sendConf = new SendConf(mHeaders);
- return sendConf;
- case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFICATION_IND");
- }
- NotificationInd notificationInd =
- new NotificationInd(mHeaders);
- return notificationInd;
- case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFYRESP_IND");
- }
- NotifyRespInd notifyRespInd =
- new NotifyRespInd(mHeaders);
- return notifyRespInd;
- case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_RETRIEVE_CONF");
- }
- RetrieveConf retrieveConf =
- new RetrieveConf(mHeaders, mBody);
-
- byte[] contentType = retrieveConf.getContentType();
- if (null == contentType) {
- return null;
- }
- String ctTypeStr = new String(contentType);
- if (ctTypeStr.equals(ContentType.MULTIPART_MIXED)
- || ctTypeStr.equals(ContentType.MULTIPART_RELATED)
- || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
- // The MMS content type must be "application/vnd.wap.multipart.mixed"
- // or "application/vnd.wap.multipart.related"
- // or "application/vnd.wap.multipart.alternative"
- return retrieveConf;
- } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
- // "application/vnd.wap.multipart.alternative"
- // should take only the first part.
- PduPart firstPart = mBody.getPart(0);
- mBody.removeAll();
- mBody.addPart(0, firstPart);
- return retrieveConf;
- }
- return null;
- case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_DELIVERY_IND");
- }
- DeliveryInd deliveryInd =
- new DeliveryInd(mHeaders);
- return deliveryInd;
- case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_ACKNOWLEDGE_IND");
- }
- AcknowledgeInd acknowledgeInd =
- new AcknowledgeInd(mHeaders);
- return acknowledgeInd;
- case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_ORIG_IND");
- }
- ReadOrigInd readOrigInd =
- new ReadOrigInd(mHeaders);
- return readOrigInd;
- case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_REC_IND");
- }
- ReadRecInd readRecInd =
- new ReadRecInd(mHeaders);
- return readRecInd;
- default:
- log("Parser doesn't support this message type in this version!");
- return null;
- }
- }
-
- /**
- * Parse pdu headers.
- *
- * @param pduDataStream pdu data input stream
- * @return headers in PduHeaders structure, null when parse fail
- */
- protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){
- if (pduDataStream == null) {
- return null;
- }
- boolean keepParsing = true;
- PduHeaders headers = new PduHeaders();
-
- while (keepParsing && (pduDataStream.available() > 0)) {
- pduDataStream.mark(1);
- int headerField = extractByteValue(pduDataStream);
- /* parse custom text header */
- if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) {
- pduDataStream.reset();
- byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "TextHeader: " + new String(bVal));
- }
- /* we should ignore it at the moment */
- continue;
- }
- switch (headerField) {
- case PduHeaders.MESSAGE_TYPE:
- {
- int messageType = extractByteValue(pduDataStream);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: messageType: " + messageType);
- }
- switch (messageType) {
- // We don't support these kind of messages now.
- case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
- case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
- case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
- case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
- case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
- case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
- return null;
- }
- try {
- headers.setOctet(messageType, headerField);
- } catch(InvalidHeaderValueException e) {
- log("Set invalid Octet value: " + messageType +
- " into the header filed: " + headerField);
- return null;
- } catch(RuntimeException e) {
- log(headerField + "is not Octet header field!");
- return null;
- }
- break;
- }
- /* Octect value */
- case PduHeaders.REPORT_ALLOWED:
- case PduHeaders.ADAPTATION_ALLOWED:
- case PduHeaders.DELIVERY_REPORT:
- case PduHeaders.DRM_CONTENT:
- case PduHeaders.DISTRIBUTION_INDICATOR:
- case PduHeaders.QUOTAS:
- case PduHeaders.READ_REPORT:
- case PduHeaders.STORE:
- case PduHeaders.STORED:
- case PduHeaders.TOTALS:
- case PduHeaders.SENDER_VISIBILITY:
- case PduHeaders.READ_STATUS:
- case PduHeaders.CANCEL_STATUS:
- case PduHeaders.PRIORITY:
- case PduHeaders.STATUS:
- case PduHeaders.REPLY_CHARGING:
- case PduHeaders.MM_STATE:
- case PduHeaders.RECOMMENDED_RETRIEVAL_MODE:
- case PduHeaders.CONTENT_CLASS:
- case PduHeaders.RETRIEVE_STATUS:
- case PduHeaders.STORE_STATUS:
- /**
- * The following field has a different value when
- * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
- * For now we ignore this fact, since we do not support these PDUs
- */
- case PduHeaders.RESPONSE_STATUS:
- {
- int value = extractByteValue(pduDataStream);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: byte: " + headerField + " value: " +
- value);
- }
-
- try {
- headers.setOctet(value, headerField);
- } catch(InvalidHeaderValueException e) {
- log("Set invalid Octet value: " + value +
- " into the header filed: " + headerField);
- return null;
- } catch(RuntimeException e) {
- log(headerField + "is not Octet header field!");
- return null;
- }
- break;
- }
-
- /* Long-Integer */
- case PduHeaders.DATE:
- case PduHeaders.REPLY_CHARGING_SIZE:
- case PduHeaders.MESSAGE_SIZE:
- {
- try {
- long value = parseLongInteger(pduDataStream);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: longint: " + headerField + " value: " +
- value);
- }
- headers.setLongInteger(value, headerField);
- } catch(RuntimeException e) {
- log(headerField + "is not Long-Integer header field!");
- return null;
- }
- break;
- }
-
- /* Integer-Value */
- case PduHeaders.MESSAGE_COUNT:
- case PduHeaders.START:
- case PduHeaders.LIMIT:
- {
- try {
- long value = parseIntegerValue(pduDataStream);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: int: " + headerField + " value: " +
- value);
- }
- headers.setLongInteger(value, headerField);
- } catch(RuntimeException e) {
- log(headerField + "is not Long-Integer header field!");
- return null;
- }
- break;
- }
-
- /* Text-String */
- case PduHeaders.TRANSACTION_ID:
- case PduHeaders.REPLY_CHARGING_ID:
- case PduHeaders.AUX_APPLIC_ID:
- case PduHeaders.APPLIC_ID:
- case PduHeaders.REPLY_APPLIC_ID:
- /**
- * The next three header fields are email addresses
- * as defined in RFC2822,
- * not including the characters "<" and ">"
- */
- case PduHeaders.MESSAGE_ID:
- case PduHeaders.REPLACE_ID:
- case PduHeaders.CANCEL_ID:
- /**
- * The following field has a different value when
- * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
- * For now we ignore this fact, since we do not support these PDUs
- */
- case PduHeaders.CONTENT_LOCATION:
- {
- byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- if (null != value) {
- try {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: string: " + headerField + " value: " +
- new String(value));
- }
- headers.setTextString(value, headerField);
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch(RuntimeException e) {
- log(headerField + "is not Text-String header field!");
- return null;
- }
- }
- break;
- }
-
- /* Encoded-string-value */
- case PduHeaders.SUBJECT:
- case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT:
- case PduHeaders.RETRIEVE_TEXT:
- case PduHeaders.STATUS_TEXT:
- case PduHeaders.STORE_STATUS_TEXT:
- /* the next one is not support
- * M-Mbox-Delete.conf and M-Delete.conf now */
- case PduHeaders.RESPONSE_TEXT:
- {
- EncodedStringValue value =
- parseEncodedStringValue(pduDataStream);
- if (null != value) {
- try {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: encoded string: " + headerField
- + " value: " + value.getString());
- }
- headers.setEncodedStringValue(value, headerField);
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch (RuntimeException e) {
- log(headerField + "is not Encoded-String-Value header field!");
- return null;
- }
- }
- break;
- }
-
- /* Addressing model */
- case PduHeaders.BCC:
- case PduHeaders.CC:
- case PduHeaders.TO:
- {
- EncodedStringValue value =
- parseEncodedStringValue(pduDataStream);
- if (null != value) {
- byte[] address = value.getTextString();
- if (null != address) {
- String str = new String(address);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: (to/cc/bcc) address: " + headerField
- + " value: " + str);
- }
- int endIndex = str.indexOf("/");
- if (endIndex > 0) {
- str = str.substring(0, endIndex);
- }
- try {
- value.setTextString(str.getBytes());
- } catch(NullPointerException e) {
- log("null pointer error!");
- return null;
- }
- }
-
- try {
- headers.appendEncodedStringValue(value, headerField);
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch(RuntimeException e) {
- log(headerField + "is not Encoded-String-Value header field!");
- return null;
- }
- }
- break;
- }
-
- /* Value-length
- * (Absolute-token Date-value | Relative-token Delta-seconds-value) */
- case PduHeaders.DELIVERY_TIME:
- case PduHeaders.EXPIRY:
- case PduHeaders.REPLY_CHARGING_DEADLINE:
- {
- /* parse Value-length */
- parseValueLength(pduDataStream);
-
- /* Absolute-token or Relative-token */
- int token = extractByteValue(pduDataStream);
-
- /* Date-value or Delta-seconds-value */
- long timeValue;
- try {
- timeValue = parseLongInteger(pduDataStream);
- } catch(RuntimeException e) {
- log(headerField + "is not Long-Integer header field!");
- return null;
- }
- if (PduHeaders.VALUE_RELATIVE_TOKEN == token) {
- /* need to convert the Delta-seconds-value
- * into Date-value */
- timeValue = System.currentTimeMillis()/1000 + timeValue;
- }
-
- try {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: time value: " + headerField
- + " value: " + timeValue);
- }
- headers.setLongInteger(timeValue, headerField);
- } catch(RuntimeException e) {
- log(headerField + "is not Long-Integer header field!");
- return null;
- }
- break;
- }
-
- case PduHeaders.FROM: {
- /* From-value =
- * Value-length
- * (Address-present-token Encoded-string-value | Insert-address-token)
- */
- EncodedStringValue from = null;
- parseValueLength(pduDataStream); /* parse value-length */
-
- /* Address-present-token or Insert-address-token */
- int fromToken = extractByteValue(pduDataStream);
-
- /* Address-present-token or Insert-address-token */
- if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) {
- /* Encoded-string-value */
- from = parseEncodedStringValue(pduDataStream);
- if (null != from) {
- byte[] address = from.getTextString();
- if (null != address) {
- String str = new String(address);
- int endIndex = str.indexOf("/");
- if (endIndex > 0) {
- str = str.substring(0, endIndex);
- }
- try {
- from.setTextString(str.getBytes());
- } catch(NullPointerException e) {
- log("null pointer error!");
- return null;
- }
- }
- }
- } else {
- try {
- from = new EncodedStringValue(
- PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes());
- } catch(NullPointerException e) {
- log(headerField + "is not Encoded-String-Value header field!");
- return null;
- }
- }
-
- try {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: from address: " + headerField
- + " value: " + from.getString());
- }
- headers.setEncodedStringValue(from, PduHeaders.FROM);
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch(RuntimeException e) {
- log(headerField + "is not Encoded-String-Value header field!");
- return null;
- }
- break;
- }
-
- case PduHeaders.MESSAGE_CLASS: {
- /* Message-class-value = Class-identifier | Token-text */
- pduDataStream.mark(1);
- int messageClass = extractByteValue(pduDataStream);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: MESSAGE_CLASS: " + headerField
- + " value: " + messageClass);
- }
-
- if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) {
- /* Class-identifier */
- try {
- if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) {
- headers.setTextString(
- PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(),
- PduHeaders.MESSAGE_CLASS);
- } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) {
- headers.setTextString(
- PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(),
- PduHeaders.MESSAGE_CLASS);
- } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) {
- headers.setTextString(
- PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(),
- PduHeaders.MESSAGE_CLASS);
- } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) {
- headers.setTextString(
- PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(),
- PduHeaders.MESSAGE_CLASS);
- }
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch(RuntimeException e) {
- log(headerField + "is not Text-String header field!");
- return null;
- }
- } else {
- /* Token-text */
- pduDataStream.reset();
- byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- if (null != messageClassString) {
- try {
- headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS);
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch(RuntimeException e) {
- log(headerField + "is not Text-String header field!");
- return null;
- }
- }
- }
- break;
- }
-
- case PduHeaders.MMS_VERSION: {
- int version = parseShortInteger(pduDataStream);
-
- try {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: MMS_VERSION: " + headerField
- + " value: " + version);
- }
- headers.setOctet(version, PduHeaders.MMS_VERSION);
- } catch(InvalidHeaderValueException e) {
- log("Set invalid Octet value: " + version +
- " into the header filed: " + headerField);
- return null;
- } catch(RuntimeException e) {
- log(headerField + "is not Octet header field!");
- return null;
- }
- break;
- }
-
- case PduHeaders.PREVIOUSLY_SENT_BY: {
- /* Previously-sent-by-value =
- * Value-length Forwarded-count-value Encoded-string-value */
- /* parse value-length */
- parseValueLength(pduDataStream);
-
- /* parse Forwarded-count-value */
- try {
- parseIntegerValue(pduDataStream);
- } catch(RuntimeException e) {
- log(headerField + " is not Integer-Value");
- return null;
- }
-
- /* parse Encoded-string-value */
- EncodedStringValue previouslySentBy =
- parseEncodedStringValue(pduDataStream);
- if (null != previouslySentBy) {
- try {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_BY: " + headerField
- + " value: " + previouslySentBy.getString());
- }
- headers.setEncodedStringValue(previouslySentBy,
- PduHeaders.PREVIOUSLY_SENT_BY);
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch(RuntimeException e) {
- log(headerField + "is not Encoded-String-Value header field!");
- return null;
- }
- }
- break;
- }
-
- case PduHeaders.PREVIOUSLY_SENT_DATE: {
- /* Previously-sent-date-value =
- * Value-length Forwarded-count-value Date-value */
- /* parse value-length */
- parseValueLength(pduDataStream);
-
- /* parse Forwarded-count-value */
- try {
- parseIntegerValue(pduDataStream);
- } catch(RuntimeException e) {
- log(headerField + " is not Integer-Value");
- return null;
- }
-
- /* Date-value */
- try {
- long perviouslySentDate = parseLongInteger(pduDataStream);
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_DATE: " + headerField
- + " value: " + perviouslySentDate);
- }
- headers.setLongInteger(perviouslySentDate,
- PduHeaders.PREVIOUSLY_SENT_DATE);
- } catch(RuntimeException e) {
- log(headerField + "is not Long-Integer header field!");
- return null;
- }
- break;
- }
-
- case PduHeaders.MM_FLAGS: {
- /* MM-flags-value =
- * Value-length
- * ( Add-token | Remove-token | Filter-token )
- * Encoded-string-value
- */
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: MM_FLAGS: " + headerField
- + " NOT REALLY SUPPORTED");
- }
-
- /* parse Value-length */
- parseValueLength(pduDataStream);
-
- /* Add-token | Remove-token | Filter-token */
- extractByteValue(pduDataStream);
-
- /* Encoded-string-value */
- parseEncodedStringValue(pduDataStream);
-
- /* not store this header filed in "headers",
- * because now PduHeaders doesn't support it */
- break;
- }
-
- /* Value-length
- * (Message-total-token | Size-total-token) Integer-Value */
- case PduHeaders.MBOX_TOTALS:
- case PduHeaders.MBOX_QUOTAS:
- {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: MBOX_TOTALS: " + headerField);
- }
- /* Value-length */
- parseValueLength(pduDataStream);
-
- /* Message-total-token | Size-total-token */
- extractByteValue(pduDataStream);
-
- /*Integer-Value*/
- try {
- parseIntegerValue(pduDataStream);
- } catch(RuntimeException e) {
- log(headerField + " is not Integer-Value");
- return null;
- }
-
- /* not store these headers filed in "headers",
- because now PduHeaders doesn't support them */
- break;
- }
-
- case PduHeaders.ELEMENT_DESCRIPTOR: {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: ELEMENT_DESCRIPTOR: " + headerField);
- }
- parseContentType(pduDataStream, null);
-
- /* not store this header filed in "headers",
- because now PduHeaders doesn't support it */
- break;
- }
-
- case PduHeaders.CONTENT_TYPE: {
- HashMap<Integer, Object> map =
- new HashMap<Integer, Object>();
- byte[] contentType =
- parseContentType(pduDataStream, map);
-
- if (null != contentType) {
- try {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: CONTENT_TYPE: " + headerField +
- contentType.toString());
- }
- headers.setTextString(contentType, PduHeaders.CONTENT_TYPE);
- } catch(NullPointerException e) {
- log("null pointer error!");
- } catch(RuntimeException e) {
- log(headerField + "is not Text-String header field!");
- return null;
- }
- }
-
- /* get start parameter */
- mStartParam = (byte[]) map.get(PduPart.P_START);
-
- /* get charset parameter */
- mTypeParam= (byte[]) map.get(PduPart.P_TYPE);
-
- keepParsing = false;
- break;
- }
-
- case PduHeaders.CONTENT:
- case PduHeaders.ADDITIONAL_HEADERS:
- case PduHeaders.ATTRIBUTES:
- default: {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "parseHeaders: Unknown header: " + headerField);
- }
- log("Unknown header");
- }
- }
- }
-
- return headers;
- }
-
- /**
- * Parse pdu parts.
- *
- * @param pduDataStream pdu data input stream
- * @return parts in PduBody structure
- */
- protected PduBody parseParts(ByteArrayInputStream pduDataStream) {
- if (pduDataStream == null) {
- return null;
- }
-
- int count = parseUnsignedInt(pduDataStream); // get the number of parts
- PduBody body = new PduBody();
-
- for (int i = 0 ; i < count ; i++) {
- int headerLength = parseUnsignedInt(pduDataStream);
- int dataLength = parseUnsignedInt(pduDataStream);
- PduPart part = new PduPart();
- int startPos = pduDataStream.available();
- if (startPos <= 0) {
- // Invalid part.
- return null;
- }
-
- /* parse part's content-type */
- HashMap<Integer, Object> map = new HashMap<Integer, Object>();
- byte[] contentType = parseContentType(pduDataStream, map);
- if (null != contentType) {
- part.setContentType(contentType);
- } else {
- part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*"
- }
-
- /* get name parameter */
- byte[] name = (byte[]) map.get(PduPart.P_NAME);
- if (null != name) {
- part.setName(name);
- }
-
- /* get charset parameter */
- Integer charset = (Integer) map.get(PduPart.P_CHARSET);
- if (null != charset) {
- part.setCharset(charset);
- }
-
- /* parse part's headers */
- int endPos = pduDataStream.available();
- int partHeaderLen = headerLength - (startPos - endPos);
- if (partHeaderLen > 0) {
- if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) {
- // Parse part header faild.
- return null;
- }
- } else if (partHeaderLen < 0) {
- // Invalid length of content-type.
- return null;
- }
-
- /* FIXME: check content-id, name, filename and content location,
- * if not set anyone of them, generate a default content-location
- */
- if ((null == part.getContentLocation())
- && (null == part.getName())
- && (null == part.getFilename())
- && (null == part.getContentId())) {
- part.setContentLocation(Long.toOctalString(
- System.currentTimeMillis()).getBytes());
- }
-
- /* get part's data */
- if (dataLength > 0) {
- byte[] partData = new byte[dataLength];
- String partContentType = new String(part.getContentType());
- pduDataStream.read(partData, 0, dataLength);
- if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) {
- // parse "multipart/vnd.wap.multipart.alternative".
- PduBody childBody = parseParts(new ByteArrayInputStream(partData));
- // take the first part of children.
- part = childBody.getPart(0);
- } else {
- // Check Content-Transfer-Encoding.
- byte[] partDataEncoding = part.getContentTransferEncoding();
- if (null != partDataEncoding) {
- String encoding = new String(partDataEncoding);
- if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) {
- // Decode "base64" into "binary".
- partData = Base64.decodeBase64(partData);
- } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) {
- // Decode "quoted-printable" into "binary".
- partData = QuotedPrintable.decodeQuotedPrintable(partData);
- } else {
- // "binary" is the default encoding.
- }
- }
- if (null == partData) {
- log("Decode part data error!");
- return null;
- }
- part.setData(partData);
- }
- }
-
- /* add this part to body */
- if (THE_FIRST_PART == checkPartPosition(part)) {
- /* this is the first part */
- body.addPart(0, part);
- } else {
- /* add the part to the end */
- body.addPart(part);
- }
- }
-
- return body;
- }
-
- /**
- * Log status.
- *
- * @param text log information
- */
- private static void log(String text) {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, text);
- }
- }
-
- /**
- * Parse unsigned integer.
- *
- * @param pduDataStream pdu data input stream
- * @return the integer, -1 when failed
- */
- protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) {
- /**
- * From wap-230-wsp-20010705-a.pdf
- * The maximum size of a uintvar is 32 bits.
- * So it will be encoded in no more than 5 octets.
- */
- assert(null != pduDataStream);
- int result = 0;
- int temp = pduDataStream.read();
- if (temp == -1) {
- return temp;
- }
-
- while((temp & 0x80) != 0) {
- result = result << 7;
- result |= temp & 0x7F;
- temp = pduDataStream.read();
- if (temp == -1) {
- return temp;
- }
- }
-
- result = result << 7;
- result |= temp & 0x7F;
-
- return result;
- }
-
- /**
- * Parse value length.
- *
- * @param pduDataStream pdu data input stream
- * @return the integer
- */
- protected static int parseValueLength(ByteArrayInputStream pduDataStream) {
- /**
- * From wap-230-wsp-20010705-a.pdf
- * Value-length = Short-length | (Length-quote Length)
- * Short-length = <Any octet 0-30>
- * Length-quote = <Octet 31>
- * Length = Uintvar-integer
- * Uintvar-integer = 1*5 OCTET
- */
- assert(null != pduDataStream);
- int temp = pduDataStream.read();
- assert(-1 != temp);
- int first = temp & 0xFF;
-
- if (first <= SHORT_LENGTH_MAX) {
- return first;
- } else if (first == LENGTH_QUOTE) {
- return parseUnsignedInt(pduDataStream);
- }
-
- throw new RuntimeException ("Value length > LENGTH_QUOTE!");
- }
-
- /**
- * Parse encoded string value.
- *
- * @param pduDataStream pdu data input stream
- * @return the EncodedStringValue
- */
- protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){
- /**
- * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf
- * Encoded-string-value = Text-string | Value-length Char-set Text-string
- */
- assert(null != pduDataStream);
- pduDataStream.mark(1);
- EncodedStringValue returnValue = null;
- int charset = 0;
- int temp = pduDataStream.read();
- assert(-1 != temp);
- int first = temp & 0xFF;
- if (first == 0) {
- return new EncodedStringValue("");
- }
-
- pduDataStream.reset();
- if (first < TEXT_MIN) {
- parseValueLength(pduDataStream);
-
- charset = parseShortInteger(pduDataStream); //get the "Charset"
- }
-
- byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
-
- try {
- if (0 != charset) {
- returnValue = new EncodedStringValue(charset, textString);
- } else {
- returnValue = new EncodedStringValue(textString);
- }
- } catch(Exception e) {
- return null;
- }
-
- return returnValue;
- }
-
- /**
- * Parse Text-String or Quoted-String.
- *
- * @param pduDataStream pdu data input stream
- * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING
- * @return the string without End-of-string in byte array
- */
- protected static byte[] parseWapString(ByteArrayInputStream pduDataStream,
- int stringType) {
- assert(null != pduDataStream);
- /**
- * From wap-230-wsp-20010705-a.pdf
- * Text-string = [Quote] *TEXT End-of-string
- * If the first character in the TEXT is in the range of 128-255,
- * a Quote character must precede it.
- * Otherwise the Quote character must be omitted.
- * The Quote is not part of the contents.
- * Quote = <Octet 127>
- * End-of-string = <Octet 0>
- *
- * Quoted-string = <Octet 34> *TEXT End-of-string
- *
- * Token-text = Token End-of-string
- */
-
- // Mark supposed beginning of Text-string
- // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG
- pduDataStream.mark(1);
-
- // Check first char
- int temp = pduDataStream.read();
- assert(-1 != temp);
- if ((TYPE_QUOTED_STRING == stringType) &&
- (QUOTED_STRING_FLAG == temp)) {
- // Mark again if QUOTED_STRING_FLAG and ignore it
- pduDataStream.mark(1);
- } else if ((TYPE_TEXT_STRING == stringType) &&
- (QUOTE == temp)) {
- // Mark again if QUOTE and ignore it
- pduDataStream.mark(1);
- } else {
- // Otherwise go back to origin
- pduDataStream.reset();
- }
-
- // We are now definitely at the beginning of string
- /**
- * Return *TOKEN or *TEXT (Text-String without QUOTE,
- * Quoted-String without QUOTED_STRING_FLAG and without End-of-string)
- */
- return getWapString(pduDataStream, stringType);
- }
-
- /**
- * Check TOKEN data defined in RFC2616.
- * @param ch checking data
- * @return true when ch is TOKEN, false when ch is not TOKEN
- */
- protected static boolean isTokenCharacter(int ch) {
- /**
- * Token = 1*<any CHAR except CTLs or separators>
- * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64)
- * | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34)
- * | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61)
- * | "{"(123) | "}"(125) | SP(32) | HT(9)
- * CHAR = <any US-ASCII character (octets 0 - 127)>
- * CTL = <any US-ASCII control character
- * (octets 0 - 31) and DEL (127)>
- * SP = <US-ASCII SP, space (32)>
- * HT = <US-ASCII HT, horizontal-tab (9)>
- */
- if((ch < 33) || (ch > 126)) {
- return false;
- }
-
- switch(ch) {
- case '"': /* '"' */
- case '(': /* '(' */
- case ')': /* ')' */
- case ',': /* ',' */
- case '/': /* '/' */
- case ':': /* ':' */
- case ';': /* ';' */
- case '<': /* '<' */
- case '=': /* '=' */
- case '>': /* '>' */
- case '?': /* '?' */
- case '@': /* '@' */
- case '[': /* '[' */
- case '\\': /* '\' */
- case ']': /* ']' */
- case '{': /* '{' */
- case '}': /* '}' */
- return false;
- }
-
- return true;
- }
-
- /**
- * Check TEXT data defined in RFC2616.
- * @param ch checking data
- * @return true when ch is TEXT, false when ch is not TEXT
- */
- protected static boolean isText(int ch) {
- /**
- * TEXT = <any OCTET except CTLs,
- * but including LWS>
- * CTL = <any US-ASCII control character
- * (octets 0 - 31) and DEL (127)>
- * LWS = [CRLF] 1*( SP | HT )
- * CRLF = CR LF
- * CR = <US-ASCII CR, carriage return (13)>
- * LF = <US-ASCII LF, linefeed (10)>
- */
- if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) {
- return true;
- }
-
- switch(ch) {
- case '\t': /* '\t' */
- case '\n': /* '\n' */
- case '\r': /* '\r' */
- return true;
- }
-
- return false;
- }
-
- protected static byte[] getWapString(ByteArrayInputStream pduDataStream,
- int stringType) {
- assert(null != pduDataStream);
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- int temp = pduDataStream.read();
- assert(-1 != temp);
- while((-1 != temp) && ('\0' != temp)) {
- // check each of the character
- if (stringType == TYPE_TOKEN_STRING) {
- if (isTokenCharacter(temp)) {
- out.write(temp);
- }
- } else {
- if (isText(temp)) {
- out.write(temp);
- }
- }
-
- temp = pduDataStream.read();
- assert(-1 != temp);
- }
-
- if (out.size() > 0) {
- return out.toByteArray();
- }
-
- return null;
- }
-
- /**
- * Extract a byte value from the input stream.
- *
- * @param pduDataStream pdu data input stream
- * @return the byte
- */
- protected static int extractByteValue(ByteArrayInputStream pduDataStream) {
- assert(null != pduDataStream);
- int temp = pduDataStream.read();
- assert(-1 != temp);
- return temp & 0xFF;
- }
-
- /**
- * Parse Short-Integer.
- *
- * @param pduDataStream pdu data input stream
- * @return the byte
- */
- protected static int parseShortInteger(ByteArrayInputStream pduDataStream) {
- /**
- * From wap-230-wsp-20010705-a.pdf
- * Short-integer = OCTET
- * Integers in range 0-127 shall be encoded as a one
- * octet value with the most significant bit set to one (1xxx xxxx)
- * and with the value in the remaining least significant bits.
- */
- assert(null != pduDataStream);
- int temp = pduDataStream.read();
- assert(-1 != temp);
- return temp & 0x7F;
- }
-
- /**
- * Parse Long-Integer.
- *
- * @param pduDataStream pdu data input stream
- * @return long integer
- */
- protected static long parseLongInteger(ByteArrayInputStream pduDataStream) {
- /**
- * From wap-230-wsp-20010705-a.pdf
- * Long-integer = Short-length Multi-octet-integer
- * The Short-length indicates the length of the Multi-octet-integer
- * Multi-octet-integer = 1*30 OCTET
- * The content octets shall be an unsigned integer value
- * with the most significant octet encoded first (big-endian representation).
- * The minimum number of octets must be used to encode the value.
- * Short-length = <Any octet 0-30>
- */
- assert(null != pduDataStream);
- int temp = pduDataStream.read();
- assert(-1 != temp);
- int count = temp & 0xFF;
-
- if (count > LONG_INTEGER_LENGTH_MAX) {
- throw new RuntimeException("Octet count greater than 8 and I can't represent that!");
- }
-
- long result = 0;
-
- for (int i = 0 ; i < count ; i++) {
- temp = pduDataStream.read();
- assert(-1 != temp);
- result <<= 8;
- result += (temp & 0xFF);
- }
-
- return result;
- }
-
- /**
- * Parse Integer-Value.
- *
- * @param pduDataStream pdu data input stream
- * @return long integer
- */
- protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) {
- /**
- * From wap-230-wsp-20010705-a.pdf
- * Integer-Value = Short-integer | Long-integer
- */
- assert(null != pduDataStream);
- pduDataStream.mark(1);
- int temp = pduDataStream.read();
- assert(-1 != temp);
- pduDataStream.reset();
- if (temp > SHORT_INTEGER_MAX) {
- return parseShortInteger(pduDataStream);
- } else {
- return parseLongInteger(pduDataStream);
- }
- }
-
- /**
- * To skip length of the wap value.
- *
- * @param pduDataStream pdu data input stream
- * @param length area size
- * @return the values in this area
- */
- protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) {
- assert(null != pduDataStream);
- byte[] area = new byte[length];
- int readLen = pduDataStream.read(area, 0, length);
- if (readLen < length) { //The actually read length is lower than the length
- return -1;
- } else {
- return readLen;
- }
- }
-
- /**
- * Parse content type parameters. For now we just support
- * four parameters used in mms: "type", "start", "name", "charset".
- *
- * @param pduDataStream pdu data input stream
- * @param map to store parameters of Content-Type field
- * @param length length of all the parameters
- */
- protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream,
- HashMap<Integer, Object> map, Integer length) {
- /**
- * From wap-230-wsp-20010705-a.pdf
- * Parameter = Typed-parameter | Untyped-parameter
- * Typed-parameter = Well-known-parameter-token Typed-value
- * the actual expected type of the value is implied by the well-known parameter
- * Well-known-parameter-token = Integer-value
- * the code values used for parameters are specified in the Assigned Numbers appendix
- * Typed-value = Compact-value | Text-value
- * In addition to the expected type, there may be no value.
- * If the value cannot be encoded using the expected type, it shall be encoded as text.
- * Compact-value = Integer-value |
- * Date-value | Delta-seconds-value | Q-value | Version-value |
- * Uri-value
- * Untyped-parameter = Token-text Untyped-value
- * the type of the value is unknown, but it shall be encoded as an integer,
- * if that is possible.
- * Untyped-value = Integer-value | Text-value
- */
- assert(null != pduDataStream);
- assert(length > 0);
-
- int startPos = pduDataStream.available();
- int tempPos = 0;
- int lastLen = length;
- while(0 < lastLen) {
- int param = pduDataStream.read();
- assert(-1 != param);
- lastLen--;
-
- switch (param) {
- /**
- * From rfc2387, chapter 3.1
- * The type parameter must be specified and its value is the MIME media
- * type of the "root" body part. It permits a MIME user agent to
- * determine the content-type without reference to the enclosed body
- * part. If the value of the type parameter and the root body part's
- * content-type differ then the User Agent's behavior is undefined.
- *
- * From wap-230-wsp-20010705-a.pdf
- * type = Constrained-encoding
- * Constrained-encoding = Extension-Media | Short-integer
- * Extension-media = *TEXT End-of-string
- */
- case PduPart.P_TYPE:
- case PduPart.P_CT_MR_TYPE:
- pduDataStream.mark(1);
- int first = extractByteValue(pduDataStream);
- pduDataStream.reset();
- if (first > TEXT_MAX) {
- // Short-integer (well-known type)
- int index = parseShortInteger(pduDataStream);
-
- if (index < PduContentTypes.contentTypes.length) {
- byte[] type = (PduContentTypes.contentTypes[index]).getBytes();
- map.put(PduPart.P_TYPE, type);
- } else {
- //not support this type, ignore it.
- }
- } else {
- // Text-String (extension-media)
- byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- if ((null != type) && (null != map)) {
- map.put(PduPart.P_TYPE, type);
- }
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- break;
-
- /**
- * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3.
- * Start Parameter Referring to Presentation
- *
- * From rfc2387, chapter 3.2
- * The start parameter, if given, is the content-ID of the compound
- * object's "root". If not present the "root" is the first body part in
- * the Multipart/Related entity. The "root" is the element the
- * applications processes first.
- *
- * From wap-230-wsp-20010705-a.pdf
- * start = Text-String
- */
- case PduPart.P_START:
- case PduPart.P_DEP_START:
- byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- if ((null != start) && (null != map)) {
- map.put(PduPart.P_START, start);
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- break;
-
- /**
- * From oma-ts-mms-conf-v1_3.pdf
- * In creation, the character set SHALL be either us-ascii
- * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode].
- * In retrieval, both us-ascii and utf-8 SHALL be supported.
- *
- * From wap-230-wsp-20010705-a.pdf
- * charset = Well-known-charset|Text-String
- * Well-known-charset = Any-charset | Integer-value
- * Both are encoded using values from Character Set
- * Assignments table in Assigned Numbers
- * Any-charset = <Octet 128>
- * Equivalent to the special RFC2616 charset value "*"
- */
- case PduPart.P_CHARSET:
- pduDataStream.mark(1);
- int firstValue = extractByteValue(pduDataStream);
- pduDataStream.reset();
- //Check first char
- if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) ||
- (END_STRING_FLAG == firstValue)) {
- //Text-String (extension-charset)
- byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- try {
- int charsetInt = CharacterSets.getMibEnumValue(
- new String(charsetStr));
- map.put(PduPart.P_CHARSET, charsetInt);
- } catch (UnsupportedEncodingException e) {
- // Not a well-known charset, use "*".
- Log.e(LOG_TAG, Arrays.toString(charsetStr), e);
- map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET);
- }
- } else {
- //Well-known-charset
- int charset = (int) parseIntegerValue(pduDataStream);
- if (map != null) {
- map.put(PduPart.P_CHARSET, charset);
- }
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- break;
-
- /**
- * From oma-ts-mms-conf-v1_3.pdf
- * A name for multipart object SHALL be encoded using name-parameter
- * for Content-Type header in WSP multipart headers.
- *
- * From wap-230-wsp-20010705-a.pdf
- * name = Text-String
- */
- case PduPart.P_DEP_NAME:
- case PduPart.P_NAME:
- byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- if ((null != name) && (null != map)) {
- map.put(PduPart.P_NAME, name);
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- break;
- default:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "Not supported Content-Type parameter");
- }
- if (-1 == skipWapValue(pduDataStream, lastLen)) {
- Log.e(LOG_TAG, "Corrupt Content-Type");
- } else {
- lastLen = 0;
- }
- break;
- }
- }
-
- if (0 != lastLen) {
- Log.e(LOG_TAG, "Corrupt Content-Type");
- }
- }
-
- /**
- * Parse content type.
- *
- * @param pduDataStream pdu data input stream
- * @param map to store parameters in Content-Type header field
- * @return Content-Type value
- */
- protected static byte[] parseContentType(ByteArrayInputStream pduDataStream,
- HashMap<Integer, Object> map) {
- /**
- * From wap-230-wsp-20010705-a.pdf
- * Content-type-value = Constrained-media | Content-general-form
- * Content-general-form = Value-length Media-type
- * Media-type = (Well-known-media | Extension-Media) *(Parameter)
- */
- assert(null != pduDataStream);
-
- byte[] contentType = null;
- pduDataStream.mark(1);
- int temp = pduDataStream.read();
- assert(-1 != temp);
- pduDataStream.reset();
-
- int cur = (temp & 0xFF);
-
- if (cur < TEXT_MIN) {
- int length = parseValueLength(pduDataStream);
- int startPos = pduDataStream.available();
- pduDataStream.mark(1);
- temp = pduDataStream.read();
- assert(-1 != temp);
- pduDataStream.reset();
- int first = (temp & 0xFF);
-
- if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) {
- contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- } else if (first > TEXT_MAX) {
- int index = parseShortInteger(pduDataStream);
-
- if (index < PduContentTypes.contentTypes.length) { //well-known type
- contentType = (PduContentTypes.contentTypes[index]).getBytes();
- } else {
- pduDataStream.reset();
- contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- }
- } else {
- Log.e(LOG_TAG, "Corrupt content-type");
- return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
- }
-
- int endPos = pduDataStream.available();
- int parameterLen = length - (startPos - endPos);
- if (parameterLen > 0) {//have parameters
- parseContentTypeParams(pduDataStream, map, parameterLen);
- }
-
- if (parameterLen < 0) {
- Log.e(LOG_TAG, "Corrupt MMS message");
- return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
- }
- } else if (cur <= TEXT_MAX) {
- contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- } else {
- contentType =
- (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes();
- }
-
- return contentType;
- }
-
- /**
- * Parse part's headers.
- *
- * @param pduDataStream pdu data input stream
- * @param part to store the header informations of the part
- * @param length length of the headers
- * @return true if parse successfully, false otherwise
- */
- protected boolean parsePartHeaders(ByteArrayInputStream pduDataStream,
- PduPart part, int length) {
- assert(null != pduDataStream);
- assert(null != part);
- assert(length > 0);
-
- /**
- * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.
- * A name for multipart object SHALL be encoded using name-parameter
- * for Content-Type header in WSP multipart headers.
- * In decoding, name-parameter of Content-Type SHALL be used if available.
- * If name-parameter of Content-Type is not available,
- * filename parameter of Content-Disposition header SHALL be used if available.
- * If neither name-parameter of Content-Type header nor filename parameter
- * of Content-Disposition header is available,
- * Content-Location header SHALL be used if available.
- *
- * Within SMIL part the reference to the media object parts SHALL use
- * either Content-ID or Content-Location mechanism [RFC2557]
- * and the corresponding WSP part headers in media object parts
- * contain the corresponding definitions.
- */
- int startPos = pduDataStream.available();
- int tempPos = 0;
- int lastLen = length;
- while(0 < lastLen) {
- int header = pduDataStream.read();
- assert(-1 != header);
- lastLen--;
-
- if (header > TEXT_MAX) {
- // Number assigned headers.
- switch (header) {
- case PduPart.P_CONTENT_LOCATION:
- /**
- * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
- * Content-location-value = Uri-value
- */
- byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- if (null != contentLocation) {
- part.setContentLocation(contentLocation);
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- break;
- case PduPart.P_CONTENT_ID:
- /**
- * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
- * Content-ID-value = Quoted-string
- */
- byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING);
- if (null != contentId) {
- part.setContentId(contentId);
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- break;
- case PduPart.P_DEP_CONTENT_DISPOSITION:
- case PduPart.P_CONTENT_DISPOSITION:
- /**
- * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
- * Content-disposition-value = Value-length Disposition *(Parameter)
- * Disposition = Form-data | Attachment | Inline | Token-text
- * Form-data = <Octet 128>
- * Attachment = <Octet 129>
- * Inline = <Octet 130>
- */
-
- /*
- * some carrier mmsc servers do not support content_disposition
- * field correctly
- */
- if (mParseContentDisposition) {
- int len = parseValueLength(pduDataStream);
- pduDataStream.mark(1);
- int thisStartPos = pduDataStream.available();
- int thisEndPos = 0;
- int value = pduDataStream.read();
-
- if (value == PduPart.P_DISPOSITION_FROM_DATA ) {
- part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA);
- } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) {
- part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT);
- } else if (value == PduPart.P_DISPOSITION_INLINE) {
- part.setContentDisposition(PduPart.DISPOSITION_INLINE);
- } else {
- pduDataStream.reset();
- /* Token-text */
- part.setContentDisposition(parseWapString(pduDataStream
- , TYPE_TEXT_STRING));
- }
-
- /* get filename parameter and skip other parameters */
- thisEndPos = pduDataStream.available();
- if (thisStartPos - thisEndPos < len) {
- value = pduDataStream.read();
- if (value == PduPart.P_FILENAME) { //filename is text-string
- part.setFilename(parseWapString(pduDataStream
- , TYPE_TEXT_STRING));
- }
-
- /* skip other parameters */
- thisEndPos = pduDataStream.available();
- if (thisStartPos - thisEndPos < len) {
- int last = len - (thisStartPos - thisEndPos);
- byte[] temp = new byte[last];
- pduDataStream.read(temp, 0, last);
- }
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- }
- break;
- default:
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "Not supported Part headers: " + header);
- }
- if (-1 == skipWapValue(pduDataStream, lastLen)) {
- Log.e(LOG_TAG, "Corrupt Part headers");
- return false;
- }
- lastLen = 0;
- break;
- }
- } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) {
- // Not assigned header.
- byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING);
- byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING);
-
- // Check the header whether it is "Content-Transfer-Encoding".
- if (true ==
- PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) {
- part.setContentTransferEncoding(tempValue);
- }
-
- tempPos = pduDataStream.available();
- lastLen = length - (startPos - tempPos);
- } else {
- if (LOCAL_LOGV) {
- Log.v(LOG_TAG, "Not supported Part headers: " + header);
- }
- // Skip all headers of this part.
- if (-1 == skipWapValue(pduDataStream, lastLen)) {
- Log.e(LOG_TAG, "Corrupt Part headers");
- return false;
- }
- lastLen = 0;
- }
- }
-
- if (0 != lastLen) {
- Log.e(LOG_TAG, "Corrupt Part headers");
- return false;
- }
-
- return true;
- }
-
- /**
- * Check the position of a specified part.
- *
- * @param part the part to be checked
- * @return part position, THE_FIRST_PART when it's the
- * first one, THE_LAST_PART when it's the last one.
- */
- private static int checkPartPosition(PduPart part) {
- assert(null != part);
- if ((null == mTypeParam) &&
- (null == mStartParam)) {
- return THE_LAST_PART;
- }
-
- /* check part's content-id */
- if (null != mStartParam) {
- byte[] contentId = part.getContentId();
- if (null != contentId) {
- if (true == Arrays.equals(mStartParam, contentId)) {
- return THE_FIRST_PART;
- }
- }
- // This is not the first part, so append to end (keeping the original order)
- // Check b/19607294 for details of this change
- return THE_LAST_PART;
- }
-
- /* check part's content-type */
- if (null != mTypeParam) {
- byte[] contentType = part.getContentType();
- if (null != contentType) {
- if (true == Arrays.equals(mTypeParam, contentType)) {
- return THE_FIRST_PART;
- }
- }
- }
-
- return THE_LAST_PART;
- }
-
- /**
- * Check mandatory headers of a pdu.
- *
- * @param headers pdu headers
- * @return true if the pdu has all of the mandatory headers, false otherwise.
- */
- protected static boolean checkMandatoryHeader(PduHeaders headers) {
- if (null == headers) {
- return false;
- }
-
- /* get message type */
- int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
-
- /* check Mms-Version field */
- int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION);
- if (0 == mmsVersion) {
- // Every message should have Mms-Version field.
- return false;
- }
-
- /* check mandatory header fields */
- switch (messageType) {
- case PduHeaders.MESSAGE_TYPE_SEND_REQ:
- // Content-Type field.
- byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
- if (null == srContentType) {
- return false;
- }
-
- // From field.
- EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM);
- if (null == srFrom) {
- return false;
- }
-
- // Transaction-Id field.
- byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
- if (null == srTransactionId) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_SEND_CONF:
- // Response-Status field.
- int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS);
- if (0 == scResponseStatus) {
- return false;
- }
-
- // Transaction-Id field.
- byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
- if (null == scTransactionId) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
- // Content-Location field.
- byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION);
- if (null == niContentLocation) {
- return false;
- }
-
- // Expiry field.
- long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY);
- if (-1 == niExpiry) {
- return false;
- }
-
- // Message-Class field.
- byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS);
- if (null == niMessageClass) {
- return false;
- }
-
- // Message-Size field.
- long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE);
- if (-1 == niMessageSize) {
- return false;
- }
-
- // Transaction-Id field.
- byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
- if (null == niTransactionId) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
- // Status field.
- int nriStatus = headers.getOctet(PduHeaders.STATUS);
- if (0 == nriStatus) {
- return false;
- }
-
- // Transaction-Id field.
- byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
- if (null == nriTransactionId) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
- // Content-Type field.
- byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
- if (null == rcContentType) {
- return false;
- }
-
- // Date field.
- long rcDate = headers.getLongInteger(PduHeaders.DATE);
- if (-1 == rcDate) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
- // Date field.
- long diDate = headers.getLongInteger(PduHeaders.DATE);
- if (-1 == diDate) {
- return false;
- }
-
- // Message-Id field.
- byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
- if (null == diMessageId) {
- return false;
- }
-
- // Status field.
- int diStatus = headers.getOctet(PduHeaders.STATUS);
- if (0 == diStatus) {
- return false;
- }
-
- // To field.
- EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO);
- if (null == diTo) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
- // Transaction-Id field.
- byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
- if (null == aiTransactionId) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
- // Date field.
- long roDate = headers.getLongInteger(PduHeaders.DATE);
- if (-1 == roDate) {
- return false;
- }
-
- // From field.
- EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM);
- if (null == roFrom) {
- return false;
- }
-
- // Message-Id field.
- byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
- if (null == roMessageId) {
- return false;
- }
-
- // Read-Status field.
- int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
- if (0 == roReadStatus) {
- return false;
- }
-
- // To field.
- EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO);
- if (null == roTo) {
- return false;
- }
-
- break;
- case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
- // From field.
- EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM);
- if (null == rrFrom) {
- return false;
- }
-
- // Message-Id field.
- byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
- if (null == rrMessageId) {
- return false;
- }
-
- // Read-Status field.
- int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
- if (0 == rrReadStatus) {
- return false;
- }
-
- // To field.
- EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO);
- if (null == rrTo) {
- return false;
- }
-
- break;
- default:
- // Parser doesn't support this message type in this version.
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/PduPart.java b/src/java/com/google/android/mms/pdu/PduPart.java
deleted file mode 100644
index 1d196f7093..0000000000
--- a/src/java/com/google/android/mms/pdu/PduPart.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Esmertec AG.
- * Copyright (C) 2007-2008 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.google.android.mms.pdu;
-
-import android.net.Uri;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * The pdu part.
- */
-public class PduPart {
- /**
- * Well-Known Parameters.
- */
- public static final int P_Q = 0x80;
- public static final int P_CHARSET = 0x81;
- public static final int P_LEVEL = 0x82;
- public static final int P_TYPE = 0x83;
- public static final int P_DEP_NAME = 0x85;
- public static final int P_DEP_FILENAME = 0x86;
- public static final int P_DIFFERENCES = 0x87;
- public static final int P_PADDING = 0x88;
- // This value of "TYPE" s used with Content-Type: multipart/related
- public static final int P_CT_MR_TYPE = 0x89;
- public static final int P_DEP_START = 0x8A;
- public static final int P_DEP_START_INFO = 0x8B;
- public static final int P_DEP_COMMENT = 0x8C;
- public static final int P_DEP_DOMAIN = 0x8D;
- public static final int P_MAX_AGE = 0x8E;
- public static final int P_DEP_PATH = 0x8F;
- public static final int P_SECURE = 0x90;
- public static final int P_SEC = 0x91;
- public static final int P_MAC = 0x92;
- public static final int P_CREATION_DATE = 0x93;
- public static final int P_MODIFICATION_DATE = 0x94;
- public static final int P_READ_DATE = 0x95;
- public static final int P_SIZE = 0x96;
- public static final int P_NAME = 0x97;
- public static final int P_FILENAME = 0x98;
- public static final int P_START = 0x99;
- public static final int P_START_INFO = 0x9A;
- public static final int P_COMMENT = 0x9B;
- public static final int P_DOMAIN = 0x9C;
- public static final int P_PATH = 0x9D;
-
- /**
- * Header field names.
- */
- public static final int P_CONTENT_TYPE = 0x91;
- public static final int P_CONTENT_LOCATION = 0x8E;
- public static final int P_CONTENT_ID = 0xC0;
- public static final int P_DEP_CONTENT_DISPOSITION = 0xAE;
- public static final int P_CONTENT_DISPOSITION = 0xC5;
- // The next header is unassigned header, use reserved header(0x48) value.
- public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8;
-
- /**
- * Content=Transfer-Encoding string.
- */
- public static final String CONTENT_TRANSFER_ENCODING =
- "Content-Transfer-Encoding";
-
- /**
- * Value of Content-Transfer-Encoding.
- */
- public static final String P_BINARY = "binary";
- public static final String P_7BIT = "7bit";
- public static final String P_8BIT = "8bit";
- public static final String P_BASE64 = "base64";
- public static final String P_QUOTED_PRINTABLE = "quoted-printable";
-
- /**
- * Value of disposition can be set to PduPart when the value is octet in
- * the PDU.
- * "from-data" instead of Form-data<Octet 128>.
- * "attachment" instead of Attachment<Octet 129>.
- * "inline" instead of Inline<Octet 130>.
- */
- static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes();
- static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes();
- static final byte[] DISPOSITION_INLINE = "inline".getBytes();
-
- /**
- * Content-Disposition value.
- */
- public static final int P_DISPOSITION_FROM_DATA = 0x80;
- public static final int P_DISPOSITION_ATTACHMENT = 0x81;
- public static final int P_DISPOSITION_INLINE = 0x82;
-
- /**
- * Header of part.
- */
- private Map<Integer, Object> mPartHeader = null;
-
- /**
- * Data uri.
- */
- private Uri mUri = null;
-
- /**
- * Part data.
- */
- private byte[] mPartData = null;
-
- private static final String TAG = "PduPart";
-
- /**
- * Empty Constructor.
- */
- public PduPart() {
- mPartHeader = new HashMap<Integer, Object>();
- }
-
- /**
- * Set part data. The data are stored as byte array.
- *
- * @param data the data
- */
- public void setData(byte[] data) {
- if(data == null) {
- return;
- }
-
- mPartData = new byte[data.length];
- System.arraycopy(data, 0, mPartData, 0, data.length);
- }
-
- /**
- * @return A copy of the part data or null if the data wasn't set or
- * the data is stored as Uri.
- * @see #getDataUri
- */
- public byte[] getData() {
- if(mPartData == null) {
- return null;
- }
-
- byte[] byteArray = new byte[mPartData.length];
- System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length);
- return byteArray;
- }
-
- /**
- * @return The length of the data, if this object have data, else 0.
- */
- public int getDataLength() {
- if(mPartData != null){
- return mPartData.length;
- } else {
- return 0;
- }
- }
-
-
- /**
- * Set data uri. The data are stored as Uri.
- *
- * @param uri the uri
- */
- public void setDataUri(Uri uri) {
- mUri = uri;
- }
-
- /**
- * @return The Uri of the part data or null if the data wasn't set or
- * the data is stored as byte array.
- * @see #getData
- */
- public Uri getDataUri() {
- return mUri;
- }
-
- /**
- * Set Content-id value
- *
- * @param contentId the content-id value
- * @throws NullPointerException if the value is null.
- */
- public void setContentId(byte[] contentId) {
- if((contentId == null) || (contentId.length == 0)) {
- throw new IllegalArgumentException(
- "Content-Id may not be null or empty.");
- }
-
- if ((contentId.length > 1)
- && ((char) contentId[0] == '<')
- && ((char) contentId[contentId.length - 1] == '>')) {
- mPartHeader.put(P_CONTENT_ID, contentId);
- return;
- }
-
- // Insert beginning '<' and trailing '>' for Content-Id.
- byte[] buffer = new byte[contentId.length + 2];
- buffer[0] = (byte) (0xff & '<');
- buffer[buffer.length - 1] = (byte) (0xff & '>');
- System.arraycopy(contentId, 0, buffer, 1, contentId.length);
- mPartHeader.put(P_CONTENT_ID, buffer);
- }
-
- /**
- * Get Content-id value.
- *
- * @return the value
- */
- public byte[] getContentId() {
- return (byte[]) mPartHeader.get(P_CONTENT_ID);
- }
-
- /**
- * Set Char-set value.
- *
- * @param charset the value
- */
- public void setCharset(int charset) {
- mPartHeader.put(P_CHARSET, charset);
- }
-
- /**
- * Get Char-set value
- *
- * @return the charset value. Return 0 if charset was not set.
- */
- public int getCharset() {
- Integer charset = (Integer) mPartHeader.get(P_CHARSET);
- if(charset == null) {
- return 0;
- } else {
- return charset.intValue();
- }
- }
-
- /**
- * Set Content-Location value.
- *
- * @param contentLocation the value
- * @throws NullPointerException if the value is null.
- */
- public void setContentLocation(byte[] contentLocation) {
- if(contentLocation == null) {
- throw new NullPointerException("null content-location");
- }
-
- mPartHeader.put(P_CONTENT_LOCATION, contentLocation);
- }
-
- /**
- * Get Content-Location value.
- *
- * @return the value
- * return PduPart.disposition[0] instead of <Octet 128> (Form-data).
- * return PduPart.disposition[1] instead of <Octet 129> (Attachment).
- * return PduPart.disposition[2] instead of <Octet 130> (Inline).
- */
- public byte[] getContentLocation() {
- return (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
- }
-
- /**
- * Set Content-Disposition value.
- * Use PduPart.disposition[0] instead of <Octet 128> (Form-data).
- * Use PduPart.disposition[1] instead of <Octet 129> (Attachment).
- * Use PduPart.disposition[2] instead of <Octet 130> (Inline).
- *
- * @param contentDisposition the value
- * @throws NullPointerException if the value is null.
- */
- public void setContentDisposition(byte[] contentDisposition) {
- if(contentDisposition == null) {
- throw new NullPointerException("null content-disposition");
- }
-
- mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition);
- }
-
- /**
- * Get Content-Disposition value.
- *
- * @return the value
- */
- public byte[] getContentDisposition() {
- return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION);
- }
-
- /**
- * Set Content-Type value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setContentType(byte[] contentType) {
- if(contentType == null) {
- throw new NullPointerException("null content-type");
- }
-
- mPartHeader.put(P_CONTENT_TYPE, contentType);
- }
-
- /**
- * Get Content-Type value of part.
- *
- * @return the value
- */
- public byte[] getContentType() {
- return (byte[]) mPartHeader.get(P_CONTENT_TYPE);
- }
-
- /**
- * Set Content-Transfer-Encoding value
- *
- * @param contentId the content-id value
- * @throws NullPointerException if the value is null.
- */
- public void setContentTransferEncoding(byte[] contentTransferEncoding) {
- if(contentTransferEncoding == null) {
- throw new NullPointerException("null content-transfer-encoding");
- }
-
- mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
- }
-
- /**
- * Get Content-Transfer-Encoding value.
- *
- * @return the value
- */
- public byte[] getContentTransferEncoding() {
- return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING);
- }
-
- /**
- * Set Content-type parameter: name.
- *
- * @param name the name value
- * @throws NullPointerException if the value is null.
- */
- public void setName(byte[] name) {
- if(null == name) {
- throw new NullPointerException("null content-id");
- }
-
- mPartHeader.put(P_NAME, name);
- }
-
- /**
- * Get content-type parameter: name.
- *
- * @return the name
- */
- public byte[] getName() {
- return (byte[]) mPartHeader.get(P_NAME);
- }
-
- /**
- * Get Content-disposition parameter: filename
- *
- * @param fileName the filename value
- * @throws NullPointerException if the value is null.
- */
- public void setFilename(byte[] fileName) {
- if(null == fileName) {
- throw new NullPointerException("null content-id");
- }
-
- mPartHeader.put(P_FILENAME, fileName);
- }
-
- /**
- * Set Content-disposition parameter: filename
- *
- * @return the filename
- */
- public byte[] getFilename() {
- return (byte[]) mPartHeader.get(P_FILENAME);
- }
-
- public String generateLocation() {
- // Assumption: At least one of the content-location / name / filename
- // or content-id should be set. This is guaranteed by the PduParser
- // for incoming messages and by MM composer for outgoing messages.
- byte[] location = (byte[]) mPartHeader.get(P_NAME);
- if(null == location) {
- location = (byte[]) mPartHeader.get(P_FILENAME);
-
- if (null == location) {
- location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
- }
- }
-
- if (null == location) {
- byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID);
- return "cid:" + new String(contentId);
- } else {
- return new String(location);
- }
- }
-}
-
diff --git a/src/java/com/google/android/mms/pdu/PduPersister.java b/src/java/com/google/android/mms/pdu/PduPersister.java
deleted file mode 100755
index ee659aee46..0000000000
--- a/src/java/com/google/android/mms/pdu/PduPersister.java
+++ /dev/null
@@ -1,1543 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Esmertec AG.
- * Copyright (C) 2007-2008 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.google.android.mms.pdu;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.drm.DrmManagerClient;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.provider.Telephony;
-import android.provider.Telephony.Mms;
-import android.provider.Telephony.Mms.Addr;
-import android.provider.Telephony.Mms.Part;
-import android.provider.Telephony.MmsSms;
-import android.provider.Telephony.MmsSms.PendingMessages;
-import android.provider.Telephony.Threads;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.google.android.mms.ContentType;
-import com.google.android.mms.InvalidHeaderValueException;
-import com.google.android.mms.MmsException;
-import com.google.android.mms.util.DownloadDrmHelper;
-import com.google.android.mms.util.DrmConvertSession;
-import com.google.android.mms.util.PduCache;
-import com.google.android.mms.util.PduCacheEntry;
-import com.google.android.mms.util.SqliteWrapper;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * This class is the high-level manager of PDU storage.
- */
-public class PduPersister {
- private static final String TAG = "PduPersister";
- private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = false;
-
- private static final long DUMMY_THREAD_ID = Long.MAX_VALUE;
-
- /**
- * The uri of temporary drm objects.
- */
- public static final String TEMPORARY_DRM_OBJECT_URI =
- "content://mms/" + Long.MAX_VALUE + "/part";
- /**
- * Indicate that we transiently failed to process a MM.
- */
- public static final int PROC_STATUS_TRANSIENT_FAILURE = 1;
- /**
- * Indicate that we permanently failed to process a MM.
- */
- public static final int PROC_STATUS_PERMANENTLY_FAILURE = 2;
- /**
- * Indicate that we have successfully processed a MM.
- */
- public static final int PROC_STATUS_COMPLETED = 3;
-
- private static PduPersister sPersister;
- private static final PduCache PDU_CACHE_INSTANCE;
-
- private static final int[] ADDRESS_FIELDS = new int[] {
- PduHeaders.BCC,
- PduHeaders.CC,
- PduHeaders.FROM,
- PduHeaders.TO
- };
-
- private static final String[] PDU_PROJECTION = new String[] {
- Mms._ID,
- Mms.MESSAGE_BOX,
- Mms.THREAD_ID,
- Mms.RETRIEVE_TEXT,
- Mms.SUBJECT,
- Mms.CONTENT_LOCATION,
- Mms.CONTENT_TYPE,
- Mms.MESSAGE_CLASS,
- Mms.MESSAGE_ID,
- Mms.RESPONSE_TEXT,
- Mms.TRANSACTION_ID,
- Mms.CONTENT_CLASS,
- Mms.DELIVERY_REPORT,
- Mms.MESSAGE_TYPE,
- Mms.MMS_VERSION,
- Mms.PRIORITY,
- Mms.READ_REPORT,
- Mms.READ_STATUS,
- Mms.REPORT_ALLOWED,
- Mms.RETRIEVE_STATUS,
- Mms.STATUS,
- Mms.DATE,
- Mms.DELIVERY_TIME,
- Mms.EXPIRY,
- Mms.MESSAGE_SIZE,
- Mms.SUBJECT_CHARSET,
- Mms.RETRIEVE_TEXT_CHARSET,
- };
-
- private static final int PDU_COLUMN_ID = 0;
- private static final int PDU_COLUMN_MESSAGE_BOX = 1;
- private static final int PDU_COLUMN_THREAD_ID = 2;
- private static final int PDU_COLUMN_RETRIEVE_TEXT = 3;
- private static final int PDU_COLUMN_SUBJECT = 4;
- private static final int PDU_COLUMN_CONTENT_LOCATION = 5;
- private static final int PDU_COLUMN_CONTENT_TYPE = 6;
- private static final int PDU_COLUMN_MESSAGE_CLASS = 7;
- private static final int PDU_COLUMN_MESSAGE_ID = 8;
- private static final int PDU_COLUMN_RESPONSE_TEXT = 9;
- private static final int PDU_COLUMN_TRANSACTION_ID = 10;
- private static final int PDU_COLUMN_CONTENT_CLASS = 11;
- private static final int PDU_COLUMN_DELIVERY_REPORT = 12;
- private static final int PDU_COLUMN_MESSAGE_TYPE = 13;
- private static final int PDU_COLUMN_MMS_VERSION = 14;
- private static final int PDU_COLUMN_PRIORITY = 15;
- private static final int PDU_COLUMN_READ_REPORT = 16;
- private static final int PDU_COLUMN_READ_STATUS = 17;
- private static final int PDU_COLUMN_REPORT_ALLOWED = 18;
- private static final int PDU_COLUMN_RETRIEVE_STATUS = 19;
- private static final int PDU_COLUMN_STATUS = 20;
- private static final int PDU_COLUMN_DATE = 21;
- private static final int PDU_COLUMN_DELIVERY_TIME = 22;
- private static final int PDU_COLUMN_EXPIRY = 23;
- private static final int PDU_COLUMN_MESSAGE_SIZE = 24;
- private static final int PDU_COLUMN_SUBJECT_CHARSET = 25;
- private static final int PDU_COLUMN_RETRIEVE_TEXT_CHARSET = 26;
-
- private static final String[] PART_PROJECTION = new String[] {
- Part._ID,
- Part.CHARSET,
- Part.CONTENT_DISPOSITION,
- Part.CONTENT_ID,
- Part.CONTENT_LOCATION,
- Part.CONTENT_TYPE,
- Part.FILENAME,
- Part.NAME,
- Part.TEXT
- };
-
- private static final int PART_COLUMN_ID = 0;
- private static final int PART_COLUMN_CHARSET = 1;
- private static final int PART_COLUMN_CONTENT_DISPOSITION = 2;
- private static final int PART_COLUMN_CONTENT_ID = 3;
- private static final int PART_COLUMN_CONTENT_LOCATION = 4;
- private static final int PART_COLUMN_CONTENT_TYPE = 5;
- private static final int PART_COLUMN_FILENAME = 6;
- private static final int PART_COLUMN_NAME = 7;
- private static final int PART_COLUMN_TEXT = 8;
-
- private static final HashMap<Uri, Integer> MESSAGE_BOX_MAP;
- // These map are used for convenience in persist() and load().
- private static final HashMap<Integer, Integer> CHARSET_COLUMN_INDEX_MAP;
- private static final HashMap<Integer, Integer> ENCODED_STRING_COLUMN_INDEX_MAP;
- private static final HashMap<Integer, Integer> TEXT_STRING_COLUMN_INDEX_MAP;
- private static final HashMap<Integer, Integer> OCTET_COLUMN_INDEX_MAP;
- private static final HashMap<Integer, Integer> LONG_COLUMN_INDEX_MAP;
- private static final HashMap<Integer, String> CHARSET_COLUMN_NAME_MAP;
- private static final HashMap<Integer, String> ENCODED_STRING_COLUMN_NAME_MAP;
- private static final HashMap<Integer, String> TEXT_STRING_COLUMN_NAME_MAP;
- private static final HashMap<Integer, String> OCTET_COLUMN_NAME_MAP;
- private static final HashMap<Integer, String> LONG_COLUMN_NAME_MAP;
-
- static {
- MESSAGE_BOX_MAP = new HashMap<Uri, Integer>();
- MESSAGE_BOX_MAP.put(Mms.Inbox.CONTENT_URI, Mms.MESSAGE_BOX_INBOX);
- MESSAGE_BOX_MAP.put(Mms.Sent.CONTENT_URI, Mms.MESSAGE_BOX_SENT);
- MESSAGE_BOX_MAP.put(Mms.Draft.CONTENT_URI, Mms.MESSAGE_BOX_DRAFTS);
- MESSAGE_BOX_MAP.put(Mms.Outbox.CONTENT_URI, Mms.MESSAGE_BOX_OUTBOX);
-
- CHARSET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
- CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT_CHARSET);
- CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT_CHARSET);
-
- CHARSET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
- CHARSET_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT_CHARSET);
- CHARSET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT_CHARSET);
-
- // Encoded string field code -> column index/name map.
- ENCODED_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
- ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT);
- ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT);
-
- ENCODED_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
- ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT);
- ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT);
-
- // Text string field code -> column index/name map.
- TEXT_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
- TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_LOCATION, PDU_COLUMN_CONTENT_LOCATION);
- TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_TYPE, PDU_COLUMN_CONTENT_TYPE);
- TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_CLASS, PDU_COLUMN_MESSAGE_CLASS);
- TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_ID, PDU_COLUMN_MESSAGE_ID);
- TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RESPONSE_TEXT, PDU_COLUMN_RESPONSE_TEXT);
- TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.TRANSACTION_ID, PDU_COLUMN_TRANSACTION_ID);
-
- TEXT_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
- TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_LOCATION, Mms.CONTENT_LOCATION);
- TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_TYPE, Mms.CONTENT_TYPE);
- TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_CLASS, Mms.MESSAGE_CLASS);
- TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_ID, Mms.MESSAGE_ID);
- TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.RESPONSE_TEXT, Mms.RESPONSE_TEXT);
- TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.TRANSACTION_ID, Mms.TRANSACTION_ID);
-
- // Octet field code -> column index/name map.
- OCTET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_CLASS, PDU_COLUMN_CONTENT_CLASS);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_REPORT, PDU_COLUMN_DELIVERY_REPORT);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_TYPE, PDU_COLUMN_MESSAGE_TYPE);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MMS_VERSION, PDU_COLUMN_MMS_VERSION);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.PRIORITY, PDU_COLUMN_PRIORITY);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_REPORT, PDU_COLUMN_READ_REPORT);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_STATUS, PDU_COLUMN_READ_STATUS);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.REPORT_ALLOWED, PDU_COLUMN_REPORT_ALLOWED);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_STATUS, PDU_COLUMN_RETRIEVE_STATUS);
- OCTET_COLUMN_INDEX_MAP.put(PduHeaders.STATUS, PDU_COLUMN_STATUS);
-
- OCTET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_CLASS, Mms.CONTENT_CLASS);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_REPORT, Mms.DELIVERY_REPORT);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_TYPE, Mms.MESSAGE_TYPE);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.MMS_VERSION, Mms.MMS_VERSION);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.PRIORITY, Mms.PRIORITY);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_REPORT, Mms.READ_REPORT);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_STATUS, Mms.READ_STATUS);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.REPORT_ALLOWED, Mms.REPORT_ALLOWED);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_STATUS, Mms.RETRIEVE_STATUS);
- OCTET_COLUMN_NAME_MAP.put(PduHeaders.STATUS, Mms.STATUS);
-
- // Long field code -> column index/name map.
- LONG_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
- LONG_COLUMN_INDEX_MAP.put(PduHeaders.DATE, PDU_COLUMN_DATE);
- LONG_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_TIME, PDU_COLUMN_DELIVERY_TIME);
- LONG_COLUMN_INDEX_MAP.put(PduHeaders.EXPIRY, PDU_COLUMN_EXPIRY);
- LONG_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_SIZE, PDU_COLUMN_MESSAGE_SIZE);
-
- LONG_COLUMN_NAME_MAP = new HashMap<Integer, String>();
- LONG_COLUMN_NAME_MAP.put(PduHeaders.DATE, Mms.DATE);
- LONG_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_TIME, Mms.DELIVERY_TIME);
- LONG_COLUMN_NAME_MAP.put(PduHeaders.EXPIRY, Mms.EXPIRY);
- LONG_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_SIZE, Mms.MESSAGE_SIZE);
-
- PDU_CACHE_INSTANCE = PduCache.getInstance();
- }
-
- private final Context mContext;
- private final ContentResolver mContentResolver;
- private final DrmManagerClient mDrmManagerClient;
- private final TelephonyManager mTelephonyManager;
-
- private PduPersister(Context context) {
- mContext = context;
- mContentResolver = context.getContentResolver();
- mDrmManagerClient = new DrmManagerClient(context);
- mTelephonyManager = (TelephonyManager)context
- .getSystemService(Context.TELEPHONY_SERVICE);
- }
-
- /** Get(or create if not exist) an instance of PduPersister */
- public static PduPersister getPduPersister(Context context) {
- if ((sPersister == null)) {
- sPersister = new PduPersister(context);
- } else if (!context.equals(sPersister.mContext)) {
- sPersister.release();
- sPersister = new PduPersister(context);
- }
-
- return sPersister;
- }
-
- private void setEncodedStringValueToHeaders(
- Cursor c, int columnIndex,
- PduHeaders headers, int mapColumn) {
- String s = c.getString(columnIndex);
- if ((s != null) && (s.length() > 0)) {
- int charsetColumnIndex = CHARSET_COLUMN_INDEX_MAP.get(mapColumn);
- int charset = c.getInt(charsetColumnIndex);
- EncodedStringValue value = new EncodedStringValue(
- charset, getBytes(s));
- headers.setEncodedStringValue(value, mapColumn);
- }
- }
-
- private void setTextStringToHeaders(
- Cursor c, int columnIndex,
- PduHeaders headers, int mapColumn) {
- String s = c.getString(columnIndex);
- if (s != null) {
- headers.setTextString(getBytes(s), mapColumn);
- }
- }
-
- private void setOctetToHeaders(
- Cursor c, int columnIndex,
- PduHeaders headers, int mapColumn) throws InvalidHeaderValueException {
- if (!c.isNull(columnIndex)) {
- int b = c.getInt(columnIndex);
- headers.setOctet(b, mapColumn);
- }
- }
-
- private void setLongToHeaders(
- Cursor c, int columnIndex,
- PduHeaders headers, int mapColumn) {
- if (!c.isNull(columnIndex)) {
- long l = c.getLong(columnIndex);
- headers.setLongInteger(l, mapColumn);
- }
- }
-
- private Integer getIntegerFromPartColumn(Cursor c, int columnIndex) {
- if (!c.isNull(columnIndex)) {
- return c.getInt(columnIndex);
- }
- return null;
- }
-
- private byte[] getByteArrayFromPartColumn(Cursor c, int columnIndex) {
- if (!c.isNull(columnIndex)) {
- return getBytes(c.getString(columnIndex));
- }
- return null;
- }
-
- private PduPart[] loadParts(long msgId) throws MmsException {
- Cursor c = SqliteWrapper.query(mContext, mContentResolver,
- Uri.parse("content://mms/" + msgId + "/part"),
- PART_PROJECTION, null, null, null);
-
- PduPart[] parts = null;
-
- try {
- if ((c == null) || (c.getCount() == 0)) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "loadParts(" + msgId + "): no part to load.");
- }
- return null;
- }
-
- int partCount = c.getCount();
- int partIdx = 0;
- parts = new PduPart[partCount];
- while (c.moveToNext()) {
- PduPart part = new PduPart();
- Integer charset = getIntegerFromPartColumn(
- c, PART_COLUMN_CHARSET);
- if (charset != null) {
- part.setCharset(charset);
- }
-
- byte[] contentDisposition = getByteArrayFromPartColumn(
- c, PART_COLUMN_CONTENT_DISPOSITION);
- if (contentDisposition != null) {
- part.setContentDisposition(contentDisposition);
- }
-
- byte[] contentId = getByteArrayFromPartColumn(
- c, PART_COLUMN_CONTENT_ID);
- if (contentId != null) {
- part.setContentId(contentId);
- }
-
- byte[] contentLocation = getByteArrayFromPartColumn(
- c, PART_COLUMN_CONTENT_LOCATION);
- if (contentLocation != null) {
- part.setContentLocation(contentLocation);
- }
-
- byte[] contentType = getByteArrayFromPartColumn(
- c, PART_COLUMN_CONTENT_TYPE);
- if (contentType != null) {
- part.setContentType(contentType);
- } else {
- throw new MmsException("Content-Type must be set.");
- }
-
- byte[] fileName = getByteArrayFromPartColumn(
- c, PART_COLUMN_FILENAME);
- if (fileName != null) {
- part.setFilename(fileName);
- }
-
- byte[] name = getByteArrayFromPartColumn(
- c, PART_COLUMN_NAME);
- if (name != null) {
- part.setName(name);
- }
-
- // Construct a Uri for this part.
- long partId = c.getLong(PART_COLUMN_ID);
- Uri partURI = Uri.parse("content://mms/part/" + partId);
- part.setDataUri(partURI);
-
- // For images/audio/video, we won't keep their data in Part
- // because their renderer accept Uri as source.
- String type = toIsoString(contentType);
- if (!ContentType.isImageType(type)
- && !ContentType.isAudioType(type)
- && !ContentType.isVideoType(type)) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- InputStream is = null;
-
- // Store simple string values directly in the database instead of an
- // external file. This makes the text searchable and retrieval slightly
- // faster.
- if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type)
- || ContentType.TEXT_HTML.equals(type)) {
- String text = c.getString(PART_COLUMN_TEXT);
- byte [] blob = new EncodedStringValue(text != null ? text : "")
- .getTextString();
- baos.write(blob, 0, blob.length);
- } else {
-
- try {
- is = mContentResolver.openInputStream(partURI);
-
- byte[] buffer = new byte[256];
- int len = is.read(buffer);
- while (len >= 0) {
- baos.write(buffer, 0, len);
- len = is.read(buffer);
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to load part data", e);
- c.close();
- throw new MmsException(e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- Log.e(TAG, "Failed to close stream", e);
- } // Ignore
- }
- }
- }
- part.setData(baos.toByteArray());
- }
- parts[partIdx++] = part;
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
-
- return parts;
- }
-
- private void loadAddress(long msgId, PduHeaders headers) {
- Cursor c = SqliteWrapper.query(mContext, mContentResolver,
- Uri.parse("content://mms/" + msgId + "/addr"),
- new String[] { Addr.ADDRESS, Addr.CHARSET, Addr.TYPE },
- null, null, null);
-
- if (c != null) {
- try {
- while (c.moveToNext()) {
- String addr = c.getString(0);
- if (!TextUtils.isEmpty(addr)) {
- int addrType = c.getInt(2);
- switch (addrType) {
- case PduHeaders.FROM:
- headers.setEncodedStringValue(
- new EncodedStringValue(c.getInt(1), getBytes(addr)),
- addrType);
- break;
- case PduHeaders.TO:
- case PduHeaders.CC:
- case PduHeaders.BCC:
- headers.appendEncodedStringValue(
- new EncodedStringValue(c.getInt(1), getBytes(addr)),
- addrType);
- break;
- default:
- Log.e(TAG, "Unknown address type: " + addrType);
- break;
- }
- }
- }
- } finally {
- c.close();
- }
- }
- }
-
- /**
- * Load a PDU from storage by given Uri.
- *
- * @param uri The Uri of the PDU to be loaded.
- * @return A generic PDU object, it may be cast to dedicated PDU.
- * @throws MmsException Failed to load some fields of a PDU.
- */
- public GenericPdu load(Uri uri) throws MmsException {
- GenericPdu pdu = null;
- PduCacheEntry cacheEntry = null;
- int msgBox = 0;
- long threadId = -1;
- try {
- synchronized(PDU_CACHE_INSTANCE) {
- if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "load: " + uri + " blocked by isUpdating()");
- }
- try {
- PDU_CACHE_INSTANCE.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "load: ", e);
- }
- cacheEntry = PDU_CACHE_INSTANCE.get(uri);
- if (cacheEntry != null) {
- return cacheEntry.getPdu();
- }
- }
- // Tell the cache to indicate to other callers that this item
- // is currently being updated.
- PDU_CACHE_INSTANCE.setUpdating(uri, true);
- }
-
- Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,
- PDU_PROJECTION, null, null, null);
- PduHeaders headers = new PduHeaders();
- Set<Entry<Integer, Integer>> set;
- long msgId = ContentUris.parseId(uri);
-
- try {
- if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) {
- throw new MmsException("Bad uri: " + uri);
- }
-
- msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX);
- threadId = c.getLong(PDU_COLUMN_THREAD_ID);
-
- set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setEncodedStringValueToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
-
- set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setTextStringToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
-
- set = OCTET_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setOctetToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
-
- set = LONG_COLUMN_INDEX_MAP.entrySet();
- for (Entry<Integer, Integer> e : set) {
- setLongToHeaders(
- c, e.getValue(), headers, e.getKey());
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
-
- // Check whether 'msgId' has been assigned a valid value.
- if (msgId == -1L) {
- throw new MmsException("Error! ID of the message: -1.");
- }
-
- // Load address information of the MM.
- loadAddress(msgId, headers);
-
- int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
- PduBody body = new PduBody();
-
- // For PDU which type is M_retrieve.conf or Send.req, we should
- // load multiparts and put them into the body of the PDU.
- if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
- || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
- PduPart[] parts = loadParts(msgId);
- if (parts != null) {
- int partsNum = parts.length;
- for (int i = 0; i < partsNum; i++) {
- body.addPart(parts[i]);
- }
- }
- }
-
- switch (msgType) {
- case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
- pdu = new NotificationInd(headers);
- break;
- case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
- pdu = new DeliveryInd(headers);
- break;
- case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
- pdu = new ReadOrigInd(headers);
- break;
- case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
- pdu = new RetrieveConf(headers, body);
- break;
- case PduHeaders.MESSAGE_TYPE_SEND_REQ:
- pdu = new SendReq(headers, body);
- break;
- case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
- pdu = new AcknowledgeInd(headers);
- break;
- case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
- pdu = new NotifyRespInd(headers);
- break;
- case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
- pdu = new ReadRecInd(headers);
- break;
- case PduHeaders.MESSAGE_TYPE_SEND_CONF:
- case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
- case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
- case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
- case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
- case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
- case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
- case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
- case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
- throw new MmsException(
- "Unsupported PDU type: " + Integer.toHexString(msgType));
-
- default:
- throw new MmsException(
- "Unrecognized PDU type: " + Integer.toHexString(msgType));
- }
- } finally {
- synchronized(PDU_CACHE_INSTANCE) {
- if (pdu != null) {
- assert(PDU_CACHE_INSTANCE.get(uri) == null);
- // Update the cache entry with the real info
- cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
- PDU_CACHE_INSTANCE.put(uri, cacheEntry);
- }
- PDU_CACHE_INSTANCE.setUpdating(uri, false);
- PDU_CACHE_INSTANCE.notifyAll(); // tell anybody waiting on this entry to go ahead
- }
- }
- return pdu;
- }
-
- private void persistAddress(
- long msgId, int type, EncodedStringValue[] array) {
- ContentValues values = new ContentValues(3);
-
- for (EncodedStringValue addr : array) {
- values.clear(); // Clear all values first.
- values.put(Addr.ADDRESS, toIsoString(addr.getTextString()));
- values.put(Addr.CHARSET, addr.getCharacterSet());
- values.put(Addr.TYPE, type);
-
- Uri uri = Uri.parse("content://mms/" + msgId + "/addr");
- SqliteWrapper.insert(mContext, mContentResolver, uri, values);
- }
- }
-
- private static String getPartContentType(PduPart part) {
- return part.getContentType() == null ? null : toIsoString(part.getContentType());
- }
-
- public Uri persistPart(PduPart part, long msgId, HashMap<Uri, InputStream> preOpenedFiles)
- throws MmsException {
- Uri uri = Uri.parse("content://mms/" + msgId + "/part");
- ContentValues values = new ContentValues(8);
-
- int charset = part.getCharset();
- if (charset != 0 ) {
- values.put(Part.CHARSET, charset);
- }
-
- String contentType = getPartContentType(part);
- if (contentType != null) {
- // There is no "image/jpg" in Android (and it's an invalid mimetype).
- // Change it to "image/jpeg"
- if (ContentType.IMAGE_JPG.equals(contentType)) {
- contentType = ContentType.IMAGE_JPEG;
- }
-
- values.put(Part.CONTENT_TYPE, contentType);
- // To ensure the SMIL part is always the first part.
- if (ContentType.APP_SMIL.equals(contentType)) {
- values.put(Part.SEQ, -1);
- }
- } else {
- throw new MmsException("MIME type of the part must be set.");
- }
-
- if (part.getFilename() != null) {
- String fileName = new String(part.getFilename());
- values.put(Part.FILENAME, fileName);
- }
-
- if (part.getName() != null) {
- String name = new String(part.getName());
- values.put(Part.NAME, name);
- }
-
- Object value = null;
- if (part.getContentDisposition() != null) {
- value = toIsoString(part.getContentDisposition());
- values.put(Part.CONTENT_DISPOSITION, (String) value);
- }
-
- if (part.getContentId() != null) {
- value = toIsoString(part.getContentId());
- values.put(Part.CONTENT_ID, (String) value);
- }
-
- if (part.getContentLocation() != null) {
- value = toIsoString(part.getContentLocation());
- values.put(Part.CONTENT_LOCATION, (String) value);
- }
-
- Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
- if (res == null) {
- throw new MmsException("Failed to persist part, return null.");
- }
-
- persistData(part, res, contentType, preOpenedFiles);
- // After successfully store the data, we should update
- // the dataUri of the part.
- part.setDataUri(res);
-
- return res;
- }
-
- /**
- * Save data of the part into storage. The source data may be given
- * by a byte[] or a Uri. If it's a byte[], directly save it
- * into storage, otherwise load source data from the dataUri and then
- * save it. If the data is an image, we may scale down it according
- * to user preference.
- *
- * @param part The PDU part which contains data to be saved.
- * @param uri The URI of the part.
- * @param contentType The MIME type of the part.
- * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
- * @throws MmsException Cannot find source data or error occurred
- * while saving the data.
- */
- private void persistData(PduPart part, Uri uri,
- String contentType, HashMap<Uri, InputStream> preOpenedFiles)
- throws MmsException {
- OutputStream os = null;
- InputStream is = null;
- DrmConvertSession drmConvertSession = null;
- Uri dataUri = null;
- String path = null;
-
- try {
- byte[] data = part.getData();
- if (ContentType.TEXT_PLAIN.equals(contentType)
- || ContentType.APP_SMIL.equals(contentType)
- || ContentType.TEXT_HTML.equals(contentType)) {
- ContentValues cv = new ContentValues();
- if (data == null) {
- data = new String("").getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
- }
- cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
- if (mContentResolver.update(uri, cv, null, null) != 1) {
- throw new MmsException("unable to update " + uri.toString());
- }
- } else {
- boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
- if (isDrm) {
- if (uri != null) {
- try (ParcelFileDescriptor pfd =
- mContentResolver.openFileDescriptor(uri, "r")) {
- if (pfd.getStatSize() > 0) {
- // we're not going to re-persist and re-encrypt an already
- // converted drm file
- return;
- }
- } catch (Exception e) {
- Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
- }
- }
- // We haven't converted the file yet, start the conversion
- drmConvertSession = DrmConvertSession.open(mContext, contentType);
- if (drmConvertSession == null) {
- throw new MmsException("Mimetype " + contentType +
- " can not be converted.");
- }
- }
- // uri can look like:
- // content://mms/part/98
- os = mContentResolver.openOutputStream(uri);
- if (data == null) {
- dataUri = part.getDataUri();
- if ((dataUri == null) || (dataUri.equals(uri))) {
- Log.w(TAG, "Can't find data for this part.");
- return;
- }
- // dataUri can look like:
- // content://com.google.android.gallery3d.provider/picasa/item/5720646660183715586
- if (preOpenedFiles != null && preOpenedFiles.containsKey(dataUri)) {
- is = preOpenedFiles.get(dataUri);
- }
- if (is == null) {
- is = mContentResolver.openInputStream(dataUri);
- }
-
- if (LOCAL_LOGV) {
- Log.v(TAG, "Saving data to: " + uri);
- }
-
- byte[] buffer = new byte[8192];
- for (int len = 0; (len = is.read(buffer)) != -1; ) {
- if (!isDrm) {
- os.write(buffer, 0, len);
- } else {
- byte[] convertedData = drmConvertSession.convert(buffer, len);
- if (convertedData != null) {
- os.write(convertedData, 0, convertedData.length);
- } else {
- throw new MmsException("Error converting drm data.");
- }
- }
- }
- } else {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Saving data to: " + uri);
- }
- if (!isDrm) {
- os.write(data);
- } else {
- dataUri = uri;
- byte[] convertedData = drmConvertSession.convert(data, data.length);
- if (convertedData != null) {
- os.write(convertedData, 0, convertedData.length);
- } else {
- throw new MmsException("Error converting drm data.");
- }
- }
- }
- }
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Failed to open Input/Output stream.", e);
- throw new MmsException(e);
- } catch (IOException e) {
- Log.e(TAG, "Failed to read/write data.", e);
- throw new MmsException(e);
- } finally {
- if (os != null) {
- try {
- os.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException while closing: " + os, e);
- } // Ignore
- }
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException while closing: " + is, e);
- } // Ignore
- }
- if (drmConvertSession != null) {
- drmConvertSession.close(path);
-
- // Reset the permissions on the encrypted part file so everyone has only read
- // permission.
- File f = new File(path);
- ContentValues values = new ContentValues(0);
- SqliteWrapper.update(mContext, mContentResolver,
- Uri.parse("content://mms/resetFilePerm/" + f.getName()),
- values, null, null);
- }
- }
- }
-
- private void updateAddress(
- long msgId, int type, EncodedStringValue[] array) {
- // Delete old address information and then insert new ones.
- SqliteWrapper.delete(mContext, mContentResolver,
- Uri.parse("content://mms/" + msgId + "/addr"),
- Addr.TYPE + "=" + type, null);
-
- persistAddress(msgId, type, array);
- }
-
- /**
- * Update headers of a SendReq.
- *
- * @param uri The PDU which need to be updated.
- * @param pdu New headers.
- * @throws MmsException Bad URI or updating failed.
- */
- public void updateHeaders(Uri uri, SendReq sendReq) {
- synchronized(PDU_CACHE_INSTANCE) {
- // If the cache item is getting updated, wait until it's done updating before
- // purging it.
- if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "updateHeaders: " + uri + " blocked by isUpdating()");
- }
- try {
- PDU_CACHE_INSTANCE.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "updateHeaders: ", e);
- }
- }
- }
- PDU_CACHE_INSTANCE.purge(uri);
-
- ContentValues values = new ContentValues(10);
- byte[] contentType = sendReq.getContentType();
- if (contentType != null) {
- values.put(Mms.CONTENT_TYPE, toIsoString(contentType));
- }
-
- long date = sendReq.getDate();
- if (date != -1) {
- values.put(Mms.DATE, date);
- }
-
- int deliveryReport = sendReq.getDeliveryReport();
- if (deliveryReport != 0) {
- values.put(Mms.DELIVERY_REPORT, deliveryReport);
- }
-
- long expiry = sendReq.getExpiry();
- if (expiry != -1) {
- values.put(Mms.EXPIRY, expiry);
- }
-
- byte[] msgClass = sendReq.getMessageClass();
- if (msgClass != null) {
- values.put(Mms.MESSAGE_CLASS, toIsoString(msgClass));
- }
-
- int priority = sendReq.getPriority();
- if (priority != 0) {
- values.put(Mms.PRIORITY, priority);
- }
-
- int readReport = sendReq.getReadReport();
- if (readReport != 0) {
- values.put(Mms.READ_REPORT, readReport);
- }
-
- byte[] transId = sendReq.getTransactionId();
- if (transId != null) {
- values.put(Mms.TRANSACTION_ID, toIsoString(transId));
- }
-
- EncodedStringValue subject = sendReq.getSubject();
- if (subject != null) {
- values.put(Mms.SUBJECT, toIsoString(subject.getTextString()));
- values.put(Mms.SUBJECT_CHARSET, subject.getCharacterSet());
- } else {
- values.put(Mms.SUBJECT, "");
- }
-
- long messageSize = sendReq.getMessageSize();
- if (messageSize > 0) {
- values.put(Mms.MESSAGE_SIZE, messageSize);
- }
-
- PduHeaders headers = sendReq.getPduHeaders();
- HashSet<String> recipients = new HashSet<String>();
- for (int addrType : ADDRESS_FIELDS) {
- EncodedStringValue[] array = null;
- if (addrType == PduHeaders.FROM) {
- EncodedStringValue v = headers.getEncodedStringValue(addrType);
- if (v != null) {
- array = new EncodedStringValue[1];
- array[0] = v;
- }
- } else {
- array = headers.getEncodedStringValues(addrType);
- }
-
- if (array != null) {
- long msgId = ContentUris.parseId(uri);
- updateAddress(msgId, addrType, array);
- if (addrType == PduHeaders.TO) {
- for (EncodedStringValue v : array) {
- if (v != null) {
- recipients.add(v.getString());
- }
- }
- }
- }
- }
- if (!recipients.isEmpty()) {
- long threadId = Threads.getOrCreateThreadId(mContext, recipients);
- values.put(Mms.THREAD_ID, threadId);
- }
-
- SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
- }
-
- private void updatePart(Uri uri, PduPart part, HashMap<Uri, InputStream> preOpenedFiles)
- throws MmsException {
- ContentValues values = new ContentValues(7);
-
- int charset = part.getCharset();
- if (charset != 0 ) {
- values.put(Part.CHARSET, charset);
- }
-
- String contentType = null;
- if (part.getContentType() != null) {
- contentType = toIsoString(part.getContentType());
- values.put(Part.CONTENT_TYPE, contentType);
- } else {
- throw new MmsException("MIME type of the part must be set.");
- }
-
- if (part.getFilename() != null) {
- String fileName = new String(part.getFilename());
- values.put(Part.FILENAME, fileName);
- }
-
- if (part.getName() != null) {
- String name = new String(part.getName());
- values.put(Part.NAME, name);
- }
-
- Object value = null;
- if (part.getContentDisposition() != null) {
- value = toIsoString(part.getContentDisposition());
- values.put(Part.CONTENT_DISPOSITION, (String) value);
- }
-
- if (part.getContentId() != null) {
- value = toIsoString(part.getContentId());
- values.put(Part.CONTENT_ID, (String) value);
- }
-
- if (part.getContentLocation() != null) {
- value = toIsoString(part.getContentLocation());
- values.put(Part.CONTENT_LOCATION, (String) value);
- }
-
- SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
-
- // Only update the data when:
- // 1. New binary data supplied or
- // 2. The Uri of the part is different from the current one.
- if ((part.getData() != null)
- || (!uri.equals(part.getDataUri()))) {
- persistData(part, uri, contentType, preOpenedFiles);
- }
- }
-
- /**
- * Update all parts of a PDU.
- *
- * @param uri The PDU which need to be updated.
- * @param body New message body of the PDU.
- * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
- * @throws MmsException Bad URI or updating failed.
- */
- public void updateParts(Uri uri, PduBody body, HashMap<Uri, InputStream> preOpenedFiles)
- throws MmsException {
- try {
- PduCacheEntry cacheEntry;
- synchronized(PDU_CACHE_INSTANCE) {
- if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "updateParts: " + uri + " blocked by isUpdating()");
- }
- try {
- PDU_CACHE_INSTANCE.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "updateParts: ", e);
- }
- cacheEntry = PDU_CACHE_INSTANCE.get(uri);
- if (cacheEntry != null) {
- ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body);
- }
- }
- // Tell the cache to indicate to other callers that this item
- // is currently being updated.
- PDU_CACHE_INSTANCE.setUpdating(uri, true);
- }
-
- ArrayList<PduPart> toBeCreated = new ArrayList<PduPart>();
- HashMap<Uri, PduPart> toBeUpdated = new HashMap<Uri, PduPart>();
-
- int partsNum = body.getPartsNum();
- StringBuilder filter = new StringBuilder().append('(');
- for (int i = 0; i < partsNum; i++) {
- PduPart part = body.getPart(i);
- Uri partUri = part.getDataUri();
- if ((partUri == null) || TextUtils.isEmpty(partUri.getAuthority())
- || !partUri.getAuthority().startsWith("mms")) {
- toBeCreated.add(part);
- } else {
- toBeUpdated.put(partUri, part);
-
- // Don't use 'i > 0' to determine whether we should append
- // 'AND' since 'i = 0' may be skipped in another branch.
- if (filter.length() > 1) {
- filter.append(" AND ");
- }
-
- filter.append(Part._ID);
- filter.append("!=");
- DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment());
- }
- }
- filter.append(')');
-
- long msgId = ContentUris.parseId(uri);
-
- // Remove the parts which doesn't exist anymore.
- SqliteWrapper.delete(mContext, mContentResolver,
- Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"),
- filter.length() > 2 ? filter.toString() : null, null);
-
- // Create new parts which didn't exist before.
- for (PduPart part : toBeCreated) {
- persistPart(part, msgId, preOpenedFiles);
- }
-
- // Update the modified parts.
- for (Map.Entry<Uri, PduPart> e : toBeUpdated.entrySet()) {
- updatePart(e.getKey(), e.getValue(), preOpenedFiles);
- }
- } finally {
- synchronized(PDU_CACHE_INSTANCE) {
- PDU_CACHE_INSTANCE.setUpdating(uri, false);
- PDU_CACHE_INSTANCE.notifyAll();
- }
- }
- }
-
- /**
- * Persist a PDU object to specific location in the storage.
- *
- * @param pdu The PDU object to be stored.
- * @param uri Where to store the given PDU object.
- * @param createThreadId if true, this function may create a thread id for the recipients
- * @param groupMmsEnabled if true, all of the recipients addressed in the PDU will be used
- * to create the associated thread. When false, only the sender will be used in finding or
- * creating the appropriate thread or conversation.
- * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
- * @return A Uri which can be used to access the stored PDU.
- */
-
- public Uri persist(GenericPdu pdu, Uri uri, boolean createThreadId, boolean groupMmsEnabled,
- HashMap<Uri, InputStream> preOpenedFiles)
- throws MmsException {
- if (uri == null) {
- throw new MmsException("Uri may not be null.");
- }
- long msgId = -1;
- try {
- msgId = ContentUris.parseId(uri);
- } catch (NumberFormatException e) {
- // the uri ends with "inbox" or something else like that
- }
- boolean existingUri = msgId != -1;
-
- if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {
- throw new MmsException(
- "Bad destination, must be one of "
- + "content://mms/inbox, content://mms/sent, "
- + "content://mms/drafts, content://mms/outbox, "
- + "content://mms/temp.");
- }
- synchronized(PDU_CACHE_INSTANCE) {
- // If the cache item is getting updated, wait until it's done updating before
- // purging it.
- if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "persist: " + uri + " blocked by isUpdating()");
- }
- try {
- PDU_CACHE_INSTANCE.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "persist1: ", e);
- }
- }
- }
- PDU_CACHE_INSTANCE.purge(uri);
-
- PduHeaders header = pdu.getPduHeaders();
- PduBody body = null;
- ContentValues values = new ContentValues();
- Set<Entry<Integer, String>> set;
-
- set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet();
- for (Entry<Integer, String> e : set) {
- int field = e.getKey();
- EncodedStringValue encodedString = header.getEncodedStringValue(field);
- if (encodedString != null) {
- String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field);
- values.put(e.getValue(), toIsoString(encodedString.getTextString()));
- values.put(charsetColumn, encodedString.getCharacterSet());
- }
- }
-
- set = TEXT_STRING_COLUMN_NAME_MAP.entrySet();
- for (Entry<Integer, String> e : set){
- byte[] text = header.getTextString(e.getKey());
- if (text != null) {
- values.put(e.getValue(), toIsoString(text));
- }
- }
-
- set = OCTET_COLUMN_NAME_MAP.entrySet();
- for (Entry<Integer, String> e : set){
- int b = header.getOctet(e.getKey());
- if (b != 0) {
- values.put(e.getValue(), b);
- }
- }
-
- set = LONG_COLUMN_NAME_MAP.entrySet();
- for (Entry<Integer, String> e : set){
- long l = header.getLongInteger(e.getKey());
- if (l != -1L) {
- values.put(e.getValue(), l);
- }
- }
-
- HashMap<Integer, EncodedStringValue[]> addressMap =
- new HashMap<Integer, EncodedStringValue[]>(ADDRESS_FIELDS.length);
- // Save address information.
- for (int addrType : ADDRESS_FIELDS) {
- EncodedStringValue[] array = null;
- if (addrType == PduHeaders.FROM) {
- EncodedStringValue v = header.getEncodedStringValue(addrType);
- if (v != null) {
- array = new EncodedStringValue[1];
- array[0] = v;
- }
- } else {
- array = header.getEncodedStringValues(addrType);
- }
- addressMap.put(addrType, array);
- }
-
- HashSet<String> recipients = new HashSet<String>();
- int msgType = pdu.getMessageType();
- // Here we only allocate thread ID for M-Notification.ind,
- // M-Retrieve.conf and M-Send.req.
- // Some of other PDU types may be allocated a thread ID outside
- // this scope.
- if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND)
- || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
- || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
- switch (msgType) {
- case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
- case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
- loadRecipients(PduHeaders.FROM, recipients, addressMap, false);
-
- // For received messages when group MMS is enabled, we want to associate this
- // message with the thread composed of all the recipients -- all but our own
- // number, that is. This includes the person who sent the
- // message or the FROM field (above) in addition to the other people the message
- // was addressed to or the TO field. Our own number is in that TO field and
- // we have to ignore it in loadRecipients.
- if (groupMmsEnabled) {
- loadRecipients(PduHeaders.TO, recipients, addressMap, true);
-
- // Also load any numbers in the CC field to address group messaging
- // compatibility issues with devices that place numbers in this field
- // for group messages.
- loadRecipients(PduHeaders.CC, recipients, addressMap, true);
- }
- break;
- case PduHeaders.MESSAGE_TYPE_SEND_REQ:
- loadRecipients(PduHeaders.TO, recipients, addressMap, false);
- break;
- }
- long threadId = 0;
- if (createThreadId && !recipients.isEmpty()) {
- // Given all the recipients associated with this message, find (or create) the
- // correct thread.
- threadId = Threads.getOrCreateThreadId(mContext, recipients);
- }
- values.put(Mms.THREAD_ID, threadId);
- }
-
- // Save parts first to avoid inconsistent message is loaded
- // while saving the parts.
- long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
-
- // Figure out if this PDU is a text-only message
- boolean textOnly = true;
-
- // Sum up the total message size
- int messageSize = 0;
-
- // Get body if the PDU is a RetrieveConf or SendReq.
- if (pdu instanceof MultimediaMessagePdu) {
- body = ((MultimediaMessagePdu) pdu).getBody();
- // Start saving parts if necessary.
- if (body != null) {
- int partsNum = body.getPartsNum();
- if (partsNum > 2) {
- // For a text-only message there will be two parts: 1-the SMIL, 2-the text.
- // Down a few lines below we're checking to make sure we've only got SMIL or
- // text. We also have to check then we don't have more than two parts.
- // Otherwise, a slideshow with two text slides would be marked as textOnly.
- textOnly = false;
- }
- for (int i = 0; i < partsNum; i++) {
- PduPart part = body.getPart(i);
- messageSize += part.getDataLength();
- persistPart(part, dummyId, preOpenedFiles);
-
- // If we've got anything besides text/plain or SMIL part, then we've got
- // an mms message with some other type of attachment.
- String contentType = getPartContentType(part);
- if (contentType != null && !ContentType.APP_SMIL.equals(contentType)
- && !ContentType.TEXT_PLAIN.equals(contentType)) {
- textOnly = false;
- }
- }
- }
- }
- // Record whether this mms message is a simple plain text or not. This is a hint for the
- // UI.
- values.put(Mms.TEXT_ONLY, textOnly ? 1 : 0);
- // The message-size might already have been inserted when parsing the
- // PDU header. If not, then we insert the message size as well.
- if (values.getAsInteger(Mms.MESSAGE_SIZE) == null) {
- values.put(Mms.MESSAGE_SIZE, messageSize);
- }
-
- Uri res = null;
- if (existingUri) {
- res = uri;
- SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);
- } else {
- res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
- if (res == null) {
- throw new MmsException("persist() failed: return null.");
- }
- // Get the real ID of the PDU and update all parts which were
- // saved with the dummy ID.
- msgId = ContentUris.parseId(res);
- }
-
- values = new ContentValues(1);
- values.put(Part.MSG_ID, msgId);
- SqliteWrapper.update(mContext, mContentResolver,
- Uri.parse("content://mms/" + dummyId + "/part"),
- values, null, null);
- // We should return the longest URI of the persisted PDU, for
- // example, if input URI is "content://mms/inbox" and the _ID of
- // persisted PDU is '8', we should return "content://mms/inbox/8"
- // instead of "content://mms/8".
- // FIXME: Should the MmsProvider be responsible for this???
- if (!existingUri) {
- res = Uri.parse(uri + "/" + msgId);
- }
-
- // Save address information.
- for (int addrType : ADDRESS_FIELDS) {
- EncodedStringValue[] array = addressMap.get(addrType);
- if (array != null) {
- persistAddress(msgId, addrType, array);
- }
- }
-
- return res;
- }
-
- /**
- * For a given address type, extract the recipients from the headers.
- *
- * @param addressType can be PduHeaders.FROM, PduHeaders.TO or PduHeaders.CC
- * @param recipients a HashSet that is loaded with the recipients from the FROM, TO or CC headers
- * @param addressMap a HashMap of the addresses from the ADDRESS_FIELDS header
- * @param excludeMyNumber if true, the number of this phone will be excluded from recipients
- */
- private void loadRecipients(int addressType, HashSet<String> recipients,
- HashMap<Integer, EncodedStringValue[]> addressMap, boolean excludeMyNumber) {
- EncodedStringValue[] array = addressMap.get(addressType);
- if (array == null) {
- return;
- }
- // If the TO recipients is only a single address, then we can skip loadRecipients when
- // we're excluding our own number because we know that address is our own.
- if (excludeMyNumber && array.length == 1) {
- return;
- }
- final SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
- final Set<String> myPhoneNumbers = new HashSet<String>();
- if (excludeMyNumber) {
- // Build a list of my phone numbers from the various sims.
- for (int subid : subscriptionManager.getActiveSubscriptionIdList()) {
- final String myNumber = mTelephonyManager.getLine1Number(subid);
- if (myNumber != null) {
- myPhoneNumbers.add(myNumber);
- }
- }
- }
-
- for (EncodedStringValue v : array) {
- if (v != null) {
- final String number = v.getString();
- if (excludeMyNumber) {
- for (final String myNumber : myPhoneNumbers) {
- if (!PhoneNumberUtils.compare(number, myNumber)
- && !recipients.contains(number)) {
- // Only add numbers which aren't my own number.
- recipients.add(number);
- break;
- }
- }
- } else if (!recipients.contains(number)){
- recipients.add(number);
- }
- }
- }
- }
-
- /**
- * Move a PDU object from one location to another.
- *
- * @param from Specify the PDU object to be moved.
- * @param to The destination location, should be one of the following:
- * "content://mms/inbox", "content://mms/sent",
- * "content://mms/drafts", "content://mms/outbox",
- * "content://mms/trash".
- * @return New Uri of the moved PDU.
- * @throws MmsException Error occurred while moving the message.
- */
- public Uri move(Uri from, Uri to) throws MmsException {
- // Check whether the 'msgId' has been assigned a valid value.
- long msgId = ContentUris.parseId(from);
- if (msgId == -1L) {
- throw new MmsException("Error! ID of the message: -1.");
- }
-
- // Get corresponding int value of destination box.
- Integer msgBox = MESSAGE_BOX_MAP.get(to);
- if (msgBox == null) {
- throw new MmsException(
- "Bad destination, must be one of "
- + "content://mms/inbox, content://mms/sent, "
- + "content://mms/drafts, content://mms/outbox, "
- + "content://mms/temp.");
- }
-
- ContentValues values = new ContentValues(1);
- values.put(Mms.MESSAGE_BOX, msgBox);
- SqliteWrapper.update(mContext, mContentResolver, from, values, null, null);
- return ContentUris.withAppendedId(to, msgId);
- }
-
- /**
- * Wrap a byte[] into a String.
- */
- public static String toIsoString(byte[] bytes) {
- try {
- return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
- } catch (UnsupportedEncodingException e) {
- // Impossible to reach here!
- Log.e(TAG, "ISO_8859_1 must be supported!", e);
- return "";
- }
- }
-
- /**
- * Unpack a given String into a byte[].
- */
- public static byte[] getBytes(String data) {
- try {
- return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
- } catch (UnsupportedEncodingException e) {
- // Impossible to reach here!
- Log.e(TAG, "ISO_8859_1 must be supported!", e);
- return new byte[0];
- }
- }
-
- /**
- * Remove all objects in the temporary path.
- */
- public void release() {
- Uri uri = Uri.parse(TEMPORARY_DRM_OBJECT_URI);
- SqliteWrapper.delete(mContext, mContentResolver, uri, null, null);
- }
-
- /**
- * Find all messages to be sent or downloaded before certain time.
- */
- public Cursor getPendingMessages(long dueTime) {
- Uri.Builder uriBuilder = PendingMessages.CONTENT_URI.buildUpon();
- uriBuilder.appendQueryParameter("protocol", "mms");
-
- String selection = PendingMessages.ERROR_TYPE + " < ?"
- + " AND " + PendingMessages.DUE_TIME + " <= ?";
-
- String[] selectionArgs = new String[] {
- String.valueOf(MmsSms.ERR_TYPE_GENERIC_PERMANENT),
- String.valueOf(dueTime)
- };
-
- return SqliteWrapper.query(mContext, mContentResolver,
- uriBuilder.build(), null, selection, selectionArgs,
- PendingMessages.DUE_TIME);
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/QuotedPrintable.java b/src/java/com/google/android/mms/pdu/QuotedPrintable.java
deleted file mode 100644
index a34ed12638..0000000000
--- a/src/java/com/google/android/mms/pdu/QuotedPrintable.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import java.io.ByteArrayOutputStream;
-
-public class QuotedPrintable {
- private static byte ESCAPE_CHAR = '=';
-
- /**
- * Decodes an array quoted-printable characters into an array of original bytes.
- * Escaped characters are converted back to their original representation.
- *
- * <p>
- * This function implements a subset of
- * quoted-printable encoding specification (rule #1 and rule #2)
- * as defined in RFC 1521.
- * </p>
- *
- * @param bytes array of quoted-printable characters
- * @return array of original bytes,
- * null if quoted-printable decoding is unsuccessful.
- */
- public static final byte[] decodeQuotedPrintable(byte[] bytes) {
- if (bytes == null) {
- return null;
- }
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- for (int i = 0; i < bytes.length; i++) {
- int b = bytes[i];
- if (b == ESCAPE_CHAR) {
- try {
- if('\r' == (char)bytes[i + 1] &&
- '\n' == (char)bytes[i + 2]) {
- i += 2;
- continue;
- }
- int u = Character.digit((char) bytes[++i], 16);
- int l = Character.digit((char) bytes[++i], 16);
- if (u == -1 || l == -1) {
- return null;
- }
- buffer.write((char) ((u << 4) + l));
- } catch (ArrayIndexOutOfBoundsException e) {
- return null;
- }
- } else {
- buffer.write(b);
- }
- }
- return buffer.toByteArray();
- }
-}
diff --git a/src/java/com/google/android/mms/pdu/ReadOrigInd.java b/src/java/com/google/android/mms/pdu/ReadOrigInd.java
deleted file mode 100644
index 1bfc0bb3e2..0000000000
--- a/src/java/com/google/android/mms/pdu/ReadOrigInd.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-public class ReadOrigInd extends GenericPdu {
- /**
- * Empty constructor.
- * Since the Pdu corresponding to this class is constructed
- * by the Proxy-Relay server, this class is only instantiated
- * by the Pdu Parser.
- *
- * @throws InvalidHeaderValueException if error occurs.
- */
- public ReadOrigInd() throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_READ_ORIG_IND);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- ReadOrigInd(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get Date value.
- *
- * @return the value
- */
- public long getDate() {
- return mPduHeaders.getLongInteger(PduHeaders.DATE);
- }
-
- /**
- * Set Date value.
- *
- * @param value the value
- */
- public void setDate(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.DATE);
- }
-
- /**
- * Get From value.
- * From-value = Value-length
- * (Address-present-token Encoded-string-value | Insert-address-token)
- *
- * @return the value
- */
- public EncodedStringValue getFrom() {
- return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
- }
-
- /**
- * Set From value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setFrom(EncodedStringValue value) {
- mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
- }
-
- /**
- * Get Message-ID value.
- *
- * @return the value
- */
- public byte[] getMessageId() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Set Message-ID value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setMessageId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Get X-MMS-Read-status value.
- *
- * @return the value
- */
- public int getReadStatus() {
- return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
- }
-
- /**
- * Set X-MMS-Read-status value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setReadStatus(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
- }
-
- /**
- * Get To value.
- *
- * @return the value
- */
- public EncodedStringValue[] getTo() {
- return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
- }
-
- /**
- * Set To value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTo(EncodedStringValue[] value) {
- mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
- }
-
- /*
- * Optional, not supported header fields:
- *
- * public byte[] getApplicId() {return null;}
- * public void setApplicId(byte[] value) {}
- *
- * public byte[] getAuxApplicId() {return null;}
- * public void getAuxApplicId(byte[] value) {}
- *
- * public byte[] getReplyApplicId() {return 0x00;}
- * public void setReplyApplicId(byte[] value) {}
- */
-}
diff --git a/src/java/com/google/android/mms/pdu/ReadRecInd.java b/src/java/com/google/android/mms/pdu/ReadRecInd.java
deleted file mode 100644
index 880e3ac3dd..0000000000
--- a/src/java/com/google/android/mms/pdu/ReadRecInd.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-public class ReadRecInd extends GenericPdu {
- /**
- * Constructor, used when composing a M-ReadRec.ind pdu.
- *
- * @param from the from value
- * @param messageId the message ID value
- * @param mmsVersion current viersion of mms
- * @param readStatus the read status value
- * @param to the to value
- * @throws InvalidHeaderValueException if parameters are invalid.
- * NullPointerException if messageId or to is null.
- */
- public ReadRecInd(EncodedStringValue from,
- byte[] messageId,
- int mmsVersion,
- int readStatus,
- EncodedStringValue[] to) throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
- setFrom(from);
- setMessageId(messageId);
- setMmsVersion(mmsVersion);
- setTo(to);
- setReadStatus(readStatus);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- ReadRecInd(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get Date value.
- *
- * @return the value
- */
- public long getDate() {
- return mPduHeaders.getLongInteger(PduHeaders.DATE);
- }
-
- /**
- * Set Date value.
- *
- * @param value the value
- */
- public void setDate(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.DATE);
- }
-
- /**
- * Get Message-ID value.
- *
- * @return the value
- */
- public byte[] getMessageId() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Set Message-ID value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setMessageId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Get To value.
- *
- * @return the value
- */
- public EncodedStringValue[] getTo() {
- return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
- }
-
- /**
- * Set To value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTo(EncodedStringValue[] value) {
- mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
- }
-
- /**
- * Get X-MMS-Read-status value.
- *
- * @return the value
- */
- public int getReadStatus() {
- return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
- }
-
- /**
- * Set X-MMS-Read-status value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setReadStatus(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
- }
-
- /*
- * Optional, not supported header fields:
- *
- * public byte[] getApplicId() {return null;}
- * public void setApplicId(byte[] value) {}
- *
- * public byte[] getAuxApplicId() {return null;}
- * public void getAuxApplicId(byte[] value) {}
- *
- * public byte[] getReplyApplicId() {return 0x00;}
- * public void setReplyApplicId(byte[] value) {}
- */
-}
diff --git a/src/java/com/google/android/mms/pdu/RetrieveConf.java b/src/java/com/google/android/mms/pdu/RetrieveConf.java
deleted file mode 100644
index 98e67c068d..0000000000
--- a/src/java/com/google/android/mms/pdu/RetrieveConf.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-/**
- * M-Retrive.conf Pdu.
- */
-public class RetrieveConf extends MultimediaMessagePdu {
- /**
- * Empty constructor.
- * Since the Pdu corresponding to this class is constructed
- * by the Proxy-Relay server, this class is only instantiated
- * by the Pdu Parser.
- *
- * @throws InvalidHeaderValueException if error occurs.
- */
- public RetrieveConf() throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- RetrieveConf(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Constructor with given headers and body
- *
- * @param headers Headers for this PDU.
- * @param body Body of this PDu.
- */
- RetrieveConf(PduHeaders headers, PduBody body) {
- super(headers, body);
- }
-
- /**
- * Get CC value.
- *
- * @return the value
- */
- public EncodedStringValue[] getCc() {
- return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
- }
-
- /**
- * Add a "CC" value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void addCc(EncodedStringValue value) {
- mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
- }
-
- /**
- * Get Content-type value.
- *
- * @return the value
- */
- public byte[] getContentType() {
- return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
- }
-
- /**
- * Set Content-type value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setContentType(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
- }
-
- /**
- * Get X-Mms-Delivery-Report value.
- *
- * @return the value
- */
- public int getDeliveryReport() {
- return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
- }
-
- /**
- * Set X-Mms-Delivery-Report value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setDeliveryReport(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
- }
-
- /**
- * Get From value.
- * From-value = Value-length
- * (Address-present-token Encoded-string-value | Insert-address-token)
- *
- * @return the value
- */
- public EncodedStringValue getFrom() {
- return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
- }
-
- /**
- * Set From value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setFrom(EncodedStringValue value) {
- mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
- }
-
- /**
- * Get X-Mms-Message-Class value.
- * Message-class-value = Class-identifier | Token-text
- * Class-identifier = Personal | Advertisement | Informational | Auto
- *
- * @return the value
- */
- public byte[] getMessageClass() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
- }
-
- /**
- * Set X-Mms-Message-Class value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setMessageClass(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
- }
-
- /**
- * Get Message-ID value.
- *
- * @return the value
- */
- public byte[] getMessageId() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Set Message-ID value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setMessageId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Get X-Mms-Read-Report value.
- *
- * @return the value
- */
- public int getReadReport() {
- return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
- }
-
- /**
- * Set X-Mms-Read-Report value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setReadReport(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
- }
-
- /**
- * Get X-Mms-Retrieve-Status value.
- *
- * @return the value
- */
- public int getRetrieveStatus() {
- return mPduHeaders.getOctet(PduHeaders.RETRIEVE_STATUS);
- }
-
- /**
- * Set X-Mms-Retrieve-Status value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setRetrieveStatus(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.RETRIEVE_STATUS);
- }
-
- /**
- * Get X-Mms-Retrieve-Text value.
- *
- * @return the value
- */
- public EncodedStringValue getRetrieveText() {
- return mPduHeaders.getEncodedStringValue(PduHeaders.RETRIEVE_TEXT);
- }
-
- /**
- * Set X-Mms-Retrieve-Text value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setRetrieveText(EncodedStringValue value) {
- mPduHeaders.setEncodedStringValue(value, PduHeaders.RETRIEVE_TEXT);
- }
-
- /**
- * Get X-Mms-Transaction-Id.
- *
- * @return the value
- */
- public byte[] getTransactionId() {
- return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
- }
-
- /**
- * Set X-Mms-Transaction-Id.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTransactionId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
- }
-
- /*
- * Optional, not supported header fields:
- *
- * public byte[] getApplicId() {return null;}
- * public void setApplicId(byte[] value) {}
- *
- * public byte[] getAuxApplicId() {return null;}
- * public void getAuxApplicId(byte[] value) {}
- *
- * public byte getContentClass() {return 0x00;}
- * public void setApplicId(byte value) {}
- *
- * public byte getDrmContent() {return 0x00;}
- * public void setDrmContent(byte value) {}
- *
- * public byte getDistributionIndicator() {return 0x00;}
- * public void setDistributionIndicator(byte value) {}
- *
- * public PreviouslySentByValue getPreviouslySentBy() {return null;}
- * public void setPreviouslySentBy(PreviouslySentByValue value) {}
- *
- * public PreviouslySentDateValue getPreviouslySentDate() {}
- * public void setPreviouslySentDate(PreviouslySentDateValue value) {}
- *
- * public MmFlagsValue getMmFlags() {return null;}
- * public void setMmFlags(MmFlagsValue value) {}
- *
- * public MmStateValue getMmState() {return null;}
- * public void getMmState(MmStateValue value) {}
- *
- * public byte[] getReplaceId() {return 0x00;}
- * public void setReplaceId(byte[] value) {}
- *
- * public byte[] getReplyApplicId() {return 0x00;}
- * public void setReplyApplicId(byte[] value) {}
- *
- * public byte getReplyCharging() {return 0x00;}
- * public void setReplyCharging(byte value) {}
- *
- * public byte getReplyChargingDeadline() {return 0x00;}
- * public void setReplyChargingDeadline(byte value) {}
- *
- * public byte[] getReplyChargingId() {return 0x00;}
- * public void setReplyChargingId(byte[] value) {}
- *
- * public long getReplyChargingSize() {return 0;}
- * public void setReplyChargingSize(long value) {}
- */
-}
diff --git a/src/java/com/google/android/mms/pdu/SendConf.java b/src/java/com/google/android/mms/pdu/SendConf.java
deleted file mode 100644
index 0568fe79c5..0000000000
--- a/src/java/com/google/android/mms/pdu/SendConf.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2007 Esmertec AG.
- * Copyright (C) 2007 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.google.android.mms.pdu;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-public class SendConf extends GenericPdu {
- /**
- * Empty constructor.
- * Since the Pdu corresponding to this class is constructed
- * by the Proxy-Relay server, this class is only instantiated
- * by the Pdu Parser.
- *
- * @throws InvalidHeaderValueException if error occurs.
- */
- public SendConf() throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_SEND_CONF);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- SendConf(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Get Message-ID value.
- *
- * @return the value
- */
- public byte[] getMessageId() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Set Message-ID value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setMessageId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
- }
-
- /**
- * Get X-Mms-Response-Status.
- *
- * @return the value
- */
- public int getResponseStatus() {
- return mPduHeaders.getOctet(PduHeaders.RESPONSE_STATUS);
- }
-
- /**
- * Set X-Mms-Response-Status.
- *
- * @param value the values
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setResponseStatus(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.RESPONSE_STATUS);
- }
-
- /**
- * Get X-Mms-Transaction-Id field value.
- *
- * @return the X-Mms-Report-Allowed value
- */
- public byte[] getTransactionId() {
- return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
- }
-
- /**
- * Set X-Mms-Transaction-Id field value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTransactionId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
- }
-
- /*
- * Optional, not supported header fields:
- *
- * public byte[] getContentLocation() {return null;}
- * public void setContentLocation(byte[] value) {}
- *
- * public EncodedStringValue getResponseText() {return null;}
- * public void setResponseText(EncodedStringValue value) {}
- *
- * public byte getStoreStatus() {return 0x00;}
- * public void setStoreStatus(byte value) {}
- *
- * public byte[] getStoreStatusText() {return null;}
- * public void setStoreStatusText(byte[] value) {}
- */
-}
diff --git a/src/java/com/google/android/mms/pdu/SendReq.java b/src/java/com/google/android/mms/pdu/SendReq.java
deleted file mode 100644
index 597cd00b99..0000000000
--- a/src/java/com/google/android/mms/pdu/SendReq.java
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2007-2008 Esmertec AG.
- * Copyright (C) 2007-2008 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.google.android.mms.pdu;
-
-import android.util.Log;
-
-import com.google.android.mms.InvalidHeaderValueException;
-
-public class SendReq extends MultimediaMessagePdu {
- private static final String TAG = "SendReq";
-
- public SendReq() {
- super();
-
- try {
- setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
- setMmsVersion(PduHeaders.CURRENT_MMS_VERSION);
- // FIXME: Content-type must be decided according to whether
- // SMIL part present.
- setContentType("application/vnd.wap.multipart.related".getBytes());
- setFrom(new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()));
- setTransactionId(generateTransactionId());
- } catch (InvalidHeaderValueException e) {
- // Impossible to reach here since all headers we set above are valid.
- Log.e(TAG, "Unexpected InvalidHeaderValueException.", e);
- throw new RuntimeException(e);
- }
- }
-
- private byte[] generateTransactionId() {
- String transactionId = "T" + Long.toHexString(System.currentTimeMillis());
- return transactionId.getBytes();
- }
-
- /**
- * Constructor, used when composing a M-Send.req pdu.
- *
- * @param contentType the content type value
- * @param from the from value
- * @param mmsVersion current viersion of mms
- * @param transactionId the transaction-id value
- * @throws InvalidHeaderValueException if parameters are invalid.
- * NullPointerException if contentType, form or transactionId is null.
- */
- public SendReq(byte[] contentType,
- EncodedStringValue from,
- int mmsVersion,
- byte[] transactionId) throws InvalidHeaderValueException {
- super();
- setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
- setContentType(contentType);
- setFrom(from);
- setMmsVersion(mmsVersion);
- setTransactionId(transactionId);
- }
-
- /**
- * Constructor with given headers.
- *
- * @param headers Headers for this PDU.
- */
- SendReq(PduHeaders headers) {
- super(headers);
- }
-
- /**
- * Constructor with given headers and body
- *
- * @param headers Headers for this PDU.
- * @param body Body of this PDu.
- */
- SendReq(PduHeaders headers, PduBody body) {
- super(headers, body);
- }
-
- /**
- * Get Bcc value.
- *
- * @return the value
- */
- public EncodedStringValue[] getBcc() {
- return mPduHeaders.getEncodedStringValues(PduHeaders.BCC);
- }
-
- /**
- * Add a "BCC" value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void addBcc(EncodedStringValue value) {
- mPduHeaders.appendEncodedStringValue(value, PduHeaders.BCC);
- }
-
- /**
- * Set "BCC" value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setBcc(EncodedStringValue[] value) {
- mPduHeaders.setEncodedStringValues(value, PduHeaders.BCC);
- }
-
- /**
- * Get CC value.
- *
- * @return the value
- */
- public EncodedStringValue[] getCc() {
- return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
- }
-
- /**
- * Add a "CC" value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void addCc(EncodedStringValue value) {
- mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
- }
-
- /**
- * Set "CC" value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setCc(EncodedStringValue[] value) {
- mPduHeaders.setEncodedStringValues(value, PduHeaders.CC);
- }
-
- /**
- * Get Content-type value.
- *
- * @return the value
- */
- public byte[] getContentType() {
- return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
- }
-
- /**
- * Set Content-type value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setContentType(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
- }
-
- /**
- * Get X-Mms-Delivery-Report value.
- *
- * @return the value
- */
- public int getDeliveryReport() {
- return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
- }
-
- /**
- * Set X-Mms-Delivery-Report value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setDeliveryReport(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
- }
-
- /**
- * Get X-Mms-Expiry value.
- *
- * Expiry-value = Value-length
- * (Absolute-token Date-value | Relative-token Delta-seconds-value)
- *
- * @return the value
- */
- public long getExpiry() {
- return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
- }
-
- /**
- * Set X-Mms-Expiry value.
- *
- * @param value the value
- */
- public void setExpiry(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
- }
-
- /**
- * Get X-Mms-MessageSize value.
- *
- * Expiry-value = size of message
- *
- * @return the value
- */
- public long getMessageSize() {
- return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
- }
-
- /**
- * Set X-Mms-MessageSize value.
- *
- * @param value the value
- */
- public void setMessageSize(long value) {
- mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
- }
-
- /**
- * Get X-Mms-Message-Class value.
- * Message-class-value = Class-identifier | Token-text
- * Class-identifier = Personal | Advertisement | Informational | Auto
- *
- * @return the value
- */
- public byte[] getMessageClass() {
- return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
- }
-
- /**
- * Set X-Mms-Message-Class value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setMessageClass(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
- }
-
- /**
- * Get X-Mms-Read-Report value.
- *
- * @return the value
- */
- public int getReadReport() {
- return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
- }
-
- /**
- * Set X-Mms-Read-Report value.
- *
- * @param value the value
- * @throws InvalidHeaderValueException if the value is invalid.
- */
- public void setReadReport(int value) throws InvalidHeaderValueException {
- mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
- }
-
- /**
- * Set "To" value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTo(EncodedStringValue[] value) {
- mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
- }
-
- /**
- * Get X-Mms-Transaction-Id field value.
- *
- * @return the X-Mms-Report-Allowed value
- */
- public byte[] getTransactionId() {
- return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
- }
-
- /**
- * Set X-Mms-Transaction-Id field value.
- *
- * @param value the value
- * @throws NullPointerException if the value is null.
- */
- public void setTransactionId(byte[] value) {
- mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
- }
-
- /*
- * Optional, not supported header fields:
- *
- * public byte getAdaptationAllowed() {return 0};
- * public void setAdaptationAllowed(btye value) {};
- *
- * public byte[] getApplicId() {return null;}
- * public void setApplicId(byte[] value) {}
- *
- * public byte[] getAuxApplicId() {return null;}
- * public void getAuxApplicId(byte[] value) {}
- *
- * public byte getContentClass() {return 0x00;}
- * public void setApplicId(byte value) {}
- *
- * public long getDeliveryTime() {return 0};
- * public void setDeliveryTime(long value) {};
- *
- * public byte getDrmContent() {return 0x00;}
- * public void setDrmContent(byte value) {}
- *
- * public MmFlagsValue getMmFlags() {return null;}
- * public void setMmFlags(MmFlagsValue value) {}
- *
- * public MmStateValue getMmState() {return null;}
- * public void getMmState(MmStateValue value) {}
- *
- * public byte[] getReplyApplicId() {return 0x00;}
- * public void setReplyApplicId(byte[] value) {}
- *
- * public byte getReplyCharging() {return 0x00;}
- * public void setReplyCharging(byte value) {}
- *
- * public byte getReplyChargingDeadline() {return 0x00;}
- * public void setReplyChargingDeadline(byte value) {}
- *
- * public byte[] getReplyChargingId() {return 0x00;}
- * public void setReplyChargingId(byte[] value) {}
- *
- * public long getReplyChargingSize() {return 0;}
- * public void setReplyChargingSize(long value) {}
- *
- * public byte[] getReplyApplicId() {return 0x00;}
- * public void setReplyApplicId(byte[] value) {}
- *
- * public byte getStore() {return 0x00;}
- * public void setStore(byte value) {}
- */
-}
diff --git a/src/java/com/google/android/mms/pdu/package.html b/src/java/com/google/android/mms/pdu/package.html
deleted file mode 100755
index c9f96a66ab..0000000000
--- a/src/java/com/google/android/mms/pdu/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/src/java/com/google/android/mms/util/AbstractCache.java b/src/java/com/google/android/mms/util/AbstractCache.java
deleted file mode 100644
index 39b2abf15a..0000000000
--- a/src/java/com/google/android/mms/util/AbstractCache.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2008 Esmertec AG.
- * Copyright (C) 2008 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.google.android.mms.util;
-
-import android.util.Log;
-
-import java.util.HashMap;
-
-public abstract class AbstractCache<K, V> {
- private static final String TAG = "AbstractCache";
- private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = false;
-
- private static final int MAX_CACHED_ITEMS = 500;
-
- private final HashMap<K, CacheEntry<V>> mCacheMap;
-
- protected AbstractCache() {
- mCacheMap = new HashMap<K, CacheEntry<V>>();
- }
-
- public boolean put(K key, V value) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Trying to put " + key + " into cache.");
- }
-
- if (mCacheMap.size() >= MAX_CACHED_ITEMS) {
- // TODO Should remove the oldest or least hit cached entry
- // and then cache the new one.
- if (LOCAL_LOGV) {
- Log.v(TAG, "Failed! size limitation reached.");
- }
- return false;
- }
-
- if (key != null) {
- CacheEntry<V> cacheEntry = new CacheEntry<V>();
- cacheEntry.value = value;
- mCacheMap.put(key, cacheEntry);
-
- if (LOCAL_LOGV) {
- Log.v(TAG, key + " cached, " + mCacheMap.size() + " items total.");
- }
- return true;
- }
- return false;
- }
-
- public V get(K key) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Trying to get " + key + " from cache.");
- }
-
- if (key != null) {
- CacheEntry<V> cacheEntry = mCacheMap.get(key);
- if (cacheEntry != null) {
- cacheEntry.hit++;
- if (LOCAL_LOGV) {
- Log.v(TAG, key + " hit " + cacheEntry.hit + " times.");
- }
- return cacheEntry.value;
- }
- }
- return null;
- }
-
- public V purge(K key) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Trying to purge " + key);
- }
-
- CacheEntry<V> v = mCacheMap.remove(key);
-
- if (LOCAL_LOGV) {
- Log.v(TAG, mCacheMap.size() + " items cached.");
- }
-
- return v != null ? v.value : null;
- }
-
- public void purgeAll() {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Purging cache, " + mCacheMap.size()
- + " items dropped.");
- }
- mCacheMap.clear();
- }
-
- public int size() {
- return mCacheMap.size();
- }
-
- private static class CacheEntry<V> {
- int hit;
- V value;
- }
-}
diff --git a/src/java/com/google/android/mms/util/DownloadDrmHelper.java b/src/java/com/google/android/mms/util/DownloadDrmHelper.java
deleted file mode 100644
index 6852eca489..0000000000
--- a/src/java/com/google/android/mms/util/DownloadDrmHelper.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2012 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.google.android.mms.util;
-
-import android.content.Context;
-import android.drm.DrmManagerClient;
-import android.util.Log;
-
-public class DownloadDrmHelper {
- private static final String TAG = "DownloadDrmHelper";
-
- /** The MIME type of special DRM files */
- public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
-
- /** The extensions of special DRM files */
- public static final String EXTENSION_DRM_MESSAGE = ".dm";
-
- public static final String EXTENSION_INTERNAL_FWDL = ".fl";
-
- /**
- * Checks if the Media Type is a DRM Media Type
- *
- * @param drmManagerClient A DrmManagerClient
- * @param mimetype Media Type to check
- * @return True if the Media Type is DRM else false
- */
- public static boolean isDrmMimeType(Context context, String mimetype) {
- boolean result = false;
- if (context != null) {
- try {
- DrmManagerClient drmClient = new DrmManagerClient(context);
- if (drmClient != null && mimetype != null && mimetype.length() > 0) {
- result = drmClient.canHandle("", mimetype);
- }
- } catch (IllegalArgumentException e) {
- Log.w(TAG,
- "DrmManagerClient instance could not be created, context is Illegal.");
- } catch (IllegalStateException e) {
- Log.w(TAG, "DrmManagerClient didn't initialize properly.");
- }
- }
- return result;
- }
-
- /**
- * Checks if the Media Type needs to be DRM converted
- *
- * @param mimetype Media type of the content
- * @return True if convert is needed else false
- */
- public static boolean isDrmConvertNeeded(String mimetype) {
- return MIMETYPE_DRM_MESSAGE.equals(mimetype);
- }
-
- /**
- * Modifies the file extension for a DRM Forward Lock file NOTE: This
- * function shouldn't be called if the file shouldn't be DRM converted
- */
- public static String modifyDrmFwLockFileExtension(String filename) {
- if (filename != null) {
- int extensionIndex;
- extensionIndex = filename.lastIndexOf(".");
- if (extensionIndex != -1) {
- filename = filename.substring(0, extensionIndex);
- }
- filename = filename.concat(EXTENSION_INTERNAL_FWDL);
- }
- return filename;
- }
-
- /**
- * Gets the original mime type of DRM protected content.
- *
- * @param context The context
- * @param path Path to the file
- * @param containingMime The current mime type of of the file i.e. the
- * containing mime type
- * @return The original mime type of the file if DRM protected else the
- * currentMime
- */
- public static String getOriginalMimeType(Context context, String path, String containingMime) {
- String result = containingMime;
- DrmManagerClient drmClient = new DrmManagerClient(context);
- try {
- if (drmClient.canHandle(path, null)) {
- result = drmClient.getOriginalMimeType(path);
- }
- } catch (IllegalArgumentException ex) {
- Log.w(TAG,
- "Can't get original mime type since path is null or empty string.");
- } catch (IllegalStateException ex) {
- Log.w(TAG, "DrmManagerClient didn't initialize properly.");
- }
- return result;
- }
-}
diff --git a/src/java/com/google/android/mms/util/DrmConvertSession.java b/src/java/com/google/android/mms/util/DrmConvertSession.java
deleted file mode 100644
index 2d8f274f2b..0000000000
--- a/src/java/com/google/android/mms/util/DrmConvertSession.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2012 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.google.android.mms.util;
-
-import android.content.Context;
-import android.drm.DrmConvertedStatus;
-import android.drm.DrmManagerClient;
-import android.util.Log;
-import android.provider.Downloads;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-
-
-public class DrmConvertSession {
- private DrmManagerClient mDrmClient;
- private int mConvertSessionId;
- private static final String TAG = "DrmConvertSession";
-
- private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
- mDrmClient = drmClient;
- mConvertSessionId = convertSessionId;
- }
-
- /**
- * Start of converting a file.
- *
- * @param context The context of the application running the convert session.
- * @param mimeType Mimetype of content that shall be converted.
- * @return A convert session or null in case an error occurs.
- */
- public static DrmConvertSession open(Context context, String mimeType) {
- DrmManagerClient drmClient = null;
- int convertSessionId = -1;
- if (context != null && mimeType != null && !mimeType.equals("")) {
- try {
- drmClient = new DrmManagerClient(context);
- try {
- convertSessionId = drmClient.openConvertSession(mimeType);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "Conversion of Mimetype: " + mimeType
- + " is not supported.", e);
- } catch (IllegalStateException e) {
- Log.w(TAG, "Could not access Open DrmFramework.", e);
- }
- } catch (IllegalArgumentException e) {
- Log.w(TAG,
- "DrmManagerClient instance could not be created, context is Illegal.");
- } catch (IllegalStateException e) {
- Log.w(TAG, "DrmManagerClient didn't initialize properly.");
- }
- }
-
- if (drmClient == null || convertSessionId < 0) {
- return null;
- } else {
- return new DrmConvertSession(drmClient, convertSessionId);
- }
- }
- /**
- * Convert a buffer of data to protected format.
- *
- * @param buffer Buffer filled with data to convert.
- * @param size The number of bytes that shall be converted.
- * @return A Buffer filled with converted data, if execution is ok, in all
- * other case null.
- */
- public byte [] convert(byte[] inBuffer, int size) {
- byte[] result = null;
- if (inBuffer != null) {
- DrmConvertedStatus convertedStatus = null;
- try {
- if (size != inBuffer.length) {
- byte[] buf = new byte[size];
- System.arraycopy(inBuffer, 0, buf, 0, size);
- convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
- } else {
- convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
- }
-
- if (convertedStatus != null &&
- convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
- convertedStatus.convertedData != null) {
- result = convertedStatus.convertedData;
- }
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
- + mConvertSessionId, e);
- } catch (IllegalStateException e) {
- Log.w(TAG, "Could not convert data. Convertsession: " +
- mConvertSessionId, e);
- }
- } else {
- throw new IllegalArgumentException("Parameter inBuffer is null");
- }
- return result;
- }
-
- /**
- * Ends a conversion session of a file.
- *
- * @param fileName The filename of the converted file.
- * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
- * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
- * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
- * occurs when accessing drm framework.
- * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
- */
- public int close(String filename) {
- DrmConvertedStatus convertedStatus = null;
- int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
- if (mDrmClient != null && mConvertSessionId >= 0) {
- try {
- convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
- if (convertedStatus == null ||
- convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
- convertedStatus.convertedData == null) {
- result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
- } else {
- RandomAccessFile rndAccessFile = null;
- try {
- rndAccessFile = new RandomAccessFile(filename, "rw");
- rndAccessFile.seek(convertedStatus.offset);
- rndAccessFile.write(convertedStatus.convertedData);
- result = Downloads.Impl.STATUS_SUCCESS;
- } catch (FileNotFoundException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
- Log.w(TAG, "File: " + filename + " could not be found.", e);
- } catch (IOException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
- Log.w(TAG, "Could not access File: " + filename + " .", e);
- } catch (IllegalArgumentException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
- Log.w(TAG, "Could not open file in mode: rw", e);
- } catch (SecurityException e) {
- Log.w(TAG, "Access to File: " + filename +
- " was denied denied by SecurityManager.", e);
- } finally {
- if (rndAccessFile != null) {
- try {
- rndAccessFile.close();
- } catch (IOException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
- Log.w(TAG, "Failed to close File:" + filename
- + ".", e);
- }
- }
- }
- }
- } catch (IllegalStateException e) {
- Log.w(TAG, "Could not close convertsession. Convertsession: " +
- mConvertSessionId, e);
- }
- }
- return result;
- }
-}
diff --git a/src/java/com/google/android/mms/util/PduCache.java b/src/java/com/google/android/mms/util/PduCache.java
deleted file mode 100644
index de83124bad..0000000000
--- a/src/java/com/google/android/mms/util/PduCache.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2008 Esmertec AG.
- * Copyright (C) 2008 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.google.android.mms.util;
-
-import android.content.ContentUris;
-import android.content.UriMatcher;
-import android.net.Uri;
-import android.provider.Telephony.Mms;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.HashSet;
-
-public final class PduCache extends AbstractCache<Uri, PduCacheEntry> {
- private static final String TAG = "PduCache";
- private static final boolean DEBUG = false;
- private static final boolean LOCAL_LOGV = false;
-
- private static final int MMS_ALL = 0;
- private static final int MMS_ALL_ID = 1;
- private static final int MMS_INBOX = 2;
- private static final int MMS_INBOX_ID = 3;
- private static final int MMS_SENT = 4;
- private static final int MMS_SENT_ID = 5;
- private static final int MMS_DRAFTS = 6;
- private static final int MMS_DRAFTS_ID = 7;
- private static final int MMS_OUTBOX = 8;
- private static final int MMS_OUTBOX_ID = 9;
- private static final int MMS_CONVERSATION = 10;
- private static final int MMS_CONVERSATION_ID = 11;
-
- private static final UriMatcher URI_MATCHER;
- private static final HashMap<Integer, Integer> MATCH_TO_MSGBOX_ID_MAP;
-
- private static PduCache sInstance;
-
- static {
- URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
- URI_MATCHER.addURI("mms", null, MMS_ALL);
- URI_MATCHER.addURI("mms", "#", MMS_ALL_ID);
- URI_MATCHER.addURI("mms", "inbox", MMS_INBOX);
- URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID);
- URI_MATCHER.addURI("mms", "sent", MMS_SENT);
- URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID);
- URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS);
- URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID);
- URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX);
- URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID);
- URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION);
- URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID);
-
- MATCH_TO_MSGBOX_ID_MAP = new HashMap<Integer, Integer>();
- MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX);
- MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT);
- MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS);
- MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX);
- }
-
- private final HashMap<Integer, HashSet<Uri>> mMessageBoxes;
- private final HashMap<Long, HashSet<Uri>> mThreads;
- private final HashSet<Uri> mUpdating;
-
- private PduCache() {
- mMessageBoxes = new HashMap<Integer, HashSet<Uri>>();
- mThreads = new HashMap<Long, HashSet<Uri>>();
- mUpdating = new HashSet<Uri>();
- }
-
- synchronized public static final PduCache getInstance() {
- if (sInstance == null) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Constructing new PduCache instance.");
- }
- sInstance = new PduCache();
- }
- return sInstance;
- }
-
- @Override
- synchronized public boolean put(Uri uri, PduCacheEntry entry) {
- int msgBoxId = entry.getMessageBox();
- HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId);
- if (msgBox == null) {
- msgBox = new HashSet<Uri>();
- mMessageBoxes.put(msgBoxId, msgBox);
- }
-
- long threadId = entry.getThreadId();
- HashSet<Uri> thread = mThreads.get(threadId);
- if (thread == null) {
- thread = new HashSet<Uri>();
- mThreads.put(threadId, thread);
- }
-
- Uri finalKey = normalizeKey(uri);
- boolean result = super.put(finalKey, entry);
- if (result) {
- msgBox.add(finalKey);
- thread.add(finalKey);
- }
- setUpdating(uri, false);
- return result;
- }
-
- synchronized public void setUpdating(Uri uri, boolean updating) {
- if (updating) {
- mUpdating.add(uri);
- } else {
- mUpdating.remove(uri);
- }
- }
-
- synchronized public boolean isUpdating(Uri uri) {
- return mUpdating.contains(uri);
- }
-
- @Override
- synchronized public PduCacheEntry purge(Uri uri) {
- int match = URI_MATCHER.match(uri);
- switch (match) {
- case MMS_ALL_ID:
- return purgeSingleEntry(uri);
- case MMS_INBOX_ID:
- case MMS_SENT_ID:
- case MMS_DRAFTS_ID:
- case MMS_OUTBOX_ID:
- String msgId = uri.getLastPathSegment();
- return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId));
- // Implicit batch of purge, return null.
- case MMS_ALL:
- case MMS_CONVERSATION:
- purgeAll();
- return null;
- case MMS_INBOX:
- case MMS_SENT:
- case MMS_DRAFTS:
- case MMS_OUTBOX:
- purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match));
- return null;
- case MMS_CONVERSATION_ID:
- purgeByThreadId(ContentUris.parseId(uri));
- return null;
- default:
- return null;
- }
- }
-
- private PduCacheEntry purgeSingleEntry(Uri key) {
- mUpdating.remove(key);
- PduCacheEntry entry = super.purge(key);
- if (entry != null) {
- removeFromThreads(key, entry);
- removeFromMessageBoxes(key, entry);
- return entry;
- }
- return null;
- }
-
- @Override
- synchronized public void purgeAll() {
- super.purgeAll();
-
- mMessageBoxes.clear();
- mThreads.clear();
- mUpdating.clear();
- }
-
- /**
- * @param uri The Uri to be normalized.
- * @return Uri The normalized key of cached entry.
- */
- private Uri normalizeKey(Uri uri) {
- int match = URI_MATCHER.match(uri);
- Uri normalizedKey = null;
-
- switch (match) {
- case MMS_ALL_ID:
- normalizedKey = uri;
- break;
- case MMS_INBOX_ID:
- case MMS_SENT_ID:
- case MMS_DRAFTS_ID:
- case MMS_OUTBOX_ID:
- String msgId = uri.getLastPathSegment();
- normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId);
- break;
- default:
- return null;
- }
-
- if (LOCAL_LOGV) {
- Log.v(TAG, uri + " -> " + normalizedKey);
- }
- return normalizedKey;
- }
-
- private void purgeByMessageBox(Integer msgBoxId) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Purge cache in message box: " + msgBoxId);
- }
-
- if (msgBoxId != null) {
- HashSet<Uri> msgBox = mMessageBoxes.remove(msgBoxId);
- if (msgBox != null) {
- for (Uri key : msgBox) {
- mUpdating.remove(key);
- PduCacheEntry entry = super.purge(key);
- if (entry != null) {
- removeFromThreads(key, entry);
- }
- }
- }
- }
- }
-
- private void removeFromThreads(Uri key, PduCacheEntry entry) {
- HashSet<Uri> thread = mThreads.get(entry.getThreadId());
- if (thread != null) {
- thread.remove(key);
- }
- }
-
- private void purgeByThreadId(long threadId) {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Purge cache in thread: " + threadId);
- }
-
- HashSet<Uri> thread = mThreads.remove(threadId);
- if (thread != null) {
- for (Uri key : thread) {
- mUpdating.remove(key);
- PduCacheEntry entry = super.purge(key);
- if (entry != null) {
- removeFromMessageBoxes(key, entry);
- }
- }
- }
- }
-
- private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) {
- HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox()));
- if (msgBox != null) {
- msgBox.remove(key);
- }
- }
-}
diff --git a/src/java/com/google/android/mms/util/PduCacheEntry.java b/src/java/com/google/android/mms/util/PduCacheEntry.java
deleted file mode 100644
index 8b41386281..0000000000
--- a/src/java/com/google/android/mms/util/PduCacheEntry.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2008 Esmertec AG.
- * Copyright (C) 2008 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.google.android.mms.util;
-
-import com.google.android.mms.pdu.GenericPdu;
-
-public final class PduCacheEntry {
- private final GenericPdu mPdu;
- private final int mMessageBox;
- private final long mThreadId;
-
- public PduCacheEntry(GenericPdu pdu, int msgBox, long threadId) {
- mPdu = pdu;
- mMessageBox = msgBox;
- mThreadId = threadId;
- }
-
- public GenericPdu getPdu() {
- return mPdu;
- }
-
- public int getMessageBox() {
- return mMessageBox;
- }
-
- public long getThreadId() {
- return mThreadId;
- }
-}
diff --git a/src/java/com/google/android/mms/util/SqliteWrapper.java b/src/java/com/google/android/mms/util/SqliteWrapper.java
deleted file mode 100644
index bcdac22ce1..0000000000
--- a/src/java/com/google/android/mms/util/SqliteWrapper.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2008 Esmertec AG.
- * Copyright (C) 2008 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.google.android.mms.util;
-
-import android.app.ActivityManager;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
-import android.net.Uri;
-import android.util.Log;
-import android.widget.Toast;
-
-public final class SqliteWrapper {
- private static final String TAG = "SqliteWrapper";
- private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
- = "unable to open database file";
-
- private SqliteWrapper() {
- // Forbidden being instantiated.
- }
-
- // FIXME: It looks like outInfo.lowMemory does not work well as we expected.
- // after run command: adb shell fillup -p 100, outInfo.lowMemory is still false.
- private static boolean isLowMemory(Context context) {
- if (null == context) {
- return false;
- }
-
- ActivityManager am = (ActivityManager)
- context.getSystemService(Context.ACTIVITY_SERVICE);
- ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
- am.getMemoryInfo(outInfo);
-
- return outInfo.lowMemory;
- }
-
- // FIXME: need to optimize this method.
- private static boolean isLowMemory(SQLiteException e) {
- return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
- }
-
- public static void checkSQLiteException(Context context, SQLiteException e) {
- if (isLowMemory(e)) {
- Toast.makeText(context, com.android.internal.R.string.low_memory,
- Toast.LENGTH_SHORT).show();
- } else {
- throw e;
- }
- }
-
- public static Cursor query(Context context, ContentResolver resolver, Uri uri,
- String[] projection, String selection, String[] selectionArgs, String sortOrder) {
- try {
- return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
- } catch (SQLiteException e) {
- Log.e(TAG, "Catch a SQLiteException when query: ", e);
- checkSQLiteException(context, e);
- return null;
- }
- }
-
- public static boolean requery(Context context, Cursor cursor) {
- try {
- return cursor.requery();
- } catch (SQLiteException e) {
- Log.e(TAG, "Catch a SQLiteException when requery: ", e);
- checkSQLiteException(context, e);
- return false;
- }
- }
- public static int update(Context context, ContentResolver resolver, Uri uri,
- ContentValues values, String where, String[] selectionArgs) {
- try {
- return resolver.update(uri, values, where, selectionArgs);
- } catch (SQLiteException e) {
- Log.e(TAG, "Catch a SQLiteException when update: ", e);
- checkSQLiteException(context, e);
- return -1;
- }
- }
-
- public static int delete(Context context, ContentResolver resolver, Uri uri,
- String where, String[] selectionArgs) {
- try {
- return resolver.delete(uri, where, selectionArgs);
- } catch (SQLiteException e) {
- Log.e(TAG, "Catch a SQLiteException when delete: ", e);
- checkSQLiteException(context, e);
- return -1;
- }
- }
-
- public static Uri insert(Context context, ContentResolver resolver,
- Uri uri, ContentValues values) {
- try {
- return resolver.insert(uri, values);
- } catch (SQLiteException e) {
- Log.e(TAG, "Catch a SQLiteException when insert: ", e);
- checkSQLiteException(context, e);
- return null;
- }
- }
-}
diff --git a/src/java/com/google/android/mms/util/package.html b/src/java/com/google/android/mms/util/package.html
deleted file mode 100755
index c9f96a66ab..0000000000
--- a/src/java/com/google/android/mms/util/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
index fe823ff55b..5a7f688ff2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -17,6 +17,7 @@ package com.android.internal.telephony;
import static android.preference.PreferenceManager.getDefaultSharedPreferences;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -39,8 +40,6 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
-import com.android.org.bouncycastle.util.io.pem.PemReader;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -48,10 +47,6 @@ import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.MockitoAnnotations;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -148,12 +143,9 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
@Test
@SmallTest
public void testParseJson() {
- ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
- Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
- PemReader reader = new PemReader(fRd);
Pair<PublicKey, Long> keyInfo = null;
try {
- keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
+ keyInfo = mCarrierKeyDM.getKeyInformation(CERT.getBytes());
} catch (Exception e) {
fail(LOG_TAG + "exception creating public key");
}
@@ -172,12 +164,9 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
@Test
@SmallTest
public void testParseJsonPublicKey() {
- ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
- Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
- PemReader reader = new PemReader(fRd);
Pair<PublicKey, Long> keyInfo = null;
try {
- keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
+ keyInfo = mCarrierKeyDM.getKeyInformation(CERT.getBytes());
} catch (Exception e) {
fail(LOG_TAG + "exception creating public key");
}
@@ -324,12 +313,9 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
@Test
@SmallTest
public void testParseJson3GppFormat() {
- ByteArrayInputStream certBytes = new ByteArrayInputStream(CERT.getBytes());
- Reader fRd = new BufferedReader(new InputStreamReader(certBytes));
- PemReader reader = new PemReader(fRd);
Pair<PublicKey, Long> keyInfo = null;
try {
- keyInfo = mCarrierKeyDM.getKeyInformation(reader.readPemObject().getContent());
+ keyInfo = mCarrierKeyDM.getKeyInformation(CERT.getBytes());
} catch (Exception e) {
fail(LOG_TAG + "exception creating public key");
}
@@ -342,4 +328,13 @@ public class CarrierKeyDownloadMgrTest extends TelephonyTest {
(Matchers.refEq(imsiEncryptionInfo)));
}
+ /**
+ * Checks if certificate string cleaning is working correctly
+ */
+ @Test
+ @SmallTest
+ public void testCleanCertString() {
+ assertEquals(CarrierKeyDownloadManager
+ .cleanCertString("Comments before" + CERT + "Comments after"), CERT);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceBindHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceBindHelperTest.java
new file mode 100644
index 0000000000..1fad90fd11
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceBindHelperTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
+import static android.telephony.TelephonyManager.MODEM_COUNT_SINGLE_MODEM;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
+
+import android.test.suitebuilder.annotation.SmallTest;
+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;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CarrierServiceBindHelperTest extends TelephonyTest {
+ CarrierServiceBindHelper mCarrierServiceBindHelper;
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ doReturn(MODEM_COUNT_SINGLE_MODEM).when(mTelephonyManager).getActiveModemCount();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Restore system properties.
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void testMultiSimConfigChanged() throws Exception {
+ clearInvocations(mPhoneConfigurationManager);
+ mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
+ assertEquals(MODEM_COUNT_SINGLE_MODEM, mCarrierServiceBindHelper.mBindings.size());
+ assertEquals(MODEM_COUNT_SINGLE_MODEM, mCarrierServiceBindHelper.mLastSimState.size());
+ assertNotNull(mCarrierServiceBindHelper.mBindings.get(0));
+ assertNotNull(mCarrierServiceBindHelper.mLastSimState.get(0));
+
+ // Verify registration of EVENT_MULTI_SIM_CONFIG_CHANGED.
+ doReturn(MODEM_COUNT_DUAL_MODEM).when(mTelephonyManager).getActiveModemCount();
+ PhoneConfigurationManager.notifyMultiSimConfigChange(MODEM_COUNT_DUAL_MODEM);
+ processAllMessages();
+
+ assertEquals(MODEM_COUNT_DUAL_MODEM, mCarrierServiceBindHelper.mBindings.size());
+ assertEquals(MODEM_COUNT_DUAL_MODEM, mCarrierServiceBindHelper.mLastSimState.size());
+ assertNotNull(mCarrierServiceBindHelper.mBindings.get(0));
+ assertNotNull(mCarrierServiceBindHelper.mBindings.get(1));
+ assertNotNull(mCarrierServiceBindHelper.mLastSimState.get(0));
+ assertNotNull(mCarrierServiceBindHelper.mLastSimState.get(1));
+
+ // Switch back to single SIM.
+ doReturn(MODEM_COUNT_SINGLE_MODEM).when(mTelephonyManager).getActiveModemCount();
+ PhoneConfigurationManager.notifyMultiSimConfigChange(MODEM_COUNT_SINGLE_MODEM);
+ processAllMessages();
+
+ assertEquals(MODEM_COUNT_SINGLE_MODEM, mCarrierServiceBindHelper.mBindings.size());
+ assertEquals(MODEM_COUNT_SINGLE_MODEM, mCarrierServiceBindHelper.mLastSimState.size());
+ assertNotNull(mCarrierServiceBindHelper.mBindings.get(0));
+ assertNotNull(mCarrierServiceBindHelper.mLastSimState.get(0));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
index 074e9fc96a..d967bf6f7e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
@@ -34,6 +34,7 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
private static final int ANOTHER_CSIRSRP = -111;
private static final int ANOTHER_CSIRSRQ = -12;
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 SSRSRP = -112;
private static final int SSRSRQ = -13;
@@ -52,6 +53,7 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
assertThat(css.getSsRsrp()).isEqualTo(SSRSRP);
assertThat(css.getSsRsrq()).isEqualTo(SSRSRQ);
assertThat(css.getSsSinr()).isEqualTo(SSSINR);
+ assertThat(css.getDbm()).isEqualTo(SSRSRP);
}
@Test
@@ -93,7 +95,7 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
public void testAsuLevel_invalidValue() {
// GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
CellSignalStrengthNr css = new CellSignalStrengthNr(
- INVALID_CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+ CSIRSRP, CSIRSRQ, CSISINR, INVALID_SSRSRP, SSRSRQ, SSSINR);
// THEN the asu level is unknown
assertThat(css.getAsuLevel()).isEqualTo(CellSignalStrengthNr.UNKNOWN_ASU_LEVEL);
@@ -101,10 +103,10 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
@Test
public void testSignalLevel_validValue() {
- for (int csiRsrp = -140; csiRsrp <= -44; csiRsrp++) {
+ 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);
+ CSIRSRP, CSIRSRQ, CSISINR, ssRsrp, SSRSRQ, SSSINR);
// THEN the signal level is valid
assertThat(css.getLevel()).isAnyOf(
@@ -119,7 +121,7 @@ public class CellSignalStrengthNrTest extends AndroidTestCase {
public void testSignalLevel_invalidValue() {
// GIVEN an instance of CellSignalStrengthNr with invalid csirsrp
CellSignalStrengthNr css = new CellSignalStrengthNr(
- INVALID_CSIRSRP, CSIRSRQ, CSISINR, SSRSRP, SSRSRQ, SSSINR);
+ CSIRSRP, CSIRSRQ, CSISINR, INVALID_SSRSRP, SSRSRQ, SSSINR);
// THEN the signal level is unknown
assertThat(css.getLevel()).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 439176446e..fc4dcc6a59 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -61,6 +61,7 @@ import android.os.IInterface;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.telephony.TelephonyRegistryManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.provider.Telephony.ServiceStateTable;
@@ -247,6 +248,8 @@ public class ContextFixture implements TestFixture<Context> {
return mTelecomManager;
case Context.DOWNLOAD_SERVICE:
return mDownloadManager;
+ case Context.TELEPHONY_REGISTRY_SERVICE:
+ return mTelephonyRegistryManager;
case Context.DISPLAY_SERVICE:
case Context.POWER_SERVICE:
// PowerManager and DisplayManager are final classes so cannot be mocked,
@@ -569,6 +572,8 @@ public class ContextFixture implements TestFixture<Context> {
private final EuiccManager mEuiccManager = mock(EuiccManager.class);
private final TelecomManager mTelecomManager = mock(TelecomManager.class);
private final PackageInfo mPackageInfo = mock(PackageInfo.class);
+ private final TelephonyRegistryManager mTelephonyRegistryManager =
+ mock(TelephonyRegistryManager.class);
private final ContentProvider mContentProvider = spy(new FakeContentProvider());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 3e1ecbe9f4..b8b5949d80 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
@@ -33,6 +32,7 @@ import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.telephony.PhoneInternalInterface.DataActivityState;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -46,8 +46,6 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
private DefaultPhoneNotifier mDefaultPhoneNotifierUT;
@Mock
- ITelephonyRegistry.Stub mTelephonyRegisteryMock;
- @Mock
SignalStrength mSignalStrength;
@Mock
CellInfo mCellInfo;
@@ -61,11 +59,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
@Before
public void setUp() throws Exception {
super.setUp(getClass().getSimpleName());
- mServiceManagerMockedServices.put("telephony.registry", mTelephonyRegisteryMock);
- doReturn(mTelephonyRegisteryMock).when(mTelephonyRegisteryMock)
- .queryLocalInterface(anyString());
-
- mDefaultPhoneNotifierUT = new DefaultPhoneNotifier();
+ mDefaultPhoneNotifierUT = new DefaultPhoneNotifier(mContext);
}
@After
@@ -76,26 +70,26 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
@Test @SmallTest
public void testNotifyCallForwarding() throws Exception {
mDefaultPhoneNotifierUT.notifyCallForwardingChanged(mPhone);
- verify(mTelephonyRegisteryMock).notifyCallForwardingChangedForSubscriber(eq(0), eq(false));
+ verify(mTelephonyRegistryManager).notifyCallForwardingChanged(eq(0), eq(false));
doReturn(true).when(mPhone).getCallForwardingIndicator();
doReturn(1).when(mPhone).getSubId();
mDefaultPhoneNotifierUT.notifyCallForwardingChanged(mPhone);
- verify(mTelephonyRegisteryMock).notifyCallForwardingChangedForSubscriber(eq(1), eq(true));
+ verify(mTelephonyRegistryManager).notifyCallForwardingChanged(eq(1), eq(true));
}
@Test @SmallTest
public void testNotifyDataActivity() throws Exception {
//mock data activity state
- doReturn(Phone.DataActivityState.NONE).when(mPhone).getDataActivityState();
+ doReturn(DataActivityState.NONE).when(mPhone).getDataActivityState();
mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
- verify(mTelephonyRegisteryMock).notifyDataActivityForSubscriber(eq(0),
+ verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(0),
eq(TelephonyManager.DATA_ACTIVITY_NONE));
doReturn(1).when(mPhone).getSubId();
- doReturn(Phone.DataActivityState.DATAIN).when(mPhone).getDataActivityState();
+ doReturn(DataActivityState.DATAIN).when(mPhone).getDataActivityState();
mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
- verify(mTelephonyRegisteryMock).notifyDataActivityForSubscriber(eq(1),
+ verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(1),
eq(TelephonyManager.DATA_ACTIVITY_IN));
}
@@ -108,14 +102,14 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
ArgumentCaptor.forClass(SignalStrength.class);
mDefaultPhoneNotifierUT.notifySignalStrength(mPhone);
- verify(mTelephonyRegisteryMock).notifySignalStrengthForPhoneId(eq(0), eq(0),
+ verify(mTelephonyRegistryManager).notifySignalStrengthChanged(eq(0), eq(0),
signalStrengthArgumentCaptor.capture());
assertEquals(99, signalStrengthArgumentCaptor.getValue().getGsmSignalStrength());
doReturn(1).when(mPhone).getSubId();
doReturn(2).when(mPhone).getPhoneId();
mDefaultPhoneNotifierUT.notifySignalStrength(mPhone);
- verify(mTelephonyRegisteryMock).notifySignalStrengthForPhoneId(eq(2), eq(1),
+ verify(mTelephonyRegistryManager).notifySignalStrengthChanged(eq(1), eq(2),
signalStrengthArgumentCaptor.capture());
assertEquals(99, signalStrengthArgumentCaptor.getValue().getGsmSignalStrength());
}
@@ -129,7 +123,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
mDefaultPhoneNotifierUT.notifyCellInfo(mPhone, mCellInfoList);
- verify(mTelephonyRegisteryMock).notifyCellInfoForSubscriber(eq(0),
+ verify(mTelephonyRegistryManager).notifyCellInfoChanged(eq(0),
cellInfoArgumentCaptor.capture());
assertEquals(mCellInfo, cellInfoArgumentCaptor.getValue().get(0));
}
@@ -138,19 +132,19 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
public void testNotifyMessageWaiting() throws Exception {
doReturn(1).when(mPhone).getPhoneId();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegisteryMock).notifyMessageWaitingChangedForPhoneId(1, 0, false);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(0, 1, false);
doReturn(2).when(mPhone).getPhoneId();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegisteryMock).notifyMessageWaitingChangedForPhoneId(2, 0, false);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(0, 2, false);
doReturn(1).when(mPhone).getSubId();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegisteryMock).notifyMessageWaitingChangedForPhoneId(2, 1, false);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(1, 2, false);
doReturn(true).when(mPhone).getMessageWaitingIndicator();
mDefaultPhoneNotifierUT.notifyMessageWaitingChanged(mPhone);
- verify(mTelephonyRegisteryMock).notifyMessageWaitingChangedForPhoneId(2, 1, true);
+ verify(mTelephonyRegistryManager).notifyMessageWaitingChanged(1, 2, true);
}
@Test @SmallTest
@@ -159,26 +153,26 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
doReturn(0).when(mPhone).getSubId();
mDefaultPhoneNotifierUT.notifyDisconnectCause(mPhone, DisconnectCause.NOT_VALID,
PreciseDisconnectCause.FDN_BLOCKED);
- verify(mTelephonyRegisteryMock).notifyDisconnectCause(1, 0, DisconnectCause.NOT_VALID,
+ verify(mTelephonyRegistryManager).notifyDisconnectCause(0, 1, DisconnectCause.NOT_VALID,
PreciseDisconnectCause.FDN_BLOCKED);
mDefaultPhoneNotifierUT.notifyDisconnectCause(mPhone, DisconnectCause.LOCAL,
PreciseDisconnectCause.CHANNEL_NOT_AVAIL);
- verify(mTelephonyRegisteryMock).notifyDisconnectCause(1, 0, DisconnectCause.LOCAL,
+ verify(mTelephonyRegistryManager).notifyDisconnectCause(0, 1, DisconnectCause.LOCAL,
PreciseDisconnectCause.CHANNEL_NOT_AVAIL);
}
@Test @SmallTest
public void testNotifyDataConnectionFailed() throws Exception {
mDefaultPhoneNotifierUT.notifyDataConnectionFailed(mPhone, "APN_0");
- verify(mTelephonyRegisteryMock).notifyDataConnectionFailedForSubscriber(0, 0, "APN_0");
+ verify(mTelephonyRegistryManager).notifyDataConnectionFailed(0, 0, "APN_0");
mDefaultPhoneNotifierUT.notifyDataConnectionFailed(mPhone, "APN_1");
- verify(mTelephonyRegisteryMock).notifyDataConnectionFailedForSubscriber(0, 0, "APN_1");
+ verify(mTelephonyRegistryManager).notifyDataConnectionFailed(0, 0, "APN_1");
doReturn(1).when(mPhone).getSubId();
mDefaultPhoneNotifierUT.notifyDataConnectionFailed(mPhone, "APN_1");
- verify(mTelephonyRegisteryMock).notifyDataConnectionFailedForSubscriber(0,1, "APN_1");
+ verify(mTelephonyRegistryManager).notifyDataConnectionFailed(1,0, "APN_1");
}
@Test @SmallTest
@@ -190,22 +184,22 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
doReturn(Call.State.IDLE).when(mRingingCall).getState();
mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegisteryMock, times(0)).notifyPreciseCallState(
+ verify(mTelephonyRegistryManager, times(0)).notifyPreciseCallState(
anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
doReturn(mForeGroundCall).when(mPhone).getForegroundCall();
mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegisteryMock, times(0)).notifyPreciseCallState(
+ verify(mTelephonyRegistryManager, times(0)).notifyPreciseCallState(
anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
doReturn(mBackGroundCall).when(mPhone).getBackgroundCall();
mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegisteryMock, times(0)).notifyPreciseCallState(
+ verify(mTelephonyRegistryManager, times(0)).notifyPreciseCallState(
anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
doReturn(mRingingCall).when(mPhone).getRingingCall();
mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegisteryMock, times(1)).notifyPreciseCallState(
+ verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
mPhone.getPhoneId(),
mPhone.getSubId(),
PreciseCallState.PRECISE_CALL_STATE_IDLE,
@@ -214,7 +208,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
doReturn(Call.State.ACTIVE).when(mForeGroundCall).getState();
mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegisteryMock, times(1)).notifyPreciseCallState(
+ verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
mPhone.getPhoneId(),
mPhone.getSubId(),
PreciseCallState.PRECISE_CALL_STATE_IDLE,
@@ -223,7 +217,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
doReturn(Call.State.HOLDING).when(mBackGroundCall).getState();
mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegisteryMock, times(1)).notifyPreciseCallState(
+ verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
mPhone.getPhoneId(),
mPhone.getSubId(),
PreciseCallState.PRECISE_CALL_STATE_IDLE,
@@ -232,7 +226,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
doReturn(Call.State.ALERTING).when(mRingingCall).getState();
mDefaultPhoneNotifierUT.notifyPreciseCallState(mPhone);
- verify(mTelephonyRegisteryMock, times(1)).notifyPreciseCallState(
+ verify(mTelephonyRegistryManager, times(1)).notifyPreciseCallState(
mPhone.getPhoneId(),
mPhone.getSubId(),
PreciseCallState.PRECISE_CALL_STATE_ALERTING,
@@ -250,7 +244,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
ArgumentCaptor.forClass(Bundle.class);
mDefaultPhoneNotifierUT.notifyCellLocation(mPhone, mGsmCellLocation);
- verify(mTelephonyRegisteryMock).notifyCellLocationForSubscriber(eq(0),
+ verify(mTelephonyRegistryManager).notifyCellLocation(eq(0),
cellLocationCapture.capture());
assertEquals(2, cellLocationCapture.getValue().getInt("lac"));
assertEquals(3, cellLocationCapture.getValue().getInt("cid"));
@@ -259,7 +253,7 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
doReturn(1).when(mPhone).getSubId();
mGsmCellLocation.setPsc(5);
mDefaultPhoneNotifierUT.notifyCellLocation(mPhone, mGsmCellLocation);
- verify(mTelephonyRegisteryMock).notifyCellLocationForSubscriber(eq(1),
+ verify(mTelephonyRegistryManager).notifyCellLocation(eq(1),
cellLocationCapture.capture());
assertEquals(2, cellLocationCapture.getValue().getInt("lac"));
assertEquals(3, cellLocationCapture.getValue().getInt("cid"));
@@ -269,11 +263,11 @@ public class DefaultPhoneNotifierTest extends TelephonyTest {
@Test @SmallTest
public void testNotifyOtaspChanged() throws Exception {
mDefaultPhoneNotifierUT.notifyOtaspChanged(mPhone, TelephonyManager.OTASP_NEEDED);
- verify(mTelephonyRegisteryMock).notifyOtaspChanged(eq(mPhone.getSubId()),
+ verify(mTelephonyRegistryManager).notifyOtaspChanged(eq(mPhone.getSubId()),
eq(TelephonyManager.OTASP_NEEDED));
mDefaultPhoneNotifierUT.notifyOtaspChanged(mPhone, TelephonyManager.OTASP_UNKNOWN);
- verify(mTelephonyRegisteryMock).notifyOtaspChanged(eq(mPhone.getSubId()),
+ verify(mTelephonyRegistryManager).notifyOtaspChanged(eq(mPhone.getSubId()),
eq(TelephonyManager.OTASP_UNKNOWN));
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 2671f84c7e..44fbabedfe 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -77,6 +78,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -583,7 +585,8 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
voiceMailNumber = "1234567893";
mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
+ InOrder inOrder = inOrder(mSimRecords);
+ inOrder.verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
messageArgumentCaptor.capture());
// SIM does not support voicemail number (IccVmNotSupportedException) so should be saved in
@@ -600,7 +603,7 @@ public class GsmCdmaPhoneTest extends TelephonyTest {
voiceMailNumber = "1234567894";
mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
+ inOrder.verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
messageArgumentCaptor.capture());
// successfully saved on SIM
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
index 2e7710f052..51aa5d0bc2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -20,14 +20,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
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 android.content.Context;
import android.content.Intent;
-import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Looper;
import android.os.Message;
@@ -56,6 +55,7 @@ public class LocaleTrackerTest extends TelephonyTest {
private static final String US_MCC = "310";
private static final String LIECHTENSTEIN_MCC = "295";
+ private static final String BOGUS_MCC = "001";
private static final String FAKE_MNC = "123";
@@ -66,7 +66,6 @@ public class LocaleTrackerTest extends TelephonyTest {
private LocaleTracker mLocaleTracker;
private CellInfoGsm mCellInfo;
- private WifiManager mWifiManager;
@Before
public void setUp() throws Exception {
@@ -77,7 +76,6 @@ public class LocaleTrackerTest extends TelephonyTest {
// This is a workaround to bypass setting system properties, which causes access violation.
doReturn(-1).when(mPhone).getPhoneId();
- mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mCellInfo = new CellInfoGsm();
mCellInfo.setCellIdentity(new CellIdentityGsm(
@@ -117,18 +115,17 @@ public class LocaleTrackerTest extends TelephonyTest {
processAllMessages();
}
- private void verifyCountryCodeNotified(String[] countryCodes) {
- ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
- verify(mWifiManager, times(countryCodes.length)).setCountryCode(
- stringArgumentCaptor.capture());
- List<String> strs = stringArgumentCaptor.getAllValues();
+ private void sendOperatorLost() {
+ mLocaleTracker.sendMessage(mLocaleTracker.obtainMessage(6 /* EVENT_OPERATOR_LOST */));
+ processAllMessages();
+ }
+ private void verifyCountryCodeNotified(String[] countryCodes) {
ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(countryCodes.length)).sendBroadcast(intentArgumentCaptor.capture());
List<Intent> intents = intentArgumentCaptor.getAllValues();
for (int i = 0; i < countryCodes.length; i++) {
- assertEquals(countryCodes[i], strs.get(i));
assertEquals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED,
intents.get(i).getAction());
assertEquals(countryCodes[i], intents.get(i).getStringExtra(
@@ -280,4 +277,21 @@ public class LocaleTrackerTest extends TelephonyTest {
assertEquals(600000, LocaleTracker.getCellInfoDelayTime(i));
}
}
+
+ @Test
+ @SmallTest
+ public void updateOperatorNumeric_NoSim_shouldHandleNetworkCountryCodeUnavailable()
+ throws Exception {
+ mLocaleTracker.updateOperatorNumeric("");
+ sendOperatorLost();
+ verify(mNitzStateMachine, times(1)).handleNetworkCountryCodeUnavailable();
+ }
+
+ @Test
+ @SmallTest
+ public void updateOperatorNumeric_BogusNetwork_shouldHandleNetworkCountryCodeSet()
+ throws Exception {
+ mLocaleTracker.updateOperatorNumeric(BOGUS_MCC + FAKE_MNC);
+ verify(mNitzStateMachine, times(1)).handleNetworkCountryCodeSet(anyBoolean());
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index 68875888e6..0a920a9825 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -108,6 +108,7 @@ public class MultiSimSettingControllerTest extends TelephonyTest {
// Sub 1 is the default sub.
// Sub 1 is in slot 0; sub 2 is in slot 1.
doReturn(DUAL_SIM).when(mTelephonyManager).getPhoneCount();
+ doReturn(DUAL_SIM).when(mTelephonyManager).getActiveModemCount();
doReturn(1).when(mSubControllerMock).getDefaultDataSubId();
doReturn(1).when(mSubControllerMock).getDefaultVoiceSubId();
doReturn(1).when(mSubControllerMock).getDefaultSmsSubId();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
index d023c79ac1..02a0cc92e6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
@@ -34,6 +34,7 @@ public final class NitzStateMachineTestSupport {
// Values used to when initializing device state but where the value isn't important.
public static final long ARBITRARY_SYSTEM_CLOCK_TIME = createUtcTime(1977, 1, 1, 12, 0, 0);
public static final long ARBITRARY_REALTIME_MILLIS = 123456789L;
+ // This zone isn't used in any of the scenarios below.
public static final String ARBITRARY_TIME_ZONE_ID = "Europe/Paris";
public static final String ARBITRARY_DEBUG_INFO = "Test debug info";
@@ -44,6 +45,7 @@ public final class NitzStateMachineTestSupport {
.setActualTimeUtc(2018, 1, 1, 12, 0, 0)
.setCountryIso("gb")
.buildFrozen();
+
public static final String UNITED_KINGDOM_COUNTRY_DEFAULT_ZONE_ID = "Europe/London";
// The US is a country that has multiple zones, but there is only one matching time zone at the
@@ -62,6 +64,17 @@ public final class NitzStateMachineTestSupport {
.setActualTimeUtc(2018, 1, 1, 12, 0, 0)
.setCountryIso("us")
.buildFrozen();
+
+ // A non-unique US scenario: the offset information is ambiguous between America/Phoenix and
+ // America/Denver during winter.
+ public static final Scenario NON_UNIQUE_US_ZONE_SCENARIO = new Scenario.Builder()
+ .setTimeZone("America/Denver")
+ .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
+ .setCountryIso("us")
+ .buildFrozen();
+ public static final String[] NON_UNIQUE_US_ZONE_SCENARIO_ZONES =
+ { "America/Denver", "America/Phoenix" };
+
public static final String US_COUNTRY_DEFAULT_ZONE_ID = "America/New_York";
// New Zealand is a country with multiple zones, but the default zone has the "boost" modifier
@@ -76,6 +89,7 @@ public final class NitzStateMachineTestSupport {
.setActualTimeUtc(2018, 1, 1, 12, 0, 0)
.setCountryIso("nz")
.buildFrozen();
+
public static final String NEW_ZEALAND_COUNTRY_DEFAULT_ZONE_ID = "Pacific/Auckland";
// A country with a single zone: the zone can be guessed from the country alone. CZ never uses
@@ -85,6 +99,7 @@ public final class NitzStateMachineTestSupport {
.setActualTimeUtc(2018, 1, 1, 12, 0, 0)
.setCountryIso("cz")
.buildFrozen();
+
public static final String CZECHIA_COUNTRY_DEFAULT_ZONE_ID = "Europe/Prague";
/**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupportTest.java b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupportTest.java
index 6f5eed05a9..4df0adf938 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupportTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupportTest.java
@@ -23,6 +23,8 @@ import static com.android.internal.telephony.NitzStateMachineTestSupport.CZECHIA
import static com.android.internal.telephony.NitzStateMachineTestSupport.NEW_ZEALAND_COUNTRY_DEFAULT_ZONE_ID;
import static com.android.internal.telephony.NitzStateMachineTestSupport.NEW_ZEALAND_DEFAULT_SCENARIO;
import static com.android.internal.telephony.NitzStateMachineTestSupport.NEW_ZEALAND_OTHER_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.NON_UNIQUE_US_ZONE_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.NON_UNIQUE_US_ZONE_SCENARIO_ZONES;
import static com.android.internal.telephony.NitzStateMachineTestSupport.UNIQUE_US_ZONE_SCENARIO1;
import static com.android.internal.telephony.NitzStateMachineTestSupport.UNIQUE_US_ZONE_SCENARIO2;
import static com.android.internal.telephony.NitzStateMachineTestSupport.UNITED_KINGDOM_COUNTRY_DEFAULT_ZONE_ID;
@@ -33,6 +35,8 @@ import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.
import static com.android.internal.telephony.TimeZoneLookupHelper.CountryResult.QUALITY_SINGLE_ZONE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.internal.telephony.TimeZoneLookupHelper.CountryResult;
import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
@@ -40,6 +44,9 @@ import com.android.internal.telephony.TimeZoneLookupHelper.OffsetResult;
import org.junit.Before;
import org.junit.Test;
+import java.util.Arrays;
+import java.util.List;
+
public class NitzStateMachineTestSupportTest {
private TimeZoneLookupHelper mTimeZoneLookupHelper;
@@ -87,6 +94,32 @@ public class NitzStateMachineTestSupportTest {
}
@Test
+ public void test_nonUniqueUs_assumptions() {
+ // Check we'll get the expected behavior from TimeZoneLookupHelper.
+
+ // quality == QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS, therefore the country's default zone
+ // shouldn't be considered a good match.
+ CountryResult expectedCountryLookupResult = new CountryResult(
+ US_COUNTRY_DEFAULT_ZONE_ID, QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS,
+ ARBITRARY_DEBUG_INFO);
+ CountryResult actualCountryLookupResult =
+ mTimeZoneLookupHelper.lookupByCountry(
+ NON_UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode(),
+ ARBITRARY_SYSTEM_CLOCK_TIME);
+ assertEquals(expectedCountryLookupResult, actualCountryLookupResult);
+
+ // By definition, there are multiple matching zones for the NON_UNIQUE_US_ZONE_SCENARIO.
+ {
+ OffsetResult actualLookupResult = mTimeZoneLookupHelper.lookupByNitzCountry(
+ NON_UNIQUE_US_ZONE_SCENARIO.createNitzData(),
+ NON_UNIQUE_US_ZONE_SCENARIO.getNetworkCountryIsoCode());
+ List<String> possibleZones = Arrays.asList(NON_UNIQUE_US_ZONE_SCENARIO_ZONES);
+ assertTrue(possibleZones.contains(actualLookupResult.getTimeZone().getID()));
+ assertFalse(actualLookupResult.getIsOnlyMatch());
+ }
+ }
+
+ @Test
public void test_unitedKingdom_assumptions() {
assertEquals(UNITED_KINGDOM_SCENARIO.getTimeZone().getID(),
UNITED_KINGDOM_COUNTRY_DEFAULT_ZONE_ID);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
new file mode 100644
index 0000000000..6f01a47b08
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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;
+
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
+import static android.telephony.TelephonyManager.EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED;
+import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
+import static android.telephony.TelephonyManager.MODEM_COUNT_SINGLE_MODEM;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+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.verify;
+
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.PhoneCapability;
+import android.test.suitebuilder.annotation.SmallTest;
+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;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PhoneConfigurationManagerTest extends TelephonyTest {
+ @Mock
+ Handler mHandler;
+ @Mock
+ CommandsInterface mMockCi;
+ @Mock
+ PhoneConfigurationManager.MockableInterface mMi;
+
+ private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 1;
+ PhoneConfigurationManager mPcm;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+ mPhone.mCi = mMockCi;
+ mCT.mCi = mMockCi;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Restore system properties.
+ super.tearDown();
+ }
+
+ private void setRebootRequiredForConfigSwitch(boolean rebootRequired) {
+ doReturn(rebootRequired).when(mMi).isRebootRequiredForModemConfigChange();
+ }
+
+ private void init(int numOfSim) throws Exception {
+ doReturn(numOfSim).when(mTelephonyManager).getActiveModemCount();
+ replaceInstance(PhoneConfigurationManager.class, "sInstance", null, null);
+ mPcm = PhoneConfigurationManager.init(mContext);
+ replaceInstance(PhoneConfigurationManager.class, "mMi", mPcm, mMi);
+ processAllMessages();
+ }
+
+ /**
+ * Test that a single phone case results in our phone being active and the RIL called
+ */
+ @Test
+ @SmallTest
+ public void testGetPhoneCount() throws Exception {
+ init(MODEM_COUNT_SINGLE_MODEM);
+ doReturn(MODEM_COUNT_SINGLE_MODEM).when(mTelephonyManager).getActiveModemCount();
+ assertEquals(MODEM_COUNT_SINGLE_MODEM, mPcm.getPhoneCount());
+ doReturn(MODEM_COUNT_DUAL_MODEM).when(mTelephonyManager).getActiveModemCount();
+ assertEquals(MODEM_COUNT_DUAL_MODEM, mPcm.getPhoneCount());
+ }
+
+ @Test
+ @SmallTest
+ public void testEnablePhone() throws Exception {
+ init(MODEM_COUNT_SINGLE_MODEM);
+ // Phone is null. No crash.
+ mPcm.enablePhone(null, true, null);
+
+ Message message = new Message();
+ mPcm.enablePhone(mPhone, false, message);
+ verify(mMockCi).enableModem(eq(false), eq(message));
+ }
+
+ @Test
+ @SmallTest
+ public void testGetDsdsCapability() throws Exception {
+ init(MODEM_COUNT_SINGLE_MODEM);
+ assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
+
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockRadioConfig).getPhoneCapability(captor.capture());
+ Message msg = captor.getValue();
+ AsyncResult.forMessage(msg, PhoneCapability.DEFAULT_DSDS_CAPABILITY, null);
+ msg.sendToTarget();
+ processAllMessages();
+
+ // Not static capability should indicate DSDS capable.
+ assertEquals(PhoneCapability.DEFAULT_DSDS_CAPABILITY, mPcm.getStaticPhoneCapability());
+ }
+
+ @Test
+ @SmallTest
+ public void testSwitchMultiSimConfig_notDsdsCapable_shouldFail() throws Exception {
+ init(MODEM_COUNT_SINGLE_MODEM);
+ assertEquals(PhoneCapability.DEFAULT_SSSS_CAPABILITY, mPcm.getStaticPhoneCapability());
+
+ // Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported.
+ mPcm.switchMultiSimConfig(MODEM_COUNT_DUAL_MODEM);
+ verify(mMockRadioConfig, never()).setModemsConfig(anyInt(), any());
+ }
+
+ @Test
+ @SmallTest
+ public void testSwitchMultiSimConfig_dsdsCapable_noRebootRequired() throws Exception {
+ init(MODEM_COUNT_SINGLE_MODEM);
+ // Register for multi SIM config change.
+ mPcm.registerForMultiSimConfigChange(mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
+ verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
+
+ // Try switching to dual SIM. Shouldn't work as we haven't indicated DSDS is supported.
+ mPcm.switchMultiSimConfig(MODEM_COUNT_DUAL_MODEM);
+ verify(mMockRadioConfig, never()).setModemsConfig(anyInt(), any());
+
+ // Send static capability back to indicate DSDS is supported.
+ clearInvocations(mMockRadioConfig);
+ testGetDsdsCapability();
+
+ // Try to switch to DSDS.
+ setRebootRequiredForConfigSwitch(false);
+ mPcm.switchMultiSimConfig(MODEM_COUNT_DUAL_MODEM);
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockRadioConfig).setModemsConfig(eq(MODEM_COUNT_DUAL_MODEM), 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(MODEM_COUNT_DUAL_MODEM);
+
+ // Capture and verify registration notification.
+ verify(mHandler).sendMessageAtTime(captor.capture(), anyLong());
+ message = captor.getValue();
+ assertEquals(EVENT_MULTI_SIM_CONFIG_CHANGED, message.what);
+ assertEquals(MODEM_COUNT_DUAL_MODEM, ((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(MODEM_COUNT_DUAL_MODEM, intent.getIntExtra(
+ EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED, 0));
+
+ // Verify RIL notification.
+ verify(mMockCi).onSlotActiveStatusChange(true);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index ff57227581..a69a9a6831 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -53,6 +53,7 @@ public class PhoneSubInfoControllerTest extends TelephonyTest {
doReturn(0).when(mSubscriptionController).getPhoneId(eq(0));
doReturn(1).when(mSubscriptionController).getPhoneId(eq(1));
doReturn(2).when(mTelephonyManager).getPhoneCount();
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
doReturn(true).when(mSubscriptionController).isActiveSubId(0, TAG);
doReturn(true).when(mSubscriptionController).isActiveSubId(1, TAG);
doReturn(new int[]{0, 1}).when(mSubscriptionManager)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index 0a201bda36..fe0e74f2bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import static android.telephony.CarrierConfigManager.KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG;
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;
@@ -503,7 +504,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
// Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, null);
processAllMessages();
- verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
+ verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
// Validation failed. Preferred data sub should remain 1, data phone should remain 0.
mPhoneSwitcher.mValidationCallback.onValidationResult(false, 2);
@@ -517,11 +518,14 @@ public class PhoneSwitcherTest extends TelephonyTest {
processAllMessages();
assertEquals(1, mPhoneSwitcher.getPreferredDataPhoneId());
- // Switching data back to primary (subId 1).
+ // Switching data back to primary (subId 1) with customized validation timeout.
+ long timeout = 1234;
+ mContextFixture.getCarrierConfigBundle().putLong(
+ KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, timeout);
mPhoneSwitcher.trySetOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true, null);
processAllMessages();
- verify(mCellularNetworkValidator).validate(eq(1), anyInt(), eq(false),
+ verify(mCellularNetworkValidator).validate(eq(1), eq(timeout), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
mPhoneSwitcher.mValidationCallback.onValidationResult(true, 1);
processAllMessages();
@@ -872,7 +876,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
clearInvocations(mSetOpptDataCallback2);
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
processAllMessages();
- verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
+ verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback2);
@@ -894,7 +898,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
// Back to back call, call 1 to switch to subId 2, call 2 to switch back.
mPhoneSwitcher.trySetOpportunisticDataSubscription(2, true, mSetOpptDataCallback1);
processAllMessages();
- verify(mCellularNetworkValidator).validate(eq(2), anyInt(), eq(false),
+ verify(mCellularNetworkValidator).validate(eq(2), anyLong(), eq(false),
eq(mPhoneSwitcher.mValidationCallback));
doReturn(true).when(mCellularNetworkValidator).isValidating();
mPhoneSwitcher.trySetOpportunisticDataSubscription(
@@ -1036,6 +1040,7 @@ public class PhoneSwitcherTest extends TelephonyTest {
}
doReturn(numPhones).when(mTelephonyManager).getPhoneCount();
+ doReturn(numPhones).when(mTelephonyManager).getActiveModemCount();
if (numPhones == 1) {
mCommandsInterfaces = new CommandsInterface[] {mCommandsInterface0};
mPhones = new Phone[] {mPhone};
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
index 05c2cb8edd..ff3c36ce4d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
@@ -16,6 +16,9 @@
package com.android.internal.telephony;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
@@ -31,10 +34,6 @@ import org.junit.Test;
import java.util.Arrays;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-
public class SmsNumberUtilsTest extends TelephonyTest {
private static final String TMO_MCC_MNC = "310260";
@@ -119,7 +118,7 @@ public class SmsNumberUtilsTest extends TelephonyTest {
mHbpcdContentProvider = new HbpcdContentProvider();
- doReturn(TMO_MCC_MNC).when(mTelephonyManager).getNetworkOperator(anyInt());
+ doReturn(TMO_MCC_MNC).when(mTelephonyManager).getNetworkOperator();
((MockContentResolver) mContextFixture.getTestDouble().getContentResolver())
.addProvider(HbpcdLookup.MccIdd.CONTENT_URI.getAuthority(), mHbpcdContentProvider);
@@ -138,103 +137,116 @@ public class SmsNumberUtilsTest extends TelephonyTest {
@Test
@SmallTest
public void testInvalidNumberConversion() {
- assertEquals("123", SmsNumberUtils.filterDestAddr(mPhone, "123"));
+ assertEquals("123", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(), "123"));
}
@Test
@SmallTest
public void testNaPcCountryCodeAreaLocalNumberConversion() {
// NP_NANP_NBPCD_CC_AREA_LOCAL tests
- doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
- assertEquals("18583420022", SmsNumberUtils.filterDestAddr(mPhone, "+1-858-342-0022"));
+ doReturn(TelephonyManager.PHONE_TYPE_CDMA).when(mTelephonyManager).getPhoneType();
+ assertEquals("18583420022", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "+1-858-342-0022"));
}
@Test
@SmallTest
public void testPcCountryCodeAreaLocalNumberConversion() {
// NP_NBPCD_CC_AREA_LOCAL tests
- assertEquals("01188671234567", SmsNumberUtils.filterDestAddr(mPhone, "+886-7-1234567"));
+ assertEquals("01188671234567", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "+886-7-1234567"));
}
@Test
@SmallTest
public void testIndiaPcCountryCodeAreaLocalNumberConversion() {
// NP_NBPCD_CC_AREA_LOCAL tests
- doReturn(INDIA_AIRTEL_MCC_MNC).when(mTelephonyManager).getNetworkOperator(anyInt());
- assertEquals("0119172345678", SmsNumberUtils.filterDestAddr(mPhone, "+91-7-234-5678"));
+ doReturn(INDIA_AIRTEL_MCC_MNC).when(mTelephonyManager).getNetworkOperator();
+ assertEquals("0119172345678", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "+91-7-234-5678"));
}
@Test
@SmallTest
public void testPcHomeIddCountryCodeAreaLocalNumberConversion() {
// NP_NBPCD_HOMEIDD_CC_AREA_LOCAL tests
- assertEquals("01188671234567", SmsNumberUtils.filterDestAddr(mPhone, "+011886-7-1234567"));
+ assertEquals("01188671234567", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "+011886-7-1234567"));
}
@Test
@SmallTest
public void testHomeIddCountryCodeAreaLocalNumberConversion() {
// NP_HOMEIDD_CC_AREA_LOCAL tests
- assertEquals("01188671234567", SmsNumberUtils.filterDestAddr(mPhone, "011886-7-1234567"));
+ assertEquals("01188671234567", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "011886-7-1234567"));
}
@Test
@SmallTest
public void testLocalIddCountryCodeAreaLocalNumberConversion() {
// NP_LOCALIDD_CC_AREA_LOCAL tests
- doReturn(TAIWAN_FET_MCC_MNC).when(mTelephonyManager).getNetworkOperator(anyInt());
- assertEquals("01118581234567", SmsNumberUtils.filterDestAddr(mPhone, "002-1-858-1234567"));
+ doReturn(TAIWAN_FET_MCC_MNC).when(mTelephonyManager).getNetworkOperator();
+ assertEquals("01118581234567", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "002-1-858-1234567"));
}
@Test
@SmallTest
public void testIndiaLocalIddCountryCodeAreaLocalNumberConversion() {
// NP_LOCALIDD_CC_AREA_LOCAL tests
- doReturn(INDIA_AIRTEL_MCC_MNC).when(mTelephonyManager).getNetworkOperator(anyInt());
- assertEquals("01118581234567", SmsNumberUtils.filterDestAddr(mPhone, "010-1-858-1234567"));
+ doReturn(INDIA_AIRTEL_MCC_MNC).when(mTelephonyManager).getNetworkOperator();
+ assertEquals("01118581234567", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "010-1-858-1234567"));
}
@Test
@SmallTest
public void testJapanLocalIddCountryCodeAreaLocalNumberConversion() {
// NP_LOCALIDD_CC_AREA_LOCAL tests
- doReturn(JAPAN_NTTDOCOMO_MCC_MNC).when(mTelephonyManager).getNetworkOperator(anyInt());
- assertEquals("01118581234567", SmsNumberUtils.filterDestAddr(mPhone, "010-1-858-1234567"));
+ doReturn(JAPAN_NTTDOCOMO_MCC_MNC).when(mTelephonyManager).getNetworkOperator();
+ assertEquals("01118581234567", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "010-1-858-1234567"));
}
@Test
@SmallTest
public void testCountryCodeAreaLocalNumberConversion() {
// NP_CC_AREA_LOCAL tests
- assertEquals("011886286281234", SmsNumberUtils.filterDestAddr(mPhone, "886-2-86281234"));
+ assertEquals("011886286281234", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "886-2-86281234"));
}
@Test
@SmallTest
public void testNaLocalNumberConversion() {
// NP_NANP_LOCAL
- assertEquals("2345678", SmsNumberUtils.filterDestAddr(mPhone, "234-5678"));
+ assertEquals("2345678", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "234-5678"));
}
@Test
@SmallTest
public void testNaAreaLocalNumberConversion() {
// NP_NANP_AREA_LOCAL
- assertEquals("8582345678", SmsNumberUtils.filterDestAddr(mPhone, "858-234-5678"));
+ assertEquals("8582345678", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "858-234-5678"));
}
@Test
@SmallTest
public void testNaNddAreaLocalNumberConversion() {
// NP_NANP_NDD_AREA_LOCAL
- assertEquals("18582345678", SmsNumberUtils.filterDestAddr(mPhone, "1-858-234-5678"));
+ assertEquals("18582345678", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "1-858-234-5678"));
}
@Test
@SmallTest
public void testNaLocalIddCcAreaLocalNumberConversion() {
// NP_NANP_LOCALIDD_CC_AREA_LOCAL
- assertEquals("+18582345678", SmsNumberUtils.filterDestAddr(mPhone, "011-1-858-234-5678"));
+ assertEquals("+18582345678", SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(),
+ "011-1-858-234-5678"));
}
@Test
@@ -242,6 +254,6 @@ public class SmsNumberUtilsTest extends TelephonyTest {
public void testNaPcHomeIddCcAreaLocalNumberConversion() {
// NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL
assertEquals("01118582345678",
- SmsNumberUtils.filterDestAddr(mPhone, "+011-1-858-234-5678"));
+ SmsNumberUtils.filterDestAddr(mContext, mPhone.getSubId(), "+011-1-858-234-5678"));
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java
index 48128dee64..e427574146 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsPermissionsTest.java
@@ -15,6 +15,9 @@
*/
package com.android.internal.telephony;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -52,6 +55,7 @@ public class SmsPermissionsTest {
private SmsPermissions mSmsPermissionsTest;
private boolean mCallerHasCarrierPrivileges;
+ private boolean mCallerIsDefaultSmsPackage;
@Before
public void setUp() throws Exception {
@@ -68,6 +72,11 @@ public class SmsPermissionsTest {
throw new SecurityException(message);
}
}
+
+ @Override
+ public boolean isDefaultSmsPackage(String packageName) {
+ return mCallerIsDefaultSmsPackage;
+ }
};
initialized.countDown();
});
@@ -166,4 +175,55 @@ public class SmsPermissionsTest {
assertFalse(mSmsPermissionsTest.checkCallingCanSendText(
false /* persistMessageForNonDefaultSmsApp */, PACKAGE, MESSAGE));
}
+
+ @Test
+ public void testCheckCallingOrSelfCanGetSmscAddressPermissions_defaultSmsApp() {
+ mCallerIsDefaultSmsPackage = true;
+ // Other permissions shouldn't matter.
+ Mockito.when(mMockContext.checkCallingOrSelfPermission(
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE))
+ .thenReturn(PERMISSION_DENIED);
+ assertTrue(mSmsPermissionsTest.checkCallingOrSelfCanGetSmscAddress(PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingOrSelfCanGetSmscAddressPermissions_hasReadPrivilegedPhoneState() {
+ Mockito.when(mMockContext.checkCallingOrSelfPermission(
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE))
+ .thenReturn(PERMISSION_GRANTED);
+ assertTrue(mSmsPermissionsTest.checkCallingOrSelfCanGetSmscAddress(PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingOrSelfCanGetSmscAddressPermissions_noPermissions() {
+ Mockito.when(mMockContext.checkCallingOrSelfPermission(
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE))
+ .thenReturn(PERMISSION_DENIED);
+ assertFalse(mSmsPermissionsTest.checkCallingOrSelfCanGetSmscAddress(PACKAGE, MESSAGE));
+ }
+ @Test
+ public void testCheckCallingOrSelfCanSetSmscAddressPermissions_defaultSmsApp() {
+ mCallerIsDefaultSmsPackage = true;
+ // Other permissions shouldn't matter.
+ Mockito.when(mMockContext.checkCallingOrSelfPermission(
+ Manifest.permission.MODIFY_PHONE_STATE))
+ .thenReturn(PERMISSION_DENIED);
+ assertTrue(mSmsPermissionsTest.checkCallingOrSelfCanSetSmscAddress(PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingOrSelfCanSetSmscAddressPermissions_hasModifyPhoneState() {
+ Mockito.when(mMockContext.checkCallingOrSelfPermission(
+ Manifest.permission.MODIFY_PHONE_STATE))
+ .thenReturn(PERMISSION_GRANTED);
+ assertTrue(mSmsPermissionsTest.checkCallingOrSelfCanSetSmscAddress(PACKAGE, MESSAGE));
+ }
+
+ @Test
+ public void testCheckCallingOrSelfCanSetSmscAddressPermissions_noPermissions() {
+ Mockito.when(mMockContext.checkCallingOrSelfPermission(
+ Manifest.permission.MODIFY_PHONE_STATE))
+ .thenReturn(PERMISSION_DENIED);
+ assertFalse(mSmsPermissionsTest.checkCallingOrSelfCanSetSmscAddress(PACKAGE, MESSAGE));
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 7d3f6e83ae..ce3281d92b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -964,6 +964,7 @@ public class SubscriptionControllerTest extends TelephonyTest {
public void testGetEnabledSubscriptionIdDualSIM() {
doReturn(SINGLE_SIM).when(mTelephonyManager).getSimCount();
doReturn(SINGLE_SIM).when(mTelephonyManager).getPhoneCount();
+ doReturn(SINGLE_SIM).when(mTelephonyManager).getActiveModemCount();
// A dual SIM device may have logical slot 0 mapped to physical slot 0
// (i.e. logical slot 1 mapped to physical slot 1)
UiccSlotInfo slot0 = getFakeUiccSlotInfo(true, 0);
@@ -972,6 +973,7 @@ public class SubscriptionControllerTest extends TelephonyTest {
UiccSlot [] uiccSlots = {mUiccSlot, mUiccSlot};
doReturn(2).when(mTelephonyManager).getPhoneCount();
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
doReturn(uiccSlotInfos).when(mTelephonyManager).getUiccSlotsInfo();
doReturn(uiccSlots).when(mUiccController).getUiccSlots();
assertEquals(2, UiccController.getInstance().getUiccSlots().length);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 10d9163899..6c34903d58 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -118,9 +118,9 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
public void setUp() throws Exception {
super.setUp(this.getClass().getSimpleName());
- replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null, new String[1]);
- replaceInstance(SubscriptionInfoUpdater.class, "mContext", null, null);
- replaceInstance(SubscriptionInfoUpdater.class, "PROJECT_SIM_NUM", null, 1);
+ replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null, new String[1]);
+ replaceInstance(SubscriptionInfoUpdater.class, "sContext", null, null);
+ replaceInstance(SubscriptionInfoUpdater.class, "SUPPORTED_MODEM_COUNT", null, 1);
replaceInstance(SubscriptionInfoUpdater.class, "sSimCardState", null, new int[1]);
replaceInstance(SubscriptionInfoUpdater.class, "sSimApplicationState", null, new int[1]);
replaceInstance(SubscriptionInfoUpdater.class, "sIsSubInfoInitialized", null, false);
@@ -132,6 +132,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt());
doReturn(1).when(mTelephonyManager).getSimCount();
doReturn(1).when(mTelephonyManager).getPhoneCount();
+ doReturn(1).when(mTelephonyManager).getActiveModemCount();
when(mContentProvider.update(any(), any(), any(), isNull())).thenAnswer(
new Answer<Integer>() {
@@ -155,7 +156,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
.getActiveSubIdList(/*visibleOnly*/false);
mIccRecord = mUiccProfile.getIccRecords();
- mUpdater = new SubscriptionInfoUpdater(Looper.myLooper(), mContext, new Phone[]{mPhone},
+ mUpdater = new SubscriptionInfoUpdater(Looper.myLooper(), mContext,
new CommandsInterface[]{mSimulatedCommands}, mPackageManager);
processAllMessages();
@@ -386,11 +387,10 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
@SmallTest
public void testDualSimLoaded() throws Exception {
// Mock there is two sim cards
- replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
+ replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mPhone});
+ replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,
new String[]{null, null});
- replaceInstance(SubscriptionInfoUpdater.class, "PROJECT_SIM_NUM", null, 2);
- replaceInstance(SubscriptionInfoUpdater.class, "mPhone", null,
- new Phone[]{mPhone, mPhone});
+ replaceInstance(SubscriptionInfoUpdater.class, "SUPPORTED_MODEM_COUNT", null, 2);
replaceInstance(SubscriptionInfoUpdater.class, "sSimCardState", null,
new int[]{0, 0});
replaceInstance(SubscriptionInfoUpdater.class, "sSimApplicationState", null,
@@ -401,6 +401,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_1));
doReturn(FAKE_SUB_ID_2).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_2));
doReturn(2).when(mTelephonyManager).getPhoneCount();
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
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();
@@ -447,7 +448,7 @@ public class SubscriptionInfoUpdaterTest extends TelephonyTest {
public void testSimLockWithIccId() throws Exception {
/* no need for IccId query */
- replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
+ replaceInstance(SubscriptionInfoUpdater.class, "sIccId", null,
new String[]{"89012604200000000000"});
mUpdater.updateInternalIccState(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
index f2d4a17bf3..08defe3b40 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyPermissionsTest.java
@@ -32,8 +32,11 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ServiceManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -50,14 +53,15 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.lang.reflect.Field;
+import java.util.Map;
@SmallTest
public class TelephonyPermissionsTest {
private static final int SUB_ID = 55555;
private static final int SUB_ID_2 = 22222;
- private static final int PID = 12345;
- private static final int UID = 54321;
+ private static final int PID = Binder.getCallingPid();
+ private static final int UID = Binder.getCallingUid();
private static final String PACKAGE = "com.example";
private static final String MSG = "message";
@@ -70,6 +74,8 @@ public class TelephonyPermissionsTest {
@Mock
private ITelephony mMockTelephony;
@Mock
+ private IBinder mMockTelephonyBinder;
+ @Mock
private PackageManager mMockPackageManager;
@Mock
private ApplicationInfo mMockApplicationInfo;
@@ -101,10 +107,13 @@ public class TelephonyPermissionsTest {
AppOpsManager.MODE_ERRORED);
when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID), eq(UID)))
.thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID_2), eq(UID)))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
when(mMockContext.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
PID, UID)).thenReturn(PackageManager.PERMISSION_DENIED);
when(mMockDevicePolicyManager.checkDeviceIdentifierAccess(eq(PACKAGE), eq(PID),
eq(UID))).thenReturn(false);
+ setTelephonyMockAsService();
}
@Test
@@ -243,8 +252,8 @@ public class TelephonyPermissionsTest {
public void testCheckReadDeviceIdentifiers_noPermissions() throws Exception {
setupMocksForDeviceIdentifiersErrorPath();
try {
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG);
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG);
fail("Should have thrown SecurityException");
} catch (SecurityException e) {
// expected
@@ -256,8 +265,8 @@ public class TelephonyPermissionsTest {
when(mMockContext.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
PID, UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
assertTrue(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -265,8 +274,8 @@ public class TelephonyPermissionsTest {
when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID), eq(UID)))
.thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
assertTrue(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -274,8 +283,8 @@ public class TelephonyPermissionsTest {
when(mMockAppOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, UID,
PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED);
assertTrue(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -283,8 +292,8 @@ public class TelephonyPermissionsTest {
when(mMockDevicePolicyManager.checkDeviceIdentifierAccess(eq(PACKAGE), eq(PID),
eq(UID))).thenReturn(true);
assertTrue(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -296,8 +305,8 @@ public class TelephonyPermissionsTest {
UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
setupMocksForDeviceIdentifiersErrorPath();
try {
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG);
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG);
fail("Should have thrown SecurityException");
} catch (SecurityException e) {
// expected
@@ -314,8 +323,8 @@ public class TelephonyPermissionsTest {
setupMocksForDeviceIdentifiersErrorPath();
mMockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
assertFalse(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -326,8 +335,8 @@ public class TelephonyPermissionsTest {
when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID_2), eq(UID))).thenReturn(
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
assertTrue(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -340,8 +349,8 @@ public class TelephonyPermissionsTest {
when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID_2), eq(UID)))
.thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
assertTrue(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID_2, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -354,8 +363,8 @@ public class TelephonyPermissionsTest {
when(mMockAppOps.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, UID,
PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED);
assertTrue(
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, PACKAGE, MSG));
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
}
@Test
@@ -365,14 +374,79 @@ public class TelephonyPermissionsTest {
// case.
setupMocksForDeviceIdentifiersErrorPath();
try {
- TelephonyPermissions.checkReadDeviceIdentifiers(mMockContext, () -> mMockTelephony,
- SUB_ID, PID, UID, null, MSG);
+ TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mMockContext,
+ SUB_ID, null, MSG);
+ fail("Should have thrown SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testCheckCallingOrSelfReadSubscriberIdentifiers_noPermissions() throws Exception {
+ setupMocksForDeviceIdentifiersErrorPath();
+ setTelephonyMockAsService();
+ when(mMockContext.checkPermission(
+ eq(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockAppOps.noteOpNoThrow(anyString(), anyInt(), eq(PACKAGE))).thenReturn(
+ AppOpsManager.MODE_ERRORED);
+ when(mMockDevicePolicyManager.checkDeviceIdentifierAccess(eq(PACKAGE), anyInt(),
+ anyInt())).thenReturn(false);
+ try {
+ TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG);
fail("Should have thrown SecurityException");
} catch (SecurityException e) {
// expected
}
}
+ @Test
+ public void testCheckCallingOrSelfReadSubscriberIdentifiers_carrierPrivileges()
+ throws Exception {
+ setTelephonyMockAsService();
+ when(mMockContext.checkPermission(
+ eq(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID), anyInt()))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ assertTrue(
+ TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG));
+ }
+
+ @Test
+ public void testCheckCallingOrSelfReadSubscriberIdentifiers_carrierPrivilegesOnOtherSub()
+ throws Exception {
+ setupMocksForDeviceIdentifiersErrorPath();
+ setTelephonyMockAsService();
+ when(mMockContext.checkPermission(
+ eq(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockSubscriptionManager.getActiveSubscriptionIdList(anyBoolean())).thenReturn(
+ new int[]{SUB_ID, SUB_ID_2});
+ when(mMockTelephony.getCarrierPrivilegeStatusForUid(eq(SUB_ID_2), anyInt())).thenReturn(
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ // Carrier privilege on the other active sub shouldn't allow access to this sub.
+ try {
+ TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers(mMockContext,
+ SUB_ID, PACKAGE, MSG);
+ fail("Should have thrown SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ // Put mMockTelephony into service cache so that TELEPHONY_SUPPLIER will get it.
+ private void setTelephonyMockAsService() throws Exception {
+ when(mMockTelephonyBinder.queryLocalInterface(anyString())).thenReturn(mMockTelephony);
+ Field field = ServiceManager.class.getDeclaredField("sCache");
+ field.setAccessible(true);
+ ((Map<String, IBinder>) field.get(null)).put(Context.TELEPHONY_SERVICE,
+ mMockTelephonyBinder);
+ }
+
public static class FakeSettingsConfigProvider extends FakeSettingsProvider {
private static final String PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED =
DeviceConfig.NAMESPACE_PRIVACY + "/"
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 6ba0518c6d..1c057436e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -49,6 +49,7 @@ import android.os.Message;
import android.os.MessageQueue;
import android.os.RegistrantList;
import android.os.ServiceManager;
+import android.os.telephony.TelephonyRegistryManager;
import android.provider.BlockedNumberContract;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -270,6 +271,7 @@ public abstract class TelephonyTest {
protected ImsCallProfile mImsCallProfile;
protected TelephonyManager mTelephonyManager;
+ protected TelephonyRegistryManager mTelephonyRegistryManager;
protected SubscriptionManager mSubscriptionManager;
protected EuiccManager mEuiccManager;
protected PackageManager mPackageManager;
@@ -397,6 +399,8 @@ public abstract class TelephonyTest {
doReturn(mUiccProfile).when(mUiccCard).getUiccProfile();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mTelephonyRegistryManager = (TelephonyRegistryManager) mContext.getSystemService(
+ Context.TELEPHONY_REGISTRY_SERVICE);
mSubscriptionManager = (SubscriptionManager) mContext.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mEuiccManager = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE);
@@ -544,6 +548,7 @@ public abstract class TelephonyTest {
nullable(Intent[].class), nullable(String[].class), anyInt(),
nullable(Bundle.class), anyInt());
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ doReturn(TelephonyManager.PHONE_TYPE_GSM).when(mTelephonyManager).getPhoneType();
doReturn(mServiceState).when(mSST).getServiceState();
mSST.mSS = mServiceState;
mSST.mRestrictedState = mRestrictedState;
@@ -563,9 +568,11 @@ public abstract class TelephonyTest {
//SIM
doReturn(1).when(mTelephonyManager).getSimCount();
doReturn(1).when(mTelephonyManager).getPhoneCount();
+ doReturn(1).when(mTelephonyManager).getActiveModemCount();
// Have getMaxPhoneCount always return the same value with getPhoneCount by default.
- doAnswer((invocation)->mTelephonyManager.getPhoneCount())
- .when(mTelephonyManager).getMaxPhoneCount();
+ doAnswer((invocation)->Math.max(mTelephonyManager.getActiveModemCount(),
+ mTelephonyManager.getPhoneCount()))
+ .when(mTelephonyManager).getSupportedModemCount();
//Data
//Initial state is: userData enabled, provisioned.
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 93e26c74bf..46af98fbd1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
@@ -353,7 +353,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
SmsMessage msg = createBroadcastSmsMessage(123, 456, BearerData.PRIORITY_NORMAL,
BearerData.LANGUAGE_ENGLISH, encoding, TEST_TEXT);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
verifyCbValues(cbMessage);
assertEquals(123, cbMessage.getServiceCategory());
assertEquals(456, cbMessage.getSerialNumber());
@@ -385,7 +385,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
SmsMessage msg = createBroadcastSmsMessage(987, 654, -1, -1,
UserData.ENCODING_IS91_EXTENDED_PROTOCOL, IS91_TEXT);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
verifyCbValues(cbMessage);
assertEquals(987, cbMessage.getServiceCategory());
assertEquals(654, cbMessage.getSerialNumber());
@@ -402,7 +402,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
serviceCategory, 1234, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
UserData.ENCODING_7BIT_ASCII, body, -1, -1, -1, -1, -1);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
verifyCbValues(cbMessage);
assertEquals(serviceCategory, cbMessage.getServiceCategory());
assertEquals(1234, cbMessage.getSerialNumber());
@@ -458,7 +458,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, SmsCbCmasInfo.CMAS_SEVERITY_SEVERE,
SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
verifyCbValues(cbMessage);
assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
cbMessage.getServiceCategory());
@@ -488,7 +488,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
0x1F, EXTREME_ALERT, -1, -1, -1, -1, -1);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
assertNull("expected null for unsupported charset", cbMessage);
}
@@ -500,7 +500,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
67890, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
UserData.ENCODING_KOREAN, EXTREME_ALERT, -1, -1, -1, -1, -1);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
assertNull("expected null for unsupported charset", cbMessage);
}
@@ -513,7 +513,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
UserData.ENCODING_7BIT_ASCII, null, -1, -1, -1, -1, -1);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
verifyCbValues(cbMessage);
assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
cbMessage.getServiceCategory());
@@ -547,7 +547,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
int category = 0x0ff0 + r.nextInt(32); // half CMAS, half non-CMAS
CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, data);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
// with random input, cbMessage will almost always be null (log when it isn't)
if (cbMessage != null) {
Rlog.d("CdmaSmsCbTest", "success: " + cbMessage);
@@ -581,7 +581,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
}
SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
} catch (Exception e) {
Rlog.d("CdmaSmsCbTest", "exception thrown", e);
fail("Exception in decoder at run " + run + " length " + len + ": " + e);
@@ -744,7 +744,7 @@ public class CdmaSmsCbTest extends AndroidTestCase {
CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE);
SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, CMAS_TEST_BEARER_DATA);
- SmsCbMessage cbMessage = msg.parseBroadcastSms("");
+ SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0);
assertNotNull("expected non-null for bearer data", cbMessage);
assertEquals("geoScope", cbMessage.getGeographicalScope(), 1);
assertEquals("serialNumber", cbMessage.getSerialNumber(), 51072);
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 71399c5aff..3d45af3757 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccControllerTest.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.telephony.euicc;
+import static android.telephony.euicc.EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES;
import static android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE;
import static org.junit.Assert.assertEquals;
@@ -984,8 +985,7 @@ public class EuiccControllerTest extends TelephonyTest {
public void testEraseSubscriptions_error() throws Exception {
setHasWriteEmbeddedPermission(true);
callEraseSubscriptions(true /* complete */, 42 /* result */);
- verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR,
- 42 /* detailedCode */);
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */);
}
@Test
@@ -997,6 +997,36 @@ public class EuiccControllerTest extends TelephonyTest {
}
@Test(expected = SecurityException.class)
+ public void testEraseSubscriptionsWithOptions_noPrivileges() throws Exception {
+ setHasWriteEmbeddedPermission(false);
+ callEraseSubscriptionsWithOptions(false /* complete */, 0 /* result */);
+ }
+
+ @Test
+ public void testEraseSubscriptionsWithOptions_serviceUnavailable() throws Exception {
+ setHasWriteEmbeddedPermission(true);
+ callEraseSubscriptionsWithOptions(false /* complete */, 0 /* result */);
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR,
+ 0 /* detailedCode */);
+ verify(mMockConnector).eraseSubscriptionsWithOptions(anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void testEraseSubscriptionsWithOptions_error() throws Exception {
+ setHasWriteEmbeddedPermission(true);
+ callEraseSubscriptionsWithOptions(true /* complete */, 42 /* result */);
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR, 42 /* detailedCode */);
+ }
+
+ @Test
+ public void testEraseSubscriptionsWithOptions_success() throws Exception {
+ setHasWriteEmbeddedPermission(true);
+ callEraseSubscriptionsWithOptions(true /* complete */, EuiccService.RESULT_OK);
+ verifyIntentSent(EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK, 0 /* detailedCode */);
+ assertTrue(mController.mCalledRefreshSubscriptionsAndSendResult);
+ }
+
+ @Test(expected = SecurityException.class)
public void testRetainSubscriptionsForFactoryReset_noPrivileges() throws Exception {
setHasMasterClearPermission(false);
callRetainSubscriptionsForFactoryReset(false /* complete */, 0 /* result */);
@@ -1318,6 +1348,25 @@ public class EuiccControllerTest extends TelephonyTest {
mController.eraseSubscriptions(CARD_ID, resultCallback);
}
+ private void callEraseSubscriptionsWithOptions(final boolean complete, final int result) {
+ PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Exception {
+ EuiccConnector.EraseCommandCallback cb = invocation
+ .getArgument(2 /* resultCallback */);
+ if (complete) {
+ cb.onEraseComplete(result);
+ } else {
+ cb.onEuiccServiceUnavailable();
+ }
+ return null;
+ }
+ }).when(mMockConnector).eraseSubscriptionsWithOptions(anyInt(), anyInt(), any());
+ mController.eraseSubscriptionsWithOptions(CARD_ID,
+ RESET_OPTION_DELETE_OPERATIONAL_PROFILES, resultCallback);
+ }
+
private void callRetainSubscriptionsForFactoryReset(final boolean complete, final int result) {
PendingIntent resultCallback = PendingIntent.getBroadcast(mContext, 0, new Intent(), 0);
doAnswer(new Answer<Void>() {
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 192f95559e..3b2351018a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -807,7 +807,7 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {
// verify that a broadcast receiver is registered for current user (user == null) based on
// implementation in ContextFixture
- verify(mContext, times(2)).registerReceiverAsUser(any(BroadcastReceiver.class),
+ verify(mContext, times(1)).registerReceiverAsUser(any(BroadcastReceiver.class),
eq((UserHandle)null), any(IntentFilter.class), eq((String)null), eq((Handler)null));
// wait for ScanRawTableThread
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java
index 689392e950..8f61c21558 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java
@@ -43,7 +43,8 @@ public class GsmSmsCbTest extends AndroidTestCase {
SmsCbHeader header = new SmsCbHeader(pdu);
byte[][] pdus = new byte[1][];
pdus[0] = pdu;
- return GsmSmsCbMessage.createSmsCbMessage(getContext(), header, sTestLocation, pdus);
+ return GsmSmsCbMessage.createSmsCbMessage(getContext(), header, sTestLocation, pdus,
+ /* slotIndex */ 0);
} catch (IllegalArgumentException e) {
return null;
}
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 ef3700c472..30baabbc27 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -1013,6 +1013,8 @@ public class ImsResolverTest extends ImsTestBase {
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();
}
@@ -1037,8 +1039,6 @@ public class ImsResolverTest extends ImsTestBase {
HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
mLooper.processAllMessages();
// ensure that startQuery was called
- when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
- .thenReturn(true);
verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
mDynamicQueryListener.onComplete(name, features);
mLooper.processAllMessages();
@@ -1047,8 +1047,6 @@ public class ImsResolverTest extends ImsTestBase {
private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) {
mLooper.processAllMessages();
// ensure that startQuery was called
- when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
- .thenReturn(true);
verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
mDynamicQueryListener.onPermanentError(name);
mLooper.processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsUtTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsUtTest.java
new file mode 100644
index 0000000000..4948a67a33
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsUtTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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
index 26afbe37cd..b715d48986 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/MmTelFeatureConnectionTest.java
@@ -60,7 +60,7 @@ public class MmTelFeatureConnectionTest extends TelephonyTest {
List<TestCallback> mCallbacks = new ArrayList<>();
CallbackManagerTest(Context context, Object lock) {
- super(context, lock);
+ super(context, lock, 0 /*slotId*/);
}
// A callback has been registered. Register that callback with the MmTelFeature.
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 b9e5031074..5acba7284e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -51,6 +51,7 @@ import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -87,7 +88,7 @@ import org.mockito.stubbing.Answer;
public class ImsPhoneCallTrackerTest extends TelephonyTest {
private ImsPhoneCallTracker mCTUT;
private MmTelFeature.Listener mMmTelListener;
- private ImsMmTelManager.RegistrationCallback mRegistrationCallback;
+ private RegistrationManager.RegistrationCallback mRegistrationCallback;
private ImsMmTelManager.CapabilityCallback mCapabilityCallback;
private ImsCall.Listener mImsCallListener;
private ImsCall mImsCall;
@@ -178,11 +179,11 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
@Override
public ImsCall answer(InvocationOnMock invocation) throws Throwable {
mImsCallListener =
- (ImsCall.Listener) invocation.getArguments()[2];
+ (ImsCall.Listener) invocation.getArguments()[1];
mImsCall.setListener(mImsCallListener);
return mImsCall;
}
- }).when(mImsManager).takeCall(any(), any(), any());
+ }).when(mImsManager).takeCall(any(), any());
doAnswer(new Answer<ImsCall>() {
@Override
@@ -199,7 +200,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
mRegistrationCallback = invocation.getArgument(0);
return mRegistrationCallback;
}).when(mImsManager).addRegistrationCallback(
- any(android.telephony.ims.ImsMmTelManager.RegistrationCallback.class));
+ any(RegistrationManager.RegistrationCallback.class));
doAnswer(invocation -> {
mCapabilityCallback = (ImsMmTelManager.CapabilityCallback) invocation.getArguments()[0];
@@ -241,10 +242,11 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
@SmallTest
public void testImsRegistered() {
// when IMS is registered
- mRegistrationCallback.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ mRegistrationCallback.onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
// then service state should be IN_SERVICE and ImsPhone state set to registered
verify(mImsPhone).setServiceState(eq(ServiceState.STATE_IN_SERVICE));
- verify(mImsPhone).setImsRegistered(eq(true));
+ verify(mImsPhone).setImsRegistrationState(eq(
+ RegistrationManager.REGISTRATION_STATE_REGISTERED));
}
@Test
@@ -254,7 +256,8 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
mRegistrationCallback.onRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
// then service state should be OUT_OF_SERVICE and ImsPhone state set to not registered
verify(mImsPhone).setServiceState(eq(ServiceState.STATE_OUT_OF_SERVICE));
- verify(mImsPhone).setImsRegistered(eq(false));
+ verify(mImsPhone).setImsRegistrationState(eq(
+ RegistrationManager.REGISTRATION_STATE_REGISTERING));
}
@Test
@@ -264,7 +267,8 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
mRegistrationCallback.onUnregistered(new ImsReasonInfo());
// then service state should be OUT_OF_SERVICE and ImsPhone state set to not registered
verify(mImsPhone).setServiceState(eq(ServiceState.STATE_OUT_OF_SERVICE));
- verify(mImsPhone).setImsRegistered(eq(false));
+ verify(mImsPhone).setImsRegistrationState(eq(
+ RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED));
}
@Test
@@ -437,7 +441,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
// mock a new MT
try {
doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
- any(Bundle.class), any(ImsCall.Listener.class));
+ any(ImsCall.Listener.class));
} catch (Exception ex) {
ex.printStackTrace();
Assert.fail("unexpected exception thrown" + ex.getMessage());
@@ -607,7 +611,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
// mock a new MT
try {
doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
- any(Bundle.class), any(ImsCall.Listener.class));
+ any(ImsCall.Listener.class));
mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
} catch (Exception ex) {
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 960cdfd74a..0cdc34ce4b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -20,7 +20,9 @@ import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
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 static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyChar;
@@ -28,6 +30,7 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.nullable;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@@ -52,6 +55,8 @@ import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -82,6 +87,9 @@ import org.mockito.Mock;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -643,6 +651,62 @@ public class ImsPhoneTest extends TelephonyTest {
@Test
@SmallTest
+ public void testImsRegistered() throws Exception {
+ mImsPhoneUT.setServiceState(ServiceState.STATE_IN_SERVICE);
+ mImsPhoneUT.setImsRegistrationState(RegistrationManager.REGISTRATION_STATE_REGISTERED);
+ assertTrue(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_REGISTERED, regResult.intValue());
+ }
+
+ @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);
+ 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_NOT_REGISTERED, regResult.intValue());
+ }
+
+ public void testGetImsRegistrationTech() throws Exception {
+ LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(1);
+ Consumer<Integer> regTechCallback = queue::offer;
+ doAnswer(invocation -> {
+ Consumer<Integer> c = (Consumer<Integer>) invocation.getArguments()[0];
+ c.accept(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+ return null;
+ }).when(mImsCT).getImsRegistrationTech(eq(regTechCallback));
+ mImsPhoneUT.getImsRegistrationTech(regTechCallback);
+ Integer regTechResult = queue.poll(1000, TimeUnit.MILLISECONDS);
+ assertNotNull(regTechResult);
+ assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, regTechResult.intValue());
+ }
+
+ @Test
+ @SmallTest
public void testRoamingDuplicateMessages() throws Exception {
doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index 67994cae0c..0ce58c2385 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -112,6 +112,9 @@ public class PhoneMock extends Phone {
protected final RegistrantList mHandoverRegistrants
= new RegistrantList();
+ protected final RegistrantList mRedialRegistrants
+ = new RegistrantList();
+
protected final RegistrantList mNewRingingConnectionRegistrants
= new RegistrantList();
@@ -223,6 +226,19 @@ public class PhoneMock extends Phone {
mHandoverRegistrants.notifyRegistrants(ar);
}
+ public void registerForRedialConnectionChanged(Handler h, int what, Object obj) {
+ mRedialRegistrants.addUnique(h, what, obj);
+ }
+
+ public void unregisterForRedialConnectionChanged(Handler h) {
+ mRedialRegistrants.remove(h);
+ }
+
+ public void notifyRedialConnectionChanged(Connection cn) {
+ AsyncResult ar = new AsyncResult(null, cn, null);
+ mRedialRegistrants.notifyRegistrants(ar);
+ }
+
public void migrateFrom(Phone from) {
throw new RuntimeException("not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
index 1fd8faea61..bc2cf620b6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.telephony.Annotation.DataFailureCause;
import android.telephony.CallQuality;
import android.telephony.CellInfo;
import android.telephony.DataFailCause;
@@ -263,12 +264,12 @@ public class TelephonyRegistryMock extends ITelephonyRegistry.Stub {
}
@Override
- public void notifyCallState(int state, String incomingNumber) {
+ public void notifyCallStateForAllSubs(int state, String incomingNumber) {
throw new RuntimeException("Not implemented");
}
@Override
- public void notifyCallStateForPhoneId(int phoneId, int subId, int state,
+ public void notifyCallState(int phoneId, int subId, int state,
String incomingNumber) {
throw new RuntimeException("Not implemented");
}
@@ -398,7 +399,7 @@ public class TelephonyRegistryMock extends ITelephonyRegistry.Stub {
@Override
public void notifyPreciseDataConnectionFailed(int phoneId, int subId,
String apnType, String apn,
- @DataFailCause.FailCause int failCause) {
+ @DataFailureCause int failCause) {
throw new RuntimeException("Not implemented");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java
new file mode 100644
index 0000000000..d4226875ee
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/NewNitzStateMachineImplTest.java
@@ -0,0 +1,628 @@
+/*
+ * 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.nitz;
+
+import static com.android.internal.telephony.NitzStateMachineTestSupport.ARBITRARY_SYSTEM_CLOCK_TIME;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.UNIQUE_US_ZONE_SCENARIO1;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.UNITED_KINGDOM_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.createTimeSuggestionFromNitzSignal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.NitzData;
+import com.android.internal.telephony.NitzStateMachineTestSupport.FakeDeviceState;
+import com.android.internal.telephony.NitzStateMachineTestSupport.Scenario;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.TimeZoneLookupHelper;
+import com.android.internal.telephony.nitz.NewNitzStateMachineImpl.NitzSignalInputFilterPredicate;
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
+import com.android.internal.util.IndentingPrintWriter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.concurrent.TimeUnit;
+
+public class NewNitzStateMachineImplTest extends TelephonyTest {
+
+ private static final int PHONE_ID = 99999;
+ private static final PhoneTimeZoneSuggestion EMPTY_TIME_ZONE_SUGGESTION =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+
+ private FakeNewTimeServiceHelper mFakeNewTimeServiceHelper;
+ private FakeDeviceState mFakeDeviceState;
+ private TimeZoneSuggesterImpl mRealTimeZoneSuggester;
+
+ private NewNitzStateMachineImpl mNitzStateMachineImpl;
+
+
+ @Before
+ public void setUp() throws Exception {
+ TelephonyTest.logd("NewNitzStateMachineImplTest +Setup!");
+ super.setUp("NewNitzStateMachineImplTest");
+
+ // In tests we use a fake impls for NewTimeServiceHelper and DeviceState.
+ mFakeDeviceState = new FakeDeviceState();
+ mFakeNewTimeServiceHelper = new FakeNewTimeServiceHelper(mFakeDeviceState);
+
+ // In tests we disable NITZ signal input filtering. The real NITZ signal filter is tested
+ // independently. This makes constructing test data simpler: we can be sure the signals
+ // won't be filtered for reasons like rate-limiting.
+ NitzSignalInputFilterPredicate mFakeNitzSignalInputFilter = (oldSignal, newSignal) -> true;
+
+ // In tests a real TimeZoneSuggesterImpl is used with the real TimeZoneLookupHelper and real
+ // country time zone data. A fake device state is used (which allows tests to fake the
+ // system clock / user settings). The tests can perform the expected lookups and confirm the
+ // state machine takes the correct action. Picking real examples from the past is easier
+ // than inventing countries / scenarios and configuring fakes.
+ TimeZoneLookupHelper timeZoneLookupHelper = new TimeZoneLookupHelper();
+ mRealTimeZoneSuggester = new TimeZoneSuggesterImpl(mFakeDeviceState, timeZoneLookupHelper);
+
+ mNitzStateMachineImpl = new NewNitzStateMachineImpl(
+ PHONE_ID, mFakeNitzSignalInputFilter, mRealTimeZoneSuggester,
+ mFakeNewTimeServiceHelper, mFakeDeviceState);
+
+ TelephonyTest.logd("NewNitzStateMachineImplTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void test_countryThenNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ String networkCountryIsoCode = scenario.getNetworkCountryIsoCode();
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ // Capture expected results from the real suggester and confirm we can tell the difference
+ // between them.
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion1 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, networkCountryIsoCode, null /* nitzSignal */);
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion2 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, networkCountryIsoCode, nitzSignal);
+ assertNotNull(expectedTimeZoneSuggestion2);
+ assertNotEquals(expectedTimeZoneSuggestion1, expectedTimeZoneSuggestion2);
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Simulate country being known.
+ script.countryReceived(networkCountryIsoCode);
+
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion1);
+
+ // Check NitzStateMachine exposed state.
+ assertNull(mNitzStateMachineImpl.getCachedNitzData());
+
+ // Simulate NITZ being received and verify the behavior.
+ script.nitzReceived(nitzSignal);
+
+ PhoneTimeSuggestion expectedTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, nitzSignal);
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedTimeSuggestion, expectedTimeZoneSuggestion2);
+
+ // Check NitzStateMachine exposed state.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+ }
+
+ @Test
+ public void test_nitzThenCountry() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ String networkCountryIsoCode = scenario.getNetworkCountryIsoCode();
+
+ // Capture test expectations from the real suggester and confirm we can tell the difference
+ // between them.
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion1 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */, nitzSignal);
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion2 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, networkCountryIsoCode, nitzSignal);
+ assertNotEquals(expectedTimeZoneSuggestion1, expectedTimeZoneSuggestion2);
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(nitzSignal);
+
+ // Verify the state machine did the right thing.
+ PhoneTimeSuggestion expectedTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, nitzSignal);
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedTimeSuggestion, expectedTimeZoneSuggestion1);
+
+ // Check NitzStateMachine exposed state.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+
+ // Simulate country being known and verify the behavior.
+ script.countryReceived(networkCountryIsoCode)
+ .verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion2);
+
+ // Check NitzStateMachine exposed state.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+ }
+
+ @Test
+ public void test_emptyCountryString_countryReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Simulate an empty country being set.
+ script.countryReceived("");
+
+ // Nothing should be set. The country is not valid.
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(EMPTY_TIME_ZONE_SUGGESTION);
+
+ // Check NitzStateMachine exposed state.
+ assertNull(mNitzStateMachineImpl.getCachedNitzData());
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(nitzSignal);
+
+ PhoneTimeSuggestion expectedTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, nitzSignal);
+ // Capture output from the real suggester and confirm it meets the test's needs /
+ // expectations.
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, "" /* countryIsoCode */, nitzSignal);
+ assertEquals(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY,
+ expectedTimeZoneSuggestion.getMatchType());
+ assertEquals(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ expectedTimeZoneSuggestion.getQuality());
+
+ // Verify the state machine did the right thing.
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedTimeSuggestion, expectedTimeZoneSuggestion);
+
+ // Check NitzStateMachine exposed state.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+ }
+
+ @Test
+ public void test_emptyCountryStringUsTime_nitzReceivedFirst() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Simulate receiving the NITZ signal.
+ script.nitzReceived(nitzSignal);
+
+ // Verify the state machine did the right thing.
+ // No time zone should be set. A NITZ signal by itself is not enough.
+ PhoneTimeSuggestion expectedTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, nitzSignal);
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedTimeSuggestion, EMPTY_TIME_ZONE_SUGGESTION);
+
+ // Check NitzStateMachine exposed state.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+
+ // Simulate an empty country being set.
+ script.countryReceived("");
+
+ // Capture output from the real suggester and confirm it meets the test's needs /
+ // expectations.
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, "" /* countryIsoCode */, nitzSignal);
+ assertEquals(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY,
+ expectedTimeZoneSuggestion.getMatchType());
+ assertEquals(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ expectedTimeZoneSuggestion.getQuality());
+
+ // Verify the state machine did the right thing.
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion);
+
+ // Check NitzStateMachine exposed state.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+ }
+
+ @Test
+ public void test_airplaneModeClearsState() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO.mutableCopy();
+ int timeStepMillis = (int) TimeUnit.HOURS.toMillis(3);
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Pre-flight: Simulate a device receiving signals that allow it to detect time and time
+ // zone.
+ TimestampedValue<NitzData> preFlightNitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeSuggestion expectedPreFlightTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, preFlightNitzSignal);
+ String preFlightCountryIsoCode = scenario.getNetworkCountryIsoCode();
+
+ // Simulate receiving the NITZ signal and country.
+ script.nitzReceived(preFlightNitzSignal)
+ .countryReceived(preFlightCountryIsoCode);
+
+ // Verify the state machine did the right thing.
+ PhoneTimeZoneSuggestion expectedPreFlightTimeZoneSuggestion =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, preFlightCountryIsoCode, preFlightNitzSignal);
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedPreFlightTimeSuggestion, expectedPreFlightTimeZoneSuggestion);
+
+ // Check state that NitzStateMachine must expose.
+ assertEquals(preFlightNitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+
+ // Boarded flight: Airplane mode turned on / time zone detection still enabled.
+ // The NitzStateMachine must lose all state and stop having an opinion about time zone.
+
+ // Simulate the passage of time and update the device realtime clock.
+ scenario.incrementTime(timeStepMillis);
+ script.incrementTime(timeStepMillis);
+
+ // Simulate airplane mode being turned on.
+ script.toggleAirplaneMode(true);
+
+ // Verify the state machine did the right thing.
+ // Check the time and time zone suggestion was withdrawn.
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(EMPTY_TIME_ZONE_SUGGESTION);
+
+ // Check state that NitzStateMachine must expose.
+ assertNull(mNitzStateMachineImpl.getCachedNitzData());
+
+ // During flight: Airplane mode turned off / time zone detection still enabled.
+ // The NitzStateMachine still must not have an opinion about time zone / hold any state.
+
+ // Simulate the passage of time and update the device realtime clock.
+ scenario.incrementTime(timeStepMillis);
+ script.incrementTime(timeStepMillis);
+
+ // Simulate airplane mode being turned off.
+ script.toggleAirplaneMode(false);
+
+ // Verify the time zone suggestion was withdrawn.
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(EMPTY_TIME_ZONE_SUGGESTION);
+
+ // Check the state that NitzStateMachine must expose.
+ assertNull(mNitzStateMachineImpl.getCachedNitzData());
+
+ // Post flight: Device has moved and receives new signals.
+
+ // Simulate the passage of time and update the device realtime clock.
+ scenario.incrementTime(timeStepMillis);
+ script.incrementTime(timeStepMillis);
+
+ // Simulate the movement to the destination.
+ scenario.changeCountry(UNIQUE_US_ZONE_SCENARIO1.getTimeZoneId(),
+ UNIQUE_US_ZONE_SCENARIO1.getNetworkCountryIsoCode());
+
+ // Simulate the device receiving NITZ signal and country again after the flight. Now the
+ // NitzStateMachine should be opinionated again.
+ TimestampedValue<NitzData> postFlightNitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ String postFlightCountryCode = scenario.getNetworkCountryIsoCode();
+ script.countryReceived(postFlightCountryCode)
+ .nitzReceived(postFlightNitzSignal);
+
+ // Verify the state machine did the right thing.
+ PhoneTimeSuggestion expectedPostFlightTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, postFlightNitzSignal);
+ PhoneTimeZoneSuggestion expectedPostFlightTimeZoneSuggestion =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, postFlightCountryCode, postFlightNitzSignal);
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedPostFlightTimeSuggestion, expectedPostFlightTimeZoneSuggestion);
+
+ // Check state that NitzStateMachine must expose.
+ assertEquals(postFlightNitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+ }
+
+ @Test
+ public void test_countryUnavailableClearsTimeZoneSuggestion() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Simulate receiving the country and verify the state machine does the right thing.
+ script.countryReceived(scenario.getNetworkCountryIsoCode());
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion1 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion1);
+
+ // Simulate receiving an NITZ signal and verify the state machine does the right thing.
+ script.nitzReceived(nitzSignal);
+ PhoneTimeSuggestion expectedTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, nitzSignal);
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion2 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedTimeSuggestion, expectedTimeZoneSuggestion2);
+
+ // Check state that NitzStateMachine must expose.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+
+ // Simulate the country becoming unavailable and verify the state machine does the right
+ // thing.
+ script.countryUnavailable();
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion3 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */, nitzSignal);
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion3);
+
+ // Check state that NitzStateMachine must expose.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+ }
+
+ @Test
+ public void test_networkAvailableClearsCacheNitzAndTimeZoneSuggestion() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ Script script = new Script()
+ .initializeSystemClock(ARBITRARY_SYSTEM_CLOCK_TIME)
+ .networkAvailable();
+
+ // Simulate receiving the country and verify the state machine does the right thing.
+ script.countryReceived(scenario.getNetworkCountryIsoCode());
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion1 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion1);
+
+ // Simulate receiving an NITZ signal and verify the state machine does the right thing.
+ script.nitzReceived(nitzSignal);
+ PhoneTimeSuggestion expectedTimeSuggestion =
+ createTimeSuggestionFromNitzSignal(PHONE_ID, nitzSignal);
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion2 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ script.verifyTimeAndTimeZoneSuggestedAndReset(
+ expectedTimeSuggestion, expectedTimeZoneSuggestion2);
+
+ // Check state that NitzStateMachine must expose.
+ assertEquals(nitzSignal.getValue(), mNitzStateMachineImpl.getCachedNitzData());
+
+ // Simulate network becoming available and verify the state machine does the right thing.
+ script.networkAvailable();
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion3 =
+ mRealTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ script.verifyOnlyTimeZoneWasSuggestedAndReset(expectedTimeZoneSuggestion3);
+
+ // Check state that NitzStateMachine must expose.
+ assertNull(mNitzStateMachineImpl.getCachedNitzData());
+ }
+
+ /**
+ * A "fluent" helper class allowing reuse of logic for test state initialization, simulation of
+ * events, and verification of device state changes with self-describing method names.
+ */
+ private class Script {
+
+ Script() {
+ // Set initial fake device state.
+ mFakeDeviceState.ignoreNitz = false;
+ mFakeDeviceState.nitzUpdateDiffMillis = 2000;
+ mFakeDeviceState.nitzUpdateSpacingMillis = 1000 * 60 * 10;
+
+ mFakeDeviceState.networkCountryIsoForPhone = "";
+ }
+
+ // Initialization methods for setting simulated device state, usually before simulation.
+
+ Script initializeSystemClock(long timeMillis) {
+ mFakeDeviceState.currentTimeMillis = timeMillis;
+ return this;
+ }
+
+ // Simulation methods that are used by tests to pretend that something happens.
+
+ Script incrementTime(int timeIncrementMillis) {
+ mFakeDeviceState.simulateTimeIncrement(timeIncrementMillis);
+ return this;
+ }
+
+ Script networkAvailable() {
+ mNitzStateMachineImpl.handleNetworkAvailable();
+ return this;
+ }
+
+ Script countryUnavailable() {
+ mNitzStateMachineImpl.handleNetworkCountryCodeUnavailable();
+ return this;
+ }
+
+ Script countryReceived(String countryIsoCode) {
+ mFakeDeviceState.networkCountryIsoForPhone = countryIsoCode;
+ mNitzStateMachineImpl.handleNetworkCountryCodeSet(true);
+ return this;
+ }
+
+ Script nitzReceived(TimestampedValue<NitzData> nitzSignal) {
+ mNitzStateMachineImpl.handleNitzReceived(nitzSignal);
+ return this;
+ }
+
+ Script toggleAirplaneMode(boolean on) {
+ mNitzStateMachineImpl.handleAirplaneModeChanged(on);
+ return this;
+ }
+
+ // Verification methods.
+
+ Script verifyOnlyTimeZoneWasSuggestedAndReset(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ justVerifyTimeZoneWasSuggested(timeZoneSuggestion);
+ justVerifyTimeWasNotSuggested();
+ commitStateChanges();
+ return this;
+ }
+
+ Script verifyTimeAndTimeZoneSuggestedAndReset(
+ PhoneTimeSuggestion timeSuggestion, PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ justVerifyTimeZoneWasSuggested(timeZoneSuggestion);
+ justVerifyTimeWasSuggested(timeSuggestion);
+ commitStateChanges();
+ return this;
+ }
+
+ private void justVerifyTimeWasNotSuggested() {
+ mFakeNewTimeServiceHelper.suggestedTimes.assertHasNotBeenSet();
+ }
+
+ private void justVerifyTimeZoneWasSuggested(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ mFakeNewTimeServiceHelper.suggestedTimeZones.assertHasBeenSet();
+ mFakeNewTimeServiceHelper.suggestedTimeZones.assertLatestEquals(timeZoneSuggestion);
+ }
+
+ private void justVerifyTimeWasSuggested(PhoneTimeSuggestion timeSuggestion) {
+ mFakeNewTimeServiceHelper.suggestedTimes.assertChangeCount(1);
+ mFakeNewTimeServiceHelper.suggestedTimes.assertLatestEquals(timeSuggestion);
+ }
+
+ private void commitStateChanges() {
+ mFakeNewTimeServiceHelper.commitState();
+ }
+ }
+
+ /** Some piece of state that tests want to track. */
+ private static class TestState<T> {
+ private T mInitialValue;
+ private LinkedList<T> mValues = new LinkedList<>();
+
+ void init(T value) {
+ mValues.clear();
+ mInitialValue = value;
+ }
+
+ void set(T value) {
+ mValues.addFirst(value);
+ }
+
+ boolean hasBeenSet() {
+ return mValues.size() > 0;
+ }
+
+ void assertHasNotBeenSet() {
+ assertFalse(hasBeenSet());
+ }
+
+ void assertHasBeenSet() {
+ assertTrue(hasBeenSet());
+ }
+
+ void commitLatest() {
+ if (hasBeenSet()) {
+ mInitialValue = mValues.getLast();
+ mValues.clear();
+ }
+ }
+
+ void assertLatestEquals(T expected) {
+ assertEquals(expected, getLatest());
+ }
+
+ void assertChangeCount(int expectedCount) {
+ assertEquals(expectedCount, mValues.size());
+ }
+
+ public T getLatest() {
+ if (hasBeenSet()) {
+ return mValues.getFirst();
+ }
+ return mInitialValue;
+ }
+ }
+
+ /**
+ * A fake implementation of {@link NewTimeServiceHelper} that enables tests to detect what
+ * {@link NewNitzStateMachineImpl} would do to a real device's state.
+ */
+ private static class FakeNewTimeServiceHelper implements NewTimeServiceHelper {
+
+ private final FakeDeviceState mFakeDeviceState;
+
+ // State we want to track.
+ public final TestState<PhoneTimeSuggestion> suggestedTimes = new TestState<>();
+ public final TestState<PhoneTimeZoneSuggestion> suggestedTimeZones = new TestState<>();
+
+ FakeNewTimeServiceHelper(FakeDeviceState fakeDeviceState) {
+ mFakeDeviceState = fakeDeviceState;
+ }
+
+ @Override
+ public void suggestDeviceTime(PhoneTimeSuggestion timeSuggestion) {
+ suggestedTimes.set(timeSuggestion);
+ // The fake time service just uses the latest suggestion.
+ mFakeDeviceState.currentTimeMillis = timeSuggestion.getUtcTime().getValue();
+ }
+
+ @Override
+ public void maybeSuggestDeviceTimeZone(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ suggestedTimeZones.set(timeZoneSuggestion);
+ }
+
+ @Override
+ public void dumpLogs(IndentingPrintWriter ipw) {
+ // No-op in tests
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ // No-op in tests
+ }
+
+ void commitState() {
+ suggestedTimeZones.commitLatest();
+ suggestedTimes.commitLatest();
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactoryTest.java
new file mode 100644
index 0000000000..3e69d23d5e
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/NitzSignalInputFilterPredicateFactoryTest.java
@@ -0,0 +1,333 @@
+/*
+ * 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.nitz;
+
+import static com.android.internal.telephony.NitzStateMachineTestSupport.UNIQUE_US_ZONE_SCENARIO1;
+import static com.android.internal.telephony.nitz.NitzSignalInputFilterPredicateFactory.createBogusElapsedRealtimeCheck;
+import static com.android.internal.telephony.nitz.NitzSignalInputFilterPredicateFactory.createIgnoreNitzPropertyCheck;
+import static com.android.internal.telephony.nitz.NitzSignalInputFilterPredicateFactory.createRateLimitCheck;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.NitzData;
+import com.android.internal.telephony.NitzStateMachineTestSupport.FakeDeviceState;
+import com.android.internal.telephony.NitzStateMachineTestSupport.Scenario;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nitz.NitzSignalInputFilterPredicateFactory.NitzSignalInputFilterPredicateImpl;
+import com.android.internal.telephony.nitz.NitzSignalInputFilterPredicateFactory.TrivalentPredicate;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NitzSignalInputFilterPredicateFactoryTest extends TelephonyTest {
+
+ private FakeDeviceState mFakeDeviceState;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp("NitzSignalInputFilterPredicateFactoryTest");
+ mFakeDeviceState = new FakeDeviceState();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testNitzSignalInputFilterPredicateImpl_nullSecondArgumentRejected() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ TrivalentPredicate[] triPredicates = {};
+ NitzSignalInputFilterPredicateImpl impl =
+ new NitzSignalInputFilterPredicateImpl(triPredicates);
+ try {
+ impl.mustProcessNitzSignal(nitzSignal, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void testNitzSignalInputFilterPredicateImpl_defaultIsTrue() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal = scenario
+ .createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ NitzSignalInputFilterPredicateImpl impl =
+ new NitzSignalInputFilterPredicateImpl(new TrivalentPredicate[0]);
+ assertTrue(impl.mustProcessNitzSignal(null, nitzSignal));
+ }
+
+ @Test
+ public void testNitzSignalInputFilterPredicateImpl_nullIsIgnored() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ TrivalentPredicate nullPredicate = (x, y) -> null;
+ TrivalentPredicate[] triPredicates = { nullPredicate };
+ NitzSignalInputFilterPredicateImpl impl =
+ new NitzSignalInputFilterPredicateImpl(triPredicates);
+ assertTrue(impl.mustProcessNitzSignal(null, nitzSignal));
+ }
+
+ @Test
+ public void testNitzSignalInputFilterPredicateImpl_trueIsHonored() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ TrivalentPredicate nullPredicate = (x, y) -> null;
+ TrivalentPredicate truePredicate = (x, y) -> true;
+ TrivalentPredicate exceptionPredicate = (x, y) -> {
+ throw new RuntimeException();
+ };
+ TrivalentPredicate[] triPredicates = {
+ nullPredicate,
+ truePredicate,
+ exceptionPredicate,
+ };
+ NitzSignalInputFilterPredicateImpl impl =
+ new NitzSignalInputFilterPredicateImpl(triPredicates);
+ assertTrue(impl.mustProcessNitzSignal(null, nitzSignal));
+ }
+
+ @Test
+ public void testNitzSignalInputFilterPredicateImpl_falseIsHonored() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ TrivalentPredicate nullPredicate = (x, y) -> null;
+ TrivalentPredicate falsePredicate = (x, y) -> false;
+ TrivalentPredicate exceptionPredicate = (x, y) -> {
+ throw new RuntimeException();
+ };
+ TrivalentPredicate[] triPredicates = {
+ nullPredicate,
+ falsePredicate,
+ exceptionPredicate,
+ };
+ NitzSignalInputFilterPredicateImpl impl =
+ new NitzSignalInputFilterPredicateImpl(triPredicates);
+ assertFalse(impl.mustProcessNitzSignal(null, nitzSignal));
+ }
+
+ @Test
+ public void testTrivalentPredicate_ignoreNitzPropertyCheck() {
+ TrivalentPredicate triPredicate = createIgnoreNitzPropertyCheck(mFakeDeviceState);
+
+ mFakeDeviceState.ignoreNitz = true;
+ assertFalse(triPredicate.mustProcessNitzSignal(null, null));
+
+ mFakeDeviceState.ignoreNitz = false;
+ assertNull(triPredicate.mustProcessNitzSignal(null, null));
+ }
+
+ @Test
+ public void testTrivalentPredicate_bogusElapsedRealtimeCheck() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ long elapsedRealtimeClock = mFakeDeviceState.elapsedRealtime();
+ TimestampedValue<NitzData> nitzSignal = scenario.createNitzSignal(elapsedRealtimeClock);
+
+ TrivalentPredicate triPredicate =
+ createBogusElapsedRealtimeCheck(mContext, mFakeDeviceState);
+ assertNull(triPredicate.mustProcessNitzSignal(null, nitzSignal));
+
+ // Any signal that claims to be from the future must be rejected.
+ TimestampedValue<NitzData> bogusNitzSignal = new TimestampedValue<>(
+ elapsedRealtimeClock + 1, nitzSignal.getValue());
+ assertFalse(triPredicate.mustProcessNitzSignal(null, bogusNitzSignal));
+ }
+
+ @Test
+ public void testTrivalentPredicate_noOldSignalCheck() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ TrivalentPredicate triPredicate =
+ NitzSignalInputFilterPredicateFactory.createNoOldSignalCheck();
+ assertTrue(triPredicate.mustProcessNitzSignal(null, nitzSignal));
+ assertNull(triPredicate.mustProcessNitzSignal(nitzSignal, nitzSignal));
+ }
+
+ @Test
+ public void testTrivalentPredicate_rateLimitCheck_elapsedRealtime() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ int nitzSpacingThreshold = mFakeDeviceState.getNitzUpdateSpacingMillis();
+ NitzData baseNitzData = scenario.createNitzData();
+
+ TrivalentPredicate triPredicate = createRateLimitCheck(mFakeDeviceState);
+
+ long baseElapsedRealtimeMillis = mFakeDeviceState.elapsedRealtime();
+ TimestampedValue<NitzData> baseSignal =
+ new TimestampedValue<>(baseElapsedRealtimeMillis, baseNitzData);
+
+ // Two identical signals: no spacing so the new signal should not be processed.
+ {
+ assertFalse(triPredicate.mustProcessNitzSignal(baseSignal, baseSignal));
+ }
+
+ // Two signals not spaced apart enough: the new signal should not processed.
+ {
+ int elapsedTimeIncrement = nitzSpacingThreshold - 1;
+ TimestampedValue<NitzData> newSignal =
+ createIncrementedNitzSignal(baseSignal, elapsedTimeIncrement);
+ assertFalse(triPredicate.mustProcessNitzSignal(baseSignal, newSignal));
+ }
+
+ // Two signals spaced apart: the new signal should be processed.
+ {
+ int elapsedTimeIncrement = nitzSpacingThreshold + 1;
+ TimestampedValue<NitzData> newSignal =
+ createIncrementedNitzSignal(baseSignal, elapsedTimeIncrement);
+ assertTrue(triPredicate.mustProcessNitzSignal(baseSignal, newSignal));
+ }
+ }
+
+ @Test
+ public void testTrivalentPredicate_rateLimitCheck_offsetDifference() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ int nitzSpacingThreshold = mFakeDeviceState.getNitzUpdateSpacingMillis();
+ NitzData baseNitzData = scenario.createNitzData();
+
+ TrivalentPredicate triPredicate = createRateLimitCheck(mFakeDeviceState);
+
+ long baseElapsedRealtimeMillis = mFakeDeviceState.elapsedRealtime();
+ TimestampedValue<NitzData> baseSignal =
+ new TimestampedValue<>(baseElapsedRealtimeMillis, baseNitzData);
+
+ // Create a new NitzSignal that should be filtered.
+ int elapsedTimeIncrement = nitzSpacingThreshold - 1;
+ TimestampedValue<NitzData> intermediateNitzSignal =
+ createIncrementedNitzSignal(baseSignal, elapsedTimeIncrement);
+ NitzData intermediateNitzData = intermediateNitzSignal.getValue();
+ assertFalse(triPredicate.mustProcessNitzSignal(baseSignal, intermediateNitzSignal));
+
+ // Two signals spaced apart so that the second would be filtered, but they contain different
+ // offset information so should be detected as "different" and processed.
+ {
+ // Modifying the local offset should be enough to recognize the NitzData as different.
+ NitzData differentOffsetNitzData = NitzData.createForTests(
+ intermediateNitzData.getLocalOffsetMillis() + 1,
+ intermediateNitzData.getDstAdjustmentMillis(),
+ intermediateNitzData.getCurrentTimeInMillis(),
+ intermediateNitzData.getEmulatorHostTimeZone());
+ TimestampedValue<NitzData> differentOffsetSignal = new TimestampedValue<>(
+ baseSignal.getReferenceTimeMillis() + elapsedTimeIncrement,
+ differentOffsetNitzData);
+ assertTrue(triPredicate.mustProcessNitzSignal(baseSignal, differentOffsetSignal));
+ }
+ }
+
+ @Test
+ public void testTrivalentPredicate_rateLimitCheck_utcTimeDifferences() {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ int nitzSpacingThreshold = mFakeDeviceState.getNitzUpdateSpacingMillis();
+ int nitzUtcDiffThreshold = mFakeDeviceState.getNitzUpdateDiffMillis();
+ NitzData baseNitzData = scenario.createNitzData();
+
+ TrivalentPredicate triPredicate = createRateLimitCheck(mFakeDeviceState);
+
+ long baseElapsedRealtimeMillis = mFakeDeviceState.elapsedRealtime();
+ TimestampedValue<NitzData> baseSignal =
+ new TimestampedValue<>(baseElapsedRealtimeMillis, baseNitzData);
+
+ // Create a new NitzSignal that should be filtered.
+ int elapsedTimeIncrement = nitzSpacingThreshold - 1;
+ TimestampedValue<NitzData> intermediateSignal =
+ createIncrementedNitzSignal(baseSignal, elapsedTimeIncrement);
+ NitzData intermediateNitzData = intermediateSignal.getValue();
+ assertFalse(triPredicate.mustProcessNitzSignal(baseSignal, intermediateSignal));
+
+ // Two signals spaced apart so that the second would normally be filtered and it contains
+ // a UTC time that is not sufficiently different.
+ {
+ NitzData incrementedUtcTimeNitzData = NitzData.createForTests(
+ intermediateNitzData.getLocalOffsetMillis(),
+ intermediateNitzData.getDstAdjustmentMillis(),
+ intermediateNitzData.getCurrentTimeInMillis() + nitzUtcDiffThreshold - 1,
+ intermediateNitzData.getEmulatorHostTimeZone());
+
+ TimestampedValue<NitzData> incrementedNitzSignal = new TimestampedValue<>(
+ intermediateSignal.getReferenceTimeMillis(), incrementedUtcTimeNitzData);
+ assertFalse(triPredicate.mustProcessNitzSignal(baseSignal, incrementedNitzSignal));
+ }
+
+ // Two signals spaced apart so that the second would normally be filtered but it contains
+ // a UTC time that is sufficiently different.
+ {
+ NitzData incrementedUtcTimeNitzData = NitzData.createForTests(
+ intermediateNitzData.getLocalOffsetMillis(),
+ intermediateNitzData.getDstAdjustmentMillis(),
+ intermediateNitzData.getCurrentTimeInMillis() + nitzUtcDiffThreshold + 1,
+ intermediateNitzData.getEmulatorHostTimeZone());
+
+ TimestampedValue<NitzData> incrementedNitzSignal = new TimestampedValue<>(
+ intermediateSignal.getReferenceTimeMillis(), incrementedUtcTimeNitzData);
+ assertTrue(triPredicate.mustProcessNitzSignal(baseSignal, incrementedNitzSignal));
+ }
+
+ // Two signals spaced apart so that the second would normally be filtered and it contains
+ // a UTC time that is not sufficiently different.
+ {
+ NitzData decrementedUtcTimeNitzData = NitzData.createForTests(
+ intermediateNitzData.getLocalOffsetMillis(),
+ intermediateNitzData.getDstAdjustmentMillis(),
+ intermediateNitzData.getCurrentTimeInMillis() - nitzUtcDiffThreshold + 1,
+ intermediateNitzData.getEmulatorHostTimeZone());
+
+ TimestampedValue<NitzData> decrementedNitzSignal = new TimestampedValue<>(
+ intermediateSignal.getReferenceTimeMillis(), decrementedUtcTimeNitzData);
+ assertFalse(triPredicate.mustProcessNitzSignal(baseSignal, decrementedNitzSignal));
+ }
+
+ // Two signals spaced apart so that the second would normally be filtered but it contains
+ // a UTC time that is sufficiently different.
+ {
+ NitzData decrementedUtcTimeNitzData = NitzData.createForTests(
+ intermediateNitzData.getLocalOffsetMillis(),
+ intermediateNitzData.getDstAdjustmentMillis(),
+ intermediateNitzData.getCurrentTimeInMillis() + nitzUtcDiffThreshold + 1,
+ intermediateNitzData.getEmulatorHostTimeZone());
+
+ TimestampedValue<NitzData> decrementedNitzSignal = new TimestampedValue<>(
+ intermediateSignal.getReferenceTimeMillis(), decrementedUtcTimeNitzData);
+ assertTrue(triPredicate.mustProcessNitzSignal(baseSignal, decrementedNitzSignal));
+ }
+ }
+
+ /**
+ * Creates an NITZ signal based on the the supplied signal but with all the fields related to
+ * elapsed time incremented by the specified number of milliseconds.
+ */
+ private static TimestampedValue<NitzData> createIncrementedNitzSignal(
+ TimestampedValue<NitzData> baseSignal, int incrementMillis) {
+ NitzData baseData = baseSignal.getValue();
+ return new TimestampedValue<>(baseSignal.getReferenceTimeMillis() + incrementMillis,
+ NitzData.createForTests(
+ baseData.getLocalOffsetMillis(),
+ baseData.getDstAdjustmentMillis(),
+ baseData.getCurrentTimeInMillis() + incrementMillis,
+ baseData.getEmulatorHostTimeZone()));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneSuggesterImplTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneSuggesterImplTest.java
new file mode 100644
index 0000000000..cdd30e4f44
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/TimeZoneSuggesterImplTest.java
@@ -0,0 +1,644 @@
+/*
+ * 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.nitz;
+
+import static com.android.internal.telephony.NitzStateMachineTestSupport.ARBITRARY_REALTIME_MILLIS;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.CZECHIA_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.NEW_ZEALAND_COUNTRY_DEFAULT_ZONE_ID;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.NEW_ZEALAND_DEFAULT_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.NEW_ZEALAND_OTHER_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.NON_UNIQUE_US_ZONE_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.NON_UNIQUE_US_ZONE_SCENARIO_ZONES;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.UNIQUE_US_ZONE_SCENARIO1;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.UNIQUE_US_ZONE_SCENARIO2;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.UNITED_KINGDOM_SCENARIO;
+import static com.android.internal.telephony.NitzStateMachineTestSupport.US_COUNTRY_DEFAULT_ZONE_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.util.TimestampedValue;
+
+import com.android.internal.telephony.NitzData;
+import com.android.internal.telephony.NitzStateMachineTestSupport.FakeDeviceState;
+import com.android.internal.telephony.NitzStateMachineTestSupport.Scenario;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.TimeZoneLookupHelper;
+import com.android.internal.telephony.nitz.NewNitzStateMachineImpl.TimeZoneSuggester;
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class TimeZoneSuggesterImplTest extends TelephonyTest {
+
+ private static final int PHONE_ID = 99999;
+ private static final PhoneTimeZoneSuggestion EMPTY_TIME_ZONE_SUGGESTION =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+
+ private FakeDeviceState mFakeDeviceState;
+ private TimeZoneSuggester mTimeZoneSuggester;
+
+ @Before
+ public void setUp() throws Exception {
+ TelephonyTest.logd("TimeZoneSuggesterImplTest +Setup!");
+ super.setUp("TimeZoneSuggesterImplTest");
+
+ // In tests a fake impl is used for DeviceState, which allows historic data to be used.
+ mFakeDeviceState = new FakeDeviceState();
+
+ // In tests the real TimeZoneLookupHelper implementation is used: this makes it easy to
+ // construct tests using known historic examples.
+ TimeZoneLookupHelper timeZoneLookupHelper = new TimeZoneLookupHelper();
+ mTimeZoneSuggester = new TimeZoneSuggesterImpl(mFakeDeviceState, timeZoneLookupHelper);
+
+ TelephonyTest.logd("TimeZoneSuggesterImplTest -Setup!");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void test_emptySuggestionForNullCountryNullNitz() throws Exception {
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION,
+ mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */, null /* nitzSignal */));
+ }
+
+ @Test
+ public void test_emptySuggestionForNullCountryWithNitz() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(ARBITRARY_REALTIME_MILLIS);
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION,
+ mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */, nitzSignal));
+ }
+
+ @Test
+ public void test_emptySuggestionForEmptyCountryNullNitz() throws Exception {
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION,
+ mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, "" /* countryIsoCoe */, null /* nitzSignal */));
+ }
+
+ /**
+ * Tests behavior for various scenarios for a user in the US. The US is a complicated case
+ * with multiple time zones, some overlapping and with no good default. The scenario used here
+ * is a "unique" scenario, meaning it is possible to determine the correct zone using both
+ * country and NITZ information.
+ */
+ @Test
+ public void test_uniqueUsZone() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+
+ // Country won't be enough to get a quality result for time zone detection but a suggestion
+ // will be made.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(US_COUNTRY_DEFAULT_ZONE_ID);
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // NITZ with a "" country code is interpreted as a test network so only offset is used
+ // to get a match.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, "" /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(PHONE_ID, actualSuggestion.getPhoneId());
+ assertEquals(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY,
+ actualSuggestion.getMatchType());
+ assertEquals(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ actualSuggestion.getQuality());
+ }
+
+ // NITZ alone is not enough to get a result when the country is not available.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION, actualSuggestion);
+ }
+
+ // Country + NITZ is enough for a unique time zone detection result for this scenario.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // Country + NITZ with a bad offset should not trigger fall back, country-only behavior
+ // since there are multiple zones to choose from.
+ {
+ // We use an NITZ from CZ to generate an NITZ signal with a bad offset.
+ TimestampedValue<NitzData> badNitzSignal =
+ CZECHIA_SCENARIO.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion = EMPTY_TIME_ZONE_SUGGESTION;
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ badNitzSignal);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+ }
+
+ /**
+ * Tests behavior for various scenarios for a user in the US. The US is a complicated case
+ * with multiple time zones, some overlapping and with no good default. The scenario used here
+ * is a "non unique" scenario, meaning it is not possible to determine the a single zone using
+ * both country and NITZ information.
+ */
+ @Test
+ public void test_nonUniqueUsZone() throws Exception {
+ Scenario scenario = NON_UNIQUE_US_ZONE_SCENARIO;
+
+ // Country won't be enough to get a quality result for time zone detection but a suggestion
+ // will be made.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(US_COUNTRY_DEFAULT_ZONE_ID);
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // NITZ with a "" country code is interpreted as a test network so only offset is used
+ // to get a match.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, "" /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(PHONE_ID, actualSuggestion.getPhoneId());
+ assertEquals(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY,
+ actualSuggestion.getMatchType());
+ assertEquals(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ actualSuggestion.getQuality());
+ }
+
+ // NITZ alone is not enough to get a result when the country is not available.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION, actualSuggestion);
+ }
+
+ // Country + NITZ is not enough for a unique time zone detection result for this scenario.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(PHONE_ID, actualSuggestion.getPhoneId());
+ assertEquals(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET,
+ actualSuggestion.getMatchType());
+ assertEquals(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ actualSuggestion.getQuality());
+ List<String> allowedZoneIds = Arrays.asList(NON_UNIQUE_US_ZONE_SCENARIO_ZONES);
+ assertTrue(allowedZoneIds.contains(actualSuggestion.getZoneId()));
+ }
+
+ // Country + NITZ with a bad offset should not trigger fall back, country-only behavior
+ // since there are multiple zones to choose from.
+ {
+ // We use an NITZ from CZ to generate an NITZ signal with a bad offset.
+ TimestampedValue<NitzData> badNitzSignal =
+ CZECHIA_SCENARIO.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion = EMPTY_TIME_ZONE_SUGGESTION;
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ badNitzSignal);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+ }
+
+ /**
+ * Tests behavior for various scenarios for a user in the UK. The UK is simple: it has a single
+ * time zone so only the country needs to be known to find a time zone. It is special in that
+ * it uses UTC for some of the year, which makes it difficult to detect bogus NITZ signals with
+ * zero'd offset information.
+ */
+ @Test
+ public void test_unitedKingdom() throws Exception {
+ Scenario scenario = UNITED_KINGDOM_SCENARIO;
+
+ // Country alone is enough to guess the time zone.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // NITZ with a "" country code is interpreted as a test network so only offset is used
+ // to get a match.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, "" /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(PHONE_ID, actualSuggestion.getPhoneId());
+ assertEquals(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY,
+ actualSuggestion.getMatchType());
+ assertEquals(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ actualSuggestion.getQuality());
+
+ }
+
+ // NITZ alone is not enough to get a result when the country is not available.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION, actualSuggestion);
+ }
+
+ // Country + NITZ is enough for both time + time zone detection.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // Country + NITZ with a bad offset should trigger fall back, country-only behavior since
+ // there's only one zone.
+ {
+ // We use an NITZ from Czechia to generate an NITZ signal with a bad offset.
+ TimestampedValue<NitzData> badNitzSignal =
+ CZECHIA_SCENARIO.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ badNitzSignal);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+ }
+
+ /**
+ * Tests behavior for various scenarios for a user in Czechia. CZ is simple: it has a single
+ * time zone so only the country needs to be known to find a time zone. It never uses UTC so it
+ * is useful to contrast with the UK and can be used for bogus signal detection.
+ */
+ @Test
+ public void test_cz() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+
+ // Country alone is enough to guess the time zone.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // NITZ with a "" country code is interpreted as a test network so only offset is used
+ // to get a match.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion =
+ mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, "" /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(PHONE_ID, actualSuggestion.getPhoneId());
+ assertEquals(PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY,
+ actualSuggestion.getMatchType());
+ assertEquals(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ actualSuggestion.getQuality());
+
+ }
+
+ // NITZ alone is not enough to get a result when the country is not available.
+ {
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, null /* countryIsoCode */,
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION, actualSuggestion);
+ }
+
+ // Country + NITZ is enough for both time + time zone detection.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime()));
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // Country + NITZ with a bad offset should trigger fall back, country-only behavior since
+ // there's only one zone.
+ {
+ // We use an NITZ from the US to generate an NITZ signal with a bad offset.
+ TimestampedValue<NitzData> badNitzSignal =
+ UNIQUE_US_ZONE_SCENARIO1.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(),
+ badNitzSignal);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+ }
+
+ @Test
+ public void test_bogusCzNitzSignal() throws Exception {
+ Scenario scenario = CZECHIA_SCENARIO;
+
+ // Country alone is enough to guess the time zone.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.SINGLE_ZONE);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // NITZ + bogus NITZ is not enough to get a result.
+ {
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ TimestampedValue<NitzData> goodNitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), badNitzSignal);
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION, actualSuggestion);
+ }
+ }
+
+ @Test
+ public void test_bogusUniqueUsNitzSignal() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+
+ // Country alone is not enough to guess the time zone.
+ {
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(US_COUNTRY_DEFAULT_ZONE_ID);
+ expectedTimeZoneSuggestion.setMatchType(
+ PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedTimeZoneSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ // NITZ + bogus NITZ is not enough to get a result.
+ {
+ // Create a corrupted NITZ signal, where the offset information has been lost.
+ TimestampedValue<NitzData> goodNitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ NitzData bogusNitzData = NitzData.createForTests(
+ 0 /* UTC! */, null /* dstOffsetMillis */,
+ goodNitzSignal.getValue().getCurrentTimeInMillis(),
+ null /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> badNitzSignal = new TimestampedValue<>(
+ goodNitzSignal.getReferenceTimeMillis(), bogusNitzData);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), badNitzSignal);
+ assertEquals(EMPTY_TIME_ZONE_SUGGESTION, actualSuggestion);
+ }
+ }
+
+ @Test
+ public void test_emulatorNitzExtensionUsedForTimeZone() throws Exception {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+
+ TimestampedValue<NitzData> originalNitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+
+ // Create an NITZ signal with an explicit time zone (as can happen on emulators).
+ NitzData originalNitzData = originalNitzSignal.getValue();
+
+ // A time zone that is obviously not in the US, but because the explicit value is present it
+ // should not be questioned.
+ String emulatorTimeZoneId = "Europe/London";
+ NitzData emulatorNitzData = NitzData.createForTests(
+ originalNitzData.getLocalOffsetMillis(),
+ originalNitzData.getDstAdjustmentMillis(),
+ originalNitzData.getCurrentTimeInMillis(),
+ java.util.TimeZone.getTimeZone(emulatorTimeZoneId) /* emulatorHostTimeZone */);
+ TimestampedValue<NitzData> emulatorNitzSignal = new TimestampedValue<>(
+ originalNitzSignal.getReferenceTimeMillis(), emulatorNitzData);
+
+ PhoneTimeZoneSuggestion expectedTimeZoneSuggestion =
+ new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedTimeZoneSuggestion.setZoneId(emulatorTimeZoneId);
+ expectedTimeZoneSuggestion.setMatchType(PhoneTimeZoneSuggestion.EMULATOR_ZONE_ID);
+ expectedTimeZoneSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), emulatorNitzSignal);
+ assertEquals(expectedTimeZoneSuggestion, actualSuggestion);
+ }
+
+ @Test
+ public void test_countryDefaultBoost() throws Exception {
+ // Demonstrate the defaultTimeZoneBoost behavior: we can get a zone only from the
+ // countryIsoCode.
+ {
+ Scenario scenario = NEW_ZEALAND_DEFAULT_SCENARIO;
+ PhoneTimeZoneSuggestion expectedSuggestion = new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedSuggestion.setZoneId(NEW_ZEALAND_COUNTRY_DEFAULT_ZONE_ID);
+ expectedSuggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+
+ // Confirm what happens when NITZ is correct for the country default.
+ {
+ Scenario scenario = NEW_ZEALAND_DEFAULT_SCENARIO;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedSuggestion = new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedSuggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ expectedSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+
+ // A valid NITZ signal for the non-default zone should still be correctly detected.
+ {
+ Scenario scenario = NEW_ZEALAND_OTHER_SCENARIO;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedSuggestion = new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedSuggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ expectedSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+
+ // Demonstrate what happens with a bogus NITZ for NZ: because the default zone is boosted
+ // then we should return to the country default zone.
+ {
+ Scenario scenario = NEW_ZEALAND_DEFAULT_SCENARIO;
+ // Use a scenario that has a different offset than NZ to generate the NITZ signal.
+ TimestampedValue<NitzData> nitzSignal =
+ CZECHIA_SCENARIO.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedSuggestion = new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedSuggestion.setZoneId(NEW_ZEALAND_COUNTRY_DEFAULT_ZONE_ID);
+ expectedSuggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+ }
+
+ @Test
+ public void test_noCountryDefaultBoost() throws Exception {
+ // Demonstrate the behavior without default country boost for a country with multiple zones:
+ // we cannot get a zone only from the countryIsoCode.
+ {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ PhoneTimeZoneSuggestion expectedSuggestion = new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedSuggestion.setZoneId(US_COUNTRY_DEFAULT_ZONE_ID);
+ expectedSuggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ expectedSuggestion.setQuality(
+ PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), null /* nitzSignal */);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+
+ // Confirm what happens when NITZ is correct for the country default.
+ {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedSuggestion = new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedSuggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ expectedSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+
+ // A valid NITZ signal for the non-default zone should still be correctly detected.
+ {
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO2;
+ TimestampedValue<NitzData> nitzSignal =
+ scenario.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedSuggestion = new PhoneTimeZoneSuggestion(PHONE_ID);
+ expectedSuggestion.setZoneId(scenario.getTimeZoneId());
+ expectedSuggestion.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET);
+ expectedSuggestion.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+
+ // Demonstrate what happens with a bogus NITZ for US: because the default zone is not
+ // boosted we should not get a suggestion.
+ {
+ // A scenario that has a different offset than US.
+ Scenario scenario = UNIQUE_US_ZONE_SCENARIO1;
+ // Use a scenario that has a different offset than the US to generate the NITZ signal.
+ TimestampedValue<NitzData> nitzSignal =
+ CZECHIA_SCENARIO.createNitzSignal(mFakeDeviceState.elapsedRealtime());
+ PhoneTimeZoneSuggestion expectedSuggestion = EMPTY_TIME_ZONE_SUGGESTION;
+ PhoneTimeZoneSuggestion actualSuggestion = mTimeZoneSuggester.getTimeZoneSuggestion(
+ PHONE_ID, scenario.getNetworkCountryIsoCode(), nitzSignal);
+ assertEquals(expectedSuggestion, actualSuggestion);
+ }
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestionTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestionTest.java
new file mode 100644
index 0000000000..54838aec03
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/service/PhoneTimeZoneSuggestionTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.nitz.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.junit.Test;
+
+public class PhoneTimeZoneSuggestionTest {
+ private static final int PHONE_ID = 99999;
+
+ @Test
+ public void testEquals() {
+ PhoneTimeZoneSuggestion one = new PhoneTimeZoneSuggestion(PHONE_ID);
+ assertEquals(one, one);
+
+ PhoneTimeZoneSuggestion two = new PhoneTimeZoneSuggestion(PHONE_ID);
+ assertEquals(one, two);
+ assertEquals(two, one);
+
+ PhoneTimeZoneSuggestion three = new PhoneTimeZoneSuggestion(PHONE_ID + 1);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+
+ one.setZoneId("Europe/London");
+ assertNotEquals(one, two);
+ two.setZoneId("Europe/Paris");
+ assertNotEquals(one, two);
+ one.setZoneId(two.getZoneId());
+ assertEquals(one, two);
+
+ one.setMatchType(PhoneTimeZoneSuggestion.EMULATOR_ZONE_ID);
+ two.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ assertNotEquals(one, two);
+ one.setMatchType(PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY);
+ assertEquals(one, two);
+
+ one.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ two.setQuality(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+ assertNotEquals(one, two);
+ one.setQuality(PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+ assertEquals(one, two);
+
+ // DebugInfo must not be considered in equals().
+ one.addDebugInfo("Debug info 1");
+ two.addDebugInfo("Debug info 2");
+ assertEquals(one, two);
+ }
+
+ @Test
+ public void testParcelable() {
+ PhoneTimeZoneSuggestion one = new PhoneTimeZoneSuggestion(PHONE_ID);
+ assertEquals(one, roundTripParcelable(one));
+
+ one.setZoneId("Europe/London");
+ one.setMatchType(PhoneTimeZoneSuggestion.EMULATOR_ZONE_ID);
+ one.setQuality(PhoneTimeZoneSuggestion.SINGLE_ZONE);
+ assertEquals(one, roundTripParcelable(one));
+
+ // DebugInfo should also be stored (but is not checked by equals()
+ one.addDebugInfo("This is debug info");
+ PhoneTimeZoneSuggestion two = roundTripParcelable(one);
+ assertEquals(one.getDebugInfo(), two.getDebugInfo());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Parcelable> T roundTripParcelable(T one) {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeTypedObject(one, 0);
+ parcel.setDataPosition(0);
+
+ T toReturn = (T) parcel.readTypedObject(PhoneTimeZoneSuggestion.CREATOR);
+ parcel.recycle();
+ return toReturn;
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceTest.java
new file mode 100644
index 0000000000..f268501020
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/service/TimeZoneDetectionServiceTest.java
@@ -0,0 +1,587 @@
+/*
+ * 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.nitz.service;
+
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.EMULATOR_ZONE_ID;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.NETWORK_COUNTRY_AND_OFFSET;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.NETWORK_COUNTRY_ONLY;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.SINGLE_ZONE;
+import static com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.TEST_NETWORK_OFFSET_ONLY;
+import static com.android.internal.telephony.nitz.service.TimeZoneDetectionService.SCORE_HIGH;
+import static com.android.internal.telephony.nitz.service.TimeZoneDetectionService.SCORE_HIGHEST;
+import static com.android.internal.telephony.nitz.service.TimeZoneDetectionService.SCORE_LOW;
+import static com.android.internal.telephony.nitz.service.TimeZoneDetectionService.SCORE_MEDIUM;
+import static com.android.internal.telephony.nitz.service.TimeZoneDetectionService.SCORE_NONE;
+import static com.android.internal.telephony.nitz.service.TimeZoneDetectionService.SCORE_USAGE_THRESHOLD;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.MatchType;
+import com.android.internal.telephony.nitz.service.PhoneTimeZoneSuggestion.Quality;
+import com.android.internal.telephony.nitz.service.TimeZoneDetectionService.QualifiedPhoneTimeZoneSuggestion;
+import com.android.internal.util.IndentingPrintWriter;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+
+/**
+ * White-box unit tests for {@link TimeZoneDetectionService}.
+ */
+public class TimeZoneDetectionServiceTest {
+
+ private static final int PHONE1_ID = 10000;
+ private static final int PHONE2_ID = 20000;
+
+ // Suggestion test cases are ordered so that each successive one is of the same or higher score
+ // than the previous.
+ private static final SuggestionTestCase[] TEST_CASES = new SuggestionTestCase[] {
+ newTestCase(NETWORK_COUNTRY_ONLY, MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW),
+ newTestCase(NETWORK_COUNTRY_ONLY, MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_MEDIUM),
+ newTestCase(NETWORK_COUNTRY_AND_OFFSET, MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_MEDIUM),
+ newTestCase(NETWORK_COUNTRY_ONLY, SINGLE_ZONE, SCORE_HIGH),
+ newTestCase(NETWORK_COUNTRY_AND_OFFSET, SINGLE_ZONE, SCORE_HIGH),
+ newTestCase(TEST_NETWORK_OFFSET_ONLY, MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_HIGHEST),
+ newTestCase(EMULATOR_ZONE_ID, SINGLE_ZONE, SCORE_HIGHEST),
+ };
+
+ private TimeZoneDetectionService mTimeZoneDetectionService;
+ private FakeTimeZoneDetectionServiceHelper mFakeTimeZoneDetectionServiceHelper;
+
+ @Before
+ public void setUp() {
+ mFakeTimeZoneDetectionServiceHelper = new FakeTimeZoneDetectionServiceHelper();
+ mTimeZoneDetectionService =
+ new TimeZoneDetectionService(mFakeTimeZoneDetectionServiceHelper);
+ }
+
+ @Test
+ public void testEmptySuggestions() {
+ PhoneTimeZoneSuggestion phone1TimeZoneSuggestion = createEmptyPhone1Suggestion();
+ PhoneTimeZoneSuggestion phone2TimeZoneSuggestion = createEmptyPhone2Suggestion();
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true)
+ .initializeTimeZoneSetting(true);
+
+ script.suggestPhoneTimeZone(phone1TimeZoneSuggestion)
+ .verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedPhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, SCORE_NONE);
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertNull(mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE2_ID));
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+
+ script.suggestPhoneTimeZone(phone2TimeZoneSuggestion)
+ .verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedPhone2ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, SCORE_NONE);
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedPhone2ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE2_ID));
+ // Phone 1 should always beat phone 2, all other things being equal.
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+ }
+
+ @Test
+ public void testFirstPlausibleSuggestionAcceptedWhenTimeZoneUninitialized() {
+ SuggestionTestCase testCase =
+ newTestCase(NETWORK_COUNTRY_ONLY, MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW);
+ PhoneTimeZoneSuggestion lowQualitySuggestion =
+ testCase.createSuggestion(PHONE1_ID, "America/New_York");
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true);
+
+ // The device is uninitialized.
+ script.initializeTimeZoneSetting(false);
+
+ // The very first suggestion will be taken.
+ script.suggestPhoneTimeZone(lowQualitySuggestion)
+ .verifyTimeZoneSetAndReset(lowQualitySuggestion);
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion, testCase.expectedScore);
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+
+ // Another low quality suggestion will be ignored now that the setting is initialized.
+ PhoneTimeZoneSuggestion lowQualitySuggestion2 =
+ testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
+ script.suggestPhoneTimeZone(lowQualitySuggestion2)
+ .verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion2 =
+ new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion2, testCase.expectedScore);
+ assertEquals(expectedScoredSuggestion2,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion2,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+ }
+
+ @Test
+ public void testTogglingTimeZoneDetection() {
+ Script script = new Script()
+ .initializeTimeZoneSetting(true);
+
+ boolean timeZoneDetectionEnabled = false;
+ script.initializeTimeZoneDetectionEnabled(timeZoneDetectionEnabled);
+
+ for (int i = 0; i < TEST_CASES.length; i++) {
+ SuggestionTestCase testCase = TEST_CASES[i];
+
+ PhoneTimeZoneSuggestion suggestion =
+ testCase.createSuggestion(PHONE1_ID, "Europe/London");
+ script.suggestPhoneTimeZone(suggestion);
+
+ // When time zone detection is already enabled the suggestion (if it scores highly
+ // enough) should be set immediately.
+ if (timeZoneDetectionEnabled) {
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(suggestion, testCase.expectedScore);
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+
+ // Now toggle the time zone detection setting: when it is toggled to on and the most
+ // recent suggestion scores highly enough, the time zone should be set.
+ timeZoneDetectionEnabled = !timeZoneDetectionEnabled;
+ script.timeZoneDetectionEnabled(timeZoneDetectionEnabled);
+ if (timeZoneDetectionEnabled) {
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+ }
+ }
+
+ @Test
+ public void testSuggestionsSinglePhone() {
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true)
+ .initializeTimeZoneSetting(true);
+
+ for (SuggestionTestCase testCase : TEST_CASES) {
+ makePhone1SuggestionAndCheckState(script, testCase);
+ }
+
+ /*
+ * This is the same test as above but the test cases are in
+ * reverse order of their expected score. New suggestions always replace previous ones:
+ * there's effectively no history and so ordering shouldn't make any difference.
+ */
+
+ // Each test case will have the same or lower score than the last.
+ ArrayList<SuggestionTestCase> descendingCasesByScore =
+ new ArrayList<>(Arrays.asList(TEST_CASES));
+ Collections.reverse(descendingCasesByScore);
+
+ for (SuggestionTestCase testCase : descendingCasesByScore) {
+ makePhone1SuggestionAndCheckState(script, testCase);
+ }
+ }
+
+ private void makePhone1SuggestionAndCheckState(Script script, SuggestionTestCase testCase) {
+ String zoneId = "Europe/London";
+ PhoneTimeZoneSuggestion zonePhone1Suggestion = testCase.createSuggestion(PHONE1_ID, zoneId);
+ QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion, testCase.expectedScore);
+
+ script.suggestPhoneTimeZone(zonePhone1Suggestion);
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+ }
+
+ /**
+ * Tries a set of test cases to see if the phone with the lowest ID is given preference. This
+ * test also confirms that the time zone setting would only be set if a suggestion is of
+ * sufficient quality.
+ */
+ @Test
+ public void testMultiplePhoneSuggestionScoringAndPhoneIdBias() {
+ String[] zoneIds = { "Europe/London", "Europe/Paris" };
+ PhoneTimeZoneSuggestion emptyPhone1Suggestion = createEmptyPhone1Suggestion();
+ PhoneTimeZoneSuggestion emptyPhone2Suggestion = createEmptyPhone2Suggestion();
+ QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, SCORE_NONE);
+ QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone2ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, SCORE_NONE);
+
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true)
+ .initializeTimeZoneSetting(true)
+ // Initialize the latest suggestions as empty so we don't need to worry about nulls
+ // below for the first loop.
+ .suggestPhoneTimeZone(emptyPhone1Suggestion)
+ .suggestPhoneTimeZone(emptyPhone2Suggestion)
+ .resetState();
+
+ for (SuggestionTestCase testCase : TEST_CASES) {
+ PhoneTimeZoneSuggestion zonePhone1Suggestion =
+ testCase.createSuggestion(PHONE1_ID, zoneIds[0]);
+ PhoneTimeZoneSuggestion zonePhone2Suggestion =
+ testCase.createSuggestion(PHONE2_ID, zoneIds[1]);
+ QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion,
+ testCase.expectedScore);
+ QualifiedPhoneTimeZoneSuggestion expectedZonePhone2ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(zonePhone2Suggestion,
+ testCase.expectedScore);
+
+ // Start the test by making a suggestion for phone 1.
+ script.suggestPhoneTimeZone(zonePhone1Suggestion);
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedEmptyPhone2ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE2_ID));
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+
+ // Phone 2 then makes an identical suggestion. Phone 1's suggestion should still "win"
+ // if it is above the required threshold.
+ script.suggestPhoneTimeZone(zonePhone2Suggestion);
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedZonePhone2ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE2_ID));
+ // Phone 1 should always beat phone 2, all other things being equal.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+
+ // Withdrawing phone 1's suggestion should leave phone 2 as the new winner. Since the
+ // zoneId is different, the time zone setting should be updated.
+ script.suggestPhoneTimeZone(emptyPhone1Suggestion);
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(zonePhone2Suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedEmptyPhone1ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedZonePhone2ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE2_ID));
+ assertEquals(expectedZonePhone2ScoredSuggestion,
+ mTimeZoneDetectionService.findBestSuggestionForTests());
+
+ // Reset the state for the next loop.
+ script.suggestPhoneTimeZone(emptyPhone2Suggestion)
+ .verifyTimeZoneNotSet();
+ assertEquals(expectedEmptyPhone1ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedEmptyPhone2ScoredSuggestion,
+ mTimeZoneDetectionService.getLatestPhoneSuggestion(PHONE2_ID));
+ }
+ }
+
+ /**
+ * The {@link TimeZoneDetectionService.Helper} is left to detect whether changing the the time
+ * zone is actually necessary. This test proves that the service doesn't assume it knows the
+ * current setting.
+ */
+ @Test
+ public void testTimeZoneDetectionServiceDoesNotAssumeCurrentSetting() {
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true);
+
+ SuggestionTestCase testCase =
+ newTestCase(NETWORK_COUNTRY_AND_OFFSET, SINGLE_ZONE, SCORE_HIGH);
+ PhoneTimeZoneSuggestion losAngelesSuggestion =
+ testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
+ PhoneTimeZoneSuggestion newYorkSuggestion =
+ testCase.createSuggestion(PHONE1_ID, "America/New_York");
+
+ // Initialization.
+ script.suggestPhoneTimeZone(losAngelesSuggestion)
+ .verifyTimeZoneSetAndReset(losAngelesSuggestion);
+ // Suggest it again - it should be set.
+ script.suggestPhoneTimeZone(losAngelesSuggestion)
+ .verifyTimeZoneSetAndReset(losAngelesSuggestion);
+
+ // Toggling time zone detection should set it.
+ script.timeZoneDetectionEnabled(false)
+ .timeZoneDetectionEnabled(true)
+ .verifyTimeZoneSetAndReset(losAngelesSuggestion);
+
+ // Simulate a user turning detection off, a new suggestion being made, and the user turning
+ // it on again.
+ script.timeZoneDetectionEnabled(false)
+ .suggestPhoneTimeZone(newYorkSuggestion)
+ .verifyTimeZoneNotSet();
+ // Latest suggestion should be used.
+ script.timeZoneDetectionEnabled(true)
+ .verifyTimeZoneSetAndReset(newYorkSuggestion);
+ }
+
+ private static PhoneTimeZoneSuggestion createEmptyPhone1Suggestion() {
+ return new PhoneTimeZoneSuggestion(PHONE1_ID);
+ }
+
+ private static PhoneTimeZoneSuggestion createEmptyPhone2Suggestion() {
+ return new PhoneTimeZoneSuggestion(PHONE2_ID);
+ }
+
+ class FakeTimeZoneDetectionServiceHelper implements TimeZoneDetectionService.Helper {
+
+ private Listener mListener;
+ private boolean mTimeZoneDetectionEnabled;
+ private boolean mTimeZoneInitialized = false;
+ private TestState<PhoneTimeZoneSuggestion> mTimeZoneSuggestion = new TestState<>();
+
+ @Override
+ public void setListener(Listener listener) {
+ this.mListener = listener;
+ }
+
+ @Override
+ public boolean isTimeZoneDetectionEnabled() {
+ return mTimeZoneDetectionEnabled;
+ }
+
+ @Override
+ public boolean isTimeZoneSettingInitialized() {
+ return mTimeZoneInitialized;
+ }
+
+ @Override
+ public void setDeviceTimeZoneFromSuggestion(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ mTimeZoneInitialized = true;
+ mTimeZoneSuggestion.set(timeZoneSuggestion);
+ }
+
+ @Override
+ public void dumpState(PrintWriter pw) {
+ // No-op for fake
+ }
+
+ @Override
+ public void dumpLogs(IndentingPrintWriter ipw) {
+ // No-op for fake
+ }
+
+ void initializeTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ }
+
+ void initializeTimeZone(boolean initialized) {
+ mTimeZoneInitialized = initialized;
+ }
+
+ void simulateTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ mListener.onTimeZoneDetectionChange(enabled);
+ }
+
+ void assertTimeZoneNotSet() {
+ mTimeZoneSuggestion.assertHasNotBeenSet();
+ }
+
+ void assertTimeZoneSuggested(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ mTimeZoneSuggestion.assertHasBeenSet();
+ mTimeZoneSuggestion.assertChangeCount(1);
+ mTimeZoneSuggestion.assertLatestEquals(timeZoneSuggestion);
+ }
+
+ void commitAllChanges() {
+ mTimeZoneSuggestion.commitLatest();
+ }
+ }
+
+ /** Some piece of state that tests want to track. */
+ private static class TestState<T> {
+ private T mInitialValue;
+ private LinkedList<T> mValues = new LinkedList<>();
+
+ void init(T value) {
+ mValues.clear();
+ mInitialValue = value;
+ }
+
+ void set(T value) {
+ mValues.addFirst(value);
+ }
+
+ boolean hasBeenSet() {
+ return mValues.size() > 0;
+ }
+
+ void assertHasNotBeenSet() {
+ assertFalse(hasBeenSet());
+ }
+
+ void assertHasBeenSet() {
+ assertTrue(hasBeenSet());
+ }
+
+ void commitLatest() {
+ if (hasBeenSet()) {
+ mInitialValue = mValues.getLast();
+ mValues.clear();
+ }
+ }
+
+ void assertLatestEquals(T expected) {
+ assertEquals(expected, getLatest());
+ }
+
+ void assertChangeCount(int expectedCount) {
+ assertEquals(expectedCount, mValues.size());
+ }
+
+ public T getLatest() {
+ if (hasBeenSet()) {
+ return mValues.getFirst();
+ }
+ return mInitialValue;
+ }
+ }
+
+ /**
+ * A "fluent" class allows reuse of code in tests: initialization, simulation and verification
+ * logic.
+ */
+ private class Script {
+
+ Script initializeTimeZoneDetectionEnabled(boolean enabled) {
+ mFakeTimeZoneDetectionServiceHelper.initializeTimeZoneDetectionEnabled(enabled);
+ return this;
+ }
+
+ Script initializeTimeZoneSetting(boolean initialized) {
+ mFakeTimeZoneDetectionServiceHelper.initializeTimeZone(initialized);
+ return this;
+ }
+
+ Script timeZoneDetectionEnabled(boolean timeZoneDetectionEnabled) {
+ mFakeTimeZoneDetectionServiceHelper.simulateTimeZoneDetectionEnabled(
+ timeZoneDetectionEnabled);
+ return this;
+ }
+
+ /** Simulates the time zone detection service receiving a phone-originated suggestion. */
+ Script suggestPhoneTimeZone(PhoneTimeZoneSuggestion phoneTimeZoneSuggestion) {
+ mTimeZoneDetectionService.suggestPhoneTimeZone(phoneTimeZoneSuggestion);
+ return this;
+ }
+
+ Script verifyTimeZoneNotSet() {
+ mFakeTimeZoneDetectionServiceHelper.assertTimeZoneNotSet();
+ return this;
+ }
+
+ Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ mFakeTimeZoneDetectionServiceHelper.assertTimeZoneSuggested(timeZoneSuggestion);
+ mFakeTimeZoneDetectionServiceHelper.commitAllChanges();
+ return this;
+ }
+
+ Script resetState() {
+ mFakeTimeZoneDetectionServiceHelper.commitAllChanges();
+ return this;
+ }
+ }
+
+ private static class SuggestionTestCase {
+ public final int matchType;
+ public final int quality;
+ public final int expectedScore;
+
+ SuggestionTestCase(int matchType, int quality, int expectedScore) {
+ this.matchType = matchType;
+ this.quality = quality;
+ this.expectedScore = expectedScore;
+ }
+
+ private PhoneTimeZoneSuggestion createSuggestion(int phoneId, String zoneId) {
+ PhoneTimeZoneSuggestion suggestion = new PhoneTimeZoneSuggestion(phoneId);
+ suggestion.setZoneId(zoneId);
+ suggestion.setMatchType(matchType);
+ suggestion.setQuality(quality);
+ return suggestion;
+ }
+ }
+
+ private static SuggestionTestCase newTestCase(
+ @MatchType int matchType, @Quality int quality, int expectedScore) {
+ return new SuggestionTestCase(matchType, quality, expectedScore);
+ }
+}
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 419243e66e..80f88670ec 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/SIMRecordsTest.java
@@ -17,6 +17,8 @@
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.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -31,6 +33,7 @@ import android.os.test.TestLooper;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.TelephonyTest;
@@ -171,4 +174,116 @@ public class SIMRecordsTest extends TelephonyTest {
.when(mFhMock)
.updateEFTransparent(anyInt(), any(byte[].class), any(Message.class));
}
+
+ @Test
+ public void testGetForbiddenPlmns() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ byte[] encodedFplmn = IccUtils.encodeFplmns(SHORT_FPLMNS_LIST, EF_SIZE);
+ AsyncResult.forMessage(response, encodedFplmn, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.getForbiddenPlmns(message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertNull(ar.exception);
+ assertEquals(SHORT_FPLMNS_LIST.toArray(new String[SHORT_FPLMNS_LIST.size()]),
+ (String[]) ar.result);
+ }
+
+ @Test
+ public void testGetForbiddenPlmnsException() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response, null, new CommandException(
+ CommandException.Error.OPERATION_NOT_ALLOWED));
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.getForbiddenPlmns(message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertTrue(ar.exception instanceof CommandException);
+ assertTrue(((CommandException) ar.exception).getCommandError() ==
+ CommandException.Error.OPERATION_NOT_ALLOWED);
+ assertNull(ar.result);
+ }
+
+ @Test
+ public void testGetForbiddenPlmnsNull() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.getForbiddenPlmns(message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertNull(ar.exception);
+ assertNull(ar.result);
+ }
+
+ @Test
+ public void testGetForbiddenPlmnsEmptyList() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ byte[] encodedFplmn = IccUtils.encodeFplmns(EMPTY_FPLMN_LIST, EF_SIZE);
+ AsyncResult.forMessage(response, encodedFplmn, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.getForbiddenPlmns(message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertNull(ar.exception);
+ assertEquals(EMPTY_FPLMN_LIST.toArray(new String[EMPTY_FPLMN_LIST.size()]),
+ (String[]) ar.result);
+ }
+
+ @Test
+ public void testGetForbiddenPlmnsInvalidLength() {
+ doAnswer(
+ invocation -> {
+ Message response = invocation.getArgument(1);
+ AsyncResult.forMessage(response, new byte[] { (byte) 0xFF, (byte) 0xFF }, null);
+ response.sendToTarget();
+ return null;
+ })
+ .when(mFhMock)
+ .loadEFTransparent(eq(SIMRecords.EF_FPLMN), any(Message.class));
+
+ Message message = Message.obtain(mTestHandler);
+ mSIMRecordsUT.getForbiddenPlmns(message);
+ mTestLooper.dispatchAll();
+
+ AsyncResult ar = (AsyncResult) message.obj;
+ assertNull(ar.exception);
+ assertNull(ar.result);
+ }
}