Create telephony-common - DO NOT MERGE
Wink Saville [Wed, 11 Jul 2012 22:41:29 +0000 (15:41 -0700)]
telephony-common was created by moving some of
  frameworks/base/telephony
to:
  frameworks/opt/telephony

Change-Id: I32cbb5eec1fa239c1587e055c8f7ef4fc48fb62c

234 files changed:
Android.mk [new file with mode: 0644]
CleanSpec.mk [new file with mode: 0644]
mockril/Android.mk [new file with mode: 0644]
mockril/src/com/android/internal/telephony/mockril/MockRilController.java [new file with mode: 0644]
src/java/android/provider/Telephony.java [new file with mode: 0644]
src/java/android/telephony/CellBroadcastMessage.java [new file with mode: 0644]
src/java/android/telephony/SmsCbCmasInfo.java [new file with mode: 0644]
src/java/android/telephony/SmsCbEtwsInfo.java [new file with mode: 0644]
src/java/android/telephony/SmsCbLocation.java [new file with mode: 0644]
src/java/android/telephony/SmsCbMessage.java [new file with mode: 0644]
src/java/android/telephony/SmsManager.java [new file with mode: 0644]
src/java/android/telephony/SmsMessage.java [new file with mode: 0644]
src/java/android/telephony/gsm/SmsManager.java [new file with mode: 0644]
src/java/android/telephony/gsm/SmsMessage.java [new file with mode: 0644]
src/java/com/android/internal/telephony/ATParseEx.java [new file with mode: 0644]
src/java/com/android/internal/telephony/ATResponseParser.java [new file with mode: 0644]
src/java/com/android/internal/telephony/AdnRecord.java [new file with mode: 0644]
src/java/com/android/internal/telephony/AdnRecordCache.java [new file with mode: 0644]
src/java/com/android/internal/telephony/AdnRecordLoader.java [new file with mode: 0644]
src/java/com/android/internal/telephony/ApnContext.java [new file with mode: 0644]
src/java/com/android/internal/telephony/ApnSetting.java [new file with mode: 0755]
src/java/com/android/internal/telephony/BaseCommands.java [new file with mode: 0644]
src/java/com/android/internal/telephony/Call.java [new file with mode: 0644]
src/java/com/android/internal/telephony/CallForwardInfo.java [new file with mode: 0644]
src/java/com/android/internal/telephony/CallManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/CallStateException.java [new file with mode: 0644]
src/java/com/android/internal/telephony/CallTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/CommandException.java [new file with mode: 0644]
src/java/com/android/internal/telephony/CommandsInterface.java [new file with mode: 0644]
src/java/com/android/internal/telephony/Connection.java [new file with mode: 0644]
src/java/com/android/internal/telephony/DataCallState.java [new file with mode: 0644]
src/java/com/android/internal/telephony/DataConnection.java [new file with mode: 0644]
src/java/com/android/internal/telephony/DataConnectionAc.java [new file with mode: 0644]
src/java/com/android/internal/telephony/DataConnectionTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/DebugService.java [new file with mode: 0644]
src/java/com/android/internal/telephony/DefaultPhoneNotifier.java [new file with mode: 0644]
src/java/com/android/internal/telephony/DriverCall.java [new file with mode: 0644]
src/java/com/android/internal/telephony/EventLogTags.logtags [new file with mode: 0644]
src/java/com/android/internal/telephony/IIccPhoneBook.aidl [new file with mode: 0644]
src/java/com/android/internal/telephony/ISms.aidl [new file with mode: 0644]
src/java/com/android/internal/telephony/IccCard.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccCardApplication.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccCardStatus.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccConstants.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccException.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccFileHandler.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccFileNotFound.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccFileTypeMismatch.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccIoResult.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccProvider.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccRecords.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccRefreshResponse.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccServiceTable.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccSmsInterfaceManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccUtils.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccVmFixedException.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IccVmNotSupportedException.java [new file with mode: 0644]
src/java/com/android/internal/telephony/IntRangeManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/MccTable.java [new file with mode: 0644]
src/java/com/android/internal/telephony/MmiCode.java [new file with mode: 0644]
src/java/com/android/internal/telephony/OperatorInfo.java [new file with mode: 0644]
src/java/com/android/internal/telephony/Phone.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneBase.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneFactory.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneNotifier.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneProxy.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneStateIntentReceiver.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneSubInfo.java [new file with mode: 0755]
src/java/com/android/internal/telephony/PhoneSubInfoProxy.java [new file with mode: 0755]
src/java/com/android/internal/telephony/RIL.java [new file with mode: 0644]
src/java/com/android/internal/telephony/RestrictedState.java [new file with mode: 0644]
src/java/com/android/internal/telephony/RetryManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SMSDispatcher.java [new file with mode: 0644]
src/java/com/android/internal/telephony/ServiceStateTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SmsAddress.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SmsHeader.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SmsMessageBase.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SmsRawData.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SmsResponse.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SmsStorageMonitor.java [new file with mode: 0644]
src/java/com/android/internal/telephony/SmsUsageMonitor.java [new file with mode: 0644]
src/java/com/android/internal/telephony/TelephonyCapabilities.java [new file with mode: 0644]
src/java/com/android/internal/telephony/UUSInfo.java [new file with mode: 0644]
src/java/com/android/internal/telephony/WapPushManagerParams.java [new file with mode: 0644]
src/java/com/android/internal/telephony/WapPushOverSms.java [new file with mode: 0755]
src/java/com/android/internal/telephony/WspTypeDecoder.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cat/AppInterface.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/BerTlv.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CatCmdMessage.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CatException.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CatLog.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CatResponseMessage.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CatService.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CommandDetails.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CommandParams.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/CommandParamsFactory.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ComprehensionTlv.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/Duration.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/FontSize.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/IconLoader.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ImageDescriptor.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/Input.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/Item.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/LaunchBrowserMode.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/Menu.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/PresentationType.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ResponseData.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ResultCode.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ResultException.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/RilMessageDecoder.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/TextAlignment.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/TextAttribute.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/TextColor.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/TextMessage.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/Tone.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ToneSettings.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/ValueParser.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cat/package.html [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CDMAPhone.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cdma/CallFailCause.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaCall.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaConnection.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cdma/CdmaDataConnection.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cdma/CdmaSubscriptionSourceManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/EriInfo.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/EriManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/RuimFileHandler.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/RuimRecords.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/SignalToneUtil.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/SmsMessage.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/TtyIntent.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/package.html [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/sms/BearerData.java [new file with mode: 0755]
src/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/sms/UserData.java [new file with mode: 0644]
src/java/com/android/internal/telephony/cdma/sms/package.html [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/CallFailCause.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GSMPhone.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmCall.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmCallTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmConnection.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmDataConnection.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmMmiCode.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmSmsAddress.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SIMFileHandler.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SIMRecords.java [new file with mode: 0755]
src/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SimTlv.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SmsCbConstants.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SmsCbHeader.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SmsMessage.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SpnOverride.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/SuppServiceNotification.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java [new file with mode: 0755]
src/java/com/android/internal/telephony/gsm/UsimServiceTable.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/VoiceMailConstants.java [new file with mode: 0644]
src/java/com/android/internal/telephony/gsm/package.html [new file with mode: 0755]
src/java/com/android/internal/telephony/ims/IsimRecords.java [new file with mode: 0644]
src/java/com/android/internal/telephony/ims/IsimUiccRecords.java [new file with mode: 0644]
src/java/com/android/internal/telephony/package.html [new file with mode: 0644]
src/java/com/android/internal/telephony/sip/SipCallBase.java [new file with mode: 0644]
src/java/com/android/internal/telephony/sip/SipCommandInterface.java [new file with mode: 0644]
src/java/com/android/internal/telephony/sip/SipConnectionBase.java [new file with mode: 0644]
src/java/com/android/internal/telephony/sip/SipPhone.java [new file with mode: 0644]
src/java/com/android/internal/telephony/sip/SipPhoneBase.java [new file with mode: 0755]
src/java/com/android/internal/telephony/sip/SipPhoneFactory.java [new file with mode: 0644]
src/java/com/android/internal/telephony/test/ModelInterpreter.java [new file with mode: 0644]
src/java/com/android/internal/telephony/test/SimulatedCommands.java [new file with mode: 0644]
src/java/com/android/internal/telephony/test/SimulatedGsmCallState.java [new file with mode: 0644]
src/java/com/android/internal/telephony/test/SimulatedRadioControl.java [new file with mode: 0644]
src/java/com/android/internal/telephony/test/package.html [new file with mode: 0755]
src/java/com/android/internal/telephony/uicc/UiccController.java [new file with mode: 0644]
tests/telephonymockriltests/Android.mk [new file with mode: 0644]
tests/telephonymockriltests/AndroidManifest.xml [new file with mode: 0644]
tests/telephonymockriltests/src/com/android/telephonymockriltests/TelephonyMockTestRunner.java [new file with mode: 0644]
tests/telephonymockriltests/src/com/android/telephonymockriltests/functional/SimpleTestUsingMockRil.java [new file with mode: 0644]
tests/telephonytests/Android.mk [new file with mode: 0644]
tests/telephonytests/AndroidManifest.xml [new file with mode: 0644]
tests/telephonytests/src/com/android/frameworks/telephonytests/TelephonyMockRilTestRunner.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/ATResponseParserTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/AdnRecordTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java [new file with mode: 0755]
tests/telephonytests/src/com/android/internal/telephony/CallerInfoTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/IccServiceTableTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/NeighboringCellInfoTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/SimPhoneBookTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/TelephonyUtilsTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/cdma/sms/CdmaSmsTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/gsm/UsimServiceTableTest.java [new file with mode: 0644]
tests/telephonytests/src/com/android/internal/telephony/mockril/MockRilTest.java [new file with mode: 0644]

diff --git a/Android.mk b/Android.mk
new file mode 100644 (file)
index 0000000..2487766
--- /dev/null
@@ -0,0 +1,34 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       src/java/com/android/internal/telephony/ISms.aidl \
+    src/java/com/android/internal/telephony/IIccPhoneBook.aidl \
+    src/java/com/android/internal/telephony/EventLogTags.logtags \
+
+LOCAL_SRC_FILES += $(call all-java-files-under, src/java)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := telephony-common
+
+include $(BUILD_JAVA_LIBRARY)
+
+# Include subdirectory makefiles
+# ============================================================
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644 (file)
index 0000000..9a63705
--- /dev/null
@@ -0,0 +1,45 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+$(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/mockril/Android.mk b/mockril/Android.mk
new file mode 100644 (file)
index 0000000..95ae84c
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2010 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.
+#
+#
+
+LOCAL_PATH:=$(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := core framework
+
+LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
+
+LOCAL_MODULE := mockrilcontroller
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/mockril/src/com/android/internal/telephony/mockril/MockRilController.java b/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
new file mode 100644 (file)
index 0000000..0e75c72
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010, 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.mockril;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.communication.MsgHeader;
+import com.android.internal.communication.Msg;
+import com.android.internal.telephony.RilChannel;
+import com.android.internal.telephony.ril_proto.RilCtrlCmds;
+import com.android.internal.telephony.ril_proto.RilCmds;
+import com.google.protobuf.micro.MessageMicro;
+
+import java.io.IOException;
+
+/**
+ * Contain a list of commands to control Mock RIL. Before using these commands the devices
+ * needs to be set with Mock RIL. Refer to hardware/ril/mockril/README.txt for details.
+ *
+ */
+public class MockRilController {
+    private static final String TAG = "MockRILController";
+    private RilChannel mRilChannel = null;
+    private Msg mMessage = null;
+
+    public MockRilController() throws IOException {
+        mRilChannel = RilChannel.makeRilChannel();
+    }
+
+    /**
+     * Close the channel after the communication is done.
+     * This method has to be called after the test is finished.
+     */
+    public void closeChannel() {
+        mRilChannel.close();
+    }
+
+    /**
+     * Send commands and return true on success
+     * @param cmd for MsgHeader
+     * @param token for MsgHeader
+     * @param status for MsgHeader
+     * @param pbData for Msg data
+     * @return true if command is sent successfully, false if it fails
+     */
+    private boolean sendCtrlCommand(int cmd, long token, int status, MessageMicro pbData) {
+        try {
+            Msg.send(mRilChannel, cmd, token, status, pbData);
+        } catch (IOException e) {
+            Log.v(TAG, "send command : %d failed: " + e.getStackTrace());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get control response
+     * @return Msg if response is received, else return null.
+     */
+    private Msg getCtrlResponse() {
+        Msg response = null;
+        try {
+            response = Msg.recv(mRilChannel);
+        } catch (IOException e) {
+            Log.v(TAG, "receive response for getRadioState() error: " + e.getStackTrace());
+            return null;
+        }
+        return response;
+    }
+
+    /**
+     * @return the radio state if it is valid, otherwise return -1
+     */
+    public int getRadioState() {
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_GET_RADIO_STATE, 0, 0, null)) {
+            return -1;
+        }
+        Msg response = getCtrlResponse();
+        if (response == null) {
+            Log.v(TAG, "failed to get response");
+            return -1;
+        }
+        response.printHeader(TAG);
+        RilCtrlCmds.CtrlRspRadioState resp =
+            response.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
+        int state = resp.getState();
+        if ((state >= RilCmds.RADIOSTATE_OFF) && (state <= RilCmds.RADIOSTATE_NV_READY))
+            return state;
+        else
+            return -1;
+    }
+
+    /**
+     * Set the radio state of mock ril to the given state
+     * @param state for given radio state
+     * @return true if the state is set successful, false if it fails
+     */
+    public boolean setRadioState(int state) {
+        RilCtrlCmds.CtrlReqRadioState req = new RilCtrlCmds.CtrlReqRadioState();
+        if (state < 0 || state > RilCmds.RADIOSTATE_NV_READY) {
+            Log.v(TAG, "the give radio state is not valid.");
+            return false;
+        }
+        req.setState(state);
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_RADIO_STATE, 0, 0, req)) {
+            Log.v(TAG, "send set radio state request failed.");
+            return false;
+        }
+        Msg response = getCtrlResponse();
+        if (response == null) {
+            Log.v(TAG, "failed to get response for setRadioState");
+            return false;
+        }
+        response.printHeader(TAG);
+        RilCtrlCmds.CtrlRspRadioState resp =
+            response.getDataAs(RilCtrlCmds.CtrlRspRadioState.class);
+        int curstate = resp.getState();
+        return curstate == state;
+    }
+
+    /**
+     * Start an incoming call for the given phone number
+     *
+     * @param phoneNumber is the number to show as incoming call
+     * @return true if the incoming call is started successfully, false if it fails.
+     */
+    public boolean startIncomingCall(String phoneNumber) {
+        RilCtrlCmds.CtrlReqSetMTCall req = new RilCtrlCmds.CtrlReqSetMTCall();
+
+        req.setPhoneNumber(phoneNumber);
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_MT_CALL, 0, 0, req)) {
+            Log.v(TAG, "send CMD_SET_MT_CALL request failed");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Hang up a connection remotelly for the given call fail cause
+     *
+     * @param connectionID is the connection to be hung up
+     * @param failCause is the call fail cause defined in ril.h
+     * @return true if the hangup is successful, false if it fails
+     */
+    public boolean hangupRemote(int connectionId, int failCause) {
+        RilCtrlCmds.CtrlHangupConnRemote req = new RilCtrlCmds.CtrlHangupConnRemote();
+        req.setConnectionId(connectionId);
+        req.setCallFailCause(failCause);
+
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_HANGUP_CONN_REMOTE, 0, 0, req)) {
+            Log.v(TAG, "send CTRL_CMD_HANGUP_CONN_REMOTE request failed");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set call transition flag to the Mock Ril
+     *
+     * @param flag is a boolean value for the call transiton flag
+     *             true: call transition: dialing->alert, alert->active is controlled
+     *             false: call transition is automatically handled by Mock Ril
+     * @return true if the request is successful, false if it failed to set the flag
+     */
+    public boolean setCallTransitionFlag(boolean flag) {
+        RilCtrlCmds.CtrlSetCallTransitionFlag req = new RilCtrlCmds.CtrlSetCallTransitionFlag();
+
+        req.setFlag(flag);
+
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_TRANSITION_FLAG, 0, 0, req)) {
+            Log.v(TAG, "send CTRL_CMD_SET_CALL_TRANSITION_FLAG request failed");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set the dialing call to alert if the call transition flag is true
+     *
+     * @return true if the call transition is successful, false if it fails
+     */
+    public boolean setDialCallToAlert() {
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ALERT, 0, 0, null)) {
+            Log.v(TAG, "send CTRL_CMD_SET_CALL_ALERT request failed");
+            return false;
+        }
+        return true;
+   }
+
+   /**
+    * Set the alert call to active if the call transition flag is true
+    *
+    * @return true if the call transition is successful, false if it fails
+    */
+   public boolean setAlertCallToActive() {
+        if (!sendCtrlCommand(RilCtrlCmds.CTRL_CMD_SET_CALL_ACTIVE, 0, 0, null)) {
+            Log.v(TAG, "send CTRL_CMD_SET_CALL_ACTIVE request failed");
+            return false;
+        }
+        return true;
+   }
+}
diff --git a/src/java/android/provider/Telephony.java b/src/java/android/provider/Telephony.java
new file mode 100644 (file)
index 0000000..dd8be66
--- /dev/null
@@ -0,0 +1,1993 @@
+/*
+ * Copyright (C) 2006 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.provider;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SqliteWrapper;
+import android.net.Uri;
+import android.os.Environment;
+import android.telephony.SmsMessage;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Patterns;
+
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The Telephony provider contains data related to phone operation.
+ *
+ * @hide
+ */
+public final class Telephony {
+    private static final String TAG = "Telephony";
+    private static final boolean DEBUG = true;
+    private static final boolean LOCAL_LOGV = false;
+
+    // Constructor
+    public Telephony() {
+    }
+
+    /**
+     * Base columns for tables that contain text based SMSs.
+     */
+    public interface TextBasedSmsColumns {
+        /**
+         * The type of the message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TYPE = "type";
+
+        public static final int MESSAGE_TYPE_ALL    = 0;
+        public static final int MESSAGE_TYPE_INBOX  = 1;
+        public static final int MESSAGE_TYPE_SENT   = 2;
+        public static final int MESSAGE_TYPE_DRAFT  = 3;
+        public static final int MESSAGE_TYPE_OUTBOX = 4;
+        public static final int MESSAGE_TYPE_FAILED = 5; // for failed outgoing messages
+        public static final int MESSAGE_TYPE_QUEUED = 6; // for messages to send later
+
+
+        /**
+         * The thread ID of the message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THREAD_ID = "thread_id";
+
+        /**
+         * The address of the other party
+         * <P>Type: TEXT</P>
+         */
+        public static final String ADDRESS = "address";
+
+        /**
+         * The person ID of the sender
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String PERSON_ID = "person";
+
+        /**
+         * The date the message was received
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE = "date";
+
+        /**
+         * The date the message was sent
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE_SENT = "date_sent";
+
+        /**
+         * Has the message been read
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String READ = "read";
+
+        /**
+         * Indicates whether this message has been seen by the user. The "seen" flag will be
+         * used to figure out whether we need to throw up a statusbar notification or not.
+         */
+        public static final String SEEN = "seen";
+
+        /**
+         * The TP-Status value for the message, or -1 if no status has
+         * been received
+         */
+        public static final String STATUS = "status";
+
+        public static final int STATUS_NONE = -1;
+        public static final int STATUS_COMPLETE = 0;
+        public static final int STATUS_PENDING = 32;
+        public static final int STATUS_FAILED = 64;
+
+        /**
+         * The subject of the message, if present
+         * <P>Type: TEXT</P>
+         */
+        public static final String SUBJECT = "subject";
+
+        /**
+         * The body of the message
+         * <P>Type: TEXT</P>
+         */
+        public static final String BODY = "body";
+
+        /**
+         * The id of the sender of the conversation, if present
+         * <P>Type: INTEGER (reference to item in content://contacts/people)</P>
+         */
+        public static final String PERSON = "person";
+
+        /**
+         * The protocol identifier code
+         * <P>Type: INTEGER</P>
+         */
+        public static final String PROTOCOL = "protocol";
+
+        /**
+         * Whether the <code>TP-Reply-Path</code> bit was set on this message
+         * <P>Type: BOOLEAN</P>
+         */
+        public static final String REPLY_PATH_PRESENT = "reply_path_present";
+
+        /**
+         * The service center (SC) through which to send the message, if present
+         * <P>Type: TEXT</P>
+         */
+        public static final String SERVICE_CENTER = "service_center";
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
+
+        /**
+         * Error code associated with sending or receiving this message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ERROR_CODE = "error_code";
+
+        /**
+         * Meta data used externally.
+         * <P>Type: TEXT</P>
+         */
+        public static final String META_DATA = "meta_data";
+    }
+
+    /**
+     * Contains all text based SMS messages.
+     */
+    public static final class Sms implements BaseColumns, TextBasedSmsColumns {
+        public static final Cursor query(ContentResolver cr, String[] projection) {
+            return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
+        }
+
+        public static final Cursor query(ContentResolver cr, String[] projection,
+                String where, String orderBy) {
+            return cr.query(CONTENT_URI, projection, where,
+                                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        }
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://sms");
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+        /**
+         * Add an SMS to the given URI.
+         *
+         * @param resolver the content resolver to use
+         * @param uri the URI to add the message to
+         * @param address the address of the sender
+         * @param body the body of the message
+         * @param subject the psuedo-subject of the message
+         * @param date the timestamp for the message
+         * @param read true if the message has been read, false if not
+         * @param deliveryReport true if a delivery report was requested, false if not
+         * @return the URI for the new message
+         */
+        public static Uri addMessageToUri(ContentResolver resolver,
+                Uri uri, String address, String body, String subject,
+                Long date, boolean read, boolean deliveryReport) {
+            return addMessageToUri(resolver, uri, address, body, subject,
+                    date, read, deliveryReport, -1L);
+        }
+
+        /**
+         * Add an SMS to the given URI with thread_id specified.
+         *
+         * @param resolver the content resolver to use
+         * @param uri the URI to add the message to
+         * @param address the address of the sender
+         * @param body the body of the message
+         * @param subject the psuedo-subject of the message
+         * @param date the timestamp for the message
+         * @param read true if the message has been read, false if not
+         * @param deliveryReport true if a delivery report was requested, false if not
+         * @param threadId the thread_id of the message
+         * @return the URI for the new message
+         */
+        public static Uri addMessageToUri(ContentResolver resolver,
+                Uri uri, String address, String body, String subject,
+                Long date, boolean read, boolean deliveryReport, long threadId) {
+            ContentValues values = new ContentValues(7);
+
+            values.put(ADDRESS, address);
+            if (date != null) {
+                values.put(DATE, date);
+            }
+            values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0));
+            values.put(SUBJECT, subject);
+            values.put(BODY, body);
+            if (deliveryReport) {
+                values.put(STATUS, STATUS_PENDING);
+            }
+            if (threadId != -1L) {
+                values.put(THREAD_ID, threadId);
+            }
+            return resolver.insert(uri, values);
+        }
+
+        /**
+         * Move a message to the given folder.
+         *
+         * @param context the context to use
+         * @param uri the message to move
+         * @param folder the folder to move to
+         * @return true if the operation succeeded
+         */
+        public static boolean moveMessageToFolder(Context context,
+                Uri uri, int folder, int error) {
+            if (uri == null) {
+                return false;
+            }
+
+            boolean markAsUnread = false;
+            boolean markAsRead = false;
+            switch(folder) {
+            case MESSAGE_TYPE_INBOX:
+            case MESSAGE_TYPE_DRAFT:
+                break;
+            case MESSAGE_TYPE_OUTBOX:
+            case MESSAGE_TYPE_SENT:
+                markAsRead = true;
+                break;
+            case MESSAGE_TYPE_FAILED:
+            case MESSAGE_TYPE_QUEUED:
+                markAsUnread = true;
+                break;
+            default:
+                return false;
+            }
+
+            ContentValues values = new ContentValues(3);
+
+            values.put(TYPE, folder);
+            if (markAsUnread) {
+                values.put(READ, Integer.valueOf(0));
+            } else if (markAsRead) {
+                values.put(READ, Integer.valueOf(1));
+            }
+            values.put(ERROR_CODE, error);
+
+            return 1 == SqliteWrapper.update(context, context.getContentResolver(),
+                            uri, values, null, null);
+        }
+
+        /**
+         * Returns true iff the folder (message type) identifies an
+         * outgoing message.
+         */
+        public static boolean isOutgoingFolder(int messageType) {
+            return  (messageType == MESSAGE_TYPE_FAILED)
+                    || (messageType == MESSAGE_TYPE_OUTBOX)
+                    || (messageType == MESSAGE_TYPE_SENT)
+                    || (messageType == MESSAGE_TYPE_QUEUED);
+        }
+
+        /**
+         * Contains all text based SMS messages in the SMS app's inbox.
+         */
+        public static final class Inbox implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                Uri.parse("content://sms/inbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Draft box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @param read true if the message has been read, false if not
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date,
+                    boolean read) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, read, false);
+            }
+        }
+
+        /**
+         * Contains all sent text based SMS messages in the SMS app's.
+         */
+        public static final class Sent implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                    Uri.parse("content://sms/sent");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Draft box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, true, false);
+            }
+        }
+
+        /**
+         * Contains all sent text based SMS messages in the SMS app's.
+         */
+        public static final class Draft implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                    Uri.parse("content://sms/draft");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Draft box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, true, false);
+            }
+
+            /**
+             * Save over an existing draft message.
+             *
+             * @param resolver the content resolver to use
+             * @param uri of existing message
+             * @param body the new body for the draft message
+             * @return true is successful, false otherwise
+             */
+            public static boolean saveMessage(ContentResolver resolver,
+                    Uri uri, String body) {
+                ContentValues values = new ContentValues(2);
+                values.put(BODY, body);
+                values.put(DATE, System.currentTimeMillis());
+                return resolver.update(uri, values, null, null) == 1;
+            }
+        }
+
+        /**
+         * Contains all pending outgoing text based SMS messages.
+         */
+        public static final class Outbox implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                Uri.parse("content://sms/outbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * Add an SMS to the Out box.
+             *
+             * @param resolver the content resolver to use
+             * @param address the address of the sender
+             * @param body the body of the message
+             * @param subject the psuedo-subject of the message
+             * @param date the timestamp for the message
+             * @param deliveryReport whether a delivery report was requested for the message
+             * @return the URI for the new message
+             */
+            public static Uri addMessage(ContentResolver resolver,
+                    String address, String body, String subject, Long date,
+                    boolean deliveryReport, long threadId) {
+                return addMessageToUri(resolver, CONTENT_URI, address, body,
+                        subject, date, true, deliveryReport, threadId);
+            }
+        }
+
+        /**
+         * Contains all sent text-based SMS messages in the SMS app's.
+         */
+        public static final class Conversations
+                implements BaseColumns, TextBasedSmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri CONTENT_URI =
+                Uri.parse("content://sms/conversations");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+            /**
+             * The first 45 characters of the body of the message
+             * <P>Type: TEXT</P>
+             */
+            public static final String SNIPPET = "snippet";
+
+            /**
+             * The number of messages in the conversation
+             * <P>Type: INTEGER</P>
+             */
+            public static final String MESSAGE_COUNT = "msg_count";
+        }
+
+        /**
+         * Contains info about SMS related Intents that are broadcast.
+         */
+        public static final class Intents {
+            /**
+             * Set by BroadcastReceiver. Indicates the message was handled
+             * successfully.
+             */
+            public static final int RESULT_SMS_HANDLED = 1;
+
+            /**
+             * Set by BroadcastReceiver. Indicates a generic error while
+             * processing the message.
+             */
+            public static final int RESULT_SMS_GENERIC_ERROR = 2;
+
+            /**
+             * Set by BroadcastReceiver. Indicates insufficient memory to store
+             * the message.
+             */
+            public static final int RESULT_SMS_OUT_OF_MEMORY = 3;
+
+            /**
+             * Set by BroadcastReceiver. Indicates the message, while
+             * possibly valid, is of a format or encoding that is not
+             * supported.
+             */
+            public static final int RESULT_SMS_UNSUPPORTED = 4;
+
+            /**
+             * Broadcast Action: A new text based SMS message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>pdus</em> - An Object[] od byte[]s containing the PDUs
+             *   that make up the message.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_RECEIVED_ACTION =
+                    "android.provider.Telephony.SMS_RECEIVED";
+
+            /**
+             * Broadcast Action: A new data based SMS message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>pdus</em> - An Object[] of byte[]s containing the PDUs
+             *   that make up the message.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String DATA_SMS_RECEIVED_ACTION =
+                    "android.intent.action.DATA_SMS_RECEIVED";
+
+            /**
+             * Broadcast Action: A new WAP PUSH message has been received by the
+             * device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>transactionId (Integer)</em> - The WAP transaction ID</li>
+             *   <li><em>pduType (Integer)</em> - The WAP PDU type</li>
+             *   <li><em>header (byte[])</em> - The header of the message</li>
+             *   <li><em>data (byte[])</em> - The data payload of the message</li>
+             *   <li><em>contentTypeParameters (HashMap&lt;String,String&gt;)</em>
+             *   - Any parameters associated with the content type
+             *   (decoded from the WSP Content-Type header)</li>
+             * </ul>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             *
+             * <p>The contentTypeParameters extra value is map of content parameters keyed by
+             * their names.</p>
+             *
+             * <p>If any unassigned well-known parameters are encountered, the key of the map will
+             * be 'unassigned/0x...', where '...' is the hex value of the unassigned parameter.  If
+             * a parameter has No-Value the value in the map will be null.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String WAP_PUSH_RECEIVED_ACTION =
+                    "android.provider.Telephony.WAP_PUSH_RECEIVED";
+
+            /**
+             * Broadcast Action: A new Cell Broadcast message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>message</em> - An SmsCbMessage object containing the broadcast message
+             *   data. This is not an emergency alert, so ETWS and CMAS data will be null.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_CB_RECEIVED_ACTION =
+                    "android.provider.Telephony.SMS_CB_RECEIVED";
+
+            /**
+             * Broadcast Action: A new Emergency Broadcast message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>message</em> - An SmsCbMessage object containing the broadcast message
+             *   data, including ETWS or CMAS warning notification info if present.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_EMERGENCY_CB_RECEIVED_ACTION =
+                    "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
+
+            /**
+             * Broadcast Action: A new CDMA SMS has been received containing Service Category
+             * Program Data (updates the list of enabled broadcast channels). The intent will
+             * have the following extra values:</p>
+             *
+             * <ul>
+             *   <li><em>operations</em> - An array of CdmaSmsCbProgramData objects containing
+             *   the service category operations (add/delete/clear) to perform.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION =
+                    "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED";
+
+            /**
+             * Broadcast Action: The SIM storage for SMS messages is full.  If
+             * space is not freed, messages targeted for the SIM (class 2) may
+             * not be saved.
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SIM_FULL_ACTION =
+                    "android.provider.Telephony.SIM_FULL";
+
+            /**
+             * Broadcast Action: An incoming SMS has been rejected by the
+             * telephony framework.  This intent is sent in lieu of any
+             * of the RECEIVED_ACTION intents.  The intent will have the
+             * following extra value:</p>
+             *
+             * <ul>
+             *   <li><em>result</em> - An int result code, eg,
+             *   <code>{@link #RESULT_SMS_OUT_OF_MEMORY}</code>,
+             *   indicating the error returned to the network.</li>
+             * </ul>
+
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_REJECTED_ACTION =
+                "android.provider.Telephony.SMS_REJECTED";
+
+            /**
+             * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
+             * {@link #DATA_SMS_RECEIVED_ACTION} intent.
+             *
+             * @param intent the intent to read from
+             * @return an array of SmsMessages for the PDUs
+             */
+            public static SmsMessage[] getMessagesFromIntent(
+                    Intent intent) {
+                Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
+                String format = intent.getStringExtra("format");
+                byte[][] pduObjs = new byte[messages.length][];
+
+                for (int i = 0; i < messages.length; i++) {
+                    pduObjs[i] = (byte[]) messages[i];
+                }
+                byte[][] pdus = new byte[pduObjs.length][];
+                int pduCount = pdus.length;
+                SmsMessage[] msgs = new SmsMessage[pduCount];
+                for (int i = 0; i < pduCount; i++) {
+                    pdus[i] = pduObjs[i];
+                    msgs[i] = SmsMessage.createFromPdu(pdus[i], format);
+                }
+                return msgs;
+            }
+        }
+    }
+
+    /**
+     * Base columns for tables that contain MMSs.
+     */
+    public interface BaseMmsColumns extends BaseColumns {
+
+        public static final int MESSAGE_BOX_ALL    = 0;
+        public static final int MESSAGE_BOX_INBOX  = 1;
+        public static final int MESSAGE_BOX_SENT   = 2;
+        public static final int MESSAGE_BOX_DRAFTS = 3;
+        public static final int MESSAGE_BOX_OUTBOX = 4;
+
+        /**
+         * The date the message was received.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE = "date";
+
+        /**
+         * The date the message was sent.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE_SENT = "date_sent";
+
+        /**
+         * The box which the message belong to, for example, MESSAGE_BOX_INBOX.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_BOX = "msg_box";
+
+        /**
+         * Has the message been read.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String READ = "read";
+
+        /**
+         * Indicates whether this message has been seen by the user. The "seen" flag will be
+         * used to figure out whether we need to throw up a statusbar notification or not.
+         */
+        public static final String SEEN = "seen";
+
+        /**
+         * The Message-ID of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MESSAGE_ID = "m_id";
+
+        /**
+         * The subject of the message, if present.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SUBJECT = "sub";
+
+        /**
+         * The character set of the subject, if present.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SUBJECT_CHARSET = "sub_cs";
+
+        /**
+         * The Content-Type of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CONTENT_TYPE = "ct_t";
+
+        /**
+         * The Content-Location of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CONTENT_LOCATION = "ct_l";
+
+        /**
+         * The address of the sender.
+         * <P>Type: TEXT</P>
+         */
+        public static final String FROM = "from";
+
+        /**
+         * The address of the recipients.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TO = "to";
+
+        /**
+         * The address of the cc. recipients.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CC = "cc";
+
+        /**
+         * The address of the bcc. recipients.
+         * <P>Type: TEXT</P>
+         */
+        public static final String BCC = "bcc";
+
+        /**
+         * The expiry time of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String EXPIRY = "exp";
+
+        /**
+         * The class of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MESSAGE_CLASS = "m_cls";
+
+        /**
+         * The type of the message defined by MMS spec.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_TYPE = "m_type";
+
+        /**
+         * The version of specification that this message conform.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MMS_VERSION = "v";
+
+        /**
+         * The size of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_SIZE = "m_size";
+
+        /**
+         * The priority of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PRIORITY = "pri";
+
+        /**
+         * The read-report of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String READ_REPORT = "rr";
+
+        /**
+         * Whether the report is allowed.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPORT_ALLOWED = "rpt_a";
+
+        /**
+         * The response-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String RESPONSE_STATUS = "resp_st";
+
+        /**
+         * The status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String STATUS = "st";
+
+        /**
+         * The transaction-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TRANSACTION_ID = "tr_id";
+
+        /**
+         * The retrieve-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String RETRIEVE_STATUS = "retr_st";
+
+        /**
+         * The retrieve-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RETRIEVE_TEXT = "retr_txt";
+
+        /**
+         * The character set of the retrieve-text.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RETRIEVE_TEXT_CHARSET = "retr_txt_cs";
+
+        /**
+         * The read-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String READ_STATUS = "read_status";
+
+        /**
+         * The content-class of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CONTENT_CLASS = "ct_cls";
+
+        /**
+         * The delivery-report of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DELIVERY_REPORT = "d_rpt";
+
+        /**
+         * The delivery-time-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DELIVERY_TIME_TOKEN = "d_tm_tok";
+
+        /**
+         * The delivery-time of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DELIVERY_TIME = "d_tm";
+
+        /**
+         * The response-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RESPONSE_TEXT = "resp_txt";
+
+        /**
+         * The sender-visibility of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SENDER_VISIBILITY = "s_vis";
+
+        /**
+         * The reply-charging of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING = "r_chg";
+
+        /**
+         * The reply-charging-deadline-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING_DEADLINE_TOKEN = "r_chg_dl_tok";
+
+        /**
+         * The reply-charging-deadline of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING_DEADLINE = "r_chg_dl";
+
+        /**
+         * The reply-charging-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPLY_CHARGING_ID = "r_chg_id";
+
+        /**
+         * The reply-charging-size of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String REPLY_CHARGING_SIZE = "r_chg_sz";
+
+        /**
+         * The previously-sent-by of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PREVIOUSLY_SENT_BY = "p_s_by";
+
+        /**
+         * The previously-sent-date of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String PREVIOUSLY_SENT_DATE = "p_s_d";
+
+        /**
+         * The store of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORE = "store";
+
+        /**
+         * The mm-state of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MM_STATE = "mm_st";
+
+        /**
+         * The mm-flags-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MM_FLAGS_TOKEN = "mm_flg_tok";
+
+        /**
+         * The mm-flags of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MM_FLAGS = "mm_flg";
+
+        /**
+         * The store-status of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORE_STATUS = "store_st";
+
+        /**
+         * The store-status-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORE_STATUS_TEXT = "store_st_txt";
+
+        /**
+         * The stored of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STORED = "stored";
+
+        /**
+         * The totals of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TOTALS = "totals";
+
+        /**
+         * The mbox-totals of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MBOX_TOTALS = "mb_t";
+
+        /**
+         * The mbox-totals-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MBOX_TOTALS_TOKEN = "mb_t_tok";
+
+        /**
+         * The quotas of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String QUOTAS = "qt";
+
+        /**
+         * The mbox-quotas of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MBOX_QUOTAS = "mb_qt";
+
+        /**
+         * The mbox-quotas-token of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MBOX_QUOTAS_TOKEN = "mb_qt_tok";
+
+        /**
+         * The message-count of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_COUNT = "m_cnt";
+
+        /**
+         * The start of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String START = "start";
+
+        /**
+         * The distribution-indicator of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String DISTRIBUTION_INDICATOR = "d_ind";
+
+        /**
+         * The element-descriptor of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ELEMENT_DESCRIPTOR = "e_des";
+
+        /**
+         * The limit of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String LIMIT = "limit";
+
+        /**
+         * The recommended-retrieval-mode of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String RECOMMENDED_RETRIEVAL_MODE = "r_r_mod";
+
+        /**
+         * The recommended-retrieval-mode-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RECOMMENDED_RETRIEVAL_MODE_TEXT = "r_r_mod_txt";
+
+        /**
+         * The status-text of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String STATUS_TEXT = "st_txt";
+
+        /**
+         * The applic-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String APPLIC_ID = "apl_id";
+
+        /**
+         * The reply-applic-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPLY_APPLIC_ID = "r_apl_id";
+
+        /**
+         * The aux-applic-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String AUX_APPLIC_ID = "aux_apl_id";
+
+        /**
+         * The drm-content of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String DRM_CONTENT = "drm_c";
+
+        /**
+         * The adaptation-allowed of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ADAPTATION_ALLOWED = "adp_a";
+
+        /**
+         * The replace-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String REPLACE_ID = "repl_id";
+
+        /**
+         * The cancel-id of the message.
+         * <P>Type: TEXT</P>
+         */
+        public static final String CANCEL_ID = "cl_id";
+
+        /**
+         * The cancel-status of the message.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CANCEL_STATUS = "cl_st";
+
+        /**
+         * The thread ID of the message
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THREAD_ID = "thread_id";
+
+        /**
+         * Has the message been locked?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LOCKED = "locked";
+
+        /**
+         * Meta data used externally.
+         * <P>Type: TEXT</P>
+         */
+        public static final String META_DATA = "meta_data";
+    }
+
+    /**
+     * Columns for the "canonical_addresses" table used by MMS and
+     * SMS."
+     */
+    public interface CanonicalAddressesColumns extends BaseColumns {
+        /**
+         * An address used in MMS or SMS.  Email addresses are
+         * converted to lower case and are compared by string
+         * equality.  Other addresses are compared using
+         * PHONE_NUMBERS_EQUAL.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ADDRESS = "address";
+    }
+
+    /**
+     * Columns for the "threads" table used by MMS and SMS.
+     */
+    public interface ThreadsColumns extends BaseColumns {
+        /**
+         * The date at which the thread was created.
+         *
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DATE = "date";
+
+        /**
+         * A string encoding of the recipient IDs of the recipients of
+         * the message, in numerical order and separated by spaces.
+         * <P>Type: TEXT</P>
+         */
+        public static final String RECIPIENT_IDS = "recipient_ids";
+
+        /**
+         * The message count of the thread.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_COUNT = "message_count";
+        /**
+         * Indicates whether all messages of the thread have been read.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String READ = "read";
+
+        /**
+         * The snippet of the latest message in the thread.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SNIPPET = "snippet";
+        /**
+         * The charset of the snippet.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SNIPPET_CHARSET = "snippet_cs";
+        /**
+         * Type of the thread, either Threads.COMMON_THREAD or
+         * Threads.BROADCAST_THREAD.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String TYPE = "type";
+        /**
+         * Indicates whether there is a transmission error in the thread.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ERROR = "error";
+        /**
+         * Indicates whether this thread contains any attachments.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String HAS_ATTACHMENT = "has_attachment";
+    }
+
+    /**
+     * Helper functions for the "threads" table used by MMS and SMS.
+     */
+    public static final class Threads implements ThreadsColumns {
+        private static final String[] ID_PROJECTION = { BaseColumns._ID };
+        private static final String STANDARD_ENCODING = "UTF-8";
+        private static final Uri THREAD_ID_CONTENT_URI = Uri.parse(
+                "content://mms-sms/threadID");
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(
+                MmsSms.CONTENT_URI, "conversations");
+        public static final Uri OBSOLETE_THREADS_URI = Uri.withAppendedPath(
+                CONTENT_URI, "obsolete");
+
+        public static final int COMMON_THREAD    = 0;
+        public static final int BROADCAST_THREAD = 1;
+
+        // No one should construct an instance of this class.
+        private Threads() {
+        }
+
+        /**
+         * This is a single-recipient version of
+         * getOrCreateThreadId.  It's convenient for use with SMS
+         * messages.
+         */
+        public static long getOrCreateThreadId(Context context, String recipient) {
+            Set<String> recipients = new HashSet<String>();
+
+            recipients.add(recipient);
+            return getOrCreateThreadId(context, recipients);
+        }
+
+        /**
+         * Given the recipients list and subject of an unsaved message,
+         * return its thread ID.  If the message starts a new thread,
+         * allocate a new thread ID.  Otherwise, use the appropriate
+         * existing thread ID.
+         *
+         * Find the thread ID of the same set of recipients (in
+         * any order, without any additions). If one
+         * is found, return it.  Otherwise, return a unique thread ID.
+         */
+        public static long getOrCreateThreadId(
+                Context context, Set<String> recipients) {
+            Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();
+
+            for (String recipient : recipients) {
+                if (Mms.isEmailAddress(recipient)) {
+                    recipient = Mms.extractAddrSpec(recipient);
+                }
+
+                uriBuilder.appendQueryParameter("recipient", recipient);
+            }
+
+            Uri uri = uriBuilder.build();
+            //if (DEBUG) Log.v(TAG, "getOrCreateThreadId uri: " + uri);
+
+            Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
+                    uri, ID_PROJECTION, null, null, null);
+            if (cursor != null) {
+                try {
+                    if (cursor.moveToFirst()) {
+                        return cursor.getLong(0);
+                    } else {
+                        Log.e(TAG, "getOrCreateThreadId returned no rows!");
+                    }
+                } finally {
+                    cursor.close();
+                }
+            }
+
+            Log.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString());
+            throw new IllegalArgumentException("Unable to find or allocate a thread ID.");
+        }
+    }
+
+    /**
+     * Contains all MMS messages.
+     */
+    public static final class Mms implements BaseMmsColumns {
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://mms");
+
+        public static final Uri REPORT_REQUEST_URI = Uri.withAppendedPath(
+                                            CONTENT_URI, "report-request");
+
+        public static final Uri REPORT_STATUS_URI = Uri.withAppendedPath(
+                                            CONTENT_URI, "report-status");
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "date DESC";
+
+        /**
+         * mailbox         =       name-addr
+         * name-addr       =       [display-name] angle-addr
+         * angle-addr      =       [CFWS] "<" addr-spec ">" [CFWS]
+         */
+        public static final Pattern NAME_ADDR_EMAIL_PATTERN =
+                Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*");
+
+        /**
+         * quoted-string   =       [CFWS]
+         *                         DQUOTE *([FWS] qcontent) [FWS] DQUOTE
+         *                         [CFWS]
+         */
+        public static final Pattern QUOTED_STRING_PATTERN =
+                Pattern.compile("\\s*\"([^\"]*)\"\\s*");
+
+        public static final Cursor query(
+                ContentResolver cr, String[] projection) {
+            return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
+        }
+
+        public static final Cursor query(
+                ContentResolver cr, String[] projection,
+                String where, String orderBy) {
+            return cr.query(CONTENT_URI, projection,
+                    where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        }
+
+        public static final String getMessageBoxName(int msgBox) {
+            switch (msgBox) {
+                case MESSAGE_BOX_ALL:
+                    return "all";
+                case MESSAGE_BOX_INBOX:
+                    return "inbox";
+                case MESSAGE_BOX_SENT:
+                    return "sent";
+                case MESSAGE_BOX_DRAFTS:
+                    return "drafts";
+                case MESSAGE_BOX_OUTBOX:
+                    return "outbox";
+                default:
+                    throw new IllegalArgumentException("Invalid message box: " + msgBox);
+            }
+        }
+
+        public static String extractAddrSpec(String address) {
+            Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address);
+
+            if (match.matches()) {
+                return match.group(2);
+            }
+            return address;
+        }
+
+        /**
+         * Returns true if the address is an email address
+         *
+         * @param address the input address to be tested
+         * @return true if address is an email address
+         */
+        public static boolean isEmailAddress(String address) {
+            if (TextUtils.isEmpty(address)) {
+                return false;
+            }
+
+            String s = extractAddrSpec(address);
+            Matcher match = Patterns.EMAIL_ADDRESS.matcher(s);
+            return match.matches();
+        }
+
+        /**
+         * Returns true if the number is a Phone number
+         *
+         * @param number the input number to be tested
+         * @return true if number is a Phone number
+         */
+        public static boolean isPhoneNumber(String number) {
+            if (TextUtils.isEmpty(number)) {
+                return false;
+            }
+
+            Matcher match = Patterns.PHONE.matcher(number);
+            return match.matches();
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's inbox.
+         */
+        public static final class Inbox implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/inbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's sent box.
+         */
+        public static final class Sent implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/sent");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's drafts box.
+         */
+        public static final class Draft implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/drafts");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        /**
+         * Contains all MMS messages in the MMS app's outbox.
+         */
+        public static final class Outbox implements BaseMmsColumns {
+            /**
+             * The content:// style URL for this table
+             */
+            public static final Uri
+                    CONTENT_URI = Uri.parse("content://mms/outbox");
+
+            /**
+             * The default sort order for this table
+             */
+            public static final String DEFAULT_SORT_ORDER = "date DESC";
+        }
+
+        public static final class Addr implements BaseColumns {
+            /**
+             * The ID of MM which this address entry belongs to.
+             */
+            public static final String MSG_ID = "msg_id";
+
+            /**
+             * The ID of contact entry in Phone Book.
+             */
+            public static final String CONTACT_ID = "contact_id";
+
+            /**
+             * The address text.
+             */
+            public static final String ADDRESS = "address";
+
+            /**
+             * Type of address, must be one of PduHeaders.BCC,
+             * PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO.
+             */
+            public static final String TYPE = "type";
+
+            /**
+             * Character set of this entry.
+             */
+            public static final String CHARSET = "charset";
+        }
+
+        public static final class Part implements BaseColumns {
+            /**
+             * The identifier of the message which this part belongs to.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String MSG_ID = "mid";
+
+            /**
+             * The order of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String SEQ = "seq";
+
+            /**
+             * The content type of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CONTENT_TYPE = "ct";
+
+            /**
+             * The name of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String NAME = "name";
+
+            /**
+             * The charset of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CHARSET = "chset";
+
+            /**
+             * The file name of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String FILENAME = "fn";
+
+            /**
+             * The content disposition of the part.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CONTENT_DISPOSITION = "cd";
+
+            /**
+             * The content ID of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String CONTENT_ID = "cid";
+
+            /**
+             * The content location of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String CONTENT_LOCATION = "cl";
+
+            /**
+             * The start of content-type of the message.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String CT_START = "ctt_s";
+
+            /**
+             * The type of content-type of the message.
+             * <P>Type: TEXT</P>
+             */
+            public static final String CT_TYPE = "ctt_t";
+
+            /**
+             * The location(on filesystem) of the binary data of the part.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String _DATA = "_data";
+
+            public static final String TEXT = "text";
+
+        }
+
+        public static final class Rate {
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(
+                    Mms.CONTENT_URI, "rate");
+            /**
+             * When a message was successfully sent.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String SENT_TIME = "sent_time";
+        }
+
+        public static final class Intents {
+            private Intents() {
+                // Non-instantiatable.
+            }
+
+            /**
+             * The extra field to store the contents of the Intent,
+             * which should be an array of Uri.
+             */
+            public static final String EXTRA_CONTENTS = "contents";
+            /**
+             * The extra field to store the type of the contents,
+             * which should be an array of String.
+             */
+            public static final String EXTRA_TYPES    = "types";
+            /**
+             * The extra field to store the 'Cc' addresses.
+             */
+            public static final String EXTRA_CC       = "cc";
+            /**
+             * The extra field to store the 'Bcc' addresses;
+             */
+            public static final String EXTRA_BCC      = "bcc";
+            /**
+             * The extra field to store the 'Subject'.
+             */
+            public static final String EXTRA_SUBJECT  = "subject";
+            /**
+             * Indicates that the contents of specified URIs were changed.
+             * The application which is showing or caching these contents
+             * should be updated.
+             */
+            public static final String
+            CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED";
+            /**
+             * An extra field which stores the URI of deleted contents.
+             */
+            public static final String DELETED_CONTENTS = "deleted_contents";
+        }
+    }
+
+    /**
+     * Contains all MMS and SMS messages.
+     */
+    public static final class MmsSms implements BaseColumns {
+        /**
+         * The column to distinguish SMS &amp; MMS messages in query results.
+         */
+        public static final String TYPE_DISCRIMINATOR_COLUMN =
+                "transport_type";
+
+        public static final Uri CONTENT_URI = Uri.parse("content://mms-sms/");
+
+        public static final Uri CONTENT_CONVERSATIONS_URI = Uri.parse(
+                "content://mms-sms/conversations");
+
+        public static final Uri CONTENT_FILTER_BYPHONE_URI = Uri.parse(
+                "content://mms-sms/messages/byphone");
+
+        public static final Uri CONTENT_UNDELIVERED_URI = Uri.parse(
+                "content://mms-sms/undelivered");
+
+        public static final Uri CONTENT_DRAFT_URI = Uri.parse(
+                "content://mms-sms/draft");
+
+        public static final Uri CONTENT_LOCKED_URI = Uri.parse(
+                "content://mms-sms/locked");
+
+        /***
+         * Pass in a query parameter called "pattern" which is the text
+         * to search for.
+         * The sort order is fixed to be thread_id ASC,date DESC.
+         */
+        public static final Uri SEARCH_URI = Uri.parse(
+                "content://mms-sms/search");
+
+        // Constants for message protocol types.
+        public static final int SMS_PROTO = 0;
+        public static final int MMS_PROTO = 1;
+
+        // Constants for error types of pending messages.
+        public static final int NO_ERROR                      = 0;
+        public static final int ERR_TYPE_GENERIC              = 1;
+        public static final int ERR_TYPE_SMS_PROTO_TRANSIENT  = 2;
+        public static final int ERR_TYPE_MMS_PROTO_TRANSIENT  = 3;
+        public static final int ERR_TYPE_TRANSPORT_FAILURE    = 4;
+        public static final int ERR_TYPE_GENERIC_PERMANENT    = 10;
+        public static final int ERR_TYPE_SMS_PROTO_PERMANENT  = 11;
+        public static final int ERR_TYPE_MMS_PROTO_PERMANENT  = 12;
+
+        public static final class PendingMessages implements BaseColumns {
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(
+                    MmsSms.CONTENT_URI, "pending");
+            /**
+             * The type of transport protocol(MMS or SMS).
+             * <P>Type: INTEGER</P>
+             */
+            public static final String PROTO_TYPE = "proto_type";
+            /**
+             * The ID of the message to be sent or downloaded.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String MSG_ID = "msg_id";
+            /**
+             * The type of the message to be sent or downloaded.
+             * This field is only valid for MM. For SM, its value is always
+             * set to 0.
+             */
+            public static final String MSG_TYPE = "msg_type";
+            /**
+             * The type of the error code.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String ERROR_TYPE = "err_type";
+            /**
+             * The error code of sending/retrieving process.
+             * <P>Type:  INTEGER</P>
+             */
+            public static final String ERROR_CODE = "err_code";
+            /**
+             * How many times we tried to send or download the message.
+             * <P>Type:  INTEGER</P>
+             */
+            public static final String RETRY_INDEX = "retry_index";
+            /**
+             * The time to do next retry.
+             */
+            public static final String DUE_TIME = "due_time";
+            /**
+             * The time we last tried to send or download the message.
+             */
+            public static final String LAST_TRY = "last_try";
+        }
+
+        public static final class WordsTable {
+            public static final String ID = "_id";
+            public static final String SOURCE_ROW_ID = "source_id";
+            public static final String TABLE_ID = "table_to_use";
+            public static final String INDEXED_TEXT = "index_text";
+        }
+    }
+
+    public static final class Carriers implements BaseColumns {
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://telephony/carriers");
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "name ASC";
+
+        public static final String NAME = "name";
+
+        public static final String APN = "apn";
+
+        public static final String PROXY = "proxy";
+
+        public static final String PORT = "port";
+
+        public static final String MMSPROXY = "mmsproxy";
+
+        public static final String MMSPORT = "mmsport";
+
+        public static final String SERVER = "server";
+
+        public static final String USER = "user";
+
+        public static final String PASSWORD = "password";
+
+        public static final String MMSC = "mmsc";
+
+        public static final String MCC = "mcc";
+
+        public static final String MNC = "mnc";
+
+        public static final String NUMERIC = "numeric";
+
+        public static final String AUTH_TYPE = "authtype";
+
+        public static final String TYPE = "type";
+
+        public static final String INACTIVE_TIMER = "inactivetimer";
+
+        // Only if enabled try Data Connection.
+        public static final String ENABLED = "enabled";
+
+        // Rules apply based on class.
+        public static final String CLASS = "class";
+
+        /**
+         * The protocol to be used to connect to this APN.
+         *
+         * One of the PDP_type values in TS 27.007 section 10.1.1.
+         * For example, "IP", "IPV6", "IPV4V6", or "PPP".
+         */
+        public static final String PROTOCOL = "protocol";
+
+        /**
+          * The protocol to be used to connect to this APN when roaming.
+          *
+          * The syntax is the same as protocol.
+          */
+        public static final String ROAMING_PROTOCOL = "roaming_protocol";
+
+        public static final String CURRENT = "current";
+
+        /**
+          * Current status of APN
+          * true : enabled APN, false : disabled APN.
+          */
+        public static final String CARRIER_ENABLED = "carrier_enabled";
+
+        /**
+          * Radio Access Technology info
+          * To check what values can hold, refer to ServiceState.java.
+          * This should be spread to other technologies,
+          * but currently only used for LTE(14) and EHRPD(13).
+          */
+        public static final String BEARER = "bearer";
+    }
+
+    /**
+     * Contains received SMS cell broadcast messages.
+     */
+    public static final class CellBroadcasts implements BaseColumns {
+
+        /** Not instantiable. */
+        private CellBroadcasts() {}
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://cellbroadcasts");
+
+        /**
+         * Message geographical scope.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
+
+        /**
+         * Message serial number.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SERIAL_NUMBER = "serial_number";
+
+        /**
+         * PLMN of broadcast sender. (SERIAL_NUMBER + PLMN + LAC + CID) uniquely identifies a
+         * broadcast for duplicate detection purposes.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PLMN = "plmn";
+
+        /**
+         * Location Area (GSM) or Service Area (UMTS) of broadcast sender. Unused for CDMA.
+         * Only included if Geographical Scope of message is not PLMN wide (01).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String LAC = "lac";
+
+        /**
+         * Cell ID of message sender (GSM/UMTS). Unused for CDMA. Only included when the
+         * Geographical Scope of message is cell wide (00 or 11).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CID = "cid";
+
+        /**
+         * Message code (OBSOLETE: merged into SERIAL_NUMBER).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String V1_MESSAGE_CODE = "message_code";
+
+        /**
+         * Message identifier (OBSOLETE: renamed to SERVICE_CATEGORY).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String V1_MESSAGE_IDENTIFIER = "message_id";
+
+        /**
+         * Service category (GSM/UMTS message identifier, CDMA service category).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SERVICE_CATEGORY = "service_category";
+
+        /**
+         * Message language code.
+         * <P>Type: TEXT</P>
+         */
+        public static final String LANGUAGE_CODE = "language";
+
+        /**
+         * Message body.
+         * <P>Type: TEXT</P>
+         */
+        public static final String MESSAGE_BODY = "body";
+
+        /**
+         * Message delivery time.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String DELIVERY_TIME = "date";
+
+        /**
+         * Has the message been viewed?
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String MESSAGE_READ = "read";
+
+        /**
+         * Message format (3GPP or 3GPP2).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_FORMAT = "format";
+
+        /**
+         * Message priority (including emergency).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String MESSAGE_PRIORITY = "priority";
+
+        /**
+         * ETWS warning type (ETWS alerts only).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ETWS_WARNING_TYPE = "etws_warning_type";
+
+        /**
+         * CMAS message class (CMAS alerts only).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CMAS_MESSAGE_CLASS = "cmas_message_class";
+
+        /**
+         * CMAS category (CMAS alerts only).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CMAS_CATEGORY = "cmas_category";
+
+        /**
+         * CMAS response type (CMAS alerts only).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CMAS_RESPONSE_TYPE = "cmas_response_type";
+
+        /**
+         * CMAS severity (CMAS alerts only).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CMAS_SEVERITY = "cmas_severity";
+
+        /**
+         * CMAS urgency (CMAS alerts only).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CMAS_URGENCY = "cmas_urgency";
+
+        /**
+         * CMAS certainty (CMAS alerts only).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String CMAS_CERTAINTY = "cmas_certainty";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC";
+
+        /**
+         * Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects.
+         */
+        public static final String[] QUERY_COLUMNS = {
+                _ID,
+                GEOGRAPHICAL_SCOPE,
+                PLMN,
+                LAC,
+                CID,
+                SERIAL_NUMBER,
+                SERVICE_CATEGORY,
+                LANGUAGE_CODE,
+                MESSAGE_BODY,
+                DELIVERY_TIME,
+                MESSAGE_READ,
+                MESSAGE_FORMAT,
+                MESSAGE_PRIORITY,
+                ETWS_WARNING_TYPE,
+                CMAS_MESSAGE_CLASS,
+                CMAS_CATEGORY,
+                CMAS_RESPONSE_TYPE,
+                CMAS_SEVERITY,
+                CMAS_URGENCY,
+                CMAS_CERTAINTY
+        };
+    }
+}
diff --git a/src/java/android/telephony/CellBroadcastMessage.java b/src/java/android/telephony/CellBroadcastMessage.java
new file mode 100644 (file)
index 0000000..36c238d
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * 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.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Telephony;
+import android.telephony.SmsCbCmasInfo;
+import android.telephony.SmsCbEtwsInfo;
+import android.telephony.SmsCbLocation;
+import android.telephony.SmsCbMessage;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.style.StyleSpan;
+
+/**
+ * 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;
+
+    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);
+    }
+
+    /** Parcelable: no special flags. */
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        mSmsCbMessage.writeToParcel(out, flags);
+        out.writeLong(mDeliveryTime);
+        out.writeInt(mIsRead ? 1 : 0);
+    }
+
+    public static final Parcelable.Creator<CellBroadcastMessage> CREATOR
+            = new Parcelable.Creator<CellBroadcastMessage>() {
+        public CellBroadcastMessage createFromParcel(Parcel in) {
+            return new CellBroadcastMessage(in);
+        }
+
+        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
+     */
+    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, 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
+     */
+    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;
+    }
+
+    public String getLanguageCode() {
+        return mSmsCbMessage.getLanguageCode();
+    }
+
+    public int getServiceCategory() {
+        return mSmsCbMessage.getServiceCategory();
+    }
+
+    public long getDeliveryTime() {
+        return mDeliveryTime;
+    }
+
+    public String getMessageBody() {
+        return mSmsCbMessage.getMessageBody();
+    }
+
+    public boolean isRead() {
+        return mIsRead;
+    }
+
+    public int getSerialNumber() {
+        return mSmsCbMessage.getSerialNumber();
+    }
+
+    public SmsCbCmasInfo getCmasWarningInfo() {
+        return mSmsCbMessage.getCmasWarningInfo();
+    }
+
+    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, but excluding lower priority Amber alert broadcasts.
+     *
+     * @return true if the message is PWS type, excluding Amber alerts
+     */
+    public boolean isEmergencyAlertMessage() {
+        if (!mSmsCbMessage.isEmergencyMessage()) {
+            return false;
+        }
+        SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo();
+        if (cmasInfo != null &&
+                cmasInfo.getMessageClass() == SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Return whether the broadcast is an ETWS emergency message type.
+     * @return true if the message is ETWS emergency type; false otherwise
+     */
+    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
+     */
+    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
+     */
+    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/android/telephony/SmsCbCmasInfo.java b/src/java/android/telephony/SmsCbCmasInfo.java
new file mode 100644 (file)
index 0000000..7a89d94
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * 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 android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains CMAS warning notification Type 1 elements for a {@link SmsCbMessage}.
+ * Supported values for each element are defined in TIA-1149-0-1 (CMAS over CDMA) and
+ * 3GPP TS 23.041 (for GSM/UMTS).
+ *
+ * {@hide}
+ */
+public class SmsCbCmasInfo implements Parcelable {
+
+    // CMAS message class (in GSM/UMTS message identifier or CDMA service category).
+
+    /** Presidential-level alert (Korean Public Alert System Class 0 message). */
+    public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0x00;
+
+    /** Extreme threat to life and property (Korean Public Alert System Class 1 message). */
+    public static final int CMAS_CLASS_EXTREME_THREAT = 0x01;
+
+    /** Severe threat to life and property (Korean Public Alert System Class 1 message). */
+    public static final int CMAS_CLASS_SEVERE_THREAT = 0x02;
+
+    /** Child abduction emergency (AMBER Alert). */
+    public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 0x03;
+
+    /** CMAS test message. */
+    public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 0x04;
+
+    /** CMAS exercise. */
+    public static final int CMAS_CLASS_CMAS_EXERCISE = 0x05;
+
+    /** CMAS category for operator defined use. */
+    public static final int CMAS_CLASS_OPERATOR_DEFINED_USE = 0x06;
+
+    /** CMAS category for warning types that are reserved for future extension. */
+    public static final int CMAS_CLASS_UNKNOWN = -1;
+
+    // CMAS alert category (in CDMA type 1 elements record).
+
+    /** CMAS alert category: Geophysical including landslide. */
+    public static final int CMAS_CATEGORY_GEO = 0x00;
+
+    /** CMAS alert category: Meteorological including flood. */
+    public static final int CMAS_CATEGORY_MET = 0x01;
+
+    /** CMAS alert category: General emergency and public safety. */
+    public static final int CMAS_CATEGORY_SAFETY = 0x02;
+
+    /** CMAS alert category: Law enforcement, military, homeland/local/private security. */
+    public static final int CMAS_CATEGORY_SECURITY = 0x03;
+
+    /** CMAS alert category: Rescue and recovery. */
+    public static final int CMAS_CATEGORY_RESCUE = 0x04;
+
+    /** CMAS alert category: Fire suppression and rescue. */
+    public static final int CMAS_CATEGORY_FIRE = 0x05;
+
+    /** CMAS alert category: Medical and public health. */
+    public static final int CMAS_CATEGORY_HEALTH = 0x06;
+
+    /** CMAS alert category: Pollution and other environmental. */
+    public static final int CMAS_CATEGORY_ENV = 0x07;
+
+    /** CMAS alert category: Public and private transportation. */
+    public static final int CMAS_CATEGORY_TRANSPORT = 0x08;
+
+    /** CMAS alert category: Utility, telecom, other non-transport infrastructure. */
+    public static final int CMAS_CATEGORY_INFRA = 0x09;
+
+    /** CMAS alert category: Chem, bio, radiological, nuclear, high explosive threat or attack. */
+    public static final int CMAS_CATEGORY_CBRNE = 0x0a;
+
+    /** CMAS alert category: Other events. */
+    public static final int CMAS_CATEGORY_OTHER = 0x0b;
+
+    /**
+     * CMAS alert category is unknown. The category is only available for CDMA broadcasts
+     * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+     */
+    public static final int CMAS_CATEGORY_UNKNOWN = -1;
+
+    // CMAS response type (in CDMA type 1 elements record).
+
+    /** CMAS response type: Take shelter in place. */
+    public static final int CMAS_RESPONSE_TYPE_SHELTER = 0x00;
+
+    /** CMAS response type: Evacuate (Relocate). */
+    public static final int CMAS_RESPONSE_TYPE_EVACUATE = 0x01;
+
+    /** CMAS response type: Make preparations. */
+    public static final int CMAS_RESPONSE_TYPE_PREPARE = 0x02;
+
+    /** CMAS response type: Execute a pre-planned activity. */
+    public static final int CMAS_RESPONSE_TYPE_EXECUTE = 0x03;
+
+    /** CMAS response type: Attend to information sources. */
+    public static final int CMAS_RESPONSE_TYPE_MONITOR = 0x04;
+
+    /** CMAS response type: Avoid hazard. */
+    public static final int CMAS_RESPONSE_TYPE_AVOID = 0x05;
+
+    /** CMAS response type: Evaluate the information in this message (not for public warnings). */
+    public static final int CMAS_RESPONSE_TYPE_ASSESS = 0x06;
+
+    /** CMAS response type: No action recommended. */
+    public static final int CMAS_RESPONSE_TYPE_NONE = 0x07;
+
+    /**
+     * CMAS response type is unknown. The response type is only available for CDMA broadcasts
+     * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
+     */
+    public static final int CMAS_RESPONSE_TYPE_UNKNOWN = -1;
+
+    // 4-bit CMAS severity (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+    /** CMAS severity type: Extraordinary threat to life or property. */
+    public static final int CMAS_SEVERITY_EXTREME = 0x0;
+
+    /** CMAS severity type: Significant threat to life or property. */
+    public static final int CMAS_SEVERITY_SEVERE = 0x1;
+
+    /**
+     * CMAS alert severity is unknown. The severity is available for CDMA warning alerts
+     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+     * Presidential-level alert class (Korean Public Alert System Class 0).
+     */
+    public static final int CMAS_SEVERITY_UNKNOWN = -1;
+
+    // CMAS urgency (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+    /** CMAS urgency type: Responsive action should be taken immediately. */
+    public static final int CMAS_URGENCY_IMMEDIATE = 0x0;
+
+    /** CMAS urgency type: Responsive action should be taken within the next hour. */
+    public static final int CMAS_URGENCY_EXPECTED = 0x1;
+
+    /**
+     * CMAS alert urgency is unknown. The urgency is available for CDMA warning alerts
+     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+     * Presidential-level alert class (Korean Public Alert System Class 0).
+     */
+    public static final int CMAS_URGENCY_UNKNOWN = -1;
+
+    // CMAS certainty (in GSM/UMTS message identifier or CDMA type 1 elements record).
+
+    /** CMAS certainty type: Determined to have occurred or to be ongoing. */
+    public static final int CMAS_CERTAINTY_OBSERVED = 0x0;
+
+    /** CMAS certainty type: Likely (probability > ~50%). */
+    public static final int CMAS_CERTAINTY_LIKELY = 0x1;
+
+    /**
+     * CMAS alert certainty is unknown. The certainty is available for CDMA warning alerts
+     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
+     * Presidential-level alert class (Korean Public Alert System Class 0).
+     */
+    public static final int CMAS_CERTAINTY_UNKNOWN = -1;
+
+    /** CMAS message class. */
+    private final int mMessageClass;
+
+    /** CMAS category. */
+    private final int mCategory;
+
+    /** CMAS response type. */
+    private final int mResponseType;
+
+    /** CMAS severity. */
+    private final int mSeverity;
+
+    /** CMAS urgency. */
+    private final int mUrgency;
+
+    /** CMAS certainty. */
+    private final int mCertainty;
+
+    /** Create a new SmsCbCmasInfo object with the specified values. */
+    public SmsCbCmasInfo(int messageClass, int category, int responseType, int severity,
+            int urgency, int certainty) {
+        mMessageClass = messageClass;
+        mCategory = category;
+        mResponseType = responseType;
+        mSeverity = severity;
+        mUrgency = urgency;
+        mCertainty = certainty;
+    }
+
+    /** Create a new SmsCbCmasInfo object from a Parcel. */
+    SmsCbCmasInfo(Parcel in) {
+        mMessageClass = in.readInt();
+        mCategory = in.readInt();
+        mResponseType = in.readInt();
+        mSeverity = in.readInt();
+        mUrgency = in.readInt();
+        mCertainty = in.readInt();
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mMessageClass);
+        dest.writeInt(mCategory);
+        dest.writeInt(mResponseType);
+        dest.writeInt(mSeverity);
+        dest.writeInt(mUrgency);
+        dest.writeInt(mCertainty);
+    }
+
+    /**
+     * Returns the CMAS message class, e.g. {@link #CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT}.
+     * @return one of the {@code CMAS_CLASS} values
+     */
+    public int getMessageClass() {
+        return mMessageClass;
+    }
+
+    /**
+     * Returns the CMAS category, e.g. {@link #CMAS_CATEGORY_GEO}.
+     * @return one of the {@code CMAS_CATEGORY} values
+     */
+    public int getCategory() {
+        return mCategory;
+    }
+
+    /**
+     * Returns the CMAS response type, e.g. {@link #CMAS_RESPONSE_TYPE_SHELTER}.
+     * @return one of the {@code CMAS_RESPONSE_TYPE} values
+     */
+    public int getResponseType() {
+        return mResponseType;
+    }
+
+    /**
+     * Returns the CMAS severity, e.g. {@link #CMAS_SEVERITY_EXTREME}.
+     * @return one of the {@code CMAS_SEVERITY} values
+     */
+    public int getSeverity() {
+        return mSeverity;
+    }
+
+    /**
+     * Returns the CMAS urgency, e.g. {@link #CMAS_URGENCY_IMMEDIATE}.
+     * @return one of the {@code CMAS_URGENCY} values
+     */
+    public int getUrgency() {
+        return mUrgency;
+    }
+
+    /**
+     * Returns the CMAS certainty, e.g. {@link #CMAS_CERTAINTY_OBSERVED}.
+     * @return one of the {@code CMAS_CERTAINTY} values
+     */
+    public int getCertainty() {
+        return mCertainty;
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbCmasInfo{messageClass=" + mMessageClass + ", category=" + mCategory
+                + ", responseType=" + mResponseType + ", severity=" + mSeverity
+                + ", urgency=" + mUrgency + ", certainty=" + mCertainty + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Creator for unparcelling objects. */
+    public static final Parcelable.Creator<SmsCbCmasInfo>
+            CREATOR = new Parcelable.Creator<SmsCbCmasInfo>() {
+        public SmsCbCmasInfo createFromParcel(Parcel in) {
+            return new SmsCbCmasInfo(in);
+        }
+
+        public SmsCbCmasInfo[] newArray(int size) {
+            return new SmsCbCmasInfo[size];
+        }
+    };
+}
diff --git a/src/java/android/telephony/SmsCbEtwsInfo.java b/src/java/android/telephony/SmsCbEtwsInfo.java
new file mode 100644 (file)
index 0000000..0890d52
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * 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 android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.Time;
+
+import com.android.internal.telephony.IccUtils;
+
+import java.util.Arrays;
+
+/**
+ * Contains information elements for a GSM or UMTS ETWS warning notification.
+ * Supported values for each element are defined in 3GPP TS 23.041.
+ *
+ * {@hide}
+ */
+public class SmsCbEtwsInfo implements Parcelable {
+
+    /** ETWS warning type for earthquake. */
+    public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00;
+
+    /** ETWS warning type for tsunami. */
+    public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01;
+
+    /** ETWS warning type for earthquake and tsunami. */
+    public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02;
+
+    /** ETWS warning type for test messages. */
+    public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 0x03;
+
+    /** ETWS warning type for other emergency types. */
+    public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 0x04;
+
+    /** Unknown ETWS warning type. */
+    public static final int ETWS_WARNING_TYPE_UNKNOWN = -1;
+
+    /** One of the ETWS warning type constants defined in this class. */
+    private final int mWarningType;
+
+    /** Whether or not to activate the emergency user alert tone and vibration. */
+    private final boolean mEmergencyUserAlert;
+
+    /** Whether or not to activate a popup alert. */
+    private final boolean mActivatePopup;
+
+    /**
+     * 50-byte security information (ETWS primary notification for GSM only). As of Release 10,
+     * 3GPP TS 23.041 states that the UE shall ignore the ETWS primary notification timestamp
+     * and digital signature if received. Therefore it is treated as a raw byte array and
+     * parceled with the broadcast intent if present, but the timestamp is only computed if an
+     * application asks for the individual components.
+     */
+    private final byte[] mWarningSecurityInformation;
+
+    /** Create a new SmsCbEtwsInfo object with the specified values. */
+    public SmsCbEtwsInfo(int warningType, boolean emergencyUserAlert, boolean activatePopup,
+            byte[] warningSecurityInformation) {
+        mWarningType = warningType;
+        mEmergencyUserAlert = emergencyUserAlert;
+        mActivatePopup = activatePopup;
+        mWarningSecurityInformation = warningSecurityInformation;
+    }
+
+    /** Create a new SmsCbEtwsInfo object from a Parcel. */
+    SmsCbEtwsInfo(Parcel in) {
+        mWarningType = in.readInt();
+        mEmergencyUserAlert = (in.readInt() != 0);
+        mActivatePopup = (in.readInt() != 0);
+        mWarningSecurityInformation = in.createByteArray();
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mWarningType);
+        dest.writeInt(mEmergencyUserAlert ? 1 : 0);
+        dest.writeInt(mActivatePopup ? 1 : 0);
+        dest.writeByteArray(mWarningSecurityInformation);
+    }
+
+    /**
+     * Returns the ETWS warning type.
+     * @return a warning type such as {@link #ETWS_WARNING_TYPE_EARTHQUAKE}
+     */
+    public int getWarningType() {
+        return mWarningType;
+    }
+
+    /**
+     * Returns the ETWS emergency user alert flag.
+     * @return true to notify terminal to activate emergency user alert; false otherwise
+     */
+    public boolean isEmergencyUserAlert() {
+        return mEmergencyUserAlert;
+    }
+
+    /**
+     * Returns the ETWS activate popup flag.
+     * @return true to notify terminal to activate display popup; false otherwise
+     */
+    public boolean isPopupAlert() {
+        return mActivatePopup;
+    }
+
+    /**
+     * Returns the Warning-Security-Information timestamp (GSM primary notifications only).
+     * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received.
+     * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present
+     */
+    public long getPrimaryNotificationTimestamp() {
+        if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) {
+            return 0;
+        }
+
+        int year = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[0]);
+        int month = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[1]);
+        int day = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[2]);
+        int hour = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[3]);
+        int minute = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[4]);
+        int second = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[5]);
+
+        // For the timezone, the most significant bit of the
+        // least significant nibble is the sign byte
+        // (meaning the max range of this field is 79 quarter-hours,
+        // which is more than enough)
+
+        byte tzByte = mWarningSecurityInformation[6];
+
+        // Mask out sign bit.
+        int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
+
+        timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
+
+        Time time = new Time(Time.TIMEZONE_UTC);
+
+        // We only need to support years above 2000.
+        time.year = year + 2000;
+        time.month = month - 1;
+        time.monthDay = day;
+        time.hour = hour;
+        time.minute = minute;
+        time.second = second;
+
+        // Timezone offset is in quarter hours.
+        return time.toMillis(true) - (long) (timezoneOffset * 15 * 60 * 1000);
+    }
+
+    /**
+     * Returns the digital signature (GSM primary notifications only). As of Release 10,
+     * 3GPP TS 23.041 states that the UE shall ignore this value if received.
+     * @return a byte array containing a copy of the primary notification digital signature
+     */
+    public byte[] getPrimaryNotificationSignature() {
+        if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 50) {
+            return null;
+        }
+        return Arrays.copyOfRange(mWarningSecurityInformation, 7, 50);
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbEtwsInfo{warningType=" + mWarningType + ", emergencyUserAlert="
+                + mEmergencyUserAlert + ", activatePopup=" + mActivatePopup + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Creator for unparcelling objects. */
+    public static final Creator<SmsCbEtwsInfo> CREATOR = new Creator<SmsCbEtwsInfo>() {
+        public SmsCbEtwsInfo createFromParcel(Parcel in) {
+            return new SmsCbEtwsInfo(in);
+        }
+
+        public SmsCbEtwsInfo[] newArray(int size) {
+            return new SmsCbEtwsInfo[size];
+        }
+    };
+}
diff --git a/src/java/android/telephony/SmsCbLocation.java b/src/java/android/telephony/SmsCbLocation.java
new file mode 100644 (file)
index 0000000..7b5bd0d
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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 android.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.gsm.GsmCellLocation;
+
+/**
+ * Represents the location and geographical scope of a cell broadcast message.
+ * For GSM/UMTS, the Location Area and Cell ID are set when the broadcast
+ * geographical scope is cell wide or Location Area wide. For CDMA, the
+ * broadcast geographical scope is always PLMN wide.
+ *
+ * @hide
+ */
+public class SmsCbLocation implements Parcelable {
+
+    /** The PLMN. Note that this field may be an empty string, but isn't allowed to be null. */
+    private final String mPlmn;
+
+    private final int mLac;
+    private final int mCid;
+
+    /**
+     * Construct an empty location object. This is used for some test cases, and for
+     * cell broadcasts saved in older versions of the database without location info.
+     */
+    public SmsCbLocation() {
+        mPlmn = "";
+        mLac = -1;
+        mCid = -1;
+    }
+
+    /**
+     * Construct a location object for the PLMN. This class is immutable, so
+     * the same object can be reused for multiple broadcasts.
+     */
+    public SmsCbLocation(String plmn) {
+        mPlmn = plmn;
+        mLac = -1;
+        mCid = -1;
+    }
+
+    /**
+     * Construct a location object for the PLMN, LAC, and Cell ID. This class is immutable, so
+     * the same object can be reused for multiple broadcasts.
+     */
+    public SmsCbLocation(String plmn, int lac, int cid) {
+        mPlmn = plmn;
+        mLac = lac;
+        mCid = cid;
+    }
+
+    /**
+     * Initialize the object from a Parcel.
+     */
+    public SmsCbLocation(Parcel in) {
+        mPlmn = in.readString();
+        mLac = in.readInt();
+        mCid = in.readInt();
+    }
+
+    /**
+     * Returns the MCC/MNC of the network as a String.
+     * @return the PLMN identifier (MCC+MNC) as a String
+     */
+    public String getPlmn() {
+        return mPlmn;
+    }
+
+    /**
+     * Returns the GSM location area code, or UMTS service area code.
+     * @return location area code, -1 if unknown, 0xffff max legal value
+     */
+    public int getLac() {
+        return mLac;
+    }
+
+    /**
+     * Returns the GSM or UMTS cell ID.
+     * @return gsm cell id, -1 if unknown, 0xffff max legal value
+     */
+    public int getCid() {
+        return mCid;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mPlmn.hashCode();
+        hash = hash * 31 + mLac;
+        hash = hash * 31 + mCid;
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null || !(o instanceof SmsCbLocation)) {
+            return false;
+        }
+        SmsCbLocation other = (SmsCbLocation) o;
+        return mPlmn.equals(other.mPlmn) && mLac == other.mLac && mCid == other.mCid;
+    }
+
+    @Override
+    public String toString() {
+        return '[' + mPlmn + ',' + mLac + ',' + mCid + ']';
+    }
+
+    /**
+     * Test whether this location is within the location area of the specified object.
+     *
+     * @param area the location area to compare with this location
+     * @return true if this location is contained within the specified location area
+     */
+    public boolean isInLocationArea(SmsCbLocation area) {
+        if (mCid != -1 && mCid != area.mCid) {
+            return false;
+        }
+        if (mLac != -1 && mLac != area.mLac) {
+            return false;
+        }
+        return mPlmn.equals(area.mPlmn);
+    }
+
+    /**
+     * Test whether this location is within the location area of the CellLocation.
+     *
+     * @param plmn the PLMN to use for comparison
+     * @param lac the Location Area (GSM) or Service Area (UMTS) to compare with
+     * @param cid the Cell ID to compare with
+     * @return true if this location is contained within the specified PLMN, LAC, and Cell ID
+     */
+    public boolean isInLocationArea(String plmn, int lac, int cid) {
+        if (!mPlmn.equals(plmn)) {
+            return false;
+        }
+
+        if (mLac != -1 && mLac != lac) {
+            return false;
+        }
+
+        if (mCid != -1 && mCid != cid) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mPlmn);
+        dest.writeInt(mLac);
+        dest.writeInt(mCid);
+    }
+
+    public static final Parcelable.Creator<SmsCbLocation> CREATOR
+            = new Parcelable.Creator<SmsCbLocation>() {
+        @Override
+        public SmsCbLocation createFromParcel(Parcel in) {
+            return new SmsCbLocation(in);
+        }
+
+        @Override
+        public SmsCbLocation[] newArray(int size) {
+            return new SmsCbLocation[size];
+        }
+    };
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/src/java/android/telephony/SmsCbMessage.java b/src/java/android/telephony/SmsCbMessage.java
new file mode 100644 (file)
index 0000000..046bf8c
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2010 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parcelable object containing a received cell broadcast message. There are four different types
+ * of Cell Broadcast messages:
+ *
+ * <ul>
+ * <li>opt-in informational broadcasts, e.g. news, weather, stock quotes, sports scores</li>
+ * <li>cell information messages, broadcast on channel 50, indicating the current cell name for
+ *  roaming purposes (required to display on the idle screen in Brazil)</li>
+ * <li>emergency broadcasts for the Japanese Earthquake and Tsunami Warning System (ETWS)</li>
+ * <li>emergency broadcasts for the American Commercial Mobile Alert Service (CMAS)</li>
+ * </ul>
+ *
+ * <p>There are also four different CB message formats: GSM, ETWS Primary Notification (GSM only),
+ * UMTS, and CDMA. Some fields are only applicable for some message formats. Other fields were
+ * unified under a common name, avoiding some names, such as "Message Identifier", that refer to
+ * two completely different concepts in 3GPP and CDMA.
+ *
+ * <p>The GSM/UMTS Message Identifier field is available via {@link #getServiceCategory}, the name
+ * of the equivalent field in CDMA. In both cases the service category is a 16-bit value, but 3GPP
+ * and 3GPP2 have completely different meanings for the respective values. For ETWS and CMAS, the
+ * application should
+ *
+ * <p>The CDMA Message Identifier field is available via {@link #getSerialNumber}, which is used
+ * to detect the receipt of a duplicate message to be discarded. In CDMA, the message ID is
+ * unique to the current PLMN. In GSM/UMTS, there is a 16-bit serial number containing a 2-bit
+ * Geographical Scope field which indicates whether the 10-bit message code and 4-bit update number
+ * are considered unique to the PLMN, to the current cell, or to the current Location Area (or
+ * Service Area in UMTS). The relevant values are concatenated into a single String which will be
+ * unique if the messages are not duplicates.
+ *
+ * <p>The SMS dispatcher does not detect duplicate messages. However, it does concatenate the
+ * pages of a GSM multi-page cell broadcast into a single SmsCbMessage object.
+ *
+ * <p>Interested applications with {@code RECEIVE_SMS_PERMISSION} can register to receive
+ * {@code SMS_CB_RECEIVED_ACTION} broadcast intents for incoming non-emergency broadcasts.
+ * Only system applications such as the CellBroadcastReceiver may receive notifications for
+ * emergency broadcasts (ETWS and CMAS). This is intended to prevent any potential for delays or
+ * interference with the immediate display of the alert message and playing of the alert sound and
+ * vibration pattern, which could be caused by poorly written or malicious non-system code.
+ *
+ * @hide
+ */
+public class SmsCbMessage implements Parcelable {
+
+    protected static final String LOG_TAG = "SMSCB";
+
+    /** Cell wide geographical scope with immediate display (GSM/UMTS only). */
+    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+    /** PLMN wide geographical scope (GSM/UMTS and all CDMA broadcasts). */
+    public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+    /** Location / service area wide geographical scope (GSM/UMTS only). */
+    public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
+
+    /** Cell wide geographical scope (GSM/UMTS only). */
+    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+    /** GSM or UMTS format cell broadcast. */
+    public static final int MESSAGE_FORMAT_3GPP = 1;
+
+    /** CDMA format cell broadcast. */
+    public static final int MESSAGE_FORMAT_3GPP2 = 2;
+
+    /** Normal message priority. */
+    public static final int MESSAGE_PRIORITY_NORMAL = 0;
+
+    /** Interactive message priority. */
+    public static final int MESSAGE_PRIORITY_INTERACTIVE = 1;
+
+    /** Urgent message priority. */
+    public static final int MESSAGE_PRIORITY_URGENT = 2;
+
+    /** Emergency message priority. */
+    public static final int MESSAGE_PRIORITY_EMERGENCY = 3;
+
+    /** Format of this message (for interpretation of service category values). */
+    private final int mMessageFormat;
+
+    /** Geographical scope of broadcast. */
+    private final int mGeographicalScope;
+
+    /**
+     * Serial number of broadcast (message identifier for CDMA, geographical scope + message code +
+     * update number for GSM/UMTS). The serial number plus the location code uniquely identify
+     * a cell broadcast for duplicate detection.
+     */
+    private final int mSerialNumber;
+
+    /**
+     * Location identifier for this message. It consists of the current operator MCC/MNC as a
+     * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+     * message is not binary 01, the Location Area is included for comparison. If the GS is
+     * 00 or 11, the Cell ID is also included. LAC and Cell ID are -1 if not specified.
+     */
+    private final SmsCbLocation mLocation;
+
+    /**
+     * 16-bit CDMA service category or GSM/UMTS message identifier. For ETWS and CMAS warnings,
+     * the information provided by the category is also available via {@link #getEtwsWarningInfo()}
+     * or {@link #getCmasWarningInfo()}.
+     */
+    private final int mServiceCategory;
+
+    /** Message language, as a two-character string, e.g. "en". */
+    private final String mLanguage;
+
+    /** Message body, as a String. */
+    private final String mBody;
+
+    /** Message priority (including emergency priority). */
+    private final int mPriority;
+
+    /** ETWS warning notification information (ETWS warnings only). */
+    private final SmsCbEtwsInfo mEtwsWarningInfo;
+
+    /** CMAS warning notification information (CMAS warnings only). */
+    private final SmsCbCmasInfo mCmasWarningInfo;
+
+    /**
+     * Create a new SmsCbMessage with the specified data.
+     */
+    public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
+            SmsCbLocation location, int serviceCategory, String language, String body,
+            int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo) {
+        mMessageFormat = messageFormat;
+        mGeographicalScope = geographicalScope;
+        mSerialNumber = serialNumber;
+        mLocation = location;
+        mServiceCategory = serviceCategory;
+        mLanguage = language;
+        mBody = body;
+        mPriority = priority;
+        mEtwsWarningInfo = etwsWarningInfo;
+        mCmasWarningInfo = cmasWarningInfo;
+    }
+
+    /** Create a new SmsCbMessage object from a Parcel. */
+    public SmsCbMessage(Parcel in) {
+        mMessageFormat = in.readInt();
+        mGeographicalScope = in.readInt();
+        mSerialNumber = in.readInt();
+        mLocation = new SmsCbLocation(in);
+        mServiceCategory = in.readInt();
+        mLanguage = in.readString();
+        mBody = in.readString();
+        mPriority = in.readInt();
+        int type = in.readInt();
+        switch (type) {
+            case 'E':
+                // unparcel ETWS warning information
+                mEtwsWarningInfo = new SmsCbEtwsInfo(in);
+                mCmasWarningInfo = null;
+                break;
+
+            case 'C':
+                // unparcel CMAS warning information
+                mEtwsWarningInfo = null;
+                mCmasWarningInfo = new SmsCbCmasInfo(in);
+                break;
+
+            default:
+                mEtwsWarningInfo = null;
+                mCmasWarningInfo = null;
+        }
+    }
+
+    /**
+     * Flatten this object into a Parcel.
+     *
+     * @param dest  The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written (ignored).
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mMessageFormat);
+        dest.writeInt(mGeographicalScope);
+        dest.writeInt(mSerialNumber);
+        mLocation.writeToParcel(dest, flags);
+        dest.writeInt(mServiceCategory);
+        dest.writeString(mLanguage);
+        dest.writeString(mBody);
+        dest.writeInt(mPriority);
+        if (mEtwsWarningInfo != null) {
+            // parcel ETWS warning information
+            dest.writeInt('E');
+            mEtwsWarningInfo.writeToParcel(dest, flags);
+        } else if (mCmasWarningInfo != null) {
+            // parcel CMAS warning information
+            dest.writeInt('C');
+            mCmasWarningInfo.writeToParcel(dest, flags);
+        } else {
+            // no ETWS or CMAS warning information
+            dest.writeInt('0');
+        }
+    }
+
+    public static final Parcelable.Creator<SmsCbMessage> CREATOR
+            = new Parcelable.Creator<SmsCbMessage>() {
+        @Override
+        public SmsCbMessage createFromParcel(Parcel in) {
+            return new SmsCbMessage(in);
+        }
+
+        @Override
+        public SmsCbMessage[] newArray(int size) {
+            return new SmsCbMessage[size];
+        }
+    };
+
+    /**
+     * Return the geographical scope of this message (GSM/UMTS only).
+     *
+     * @return Geographical scope
+     */
+    public int getGeographicalScope() {
+        return mGeographicalScope;
+    }
+
+    /**
+     * Return the broadcast serial number of broadcast (message identifier for CDMA, or
+     * geographical scope + message code + update number for GSM/UMTS). The serial number plus
+     * the location code uniquely identify a cell broadcast for duplicate detection.
+     *
+     * @return the 16-bit CDMA message identifier or GSM/UMTS serial number
+     */
+    public int getSerialNumber() {
+        return mSerialNumber;
+    }
+
+    /**
+     * Return the location identifier for this message, consisting of the MCC/MNC as a
+     * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
+     * message is not binary 01, the Location Area is included. If the GS is 00 or 11, the
+     * cell ID is also included. The {@link SmsCbLocation} object includes a method to test
+     * if the location is included within another location area or within a PLMN and CellLocation.
+     *
+     * @return the geographical location code for duplicate message detection
+     */
+    public SmsCbLocation getLocation() {
+        return mLocation;
+    }
+
+    /**
+     * Return the 16-bit CDMA service category or GSM/UMTS message identifier. The interpretation
+     * of the category is radio technology specific. For ETWS and CMAS warnings, the information
+     * provided by the category is available via {@link #getEtwsWarningInfo()} or
+     * {@link #getCmasWarningInfo()} in a radio technology independent format.
+     *
+     * @return the radio technology specific service category
+     */
+    public int getServiceCategory() {
+        return mServiceCategory;
+    }
+
+    /**
+     * Get the ISO-639-1 language code for this message, or null if unspecified
+     *
+     * @return Language code
+     */
+    public String getLanguageCode() {
+        return mLanguage;
+    }
+
+    /**
+     * Get the body of this message, or null if no body available
+     *
+     * @return Body, or null
+     */
+    public String getMessageBody() {
+        return mBody;
+    }
+
+    /**
+     * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
+     * @return an integer representing 3GPP or 3GPP2 message format
+     */
+    public int getMessageFormat() {
+        return mMessageFormat;
+    }
+
+    /**
+     * Get the message priority. Normal broadcasts return {@link #MESSAGE_PRIORITY_NORMAL}
+     * and emergency broadcasts return {@link #MESSAGE_PRIORITY_EMERGENCY}. CDMA also may return
+     * {@link #MESSAGE_PRIORITY_INTERACTIVE} or {@link #MESSAGE_PRIORITY_URGENT}.
+     * @return an integer representing the message priority
+     */
+    public int getMessagePriority() {
+        return mPriority;
+    }
+
+    /**
+     * If this is an ETWS warning notification then this method will return an object containing
+     * the ETWS warning type, the emergency user alert flag, and the popup flag. If this is an
+     * ETWS primary notification (GSM only), there will also be a 7-byte timestamp and 43-byte
+     * digital signature. As of Release 10, 3GPP TS 23.041 states that the UE shall ignore the
+     * ETWS primary notification timestamp and digital signature if received.
+     *
+     * @return an SmsCbEtwsInfo object, or null if this is not an ETWS warning notification
+     */
+    public SmsCbEtwsInfo getEtwsWarningInfo() {
+        return mEtwsWarningInfo;
+    }
+
+    /**
+     * If this is a CMAS warning notification then this method will return an object containing
+     * the CMAS message class, category, response type, severity, urgency and certainty.
+     * The message class is always present. Severity, urgency and certainty are present for CDMA
+     * warning notifications containing a type 1 elements record and for GSM and UMTS warnings
+     * except for the Presidential-level alert category. Category and response type are only
+     * available for CDMA notifications containing a type 1 elements record.
+     *
+     * @return an SmsCbCmasInfo object, or null if this is not a CMAS warning notification
+     */
+    public SmsCbCmasInfo getCmasWarningInfo() {
+        return mCmasWarningInfo;
+    }
+
+    /**
+     * Return whether this message is an emergency (PWS) message type.
+     * @return true if the message is a public warning notification; false otherwise
+     */
+    public boolean isEmergencyMessage() {
+        return mPriority == MESSAGE_PRIORITY_EMERGENCY;
+    }
+
+    /**
+     * Return whether this message is an ETWS warning alert.
+     * @return true if the message is an ETWS warning notification; false otherwise
+     */
+    public boolean isEtwsMessage() {
+        return mEtwsWarningInfo != null;
+    }
+
+    /**
+     * Return whether this message is a CMAS warning alert.
+     * @return true if the message is a CMAS warning notification; false otherwise
+     */
+    public boolean isCmasMessage() {
+        return mCmasWarningInfo != null;
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbMessage{geographicalScope=" + mGeographicalScope + ", serialNumber="
+                + mSerialNumber + ", location=" + mLocation + ", serviceCategory="
+                + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
+                + ", priority=" + mPriority
+                + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
+                + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "") + '}';
+    }
+
+    /**
+     * Describe the kinds of special objects contained in the marshalled representation.
+     * @return a bitmask indicating this Parcelable contains no special objects
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/src/java/android/telephony/SmsManager.java b/src/java/android/telephony/SmsManager.java
new file mode 100644 (file)
index 0000000..44bdaeb
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+ * 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 android.telephony;
+
+import android.app.PendingIntent;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.IccConstants;
+import com.android.internal.telephony.SmsRawData;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/*
+ * TODO(code review): Curious question... Why are a lot of these
+ * methods not declared as static, since they do not seem to require
+ * any local object state?  Presumably this cannot be changed without
+ * interfering with the API...
+ */
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method SmsManager.getDefault().
+ */
+public final class SmsManager {
+    /** Singleton object constructed during class initialization. */
+    private static final SmsManager sInstance = new SmsManager();
+
+    /**
+     * Send a text based SMS.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or text are empty
+     */
+    public void sendTextMessage(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (TextUtils.isEmpty(text)) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Divide a message text into several fragments, none bigger than
+     * the maximum SMS message size.
+     *
+     * @param text the original message.  Must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     */
+    public ArrayList<String> divideMessage(String text) {
+        return SmsMessage.fragmentText(text);
+    }
+
+    /**
+     * Send a multi-part text based SMS.  The callee should have already
+     * divided the message into correctly sized parts by calling
+     * <code>divideMessage</code>.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *   the current default SMSC
+     * @param parts an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     * @param sentIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been sent.
+     *   The result code will be <code>Activity.RESULT_OK</code> for success,
+     *   or one of these errors:<br>
+     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *   <code>RESULT_ERROR_NULL_PDU</code><br>
+     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+     *   the extra "errorCode" containing a radio technology specific value,
+     *   generally only useful for troubleshooting.<br>
+     *   The per-application based SMS control checks sentIntent. If sentIntent
+     *   is NULL the caller will be checked against all unknown applications,
+     *   which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been delivered
+     *   to the recipient.  The raw pdu of the status report is in the
+     *   extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     */
+    public void sendMultipartTextMessage(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+        if (parts == null || parts.size() < 1) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        if (parts.size() > 1) {
+            try {
+                ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+                if (iccISms != null) {
+                    iccISms.sendMultipartText(destinationAddress, scAddress, parts,
+                            sentIntents, deliveryIntents);
+                }
+            } catch (RemoteException ex) {
+                // ignore it
+            }
+        } else {
+            PendingIntent sentIntent = null;
+            PendingIntent deliveryIntent = null;
+            if (sentIntents != null && sentIntents.size() > 0) {
+                sentIntent = sentIntents.get(0);
+            }
+            if (deliveryIntents != null && deliveryIntents.size() > 0) {
+                deliveryIntent = deliveryIntents.get(0);
+            }
+            sendTextMessage(destinationAddress, scAddress, parts.get(0),
+                    sentIntent, deliveryIntent);
+        }
+    }
+
+    /**
+     * Send a data based SMS to a specific application port.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param destinationPort the port to deliver the message to
+     * @param data the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
+     *  or one of these errors:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     */
+    public void sendDataMessage(
+            String destinationAddress, String scAddress, short destinationPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (data == null || data.length == 0) {
+            throw new IllegalArgumentException("Invalid message data");
+        }
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                iccISms.sendData(destinationAddress, scAddress, destinationPort & 0xFFFF,
+                        data, sentIntent, deliveryIntent);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Get the default instance of the SmsManager
+     *
+     * @return the default instance of the SmsManager
+     */
+    public static SmsManager getDefault() {
+        return sInstance;
+    }
+
+    private SmsManager() {
+        //nothing
+    }
+
+    /**
+     * Copy a raw SMS PDU to the ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * @param smsc the SMSC for this message, or NULL for the default SMSC
+     * @param pdu the raw PDU to store
+     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
+     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
+     * @return true for success
+     *
+     * {@hide}
+     */
+    public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.copyMessageToIccEf(status, pdu, smsc);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Delete the specified message from the ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * @param messageIndex is the record index of the message on ICC
+     * @return true for success
+     *
+     * {@hide}
+     */
+    public boolean
+    deleteMessageFromIcc(int messageIndex) {
+        boolean success = false;
+        byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
+        Arrays.fill(pdu, (byte)0xff);
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.updateMessageOnIccEf(messageIndex, STATUS_ON_ICC_FREE, pdu);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Update the specified message on the ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * @param messageIndex record index of message to update
+     * @param newStatus new message status (STATUS_ON_ICC_READ,
+     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
+     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
+     * @param pdu the raw PDU to store
+     * @return true for success
+     *
+     * {@hide}
+     */
+    public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.updateMessageOnIccEf(messageIndex, newStatus, pdu);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Retrieves all messages currently stored on ICC.
+     * ICC (Integrated Circuit Card) is the card of the device.
+     * For example, this can be the SIM or USIM for GSM.
+     *
+     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
+     *
+     * {@hide}
+     */
+    public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
+        List<SmsRawData> records = null;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                records = iccISms.getAllMessagesFromIccEf();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return createMessageListFromRawRecords(records);
+    }
+
+    /**
+     * Enable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages. All received messages will be broadcast in an
+     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param messageIdentifier Message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     * @see #disableCellBroadcast(int)
+     *
+     * {@hide}
+     */
+    public boolean enableCellBroadcast(int messageIdentifier) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.enableCellBroadcast(messageIdentifier);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Disable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages.
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param messageIdentifier Message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #enableCellBroadcast(int)
+     *
+     * {@hide}
+     */
+    public boolean disableCellBroadcast(int messageIdentifier) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.disableCellBroadcast(messageIdentifier);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Enable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages. All received messages will be broadcast in an
+     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041
+     * @param endMessageId last message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     * @see #disableCellBroadcastRange(int, int)
+     *
+     * {@hide}
+     */
+    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Disable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages.
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041
+     * @param endMessageId last message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #enableCellBroadcastRange(int, int)
+     *
+     * {@hide}
+     */
+    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
+     * records returned by <code>getAllMessagesFromIcc()</code>
+     *
+     * @param records SMS EF records, returned by
+     *   <code>getAllMessagesFromIcc</code>
+     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
+     */
+    private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+        ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
+        if (records != null) {
+            int count = records.size();
+            for (int i = 0; i < count; i++) {
+                SmsRawData data = records.get(i);
+                // List contains all records, including "free" records (null)
+                if (data != null) {
+                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
+                    if (sms != null) {
+                        messages.add(sms);
+                    }
+                }
+            }
+        }
+        return messages;
+    }
+
+    // see SmsMessage.getStatusOnIcc
+
+    /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    static public final int STATUS_ON_ICC_FREE      = 0;
+
+    /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    static public final int STATUS_ON_ICC_READ      = 1;
+
+    /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    static public final int STATUS_ON_ICC_UNREAD    = 3;
+
+    /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    static public final int STATUS_ON_ICC_SENT      = 5;
+
+    /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
+    static public final int STATUS_ON_ICC_UNSENT    = 7;
+
+    // SMS send failure result codes
+
+    /** Generic failure cause */
+    static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
+    /** Failed because radio was explicitly turned off */
+    static public final int RESULT_ERROR_RADIO_OFF          = 2;
+    /** Failed because no pdu provided */
+    static public final int RESULT_ERROR_NULL_PDU           = 3;
+    /** Failed because service is currently unavailable */
+    static public final int RESULT_ERROR_NO_SERVICE         = 4;
+    /** Failed because we reached the sending queue limit.  {@hide} */
+    static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
+    /** Failed because FDN is enabled. {@hide} */
+    static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
+}
diff --git a/src/java/android/telephony/SmsMessage.java b/src/java/android/telephony/SmsMessage.java
new file mode 100644 (file)
index 0000000..b94609e
--- /dev/null
@@ -0,0 +1,688 @@
+/*
+ * 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 android.telephony;
+
+import android.os.Parcel;
+import android.util.Log;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+
+import java.lang.Math;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+
+/**
+ * A Short Message Service message.
+ */
+public class SmsMessage {
+    private static final String LOG_TAG = "SMS";
+
+    /**
+     * SMS Class enumeration.
+     * See TS 23.038.
+     *
+     */
+    public enum MessageClass{
+        UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+    }
+
+    /** User data text encoding code unit size */
+    public static final int ENCODING_UNKNOWN = 0;
+    public static final int ENCODING_7BIT = 1;
+    public static final int ENCODING_8BIT = 2;
+    public static final int ENCODING_16BIT = 3;
+    /**
+     * @hide This value is not defined in global standard. Only in Korea, this is used.
+     */
+    public static final int ENCODING_KSC5601 = 4;
+
+    /** The maximum number of payload bytes per message */
+    public static final int MAX_USER_DATA_BYTES = 140;
+
+    /**
+     * The maximum number of payload bytes per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     */
+    public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+    /** The maximum number of payload septets per message */
+    public static final int MAX_USER_DATA_SEPTETS = 160;
+
+    /**
+     * The maximum number of payload septets per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     */
+    public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+    /**
+     * Indicates a 3GPP format SMS message.
+     * @hide pending API council approval
+     */
+    public static final String FORMAT_3GPP = "3gpp";
+
+    /**
+     * Indicates a 3GPP2 format SMS message.
+     * @hide pending API council approval
+     */
+    public static final String FORMAT_3GPP2 = "3gpp2";
+
+    /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+     *
+     * @hide
+     */
+    public SmsMessageBase mWrappedSmsMessage;
+
+    public static class SubmitPdu {
+
+        public byte[] encodedScAddress; // Null if not applicable.
+        public byte[] encodedMessage;
+
+        public String toString() {
+            return "SubmitPdu: encodedScAddress = "
+                    + Arrays.toString(encodedScAddress)
+                    + ", encodedMessage = "
+                    + Arrays.toString(encodedMessage);
+        }
+
+        /**
+         * @hide
+         */
+        protected SubmitPdu(SubmitPduBase spb) {
+            this.encodedMessage = spb.encodedMessage;
+            this.encodedScAddress = spb.encodedScAddress;
+        }
+
+    }
+
+    private SmsMessage(SmsMessageBase smb) {
+        mWrappedSmsMessage = smb;
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU.
+     *
+     * <p><b>This method will soon be deprecated</b> and all applications which handle
+     * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
+     * intent <b>must</b> now pass the new {@code format} String extra from the intent
+     * into the new method {@code createFromPdu(byte[], String)} which takes an
+     * extra format parameter. This is required in order to correctly decode the PDU on
+     * devices that require support for both 3GPP and 3GPP2 formats at the same time,
+     * such as dual-mode GSM/CDMA and CDMA/LTE phones.
+     */
+    public static SmsMessage createFromPdu(byte[] pdu) {
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        String format = (PHONE_TYPE_CDMA == activePhone) ?
+                SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
+        return createFromPdu(pdu, format);
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU with the specified message format. The
+     * message format is passed in the {@code SMS_RECEIVED_ACTION} as the {@code format}
+     * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
+     * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
+     *
+     * @param pdu the message PDU from the SMS_RECEIVED_ACTION intent
+     * @param format the format extra from the SMS_RECEIVED_ACTION intent
+     * @hide pending API council approval
+     */
+    public static SmsMessage createFromPdu(byte[] pdu, String format) {
+        SmsMessageBase wrappedMessage;
+
+        if (SmsConstants.FORMAT_3GPP2.equals(format)) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+        } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+        } else {
+            Log.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
+            return null;
+        }
+
+        return new SmsMessage(wrappedMessage);
+    }
+
+    /**
+     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
+     * +CMT unsolicited response (PDU mode, of course)
+     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
+     *
+     * Only public for debugging and for RIL
+     *
+     * {@hide}
+     */
+    public static SmsMessage newFromCMT(String[] lines) {
+        // received SMS in 3GPP format
+        SmsMessageBase wrappedMessage =
+                com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines);
+
+        return new SmsMessage(wrappedMessage);
+    }
+
+    /** @hide */
+    public static SmsMessage newFromParcel(Parcel p) {
+        // received SMS in 3GPP2 format
+        SmsMessageBase wrappedMessage =
+                com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p);
+
+        return new SmsMessage(wrappedMessage);
+    }
+
+    /**
+     * Create an SmsMessage from an SMS EF record.
+     *
+     * @param index Index of SMS record. This should be index in ArrayList
+     *              returned by SmsManager.getAllMessagesFromSim + 1.
+     * @param data Record data.
+     * @return An SmsMessage representing the record.
+     *
+     * @hide
+     */
+    public static SmsMessage createFromEfRecord(int index, byte[] data) {
+        SmsMessageBase wrappedMessage;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
+                    index, data);
+        } else {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
+                    index, data);
+        }
+
+        return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null;
+    }
+
+    /**
+     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+     * length in bytes (not hex chars) less the SMSC header
+     *
+     * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
+     * We should probably deprecate it and remove the obsolete test case.
+     */
+    public static int getTPLayerLengthForPDU(String pdu) {
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+        } else {
+            return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+        }
+    }
+
+    /*
+     * TODO(cleanup): It would make some sense if the result of
+     * preprocessing a message to determine the proper encoding (i.e.
+     * the resulting data structure from calculateLength) could be
+     * passed as an argument to the actual final encoding function.
+     * This would better ensure that the logic behind size calculation
+     * actually matched the encoding.
+     */
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and
+     * the number of characters remaining until the next message.
+     *
+     * @param msgBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the
+     *         radio-specific 7-bit encoding are counted as single
+     *         space chars.  If false, and if the messageBody contains
+     *         non-7-bit encodable characters, length is calculated
+     *         using a 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's
+     *         required, int[1] the number of code units used, and
+     *         int[2] is the number of code units remaining until the
+     *         next message. int[3] is an indicator of the encoding
+     *         code unit size (see the ENCODING_* definitions in SmsConstants)
+     */
+    public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+            com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly) :
+            com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
+        int ret[] = new int[4];
+        ret[0] = ted.msgCount;
+        ret[1] = ted.codeUnitCount;
+        ret[2] = ted.codeUnitsRemaining;
+        ret[3] = ted.codeUnitSize;
+        return ret;
+    }
+
+    /**
+     * Divide a message text into several fragments, none bigger than
+     * the maximum SMS message text size.
+     *
+     * @param text text, must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original msg text
+     *
+     * @hide
+     */
+    public static ArrayList<String> fragmentText(String text) {
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+            com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) :
+            com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
+
+        // TODO(cleanup): The code here could be rolled into the logic
+        // below cleanly if these MAX_* constants were defined more
+        // flexibly...
+
+        int limit;
+        if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+            int udhLength;
+            if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
+                udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
+            } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
+                udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
+            } else {
+                udhLength = 0;
+            }
+
+            if (ted.msgCount > 1) {
+                udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
+            }
+
+            if (udhLength != 0) {
+                udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
+            }
+
+            limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
+        } else {
+            if (ted.msgCount > 1) {
+                limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
+            } else {
+                limit = SmsConstants.MAX_USER_DATA_BYTES;
+            }
+        }
+
+        int pos = 0;  // Index in code units.
+        int textLen = text.length();
+        ArrayList<String> result = new ArrayList<String>(ted.msgCount);
+        while (pos < textLen) {
+            int nextPos = 0;  // Counts code units.
+            if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
+                if (activePhone == PHONE_TYPE_CDMA && ted.msgCount == 1) {
+                    // For a singleton CDMA message, the encoding must be ASCII...
+                    nextPos = pos + Math.min(limit, textLen - pos);
+                } else {
+                    // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
+                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit,
+                            ted.languageTable, ted.languageShiftTable);
+                }
+            } else {  // Assume unicode.
+                nextPos = pos + Math.min(limit / 2, textLen - pos);
+            }
+            if ((nextPos <= pos) || (nextPos > textLen)) {
+                Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
+                          nextPos + " >= " + textLen + ")");
+                break;
+            }
+            result.add(text.substring(pos, nextPos));
+            pos = nextPos;
+        }
+        return result;
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and
+     * the number of characters remaining until the next message, given the
+     * current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the radio
+     *         specific (GSM / CDMA) alphabet encoding are converted to as a
+     *         single space characters. If false, a messageBody containing
+     *         non-GSM or non-CDMA alphabet characters are encoded using
+     *         16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1]
+     *         the number of code units used, and int[2] is the number of code
+     *         units remaining until the next message. int[3] is the encoding
+     *         type that should be used for the message.
+     */
+    public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+        return calculateLength((CharSequence)messageBody, use7bitOnly);
+    }
+
+    /*
+     * TODO(cleanup): It looks like there is now no useful reason why
+     * apps should generate pdus themselves using these routines,
+     * instead of handing the raw data to SMSDispatcher (and thereby
+     * have the phone process do the encoding).  Moreover, CDMA now
+     * has shared state (in the form of the msgId system property)
+     * which can only be modified by the phone process, and hence
+     * makes the output of these routines incorrect.  Since they now
+     * serve no purpose, they should probably just return null
+     * directly, and be deprecated.  Going further in that direction,
+     * the above parsers of serialized pdu data should probably also
+     * be gotten rid of, hiding all but the necessarily visible
+     * structured data from client apps.  A possible concern with
+     * doing this is that apps may be using these routines to generate
+     * pdus that are then sent elsewhere, some network server, for
+     * example, and that always returning null would thereby break
+     * otherwise useful apps.
+     */
+
+    /**
+     * Get an SMS-SUBMIT PDU for a destination address and a message.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address.  Null means use default.
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message, boolean statusReportRequested) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested, null);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
+     * This method will not attempt to use any GSM national language 7 bit encodings.
+     *
+     * @param scAddress Service Centre address. null == use default
+     * @param destinationAddress the address of the destination for the message
+     * @param destinationPort the port to deliver the message to at the
+     *        destination
+     * @param data the data for the message
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, short destinationPort, byte[] data,
+            boolean statusReportRequested) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Returns the address of the SMS service center that relayed this message
+     * or null if there is none.
+     */
+    public String getServiceCenterAddress() {
+        return mWrappedSmsMessage.getServiceCenterAddress();
+    }
+
+    /**
+     * Returns the originating address (sender) of this SMS message in String
+     * form or null if unavailable
+     */
+    public String getOriginatingAddress() {
+        return mWrappedSmsMessage.getOriginatingAddress();
+    }
+
+    /**
+     * Returns the originating address, or email from address if this message
+     * was from an email gateway. Returns null if originating address
+     * unavailable.
+     */
+    public String getDisplayOriginatingAddress() {
+        return mWrappedSmsMessage.getDisplayOriginatingAddress();
+    }
+
+    /**
+     * Returns the message body as a String, if it exists and is text based.
+     * @return message body is there is one, otherwise null
+     */
+    public String getMessageBody() {
+        return mWrappedSmsMessage.getMessageBody();
+    }
+
+    /**
+     * Returns the class of this message.
+     */
+    public MessageClass getMessageClass() {
+        switch(mWrappedSmsMessage.getMessageClass()) {
+            case CLASS_0: return MessageClass.CLASS_0;
+            case CLASS_1: return MessageClass.CLASS_1;
+            case CLASS_2: return MessageClass.CLASS_2;
+            case CLASS_3: return MessageClass.CLASS_3;
+            default: return MessageClass.UNKNOWN;
+
+        }
+    }
+
+    /**
+     * Returns the message body, or email message body if this message was from
+     * an email gateway. Returns null if message body unavailable.
+     */
+    public String getDisplayMessageBody() {
+        return mWrappedSmsMessage.getDisplayMessageBody();
+    }
+
+    /**
+     * Unofficial convention of a subject line enclosed in parens empty string
+     * if not present
+     */
+    public String getPseudoSubject() {
+        return mWrappedSmsMessage.getPseudoSubject();
+    }
+
+    /**
+     * Returns the service centre timestamp in currentTimeMillis() format
+     */
+    public long getTimestampMillis() {
+        return mWrappedSmsMessage.getTimestampMillis();
+    }
+
+    /**
+     * Returns true if message is an email.
+     *
+     * @return true if this message came through an email gateway and email
+     *         sender / subject / parsed body are available
+     */
+    public boolean isEmail() {
+        return mWrappedSmsMessage.isEmail();
+    }
+
+     /**
+     * @return if isEmail() is true, body of the email sent through the gateway.
+     *         null otherwise
+     */
+    public String getEmailBody() {
+        return mWrappedSmsMessage.getEmailBody();
+    }
+
+    /**
+     * @return if isEmail() is true, email from address of email sent through
+     *         the gateway. null otherwise
+     */
+    public String getEmailFrom() {
+        return mWrappedSmsMessage.getEmailFrom();
+    }
+
+    /**
+     * Get protocol identifier.
+     */
+    public int getProtocolIdentifier() {
+        return mWrappedSmsMessage.getProtocolIdentifier();
+    }
+
+    /**
+     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
+     * SMS
+     */
+    public boolean isReplace() {
+        return mWrappedSmsMessage.isReplace();
+    }
+
+    /**
+     * Returns true for CPHS MWI toggle message.
+     *
+     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
+     *         B.4.2
+     */
+    public boolean isCphsMwiMessage() {
+        return mWrappedSmsMessage.isCphsMwiMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) clear message
+     */
+    public boolean isMWIClearMessage() {
+        return mWrappedSmsMessage.isMWIClearMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) set message
+     */
+    public boolean isMWISetMessage() {
+        return mWrappedSmsMessage.isMWISetMessage();
+    }
+
+    /**
+     * returns true if this message is a "Message Waiting Indication Group:
+     * Discard Message" notification and should not be stored.
+     */
+    public boolean isMwiDontStore() {
+        return mWrappedSmsMessage.isMwiDontStore();
+    }
+
+    /**
+     * returns the user data section minus the user data header if one was
+     * present.
+     */
+    public byte[] getUserData() {
+        return mWrappedSmsMessage.getUserData();
+    }
+
+    /**
+     * Returns the raw PDU for the message.
+     *
+     * @return the raw PDU for the message.
+     */
+    public byte[] getPdu() {
+        return mWrappedSmsMessage.getPdu();
+    }
+
+    /**
+     * Returns the status of the message on the SIM (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the SIM.  These are:
+     *         SmsManager.STATUS_ON_SIM_FREE
+     *         SmsManager.STATUS_ON_SIM_READ
+     *         SmsManager.STATUS_ON_SIM_UNREAD
+     *         SmsManager.STATUS_ON_SIM_SEND
+     *         SmsManager.STATUS_ON_SIM_UNSENT
+     * @deprecated Use getStatusOnIcc instead.
+     */
+    @Deprecated public int getStatusOnSim() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the status of the message on the ICC (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the ICC.  These are:
+     *         SmsManager.STATUS_ON_ICC_FREE
+     *         SmsManager.STATUS_ON_ICC_READ
+     *         SmsManager.STATUS_ON_ICC_UNREAD
+     *         SmsManager.STATUS_ON_ICC_SEND
+     *         SmsManager.STATUS_ON_ICC_UNSENT
+     */
+    public int getStatusOnIcc() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the SIM (1-based index).
+     * @return the record index of the message on the SIM, or -1 if this
+     *         SmsMessage was not created from a SIM SMS EF record.
+     * @deprecated Use getIndexOnIcc instead.
+     */
+    @Deprecated public int getIndexOnSim() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the ICC (1-based index).
+     * @return the record index of the message on the ICC, or -1 if this
+     *         SmsMessage was not created from a ICC SMS EF record.
+     */
+    public int getIndexOnIcc() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * GSM:
+     * For an SMS-STATUS-REPORT message, this returns the status field from
+     * the status report.  This field indicates the status of a previously
+     * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
+     * description of values.
+     * CDMA:
+     * For not interfering with status codes from GSM, the value is
+     * shifted to the bits 31-16.
+     * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+     * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+     *
+     * @return 0 indicates the previously sent message was received.
+     *         See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+     *         for a description of other possible values.
+     */
+    public int getStatus() {
+        return mWrappedSmsMessage.getStatus();
+    }
+
+    /**
+     * Return true iff the message is a SMS-STATUS-REPORT message.
+     */
+    public boolean isStatusReportMessage() {
+        return mWrappedSmsMessage.isStatusReportMessage();
+    }
+
+    /**
+     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
+     * this message.
+     */
+    public boolean isReplyPathPresent() {
+        return mWrappedSmsMessage.isReplyPathPresent();
+    }
+}
diff --git a/src/java/android/telephony/gsm/SmsManager.java b/src/java/android/telephony/gsm/SmsManager.java
new file mode 100644 (file)
index 0000000..3b75298
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * 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 android.telephony.gsm;
+
+import android.app.PendingIntent;
+
+import java.util.ArrayList;
+
+
+/**
+ * Manages SMS operations such as sending data, text, and pdu SMS messages.
+ * Get this object by calling the static method SmsManager.getDefault().
+ * @deprecated Replaced by android.telephony.SmsManager that supports both GSM and CDMA.
+ */
+@Deprecated public final class SmsManager {
+    private static SmsManager sInstance;
+    private android.telephony.SmsManager mSmsMgrProxy;
+
+    /** Get the default instance of the SmsManager
+     *
+     * @return the default instance of the SmsManager
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public static final SmsManager getDefault() {
+        if (sInstance == null) {
+            sInstance = new SmsManager();
+        }
+        return sInstance;
+    }
+
+    @Deprecated
+    private SmsManager() {
+        mSmsMgrProxy = android.telephony.SmsManager.getDefault();
+    }
+
+    /**
+     * Send a text based SMS.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
+     *  <code>RESULT_ERROR_RADIO_OFF</code>
+     *  <code>RESULT_ERROR_NULL_PDU</code>.
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applications,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final void sendTextMessage(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        mSmsMgrProxy.sendTextMessage(destinationAddress, scAddress, text,
+                sentIntent, deliveryIntent);
+    }
+
+    /**
+     * Divide a text message into several messages, none bigger than
+     * the maximum SMS message size.
+     *
+     * @param text the original message.  Must not be null.
+     * @return an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final ArrayList<String> divideMessage(String text) {
+        return mSmsMgrProxy.divideMessage(text);
+    }
+
+    /**
+     * Send a multi-part text based SMS.  The callee should have already
+     * divided the message into correctly sized parts by calling
+     * <code>divideMessage</code>.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *   the current default SMSC
+     * @param parts an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     * @param sentIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been sent.
+     *   The result code will be <code>Activity.RESULT_OK<code> for success,
+     *   or one of these errors:
+     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
+     *   <code>RESULT_ERROR_RADIO_OFF</code>
+     *   <code>RESULT_ERROR_NULL_PDU</code>.
+     *   The per-application based SMS control checks sentIntent. If sentIntent
+     *   is NULL the caller will be checked against all unknown applicaitons,
+     *   which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been delivered
+     *   to the recipient.  The raw pdu of the status report is in the
+     *   extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final void sendMultipartTextMessage(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+        mSmsMgrProxy.sendMultipartTextMessage(destinationAddress, scAddress, parts,
+                sentIntents, deliveryIntents);
+    }
+
+    /**
+     * Send a data based SMS to a specific application port.
+     *
+     * @param destinationAddress the address to send the message to
+     * @param scAddress is the service center address or null to use
+     *  the current default SMSC
+     * @param destinationPort the port to deliver the message to
+     * @param data the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is sucessfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  or one of these errors:
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code>
+     *  <code>RESULT_ERROR_RADIO_OFF</code>
+     *  <code>RESULT_ERROR_NULL_PDU</code>.
+     *  The per-application based SMS control checks sentIntent. If sentIntent
+     *  is NULL the caller will be checked against all unknown applicaitons,
+     *  which cause smaller number of SMS to be sent in checking period.
+     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     *
+     * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @deprecated Use android.telephony.SmsManager.
+     */
+    @Deprecated
+    public final void sendDataMessage(
+            String destinationAddress, String scAddress, short destinationPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        mSmsMgrProxy.sendDataMessage(destinationAddress, scAddress, destinationPort,
+                data, sentIntent, deliveryIntent);
+    }
+
+    /**
+     * Copy a raw SMS PDU to the SIM.
+     *
+     * @param smsc the SMSC for this message, or NULL for the default SMSC
+     * @param pdu the raw PDU to store
+     * @param status message status (STATUS_ON_SIM_READ, STATUS_ON_SIM_UNREAD,
+     *               STATUS_ON_SIM_SENT, STATUS_ON_SIM_UNSENT)
+     * @return true for success
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final boolean copyMessageToSim(byte[] smsc, byte[] pdu, int status) {
+        return mSmsMgrProxy.copyMessageToIcc(smsc, pdu, status);
+    }
+
+    /**
+     * Delete the specified message from the SIM.
+     *
+     * @param messageIndex is the record index of the message on SIM
+     * @return true for success
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final boolean deleteMessageFromSim(int messageIndex) {
+        return mSmsMgrProxy.deleteMessageFromIcc(messageIndex);
+    }
+
+    /**
+     * Update the specified message on the SIM.
+     *
+     * @param messageIndex record index of message to update
+     * @param newStatus new message status (STATUS_ON_SIM_READ,
+     *                  STATUS_ON_SIM_UNREAD, STATUS_ON_SIM_SENT,
+     *                  STATUS_ON_SIM_UNSENT, STATUS_ON_SIM_FREE)
+     * @param pdu the raw PDU to store
+     * @return true for success
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final boolean updateMessageOnSim(int messageIndex, int newStatus, byte[] pdu) {
+        return mSmsMgrProxy.updateMessageOnIcc(messageIndex, newStatus, pdu);
+    }
+
+    /**
+     * Retrieves all messages currently stored on SIM.
+     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
+     * @deprecated Use android.telephony.SmsManager.
+     * {@hide}
+     */
+    @Deprecated
+    public final ArrayList<android.telephony.SmsMessage> getAllMessagesFromSim() {
+        return mSmsMgrProxy.getAllMessagesFromIcc();
+    }
+
+    /** Free space (TS 51.011 10.5.3).
+     *  @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_FREE      = 0;
+
+    /** Received and read (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_READ      = 1;
+
+    /** Received and unread (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_UNREAD    = 3;
+
+    /** Stored and sent (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_SENT      = 5;
+
+    /** Stored and unsent (TS 51.011 10.5.3).
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int STATUS_ON_SIM_UNSENT    = 7;
+
+    /** Generic failure cause
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
+
+    /** Failed because radio was explicitly turned off
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_RADIO_OFF          = 2;
+
+    /** Failed because no pdu provided
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_NULL_PDU           = 3;
+
+    /** Failed because service is currently unavailable
+     * @deprecated Use android.telephony.SmsManager. */
+    @Deprecated static public final int RESULT_ERROR_NO_SERVICE         = 4;
+
+}
diff --git a/src/java/android/telephony/gsm/SmsMessage.java b/src/java/android/telephony/gsm/SmsMessage.java
new file mode 100644 (file)
index 0000000..7a814c3
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * 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 android.telephony.gsm;
+
+import android.os.Parcel;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.EncodeException;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+
+import java.util.Arrays;
+
+import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+
+
+/**
+ * A Short Message Service message.
+ * @deprecated Replaced by android.telephony.SmsMessage that supports both GSM and CDMA.
+ */
+@Deprecated
+public class SmsMessage {
+    private static final boolean LOCAL_DEBUG = true;
+    private static final String LOG_TAG = "SMS";
+
+    /**
+     * SMS Class enumeration.
+     * See TS 23.038.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public enum MessageClass{
+        UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
+    }
+
+    /** Unknown encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_UNKNOWN = 0;
+
+    /** 7-bit encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_7BIT = 1;
+
+    /** 8-bit encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_8BIT = 2;
+
+    /** 16-bit encoding scheme (see TS 23.038)
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int ENCODING_16BIT = 3;
+
+    /** The maximum number of payload bytes per message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int MAX_USER_DATA_BYTES = 140;
+
+    /**
+     * The maximum number of payload bytes per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     *
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide pending API Council approval to extend the public API
+     */
+    @Deprecated public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
+
+    /** The maximum number of payload septets per message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int MAX_USER_DATA_SEPTETS = 160;
+
+    /**
+     * The maximum number of payload septets per message if a user data header
+     * is present.  This assumes the header only contains the
+     * CONCATENATED_8_BIT_REFERENCE element.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
+
+    /** Contains actual SmsMessage. Only public for debugging and for framework layer.
+     * @deprecated Use android.telephony.SmsMessage.
+     * {@hide}
+     */
+    @Deprecated public SmsMessageBase mWrappedSmsMessage;
+
+    /** @deprecated Use android.telephony.SmsMessage. */
+    @Deprecated
+    public static class SubmitPdu {
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Deprecated public byte[] encodedScAddress; // Null if not applicable.
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Deprecated public byte[] encodedMessage;
+
+        //Constructor
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Deprecated
+        public SubmitPdu() {
+        }
+
+        /** @deprecated Use android.telephony.SmsMessage.
+         * {@hide}
+         */
+        @Deprecated
+        protected SubmitPdu(SubmitPduBase spb) {
+            this.encodedMessage = spb.encodedMessage;
+            this.encodedScAddress = spb.encodedScAddress;
+        }
+
+        /** @deprecated Use android.telephony.SmsMessage. */
+        @Deprecated
+        public String toString() {
+            return "SubmitPdu: encodedScAddress = "
+                    + Arrays.toString(encodedScAddress)
+                    + ", encodedMessage = "
+                    + Arrays.toString(encodedMessage);
+        }
+    }
+
+    // Constructor
+    /** @deprecated Use android.telephony.SmsMessage. */
+    @Deprecated
+    public SmsMessage() {
+        this(getSmsFacility());
+    }
+
+    private SmsMessage(SmsMessageBase smb) {
+        mWrappedSmsMessage = smb;
+    }
+
+    /**
+     * Create an SmsMessage from a raw PDU.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static SmsMessage createFromPdu(byte[] pdu) {
+        SmsMessageBase wrappedMessage;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
+        } else {
+            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
+        }
+
+        return new SmsMessage(wrappedMessage);
+    }
+
+    /**
+     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
+     * length in bytes (not hex chars) less the SMSC header
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static int getTPLayerLengthForPDU(String pdu) {
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
+        } else {
+            return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
+        }
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and
+     * the number of characters remaining until the next message, given the
+     * current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the GSM
+     *         alphabet are counted as a single space char.  If false, a
+     *         messageBody containing non-GSM alphabet characters is calculated
+     *         for 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1]
+     *         the number of code units used, and int[2] is the number of code
+     *         units remaining until the next message. int[3] is the encoding
+     *         type that should be used for the message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static int[] calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+        GsmAlphabet.TextEncodingDetails ted =
+                com.android.internal.telephony.gsm.SmsMessage
+                        .calculateLength(messageBody, use7bitOnly);
+        int ret[] = new int[4];
+        ret[0] = ted.msgCount;
+        ret[1] = ted.codeUnitCount;
+        ret[2] = ted.codeUnitsRemaining;
+        ret[3] = ted.codeUnitSize;
+        return ret;
+    }
+
+    /**
+     * Calculates the number of SMS's required to encode the message body and
+     * the number of characters remaining until the next message, given the
+     * current encoding.
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly if true, characters that are not part of the GSM
+     *         alphabet are counted as a single space char.  If false, a
+     *         messageBody containing non-GSM alphabet characters is calculated
+     *         for 16-bit encoding.
+     * @return an int[4] with int[0] being the number of SMS's required, int[1]
+     *         the number of code units used, and int[2] is the number of code
+     *         units remaining until the next message. int[3] is the encoding
+     *         type that should be used for the message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
+        return calculateLength((CharSequence)messageBody, use7bitOnly);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a destination address and a message
+     *
+     * @param scAddress Service Centre address.  Null means use default.
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide
+     */
+    @Deprecated
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message,
+            boolean statusReportRequested, byte[] header) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested,
+                    SmsHeader.fromByteArray(header));
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested, header);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a destination address and a message
+     *
+     * @param scAddress Service Centre address.  Null means use default.
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message, boolean statusReportRequested) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested, null);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, message, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
+     *
+     * @param scAddress Service Centre address. null == use default
+     * @param destinationAddress the address of the destination for the message
+     * @param destinationPort the port to deliver the message to at the
+     *        destination
+     * @param data the dat for the message
+     * @return a <code>SubmitPdu</code> containing the encoded SC
+     *         address, if applicable, and the encoded message.
+     *         Returns null on encode error.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, short destinationPort, byte[] data,
+            boolean statusReportRequested) {
+        SubmitPduBase spb;
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+
+        if (PHONE_TYPE_CDMA == activePhone) {
+            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        } else {
+            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                    destinationAddress, destinationPort, data, statusReportRequested);
+        }
+
+        return new SubmitPdu(spb);
+    }
+
+    /**
+     * Returns the address of the SMS service center that relayed this message
+     * or null if there is none.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getServiceCenterAddress() {
+        return mWrappedSmsMessage.getServiceCenterAddress();
+    }
+
+    /**
+     * Returns the originating address (sender) of this SMS message in String
+     * form or null if unavailable
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getOriginatingAddress() {
+        return mWrappedSmsMessage.getOriginatingAddress();
+    }
+
+    /**
+     * Returns the originating address, or email from address if this message
+     * was from an email gateway. Returns null if originating address
+     * unavailable.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getDisplayOriginatingAddress() {
+        return mWrappedSmsMessage.getDisplayOriginatingAddress();
+    }
+
+    /**
+     * Returns the message body as a String, if it exists and is text based.
+     * @return message body is there is one, otherwise null
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getMessageBody() {
+        return mWrappedSmsMessage.getMessageBody();
+    }
+
+    /**
+     * Returns the class of this message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public MessageClass getMessageClass() {
+        int index = mWrappedSmsMessage.getMessageClass().ordinal();
+
+        return MessageClass.values()[index];
+    }
+
+    /**
+     * Returns the message body, or email message body if this message was from
+     * an email gateway. Returns null if message body unavailable.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getDisplayMessageBody() {
+        return mWrappedSmsMessage.getDisplayMessageBody();
+    }
+
+    /**
+     * Unofficial convention of a subject line enclosed in parens empty string
+     * if not present
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getPseudoSubject() {
+        return mWrappedSmsMessage.getPseudoSubject();
+    }
+
+    /**
+     * Returns the service centre timestamp in currentTimeMillis() format
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public long getTimestampMillis() {
+        return mWrappedSmsMessage.getTimestampMillis();
+    }
+
+    /**
+     * Returns true if message is an email.
+     *
+     * @return true if this message came through an email gateway and email
+     *         sender / subject / parsed body are available
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isEmail() {
+        return mWrappedSmsMessage.isEmail();
+    }
+
+     /**
+     * @return if isEmail() is true, body of the email sent through the gateway.
+     *         null otherwise
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getEmailBody() {
+        return mWrappedSmsMessage.getEmailBody();
+    }
+
+    /**
+     * @return if isEmail() is true, email from address of email sent through
+     *         the gateway. null otherwise
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public String getEmailFrom() {
+        return mWrappedSmsMessage.getEmailFrom();
+    }
+
+    /**
+     * Get protocol identifier.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public int getProtocolIdentifier() {
+        return mWrappedSmsMessage.getProtocolIdentifier();
+    }
+
+    /**
+     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message" SMS
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isReplace() {
+        return mWrappedSmsMessage.isReplace();
+    }
+
+    /**
+     * Returns true for CPHS MWI toggle message.
+     *
+     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section B.4.2
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isCphsMwiMessage() {
+        return mWrappedSmsMessage.isCphsMwiMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) clear message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isMWIClearMessage() {
+        return mWrappedSmsMessage.isMWIClearMessage();
+    }
+
+    /**
+     * returns true if this message is a CPHS voicemail / message waiting
+     * indicator (MWI) set message
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isMWISetMessage() {
+        return mWrappedSmsMessage.isMWISetMessage();
+    }
+
+    /**
+     * returns true if this message is a "Message Waiting Indication Group:
+     * Discard Message" notification and should not be stored.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isMwiDontStore() {
+        return mWrappedSmsMessage.isMwiDontStore();
+    }
+
+    /**
+     * returns the user data section minus the user data header if one was present.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public byte[] getUserData() {
+        return mWrappedSmsMessage.getUserData();
+    }
+
+    /* Not part of the SDK interface and only needed by specific classes:
+       protected SmsHeader getUserDataHeader()
+    */
+
+    /**
+     * Returns the raw PDU for the message.
+     *
+     * @return the raw PDU for the message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public byte[] getPdu() {
+        return mWrappedSmsMessage.getPdu();
+    }
+
+    /**
+     * Returns the status of the message on the SIM (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the SIM.  These are:
+     *         SmsManager.STATUS_ON_SIM_FREE
+     *         SmsManager.STATUS_ON_SIM_READ
+     *         SmsManager.STATUS_ON_SIM_UNREAD
+     *         SmsManager.STATUS_ON_SIM_SEND
+     *         SmsManager.STATUS_ON_SIM_UNSENT
+     * @deprecated Use android.telephony.SmsMessage and getStatusOnIcc instead.
+     */
+    @Deprecated
+    public int getStatusOnSim() {
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the status of the message on the ICC (read, unread, sent, unsent).
+     *
+     * @return the status of the message on the ICC.  These are:
+     *         SmsManager.STATUS_ON_ICC_FREE
+     *         SmsManager.STATUS_ON_ICC_READ
+     *         SmsManager.STATUS_ON_ICC_UNREAD
+     *         SmsManager.STATUS_ON_ICC_SEND
+     *         SmsManager.STATUS_ON_ICC_UNSENT
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide
+     */
+    @Deprecated
+    public int getStatusOnIcc() {
+
+        return mWrappedSmsMessage.getStatusOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the SIM (1-based index).
+     * @return the record index of the message on the SIM, or -1 if this
+     *         SmsMessage was not created from a SIM SMS EF record.
+     * @deprecated Use android.telephony.SmsMessage and getIndexOnIcc instead.
+     */
+    @Deprecated
+    public int getIndexOnSim() {
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * Returns the record index of the message on the ICC (1-based index).
+     * @return the record index of the message on the ICC, or -1 if this
+     *         SmsMessage was not created from a ICC SMS EF record.
+     * @deprecated Use android.telephony.SmsMessage.
+     * @hide
+     */
+    @Deprecated
+    public int getIndexOnIcc() {
+
+        return mWrappedSmsMessage.getIndexOnIcc();
+    }
+
+    /**
+     * GSM:
+     * For an SMS-STATUS-REPORT message, this returns the status field from
+     * the status report.  This field indicates the status of a previously
+     * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
+     * description of values.
+     * CDMA:
+     * For not interfering with status codes from GSM, the value is
+     * shifted to the bits 31-16.
+     * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
+     * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
+     *
+     * @return 0 indicates the previously sent message was received.
+     *         See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
+     *         for a description of other possible values.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public int getStatus() {
+        return mWrappedSmsMessage.getStatus();
+    }
+
+    /**
+     * Return true iff the message is a SMS-STATUS-REPORT message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isStatusReportMessage() {
+        return mWrappedSmsMessage.isStatusReportMessage();
+    }
+
+    /**
+     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
+     * this message.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    @Deprecated
+    public boolean isReplyPathPresent() {
+        return mWrappedSmsMessage.isReplyPathPresent();
+    }
+
+    /** This method returns the reference to a specific
+     *  SmsMessage object, which is used for accessing its static methods.
+     * @return Specific SmsMessage.
+     * @deprecated Use android.telephony.SmsMessage.
+     */
+    private static final SmsMessageBase getSmsFacility(){
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        if (PHONE_TYPE_CDMA == activePhone) {
+            return new com.android.internal.telephony.cdma.SmsMessage();
+        } else {
+            return new com.android.internal.telephony.gsm.SmsMessage();
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ATParseEx.java b/src/java/com/android/internal/telephony/ATParseEx.java
new file mode 100644 (file)
index 0000000..c93b875
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2006 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;
+
+/**
+ * {@hide}
+ */
+public class ATParseEx extends RuntimeException
+{
+    public
+    ATParseEx()
+    {
+        super();
+    }
+
+    public
+    ATParseEx(String s)
+    {
+        super(s);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ATResponseParser.java b/src/java/com/android/internal/telephony/ATResponseParser.java
new file mode 100644 (file)
index 0000000..fdb0526
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2006 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;
+
+/**
+ * {@hide}
+ */
+public class ATResponseParser
+{
+    /*************************** Instance Variables **************************/
+
+    private String line;
+    private int next = 0;
+    private int tokStart, tokEnd;
+
+    /***************************** Class Methods *****************************/
+
+    public
+    ATResponseParser (String line)
+    {
+        this.line = line;
+    }
+
+    public boolean
+    nextBoolean()
+    {
+        // "\s*(\d)(,|$)"
+        // \d is '0' or '1'
+
+        nextTok();
+
+        if (tokEnd - tokStart > 1) {
+            throw new ATParseEx();
+        }
+        char c = line.charAt(tokStart);
+
+        if (c == '0') return false;
+        if (c ==  '1') return true;
+        throw new ATParseEx();
+    }
+
+
+    /** positive int only */
+    public int
+    nextInt()
+    {
+        // "\s*(\d+)(,|$)"
+        int ret = 0;
+
+        nextTok();
+
+        for (int i = tokStart ; i < tokEnd ; i++) {
+            char c = line.charAt(i);
+
+            // Yes, ASCII decimal digits only
+            if (c < '0' || c > '9') {
+                throw new ATParseEx();
+            }
+
+            ret *= 10;
+            ret += c - '0';
+        }
+
+        return ret;
+    }
+
+    public String
+    nextString()
+    {
+        nextTok();
+
+        return line.substring(tokStart, tokEnd);
+    }
+
+    public boolean
+    hasMore()
+    {
+        return next < line.length();
+    }
+
+    private void
+    nextTok()
+    {
+        int len = line.length();
+
+        if (next == 0) {
+            skipPrefix();
+        }
+
+        if (next >= len) {
+            throw new ATParseEx();
+        }
+
+        try {
+            // \s*("([^"]*)"|(.*)\s*)(,|$)
+
+            char c = line.charAt(next++);
+            boolean hasQuote = false;
+
+            c = skipWhiteSpace(c);
+
+            if (c == '"') {
+                if (next >= len) {
+                    throw new ATParseEx();
+                }
+                c = line.charAt(next++);
+                tokStart = next - 1;
+                while (c != '"' && next < len) {
+                    c = line.charAt(next++);
+                }
+                if (c != '"') {
+                    throw new ATParseEx();
+                }
+                tokEnd = next - 1;
+                if (next < len && line.charAt(next++) != ',') {
+                    throw new ATParseEx();
+                }
+            } else {
+                tokStart = next - 1;
+                tokEnd = tokStart;
+                while (c != ',') {
+                    if (!Character.isWhitespace(c)) {
+                        tokEnd = next;
+                    }
+                    if (next == len) {
+                        break;
+                    }
+                    c = line.charAt(next++);
+                }
+            }
+        } catch (StringIndexOutOfBoundsException ex) {
+            throw new ATParseEx();
+        }
+    }
+
+
+    /** Throws ATParseEx if whitespace extends to the end of string */
+    private char
+    skipWhiteSpace (char c)
+    {
+        int len;
+        len = line.length();
+        while (next < len && Character.isWhitespace(c)) {
+            c = line.charAt(next++);
+        }
+
+        if (Character.isWhitespace(c)) {
+            throw new ATParseEx();
+        }
+        return c;
+    }
+
+
+    private void
+    skipPrefix()
+    {
+        // consume "^[^:]:"
+
+        next = 0;
+        int s = line.length();
+        while (next < s){
+            char c = line.charAt(next++);
+
+            if (c == ':') {
+                return;
+            }
+        }
+
+        throw new ATParseEx("missing prefix");
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/AdnRecord.java b/src/java/com/android/internal/telephony/AdnRecord.java
new file mode 100644 (file)
index 0000000..1bf2d3c
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2006 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.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+
+
+/**
+ *
+ * Used to load or store ADNs (Abbreviated Dialing Numbers).
+ *
+ * {@hide}
+ *
+ */
+public class AdnRecord implements Parcelable {
+    static final String LOG_TAG = "GSM";
+
+    //***** Instance Variables
+
+    String alphaTag = null;
+    String number = null;
+    String[] emails;
+    int extRecord = 0xff;
+    int efid;                   // or 0 if none
+    int recordNumber;           // or 0 if none
+
+
+    //***** Constants
+
+    // In an ADN record, everything but the alpha identifier
+    // is in a footer that's 14 bytes
+    static final int FOOTER_SIZE_BYTES = 14;
+
+    // Maximum size of the un-extended number field
+    static final int MAX_NUMBER_SIZE_BYTES = 11;
+
+    static final int EXT_RECORD_LENGTH_BYTES = 13;
+    static final int EXT_RECORD_TYPE_ADDITIONAL_DATA = 2;
+    static final int EXT_RECORD_TYPE_MASK = 3;
+    static final int MAX_EXT_CALLED_PARTY_LENGTH = 0xa;
+
+    // ADN offset
+    static final int ADN_BCD_NUMBER_LENGTH = 0;
+    static final int ADN_TON_AND_NPI = 1;
+    static final int ADN_DIALING_NUMBER_START = 2;
+    static final int ADN_DIALING_NUMBER_END = 11;
+    static final int ADN_CAPABILITY_ID = 12;
+    static final int ADN_EXTENSION_ID = 13;
+
+    //***** Static Methods
+
+    public static final Parcelable.Creator<AdnRecord> CREATOR
+            = new Parcelable.Creator<AdnRecord>() {
+        public AdnRecord createFromParcel(Parcel source) {
+            int efid;
+            int recordNumber;
+            String alphaTag;
+            String number;
+            String[] emails;
+
+            efid = source.readInt();
+            recordNumber = source.readInt();
+            alphaTag = source.readString();
+            number = source.readString();
+            emails = source.readStringArray();
+
+            return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
+        }
+
+        public AdnRecord[] newArray(int size) {
+            return new AdnRecord[size];
+        }
+    };
+
+
+    //***** Constructor
+    public AdnRecord (byte[] record) {
+        this(0, 0, record);
+    }
+
+    public AdnRecord (int efid, int recordNumber, byte[] record) {
+        this.efid = efid;
+        this.recordNumber = recordNumber;
+        parseRecord(record);
+    }
+
+    public AdnRecord (String alphaTag, String number) {
+        this(0, 0, alphaTag, number);
+    }
+
+    public AdnRecord (String alphaTag, String number, String[] emails) {
+        this(0, 0, alphaTag, number, emails);
+    }
+
+    public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
+        this.efid = efid;
+        this.recordNumber = recordNumber;
+        this.alphaTag = alphaTag;
+        this.number = number;
+        this.emails = emails;
+    }
+
+    public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
+        this.efid = efid;
+        this.recordNumber = recordNumber;
+        this.alphaTag = alphaTag;
+        this.number = number;
+        this.emails = null;
+    }
+
+    //***** Instance Methods
+
+    public String getAlphaTag() {
+        return alphaTag;
+    }
+
+    public String getNumber() {
+        return number;
+    }
+
+    public String[] getEmails() {
+        return emails;
+    }
+
+    public void setEmails(String[] emails) {
+        this.emails = emails;
+    }
+
+    public String toString() {
+        return "ADN Record '" + alphaTag + "' '" + number + " " + emails + "'";
+    }
+
+    public boolean isEmpty() {
+        return TextUtils.isEmpty(alphaTag) && TextUtils.isEmpty(number) && emails == null;
+    }
+
+    public boolean hasExtendedRecord() {
+        return extRecord != 0 && extRecord != 0xff;
+    }
+
+    /** Helper function for {@link #isEqual}. */
+    private static boolean stringCompareNullEqualsEmpty(String s1, String s2) {
+        if (s1 == s2) {
+            return true;
+        }
+        if (s1 == null) {
+            s1 = "";
+        }
+        if (s2 == null) {
+            s2 = "";
+        }
+        return (s1.equals(s2));
+    }
+
+    public boolean isEqual(AdnRecord adn) {
+        return ( stringCompareNullEqualsEmpty(alphaTag, adn.alphaTag) &&
+                stringCompareNullEqualsEmpty(number, adn.number) &&
+                Arrays.equals(emails, adn.emails));
+    }
+    //***** Parcelable Implementation
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(efid);
+        dest.writeInt(recordNumber);
+        dest.writeString(alphaTag);
+        dest.writeString(number);
+        dest.writeStringArray(emails);
+    }
+
+    /**
+     * Build adn hex byte array based on record size
+     * The format of byte array is defined in 51.011 10.5.1
+     *
+     * @param recordSize is the size X of EF record
+     * @return hex byte[recordSize] to be written to EF record
+     *          return null for wrong format of dialing number or tag
+     */
+    public byte[] buildAdnString(int recordSize) {
+        byte[] bcdNumber;
+        byte[] byteTag;
+        byte[] adnString;
+        int footerOffset = recordSize - FOOTER_SIZE_BYTES;
+
+        // create an empty record
+        adnString = new byte[recordSize];
+        for (int i = 0; i < recordSize; i++) {
+            adnString[i] = (byte) 0xFF;
+        }
+
+        if (TextUtils.isEmpty(number)) {
+            Log.w(LOG_TAG, "[buildAdnString] Empty dialing number");
+            return adnString;   // return the empty record (for delete)
+        } else if (number.length()
+                > (ADN_DIALING_NUMBER_END - ADN_DIALING_NUMBER_START + 1) * 2) {
+            Log.w(LOG_TAG,
+                    "[buildAdnString] Max length of dialing number is 20");
+            return null;
+        } else if (alphaTag != null && alphaTag.length() > footerOffset) {
+            Log.w(LOG_TAG,
+                    "[buildAdnString] Max length of tag is " + footerOffset);
+            return null;
+        } else {
+            bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(number);
+
+            System.arraycopy(bcdNumber, 0, adnString,
+                    footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
+
+            adnString[footerOffset + ADN_BCD_NUMBER_LENGTH]
+                    = (byte) (bcdNumber.length);
+            adnString[footerOffset + ADN_CAPABILITY_ID]
+                    = (byte) 0xFF; // Capability Id
+            adnString[footerOffset + ADN_EXTENSION_ID]
+                    = (byte) 0xFF; // Extension Record Id
+
+            if (!TextUtils.isEmpty(alphaTag)) {
+                byteTag = GsmAlphabet.stringToGsm8BitPacked(alphaTag);
+                System.arraycopy(byteTag, 0, adnString, 0, byteTag.length);
+            }
+
+            return adnString;
+        }
+    }
+
+    /**
+     * See TS 51.011 10.5.10
+     */
+    public void
+    appendExtRecord (byte[] extRecord) {
+        try {
+            if (extRecord.length != EXT_RECORD_LENGTH_BYTES) {
+                return;
+            }
+
+            if ((extRecord[0] & EXT_RECORD_TYPE_MASK)
+                    != EXT_RECORD_TYPE_ADDITIONAL_DATA) {
+                return;
+            }
+
+            if ((0xff & extRecord[1]) > MAX_EXT_CALLED_PARTY_LENGTH) {
+                // invalid or empty record
+                return;
+            }
+
+            number += PhoneNumberUtils.calledPartyBCDFragmentToString(
+                                        extRecord, 2, 0xff & extRecord[1]);
+
+            // We don't support ext record chaining.
+
+        } catch (RuntimeException ex) {
+            Log.w(LOG_TAG, "Error parsing AdnRecord ext record", ex);
+        }
+    }
+
+    //***** Private Methods
+
+    /**
+     * alphaTag and number are set to null on invalid format
+     */
+    private void
+    parseRecord(byte[] record) {
+        try {
+            alphaTag = IccUtils.adnStringFieldToString(
+                            record, 0, record.length - FOOTER_SIZE_BYTES);
+
+            int footerOffset = record.length - FOOTER_SIZE_BYTES;
+
+            int numberLength = 0xff & record[footerOffset];
+
+            if (numberLength > MAX_NUMBER_SIZE_BYTES) {
+                // Invalid number length
+                number = "";
+                return;
+            }
+
+            // Please note 51.011 10.5.1:
+            //
+            // "If the Dialling Number/SSC String does not contain
+            // a dialling number, e.g. a control string deactivating
+            // a service, the TON/NPI byte shall be set to 'FF' by
+            // the ME (see note 2)."
+
+            number = PhoneNumberUtils.calledPartyBCDToString(
+                            record, footerOffset + 1, numberLength);
+
+
+            extRecord = 0xff & record[record.length - 1];
+
+            emails = null;
+
+        } catch (RuntimeException ex) {
+            Log.w(LOG_TAG, "Error parsing AdnRecord", ex);
+            number = "";
+            alphaTag = "";
+            emails = null;
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/AdnRecordCache.java b/src/java/com/android/internal/telephony/AdnRecordCache.java
new file mode 100644 (file)
index 0000000..db5f4da
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006 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.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.telephony.gsm.UsimPhoneBookManager;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * {@hide}
+ */
+public final class AdnRecordCache extends Handler implements IccConstants {
+    //***** Instance Variables
+
+    private IccFileHandler mFh;
+    private UsimPhoneBookManager mUsimPhoneBookManager;
+
+    // Indexed by EF ID
+    SparseArray<ArrayList<AdnRecord>> adnLikeFiles
+        = new SparseArray<ArrayList<AdnRecord>>();
+
+    // People waiting for ADN-like files to be loaded
+    SparseArray<ArrayList<Message>> adnLikeWaiters
+        = new SparseArray<ArrayList<Message>>();
+
+    // People waiting for adn record to be updated
+    SparseArray<Message> userWriteResponse = new SparseArray<Message>();
+
+    //***** Event Constants
+
+    static final int EVENT_LOAD_ALL_ADN_LIKE_DONE = 1;
+    static final int EVENT_UPDATE_ADN_DONE = 2;
+
+    //***** Constructor
+
+
+
+    public AdnRecordCache(IccFileHandler fh) {
+        mFh = fh;
+        mUsimPhoneBookManager = new UsimPhoneBookManager(mFh, this);
+    }
+
+    //***** Called from SIMRecords
+
+    /**
+     * Called from SIMRecords.onRadioNotAvailable and SIMRecords.handleSimRefresh.
+     */
+    public void reset() {
+        adnLikeFiles.clear();
+        mUsimPhoneBookManager.reset();
+
+        clearWaiters();
+        clearUserWriters();
+
+    }
+
+    private void clearWaiters() {
+        int size = adnLikeWaiters.size();
+        for (int i = 0; i < size; i++) {
+            ArrayList<Message> waiters = adnLikeWaiters.valueAt(i);
+            AsyncResult ar = new AsyncResult(null, null, new RuntimeException("AdnCache reset"));
+            notifyWaiters(waiters, ar);
+        }
+        adnLikeWaiters.clear();
+    }
+
+    private void clearUserWriters() {
+        int size = userWriteResponse.size();
+        for (int i = 0; i < size; i++) {
+            sendErrorResponse(userWriteResponse.valueAt(i), "AdnCace reset");
+        }
+        userWriteResponse.clear();
+    }
+
+    /**
+     * @return List of AdnRecords for efid if we've already loaded them this
+     * radio session, or null if we haven't
+     */
+    public ArrayList<AdnRecord>
+    getRecordsIfLoaded(int efid) {
+        return adnLikeFiles.get(efid);
+    }
+
+    /**
+     * Returns extension ef associated with ADN-like EF or -1 if
+     * we don't know.
+     *
+     * See 3GPP TS 51.011 for this mapping
+     */
+    int extensionEfForEf(int efid) {
+        switch (efid) {
+            case EF_MBDN: return EF_EXT6;
+            case EF_ADN: return EF_EXT1;
+            case EF_SDN: return EF_EXT3;
+            case EF_FDN: return EF_EXT2;
+            case EF_MSISDN: return EF_EXT1;
+            case EF_PBR: return 0; // The EF PBR doesn't have an extension record
+            default: return -1;
+        }
+    }
+
+    private void sendErrorResponse(Message response, String errString) {
+        if (response != null) {
+            Exception e = new RuntimeException(errString);
+            AsyncResult.forMessage(response).exception = e;
+            response.sendToTarget();
+        }
+    }
+
+    /**
+     * Update an ADN-like record in EF by record index
+     *
+     * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
+     * @param adn is the new adn to be stored
+     * @param recordIndex is the 1-based adn record index
+     * @param pin2 is required to update EF_FDN, otherwise must be null
+     * @param response message to be posted when done
+     *        response.exception hold the exception in error
+     */
+    public void updateAdnByIndex(int efid, AdnRecord adn, int recordIndex, String pin2,
+            Message response) {
+
+        int extensionEF = extensionEfForEf(efid);
+        if (extensionEF < 0) {
+            sendErrorResponse(response, "EF is not known ADN-like EF:" + efid);
+            return;
+        }
+
+        Message pendingResponse = userWriteResponse.get(efid);
+        if (pendingResponse != null) {
+            sendErrorResponse(response, "Have pending update for EF:" + efid);
+            return;
+        }
+
+        userWriteResponse.put(efid, response);
+
+        new AdnRecordLoader(mFh).updateEF(adn, efid, extensionEF,
+                recordIndex, pin2,
+                obtainMessage(EVENT_UPDATE_ADN_DONE, efid, recordIndex, adn));
+    }
+
+    /**
+     * Replace oldAdn with newAdn in ADN-like record in EF
+     *
+     * The ADN-like records must be read through requestLoadAllAdnLike() before
+     *
+     * @param efid must be one of EF_ADN, EF_FDN, and EF_SDN
+     * @param oldAdn is the adn to be replaced
+     *        If oldAdn.isEmpty() is ture, it insert the newAdn
+     * @param newAdn is the adn to be stored
+     *        If newAdn.isEmpty() is true, it delete the oldAdn
+     * @param pin2 is required to update EF_FDN, otherwise must be null
+     * @param response message to be posted when done
+     *        response.exception hold the exception in error
+     */
+    public void updateAdnBySearch(int efid, AdnRecord oldAdn, AdnRecord newAdn,
+            String pin2, Message response) {
+
+        int extensionEF;
+        extensionEF = extensionEfForEf(efid);
+
+        if (extensionEF < 0) {
+            sendErrorResponse(response, "EF is not known ADN-like EF:" + efid);
+            return;
+        }
+
+        ArrayList<AdnRecord>  oldAdnList;
+
+        if (efid == EF_PBR) {
+            oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim();
+        } else {
+            oldAdnList = getRecordsIfLoaded(efid);
+        }
+
+        if (oldAdnList == null) {
+            sendErrorResponse(response, "Adn list not exist for EF:" + efid);
+            return;
+        }
+
+        int index = -1;
+        int count = 1;
+        for (Iterator<AdnRecord> it = oldAdnList.iterator(); it.hasNext(); ) {
+            if (oldAdn.isEqual(it.next())) {
+                index = count;
+                break;
+            }
+            count++;
+        }
+
+        if (index == -1) {
+            sendErrorResponse(response, "Adn record don't exist for " + oldAdn);
+            return;
+        }
+
+        if (efid == EF_PBR) {
+            AdnRecord foundAdn = oldAdnList.get(index-1);
+            efid = foundAdn.efid;
+            extensionEF = foundAdn.extRecord;
+            index = foundAdn.recordNumber;
+
+            newAdn.efid = efid;
+            newAdn.extRecord = extensionEF;
+            newAdn.recordNumber = index;
+        }
+
+        Message pendingResponse = userWriteResponse.get(efid);
+
+        if (pendingResponse != null) {
+            sendErrorResponse(response, "Have pending update for EF:" + efid);
+            return;
+        }
+
+        userWriteResponse.put(efid, response);
+
+        new AdnRecordLoader(mFh).updateEF(newAdn, efid, extensionEF,
+                index, pin2,
+                obtainMessage(EVENT_UPDATE_ADN_DONE, efid, index, newAdn));
+    }
+
+
+    /**
+     * Responds with exception (in response) if efid is not a known ADN-like
+     * record
+     */
+    public void
+    requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
+        ArrayList<Message> waiters;
+        ArrayList<AdnRecord> result;
+
+        if (efid == EF_PBR) {
+            result = mUsimPhoneBookManager.loadEfFilesFromUsim();
+        } else {
+            result = getRecordsIfLoaded(efid);
+        }
+
+        // Have we already loaded this efid?
+        if (result != null) {
+            if (response != null) {
+                AsyncResult.forMessage(response).result = result;
+                response.sendToTarget();
+            }
+
+            return;
+        }
+
+        // Have we already *started* loading this efid?
+
+        waiters = adnLikeWaiters.get(efid);
+
+        if (waiters != null) {
+            // There's a pending request for this EF already
+            // just add ourselves to it
+
+            waiters.add(response);
+            return;
+        }
+
+        // Start loading efid
+
+        waiters = new ArrayList<Message>();
+        waiters.add(response);
+
+        adnLikeWaiters.put(efid, waiters);
+
+
+        if (extensionEf < 0) {
+            // respond with error if not known ADN-like record
+
+            if (response != null) {
+                AsyncResult.forMessage(response).exception
+                    = new RuntimeException("EF is not known ADN-like EF:" + efid);
+                response.sendToTarget();
+            }
+
+            return;
+        }
+
+        new AdnRecordLoader(mFh).loadAllFromEF(efid, extensionEf,
+            obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));
+    }
+
+    //***** Private methods
+
+    private void
+    notifyWaiters(ArrayList<Message> waiters, AsyncResult ar) {
+
+        if (waiters == null) {
+            return;
+        }
+
+        for (int i = 0, s = waiters.size() ; i < s ; i++) {
+            Message waiter = waiters.get(i);
+
+            AsyncResult.forMessage(waiter, ar.result, ar.exception);
+            waiter.sendToTarget();
+        }
+    }
+
+    //***** Overridden from Handler
+
+    public void
+    handleMessage(Message msg) {
+        AsyncResult ar;
+        int efid;
+
+        switch(msg.what) {
+            case EVENT_LOAD_ALL_ADN_LIKE_DONE:
+                /* arg1 is efid, obj.result is ArrayList<AdnRecord>*/
+                ar = (AsyncResult) msg.obj;
+                efid = msg.arg1;
+                ArrayList<Message> waiters;
+
+                waiters = adnLikeWaiters.get(efid);
+                adnLikeWaiters.delete(efid);
+
+                if (ar.exception == null) {
+                    adnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result);
+                }
+                notifyWaiters(waiters, ar);
+                break;
+            case EVENT_UPDATE_ADN_DONE:
+                ar = (AsyncResult)msg.obj;
+                efid = msg.arg1;
+                int index = msg.arg2;
+                AdnRecord adn = (AdnRecord) (ar.userObj);
+
+                if (ar.exception == null) {
+                    adnLikeFiles.get(efid).set(index - 1, adn);
+                    mUsimPhoneBookManager.invalidateCache();
+                }
+
+                Message response = userWriteResponse.get(efid);
+                userWriteResponse.delete(efid);
+
+                AsyncResult.forMessage(response, null, ar.exception);
+                response.sendToTarget();
+                break;
+        }
+
+    }
+
+
+}
diff --git a/src/java/com/android/internal/telephony/AdnRecordLoader.java b/src/java/com/android/internal/telephony/AdnRecordLoader.java
new file mode 100644 (file)
index 0000000..084fae6
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2006 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 java.util.ArrayList;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+
+public class AdnRecordLoader extends Handler {
+    final static String LOG_TAG = "RIL_AdnRecordLoader";
+
+    //***** Instance Variables
+
+    private IccFileHandler mFh;
+    int ef;
+    int extensionEF;
+    int pendingExtLoads;
+    Message userResponse;
+    String pin2;
+
+    // For "load one"
+    int recordNumber;
+
+    // for "load all"
+    ArrayList<AdnRecord> adns; // only valid after EVENT_ADN_LOAD_ALL_DONE
+
+    // Either an AdnRecord or a reference to adns depending
+    // if this is a load one or load all operation
+    Object result;
+
+    //***** Event Constants
+
+    static final int EVENT_ADN_LOAD_DONE = 1;
+    static final int EVENT_EXT_RECORD_LOAD_DONE = 2;
+    static final int EVENT_ADN_LOAD_ALL_DONE = 3;
+    static final int EVENT_EF_LINEAR_RECORD_SIZE_DONE = 4;
+    static final int EVENT_UPDATE_RECORD_DONE = 5;
+
+    //***** Constructor
+
+    public AdnRecordLoader(IccFileHandler fh) {
+        // The telephony unit-test cases may create AdnRecords
+        // in secondary threads
+        super(Looper.getMainLooper());
+        mFh = fh;
+    }
+
+    /**
+     * Resulting AdnRecord is placed in response.obj.result
+     * or response.obj.exception is set
+     */
+    public void
+    loadFromEF(int ef, int extensionEF, int recordNumber,
+                Message response) {
+        this.ef = ef;
+        this.extensionEF = extensionEF;
+        this.recordNumber = recordNumber;
+        this.userResponse = response;
+
+        mFh.loadEFLinearFixed(
+                    ef, recordNumber,
+                    obtainMessage(EVENT_ADN_LOAD_DONE));
+
+    }
+
+
+    /**
+     * Resulting ArrayList&lt;adnRecord> is placed in response.obj.result
+     * or response.obj.exception is set
+     */
+    public void
+    loadAllFromEF(int ef, int extensionEF,
+                Message response) {
+        this.ef = ef;
+        this.extensionEF = extensionEF;
+        this.userResponse = response;
+
+        mFh.loadEFLinearFixedAll(
+                    ef,
+                    obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
+
+    }
+
+    /**
+     * Write adn to a EF SIM record
+     * It will get the record size of EF record and compose hex adn array
+     * then write the hex array to EF record
+     *
+     * @param adn is set with alphaTag and phone number
+     * @param ef EF fileid
+     * @param extensionEF extension EF fileid
+     * @param recordNumber 1-based record index
+     * @param pin2 for CHV2 operations, must be null if pin2 is not needed
+     * @param response will be sent to its handler when completed
+     */
+    public void
+    updateEF(AdnRecord adn, int ef, int extensionEF, int recordNumber,
+            String pin2, Message response) {
+        this.ef = ef;
+        this.extensionEF = extensionEF;
+        this.recordNumber = recordNumber;
+        this.userResponse = response;
+        this.pin2 = pin2;
+
+        mFh.getEFLinearRecordSize( ef,
+            obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn));
+    }
+
+    //***** Overridden from Handler
+
+    public void
+    handleMessage(Message msg) {
+        AsyncResult ar;
+        byte data[];
+        AdnRecord adn;
+
+        try {
+            switch (msg.what) {
+                case EVENT_EF_LINEAR_RECORD_SIZE_DONE:
+                    ar = (AsyncResult)(msg.obj);
+                    adn = (AdnRecord)(ar.userObj);
+
+                    if (ar.exception != null) {
+                        throw new RuntimeException("get EF record size failed",
+                                ar.exception);
+                    }
+
+                    int[] recordSize = (int[])ar.result;
+                    // recordSize is int[3] array
+                    // int[0]  is the record length
+                    // int[1]  is the total length of the EF file
+                    // int[2]  is the number of records in the EF file
+                    // So int[0] * int[2] = int[1]
+                   if (recordSize.length != 3 || recordNumber > recordSize[2]) {
+                        throw new RuntimeException("get wrong EF record size format",
+                                ar.exception);
+                    }
+
+                    data = adn.buildAdnString(recordSize[0]);
+
+                    if(data == null) {
+                        throw new RuntimeException("wrong ADN format",
+                                ar.exception);
+                    }
+
+                    mFh.updateEFLinearFixed(ef, recordNumber,
+                            data, pin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
+
+                    pendingExtLoads = 1;
+
+                    break;
+                case EVENT_UPDATE_RECORD_DONE:
+                    ar = (AsyncResult)(msg.obj);
+                    if (ar.exception != null) {
+                        throw new RuntimeException("update EF adn record failed",
+                                ar.exception);
+                    }
+                    pendingExtLoads = 0;
+                    result = null;
+                    break;
+                case EVENT_ADN_LOAD_DONE:
+                    ar = (AsyncResult)(msg.obj);
+                    data = (byte[])(ar.result);
+
+                    if (ar.exception != null) {
+                        throw new RuntimeException("load failed", ar.exception);
+                    }
+
+                    if (false) {
+                        Log.d(LOG_TAG,"ADN EF: 0x"
+                            + Integer.toHexString(ef)
+                            + ":" + recordNumber
+                            + "\n" + IccUtils.bytesToHexString(data));
+                    }
+
+                    adn = new AdnRecord(ef, recordNumber, data);
+                    result = adn;
+
+                    if (adn.hasExtendedRecord()) {
+                        // If we have a valid value in the ext record field,
+                        // we're not done yet: we need to read the corresponding
+                        // ext record and append it
+
+                        pendingExtLoads = 1;
+
+                        mFh.loadEFLinearFixed(
+                            extensionEF, adn.extRecord,
+                            obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
+                    }
+                break;
+
+                case EVENT_EXT_RECORD_LOAD_DONE:
+                    ar = (AsyncResult)(msg.obj);
+                    data = (byte[])(ar.result);
+                    adn = (AdnRecord)(ar.userObj);
+
+                    if (ar.exception != null) {
+                        throw new RuntimeException("load failed", ar.exception);
+                    }
+
+                    Log.d(LOG_TAG,"ADN extension EF: 0x"
+                        + Integer.toHexString(extensionEF)
+                        + ":" + adn.extRecord
+                        + "\n" + IccUtils.bytesToHexString(data));
+
+                    adn.appendExtRecord(data);
+
+                    pendingExtLoads--;
+                    // result should have been set in
+                    // EVENT_ADN_LOAD_DONE or EVENT_ADN_LOAD_ALL_DONE
+                break;
+
+                case EVENT_ADN_LOAD_ALL_DONE:
+                    ar = (AsyncResult)(msg.obj);
+                    ArrayList<byte[]> datas = (ArrayList<byte[]>)(ar.result);
+
+                    if (ar.exception != null) {
+                        throw new RuntimeException("load failed", ar.exception);
+                    }
+
+                    adns = new ArrayList<AdnRecord>(datas.size());
+                    result = adns;
+                    pendingExtLoads = 0;
+
+                    for(int i = 0, s = datas.size() ; i < s ; i++) {
+                        adn = new AdnRecord(ef, 1 + i, datas.get(i));
+                        adns.add(adn);
+
+                        if (adn.hasExtendedRecord()) {
+                            // If we have a valid value in the ext record field,
+                            // we're not done yet: we need to read the corresponding
+                            // ext record and append it
+
+                            pendingExtLoads++;
+
+                            mFh.loadEFLinearFixed(
+                                extensionEF, adn.extRecord,
+                                obtainMessage(EVENT_EXT_RECORD_LOAD_DONE, adn));
+                        }
+                    }
+                break;
+            }
+        } catch (RuntimeException exc) {
+            if (userResponse != null) {
+                AsyncResult.forMessage(userResponse)
+                                .exception = exc;
+                userResponse.sendToTarget();
+                // Loading is all or nothing--either every load succeeds
+                // or we fail the whole thing.
+                userResponse = null;
+            }
+            return;
+        }
+
+        if (userResponse != null && pendingExtLoads == 0) {
+            AsyncResult.forMessage(userResponse).result
+                = result;
+
+            userResponse.sendToTarget();
+            userResponse = null;
+        }
+    }
+
+
+}
diff --git a/src/java/com/android/internal/telephony/ApnContext.java b/src/java/com/android/internal/telephony/ApnContext.java
new file mode 100644 (file)
index 0000000..4817a7b
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Maintain the Apn context
+ */
+public class ApnContext {
+
+    public final String LOG_TAG;
+
+    protected static final boolean DBG = true;
+
+    private final String mApnType;
+
+    private DctConstants.State mState;
+
+    private ArrayList<ApnSetting> mWaitingApns = null;
+
+    /** A zero indicates that all waiting APNs had a permanent error */
+    private AtomicInteger mWaitingApnsPermanentFailureCountDown;
+
+    private ApnSetting mApnSetting;
+
+    DataConnection mDataConnection;
+
+    DataConnectionAc mDataConnectionAc;
+
+    String mReason;
+
+    int mRetryCount;
+
+    /**
+     * user/app requested connection on this APN
+     */
+    AtomicBoolean mDataEnabled;
+
+    /**
+     * carrier requirements met
+     */
+    AtomicBoolean mDependencyMet;
+
+    public ApnContext(String apnType, String logTag) {
+        mApnType = apnType;
+        mState = DctConstants.State.IDLE;
+        setReason(Phone.REASON_DATA_ENABLED);
+        setRetryCount(0);
+        mDataEnabled = new AtomicBoolean(false);
+        mDependencyMet = new AtomicBoolean(true);
+        mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0);
+        LOG_TAG = logTag;
+    }
+
+    public String getApnType() {
+        return mApnType;
+    }
+
+    public synchronized DataConnection getDataConnection() {
+        return mDataConnection;
+    }
+
+    public synchronized void setDataConnection(DataConnection dc) {
+        if (DBG) {
+            log("setDataConnection: old dc=" + mDataConnection + " new dc=" + dc + " this=" + this);
+        }
+        mDataConnection = dc;
+    }
+
+
+    public synchronized DataConnectionAc getDataConnectionAc() {
+        return mDataConnectionAc;
+    }
+
+    public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
+        if (DBG) {
+            log("setDataConnectionAc: old dcac=" + mDataConnectionAc + " new dcac=" + dcac);
+        }
+        if (dcac != null) {
+            dcac.addApnContextSync(this);
+        } else {
+            if (mDataConnectionAc != null) {
+                mDataConnectionAc.removeApnContextSync(this);
+            }
+        }
+        mDataConnectionAc = dcac;
+    }
+
+    public synchronized ApnSetting getApnSetting() {
+        return mApnSetting;
+    }
+
+    public synchronized void setApnSetting(ApnSetting apnSetting) {
+        mApnSetting = apnSetting;
+    }
+
+    public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
+        mWaitingApns = waitingApns;
+        mWaitingApnsPermanentFailureCountDown.set(mWaitingApns.size());
+    }
+
+    public int getWaitingApnsPermFailCount() {
+        return mWaitingApnsPermanentFailureCountDown.get();
+    }
+
+    public void decWaitingApnsPermFailCount() {
+        mWaitingApnsPermanentFailureCountDown.decrementAndGet();
+    }
+
+    public synchronized ApnSetting getNextWaitingApn() {
+        ArrayList<ApnSetting> list = mWaitingApns;
+        ApnSetting apn = null;
+
+        if (list != null) {
+            if (!list.isEmpty()) {
+                apn = list.get(0);
+            }
+        }
+        return apn;
+    }
+
+    public synchronized void removeWaitingApn(ApnSetting apn) {
+        if (mWaitingApns != null) {
+            mWaitingApns.remove(apn);
+        }
+    }
+
+    public synchronized ArrayList<ApnSetting> getWaitingApns() {
+        return mWaitingApns;
+    }
+
+    public synchronized void setState(DctConstants.State s) {
+        if (DBG) {
+            log("setState: " + s + ", previous state:" + mState);
+        }
+
+        mState = s;
+
+        if (mState == DctConstants.State.FAILED) {
+            if (mWaitingApns != null) {
+                mWaitingApns.clear(); // when teardown the connection and set to IDLE
+            }
+        }
+    }
+
+    public synchronized DctConstants.State getState() {
+        return mState;
+    }
+
+    public boolean isDisconnected() {
+        DctConstants.State currentState = getState();
+        return ((currentState == DctConstants.State.IDLE) ||
+                    currentState == DctConstants.State.FAILED);
+    }
+
+    public synchronized void setReason(String reason) {
+        if (DBG) {
+            log("set reason as " + reason + ",current state " + mState);
+        }
+        mReason = reason;
+    }
+
+    public synchronized String getReason() {
+        return mReason;
+    }
+
+    public synchronized void setRetryCount(int retryCount) {
+        if (DBG) {
+            log("setRetryCount: " + retryCount);
+        }
+        mRetryCount = retryCount;
+        DataConnection dc = mDataConnection;
+        if (dc != null) {
+            dc.setRetryCount(retryCount);
+        }
+    }
+
+    public synchronized int getRetryCount() {
+        return mRetryCount;
+    }
+
+    public boolean isReady() {
+        return mDataEnabled.get() && mDependencyMet.get();
+    }
+
+    public void setEnabled(boolean enabled) {
+        if (DBG) {
+            log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
+        }
+        mDataEnabled.set(enabled);
+    }
+
+    public boolean isEnabled() {
+        return mDataEnabled.get();
+    }
+
+    public void setDependencyMet(boolean met) {
+        if (DBG) {
+            log("set mDependencyMet as " + met + " current state is " + mDependencyMet.get());
+        }
+        mDependencyMet.set(met);
+    }
+
+    public boolean getDependencyMet() {
+       return mDependencyMet.get();
+    }
+
+    @Override
+    public String toString() {
+        // We don't print mDataConnection because its recursive.
+        return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns=" + mWaitingApns +
+                " mWaitingApnsPermanentFailureCountDown=" + mWaitingApnsPermanentFailureCountDown +
+                " mApnSetting=" + mApnSetting + " mDataConnectionAc=" + mDataConnectionAc +
+                " mReason=" + mReason + " mRetryCount=" + mRetryCount +
+                " mDataEnabled=" + mDataEnabled + " mDependencyMet=" + mDependencyMet + "}";
+    }
+
+    protected void log(String s) {
+        Log.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("ApnContext: " + this.toString());
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ApnSetting.java b/src/java/com/android/internal/telephony/ApnSetting.java
new file mode 100755 (executable)
index 0000000..b84c69c
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2006 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;
+
+/**
+ * This class represents a apn setting for create PDP link
+ */
+public class ApnSetting {
+
+    static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
+
+    public final String carrier;
+    public final String apn;
+    public final String proxy;
+    public final String port;
+    public final String mmsc;
+    public final String mmsProxy;
+    public final String mmsPort;
+    public final String user;
+    public final String password;
+    public final int authType;
+    public final String[] types;
+    public final int id;
+    public final String numeric;
+    public final String protocol;
+    public final String roamingProtocol;
+    /**
+      * Current status of APN
+      * true : enabled APN, false : disabled APN.
+      */
+    public final boolean carrierEnabled;
+    /**
+      * Radio Access Technology info
+      * To check what values can hold, refer to ServiceState.java.
+      * This should be spread to other technologies,
+      * but currently only used for LTE(14) and EHRPD(13).
+      */
+    public final int bearer;
+
+    public ApnSetting(int id, String numeric, String carrier, String apn,
+            String proxy, String port,
+            String mmsc, String mmsProxy, String mmsPort,
+            String user, String password, int authType, String[] types,
+            String protocol, String roamingProtocol, boolean carrierEnabled, int bearer) {
+        this.id = id;
+        this.numeric = numeric;
+        this.carrier = carrier;
+        this.apn = apn;
+        this.proxy = proxy;
+        this.port = port;
+        this.mmsc = mmsc;
+        this.mmsProxy = mmsProxy;
+        this.mmsPort = mmsPort;
+        this.user = user;
+        this.password = password;
+        this.authType = authType;
+        this.types = types;
+        this.protocol = protocol;
+        this.roamingProtocol = roamingProtocol;
+        this.carrierEnabled = carrierEnabled;
+        this.bearer = bearer;
+    }
+
+    /**
+     * Creates an ApnSetting object from a string.
+     *
+     * @param data the string to read.
+     *
+     * The string must be in one of two formats (newlines added for clarity,
+     * spaces are optional):
+     *
+     * v1 format:
+     *   <carrier>, <apn>, <proxy>, <port>, <mmsc>, <mmsproxy>,
+     *   <mmsport>, <user>, <password>, <authtype>, <mcc>,<mnc>,
+     *   <type>[, <type>...]
+     *
+     * v2 format:
+     *   [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <mmsc>, <mmsproxy>,
+     *   <mmsport>, <user>, <password>, <authtype>, <mcc>, <mnc>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearer>
+     *
+     * Note that the strings generated by toString() do not contain the username
+     * and password and thus cannot be read by this method.
+     *
+     * @see ApnSettingTest
+     */
+    public static ApnSetting fromString(String data) {
+        if (data == null) return null;
+
+        int version;
+        // matches() operates on the whole string, so append .* to the regex.
+        if (data.matches(V2_FORMAT_REGEX + ".*")) {
+            version = 2;
+            data = data.replaceFirst(V2_FORMAT_REGEX, "");
+        } else {
+            version = 1;
+        }
+
+        String[] a = data.split("\\s*,\\s*");
+        if (a.length < 14) {
+            return null;
+        }
+
+        int authType;