From 767a662ecde33c3979bf02b793d392aca0403162 Mon Sep 17 00:00:00 2001 From: Wink Saville <> Date: Thu, 2 Apr 2009 01:37:02 -0700 Subject: AI 144185: Integrate cdma into the main code base. Automated import of CL 144185 --- .../internal/telephony/ATResponseParser.java | 4 +- .../com/android/internal/telephony/AdnRecord.aidl | 20 + .../com/android/internal/telephony/AdnRecord.java | 283 ++ .../android/internal/telephony/AdnRecordCache.java | 339 +++ .../internal/telephony/AdnRecordLoader.java | 285 ++ .../android/internal/telephony/BaseCommands.java | 580 ++++ .../java/com/android/internal/telephony/Call.java | 49 +- .../internal/telephony/CallForwardInfo.java | 42 + .../android/internal/telephony/CallTracker.java | 123 + .../internal/telephony/CallerInfoAsyncQuery.java | 2 +- .../internal/telephony/CommandException.java | 83 + .../internal/telephony/CommandsInterface.java | 1193 ++++++++ .../com/android/internal/telephony/Connection.java | 57 +- .../android/internal/telephony/DataConnection.java | 299 ++ .../internal/telephony/DataConnectionTracker.java | 313 ++ .../com/android/internal/telephony/DataLink.java | 40 + .../internal/telephony/DataLinkInterface.java | 77 + .../internal/telephony/DefaultPhoneNotifier.java | 6 +- .../com/android/internal/telephony/DriverCall.java | 155 + .../internal/telephony/EncodeException.java | 35 + .../android/internal/telephony/GsmAlphabet.java | 798 +++++ .../android/internal/telephony/IIccPhoneBook.aidl | 101 + .../android/internal/telephony/IPhoneSubInfo.aidl | 16 +- .../java/com/android/internal/telephony/ISms.aidl | 115 + .../com/android/internal/telephony/ITelephony.aidl | 15 +- .../com/android/internal/telephony/IccCard.java | 207 ++ .../internal/telephony/IccCardApplication.java | 178 ++ .../android/internal/telephony/IccCardStatus.java | 86 + .../android/internal/telephony/IccConstants.java | 59 + .../android/internal/telephony/IccException.java | 30 + .../android/internal/telephony/IccFileHandler.java | 513 ++++ .../internal/telephony/IccFileNotFound.java | 34 + .../internal/telephony/IccFileTypeMismatch.java | 30 + .../android/internal/telephony/IccIoResult.java | 70 + .../telephony/IccPhoneBookInterfaceManager.java | 266 ++ .../IccPhoneBookInterfaceManagerProxy.java | 75 + .../android/internal/telephony/IccProvider.java | 456 +++ .../com/android/internal/telephony/IccRecords.java | 237 ++ .../internal/telephony/IccSmsInterfaceManager.java | 166 ++ .../telephony/IccSmsInterfaceManagerProxy.java | 66 + .../com/android/internal/telephony/IccUtils.java | 487 ++++ .../internal/telephony/IccVmFixedException.java | 32 + .../telephony/IccVmNotSupportedException.java | 32 + .../com/android/internal/telephony/MmiCode.java | 4 +- .../java/com/android/internal/telephony/Phone.java | 570 ++-- .../com/android/internal/telephony/PhoneBase.java | 326 ++- .../android/internal/telephony/PhoneFactory.java | 144 +- .../com/android/internal/telephony/PhoneProxy.java | 675 +++++ .../telephony/PhoneStateIntentReceiver.java | 6 +- .../android/internal/telephony/PhoneSubInfo.java | 36 +- .../internal/telephony/PhoneSubInfoProxy.java | 85 + .../java/com/android/internal/telephony/RIL.java | 3050 ++++++++++++++++++++ .../android/internal/telephony/RILConstants.java | 236 ++ .../android/internal/telephony/SMSDispatcher.java | 744 +++++ .../internal/telephony/ServiceStateTracker.java | 244 ++ .../com/android/internal/telephony/SimCard.java | 208 -- .../com/android/internal/telephony/SmsAddress.java | 65 + .../com/android/internal/telephony/SmsHeader.java | 242 ++ .../android/internal/telephony/SmsMessageBase.java | 388 +++ .../com/android/internal/telephony/SmsRawData.aidl | 19 + .../com/android/internal/telephony/SmsRawData.java | 62 + .../android/internal/telephony/SmsResponse.java | 34 + .../internal/telephony/TelephonyEventLog.java | 33 + .../internal/telephony/TelephonyIntents.java | 20 +- .../internal/telephony/TelephonyProperties.java | 23 +- .../android/internal/telephony/WapPushOverSms.java | 5 +- .../android/internal/telephony/cdma/CDMAPhone.java | 916 ++++++ .../internal/telephony/cdma/CallFailCause.java | 50 + .../android/internal/telephony/cdma/CdmaCall.java | 209 ++ .../internal/telephony/cdma/CdmaCallTracker.java | 860 ++++++ .../internal/telephony/cdma/CdmaConnection.java | 705 +++++ .../telephony/cdma/CdmaDataConnection.java | 318 ++ .../telephony/cdma/CdmaDataConnectionTracker.java | 929 ++++++ .../internal/telephony/cdma/CdmaSMSDispatcher.java | 394 +++ .../telephony/cdma/CdmaServiceStateTracker.java | 1016 +++++++ .../internal/telephony/cdma/FeatureCode.java | 312 ++ .../android/internal/telephony/cdma/RuimCard.java | 522 ++++ .../internal/telephony/cdma/RuimFileHandler.java | 79 + .../cdma/RuimPhoneBookInterfaceManager.java | 101 + .../internal/telephony/cdma/RuimRecords.java | 380 +++ .../telephony/cdma/RuimSmsInterfaceManager.java | 192 ++ .../internal/telephony/cdma/SmsMessage.java | 905 ++++++ .../android/internal/telephony/cdma/TtyIntent.java | 46 + .../android/internal/telephony/cdma/package.html | 6 + .../internal/telephony/cdma/sms/BearerData.java | 192 ++ .../telephony/cdma/sms/CdmaSmsAddress.java | 98 + .../internal/telephony/cdma/sms/SmsDataCoding.java | 371 +++ .../internal/telephony/cdma/sms/SmsEnvelope.java | 110 + .../internal/telephony/cdma/sms/UserData.java | 63 + .../internal/telephony/cdma/sms/package.html | 6 + .../android/internal/telephony/gsm/AdnRecord.aidl | 19 - .../android/internal/telephony/gsm/AdnRecord.java | 570 ---- .../internal/telephony/gsm/AdnRecordCache.java | 346 --- .../internal/telephony/gsm/BaseCommands.java | 367 --- .../internal/telephony/gsm/CallForwardInfo.java | 44 - .../internal/telephony/gsm/CallTracker.java | 984 ------- .../internal/telephony/gsm/CommandException.java | 85 - .../internal/telephony/gsm/CommandsInterface.java | 926 ------ .../telephony/gsm/DataConnectionTracker.java | 1836 ------------ .../android/internal/telephony/gsm/DataLink.java | 41 - .../internal/telephony/gsm/DataLinkInterface.java | 77 - .../android/internal/telephony/gsm/DriverCall.java | 162 -- .../internal/telephony/gsm/EncodeException.java | 39 - .../android/internal/telephony/gsm/GSMCall.java | 221 -- .../internal/telephony/gsm/GSMConnection.java | 767 ----- .../android/internal/telephony/gsm/GSMPhone.java | 732 +++-- .../internal/telephony/gsm/GsmAlphabet.java | 813 ------ .../android/internal/telephony/gsm/GsmCall.java | 208 ++ .../internal/telephony/gsm/GsmCallTracker.java | 908 ++++++ .../internal/telephony/gsm/GsmConnection.java | 732 +++++ .../telephony/gsm/GsmDataConnectionTracker.java | 1718 +++++++++++ .../android/internal/telephony/gsm/GsmMmiCode.java | 324 +-- .../internal/telephony/gsm/GsmSMSDispatcher.java | 379 +++ .../telephony/gsm/GsmServiceStateTracker.java | 1588 ++++++++++ .../android/internal/telephony/gsm/GsmSimCard.java | 506 ---- .../internal/telephony/gsm/GsmSmsAddress.java | 148 + .../internal/telephony/gsm/ISimPhoneBook.aidl | 101 - .../com/android/internal/telephony/gsm/ISms.aidl | 115 - .../android/internal/telephony/gsm/MccTable.java | 6 +- .../internal/telephony/gsm/NetworkInfo.aidl | 4 +- .../internal/telephony/gsm/NetworkInfo.java | 63 +- .../internal/telephony/gsm/PDPContextState.java | 3 +- .../internal/telephony/gsm/PdpConnection.java | 418 +-- .../android/internal/telephony/gsm/PppLink.java | 50 +- .../com/android/internal/telephony/gsm/RIL.java | 2586 ----------------- .../internal/telephony/gsm/RILConstants.java | 177 -- .../internal/telephony/gsm/SIMFileHandler.java | 500 +--- .../android/internal/telephony/gsm/SIMRecords.java | 399 ++- .../internal/telephony/gsm/SMSDispatcher.java | 979 ------- .../telephony/gsm/ServiceStateTracker.java | 1696 ----------- .../android/internal/telephony/gsm/SimCard.java | 512 ++++ .../internal/telephony/gsm/SimConstants.java | 55 - .../internal/telephony/gsm/SimException.java | 58 - .../internal/telephony/gsm/SimFileNotFound.java | 38 - .../telephony/gsm/SimFileTypeMismatch.java | 33 - .../internal/telephony/gsm/SimIoResult.java | 75 - .../gsm/SimPhoneBookInterfaceManager.java | 233 +- .../internal/telephony/gsm/SimProvider.java | 455 --- .../telephony/gsm/SimSmsInterfaceManager.java | 181 +- .../com/android/internal/telephony/gsm/SimTlv.java | 31 +- .../android/internal/telephony/gsm/SimUtils.java | 476 --- .../android/internal/telephony/gsm/SmsHeader.java | 236 -- .../android/internal/telephony/gsm/SmsMessage.java | 1058 +++++++ .../android/internal/telephony/gsm/SmsRawData.aidl | 19 - .../android/internal/telephony/gsm/SmsRawData.java | 62 - .../internal/telephony/gsm/SmsResponse.java | 34 - .../internal/telephony/gsm/TelephonyEventLog.java | 33 - .../android/internal/telephony/gsm/stk/BerTlv.java | 12 +- .../internal/telephony/gsm/stk/CommandParams.java | 2 +- .../telephony/gsm/stk/CommandParamsFactory.java | 2 +- .../telephony/gsm/stk/ComprehensionTlv.java | 10 +- .../internal/telephony/gsm/stk/IconLoader.java | 28 +- .../telephony/gsm/stk/ImageDescriptor.java | 5 +- .../internal/telephony/gsm/stk/ResponseData.java | 4 +- .../telephony/gsm/stk/RilMessageDecoder.java | 4 +- .../internal/telephony/gsm/stk/StkService.java | 50 +- .../internal/telephony/gsm/stk/ValueParser.java | 8 +- .../internal/telephony/test/SimulatedCommands.java | 380 ++- .../telephony/test/SimulatedGsmCallState.java | 104 +- 159 files changed, 32003 insertions(+), 17885 deletions(-) create mode 100644 telephony/java/com/android/internal/telephony/AdnRecord.aidl create mode 100644 telephony/java/com/android/internal/telephony/AdnRecord.java create mode 100644 telephony/java/com/android/internal/telephony/AdnRecordCache.java create mode 100644 telephony/java/com/android/internal/telephony/AdnRecordLoader.java create mode 100644 telephony/java/com/android/internal/telephony/BaseCommands.java create mode 100644 telephony/java/com/android/internal/telephony/CallForwardInfo.java create mode 100644 telephony/java/com/android/internal/telephony/CallTracker.java create mode 100644 telephony/java/com/android/internal/telephony/CommandException.java create mode 100644 telephony/java/com/android/internal/telephony/CommandsInterface.java create mode 100644 telephony/java/com/android/internal/telephony/DataConnection.java create mode 100644 telephony/java/com/android/internal/telephony/DataConnectionTracker.java create mode 100644 telephony/java/com/android/internal/telephony/DataLink.java create mode 100644 telephony/java/com/android/internal/telephony/DataLinkInterface.java create mode 100644 telephony/java/com/android/internal/telephony/DriverCall.java create mode 100644 telephony/java/com/android/internal/telephony/EncodeException.java create mode 100644 telephony/java/com/android/internal/telephony/GsmAlphabet.java create mode 100644 telephony/java/com/android/internal/telephony/IIccPhoneBook.aidl create mode 100644 telephony/java/com/android/internal/telephony/ISms.aidl create mode 100644 telephony/java/com/android/internal/telephony/IccCard.java create mode 100644 telephony/java/com/android/internal/telephony/IccCardApplication.java create mode 100644 telephony/java/com/android/internal/telephony/IccCardStatus.java create mode 100644 telephony/java/com/android/internal/telephony/IccConstants.java create mode 100644 telephony/java/com/android/internal/telephony/IccException.java create mode 100644 telephony/java/com/android/internal/telephony/IccFileHandler.java create mode 100644 telephony/java/com/android/internal/telephony/IccFileNotFound.java create mode 100644 telephony/java/com/android/internal/telephony/IccFileTypeMismatch.java create mode 100644 telephony/java/com/android/internal/telephony/IccIoResult.java create mode 100644 telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java create mode 100644 telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java create mode 100644 telephony/java/com/android/internal/telephony/IccProvider.java create mode 100644 telephony/java/com/android/internal/telephony/IccRecords.java create mode 100644 telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java create mode 100644 telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java create mode 100644 telephony/java/com/android/internal/telephony/IccUtils.java create mode 100644 telephony/java/com/android/internal/telephony/IccVmFixedException.java create mode 100644 telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java create mode 100644 telephony/java/com/android/internal/telephony/PhoneProxy.java create mode 100644 telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java create mode 100644 telephony/java/com/android/internal/telephony/RIL.java create mode 100644 telephony/java/com/android/internal/telephony/RILConstants.java create mode 100644 telephony/java/com/android/internal/telephony/SMSDispatcher.java create mode 100644 telephony/java/com/android/internal/telephony/ServiceStateTracker.java delete mode 100644 telephony/java/com/android/internal/telephony/SimCard.java create mode 100644 telephony/java/com/android/internal/telephony/SmsAddress.java create mode 100644 telephony/java/com/android/internal/telephony/SmsHeader.java create mode 100644 telephony/java/com/android/internal/telephony/SmsMessageBase.java create mode 100644 telephony/java/com/android/internal/telephony/SmsRawData.aidl create mode 100644 telephony/java/com/android/internal/telephony/SmsRawData.java create mode 100644 telephony/java/com/android/internal/telephony/SmsResponse.java create mode 100644 telephony/java/com/android/internal/telephony/TelephonyEventLog.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CallFailCause.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaCall.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/FeatureCode.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/RuimCard.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/RuimRecords.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/SmsMessage.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/TtyIntent.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/package.html create mode 100644 telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/sms/SmsDataCoding.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/sms/UserData.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/sms/package.html delete mode 100644 telephony/java/com/android/internal/telephony/gsm/AdnRecord.aidl delete mode 100644 telephony/java/com/android/internal/telephony/gsm/AdnRecord.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/AdnRecordCache.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/BaseCommands.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/CallForwardInfo.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/CallTracker.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/CommandException.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/DataLink.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/DataLinkInterface.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/DriverCall.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/EncodeException.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/GSMCall.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/GSMConnection.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmCall.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmConnection.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmSimCard.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/ISimPhoneBook.aidl delete mode 100644 telephony/java/com/android/internal/telephony/gsm/ISms.aidl delete mode 100644 telephony/java/com/android/internal/telephony/gsm/RIL.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/RILConstants.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/SimCard.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SimConstants.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SimException.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SimFileNotFound.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SimFileTypeMismatch.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SimIoResult.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SimProvider.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SimUtils.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SmsHeader.java create mode 100644 telephony/java/com/android/internal/telephony/gsm/SmsMessage.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SmsRawData.aidl delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SmsRawData.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/SmsResponse.java delete mode 100644 telephony/java/com/android/internal/telephony/gsm/TelephonyEventLog.java (limited to 'telephony/java/com') diff --git a/telephony/java/com/android/internal/telephony/ATResponseParser.java b/telephony/java/com/android/internal/telephony/ATResponseParser.java index 93ec455..fdb0526 100644 --- a/telephony/java/com/android/internal/telephony/ATResponseParser.java +++ b/telephony/java/com/android/internal/telephony/ATResponseParser.java @@ -34,7 +34,7 @@ public class ATResponseParser { this.line = line; } - + public boolean nextBoolean() { @@ -147,7 +147,7 @@ public class ATResponseParser } } - + /** Throws ATParseEx if whitespace extends to the end of string */ private char skipWhiteSpace (char c) diff --git a/telephony/java/com/android/internal/telephony/AdnRecord.aidl b/telephony/java/com/android/internal/telephony/AdnRecord.aidl new file mode 100644 index 0000000..b4a1a29 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/AdnRecord.aidl @@ -0,0 +1,20 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.internal.telephony; + +parcelable AdnRecord; + diff --git a/telephony/java/com/android/internal/telephony/AdnRecord.java b/telephony/java/com/android/internal/telephony/AdnRecord.java new file mode 100644 index 0000000..5f40579 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/AdnRecord.java @@ -0,0 +1,283 @@ +/* + * 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.util.Log; + +import com.android.internal.telephony.GsmAlphabet; + + +/** + * + * 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 = ""; + String number = ""; + 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_DAILING_NUMBER_START = 2; + static final int ADN_DAILING_NUMBER_END = 11; + static final int ADN_CAPABILITY_ID = 12; + static final int ADN_EXTENSION_ID = 13; + + //***** Static Methods + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public AdnRecord createFromParcel(Parcel source) { + int efid; + int recordNumber; + String alphaTag; + String number; + + efid = source.readInt(); + recordNumber = source.readInt(); + alphaTag = source.readString(); + number = source.readString(); + + return new AdnRecord(efid, recordNumber, alphaTag, number); + } + + 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 (int efid, int recordNumber, String alphaTag, String number) { + this.efid = efid; + this.recordNumber = recordNumber; + this.alphaTag = alphaTag; + this.number = number; + } + + //***** Instance Methods + + public String getAlphaTag() { + return alphaTag; + } + + public String getNumber() { + return number; + } + + public String toString() { + return "ADN Record '" + alphaTag + "' '" + number + "'"; + } + + public boolean isEmpty() { + return alphaTag.equals("") && number.equals(""); + } + + public boolean hasExtendedRecord() { + return extRecord != 0 && extRecord != 0xff; + } + + public boolean isEqual(AdnRecord adn) { + return ( alphaTag.equals(adn.getAlphaTag()) && + number.equals(adn.getNumber()) ); + } + //***** 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); + } + + /** + * 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 nulll for wrong format of dialing nubmer or tag + */ + public byte[] buildAdnString(int recordSize) { + byte[] bcdNumber; + byte[] byteTag; + byte[] adnString = null; + int footerOffset = recordSize - FOOTER_SIZE_BYTES; + + if (number == null || number.equals("") || + alphaTag == null || alphaTag.equals("")) { + + Log.w(LOG_TAG, "[buildAdnString] Empty alpha tag or number"); + adnString = new byte[recordSize]; + for (int i = 0; i < recordSize; i++) { + adnString[i] = (byte) 0xFF; + } + } else if (number.length() + > (ADN_DAILING_NUMBER_END - ADN_DAILING_NUMBER_START + 1) * 2) { + Log.w(LOG_TAG, + "[buildAdnString] Max length of dailing number is 20"); + } else if (alphaTag.length() > footerOffset) { + Log.w(LOG_TAG, + "[buildAdnString] Max length of tag is " + footerOffset); + } else { + + adnString = new byte[recordSize]; + for (int i = 0; i < recordSize; i++) { + adnString[i] = (byte) 0xFF; + } + + 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; // Capacility Id + adnString[footerOffset + ADN_EXTENSION_ID] + = (byte) 0xFF; // Extension Record Id + + 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]; + + } catch (RuntimeException ex) { + Log.w(LOG_TAG, "Error parsing AdnRecord", ex); + number = ""; + alphaTag = ""; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/AdnRecordCache.java b/telephony/java/com/android/internal/telephony/AdnRecordCache.java new file mode 100644 index 0000000..c270ae5 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/AdnRecordCache.java @@ -0,0 +1,339 @@ +/* + * 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.SparseArray; +import android.util.Log; +import android.os.Message; +import android.os.Handler; +import android.os.AsyncResult; +import java.util.ArrayList; +import java.util.Iterator; +import com.android.internal.telephony.IccConstants; + +/** + * {@hide} + */ +public final class AdnRecordCache extends Handler implements IccConstants { + //***** Instance Variables + + PhoneBase phone; + + // Indexed by EF ID + SparseArray> adnLikeFiles + = new SparseArray>(); + + // People waiting for ADN-like files to be loaded + SparseArray> adnLikeWaiters + = new SparseArray>(); + + // People waiting for adn record to be updated + SparseArray userWriteResponse = new SparseArray(); + + //***** Event Constants + + static final int EVENT_LOAD_ALL_ADN_LIKE_DONE = 1; + static final int EVENT_UPDATE_ADN_DONE = 2; + + //***** Constructor + + + + public AdnRecordCache(PhoneBase phone) { + this.phone = phone; + } + + //***** Called from SIMRecords + + /** + * Called from SIMRecords.onRadioNotAvailable and SIMRecords.handleSimRefresh. + */ + public void reset() { + adnLikeFiles.clear(); + + clearWaiters(); + clearUserWriters(); + + } + + private void clearWaiters() { + int size = adnLikeWaiters.size(); + for (int i = 0; i < size; i++) { + ArrayList 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 + 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 + */ + private 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; + 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(phone).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 oldAdnList; + oldAdnList = getRecordsIfLoaded(efid); + + if (oldAdnList == null) { + sendErrorResponse(response, "Adn list not exist for EF:" + efid); + return; + } + + int index = -1; + int count = 1; + for (Iterator 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; + } + + Message pendingResponse = userWriteResponse.get(efid); + + if (pendingResponse != null) { + sendErrorResponse(response, "Have pending update for EF:" + efid); + return; + } + + userWriteResponse.put(efid, response); + + new AdnRecordLoader(phone).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, Message response) { + ArrayList waiters; + ArrayList result; + + 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(); + waiters.add(response); + + adnLikeWaiters.put(efid, waiters); + + int extensionEF = extensionEfForEf(efid); + + 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(phone).loadAllFromEF(efid, extensionEF, + obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0)); + } + + //***** Private methods + + private void + notifyWaiters(ArrayList 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*/ + ar = (AsyncResult) msg.obj; + efid = msg.arg1; + ArrayList waiters; + + waiters = adnLikeWaiters.get(efid); + adnLikeWaiters.delete(efid); + + if (ar.exception == null) { + adnLikeFiles.put(efid, (ArrayList) (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); + } + + Message response = userWriteResponse.get(efid); + userWriteResponse.delete(efid); + + AsyncResult.forMessage(response, null, ar.exception); + response.sendToTarget(); + break; + } + + } + + +} diff --git a/telephony/java/com/android/internal/telephony/AdnRecordLoader.java b/telephony/java/com/android/internal/telephony/AdnRecordLoader.java new file mode 100644 index 0000000..cfb5aaa --- /dev/null +++ b/telephony/java/com/android/internal/telephony/AdnRecordLoader.java @@ -0,0 +1,285 @@ +/* + * 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.Message; +import android.util.Log; + + +public class AdnRecordLoader extends Handler { + static String LOG_TAG; + + //***** Instance Variables + + PhoneBase phone; + int ef; + int extensionEF; + int pendingExtLoads; + Message userResponse; + String pin2; + + // For "load one" + int recordNumber; + + // for "load all" + ArrayList 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(PhoneBase phone) { + // The telephony unit-test cases may create AdnRecords + // in secondary threads + super(phone.getHandler().getLooper()); + + this.phone = phone; + LOG_TAG = phone.getPhoneName(); + } + + /** + * 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; + + phone.mIccFileHandler.loadEFLinearFixed( + ef, recordNumber, + obtainMessage(EVENT_ADN_LOAD_DONE)); + + } + + + /** + * Resulting ArrayList<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; + + phone.mIccFileHandler.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 phoneNubmer + * @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; + + phone.mIccFileHandler.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("worong ADN format", + ar.exception); + } + + phone.mIccFileHandler.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; + + phone.mIccFileHandler.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 extention 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 datas = (ArrayList)(ar.result); + + if (ar.exception != null) { + throw new RuntimeException("load failed", ar.exception); + } + + adns = new ArrayList(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++; + + phone.mIccFileHandler.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/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java new file mode 100644 index 0000000..11e7461 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -0,0 +1,580 @@ +/* + * 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.content.Context; +import android.os.RegistrantList; +import android.os.Registrant; +import android.os.Handler; +import android.os.AsyncResult; +import android.provider.Checkin; +import android.util.Config; +import android.util.Log; + +/** + * {@hide} + */ +public abstract class BaseCommands implements CommandsInterface { + static final String LOG_TAG = "RILB"; + + //***** Instance Variables + protected Context mContext; + protected RadioState mState = RadioState.RADIO_UNAVAILABLE; + protected Object mStateMonitor = new Object(); + + protected RegistrantList mRadioStateChangedRegistrants = new RegistrantList(); + protected RegistrantList mOnRegistrants = new RegistrantList(); + protected RegistrantList mAvailRegistrants = new RegistrantList(); + protected RegistrantList mOffOrNotAvailRegistrants = new RegistrantList(); + protected RegistrantList mNotAvailRegistrants = new RegistrantList(); + protected RegistrantList mSIMReadyRegistrants = new RegistrantList(); + protected RegistrantList mSIMLockedRegistrants = new RegistrantList(); + protected RegistrantList mRUIMReadyRegistrants = new RegistrantList(); + protected RegistrantList mRUIMLockedRegistrants = new RegistrantList(); + protected RegistrantList mNVReadyRegistrants = new RegistrantList(); + protected RegistrantList mCallStateRegistrants = new RegistrantList(); + protected RegistrantList mNetworkStateRegistrants = new RegistrantList(); + protected RegistrantList mDataConnectionRegistrants = new RegistrantList(); + protected RegistrantList mRadioTechnologyChangedRegistrants = new RegistrantList(); + protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList(); + protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList(); + protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList(); + protected Registrant mSMSRegistrant; + protected Registrant mNITZTimeRegistrant; + protected Registrant mSignalStrengthRegistrant; + protected Registrant mUSSDRegistrant; + protected Registrant mSmsOnSimRegistrant; + /** Registrant for handling SMS Status Reports */ + protected Registrant mSmsStatusRegistrant; + /** Registrant for handling Supplementary Service Notifications */ + protected Registrant mSsnRegistrant; + protected Registrant mStkSessionEndRegistrant; + protected Registrant mStkProCmdRegistrant; + protected Registrant mStkEventRegistrant; + protected Registrant mStkCallSetUpRegistrant; + /** Registrant for handling SIM/RUIM SMS storage full messages */ + protected Registrant mIccSmsFullRegistrant; + /** Registrant for handling Icc Refresh notifications */ + protected Registrant mIccRefreshRegistrant; + /** Registrant for handling RING notifications */ + protected Registrant mRingRegistrant; + /** Registrant for handling RESTRICTED STATE changed notification */ + protected Registrant mRestrictedStateRegistrant; + + //Network Mode received from PhoneFactory + protected int mNetworkMode; + //CDMA subscription received from PhoneFactory + protected int mCdmaSubscription; + //Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone. + protected int mPhoneType; + + + public BaseCommands(Context context) { + mContext = context; // May be null (if so we won't log statistics) + } + + //***** CommandsInterface implementation + + public RadioState getRadioState() { + return mState; + } + + + public void registerForRadioStateChanged(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mRadioStateChangedRegistrants.add(r); + r.notifyRegistrant(); + } + } + + public void unregisterForRadioStateChanged(Handler h) { + synchronized (mStateMonitor) { + mRadioStateChangedRegistrants.remove(h); + } + } + + public void registerForOn(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mOnRegistrants.add(r); + + if (mState.isOn()) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + public void unregisterForOn(Handler h) { + synchronized (mStateMonitor) { + mOnRegistrants.remove(h); + } + } + + + public void registerForAvailable(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mAvailRegistrants.add(r); + + if (mState.isAvailable()) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + + public void unregisterForAvailable(Handler h) { + synchronized(mStateMonitor) { + mAvailRegistrants.remove(h); + } + } + + public void registerForNotAvailable(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mNotAvailRegistrants.add(r); + + if (!mState.isAvailable()) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + + public void unregisterForNotAvailable(Handler h) { + synchronized (mStateMonitor) { + mNotAvailRegistrants.remove(h); + } + } + + public void registerForOffOrNotAvailable(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mOffOrNotAvailRegistrants.add(r); + + if (mState == RadioState.RADIO_OFF || !mState.isAvailable()) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + public void unregisterForOffOrNotAvailable(Handler h) { + synchronized(mStateMonitor) { + mOffOrNotAvailRegistrants.remove(h); + } + } + + + /** Any transition into SIM_READY */ + public void registerForSIMReady(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mSIMReadyRegistrants.add(r); + + if (mState.isSIMReady()) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + + public void unregisterForSIMReady(Handler h) { + synchronized (mStateMonitor) { + mSIMReadyRegistrants.remove(h); + } + } + + /** Any transition into RUIM_READY */ + public void registerForRUIMReady(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mRUIMReadyRegistrants.add(r); + + if (mState.isRUIMReady()) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + + public void unregisterForRUIMReady(Handler h) { + synchronized(mStateMonitor) { + mRUIMReadyRegistrants.remove(h); + } + } + + /** Any transition into NV_READY */ + public void registerForNVReady(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mNVReadyRegistrants.add(r); + + if (mState.isNVReady()) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + + public void unregisterForNVReady(Handler h) { + synchronized (mStateMonitor) { + mNVReadyRegistrants.remove(h); + } + } + + public void registerForSIMLockedOrAbsent(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mSIMLockedRegistrants.add(r); + + if (mState == RadioState.SIM_LOCKED_OR_ABSENT) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + + public void unregisterForSIMLockedOrAbsent(Handler h) { + synchronized (mStateMonitor) { + mSIMLockedRegistrants.remove(h); + } + } + + public void registerForRUIMLockedOrAbsent(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + synchronized (mStateMonitor) { + mRUIMLockedRegistrants.add(r); + + if (mState == RadioState.RUIM_LOCKED_OR_ABSENT) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + } + + public void unregisterForRUIMLockedOrAbsent(Handler h) { + synchronized (mStateMonitor) { + mRUIMLockedRegistrants.remove(h); + } + } + + public void registerForCallStateChanged(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + mCallStateRegistrants.add(r); + } + + public void unregisterForCallStateChanged(Handler h) { + mCallStateRegistrants.remove(h); + } + + public void registerForNetworkStateChanged(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + mNetworkStateRegistrants.add(r); + } + + public void unregisterForNetworkStateChanged(Handler h) { + mNetworkStateRegistrants.remove(h); + } + + public void registerForDataStateChanged(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + mDataConnectionRegistrants.add(r); + } + + public void unregisterForDataStateChanged(Handler h) { + mDataConnectionRegistrants.remove(h); + } + + public void registerForRadioTechnologyChanged(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mRadioTechnologyChangedRegistrants.add(r); + } + + public void unregisterForRadioTechnologyChanged(Handler h) { + mRadioTechnologyChangedRegistrants.remove(h); + } + + public void registerForIccStatusChanged(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mIccStatusChangedRegistrants.add(r); + } + + public void unregisterForIccStatusChanged(Handler h) { + mIccStatusChangedRegistrants.remove(h); + } + + public void setOnNewSMS(Handler h, int what, Object obj) { + mSMSRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnNewSMS(Handler h) { + mSMSRegistrant.clear(); + } + + public void setOnSmsOnSim(Handler h, int what, Object obj) { + mSmsOnSimRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnSmsOnSim(Handler h) { + mSmsOnSimRegistrant.clear(); + } + + public void setOnSmsStatus(Handler h, int what, Object obj) { + mSmsStatusRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnSmsStatus(Handler h) { + mSmsStatusRegistrant.clear(); + } + + public void setOnSignalStrengthUpdate(Handler h, int what, Object obj) { + mSignalStrengthRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnSignalStrengthUpdate(Handler h) { + mSignalStrengthRegistrant.clear(); + } + + public void setOnNITZTime(Handler h, int what, Object obj) { + mNITZTimeRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnNITZTime(Handler h) { + mNITZTimeRegistrant.clear(); + } + + public void setOnUSSD(Handler h, int what, Object obj) { + mUSSDRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnUSSD(Handler h) { + mUSSDRegistrant.clear(); + } + + public void setOnSuppServiceNotification(Handler h, int what, Object obj) { + mSsnRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnSuppServiceNotification(Handler h) { + mSsnRegistrant.clear(); + } + + public void setOnStkSessionEnd(Handler h, int what, Object obj) { + mStkSessionEndRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnStkSessionEnd(Handler h) { + mStkSessionEndRegistrant.clear(); + } + + public void setOnStkProactiveCmd(Handler h, int what, Object obj) { + mStkProCmdRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnStkProactiveCmd(Handler h) { + mStkProCmdRegistrant.clear(); + } + + public void setOnStkEvent(Handler h, int what, Object obj) { + mStkEventRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnStkEvent(Handler h) { + mStkEventRegistrant.clear(); + } + + public void setOnStkCallSetUp(Handler h, int what, Object obj) { + mStkCallSetUpRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnStkCallSetUp(Handler h) { + mStkCallSetUpRegistrant.clear(); + } + + public void setOnIccSmsFull(Handler h, int what, Object obj) { + mIccSmsFullRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnIccSmsFull(Handler h) { + mIccSmsFullRegistrant.clear(); + } + + public void setOnIccRefresh(Handler h, int what, Object obj) { + mIccRefreshRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnIccRefresh(Handler h) { + mIccRefreshRegistrant.clear(); + } + + public void setOnCallRing(Handler h, int what, Object obj) { + mRingRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnCallRing(Handler h) { + mRingRegistrant.clear(); + } + + public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mVoicePrivacyOnRegistrants.add(r); + } + + public void unregisterForInCallVoicePrivacyOn(Handler h){ + mVoicePrivacyOnRegistrants.remove(h); + } + + public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mVoicePrivacyOffRegistrants.add(r); + } + + public void unregisterForInCallVoicePrivacyOff(Handler h){ + mVoicePrivacyOffRegistrants.remove(h); + } + + public void setOnRestrictedStateChanged(Handler h, int what, Object obj) { + mRestrictedStateRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnRestrictedStateChanged(Handler h) { + mRestrictedStateRegistrant.clear(); + } + + //***** Protected Methods + /** + * Store new RadioState and send notification based on the changes + * + * This function is called only by RIL.java when receiving unsolicited + * RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED + * + * RadioState has 5 values : RADIO_OFF, RADIO_UNAVAILABLE, SIM_NOT_READY, + * SIM_LOCKED_OR_ABSENT, and SIM_READY. + * + * @param newState new RadioState decoded from RIL_UNSOL_RADIO_STATE_CHANGED + */ + protected void setRadioState(RadioState newState) { + RadioState oldState; + + synchronized (mStateMonitor) { + if (Config.LOGV) { + Log.v(LOG_TAG, "setRadioState old: " + mState + + " new " + newState); + } + + oldState = mState; + mState = newState; + + if (oldState == mState) { + // no state transition + return; + } + + if (mContext != null && + newState == RadioState.RADIO_UNAVAILABLE && + oldState != RadioState.RADIO_OFF) { + Checkin.updateStats(mContext.getContentResolver(), + Checkin.Stats.Tag.PHONE_RADIO_RESETS, 1, 0.0); + } + + mRadioStateChangedRegistrants.notifyRegistrants(); + + if (mState.isAvailable() && !oldState.isAvailable()) { + Log.d(LOG_TAG,"Notifying: radio available"); + mAvailRegistrants.notifyRegistrants(); + onRadioAvailable(); + } + + if (!mState.isAvailable() && oldState.isAvailable()) { + Log.d(LOG_TAG,"Notifying: radio not available"); + mNotAvailRegistrants.notifyRegistrants(); + } + + if (mState.isSIMReady() && !oldState.isSIMReady()) { + Log.d(LOG_TAG,"Notifying: SIM ready"); + mSIMReadyRegistrants.notifyRegistrants(); + } + + if (mState == RadioState.SIM_LOCKED_OR_ABSENT) { + Log.d(LOG_TAG,"Notifying: SIM locked or absent"); + mSIMLockedRegistrants.notifyRegistrants(); + } + + if (mState.isRUIMReady() && !oldState.isRUIMReady()) { + Log.d(LOG_TAG,"Notifying: RUIM ready"); + mRUIMReadyRegistrants.notifyRegistrants(); + } + + if (mState == RadioState.RUIM_LOCKED_OR_ABSENT) { + Log.d(LOG_TAG,"Notifying: RUIM locked or absent"); + mRUIMLockedRegistrants.notifyRegistrants(); + } + if (mState.isNVReady() && !oldState.isNVReady()) { + Log.d(LOG_TAG,"Notifying: NV ready"); + mNVReadyRegistrants.notifyRegistrants(); + } + + if (mState.isOn() && !oldState.isOn()) { + Log.d(LOG_TAG,"Notifying: Radio On"); + mOnRegistrants.notifyRegistrants(); + } + + if ((!mState.isOn() || !mState.isAvailable()) + && !((!oldState.isOn() || !oldState.isAvailable())) + ) { + Log.d(LOG_TAG,"Notifying: radio off or not available"); + mOffOrNotAvailRegistrants.notifyRegistrants(); + } + + /* Radio Technology Change events + * NOTE: isGsm and isCdma have no common states in RADIO_OFF or RADIO_UNAVAILABLE; the + * current phone is determined by mPhoneType + * NOTE: at startup no phone have been created and the RIL determines the mPhoneType + * looking based on the networkMode set by the PhoneFactory in the constructor + */ + + if (mState.isGsm() && oldState.isCdma()) { + Log.d(LOG_TAG,"Notifying: radio technology change CDMA to GSM"); + mRadioTechnologyChangedRegistrants.notifyRegistrants(); + } + + if (mState.isGsm() && !oldState.isOn() && (mPhoneType == RILConstants.CDMA_PHONE)) { + Log.d(LOG_TAG,"Notifying: radio technology change CDMA OFF to GSM"); + mRadioTechnologyChangedRegistrants.notifyRegistrants(); + } + + if (mState.isCdma() && oldState.isGsm()) { + Log.d(LOG_TAG,"Notifying: radio technology change GSM to CDMA"); + mRadioTechnologyChangedRegistrants.notifyRegistrants(); + } + + if (mState.isCdma() && !oldState.isOn() && (mPhoneType == RILConstants.GSM_PHONE)) { + Log.d(LOG_TAG,"Notifying: radio technology change GSM OFF to CDMA"); + mRadioTechnologyChangedRegistrants.notifyRegistrants(); + } + } + } + + protected void onRadioAvailable() { + } +} diff --git a/telephony/java/com/android/internal/telephony/Call.java b/telephony/java/com/android/internal/telephony/Call.java index 82aeb25..70471b6 100644 --- a/telephony/java/com/android/internal/telephony/Call.java +++ b/telephony/java/com/android/internal/telephony/Call.java @@ -17,6 +17,7 @@ package com.android.internal.telephony; import java.util.List; + /** * {@hide} */ @@ -39,6 +40,13 @@ public abstract class Call { } } + + /* Instance Variables */ + + public State state = State.IDLE; + + + /* Instance Methods */ /** Do not modify the List result!!! This list is not yours to keep @@ -46,36 +54,46 @@ public abstract class Call { */ public abstract List getConnections(); - public abstract State getState(); public abstract Phone getPhone(); + public abstract boolean isMultiparty(); + public abstract void hangup() throws CallStateException; + /** * hasConnection - * + * * @param c a Connection object * @return true if the call contains the connection object passed in */ public boolean hasConnection(Connection c) { return c.getCall() == this; } - + /** * hasConnections * @return true if the call contains one or more connections */ public boolean hasConnections() { List connections = getConnections(); - + if (connections == null) { return false; } - + return connections.size() > 0; } - + + /** + * getState + * @return state of class call + */ + public State getState() { + return state; + } + /** * isIdle - * + * * FIXME rename * @return true if the call contains only disconnected connections (if any) */ @@ -93,27 +111,27 @@ public abstract class Call { long time = Long.MAX_VALUE; Connection c; Connection earliest = null; - + l = getConnections(); - + if (l.size() == 0) { return null; } - + for (int i = 0, s = l.size() ; i < s ; i++) { c = (Connection) l.get(i); long t; - + t = c.getCreateTime(); - + if (t < time) { earliest = c; } } - + return earliest; } - + public long getEarliestCreateTime() { List l; @@ -160,9 +178,6 @@ public abstract class Call { return time; } - public abstract boolean isMultiparty(); - - public abstract void hangup() throws CallStateException; public boolean isDialingOrAlerting() { diff --git a/telephony/java/com/android/internal/telephony/CallForwardInfo.java b/telephony/java/com/android/internal/telephony/CallForwardInfo.java new file mode 100644 index 0000000..8b853b0 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CallForwardInfo.java @@ -0,0 +1,42 @@ +/* + * 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.telephony.PhoneNumberUtils; + +/** + * See also RIL_CallForwardInfo in include/telephony/ril.h + * + * {@hide} + */ +public class CallForwardInfo { + public int status; /*1 = active, 0 = not active */ + public int reason; /* from TS 27.007 7.11 "reason" */ + public int serviceClass; /* Sum of CommandsInterface.SERVICE_CLASS */ + public int toa; /* "type" from TS 27.007 7.11 */ + public String number; /* "number" from TS 27.007 7.11 */ + public int timeSeconds; /* for CF no reply only */ + + public String toString() { + return super.toString() + (status == 0 ? " not active " : " active ") + + " reason: " + reason + + " serviceClass: " + serviceClass + + " \"" + PhoneNumberUtils.stringFromStringAndTOA(number, toa) + "\" " + + timeSeconds + " seconds"; + + } +} diff --git a/telephony/java/com/android/internal/telephony/CallTracker.java b/telephony/java/com/android/internal/telephony/CallTracker.java new file mode 100644 index 0000000..eb339f8 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CallTracker.java @@ -0,0 +1,123 @@ +/* + * 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 com.android.internal.telephony.CommandException; + + +/** + * {@hide} + */ +public abstract class CallTracker extends Handler { + + private static final boolean DBG_POLL = false; + + //***** Constants + + static final int POLL_DELAY_MSEC = 250; + + protected int pendingOperations; + protected boolean needsPoll; + protected Message lastRelevantPoll; + + public CommandsInterface cm; + + + //***** Events + + protected static final int EVENT_POLL_CALLS_RESULT = 1; + protected static final int EVENT_CALL_STATE_CHANGE = 2; + protected static final int EVENT_REPOLL_AFTER_DELAY = 3; + protected static final int EVENT_OPERATION_COMPLETE = 4; + protected static final int EVENT_GET_LAST_CALL_FAIL_CAUSE = 5; + + protected static final int EVENT_SWITCH_RESULT = 8; + protected static final int EVENT_RADIO_AVAILABLE = 9; + protected static final int EVENT_RADIO_NOT_AVAILABLE = 10; + protected static final int EVENT_CONFERENCE_RESULT = 11; + protected static final int EVENT_SEPARATE_RESULT = 12; + protected static final int EVENT_ECT_RESULT = 13; + + + protected void pollCallsWhenSafe() { + needsPoll = true; + + if (checkNoOperationsPending()) { + lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); + cm.getCurrentCalls(lastRelevantPoll); + } + } + + protected void + pollCallsAfterDelay() { + Message msg = obtainMessage(); + + msg.what = EVENT_REPOLL_AFTER_DELAY; + sendMessageDelayed(msg, POLL_DELAY_MSEC); + } + + protected boolean + isCommandExceptionRadioNotAvailable(Throwable e) { + return e != null && e instanceof CommandException + && ((CommandException)e).getCommandError() + == CommandException.Error.RADIO_NOT_AVAILABLE; + } + + protected abstract void handlePollCalls(AsyncResult ar); + + protected void handleRadioAvailable() { + pollCallsWhenSafe(); + } + + /** + * Obtain a complete message that indicates that this operation + * does not require polling of getCurrentCalls(). However, if other + * operations that do need getCurrentCalls() are pending or are + * scheduled while this operation is pending, the invocation + * of getCurrentCalls() will be postponed until this + * operation is also complete. + */ + protected Message + obtainNoPollCompleteMessage(int what) { + pendingOperations++; + lastRelevantPoll = null; + return obtainMessage(what); + } + + /** + * @return true if we're idle or there's a call to getCurrentCalls() pending + * but nothing else + */ + private boolean + checkNoOperationsPending() { + if (DBG_POLL) log("checkNoOperationsPending: pendingOperations=" + + pendingOperations); + return pendingOperations == 0; + } + + + //***** Overridden from Handler + public abstract void handleMessage (Message msg); + + protected abstract void log(String msg); + +} diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index 145e5d8..04da9f7 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -37,7 +37,7 @@ import android.util.Log; public class CallerInfoAsyncQuery { private static final boolean DBG = false; - private static final String LOG_TAG = "CallerInfoAsyncQuery"; + private static final String LOG_TAG = "PHONE"; private static final int EVENT_NEW_QUERY = 1; private static final int EVENT_ADD_LISTENER = 2; diff --git a/telephony/java/com/android/internal/telephony/CommandException.java b/telephony/java/com/android/internal/telephony/CommandException.java new file mode 100644 index 0000000..a5d11cf --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CommandException.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import com.android.internal.telephony.RILConstants; + +import android.util.Log; + +/** + * {@hide} + */ +public class CommandException extends RuntimeException { + private Error e; + + public enum Error { + INVALID_RESPONSE, + RADIO_NOT_AVAILABLE, + GENERIC_FAILURE, + PASSWORD_INCORRECT, + SIM_PIN2, + SIM_PUK2, + REQUEST_NOT_SUPPORTED, + OP_NOT_ALLOWED_DURING_VOICE_CALL, + OP_NOT_ALLOWED_BEFORE_REG_NW, + SMS_FAIL_RETRY, + } + + public CommandException(Error e) { + super(e.toString()); + this.e = e; + } + + public static CommandException + fromRilErrno(int ril_errno) { + switch(ril_errno) { + case RILConstants.SUCCESS: return null; + case RILConstants.RIL_ERRNO_INVALID_RESPONSE: + return new CommandException(Error.INVALID_RESPONSE); + case RILConstants.RADIO_NOT_AVAILABLE: + return new CommandException(Error.RADIO_NOT_AVAILABLE); + case RILConstants.GENERIC_FAILURE: + return new CommandException(Error.GENERIC_FAILURE); + case RILConstants.PASSWORD_INCORRECT: + return new CommandException(Error.PASSWORD_INCORRECT); + case RILConstants.SIM_PIN2: + return new CommandException(Error.SIM_PIN2); + case RILConstants.SIM_PUK2: + return new CommandException(Error.SIM_PUK2); + case RILConstants.REQUEST_NOT_SUPPORTED: + return new CommandException(Error.REQUEST_NOT_SUPPORTED); + case RILConstants.OP_NOT_ALLOWED_DURING_VOICE_CALL: + return new CommandException(Error.OP_NOT_ALLOWED_DURING_VOICE_CALL); + case RILConstants.OP_NOT_ALLOWED_BEFORE_REG_NW: + return new CommandException(Error.OP_NOT_ALLOWED_BEFORE_REG_NW); + case RILConstants.SMS_SEND_FAIL_RETRY: + return new CommandException(Error.SMS_FAIL_RETRY); + default: + Log.e("GSM", "Unrecognized RIL errno " + ril_errno); + return new CommandException(Error.INVALID_RESPONSE); + } + } + + public Error getCommandError() { + return e; + } + + + +} diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java new file mode 100644 index 0000000..aec7238 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -0,0 +1,1193 @@ +/* + * 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.Message; +import android.os.Handler; + + +/** + * {@hide} + */ +public interface CommandsInterface { + enum RadioState { + RADIO_OFF, /* Radio explictly powered off (eg CFUN=0) */ + RADIO_UNAVAILABLE, /* Radio unavailable (eg, resetting or not booted) */ + SIM_NOT_READY, /* Radio is on, but the SIM interface is not ready */ + SIM_LOCKED_OR_ABSENT, /* SIM PIN locked, PUK required, network + personalization, or SIM absent */ + SIM_READY, /* Radio is on and SIM interface is available */ + RUIM_NOT_READY, /* Radio is on, but the RUIM interface is not ready */ + RUIM_READY, /* Radio is on and the RUIM interface is available */ + RUIM_LOCKED_OR_ABSENT, /* RUIM PIN locked, PUK required, network + personalization locked, or RUIM absent */ + NV_NOT_READY, /* Radio is on, but the NV interface is not available */ + NV_READY; /* Radio is on and the NV interface is available */ + + public boolean isOn() /* and available...*/ { + return this == SIM_NOT_READY + || this == SIM_LOCKED_OR_ABSENT + || this == SIM_READY + || this == RUIM_NOT_READY + || this == RUIM_READY + || this == RUIM_LOCKED_OR_ABSENT + || this == NV_NOT_READY + || this == NV_READY; + } + + public boolean isAvailable() { + return this != RADIO_UNAVAILABLE; + } + + public boolean isSIMReady() { + return this == SIM_READY; + } + + public boolean isRUIMReady() { + return this == RUIM_READY; + } + + public boolean isNVReady() { + return this == NV_READY; + } + + public boolean isGsm() { + return this == SIM_NOT_READY + || this == SIM_LOCKED_OR_ABSENT + || this == SIM_READY; + } + + public boolean isCdma() { + return this == RUIM_NOT_READY + || this == RUIM_READY + || this == RUIM_LOCKED_OR_ABSENT + || this == NV_NOT_READY + || this == NV_READY; + } + } + + enum IccStatus { + ICC_ABSENT, + ICC_NOT_READY, + ICC_READY, + ICC_PIN, + ICC_PUK, + ICC_NETWORK_PERSONALIZATION + } + + //***** Constants + + // Used as parameter to dial() and setCLIR() below + static final int CLIR_DEFAULT = 0; // "use subscription default value" + static final int CLIR_INVOCATION = 1; // (restrict CLI presentation) + static final int CLIR_SUPPRESSION = 2; // (allow CLI presentation) + + + // Used as parameters for call forward methods below + static final int CF_ACTION_DISABLE = 0; + static final int CF_ACTION_ENABLE = 1; +// static final int CF_ACTION_UNUSED = 2; + static final int CF_ACTION_REGISTRATION = 3; + static final int CF_ACTION_ERASURE = 4; + + static final int CF_REASON_UNCONDITIONAL = 0; + static final int CF_REASON_BUSY = 1; + static final int CF_REASON_NO_REPLY = 2; + static final int CF_REASON_NOT_REACHABLE = 3; + static final int CF_REASON_ALL = 4; + static final int CF_REASON_ALL_CONDITIONAL = 5; + + // Used for call barring methods below + static final String CB_FACILITY_BAOC = "AO"; + static final String CB_FACILITY_BAOIC = "OI"; + static final String CB_FACILITY_BAOICxH = "OX"; + static final String CB_FACILITY_BAIC = "AI"; + static final String CB_FACILITY_BAICr = "IR"; + static final String CB_FACILITY_BA_ALL = "AB"; + static final String CB_FACILITY_BA_MO = "AG"; + static final String CB_FACILITY_BA_MT = "AC"; + static final String CB_FACILITY_BA_SIM = "SC"; + static final String CB_FACILITY_BA_FD = "FD"; + + + // Used for various supp services apis + // See 27.007 +CCFC or +CLCK + static final int SERVICE_CLASS_NONE = 0; // no user input + static final int SERVICE_CLASS_VOICE = (1 << 0); + static final int SERVICE_CLASS_DATA = (1 << 1); //synoym for 16+32+64+128 + static final int SERVICE_CLASS_FAX = (1 << 2); + static final int SERVICE_CLASS_SMS = (1 << 3); + static final int SERVICE_CLASS_DATA_SYNC = (1 << 4); + static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5); + static final int SERVICE_CLASS_PACKET = (1 << 6); + static final int SERVICE_CLASS_PAD = (1 << 7); + static final int SERVICE_CLASS_MAX = (1 << 7); // Max SERVICE_CLASS value + + // Numeric representation of string values returned + // by messages sent to setOnUSSD handler + static final int USSD_MODE_NOTIFY = 0; + static final int USSD_MODE_REQUEST = 1; + + // SIM Refresh results, passed up from RIL. + static final int SIM_REFRESH_FILE_UPDATED = 0; // Single file updated + static final int SIM_REFRESH_INIT = 1; // SIM initialized; reload all + static final int SIM_REFRESH_RESET = 2; // SIM reset; may be locked + + //***** Methods + + RadioState getRadioState(); + + /** + * Fires on any RadioState transition + * Always fires immediately as well + * + * do not attempt to calculate transitions by storing getRadioState() values + * on previous invocations of this notification. Instead, use the other + * registration methods + */ + void registerForRadioStateChanged(Handler h, int what, Object obj); + void unregisterForRadioStateChanged(Handler h); + + /** + * Fires on any transition into RadioState.isOn() + * Fires immediately if currently in that state + * In general, actions should be idempotent. State may change + * before event is received. + */ + void registerForOn(Handler h, int what, Object obj); + void unregisterForOn(Handler h); + + /** + * Fires on any transition out of RadioState.isAvailable() + * Fires immediately if currently in that state + * In general, actions should be idempotent. State may change + * before event is received. + */ + void registerForAvailable(Handler h, int what, Object obj); + void unregisterForAvailable(Handler h); + + /** + * Fires on any transition into !RadioState.isAvailable() + * Fires immediately if currently in that state + * In general, actions should be idempotent. State may change + * before event is received. + */ + void registerForNotAvailable(Handler h, int what, Object obj); + void unregisterForNotAvailable(Handler h); + + /** + * Fires on any transition into RADIO_OFF or !RadioState.isAvailable() + * Fires immediately if currently in that state + * In general, actions should be idempotent. State may change + * before event is received. + */ + void registerForOffOrNotAvailable(Handler h, int what, Object obj); + void unregisterForOffOrNotAvailable(Handler h); + + /** + * Fires on any transition into SIM_READY + * Fires immediately if if currently in that state + * In general, actions should be idempotent. State may change + * before event is received. + */ + void registerForSIMReady(Handler h, int what, Object obj); + void unregisterForSIMReady(Handler h); + + /** Any transition into SIM_LOCKED_OR_ABSENT */ + void registerForSIMLockedOrAbsent(Handler h, int what, Object obj); + void unregisterForSIMLockedOrAbsent(Handler h); + + void registerForCallStateChanged(Handler h, int what, Object obj); + void unregisterForCallStateChanged(Handler h); + void registerForNetworkStateChanged(Handler h, int what, Object obj); + void unregisterForNetworkStateChanged(Handler h); + void registerForDataStateChanged(Handler h, int what, Object obj); + void unregisterForDataStateChanged(Handler h); + + void registerForRadioTechnologyChanged(Handler h, int what, Object obj); + void unregisterForRadioTechnologyChanged(Handler h); + void registerForNVReady(Handler h, int what, Object obj); + void unregisterForNVReady(Handler h); + void registerForRUIMLockedOrAbsent(Handler h, int what, Object obj); + void unregisterForRUIMLockedOrAbsent(Handler h); + + /** InCall voice privacy notifications */ + void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj); + void unregisterForInCallVoicePrivacyOn(Handler h); + void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj); + void unregisterForInCallVoicePrivacyOff(Handler h); + + /** + * Fires on any transition into RUIM_READY + * Fires immediately if if currently in that state + * In general, actions should be idempotent. State may change + * before event is received. + */ + void registerForRUIMReady(Handler h, int what, Object obj); + void unregisterForRUIMReady(Handler h); + + /** + * unlike the register* methods, there's only one new SMS handler + * if you need to unregister, you should also tell the radio to stop + * sending SMS's to you (via AT+CNMI) + * + * AsyncResult.result is a String containing the SMS PDU + */ + void setOnNewSMS(Handler h, int what, Object obj); + void unSetOnNewSMS(Handler h); + + /** + * Register for NEW_SMS_ON_SIM unsolicited message + * + * AsyncResult.result is an int array containing the index of new SMS + */ + void setOnSmsOnSim(Handler h, int what, Object obj); + void unSetOnSmsOnSim(Handler h); + + /** + * Register for NEW_SMS_STATUS_REPORT unsolicited message + * + * AsyncResult.result is a String containing the status report PDU + */ + void setOnSmsStatus(Handler h, int what, Object obj); + void unSetOnSmsStatus(Handler h); + + /** + * unlike the register* methods, there's only one NITZ time handler + * + * AsyncResult.result is an Object[] + * ((Object[])AsyncResult.result)[0] is a String containing the NITZ time string + * ((Object[])AsyncResult.result)[1] is a Long containing the milliseconds since boot as + * returned by elapsedRealtime() when this NITZ time + * was posted. + * + * Please note that the delivery of this message may be delayed several + * seconds on system startup + */ + void setOnNITZTime(Handler h, int what, Object obj); + void unSetOnNITZTime(Handler h); + + /** + * unlike the register* methods, there's only one USSD notify handler + * + * Represents the arrival of a USSD "notify" message, which may + * or may not have been triggered by a previous USSD send + * + * AsyncResult.result is a String[] + * ((String[])(AsyncResult.result))[0] contains status code + * "0" USSD-Notify -- text in ((const char **)data)[1] + * "1" USSD-Request -- text in ((const char **)data)[1] + * "2" Session terminated by network + * "3" other local client (eg, SIM Toolkit) has responded + * "4" Operation not supported + * "5" Network timeout + * + * ((String[])(AsyncResult.result))[1] contains the USSD message + * The numeric representations of these are in USSD_MODE_* + */ + + void setOnUSSD(Handler h, int what, Object obj); + void unSetOnUSSD(Handler h); + + /** + * unlike the register* methods, there's only one signal strength handler + * AsyncResult.result is an int[2] + * response.obj.result[0] is received signal strength (0-31, 99) + * response.obj.result[1] is bit error rate (0-7, 99) + * as defined in TS 27.007 8.5 + */ + + void setOnSignalStrengthUpdate(Handler h, int what, Object obj); + void unSetOnSignalStrengthUpdate(Handler h); + + /** + * Sets the handler for SIM/RUIM SMS storage full unsolicited message. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnIccSmsFull(Handler h, int what, Object obj); + void unSetOnIccSmsFull(Handler h); + + /** + * Sets the handler for SIM Refresh notifications. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnIccRefresh(Handler h, int what, Object obj); + void unSetOnIccRefresh(Handler h); + + /** + * Sets the handler for RING notifications. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnCallRing(Handler h, int what, Object obj); + void unSetOnCallRing(Handler h); + + /** + * Sets the handler for RESTRICTED_STATE changed notification, + * eg, for Domain Specific Access Control + * unlike the register* methods, there's only one signal strength handler + * + * AsyncResult.result is an int[1] + * response.obj.result[0] is a bitmask of RIL_RESTRICTED_STATE_* values + */ + + void setOnRestrictedStateChanged(Handler h, int what, Object obj); + void unSetOnRestrictedStateChanged(Handler h); + + /** + * Sets the handler for Supplementary Service Notifications. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnSuppServiceNotification(Handler h, int what, Object obj); + void unSetOnSuppServiceNotification(Handler h); + + /** + * Sets the handler for Session End Notifications for STK. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnStkSessionEnd(Handler h, int what, Object obj); + void unSetOnStkSessionEnd(Handler h); + + /** + * Sets the handler for Proactive Commands for STK. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnStkProactiveCmd(Handler h, int what, Object obj); + void unSetOnStkProactiveCmd(Handler h); + + /** + * Sets the handler for Event Notifications for STK. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnStkEvent(Handler h, int what, Object obj); + void unSetOnStkEvent(Handler h); + + /** + * Sets the handler for Call Set Up Notifications for STK. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void setOnStkCallSetUp(Handler h, int what, Object obj); + void unSetOnStkCallSetUp(Handler h); + + /** + * Enables/disbables supplementary service related notifications from + * the network. + * + * @param enable true to enable notifications, false to disable. + * @param result Message to be posted when command completes. + */ + void setSuppServiceNotifications(boolean enable, Message result); + //void unSetSuppServiceNotifications(Handler h); + + + /** + * Returns current ICC status. + * + * AsyncResult.result is IccStatus + * + */ + + void getIccStatus(Message result); + + /** + * Supply the ICC PIN to the ICC card + * + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * This exception is CommandException with an error of PASSWORD_INCORRECT + * if the password is incorrect + * + * ar.exception and ar.result are null on success + */ + + void supplyIccPin(String pin, Message result); + + /** + * Supply the ICC PUK to the ICC card + * + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * This exception is CommandException with an error of PASSWORD_INCORRECT + * if the password is incorrect + * + * ar.exception and ar.result are null on success + */ + + void supplyIccPuk(String puk, String newPin, Message result); + + /** + * Supply the ICC PIN2 to the ICC card + * Only called following operation where ICC_PIN2 was + * returned as a a failure from a previous operation + * + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * This exception is CommandException with an error of PASSWORD_INCORRECT + * if the password is incorrect + * + * ar.exception and ar.result are null on success + */ + + void supplyIccPin2(String pin2, Message result); + + /** + * Supply the SIM PUK2 to the SIM card + * Only called following operation where SIM_PUK2 was + * returned as a a failure from a previous operation + * + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * This exception is CommandException with an error of PASSWORD_INCORRECT + * if the password is incorrect + * + * ar.exception and ar.result are null on success + */ + + void supplyIccPuk2(String puk2, String newPin2, Message result); + + void changeIccPin(String oldPin, String newPin, Message result); + void changeIccPin2(String oldPin2, String newPin2, Message result); + + void changeBarringPassword(String facility, String oldPwd, String newPwd, Message result); + + void supplyNetworkDepersonalization(String netpin, Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result contains a List of DriverCall + * The ar.result List is sorted by DriverCall.index + */ + void getCurrentCalls (Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result contains a List of PDPContextState + * @deprecated + */ + void getPDPContextList(Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result contains a List of PDPContextState + */ + void getDataCallList(Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + * + * CLIR_DEFAULT == on "use subscription default value" + * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation) + * CLIR_INVOCATION == on "CLIR invocation" (restrict CLI presentation) + */ + void dial (String address, int clirMode, Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is String containing IMSI on success + */ + void getIMSI(Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is String containing IMEI on success + */ + void getIMEI(Message result); + + /** + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is String containing IMEISV on success + */ + void getIMEISV(Message result); + + /** + * Hang up one individual connection. + * returned message + * retMsg.obj = AsyncResult ar + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + * + * 3GPP 22.030 6.5.5 + * "Releases a specific active call X" + */ + void hangupConnection (int gsmIndex, Message result); + + /** + * 3GPP 22.030 6.5.5 + * "Releases all held calls or sets User Determined User Busy (UDUB) + * for a waiting call." + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void hangupWaitingOrBackground (Message result); + + /** + * 3GPP 22.030 6.5.5 + * "Releases all active calls (if any exist) and accepts + * the other (held or waiting) call." + * + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void hangupForegroundResumeBackground (Message result); + + /** + * 3GPP 22.030 6.5.5 + * "Places all active calls (if any exist) on hold and accepts + * the other (held or waiting) call." + * + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void switchWaitingOrHoldingAndActive (Message result); + + /** + * 3GPP 22.030 6.5.5 + * "Adds a held call to the conversation" + * + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void conference (Message result); + + /** + * Set preferred Voice Privacy (VP). + * + * @param enable true is enhanced and false is normal VP + * @param result is a callback message + */ + void setPreferredVoicePrivacy(boolean enable, Message result); + + /** + * Get currently set preferred Voice Privacy (VP) mode. + * + * @param result is a callback message + */ + void getPreferredVoicePrivacy(Message result); + + /** + * 3GPP 22.030 6.5.5 + * "Places all active calls on hold except call X with which + * communication shall be supported." + */ + void separateConnection (int gsmIndex, Message result); + + /** + * + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void acceptCall (Message result); + + /** + * also known as UDUB + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void rejectCall (Message result); + + /** + * 3GPP 22.030 6.5.5 + * "Connects the two calls and disconnects the subscriber from both calls" + * + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void explicitCallTransfer (Message result); + + /** + * cause code returned as int[0] in Message.obj.response + * Returns integer cause code defined in TS 24.008 + * Annex H or closest approximation. + * Most significant codes: + * - Any defined in 22.001 F.4 (for generating busy/congestion) + * - Cause 68: ACM >= ACMMax + */ + void getLastCallFailCause (Message result); + + + /** + * Reason for last PDP context deactivate or failure to activate + * cause code returned as int[0] in Message.obj.response + * returns an integer cause code defined in TS 24.008 + * section 6.1.3.1.3 or close approximation + * @deprecated + */ + void getLastPdpFailCause (Message result); + + /** + * The preferred new alternative to getLastPdpFailCause + * that is also CDMA-compatible. + */ + void getLastDataCallFailCause (Message result); + + void setMute (boolean enableMute, Message response); + + void getMute (Message response); + + /** + * response.obj is an AsyncResult + * response.obj.result is an int[2] + * response.obj.result[0] is received signal strength (0-31, 99) + * response.obj.result[1] is bit error rate (0-7, 99) + * as defined in TS 27.007 8.5 + */ + void getSignalStrength (Message response); + + + /** + * response.obj.result is an int[3] + * response.obj.result[0] is registration state 0-5 from TS 27.007 7.2 + * response.obj.result[1] is LAC if registered or -1 if not + * response.obj.result[2] is CID if registered or -1 if not + * valid LAC and CIDs are 0x0000 - 0xffff + * + * Please note that registration state 4 ("unknown") is treated + * as "out of service" above + */ + void getRegistrationState (Message response); + + /** + * response.obj.result is an int[3] + * response.obj.result[0] is registration state 0-5 from TS 27.007 7.2 + * response.obj.result[1] is LAC if registered or -1 if not + * response.obj.result[2] is CID if registered or -1 if not + * valid LAC and CIDs are 0x0000 - 0xffff + * + * Please note that registration state 4 ("unknown") is treated + * as "out of service" above + */ + void getGPRSRegistrationState (Message response); + + /** + * response.obj.result is a String[3] + * response.obj.result[0] is long alpha or null if unregistered + * response.obj.result[1] is short alpha or null if unregistered + * response.obj.result[2] is numeric or null if unregistered + */ + void getOperator(Message response); + + /** + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void sendDtmf(char c, Message result); + + + /** + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void startDtmf(char c, Message result); + + /** + * ar.exception carries exception on failure + * ar.userObject contains the orignal value of result.obj + * ar.result is null on success and failure + */ + void stopDtmf(Message result); + + + /** + * smscPDU is smsc address in PDU form GSM BCD format prefixed + * by a length byte (as expected by TS 27.005) or NULL for default SMSC + * pdu is SMS in PDU format as an ASCII hex string + * less the SMSC address + */ + void sendSMS (String smscPDU, String pdu, Message response); + + /** + * @param pdu is CDMA-SMS in internal pseudo-PDU format + * @param response sent when operation completes + */ + void sendCdmaSms(byte[] pdu, Message response); + + /** + * Deletes the specified SMS record from SIM memory (EF_SMS). + * + * @param index index of the SMS record to delete + * @param response sent when operation completes + */ + void deleteSmsOnSim(int index, Message response); + + /** + * Deletes the specified SMS record from RUIM memory (EF_SMS in DF_CDMA). + * + * @param index index of the SMS record to delete + * @param response sent when operation completes + */ + void deleteSmsOnRuim(int index, Message response); + + /** + * Writes an SMS message to SIM memory (EF_SMS). + * + * @param status status of message on SIM. One of: + * SmsManger.STATUS_ON_ICC_READ + * SmsManger.STATUS_ON_ICC_UNREAD + * SmsManger.STATUS_ON_ICC_SENT + * SmsManger.STATUS_ON_ICC_UNSENT + * @param pdu message PDU, as hex string + * @param response sent when operation completes. + * response.obj will be an AsyncResult, and will indicate + * any error that may have occurred (eg, out of memory). + */ + void writeSmsToSim(int status, String smsc, String pdu, Message response); + + void writeSmsToRuim(int status, String pdu, Message response); + + /** + * @deprecated + * @param apn + * @param user + * @param password + * @param response + */ + void setupDefaultPDP(String apn, String user, String password, Message response); + + /** + * @deprecated + * @param cid + * @param response + */ + void deactivateDefaultPDP(int cid, Message response); + + void setRadioPower(boolean on, Message response); + + void acknowledgeLastIncomingSMS(boolean success, Message response); + + void acknowledgeLastIncomingCdmaSms(boolean success, Message response); + + /** + * parameters equivilient to 27.007 AT+CRSM command + * response.obj will be an AsyncResult + * response.obj.userObj will be a IccIoResult on success + */ + void iccIO (int command, int fileid, String path, int p1, int p2, int p3, + String data, String pin2, Message response); + + /** + * (AsyncResult)response.obj).result is an int[] with element [0] set to + * 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned". + * + * @param response is callback message + */ + + void queryCLIP(Message response); + + /** + * response.obj will be a an int[2] + * + * response.obj[0] will be TS 27.007 +CLIR parameter 'n' + * 0 presentation indicator is used according to the subscription of the CLIR service + * 1 CLIR invocation + * 2 CLIR suppression + * + * response.obj[1] will be TS 27.007 +CLIR parameter 'm' + * 0 CLIR not provisioned + * 1 CLIR provisioned in permanent mode + * 2 unknown (e.g. no network, etc.) + * 3 CLIR temporary mode presentation restricted + * 4 CLIR temporary mode presentation allowed + */ + + void getCLIR(Message response); + + /** + * clirMode is one of the CLIR_* constants above + * + * response.obj is null + */ + + void setCLIR(int clirMode, Message response); + + /** + * (AsyncResult)response.obj).result is an int[] with element [0] set to + * 0 for disabled, 1 for enabled. + * + * @param serviceClass is a sum of SERVICE_CLASS_* + * @param response is callback message + */ + + void queryCallWaiting(int serviceClass, Message response); + + /** + * @param enable is true to enable, false to disable + * @param serviceClass is a sum of SERVICE_CLASS_* + * @param response is callback message + */ + + void setCallWaiting(boolean enable, int serviceClass, Message response); + + /** + * @param action is one of CF_ACTION_* + * @param cfReason is one of CF_REASON_* + * @param serviceClass is a sum of SERVICE_CLASSS_* + */ + void setCallForward(int action, int cfReason, int serviceClass, + String number, int timeSeconds, Message response); + + /** + * cfReason is one of CF_REASON_* + * + * ((AsyncResult)response.obj).result will be an array of + * CallForwardInfo's + * + * An array of length 0 means "disabled for all codes" + */ + void queryCallForwardStatus(int cfReason, int serviceClass, + String number, Message response); + + void setNetworkSelectionModeAutomatic(Message response); + + void setNetworkSelectionModeManual(String operatorNumeric, Message response); + + /** + * Queries whether the current network selection mode is automatic + * or manual + * + * ((AsyncResult)response.obj).result is an int[] with element [0] being + * a 0 for automatic selection and a 1 for manual selection + */ + + void getNetworkSelectionMode(Message response); + + /** + * Queries the currently available networks + * + * ((AsyncResult)response.obj).result is a List of NetworkInfo objects + */ + void getAvailableNetworks(Message response); + + void getBasebandVersion (Message response); + + + /** + * (AsyncResult)response.obj).result will be an Integer representing + * the sum of enabled serivice classes (sum of SERVICE_CLASS_*) + * + * @param facility one of CB_FACILTY_* + * @param password password or "" if not required + * @param serviceClass is a sum of SERVICE_CLASS_* + * @param response is callback message + */ + + void queryFacilityLock (String facility, String password, int serviceClass, + Message response); + + /** + * @param facility one of CB_FACILTY_* + * @param lockState true means lock, false means unlock + * @param password password or "" if not required + * @param serviceClass is a sum of SERVICE_CLASS_* + * @param response is callback message + */ + void setFacilityLock (String facility, boolean lockState, String password, + int serviceClass, Message response); + + + void sendUSSD (String ussdString, Message response); + + /** + * Cancels a pending USSD session if one exists. + * @param response callback message + */ + void cancelPendingUssd (Message response); + + void resetRadio(Message result); + + /** + * Assign a specified band for RF configuration. + * + * @param bandMode one of BM_*_BAND + * @param response is callback message + */ + void setBandMode (int bandMode, Message response); + + /** + * Query the list of band mode supported by RF. + * + * @param response is callback message + * ((AsyncResult)response.obj).result is an int[] with every + * element representing one avialable BM_*_BAND + */ + void queryAvailableBandMode (Message response); + + /** + * Requests to set the preferred network type for searching and registering + * (CS/PS domain, RAT, and operation mode) + * @param networkType one of NT_*_TYPE + * @param response is callback message + */ + void setPreferredNetworkType(int networkType , Message response); + + /** + * Query the preferred network type setting + * + * @param response is callback message to report one of NT_*_TYPE + */ + void getPreferredNetworkType(Message response); + + /** + * Query neighboring cell ids + * + * @param response s callback message to cell ids + */ + void getNeighboringCids(Message response); + + /** + * Request to enable/disable network state change notifications when + * location informateion (lac and/or cid) has changed. + * + * @param enable true to enable, false to disable + * @param response callback message + */ + void setLocationUpdates(boolean enable, Message response); + + + void invokeOemRilRequestRaw(byte[] data, Message response); + + void invokeOemRilRequestStrings(String[] strings, Message response); + + + /** + * Send TERMINAL RESPONSE to the SIM, after processing a proactive command + * sent by the SIM. + * + * @param contents String containing SAT/USAT response in hexadecimal + * format starting with first byte of response data. See + * TS 102 223 for details. + * @param response Callback message + */ + public void sendTerminalResponse(String contents, Message response); + + /** + * Send ENVELOPE to the SIM, after processing a proactive command sent by + * the SIM. + * + * @param contents String containing SAT/USAT response in hexadecimal + * format starting with command tag. See TS 102 223 for + * details. + * @param response Callback message + */ + public void sendEnvelope(String contents, Message response); + + /** + * Accept or reject the call setup request from SIM. + * + * @param accept true if the call is to be accepted, false otherwise. + * @param response Callback message + */ + public void handleCallSetupRequestFromSim(boolean accept, Message response); + + //***** new Methods for CDMA support + + /** + * Request the device ESN / MEID / IMEI / IMEISV. + * "response" is const char ** + * [0] is IMEI if GSM subscription is available + * [1] is IMEISV if GSM subscription is available + * [2] is ESN if CDMA subscription is available + * [3] is MEID if CDMA subscription is available + */ + public void getDeviceIdentity(Message response); + + /** + * Request the device IMSI_M / MDN / AH_SID / H_SID / H_NID. + * "response" is const char ** + * [0] is IMSI_M if CDMA subscription is available + * [1] is MDN if CDMA subscription is available + * [2] is AH_SID (Analog Home SID) if CDMA subscription + * [3] is H_SID (Home SID) if CDMA subscription is available + * [4] is H_NID (Home SID) if CDMA subscription is available + */ + public void getCDMASubscription(Message response); + + /** + * Send Flash Code. + * "response" is is NULL + * [0] is a FLASH string + */ + public void sendCDMAFeatureCode(String FeatureCode, Message response); + + /** Set the Phone type created */ + void setPhoneType(int phoneType); + /** + * Query the CDMA roaming preference setting + * + * @param response is callback message to report one of CDMA_RM_* + */ + void queryCdmaRoamingPreference(Message response); + + /** + * Requests to set the CDMA roaming preference + * @param cdmaRoamingType one of CDMA_RM_* + * @param response is callback message + */ + void setCdmaRoamingPreference(int cdmaRoamingType, Message response); + + /** + * Requests to set the CDMA subscription mode + * @param cdmaSubscriptionType one of CDMA_SUBSCRIPTION_* + * @param response is callback message + */ + void setCdmaSubscription(int cdmaSubscriptionType, Message response); + + /** + * Set the TTY mode for the CDMA phone + * + * @param enable is true to enable, false to disable + * @param response is callback message + */ + void setTTYModeEnabled(boolean enable, Message response); + + /** + * Query the TTY mode for the CDMA phone + * (AsyncResult)response.obj).result is an int[] with element [0] set to + * 0 for disabled, 1 for enabled. + * + * @param response is callback message + */ + void queryTTYModeEnabled(Message response); + + /** + * Setup a packet data connection On successful completion, the result + * message will return the following: [0] indicating PDP CID, which is + * generated by RIL. This Connection ID is used in both GSM/UMTS and CDMA + * modes [1] indicating the network interface name for GSM/UMTS or CDMA [2] + * indicating the IP address for this interface for GSM/UMTS and NULL in the + * case of CDMA + * + * @param radioTechnology + * indicates whether to setup connection on radio technology CDMA + * (0) or GSM/UMTS (1) + * @param profile + * Profile Number or NULL to indicate default profile + * @param apn + * the APN to connect to if radio technology is GSM/UMTS. + * Otherwise null for CDMA. + * @param user + * the username for APN, or NULL + * @param password + * the password for APN, or NULL + * @param result + * Callback message + */ + public void setupDataCall(String radioTechnology, String profile, String apn, + String user, String password, Message result); + + /** + * Deactivate packet data connection + * + * @param cid + * The connection ID + * @param result + * Callback message is empty on completion + */ + public void deactivateDataCall(int cid, Message result); + + /** + * Activate or deactivate cell broadcast SMS. + * + * @param activate + * 0 = activate, 1 = deactivate + * @param result + * Callback message is empty on completion + */ + public void activateCdmaBroadcastSms(int activate, Message result); + + /** + * Configure cdma cell broadcast SMS. + * + * @param result + * Callback message is empty on completion + */ + public void setCdmaBroadcastConfig(int[] configValuesArray, Message result); + + /** + * Query the current configuration of cdma cell broadcast SMS. + * + * @param result + * Callback message contains the configuration from the modem on completion + */ + public void getCdmaBroadcastConfig(Message result); +} diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index ead49bf..86ceb89 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -19,8 +19,8 @@ package com.android.internal.telephony; /** * {@hide} */ -public abstract class Connection -{ +public abstract class Connection { + // Number presentation type for caller id display public static int PRESENTATION_ALLOWED = 1; // normal public static int PRESENTATION_RESTRICTED = 2; // block by user @@ -42,7 +42,7 @@ public abstract class Connection INCOMING_REJECTED, /* an incoming call that was rejected */ POWER_OFF, /* radio is turned off explicitly */ OUT_OF_SERVICE, /* out of service */ - SIM_ERROR, /* No SIM, SIM locked, or other SIM error */ + ICC_ERROR, /* No ICC, ICC locked, or other ICC error */ CALL_BARRED, /* call was blocked by call barrring */ FDN_BLOCKED, /* call was blocked by fixed dial number */ CS_RESTRICTED, /* call was blocked by restricted all voice access */ @@ -54,7 +54,7 @@ public abstract class Connection /* Instance Methods */ - /** + /** * Gets address (e.g., phone number) associated with connection * TODO: distinguish reasons for unavailablity * @@ -92,7 +92,7 @@ public abstract class Connection public abstract long getDisconnectTime(); /** - * returns the number of milliseconds the call has been connected, + * returns the number of milliseconds the call has been connected, * or 0 if the call has never connected. * If the call is still connected, then returns the elapsed * time since connect @@ -113,8 +113,8 @@ public abstract class Connection public abstract DisconnectCause getDisconnectCause(); /** - * Returns true of this connection originated elsewhere - * ("MT" or mobile terminated; another party called this terminal) + * Returns true of this connection originated elsewhere + * ("MT" or mobile terminated; another party called this terminal) * or false if this call originated here (MO or mobile originated) */ public abstract boolean isIncoming(); @@ -122,32 +122,30 @@ public abstract class Connection /** * If this Connection is connected, then it is associated with * a Call. - * + * * Returns getCall().getState() or Call.State.IDLE if not * connected */ - public Call.State getState() - { + public Call.State getState() { Call c; c = getCall(); - if (c == null) { + if (c == null) { return Call.State.IDLE; } else { return c.getState(); } } - + /** * isAlive() - * + * * @return true if the connection isn't disconnected * (could be active, holding, ringing, dialing, etc) */ public boolean - isAlive() - { + isAlive() { return getState().isAlive(); } @@ -155,29 +153,26 @@ public abstract class Connection * Returns true if Connection is connected and is INCOMING or WAITING */ public boolean - isRinging() - { + isRinging() { return getState().isRinging(); } /** - * + * * @return the userdata set in setUserData() */ - public Object getUserData() - { + public Object getUserData() { return userData; } /** - * + * * @param userdata user can store an any userdata in the Connection object. */ - public void setUserData(Object userdata) - { + public void setUserData(Object userdata) { this.userData = userdata; } - + /** * Hangup individual Connection */ @@ -191,16 +186,16 @@ public abstract class Connection public abstract void separate() throws CallStateException; public enum PostDialState { - NOT_STARTED, /* The post dial string playback hasn't - been started, or this call is not yet + NOT_STARTED, /* The post dial string playback hasn't + been started, or this call is not yet connected, or this is an incoming call */ STARTED, /* The post dial string playback has begun */ - WAIT, /* The post dial string playback is waiting for a + WAIT, /* The post dial string playback is waiting for a call to proceedAfterWaitChar() */ - WILD, /* The post dial string playback is waiting for a + WILD, /* The post dial string playback is waiting for a call to proceedAfterWildChar() */ COMPLETE, /* The post dial string playback is complete */ - CANCELLED /* The post dial string playback was cancelled + CANCELLED /* The post dial string playback was cancelled with cancelPostDial() */ } @@ -215,7 +210,7 @@ public abstract class Connection /** * See Phone.setOnPostDialWaitCharacter() */ - + public abstract void proceedAfterWaitChar(); /** @@ -232,5 +227,5 @@ public abstract class Connection * @return one of PRESENTATION_* */ public abstract int getNumberPresentation(); - + } diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java new file mode 100644 index 0000000..6e9d1ab --- /dev/null +++ b/telephony/java/com/android/internal/telephony/DataConnection.java @@ -0,0 +1,299 @@ +/* + * 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; + +/** + * {@hide} + */ +public abstract class DataConnection extends Handler { + + // the inherited class + + public enum State { + ACTIVE, /* has active data connection */ + ACTIVATING, /* during connecting process */ + INACTIVE; /* has empty data connection */ + + public String toString() { + switch (this) { + case ACTIVE: + return "active"; + case ACTIVATING: + return "setting up"; + default: + return "inactive"; + } + } + + public boolean isActive() { + return this == ACTIVE; + } + + public boolean isInactive() { + return this == INACTIVE; + } + } + + public enum FailCause { + NONE, + BAD_APN, + BAD_PAP_SECRET, + BARRED, + USER_AUTHENTICATION, + SERVICE_OPTION_NOT_SUPPORTED, + SERVICE_OPTION_NOT_SUBSCRIBED, + SIM_LOCKED, + RADIO_OFF, + NO_SIGNAL, + NO_DATA_PLAN, + RADIO_NOT_AVAILABLE, + SUSPENED_TEMPORARY, + RADIO_ERROR_RETRY, + UNKNOWN; + + public boolean isPermanentFail() { + return (this == RADIO_OFF); + } + + public String toString() { + switch (this) { + case NONE: + return "no error"; + case BAD_APN: + return "bad apn"; + case BAD_PAP_SECRET: + return "bad pap secret"; + case BARRED: + return "barred"; + case USER_AUTHENTICATION: + return "error user autentication"; + case SERVICE_OPTION_NOT_SUPPORTED: + return "data not supported"; + case SERVICE_OPTION_NOT_SUBSCRIBED: + return "datt not subcribed"; + case SIM_LOCKED: + return "sim locked"; + case RADIO_OFF: + return "radio is off"; + case NO_SIGNAL: + return "no signal"; + case NO_DATA_PLAN: + return "no data plan"; + case RADIO_NOT_AVAILABLE: + return "radio not available"; + case SUSPENED_TEMPORARY: + return "suspend temporary"; + case RADIO_ERROR_RETRY: + return "transient radio error"; + default: + return "unknown data error"; + } + } + } + + // ***** Event codes + protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = 1; + protected static final int EVENT_GET_LAST_FAIL_DONE = 2; + protected static final int EVENT_LINK_STATE_CHANGED = 3; + protected static final int EVENT_DEACTIVATE_DONE = 4; + protected static final int EVENT_FORCE_RETRY = 5; + + //***** Tag IDs for EventLog + protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; + + + //***** Member Variables + protected PhoneBase phone; + protected Message onConnectCompleted; + protected Message onDisconnect; + protected int cid; + protected String interfaceName; + protected String ipAddress; + protected String gatewayAddress; + protected String[] dnsServers; + protected State state; + protected long createTime; + protected long lastFailTime; + protected FailCause lastFailCause; + protected static final String NULL_IP = "0.0.0.0"; + Object userData; + + // receivedDisconnectReq is set when disconnect during activation + protected boolean receivedDisconnectReq; + + /* Instance Methods */ + protected abstract void onSetupConnectionCompleted(AsyncResult ar); + + protected abstract void onDeactivated(AsyncResult ar); + + protected abstract void disconnect(Message msg); + + protected abstract void notifyFail(FailCause cause, Message onCompleted); + + protected abstract void notifyDisconnect(Message msg); + + protected abstract void onLinkStateChanged(DataLink.LinkState linkState); + + protected abstract FailCause getFailCauseFromRequest(int rilCause); + + public abstract String toString(); + + protected abstract void log(String s); + + + //***** Constructor + protected DataConnection(PhoneBase phone) { + super(); + this.phone = phone; + onConnectCompleted = null; + onDisconnect = null; + this.cid = -1; + receivedDisconnectReq = false; + this.dnsServers = new String[2]; + + clearSettings(); + } + + protected void setHttpProxy(String httpProxy, String httpPort) { + if (httpProxy == null || httpProxy.length() == 0) { + phone.setSystemProperty("net.gprs.http-proxy", null); + return; + } + + if (httpPort == null || httpPort.length() == 0) { + httpPort = "8080"; // Default to port 8080 + } + + phone.setSystemProperty("net.gprs.http-proxy", + "http://" + httpProxy + ":" + httpPort + "/"); + } + + public String getInterface() { + return interfaceName; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getGatewayAddress() { + return gatewayAddress; + } + + public String[] getDnsServers() { + return dnsServers; + } + + public void clearSettings() { + log("DataConnection.clearSettings()"); + + this.state = State.INACTIVE; + this.createTime = -1; + this.lastFailTime = -1; + this.lastFailCause = FailCause.NONE; + + receivedDisconnectReq = false; + onConnectCompleted = null; + interfaceName = null; + ipAddress = null; + gatewayAddress = null; + dnsServers[0] = null; + dnsServers[1] = null; + } + + protected void onGetLastFailCompleted(AsyncResult ar) { + if (receivedDisconnectReq) { + // Don't bother reporting the error if there's already a + // pending disconnect request, since DataConnectionTracker + // has already updated its state. + notifyDisconnect(onDisconnect); + } else { + FailCause cause = FailCause.UNKNOWN; + + if (ar.exception == null) { + int rilFailCause = ((int[]) (ar.result))[0]; + cause = getFailCauseFromRequest(rilFailCause); + } + notifyFail(cause, onConnectCompleted); + } + } + + protected void onForceRetry() { + if (receivedDisconnectReq) { + notifyDisconnect(onDisconnect); + } else { + notifyFail(FailCause.RADIO_ERROR_RETRY, onConnectCompleted); + } + } + + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + + log("DataConnection.handleMessage()"); + + switch (msg.what) { + + case EVENT_SETUP_DATA_CONNECTION_DONE: + onSetupConnectionCompleted((AsyncResult) msg.obj); + break; + + case EVENT_FORCE_RETRY: + onForceRetry(); + break; + + case EVENT_GET_LAST_FAIL_DONE: + onGetLastFailCompleted((AsyncResult) msg.obj); + break; + + case EVENT_LINK_STATE_CHANGED: + ar = (AsyncResult) msg.obj; + DataLink.LinkState ls = (DataLink.LinkState) ar.result; + onLinkStateChanged(ls); + break; + + case EVENT_DEACTIVATE_DONE: + onDeactivated((AsyncResult) msg.obj); + break; + } + } + + public State getState() { + log("DataConnection.getState()"); + return state; + } + + public long getConnectionTime() { + log("DataConnection.getConnectionTime()"); + return createTime; + } + + public long getLastFailTime() { + log("DataConnection.getLastFailTime()"); + return lastFailTime; + } + + public FailCause getLastFailCause() { + log("DataConnection.getLastFailCause()"); + return lastFailCause; + } +} diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java new file mode 100644 index 0000000..5b826b2 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -0,0 +1,313 @@ +/* + * 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.app.PendingIntent; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.INetStatService; +import android.os.Message; +import android.os.RemoteException; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.util.Log; + +/** + * {@hide} + * + */ +public abstract class DataConnectionTracker extends Handler { + private static final boolean DBG = true; + + /** + * IDLE: ready to start data connection setup, default state + * INITING: state of issued setupDefaultPDP() but not finish yet + * CONNECTING: state of issued startPppd() but not finish yet + * SCANNING: data connection fails with one apn but other apns are available + * ready to start data connection on other apns (before INITING) + * CONNECTED: IP connection is setup + * DISCONNECTING: Connection.disconnect() has been called, but PDP + * context is not yet deactivated + * FAILED: data connection fail for all apns settings + * + * getDataConnectionState() maps State to DataState + * FAILED or IDLE : DISCONNECTED + * INITING or CONNECTING or SCANNING: CONNECTING + * CONNECTED : CONNECTED or DISCONNECTING + */ + public enum State { + IDLE, + INITING, + CONNECTING, + SCANNING, + CONNECTED, + DISCONNECTING, + FAILED + } + + public enum Activity { + NONE, + DATAIN, + DATAOUT, + DATAINANDOUT + } + + //***** Event Codes + protected static final int EVENT_DATA_SETUP_COMPLETE = 1; + protected static final int EVENT_RADIO_AVAILABLE = 3; + protected static final int EVENT_RECORDS_LOADED = 4; + protected static final int EVENT_TRY_SETUP_DATA = 5; + protected static final int EVENT_DATA_STATE_CHANGED = 6; + protected static final int EVENT_POLL_PDP = 7; + protected static final int EVENT_GET_PDP_LIST_COMPLETE = 11; + protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 12; + protected static final int EVENT_VOICE_CALL_STARTED = 14; + protected static final int EVENT_VOICE_CALL_ENDED = 15; + protected static final int EVENT_GPRS_DETACHED = 19; + protected static final int EVENT_LINK_STATE_CHANGED = 20; + protected static final int EVENT_ROAMING_ON = 21; + protected static final int EVENT_ROAMING_OFF = 22; + protected static final int EVENT_ENABLE_NEW_APN = 23; + protected static final int EVENT_RESTORE_DEFAULT_APN = 24; + protected static final int EVENT_DISCONNECT_DONE = 25; + protected static final int EVENT_GPRS_ATTACHED = 26; + protected static final int EVENT_START_NETSTAT_POLL = 27; + protected static final int EVENT_START_RECOVERY = 28; + protected static final int EVENT_APN_CHANGED = 29; + protected static final int EVENT_CDMA_DATA_DETACHED = 30; + protected static final int EVENT_NV_READY = 31; + protected static final int EVENT_PS_RESTRICT_ENABLED = 32; + protected static final int EVENT_PS_RESTRICT_DISABLED = 33; + + //***** Constants + protected static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000; + + /** Cap out with 1 hour retry interval. */ + protected static final int RECONNECT_DELAY_MAX_MILLIS = 60 * 60 * 1000; + + /** Slow poll when attempting connection recovery. */ + protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000; + /** Default ping deadline, in seconds. */ + protected final int DEFAULT_PING_DEADLINE = 5; + /** Default max failure count before attempting to network re-registration. */ + protected final int DEFAULT_MAX_PDP_RESET_FAIL = 3; + + /** + * After detecting a potential connection problem, this is the max number + * of subsequent polls before attempting a radio reset. At this point, + * poll interval is 5 seconds (POLL_NETSTAT_SLOW_MILLIS), so set this to + * poll for about 2 more minutes. + */ + protected static final int NO_RECV_POLL_LIMIT = 24; + + // 1 sec. default polling interval when screen is on. + protected static final int POLL_NETSTAT_MILLIS = 1000; + // 10 min. default polling interval when screen is off. + protected static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10; + // 2 min for round trip time + protected static final int POLL_LONGEST_RTT = 120 * 1000; + // 10 for packets without ack + protected static final int NUMBER_SENT_PACKETS_OF_HANG = 10; + // how long to wait before switching back to default APN + protected static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000; + // system property that can override the above value + protected static final String APN_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; + // represents an invalid IP address + protected static final String NULL_IP = "0.0.0.0"; + + + // member variables + protected PhoneBase phone; + protected Activity activity = Activity.NONE; + protected State state = State.IDLE; + protected Handler mDataConnectionTracker = null; + + + protected INetStatService netstat; + protected long txPkts, rxPkts, sentSinceLastRecv; + protected int netStatPollPeriod; + protected int mNoRecvPollCount = 0; + protected boolean netStatPollEnabled = false; + + // wifi connection status will be updated by sticky intent + protected boolean mIsWifiConnected = false; + + /** Intent sent when the reconnect alarm fires. */ + protected PendingIntent mReconnectIntent = null; + + /** CID of active data connection */ + protected int cidActive; + + /** + * Default constructor + */ + protected DataConnectionTracker(PhoneBase phone) { + super(); + this.phone = phone; + } + + public Activity getActivity() { + return activity; + } + + public State getState() { + return state; + } + + public String getStateInString() { + switch (state) { + case IDLE: return "IDLE"; + case INITING: return "INIT"; + case CONNECTING: return "CING"; + case SCANNING: return "SCAN"; + case CONNECTED: return "CNTD"; + case DISCONNECTING: return "DING"; + case FAILED: return "FAIL"; + default: return "ERRO"; + } + } + + /** + * The data connection is expected to be setup while device + * 1. has Icc card + * 2. registered for data service + * 3. user doesn't explicitly disable data service + * 4. wifi is not on + * + * @return false while no data connection if all above requirements are met. + */ + public abstract boolean isDataConnectionAsDesired(); + + //The data roaming setting is now located in the shared preferences. + // See if the requested preference value is the same as that stored in + // the shared values. If it is not, then update it. + public void setDataOnRoamingEnabled(boolean enabled) { + if (getDataOnRoamingEnabled() != enabled) { + Settings.Secure.putInt(phone.getContext().getContentResolver(), + Settings.Secure.DATA_ROAMING, enabled ? 1 : 0); + } + Message roamingMsg = phone.getServiceState().getRoaming() ? + obtainMessage(EVENT_ROAMING_ON) : obtainMessage(EVENT_ROAMING_OFF); + sendMessage(roamingMsg); + } + + //Retrieve the data roaming setting from the shared preferences. + public boolean getDataOnRoamingEnabled() { + try { + return Settings.Secure.getInt(phone.getContext().getContentResolver(), + Settings.Secure.DATA_ROAMING) > 0; + } catch (SettingNotFoundException snfe) { + return false; + } + } + + // abstract handler methods + protected abstract void onTrySetupData(); + protected abstract void onRoamingOff(); + protected abstract void onRoamingOn(); + protected abstract void onRadioAvailable(); + protected abstract void onRadioOffOrNotAvailable(); + protected abstract void onDataSetupComplete(AsyncResult ar); + protected abstract void onDisconnectDone(AsyncResult ar); + protected abstract void onVoiceCallStarted(); + protected abstract void onVoiceCallEnded(); + + //***** Overridden from Handler + public void handleMessage (Message msg) { + switch (msg.what) { + + case EVENT_TRY_SETUP_DATA: + onTrySetupData(); + break; + + case EVENT_ROAMING_OFF: + onRoamingOff(); + break; + + case EVENT_ROAMING_ON: + onRoamingOn(); + break; + + case EVENT_RADIO_AVAILABLE: + onRadioAvailable(); + break; + + case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: + onRadioOffOrNotAvailable(); + break; + + case EVENT_DATA_SETUP_COMPLETE: + cidActive = msg.arg1; + onDataSetupComplete((AsyncResult) msg.obj); + break; + + case EVENT_DISCONNECT_DONE: + onDisconnectDone((AsyncResult) msg.obj); + break; + + case EVENT_VOICE_CALL_STARTED: + onVoiceCallStarted(); + break; + + case EVENT_VOICE_CALL_ENDED: + onVoiceCallEnded(); + break; + + default: + Log.e("DATA", "Unidentified event = " + msg.what); + break; + } + } + + /** + * Simply tear down data connections due to radio off + * and don't setup again. + */ + public abstract void cleanConnectionBeforeRadioOff(); + + /** + * Report the current state of data connectivity (enabled or disabled) + * @return {@code false} if data connectivity has been explicitly disabled, + * {@code true} otherwise. + */ + public abstract boolean getDataEnabled(); + + /** + * Report on whether data connectivity is enabled + * @return {@code false} if data connectivity has been explicitly disabled, + * {@code true} otherwise. + */ + public abstract boolean getAnyDataEnabled(); + + /** + * Prevent mobile data connections from being established, + * or once again allow mobile data connections. If the state + * toggles, then either tear down or set up data, as + * appropriate to match the new state. + * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data + * @return {@code true} if the operation succeeded + */ + public abstract boolean setDataEnabled(boolean enable); + + protected abstract void startNetStatPoll(); + + protected abstract void stopNetStatPoll(); + + protected abstract void restartRadio(); + + protected abstract void log(String s); +} diff --git a/telephony/java/com/android/internal/telephony/DataLink.java b/telephony/java/com/android/internal/telephony/DataLink.java new file mode 100644 index 0000000..8132d91 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/DataLink.java @@ -0,0 +1,40 @@ +/* + * 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.Handler; +import android.os.Registrant; + +/** + * Base class representing the data link layer (eg, PPP). + * + * {@hide} + */ +public abstract class DataLink extends Handler implements DataLinkInterface { + + /** Registrant for link status change notifications. */ + protected Registrant mLinkChangeRegistrant; + protected DataConnectionTracker dataConnection; + + protected DataLink(DataConnectionTracker dc) { + dataConnection = dc; + } + + public void setOnLinkChange(Handler h, int what, Object obj) { + mLinkChangeRegistrant = new Registrant(h, what, obj); + } +} diff --git a/telephony/java/com/android/internal/telephony/DataLinkInterface.java b/telephony/java/com/android/internal/telephony/DataLinkInterface.java new file mode 100644 index 0000000..e8148a8 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/DataLinkInterface.java @@ -0,0 +1,77 @@ +/* + * 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.database.Cursor; +import android.os.Handler; + +/** + * Data link interface. + * + * {@hide} + */ +public interface DataLinkInterface { + /** + * Link state enumeration. + * + */ + enum LinkState { + LINK_UNKNOWN, + LINK_UP, + LINK_DOWN, + LINK_EXITED + } + + /** Normal exit */ + final static int EXIT_OK = 0; + /** Open failed */ + final static int EXIT_OPEN_FAILED = 7; + + /** + * Sets the handler for link state change events. + * + * @param h Handler + * @param what User-defined message code + * @param obj User object + */ + void setOnLinkChange(Handler h, int what, Object obj); + + /** + * Sets up the data link. + */ + void connect(); + + /** + * Tears down the data link. + */ + void disconnect(); + + /** + * Returns the exit code for a data link failure. + * + * @return exit code + */ + int getLastLinkExitCode(); + + /** + * Sets password information that may be required by the data link + * (eg, PAP secrets). + * + * @param cursor cursor to carriers table + */ + void setPasswordInfo(Cursor cursor); +} diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java index 81ef623..79b4afe 100644 --- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java @@ -33,7 +33,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier { private static final boolean DBG = true; private ITelephonyRegistry mRegistry; - /*package*/ + /*package*/ DefaultPhoneNotifier() { mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( "telephony.registry")); @@ -94,7 +94,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier { public void notifyDataConnection(Phone sender, String reason) { try { - mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()), + mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()), sender.isDataConnectivityPossible(), reason, sender.getActiveApn(), sender.getInterfaceName(null)); } catch (RemoteException ex) { @@ -119,7 +119,7 @@ public class DefaultPhoneNotifier implements PhoneNotifier { // system process is dead } } - + private void log(String s) { Log.d(LOG_TAG, "[PhoneNotifier] " + s); } diff --git a/telephony/java/com/android/internal/telephony/DriverCall.java b/telephony/java/com/android/internal/telephony/DriverCall.java new file mode 100644 index 0000000..6c4e71f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/DriverCall.java @@ -0,0 +1,155 @@ +/* + * 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 com.android.internal.telephony.*; +import android.util.Log; +import java.lang.Comparable; +import android.telephony.PhoneNumberUtils; + +/** + * {@hide} + */ +public class DriverCall implements Comparable { + static final String LOG_TAG = "RILB"; + + public enum State { + ACTIVE, + HOLDING, + DIALING, // MO call only + ALERTING, // MO call only + INCOMING, // MT call only + WAITING; // MT call only + // If you add a state, make sure to look for the switch() + // statements that use this enum + } + + public int index; + public boolean isMT; + public State state; // May be null if unavail + public boolean isMpty; + public String number; + public int TOA; + public boolean isVoice; + public int als; + public int numberPresentation; + + /** returns null on error */ + static DriverCall + fromCLCCLine(String line) { + DriverCall ret = new DriverCall(); + + //+CLCC: 1,0,2,0,0,\"+18005551212\",145 + // index,isMT,state,mode,isMpty(,number,TOA)? + ATResponseParser p = new ATResponseParser(line); + + try { + ret.index = p.nextInt(); + ret.isMT = p.nextBoolean(); + ret.state = stateFromCLCC(p.nextInt()); + + ret.isVoice = (0 == p.nextInt()); + ret.isMpty = p.nextBoolean(); + + // use ALLOWED as default presentation while parsing CLCC + ret.numberPresentation = Connection.PRESENTATION_ALLOWED; + + if (p.hasMore()) { + // Some lame implementations return strings + // like "NOT AVAILABLE" in the CLCC line + ret.number = PhoneNumberUtils.extractNetworkPortion( + p.nextString()); + + if (ret.number.length() == 0) { + ret.number = null; + } + + ret.TOA = p.nextInt(); + + // Make sure there's a leading + on addresses with a TOA + // of 145 + + ret.number = PhoneNumberUtils.stringFromStringAndTOA( + ret.number, ret.TOA); + + } + } catch (ATParseEx ex) { + Log.e(LOG_TAG,"Invalid CLCC line: '" + line + "'"); + return null; + } + + return ret; + } + + public + DriverCall() { + } + + public String + toString() { + return "id=" + index + "," + + (isMT ? "mt" : "mo") + "," + + state + "," + + (isVoice ? "voice" : "no_voc") + "," + + (isMpty ? "conf" : "norm") + "," + + TOA + "," + als + ",cli " + numberPresentation; + } + + public static State + stateFromCLCC(int state) throws ATParseEx { + switch(state) { + case 0: return State.ACTIVE; + case 1: return State.HOLDING; + case 2: return State.DIALING; + case 3: return State.ALERTING; + case 4: return State.INCOMING; + case 5: return State.WAITING; + default: + throw new ATParseEx("illegal call state " + state); + } + } + + public static int + presentationFromCLIP(int cli) throws ATParseEx + { + switch(cli) { + case 0: return Connection.PRESENTATION_ALLOWED; + case 1: return Connection.PRESENTATION_RESTRICTED; + case 2: return Connection.PRESENTATION_UNKNOWN; + case 3: return Connection.PRESENTATION_PAYPHONE; + default: + throw new ATParseEx("illegal presentation " + cli); + } + } + + //***** Comparable Implementation + + /** For sorting by index */ + public int + compareTo (Object o) { + DriverCall dc; + + dc = (DriverCall)o; + + if (index < dc.index) { + return -1; + } else if (index == dc.index) { + return 0; + } else { /*index > dc.index*/ + return 1; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/EncodeException.java b/telephony/java/com/android/internal/telephony/EncodeException.java new file mode 100644 index 0000000..0436ba0 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/EncodeException.java @@ -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 EncodeException extends Exception { + public EncodeException() { + super(); + } + + public EncodeException(String s) { + super(s); + } + + public EncodeException(char c) { + super("Unencodable char: '" + c + "'"); + } +} + diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java new file mode 100644 index 0000000..8f4c69c --- /dev/null +++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java @@ -0,0 +1,798 @@ +/* + * 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.telephony.SmsMessage; +import android.util.SparseIntArray; + +import android.util.Log; + +/** + * This class implements the character set mapping between + * the GSM SMS 7-bit alphabet specifed in TS 23.038 6.2.1 + * and UTF-16 + * + * {@hide} + */ +public class GsmAlphabet { + static final String LOG_TAG = "GSM"; + + + + //***** Constants + + /** + * This escapes extended characters, and when present indicates that the + * following character should + * be looked up in the "extended" table + * + * gsmToChar(GSM_EXTENDED_ESCAPE) returns 0xffff + */ + + public static final byte GSM_EXTENDED_ESCAPE = 0x1B; + + + /** + * char to GSM alphabet char + * Returns ' ' in GSM alphabet if there's no possible match + * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table + * In this case, you must call charToGsmExtended() for the value that + * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string + */ + public static int + charToGsm(char c) { + try { + return charToGsm(c, false); + } catch (EncodeException ex) { + // this should never happen + return sGsmSpaceChar; + } + } + + /** + * char to GSM alphabet char + * @param throwException If true, throws EncodeException on invalid char. + * If false, returns GSM alphabet ' ' char. + * + * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table + * In this case, you must call charToGsmExtended() for the value that + * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string + */ + + public static int + charToGsm(char c, boolean throwException) throws EncodeException { + int ret; + + ret = charToGsm.get(c, -1); + + if (ret == -1) { + ret = charToGsmExtended.get(c, -1); + + if (ret == -1) { + if (throwException) { + throw new EncodeException(c); + } else { + return sGsmSpaceChar; + } + } else { + return GSM_EXTENDED_ESCAPE; + } + } + + return ret; + + } + + + /** + * char to extended GSM alphabet char + * + * Extended chars should be escaped with GSM_EXTENDED_ESCAPE + * + * Returns ' ' in GSM alphabet if there's no possible match + * + */ + public static int + charToGsmExtended(char c) { + int ret; + + ret = charToGsmExtended.get(c, -1); + + if (ret == -1) { + return sGsmSpaceChar; + } + + return ret; + } + + /** + * Converts a character in the GSM alphabet into a char + * + * if GSM_EXTENDED_ESCAPE is passed, 0xffff is returned. In this case, + * the following character in the stream should be decoded with + * gsmExtendedToChar() + * + * If an unmappable value is passed (one greater than 127), ' ' is returned + */ + + public static char + gsmToChar(int gsmChar) { + return (char)gsmToChar.get(gsmChar, ' '); + } + + /** + * Converts a character in the extended GSM alphabet into a char + * + * if GSM_EXTENDED_ESCAPE is passed, ' ' is returned since no second + * extension page has yet been defined (see Note 1 in table 6.2.1.1 of + * TS 23.038 v7.00) + * + * If an unmappable value is passed , ' ' is returned + */ + + public static char + gsmExtendedToChar(int gsmChar) { + int ret; + + ret = gsmExtendedToChar.get(gsmChar, -1); + + if (ret == -1) { + return ' '; + } + + return (char)ret; + } + + /** + * Converts a String into a byte array containing the 7-bit packed + * GSM Alphabet representation of the string. If a header is provided, + * this is included in the returned byte array and padded to a septet + * boundary. + * + * Unencodable chars are encoded as spaces + * + * Byte 0 in the returned byte array is the count of septets used, + * including the header and header padding. The returned byte array is + * the minimum size required to store the packed septets. The returned + * array cannot contain more than 255 septets. + * + * @param data The text string to encode. + * @param header Optional header (includeing length byte) that precedes + * the encoded data, padded to septet boundary. + * @return Byte array containing header and encoded data. + */ + public static byte[] stringToGsm7BitPackedWithHeader(String data, byte[] header) + throws EncodeException { + + if (header == null || header.length == 0) { + return stringToGsm7BitPacked(data); + } + + int headerBits = header.length * 8; + int headerSeptets = headerBits / 7; + headerSeptets += (headerBits % 7) > 0 ? 1 : 0; + + int sz = data.length(); + int septetCount; + septetCount = countGsmSeptets(data, true) + headerSeptets; + + byte[] ret = stringToGsm7BitPacked(data, 0, septetCount, + (headerSeptets*7), true); + + // Paste in the header + System.arraycopy(header, 0, ret, 1, header.length); + return ret; + } + + /** + * Converts a String into a byte array containing + * the 7-bit packed GSM Alphabet representation of the string. + * + * Unencodable chars are encoded as spaces + * + * Byte 0 in the returned byte array is the count of septets used + * The returned byte array is the minimum size required to store + * the packed septets. The returned array cannot contain more than 255 + * septets. + * + * @param data the data string to endcode + * @throws EncodeException if String is too large to encode + */ + public static byte[] stringToGsm7BitPacked(String data) + throws EncodeException { + return stringToGsm7BitPacked(data, 0, -1, 0, true); + } + + /** + * Converts a String into a byte array containing + * the 7-bit packed GSM Alphabet representation of the string. + * + * Byte 0 in the returned byte array is the count of septets used + * The returned byte array is the minimum size required to store + * the packed septets. The returned array cannot contain more than 255 + * septets. + * + * @param data the text to convert to septets + * @param dataOffset the character offset in data to start the encoding from + * @param maxSeptets the maximum number of septets to convert, or -1 for no + * enforced maximum. + * @param startingBitOffset the number of padding bits to put before + * the start of the first septet at the begining of the array + * @param throwException If true, throws EncodeException on invalid char. + * If false, replaces unencodable char with GSM alphabet space char. + * + * @throws EncodeException if String is too large to encode + */ + public static byte[] stringToGsm7BitPacked(String data, int dataOffset, + int maxSeptets, int startingBitOffset, boolean throwException) + throws EncodeException { + + int sz = data.length(); + int septetCount; + if (maxSeptets == -1) { + septetCount = countGsmSeptets(data, true); + } else { + septetCount = maxSeptets; + } + + if(septetCount > 0xff) { + throw new EncodeException("Payload cannot exceed " + Short.MAX_VALUE + + " septets"); + } + + // Enough for all the septets and the length 2 byte prefix + byte[] ret = new byte[1 + (((septetCount * 7) + 7) / 8)]; + + int bitOffset = startingBitOffset; + int septets = startingBitOffset/7; + for (int i = dataOffset; i < sz && septets < septetCount; i++, bitOffset += 7) { + char c = data.charAt(i); + + int v = GsmAlphabet.charToGsm(c, throwException); + if (v == GSM_EXTENDED_ESCAPE) { + // Lookup the extended char + v = GsmAlphabet.charToGsmExtended(c); + + packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE); + bitOffset += 7; + septets++; + } + + packSmsChar(ret, bitOffset, v); + septets++; + } + + // See check for > 0xff above + ret[0] = (byte)septets; + + return ret; + } + + /** + * Pack a 7-bit char into its appropirate place in a byte array + * + * @param bitOffset the bit offset that the septet should be packed at + * (septet index * 7) + */ + private static void + packSmsChar(byte[] packedChars, int bitOffset, int value) { + int byteOffset = bitOffset / 8; + int shift = bitOffset % 8; + + packedChars[++byteOffset] |= value << shift; + + if (shift > 1) { + packedChars[++byteOffset] = (byte)(value >> (8 - shift)); + } + } + + /** + * Convert a GSM alphabet 7 bit packed string (SMS string) into a + * {@link java.lang.String}. + * + * See TS 23.038 6.1.2.1 for SMS Character Packing + * + * @param pdu the raw data from the pdu + * @param offset the byte offset of + * @param lengthSeptets string length in septets, not bytes + * @return String representation or null on decoding exception + */ + public static String gsm7BitPackedToString(byte[] pdu, int offset, + int lengthSeptets) { + return gsm7BitPackedToString(pdu, offset, lengthSeptets, 0); + } + + /** + * Convert a GSM alphabet 7 bit packed string (SMS string) into a + * {@link java.lang.String}. + * + * See TS 23.038 6.1.2.1 for SMS Character Packing + * + * @param pdu the raw data from the pdu + * @param offset the byte offset of + * @param lengthSeptets string length in septets, not bytes + * @param numPaddingBits the number of padding bits before the start of the + * string in the first byte + * @return String representation or null on decoding exception + */ + public static String gsm7BitPackedToString(byte[] pdu, int offset, + int lengthSeptets, int numPaddingBits) { + StringBuilder ret = new StringBuilder(lengthSeptets); + boolean prevCharWasEscape; + + try { + prevCharWasEscape = false; + + for (int i = 0 ; i < lengthSeptets ; i++) { + int bitOffset = (7 * i) + numPaddingBits; + + int byteOffset = bitOffset / 8; + int shift = bitOffset % 8; + int gsmVal; + + gsmVal = (0x7f & (pdu[offset + byteOffset] >> shift)); + + // if it crosses a byte boundry + if (shift > 1) { + // set msb bits to 0 + gsmVal &= 0x7f >> (shift - 1); + + gsmVal |= 0x7f & (pdu[offset + byteOffset + 1] << (8 - shift)); + } + + if (prevCharWasEscape) { + ret.append(GsmAlphabet.gsmExtendedToChar(gsmVal)); + prevCharWasEscape = false; + } else if (gsmVal == GSM_EXTENDED_ESCAPE) { + prevCharWasEscape = true; + } else { + ret.append(GsmAlphabet.gsmToChar(gsmVal)); + } + } + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "Error GSM 7 bit packed: ", ex); + return null; + } + + return ret.toString(); + } + + + /** + * Convert a GSM alphabet string that's stored in 8-bit unpacked + * format (as it often appears in SIM records) into a String + * + * Field may be padded with trailing 0xff's. The decode stops + * at the first 0xff encountered. + */ + public static String + gsm8BitUnpackedToString(byte[] data, int offset, int length) { + boolean prevWasEscape; + StringBuilder ret = new StringBuilder(length); + + prevWasEscape = false; + for (int i = offset ; i < offset + length ; i++) { + // Never underestimate the pain that can be caused + // by signed bytes + int c = data[i] & 0xff; + + if (c == 0xff) { + break; + } else if (c == GSM_EXTENDED_ESCAPE) { + if (prevWasEscape) { + // Two escape chars in a row + // We treat this as a space + // See Note 1 in table 6.2.1.1 of TS 23.038 v7.00 + ret.append(' '); + prevWasEscape = false; + } else { + prevWasEscape = true; + } + } else { + if (prevWasEscape) { + ret.append((char)gsmExtendedToChar.get(c, ' ')); + } else { + ret.append((char)gsmToChar.get(c, ' ')); + } + prevWasEscape = false; + } + } + + return ret.toString(); + } + + /** + * Convert a string into an 8-bit unpacked GSM alphabet byte + * array + */ + public static byte[] + stringToGsm8BitPacked(String s) { + byte[] ret; + + int septets = 0; + + septets = countGsmSeptets(s); + + // Enough for all the septets and the length byte prefix + ret = new byte[septets]; + + stringToGsm8BitUnpackedField(s, ret, 0, ret.length); + + return ret; + } + + + /** + * Write a String into a GSM 8-bit unpacked field of + * @param length size at @param offset in @param dest + * + * Field is padded with 0xff's, string is truncated if necessary + */ + + public static void + stringToGsm8BitUnpackedField(String s, byte dest[], int offset, int length) { + int outByteIndex = offset; + + // Septets are stored in byte-aligned octets + for (int i = 0, sz = s.length() + ; i < sz && (outByteIndex - offset) < length + ; i++ + ) { + char c = s.charAt(i); + + int v = GsmAlphabet.charToGsm(c); + + if (v == GSM_EXTENDED_ESCAPE) { + // make sure we can fit an escaped char + if (! (outByteIndex + 1 - offset < length)) { + break; + } + + dest[outByteIndex++] = GSM_EXTENDED_ESCAPE; + + v = GsmAlphabet.charToGsmExtended(c); + } + + dest[outByteIndex++] = (byte)v; + } + + // pad with 0xff's + while((outByteIndex - offset) < length) { + dest[outByteIndex++] = (byte)0xff; + } + } + + /** + * Returns the count of 7-bit GSM alphabet characters + * needed to represent this character. Counts unencodable char as 1 septet. + */ + public static int + countGsmSeptets(char c) { + try { + return countGsmSeptets(c, false); + } catch (EncodeException ex) { + // This should never happen. + return 0; + } + } + + /** + * Returns the count of 7-bit GSM alphabet characters + * needed to represent this character + * @param throwsException If true, throws EncodeException if unencodable + * char. Otherwise, counts invalid char as 1 septet + */ + public static int + countGsmSeptets(char c, boolean throwsException) throws EncodeException { + if (charToGsm.get(c, -1) != -1) { + return 1; + } + + if (charToGsmExtended.get(c, -1) != -1) { + return 2; + } + + if (throwsException) { + throw new EncodeException(c); + } else { + // count as a space char + return 1; + } + } + + /** + * Returns the count of 7-bit GSM alphabet characters + * needed to represent this string. Counts unencodable char as 1 septet. + */ + public static int + countGsmSeptets(CharSequence s) { + try { + return countGsmSeptets(s, false); + } catch (EncodeException ex) { + // this should never happen + return 0; + } + } + + /** + * Returns the count of 7-bit GSM alphabet characters + * needed to represent this string. + * @param throwsException If true, throws EncodeException if unencodable + * char. Otherwise, counts invalid char as 1 septet + */ + public static int + countGsmSeptets(CharSequence s, boolean throwsException) throws EncodeException { + int charIndex = 0; + int sz = s.length(); + int count = 0; + + while (charIndex < sz) { + count += countGsmSeptets(s.charAt(charIndex), throwsException); + charIndex++; + } + + return count; + } + + /** + * Returns the index into s of the first character + * after limit septets have been reached, starting at + * index start. This is used when dividing messages + * into units within the SMS message size limit. + * + * @param s source string + * @param start index of where to start counting septets + * @param limit maximum septets to include, + * e.g. MAX_USER_DATA_SEPTETS + * @return index of first character that won't fit, or the length + * of the entire string if everything fits + */ + public static int + findGsmSeptetLimitIndex(String s, int start, int limit) { + int accumulator = 0; + int size = s.length(); + + for (int i = start; i < size; i++) { + accumulator += countGsmSeptets(s.charAt(i)); + if (accumulator > limit) { + return i; + } + } + return size; + } + + /** + * Returns the index into s of the first character + * after limit octets have been reached, starting at + * index start. This is used when dividing messages + * in UCS2 encoding into units within the SMS message size limit. + * + * @param s source string + * @param start index of where to start counting septets + * @param limit maximum septets to include, + * e.g. MAX_USER_DATA_BYTES + * @return index of first character that won't fit, or the length + * of the entire string if everything fits + */ + public static int + findUCS2LimitIndex(String s, int start, int limit) { + int numCharToBeEncoded = s.length() - start; + return ((numCharToBeEncoded*2 > limit)? limit/2: numCharToBeEncoded) + start; + } + + /** + * Returns the index into s of the first character + * after limit septets/octets have been reached + * according to the encodingType, starting at + * index start. This is used when dividing messages + * units within the SMS message size limit. + * + * @param s source string + * @param start index of where to start counting septets + * @param limit maximum septets to include, + * e.g. MAX_USER_DATA_BYTES + * @return index of first character that won't fit, or the length + * of the entire string if everything fits + */ + public static int + findLimitIndex(String s, int start, int limit, int encodingType) throws EncodeException { + if (encodingType == SmsMessage.ENCODING_7BIT) { + return findGsmSeptetLimitIndex(s, start, limit); + } + else if (encodingType == SmsMessage.ENCODING_16BIT) { + return findUCS2LimitIndex(s, start, limit); + } + else { + throw new EncodeException("Unsupported encoding type: " + encodingType); + } + } + + // Set in the static initializer + private static int sGsmSpaceChar; + + private static final SparseIntArray charToGsm = new SparseIntArray(); + private static final SparseIntArray gsmToChar = new SparseIntArray(); + private static final SparseIntArray charToGsmExtended = new SparseIntArray(); + private static final SparseIntArray gsmExtendedToChar = new SparseIntArray(); + + static { + int i = 0; + + charToGsm.put('@', i++); + charToGsm.put('\u00a3', i++); + charToGsm.put('$', i++); + charToGsm.put('\u00a5', i++); + charToGsm.put('\u00e8', i++); + charToGsm.put('\u00e9', i++); + charToGsm.put('\u00f9', i++); + charToGsm.put('\u00ec', i++); + charToGsm.put('\u00f2', i++); + charToGsm.put('\u00c7', i++); + charToGsm.put('\n', i++); + charToGsm.put('\u00d8', i++); + charToGsm.put('\u00f8', i++); + charToGsm.put('\r', i++); + charToGsm.put('\u00c5', i++); + charToGsm.put('\u00e5', i++); + + charToGsm.put('\u0394', i++); + charToGsm.put('_', i++); + charToGsm.put('\u03a6', i++); + charToGsm.put('\u0393', i++); + charToGsm.put('\u039b', i++); + charToGsm.put('\u03a9', i++); + charToGsm.put('\u03a0', i++); + charToGsm.put('\u03a8', i++); + charToGsm.put('\u03a3', i++); + charToGsm.put('\u0398', i++); + charToGsm.put('\u039e', i++); + charToGsm.put('\uffff', i++); + charToGsm.put('\u00c6', i++); + charToGsm.put('\u00e6', i++); + charToGsm.put('\u00df', i++); + charToGsm.put('\u00c9', i++); + + charToGsm.put(' ', i++); + charToGsm.put('!', i++); + charToGsm.put('"', i++); + charToGsm.put('#', i++); + charToGsm.put('\u00a4', i++); + charToGsm.put('%', i++); + charToGsm.put('&', i++); + charToGsm.put('\'', i++); + charToGsm.put('(', i++); + charToGsm.put(')', i++); + charToGsm.put('*', i++); + charToGsm.put('+', i++); + charToGsm.put(',', i++); + charToGsm.put('-', i++); + charToGsm.put('.', i++); + charToGsm.put('/', i++); + + charToGsm.put('0', i++); + charToGsm.put('1', i++); + charToGsm.put('2', i++); + charToGsm.put('3', i++); + charToGsm.put('4', i++); + charToGsm.put('5', i++); + charToGsm.put('6', i++); + charToGsm.put('7', i++); + charToGsm.put('8', i++); + charToGsm.put('9', i++); + charToGsm.put(':', i++); + charToGsm.put(';', i++); + charToGsm.put('<', i++); + charToGsm.put('=', i++); + charToGsm.put('>', i++); + charToGsm.put('?', i++); + + charToGsm.put('\u00a1', i++); + charToGsm.put('A', i++); + charToGsm.put('B', i++); + charToGsm.put('C', i++); + charToGsm.put('D', i++); + charToGsm.put('E', i++); + charToGsm.put('F', i++); + charToGsm.put('G', i++); + charToGsm.put('H', i++); + charToGsm.put('I', i++); + charToGsm.put('J', i++); + charToGsm.put('K', i++); + charToGsm.put('L', i++); + charToGsm.put('M', i++); + charToGsm.put('N', i++); + charToGsm.put('O', i++); + + charToGsm.put('P', i++); + charToGsm.put('Q', i++); + charToGsm.put('R', i++); + charToGsm.put('S', i++); + charToGsm.put('T', i++); + charToGsm.put('U', i++); + charToGsm.put('V', i++); + charToGsm.put('W', i++); + charToGsm.put('X', i++); + charToGsm.put('Y', i++); + charToGsm.put('Z', i++); + charToGsm.put('\u00c4', i++); + charToGsm.put('\u00d6', i++); + charToGsm.put('\u0147', i++); + charToGsm.put('\u00dc', i++); + charToGsm.put('\u00a7', i++); + + charToGsm.put('\u00bf', i++); + charToGsm.put('a', i++); + charToGsm.put('b', i++); + charToGsm.put('c', i++); + charToGsm.put('d', i++); + charToGsm.put('e', i++); + charToGsm.put('f', i++); + charToGsm.put('g', i++); + charToGsm.put('h', i++); + charToGsm.put('i', i++); + charToGsm.put('j', i++); + charToGsm.put('k', i++); + charToGsm.put('l', i++); + charToGsm.put('m', i++); + charToGsm.put('n', i++); + charToGsm.put('o', i++); + + charToGsm.put('p', i++); + charToGsm.put('q', i++); + charToGsm.put('r', i++); + charToGsm.put('s', i++); + charToGsm.put('t', i++); + charToGsm.put('u', i++); + charToGsm.put('v', i++); + charToGsm.put('w', i++); + charToGsm.put('x', i++); + charToGsm.put('y', i++); + charToGsm.put('z', i++); + charToGsm.put('\u00e4', i++); + charToGsm.put('\u00f6', i++); + charToGsm.put('\u00f1', i++); + charToGsm.put('\u00fc', i++); + charToGsm.put('\u00e0', i++); + + + charToGsmExtended.put('\f', 10); + charToGsmExtended.put('^', 20); + charToGsmExtended.put('{', 40); + charToGsmExtended.put('}', 41); + charToGsmExtended.put('\\', 47); + charToGsmExtended.put('[', 60); + charToGsmExtended.put('~', 61); + charToGsmExtended.put(']', 62); + charToGsmExtended.put('|', 64); + charToGsmExtended.put('\u20ac', 101); + + int size = charToGsm.size(); + for (int j=0; jThe following code snippet demonstrates a static method to + * retrieve the IIccPhoneBook interface from Android:

+ *
private static IIccPhoneBook getSimPhoneBookInterface()
+            throws DeadObjectException {
+    IServiceManager sm = ServiceManagerNative.getDefault();
+    IIccPhoneBook spb;
+    spb = IIccPhoneBook.Stub.asInterface(sm.getService("iccphonebook"));
+    return spb;
+}
+ * 
+ */ + +interface IIccPhoneBook { + + /** + * Loads the AdnRecords in efid and returns them as a + * List of AdnRecords + * + * @param efid the EF id of a ADN-like SIM + * @return List of AdnRecord + */ + List getAdnRecordsInEf(int efid); + + /** + * Replace oldAdn with newAdn in ADN-like record in EF + * + * getAdnRecordsInEf must be called at least once before this function, + * otherwise an error will be returned + * + * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN + * @param oldTag adn tag to be replaced + * @param oldPhoneNumber adn number to be replaced + * Set both oldTag and oldPhoneNubmer to "" means to replace an + * empty record, aka, insert new record + * @param newTag adn tag to be stored + * @param newPhoneNumber adn number ot be stored + * Set both newTag and newPhoneNubmer to "" means to replace the old + * record with empty one, aka, delete old record + * @param pin2 required to update EF_FDN, otherwise must be null + * @return true for success + */ + boolean updateAdnRecordsInEfBySearch(int efid, + String oldTag, String oldPhoneNumber, + String newTag, String newPhoneNumber, + String pin2); + + /** + * Update an ADN-like EF record by record index + * + * This is useful for iteration the whole ADN file, such as write the whole + * phone book or erase/format the whole phonebook + * + * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN + * @param newTag adn tag to be stored + * @param newPhoneNumber adn number to be stored + * Set both newTag and newPhoneNubmer to "" means to replace the old + * record with empty one, aka, delete old record + * @param index is 1-based adn record index to be updated + * @param pin2 required to update EF_FDN, otherwise must be null + * @return true for success + */ + boolean updateAdnRecordsInEfByIndex(int efid, String newTag, + String newPhoneNumber, int index, + String pin2); + + /** + * Get the max munber of records in efid + * + * @param efid the EF id of a ADN-like SIM + * @return int[3] array + * recordSizes[0] is the single record length + * recordSizes[1] is the total length of the EF file + * recordSizes[2] is the number of records in the EF file + */ + int[] getAdnRecordsSize(int efid); + +} diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl index 00cbaf9..e74b9e4 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl @@ -39,9 +39,9 @@ interface IPhoneSubInfo { String getSubscriberId(); /** - * Retrieves the serial number of the SIM, if applicable. + * Retrieves the serial number of the ICC, if applicable. */ - String getSimSerialNumber(); + String getIccSerialNumber(); /** * Retrieves the phone number string for line 1. @@ -53,13 +53,13 @@ interface IPhoneSubInfo { */ String getLine1AlphaTag(); - /** - * Retrieves the voice mail number. - */ + /** + * Retrieves the voice mail number. + */ String getVoiceMailNumber(); - /** - * Retrieves the alpha identifier associated with the voice mail number. - */ + /** + * Retrieves the alpha identifier associated with the voice mail number. + */ String getVoiceMailAlphaTag(); } diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl new file mode 100644 index 0000000..257f1e6 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -0,0 +1,115 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.internal.telephony; + +import android.app.PendingIntent; +import com.android.internal.telephony.SmsRawData; + +/** Interface for applications to access the ICC phone book. + * + *

The following code snippet demonstrates a static method to + * retrieve the ISms interface from Android:

+ *
private static ISms getSmsInterface()
+            throws DeadObjectException {
+    IServiceManager sm = ServiceManagerNative.getDefault();
+    ISms ss;
+    ss = ISms.Stub.asInterface(sm.getService("isms"));
+    return ss;
+}
+ * 
+ */ + +interface ISms { + /** + * Retrieves all messages currently stored on ICC. + * + * @return list of SmsRawData of all sms on ICC + */ + List getAllMessagesFromIccEf(); + + /** + * Update the specified message on the ICC. + * + * @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 success or not + * + */ + boolean updateMessageOnIccEf(int messageIndex, int newStatus, + in byte[] pdu); + + /** + * Copy a raw SMS PDU to the ICC. + * + * @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 success or not + * + */ + boolean copyMessageToIccEf(int status, in byte[] pdu, in byte[] smsc); + + /** + * Send a SMS + * + * @param smsc the SMSC to send the message through, or NULL for the + * default SMSC + * @param pdu the raw PDU to send + * @param sentIntent if not NULL this Intent is + * broadcast when the message is successfully sent, or failed. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * @param deliveryIntent if not NULL this Intent is + * broadcast when the message is delivered to the recipient. The + * raw pdu of the status report is in the extended data ("pdu"). + */ + void sendRawPdu(in byte[] smsc, in byte[] pdu, in PendingIntent sentIntent, + in PendingIntent deliveryIntent); + + /** + * Send a multi-part 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 parts an ArrayList of strings that, in order, + * comprise the original message + * @param sentIntents if not null, an ArrayList of + * PendingIntents (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * @param deliveryIntents if not null, an ArrayList of + * PendingIntents (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"). + */ + void sendMultipartText(in String destinationAddress, in String scAddress, + in List parts, in List sentIntents, + in List deliveryIntents); + +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 2b4195b..bab0603 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -21,7 +21,7 @@ import java.util.List; import android.telephony.NeighboringCellInfo; /** - * Interface used to interact with the phone. Mostly this is used by the + * Interface used to interact with the phone. Mostly this is used by the * TelephonyManager class. A few places are still using this directly. * Please clean them up if possible and use TelephonyManager insteadl. * @@ -135,7 +135,7 @@ interface ITelephony { /** * Cancels the missed calls notification. */ - void cancelMissedCallsNotification(); + void cancelMissedCallsNotification(); /** * Supply a pin to unlock the SIM. Blocks until a result is determined. @@ -147,7 +147,7 @@ interface ITelephony { /** * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated * without SEND (so dial is not appropriate). - * + * * @param dialString the MMI command to be executed. * @return true if MMI command is executed. */ @@ -213,4 +213,13 @@ interface ITelephony { int getCallState(); int getDataActivity(); int getDataState(); + + /** + * Returns the current active phone type as integer. + * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE + * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE + */ + int getActivePhoneType(); + } + diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java new file mode 100644 index 0000000..d7ad492 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccCard.java @@ -0,0 +1,207 @@ +/* + * 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.Message; +import android.os.Handler; + +/** + * {@hide} + */ +public interface IccCard { + /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */ + static public final String INTENT_KEY_ICC_STATE = "ss"; + /* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */ + static public final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY"; + /* ABSENT means ICC is missing */ + static public final String INTENT_VALUE_ICC_ABSENT = "ABSENT"; + /* LOCKED means ICC is locked by pin or by network */ + static public final String INTENT_VALUE_ICC_LOCKED = "LOCKED"; + /* READY means ICC is ready to access */ + static public final String INTENT_VALUE_ICC_READY = "READY"; + /* IMSI means ICC IMSI is ready in property */ + static public final String INTENT_VALUE_ICC_IMSI = "IMSI"; + /* LOADED means all ICC records, including IMSI, are loaded */ + static public final String INTENT_VALUE_ICC_LOADED = "LOADED"; + /* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */ + static public final String INTENT_KEY_LOCKED_REASON = "reason"; + /* PIN means ICC is locked on PIN1 */ + static public final String INTENT_VALUE_LOCKED_ON_PIN = "PIN"; + /* PUK means ICC is locked on PUK1 */ + static public final String INTENT_VALUE_LOCKED_ON_PUK = "PUK"; + /* NETWORK means ICC is locked on NETWORK PERSONALIZATION */ + static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK"; + + + /* + UNKNOWN is a transient state, for example, after uesr inputs ICC pin under + PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it + turns to READY + */ + public enum State { + UNKNOWN, + ABSENT, + PIN_REQUIRED, + PUK_REQUIRED, + NETWORK_LOCKED, + READY; + + public boolean isPinLocked() { + return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)); + } + } + + State getState(); + + + /** + * Notifies handler of any transition into State.ABSENT + */ + void registerForAbsent(Handler h, int what, Object obj); + void unregisterForAbsent(Handler h); + + /** + * Notifies handler of any transition into State.isPinLocked() + */ + void registerForLocked(Handler h, int what, Object obj); + void unregisterForLocked(Handler h); + + /** + * Notifies handler of any transition into State.NETWORK_LOCKED + */ + void registerForNetworkLocked(Handler h, int what, Object obj); + void unregisterForNetworkLocked(Handler h); + + /** + * Supply the ICC PIN to the ICC + * + * When the operation is complete, onComplete will be sent to it's + * Handler. + * + * onComplete.obj will be an AsyncResult + * + * ((AsyncResult)onComplete.obj).exception == null on success + * ((AsyncResult)onComplete.obj).exception != null on fail + * + * If the supplied PIN is incorrect: + * ((AsyncResult)onComplete.obj).exception != null + * && ((AsyncResult)onComplete.obj).exception + * instanceof com.android.internal.telephony.gsm.CommandException) + * && ((CommandException)(((AsyncResult)onComplete.obj).exception)) + * .getCommandError() == CommandException.Error.PASSWORD_INCORRECT + * + * + */ + + void supplyPin (String pin, Message onComplete); + void supplyPuk (String puk, String newPin, Message onComplete); + void supplyPin2 (String pin2, Message onComplete); + void supplyPuk2 (String puk2, String newPin2, Message onComplete); + + /** + * Check whether ICC pin lock is enabled + * This is a sync call which returns the cached pin enabled state + * + * @return true for ICC locked enabled + * false for ICC locked disabled + */ + boolean getIccLockEnabled (); + + /** + * Set the ICC pin lock enabled or disabled + * When the operation is complete, onComplete will be sent to its handler + * + * @param enabled "true" for locked "false" for unlocked. + * @param password needed to change the ICC pin state, aka. Pin1 + * @param onComplete + * onComplete.obj will be an AsyncResult + * ((AsyncResult)onComplete.obj).exception == null on success + * ((AsyncResult)onComplete.obj).exception != null on fail + */ + void setIccLockEnabled(boolean enabled, String password, Message onComplete); + + + /** + * Change the ICC password used in ICC pin lock + * When the operation is complete, onComplete will be sent to its handler + * + * @param oldPassword is the old password + * @param newPassword is the new password + * @param onComplete + * onComplete.obj will be an AsyncResult + * ((AsyncResult)onComplete.obj).exception == null on success + * ((AsyncResult)onComplete.obj).exception != null on fail + */ + void changeIccLockPassword(String oldPassword, String newPassword, + Message onComplete); + + /** + * Check whether ICC fdn (fixed dialing number) is enabled + * This is a sync call which returns the cached pin enabled state + * + * @return true for ICC fdn enabled + * false for ICC fdn disabled + */ + boolean getIccFdnEnabled (); + + /** + * Set the ICC fdn enabled or disabled + * When the operation is complete, onComplete will be sent to its handler + * + * @param enabled "true" for locked "false" for unlocked. + * @param password needed to change the ICC fdn enable, aka Pin2 + * @param onComplete + * onComplete.obj will be an AsyncResult + * ((AsyncResult)onComplete.obj).exception == null on success + * ((AsyncResult)onComplete.obj).exception != null on fail + */ + void setIccFdnEnabled(boolean enabled, String password, Message onComplete); + + /** + * Change the ICC password used in ICC fdn enable + * When the operation is complete, onComplete will be sent to its handler + * + * @param oldPassword is the old password + * @param newPassword is the new password + * @param onComplete + * onComplete.obj will be an AsyncResult + * ((AsyncResult)onComplete.obj).exception == null on success + * ((AsyncResult)onComplete.obj).exception != null on fail + */ + void changeIccFdnPassword(String oldPassword, String newPassword, + Message onComplete); + + void supplyNetworkDepersonalization (String pin, Message onComplete); + + /** + * Returns service provider name stored in ICC card. + * If there is no service provider name associated or the record is not + * yet available, null will be returned

+ * + * Please use this value when display Service Provider Name in idle mode

+ * + * Usage of this provider name in the UI is a common carrier requirement. + * + * Also available via Android property "gsm.sim.operator.alpha" + * + * @return Service Provider Name stored in ICC card + * null if no service provider name associated or the record is not + * yet available + * + */ + String getServiceProviderName(); +} diff --git a/telephony/java/com/android/internal/telephony/IccCardApplication.java b/telephony/java/com/android/internal/telephony/IccCardApplication.java new file mode 100644 index 0000000..9f60a6c --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccCardApplication.java @@ -0,0 +1,178 @@ +/* + * 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; + + +/** + * See also RIL_AppStatus in include/telephony/ril.h + * + * {@hide} + */ +public class IccCardApplication { + public enum AppType{ + APPTYPE_UNKNOWN, + APPTYPE_SIM, + APPTYPE_USIM, + APPTYPE_RUIM, + APPTYPE_CSIM + }; + + public enum AppState{ + APPSTATE_UNKNOWN, + APPSTATE_DETECTED, + APPSTATE_PIN, + APPSTATE_PUK, + APPSTATE_SUBSCRIPTION_PERSO, + APPSTATE_READY; + + boolean isPinRequired() { + return this == APPSTATE_PIN; + } + + boolean isPukRequired() { + return this == APPSTATE_PUK; + } + + boolean isSubscriptionPersoEnabled() { + return this == APPSTATE_SUBSCRIPTION_PERSO; + } + + boolean isAppReady() { + return this == APPSTATE_READY; + } + + boolean isAppNotReady() { + return this == APPSTATE_UNKNOWN || + this == APPSTATE_DETECTED; + } + }; + + public enum PersoSubState{ + PERSOSUBSTATE_UNKNOWN, + PERSOSUBSTATE_IN_PROGRESS, + PERSOSUBSTATE_READY, + PERSOSUBSTATE_SIM_NETWORK, + PERSOSUBSTATE_SIM_NETWORK_SUBSET, + PERSOSUBSTATE_SIM_CORPORATE, + PERSOSUBSTATE_SIM_SERVICE_PROVIDER, + PERSOSUBSTATE_SIM_SIM, + PERSOSUBSTATE_SIM_NETWORK_PUK, + PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK, + PERSOSUBSTATE_SIM_CORPORATE_PUK, + PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK, + PERSOSUBSTATE_SIM_SIM_PUK, + PERSOSUBSTATE_RUIM_NETWORK1, + PERSOSUBSTATE_RUIM_NETWORK2, + PERSOSUBSTATE_RUIM_HRPD, + PERSOSUBSTATE_RUIM_CORPORATE, + PERSOSUBSTATE_RUIM_SERVICE_PROVIDER, + PERSOSUBSTATE_RUIM_RUIM, + PERSOSUBSTATE_RUIM_NETWORK1_PUK, + PERSOSUBSTATE_RUIM_NETWORK2_PUK, + PERSOSUBSTATE_RUIM_HRPD_PUK, + PERSOSUBSTATE_RUIM_CORPORATE_PUK, + PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK, + PERSOSUBSTATE_RUIM_RUIM_PUK; + + boolean isPersoSubStateUnknown() { + return this == PERSOSUBSTATE_UNKNOWN; + } + }; + + public AppType app_type; + public AppState app_state; + // applicable only if app_state == RIL_APPSTATE_SUBSCRIPTION_PERSO + public PersoSubState perso_substate; + // null terminated string, e.g., from 0xA0, 0x00 -> 0x41, 0x30, 0x30, 0x30 */ + public String aid; + // null terminated string + public String app_label; + // applicable to USIM and CSIM + public int pin1_replaced; + public int pin1; + public int pin2; + + AppType AppTypeFromRILInt(int type) { + AppType newType; + /* RIL_AppType ril.h */ + switch(type) { + case 0: newType = AppType.APPTYPE_UNKNOWN; break; + case 1: newType = AppType.APPTYPE_SIM; break; + case 2: newType = AppType.APPTYPE_USIM; break; + case 3: newType = AppType.APPTYPE_RUIM; break; + case 4: newType = AppType.APPTYPE_CSIM; break; + default: + throw new RuntimeException( + "Unrecognized RIL_AppType: " +type); + } + return newType; + } + + AppState AppStateFromRILInt(int state) { + AppState newState; + /* RIL_AppState ril.h */ + switch(state) { + case 0: newState = AppState.APPSTATE_UNKNOWN; break; + case 1: newState = AppState.APPSTATE_DETECTED; break; + case 2: newState = AppState.APPSTATE_PIN; break; + case 3: newState = AppState.APPSTATE_PUK; break; + case 4: newState = AppState.APPSTATE_SUBSCRIPTION_PERSO; break; + case 5: newState = AppState.APPSTATE_READY; break; + default: + throw new RuntimeException( + "Unrecognized RIL_AppState: " +state); + } + return newState; + } + + PersoSubState PersoSubstateFromRILInt(int substate) { + PersoSubState newSubState; + /* RIL_PeroSubstate ril.h */ + switch(substate) { + case 0: newSubState = PersoSubState.PERSOSUBSTATE_UNKNOWN; break; + case 1: newSubState = PersoSubState.PERSOSUBSTATE_IN_PROGRESS; break; + case 2: newSubState = PersoSubState.PERSOSUBSTATE_READY; break; + case 3: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK; break; + case 4: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK_SUBSET; break; + case 5: newSubState = PersoSubState.PERSOSUBSTATE_SIM_CORPORATE; break; + case 6: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SERVICE_PROVIDER; break; + case 7: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SIM; break; + case 8: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK_PUK; break; + case 9: newSubState = PersoSubState.PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK; break; + case 10: newSubState = PersoSubState.PERSOSUBSTATE_SIM_CORPORATE_PUK; break; + case 11: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK; break; + case 12: newSubState = PersoSubState.PERSOSUBSTATE_SIM_SIM_PUK; break; + case 13: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK1; break; + case 14: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK2; break; + case 15: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_HRPD; break; + case 16: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_CORPORATE; break; + case 17: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_SERVICE_PROVIDER; break; + case 18: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_RUIM; break; + case 19: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK1_PUK; break; + case 20: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_NETWORK2_PUK; break; + case 21: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_HRPD_PUK ; break; + case 22: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_CORPORATE_PUK; break; + case 23: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK; break; + case 24: newSubState = PersoSubState.PERSOSUBSTATE_RUIM_RUIM_PUK; break; + default: + throw new RuntimeException( + "Unrecognized RIL_PersoSubstate: " +substate); + } + return newSubState; + } + +} diff --git a/telephony/java/com/android/internal/telephony/IccCardStatus.java b/telephony/java/com/android/internal/telephony/IccCardStatus.java new file mode 100644 index 0000000..b602b1c --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccCardStatus.java @@ -0,0 +1,86 @@ +/* + * 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; + +/** + * See also RIL_CardStatus in include/telephony/ril.h + * + * {@hide} + */ +public class IccCardStatus { + static final int CARD_MAX_APPS = 8; + + public enum CardState { + CARDSTATE_ABSENT, + CARDSTATE_PRESENT, + CARDSTATE_ERROR; + + boolean isCardPresent() { + return this == CARDSTATE_PRESENT; + } + }; + + public enum PinState { + PINSTATE_UNKNOWN, + PINSTATE_ENABLED_NOT_VERIFIED, + PINSTATE_ENABLED_VERIFIED, + PINSTATE_DISABLED, + PINSTATE_ENABLED_BLOCKED, + PINSTATE_ENABLED_PERM_BLOCKED + }; + + public CardState card_state; + public PinState universal_pin_state; + public int gsm_umts_subscription_app_index; + public int cdma_subscription_app_index; + public int num_applications; + + ArrayList application = new ArrayList(CARD_MAX_APPS); + + CardState CardStateFromRILInt(int state) { + CardState newState; + /* RIL_CardState ril.h */ + switch(state) { + case 0: newState = CardState.CARDSTATE_ABSENT; break; + case 1: newState = CardState.CARDSTATE_PRESENT; break; + case 2: newState = CardState.CARDSTATE_ERROR; break; + default: + throw new RuntimeException( + "Unrecognized RIL_CardState: " +state); + } + return newState; + } + + PinState PinStateFromRILInt(int state) { + PinState newState; + /* RIL_PinState ril.h */ + switch(state) { + case 0: newState = PinState.PINSTATE_UNKNOWN; break; + case 1: newState = PinState.PINSTATE_ENABLED_NOT_VERIFIED; break; + case 2: newState = PinState.PINSTATE_ENABLED_VERIFIED; break; + case 3: newState = PinState.PINSTATE_DISABLED; break; + case 4: newState = PinState.PINSTATE_ENABLED_BLOCKED; break; + case 5: newState = PinState.PINSTATE_ENABLED_PERM_BLOCKED; break; + default: + throw new RuntimeException( + "Unrecognized RIL_PinState: " +state); + } + return newState; + } +} diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java new file mode 100644 index 0000000..59ce5bb --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccConstants.java @@ -0,0 +1,59 @@ +/* + * 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 interface IccConstants { + // GSM SIM file ids from TS 51.011 + public static final int EF_ADN = 0x6F3A; + public static final int EF_FDN = 0x6F3B; + public static final int EF_SDN = 0x6F49; + public static final int EF_EXT1 = 0x6F4A; + public static final int EF_EXT2 = 0x6F4B; + public static final int EF_EXT3 = 0x6F4C; + public static final int EF_EXT6 = 0x6fc8; // Ext record for EF[MBDN] + public static final int EF_MWIS = 0x6FCA; + public static final int EF_MBDN = 0x6fc7; + public static final int EF_PNN = 0x6fc5; + public static final int EF_SPN = 0x6F46; + public static final int EF_SMS = 0x6F3C; + public static final int EF_ICCID = 0x2fe2; + public static final int EF_AD = 0x6FAD; + public static final int EF_MBI = 0x6fc9; + public static final int EF_MSISDN = 0x6f40; + public static final int EF_SPDI = 0x6fcd; + public static final int EF_SST = 0x6f38; + public static final int EF_CFIS = 0x6FCB; + public static final int EF_IMG = 0x4f20; + + // GSM SIM file ids from CPHS (phase 2, version 4.2) CPHS4_2.WW6 + public static final int EF_MAILBOX_CPHS = 0x6F17; + public static final int EF_VOICE_MAIL_INDICATOR_CPHS = 0x6F11; + public static final int EF_CFF_CPHS = 0x6F13; + public static final int EF_SPN_CPHS = 0x6f14; + public static final int EF_SPN_SHORT_CPHS = 0x6f18; + public static final int EF_INFO_CPHS = 0x6f16; + + // CDMA RUIM file ids from 3GPP2 C.S0023-0 + public static final int EF_CST = 0x6f32; + public static final int EF_RUIM_SPN =0x6F41; + + // SMS record length from TS 51.011 10.5.3 + static public final int SMS_RECORD_LENGTH = 176; +} diff --git a/telephony/java/com/android/internal/telephony/IccException.java b/telephony/java/com/android/internal/telephony/IccException.java new file mode 100644 index 0000000..1659a4e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccException.java @@ -0,0 +1,30 @@ +/* + * 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 IccException extends Exception { + public IccException() { + + } + + public IccException(String s) { + super(s); + } +} diff --git a/telephony/java/com/android/internal/telephony/IccFileHandler.java b/telephony/java/com/android/internal/telephony/IccFileHandler.java new file mode 100644 index 0000000..e751c5e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccFileHandler.java @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.os.*; +import android.util.Log; +import java.util.ArrayList; + +/** + * {@hide} + */ +public abstract class IccFileHandler extends Handler { + + //from TS 11.11 9.1 or elsewhere + static protected final int COMMAND_READ_BINARY = 0xb0; + static protected final int COMMAND_UPDATE_BINARY = 0xd6; + static protected final int COMMAND_READ_RECORD = 0xb2; + static protected final int COMMAND_UPDATE_RECORD = 0xdc; + static protected final int COMMAND_SEEK = 0xa2; + static protected final int COMMAND_GET_RESPONSE = 0xc0; + + // from TS 11.11 9.2.5 + static protected final int READ_RECORD_MODE_ABSOLUTE = 4; + + //***** types of files TS 11.11 9.3 + static protected final int EF_TYPE_TRANSPARENT = 0; + static protected final int EF_TYPE_LINEAR_FIXED = 1; + static protected final int EF_TYPE_CYCLIC = 3; + + //***** types of files TS 11.11 9.3 + static protected final int TYPE_RFU = 0; + static protected final int TYPE_MF = 1; + static protected final int TYPE_DF = 2; + static protected final int TYPE_EF = 4; + + // size of GET_RESPONSE for EF's + static protected final int GET_RESPONSE_EF_SIZE_BYTES = 15; + static protected final int GET_RESPONSE_EF_IMG_SIZE_BYTES = 10; + + // Byte order received in response to COMMAND_GET_RESPONSE + // Refer TS 51.011 Section 9.2.1 + static protected final int RESPONSE_DATA_RFU_1 = 0; + static protected final int RESPONSE_DATA_RFU_2 = 1; + + static protected final int RESPONSE_DATA_FILE_SIZE_1 = 2; + static protected final int RESPONSE_DATA_FILE_SIZE_2 = 3; + + static protected final int RESPONSE_DATA_FILE_ID_1 = 4; + static protected final int RESPONSE_DATA_FILE_ID_2 = 5; + static protected final int RESPONSE_DATA_FILE_TYPE = 6; + static protected final int RESPONSE_DATA_RFU_3 = 7; + static protected final int RESPONSE_DATA_ACCESS_CONDITION_1 = 8; + static protected final int RESPONSE_DATA_ACCESS_CONDITION_2 = 9; + static protected final int RESPONSE_DATA_ACCESS_CONDITION_3 = 10; + static protected final int RESPONSE_DATA_FILE_STATUS = 11; + static protected final int RESPONSE_DATA_LENGTH = 12; + static protected final int RESPONSE_DATA_STRUCTURE = 13; + static protected final int RESPONSE_DATA_RECORD_LENGTH = 14; + + + //***** Events + + /** Finished retrieving size of transparent EF; start loading. */ + static protected final int EVENT_GET_BINARY_SIZE_DONE = 4; + /** Finished loading contents of transparent EF; post result. */ + static protected final int EVENT_READ_BINARY_DONE = 5; + /** Finished retrieving size of records for linear-fixed EF; now load. */ + static protected final int EVENT_GET_RECORD_SIZE_DONE = 6; + /** Finished loading single record from a linear-fixed EF; post result. */ + static protected final int EVENT_READ_RECORD_DONE = 7; + /** Finished retrieving record size; post result. */ + static protected final int EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE = 8; + /** Finished retrieving image instance record; post result. */ + static protected final int EVENT_READ_IMG_DONE = 9; + /** Finished retrieving icon data; post result. */ + static protected final int EVENT_READ_ICON_DONE = 10; + + // member variables + protected PhoneBase phone; + + static class LoadLinearFixedContext { + + int efid; + int recordNum, recordSize, countRecords; + boolean loadAll; + + Message onLoaded; + + ArrayList results; + + LoadLinearFixedContext(int efid, int recordNum, Message onLoaded) { + this.efid = efid; + this.recordNum = recordNum; + this.onLoaded = onLoaded; + this.loadAll = false; + } + + LoadLinearFixedContext(int efid, Message onLoaded) { + this.efid = efid; + this.recordNum = 1; + this.loadAll = true; + this.onLoaded = onLoaded; + } + } + + /** + * Default constructor + */ + protected IccFileHandler(PhoneBase phone) { + super(); + this.phone = phone; + } + + public void dispose() { + } + + //***** Public Methods + + /** + * Load a record from a SIM Linear Fixed EF + * + * @param fileid EF id + * @param recordNum 1-based (not 0-based) record number + * @param onLoaded + * + * ((AsyncResult)(onLoaded.obj)).result is the byte[] + * + */ + public void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) { + Message response + = obtainMessage(EVENT_GET_RECORD_SIZE_DONE, + new LoadLinearFixedContext(fileid, recordNum, onLoaded)); + + phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, null, + 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); + } + + /** + * Load a image instance record from a SIM Linear Fixed EF-IMG + * + * @param recordNum 1-based (not 0-based) record number + * @param onLoaded + * + * ((AsyncResult)(onLoaded.obj)).result is the byte[] + * + */ + public void loadEFImgLinearFixed(int recordNum, Message onLoaded) { + Message response = obtainMessage(EVENT_READ_IMG_DONE, + new LoadLinearFixedContext(IccConstants.EF_IMG, recordNum, + onLoaded)); + + phone.mCM.iccIO(COMMAND_GET_RESPONSE, IccConstants.EF_IMG, "img", + recordNum, READ_RECORD_MODE_ABSOLUTE, + GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null, response); + } + + /** + * get record size for a linear fixed EF + * + * @param fileid EF id + * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[] + * int[0] is the record length int[1] is the total length of the EF + * file int[3] is the number of records in the EF file So int[0] * + * int[3] = int[1] + */ + public void getEFLinearRecordSize(int fileid, Message onLoaded) { + Message response + = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE, + new LoadLinearFixedContext(fileid, onLoaded)); + phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, null, + 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); + } + + /** + * Load all records from a SIM Linear Fixed EF + * + * @param fileid EF id + * @param onLoaded + * + * ((AsyncResult)(onLoaded.obj)).result is an ArrayList + * + */ + public void loadEFLinearFixedAll(int fileid, Message onLoaded) { + Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE, + new LoadLinearFixedContext(fileid,onLoaded)); + + phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, null, + 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); + } + + /** + * Load a SIM Transparent EF + * + * @param fileid EF id + * @param onLoaded + * + * ((AsyncResult)(onLoaded.obj)).result is the byte[] + * + */ + + public void loadEFTransparent(int fileid, Message onLoaded) { + Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE, + fileid, 0, onLoaded); + + phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, null, + 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); + } + + /** + * Load a SIM Transparent EF-IMG. Used right after loadEFImgLinearFixed to + * retrive STK's icon data. + * + * @param fileid EF id + * @param onLoaded + * + * ((AsyncResult)(onLoaded.obj)).result is the byte[] + * + */ + public void loadEFImgTransparent(int fileid, int highOffset, int lowOffset, + int length, Message onLoaded) { + Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0, + onLoaded); + + phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset, + length, null, null, response); + } + + /** + * Update a record in a linear fixed EF + * @param fileid EF id + * @param recordNum 1-based (not 0-based) record number + * @param data must be exactly as long as the record in the EF + * @param pin2 for CHV2 operations, otherwist must be null + * @param onComplete onComplete.obj will be an AsyncResult + * onComplete.obj.userObj will be a IccIoResult on success + */ + public void updateEFLinearFixed(int fileid, int recordNum, byte[] data, + String pin2, Message onComplete) { + phone.mCM.iccIO(COMMAND_UPDATE_RECORD, fileid, null, + recordNum, READ_RECORD_MODE_ABSOLUTE, data.length, + IccUtils.bytesToHexString(data), pin2, onComplete); + } + + /** + * Update a transparent EF + * @param fileid EF id + * @param data must be exactly as long as the EF + */ + public void updateEFTransparent(int fileid, byte[] data, Message onComplete) { + phone.mCM.iccIO(COMMAND_UPDATE_BINARY, fileid, null, + 0, 0, data.length, + IccUtils.bytesToHexString(data), null, onComplete); + } + + + //***** Abstract Methods + + + //***** Private Methods + + private void sendResult(Message response, Object result, Throwable ex) { + if (response == null) { + return; + } + + AsyncResult.forMessage(response, result, ex); + + response.sendToTarget(); + } + + //***** Overridden from Handler + + public void handleMessage(Message msg) { + AsyncResult ar; + IccIoResult result; + Message response = null; + String str; + LoadLinearFixedContext lc; + + IccException iccException; + byte data[]; + int size; + int fileid; + int recordNum; + int recordSize[]; + + try { + switch (msg.what) { + case EVENT_READ_IMG_DONE: + ar = (AsyncResult) msg.obj; + lc = (LoadLinearFixedContext) ar.userObj; + result = (IccIoResult) ar.result; + response = lc.onLoaded; + + iccException = result.getException(); + if (iccException != null) { + sendResult(response, result.payload, ar.exception); + } + break; + case EVENT_READ_ICON_DONE: + ar = (AsyncResult) msg.obj; + response = (Message) ar.userObj; + result = (IccIoResult) ar.result; + + iccException = result.getException(); + if (iccException != null) { + sendResult(response, result.payload, ar.exception); + } + break; + case EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE: + ar = (AsyncResult)msg.obj; + lc = (LoadLinearFixedContext) ar.userObj; + result = (IccIoResult) ar.result; + response = lc.onLoaded; + + if (ar.exception != null) { + sendResult(response, null, ar.exception); + break; + } + + iccException = result.getException(); + if (iccException != null) { + sendResult(response, null, iccException); + break; + } + + data = result.payload; + + if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE] || + EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) { + throw new IccFileTypeMismatch(); + } + + recordSize = new int[3]; + recordSize[0] = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF; + recordSize[1] = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) + + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); + recordSize[2] = recordSize[1] / recordSize[0]; + + sendResult(response, recordSize, null); + break; + case EVENT_GET_RECORD_SIZE_DONE: + ar = (AsyncResult)msg.obj; + lc = (LoadLinearFixedContext) ar.userObj; + result = (IccIoResult) ar.result; + response = lc.onLoaded; + + if (ar.exception != null) { + sendResult(response, null, ar.exception); + break; + } + + iccException = result.getException(); + + if (iccException != null) { + sendResult(response, null, iccException); + break; + } + + data = result.payload; + fileid = lc.efid; + recordNum = lc.recordNum; + + if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) { + throw new IccFileTypeMismatch(); + } + + if (EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) { + throw new IccFileTypeMismatch(); + } + + lc.recordSize = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF; + + size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) + + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); + + lc.countRecords = size / lc.recordSize; + + if (lc.loadAll) { + lc.results = new ArrayList(lc.countRecords); + } + + phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, null, + lc.recordNum, + READ_RECORD_MODE_ABSOLUTE, + lc.recordSize, null, null, + obtainMessage(EVENT_READ_RECORD_DONE, lc)); + break; + case EVENT_GET_BINARY_SIZE_DONE: + ar = (AsyncResult)msg.obj; + response = (Message) ar.userObj; + result = (IccIoResult) ar.result; + + if (ar.exception != null) { + sendResult(response, null, ar.exception); + break; + } + + iccException = result.getException(); + + if (iccException != null) { + sendResult(response, null, iccException); + break; + } + + data = result.payload; + + fileid = msg.arg1; + + if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) { + throw new IccFileTypeMismatch(); + } + + if (EF_TYPE_TRANSPARENT != data[RESPONSE_DATA_STRUCTURE]) { + throw new IccFileTypeMismatch(); + } + + size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) + + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); + + phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, null, + 0, 0, size, null, null, + obtainMessage(EVENT_READ_BINARY_DONE, + fileid, 0, response)); + break; + + case EVENT_READ_RECORD_DONE: + + ar = (AsyncResult)msg.obj; + lc = (LoadLinearFixedContext) ar.userObj; + result = (IccIoResult) ar.result; + response = lc.onLoaded; + + if (ar.exception != null) { + sendResult(response, null, ar.exception); + break; + } + + iccException = result.getException(); + + if (iccException != null) { + sendResult(response, null, iccException); + break; + } + + if (!lc.loadAll) { + sendResult(response, result.payload, null); + } else { + lc.results.add(result.payload); + + lc.recordNum++; + + if (lc.recordNum > lc.countRecords) { + sendResult(response, lc.results, null); + } else { + phone.mCM.iccIO(COMMAND_READ_RECORD, lc.efid, null, + lc.recordNum, + READ_RECORD_MODE_ABSOLUTE, + lc.recordSize, null, null, + obtainMessage(EVENT_READ_RECORD_DONE, lc)); + } + } + + break; + + case EVENT_READ_BINARY_DONE: + ar = (AsyncResult)msg.obj; + response = (Message) ar.userObj; + result = (IccIoResult) ar.result; + + if (ar.exception != null) { + sendResult(response, null, ar.exception); + break; + } + + iccException = result.getException(); + + if (iccException != null) { + sendResult(response, null, iccException); + break; + } + + sendResult(response, result.payload, null); + break; + + }} catch (Exception exc) { + if (response != null) { + sendResult(response, null, exc); + } else { + loge("uncaught exception" + exc); + } + } + } + + protected abstract void logd(String s); + + protected abstract void loge(String s); + +} diff --git a/telephony/java/com/android/internal/telephony/IccFileNotFound.java b/telephony/java/com/android/internal/telephony/IccFileNotFound.java new file mode 100644 index 0000000..915cea6 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccFileNotFound.java @@ -0,0 +1,34 @@ +/* + * 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 IccFileNotFound extends IccException { + IccFileNotFound() { + + } + + IccFileNotFound(String s) { + super(s); + } + + IccFileNotFound(int ef) { + super("ICC EF Not Found 0x" + Integer.toHexString(ef)); + } +} diff --git a/telephony/java/com/android/internal/telephony/IccFileTypeMismatch.java b/telephony/java/com/android/internal/telephony/IccFileTypeMismatch.java new file mode 100644 index 0000000..66fcfa9 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccFileTypeMismatch.java @@ -0,0 +1,30 @@ +/* + * 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 IccFileTypeMismatch extends IccException { + public IccFileTypeMismatch() { + + } + + public IccFileTypeMismatch(String s) { + super(s); + } +} diff --git a/telephony/java/com/android/internal/telephony/IccIoResult.java b/telephony/java/com/android/internal/telephony/IccIoResult.java new file mode 100644 index 0000000..a6e0ec3 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccIoResult.java @@ -0,0 +1,70 @@ +/* + * 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 +IccIoResult { + int sw1; + int sw2; + + public byte[] payload; + + public IccIoResult(int sw1, int sw2, byte[] payload) { + this.sw1 = sw1; + this.sw2 = sw2; + this.payload = payload; + } + + public IccIoResult(int sw1, int sw2, String hexString) { + this(sw1, sw2, IccUtils.hexStringToBytes(hexString)); + } + + public String toString() { + return "IccIoResponse sw1:0x" + Integer.toHexString(sw1) + " sw2:0x" + + Integer.toHexString(sw2); + } + + /** + * true if this operation was successful + * See GSM 11.11 Section 9.4 + * (the fun stuff is absent in 51.011) + */ + public boolean success() { + return sw1 == 0x90 || sw1 == 0x91 || sw1 == 0x9e || sw1 == 0x9f; + } + + /** + * Returns exception on error or null if success + */ + public IccException getException() { + if (success()) return null; + + switch (sw1) { + case 0x94: + if (sw2 == 0x08) { + return new IccFileTypeMismatch(); + } else { + return new IccFileNotFound(); + } + default: + return new IccException("sw1:" + sw1 + " sw2:" + sw2); + } + } +} diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java new file mode 100644 index 0000000..0bcaaa6 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java @@ -0,0 +1,266 @@ +/* + * 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.content.pm.PackageManager; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.ServiceManager; +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * SimPhoneBookInterfaceManager to provide an inter-process communication to + * access ADN-like SIM records. + */ +public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { + protected static final boolean DBG = true; + + protected PhoneBase phone; + protected AdnRecordCache adnCache; + protected Object mLock = new Object(); + protected int recordSize[]; + protected boolean success; + protected List records; + + protected static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false; + + protected static final int EVENT_GET_SIZE_DONE = 1; + protected static final int EVENT_LOAD_DONE = 2; + protected static final int EVENT_UPDATE_DONE = 3; + + protected Handler mBaseHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + + switch (msg.what) { + case EVENT_GET_SIZE_DONE: + ar = (AsyncResult) msg.obj; + synchronized (mLock) { + if (ar.exception == null) { + recordSize = (int[])ar.result; + // recordSize[0] is the record length + // recordSize[1] is the total length of the EF file + // recordSize[2] is the number of records in the EF file + logd("GET_RECORD_SIZE Size " + recordSize[0] + + " total " + recordSize[1] + + " #record " + recordSize[2]); + mLock.notifyAll(); + } + } + break; + case EVENT_UPDATE_DONE: + ar = (AsyncResult) msg.obj; + synchronized (mLock) { + success = (ar.exception == null); + mLock.notifyAll(); + } + break; + case EVENT_LOAD_DONE: + ar = (AsyncResult)msg.obj; + synchronized (mLock) { + if (ar.exception == null) { + records = (List) + ((ArrayList) ar.result); + } else { + if(DBG) logd("Cannot load ADN records"); + if (records != null) { + records.clear(); + } + } + mLock.notifyAll(); + } + break; + } + } + }; + + public IccPhoneBookInterfaceManager(PhoneBase phone) { + this.phone = phone; + } + + public void dispose() { + } + + protected void publish() { + //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy + ServiceManager.addService("simphonebook", this); + } + + protected abstract void logd(String msg); + + protected abstract void loge(String msg); + + /** + * Replace oldAdn with newAdn in ADN-like record in EF + * + * getAdnRecordsInEf must be called at least once before this function, + * otherwise an error will be returned + * throws SecurityException if no WRITE_CONTACTS permission + * + * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN + * @param oldTag adn tag to be replaced + * @param oldPhoneNumber adn number to be replaced + * Set both oldTag and oldPhoneNubmer to "" means to replace an + * empty record, aka, insert new record + * @param newTag adn tag to be stored + * @param newPhoneNumber adn number ot be stored + * Set both newTag and newPhoneNubmer to "" means to replace the old + * record with empty one, aka, delete old record + * @param pin2 required to update EF_FDN, otherwise must be null + * @return true for success + */ + public boolean + updateAdnRecordsInEfBySearch (int efid, + String oldTag, String oldPhoneNumber, + String newTag, String newPhoneNumber, String pin2) { + + + if (phone.getContext().checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_CONTACTS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "Requires android.permission.WRITE_CONTACTS permission"); + } + + + if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid + + " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" + + " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); + synchronized(mLock) { + checkThread(); + success = false; + Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE); + AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber); + AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); + adnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response); + try { + mLock.wait(); + } catch (InterruptedException e) { + logd("interrupted while trying to update by search"); + } + } + return success; + } + + /** + * Update an ADN-like EF record by record index + * + * This is useful for iteration the whole ADN file, such as write the whole + * phone book or erase/format the whole phonebook + * throws SecurityException if no WRITE_CONTACTS permission + * + * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN + * @param newTag adn tag to be stored + * @param newPhoneNumber adn number to be stored + * Set both newTag and newPhoneNubmer to "" means to replace the old + * record with empty one, aka, delete old record + * @param index is 1-based adn record index to be updated + * @param pin2 required to update EF_FDN, otherwise must be null + * @return true for success + */ + public boolean + updateAdnRecordsInEfByIndex(int efid, String newTag, + String newPhoneNumber, int index, String pin2) { + + if (phone.getContext().checkCallingOrSelfPermission( + android.Manifest.permission.WRITE_CONTACTS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "Requires android.permission.WRITE_CONTACTS permission"); + } + + if (DBG) logd("updateAdnRecordsInEfByIndex: efid=" + efid + + " Index=" + index + " ==> " + + "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); + synchronized(mLock) { + checkThread(); + success = false; + Message response = mBaseHandler.obtainMessage(EVENT_UPDATE_DONE); + AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); + adnCache.updateAdnByIndex(efid, newAdn, index, pin2, response); + try { + mLock.wait(); + } catch (InterruptedException e) { + logd("interrupted while trying to update by index"); + } + } + return success; + } + + /** + * Get the capacity of records in efid + * + * @param efid the EF id of a ADN-like ICC + * @return int[3] array + * recordSizes[0] is the single record length + * recordSizes[1] is the total length of the EF file + * recordSizes[2] is the number of records in the EF file + */ + public abstract int[] getAdnRecordsSize(int efid); + + /** + * Loads the AdnRecords in efid and returns them as a + * List of AdnRecords + * + * throws SecurityException if no READ_CONTACTS permission + * + * @param efid the EF id of a ADN-like ICC + * @return List of AdnRecord + */ + public List getAdnRecordsInEf(int efid) { + + if (phone.getContext().checkCallingOrSelfPermission( + android.Manifest.permission.READ_CONTACTS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "Requires android.permission.READ_CONTACTS permission"); + } + + if (DBG) logd("getAdnRecordsInEF: efid=" + efid); + + synchronized(mLock) { + checkThread(); + Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE); + adnCache.requestLoadAllAdnLike(efid, response); + try { + mLock.wait(); + } catch (InterruptedException e) { + logd("interrupted while trying to load from the SIM"); + } + } + return records; + } + + protected void checkThread() { + if (!ALLOW_SIM_OP_IN_UI_THREAD) { + // Make sure this isn't the UI thread, since it will block + if (mBaseHandler.getLooper().equals(Looper.myLooper())) { + loge("query() called on the main UI thread!"); + throw new IllegalStateException( + "You cannot call query on this provder from the main UI thread."); + } + } + } +} + diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java new file mode 100644 index 0000000..1c0fc52 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManagerProxy.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.content.pm.PackageManager; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.ServiceManager; +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + + +/** + * SimPhoneBookInterfaceManager to provide an inter-process communication to + * access ADN-like SIM records. + */ +public class IccPhoneBookInterfaceManagerProxy extends IIccPhoneBook.Stub { + private IccPhoneBookInterfaceManager mIccPhoneBookInterfaceManager; + + public IccPhoneBookInterfaceManagerProxy(IccPhoneBookInterfaceManager + iccPhoneBookInterfaceManager) { + mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager; + if(ServiceManager.getService("simphonebook") == null) { + ServiceManager.addService("simphonebook", this); + } + } + + public void setmIccPhoneBookInterfaceManager( + IccPhoneBookInterfaceManager iccPhoneBookInterfaceManager) { + this.mIccPhoneBookInterfaceManager = iccPhoneBookInterfaceManager; + } + + public boolean + updateAdnRecordsInEfBySearch (int efid, + String oldTag, String oldPhoneNumber, + String newTag, String newPhoneNumber, + String pin2) throws android.os.RemoteException { + return mIccPhoneBookInterfaceManager.updateAdnRecordsInEfBySearch( + efid, oldTag, oldPhoneNumber, newTag, newPhoneNumber, pin2); + } + + public boolean + updateAdnRecordsInEfByIndex(int efid, String newTag, + String newPhoneNumber, int index, String pin2) throws android.os.RemoteException { + return mIccPhoneBookInterfaceManager.updateAdnRecordsInEfByIndex(efid, + newTag, newPhoneNumber, index, pin2); + } + + public int[] getAdnRecordsSize(int efid) throws android.os.RemoteException { + return mIccPhoneBookInterfaceManager.getAdnRecordsSize(efid); + } + + public List getAdnRecordsInEf(int efid) throws android.os.RemoteException { + return mIccPhoneBookInterfaceManager.getAdnRecordsInEf(efid); + } +} diff --git a/telephony/java/com/android/internal/telephony/IccProvider.java b/telephony/java/com/android/internal/telephony/IccProvider.java new file mode 100644 index 0000000..4cbd779 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccProvider.java @@ -0,0 +1,456 @@ +/* + * 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.content.ContentProvider; +import android.content.UriMatcher; +import android.content.ContentValues; +import com.android.internal.database.ArrayListCursor; +import android.database.Cursor; +import android.net.Uri; +import android.os.SystemProperties; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import com.android.internal.telephony.IccConstants; +import com.android.internal.telephony.AdnRecord; +import com.android.internal.telephony.IIccPhoneBook; + + +/** + * {@hide} + */ +public class IccProvider extends ContentProvider { + private static final String TAG = "IccProvider"; + private static final boolean DBG = false; + + + private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { + "name", + "number" + }; + + private static final int ADN = 1; + private static final int FDN = 2; + private static final int SDN = 3; + + private static final String STR_TAG = "tag"; + private static final String STR_NUMBER = "number"; + private static final String STR_PIN2 = "pin2"; + + private static final UriMatcher URL_MATCHER = + new UriMatcher(UriMatcher.NO_MATCH); + + static { + URL_MATCHER.addURI("icc", "adn", ADN); + URL_MATCHER.addURI("icc", "fdn", FDN); + URL_MATCHER.addURI("icc", "sdn", SDN); + } + + + private boolean mSimulator; + + @Override + public boolean onCreate() { + String device = SystemProperties.get("ro.product.device"); + if (!TextUtils.isEmpty(device)) { + mSimulator = false; + } else { + // simulator + mSimulator = true; + } + + return true; + } + + @Override + public Cursor query(Uri url, String[] projection, String selection, + String[] selectionArgs, String sort) { + ArrayList results; + + if (!mSimulator) { + switch (URL_MATCHER.match(url)) { + case ADN: + results = loadFromEf(IccConstants.EF_ADN); + break; + + case FDN: + results = loadFromEf(IccConstants.EF_FDN); + break; + + case SDN: + results = loadFromEf(IccConstants.EF_SDN); + break; + + default: + throw new IllegalArgumentException("Unknown URL " + url); + } + } else { + // Fake up some data for the simulator + results = new ArrayList(4); + ArrayList contact; + + contact = new ArrayList(); + contact.add("Ron Stevens/H"); + contact.add("512-555-5038"); + results.add(contact); + + contact = new ArrayList(); + contact.add("Ron Stevens/M"); + contact.add("512-555-8305"); + results.add(contact); + + contact = new ArrayList(); + contact.add("Melissa Owens"); + contact.add("512-555-8305"); + results.add(contact); + + contact = new ArrayList(); + contact.add("Directory Assistence"); + contact.add("411"); + results.add(contact); + } + + return new ArrayListCursor(ADDRESS_BOOK_COLUMN_NAMES, results); + } + + @Override + public String getType(Uri url) { + switch (URL_MATCHER.match(url)) { + case ADN: + case FDN: + case SDN: + return "vnd.android.cursor.dir/sim-contact"; + + default: + throw new IllegalArgumentException("Unknown URL " + url); + } + } + + @Override + public Uri insert(Uri url, ContentValues initialValues) { + Uri resultUri; + int efType; + String pin2 = null; + + if (DBG) log("insert"); + + int match = URL_MATCHER.match(url); + switch (match) { + case ADN: + efType = IccConstants.EF_ADN; + break; + + case FDN: + efType = IccConstants.EF_FDN; + pin2 = initialValues.getAsString("pin2"); + break; + + default: + throw new UnsupportedOperationException( + "Cannot insert into URL: " + url); + } + + String tag = initialValues.getAsString("tag"); + String number = initialValues.getAsString("number"); + boolean success = addIccRecordToEf(efType, tag, number, pin2); + + if (!success) { + return null; + } + + StringBuilder buf = new StringBuilder("content://im/"); + switch (match) { + case ADN: + buf.append("adn/"); + break; + + case FDN: + buf.append("fdn/"); + break; + } + + // TODO: we need to find out the rowId for the newly added record + buf.append(0); + + resultUri = Uri.parse(buf.toString()); + + /* + // notify interested parties that an insertion happened + getContext().getContentResolver().notifyInsert( + resultUri, rowID, null); + */ + + return resultUri; + } + + private String normalizeValue(String inVal) { + int len = inVal.length(); + String retVal = inVal; + + if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') { + retVal = inVal.substring(1, len-1); + } + + return retVal; + } + + @Override + public int delete(Uri url, String where, String[] whereArgs) { + int efType; + + if (DBG) log("delete"); + + int match = URL_MATCHER.match(url); + switch (match) { + case ADN: + efType = IccConstants.EF_ADN; + break; + + case FDN: + efType = IccConstants.EF_FDN; + break; + + default: + throw new UnsupportedOperationException( + "Cannot insert into URL: " + url); + } + + // parse where clause + String tag = null; + String number = null; + String pin2 = null; + + String[] tokens = where.split("AND"); + int n = tokens.length; + + while (--n >= 0) { + String param = tokens[n]; + if (DBG) log("parsing '" + param + "'"); + + String[] pair = param.split("="); + + if (pair.length != 2) { + Log.e(TAG, "resolve: bad whereClause parameter: " + param); + continue; + } + + String key = pair[0].trim(); + String val = pair[1].trim(); + + if (STR_TAG.equals(key)) { + tag = normalizeValue(val); + } else if (STR_NUMBER.equals(key)) { + number = normalizeValue(val); + } else if (STR_PIN2.equals(key)) { + pin2 = normalizeValue(val); + } + } + + if (TextUtils.isEmpty(tag)) { + return 0; + } + + if (efType == FDN && TextUtils.isEmpty(pin2)) { + return 0; + } + + boolean success = deleteIccRecordFromEf(efType, tag, number, pin2); + if (!success) { + return 0; + } + + return 1; + } + + @Override + public int update(Uri url, ContentValues values, String where, String[] whereArgs) { + int efType; + String pin2 = null; + + if (DBG) log("update"); + + int match = URL_MATCHER.match(url); + switch (match) { + case ADN: + efType = IccConstants.EF_ADN; + break; + + case FDN: + efType = IccConstants.EF_FDN; + pin2 = values.getAsString("pin2"); + break; + + default: + throw new UnsupportedOperationException( + "Cannot insert into URL: " + url); + } + + String tag = values.getAsString("tag"); + String number = values.getAsString("number"); + String newTag = values.getAsString("newTag"); + String newNumber = values.getAsString("newNumber"); + + boolean success = updateIccRecordInEf(efType, tag, number, + newTag, newNumber, pin2); + + if (!success) { + return 0; + } + + return 1; + } + + private ArrayList loadFromEf(int efType) { + ArrayList results = new ArrayList(); + List adnRecords = null; + + if (DBG) log("loadFromEf: efType=" + efType); + + try { + IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( + ServiceManager.getService("simphonebook")); + if (iccIpb != null) { + adnRecords = iccIpb.getAdnRecordsInEf(efType); + } + } catch (RemoteException ex) { + // ignore it + } catch (SecurityException ex) { + if (DBG) log(ex.toString()); + } + if (adnRecords != null) { + // Load the results + + int N = adnRecords.size(); + if (DBG) log("adnRecords.size=" + N); + for (int i = 0; i < N ; i++) { + loadRecord(adnRecords.get(i), results); + } + } else { + // No results to load + Log.w(TAG, "Cannot load ADN records"); + results.clear(); + } + if (DBG) log("loadFromEf: return results"); + return results; + } + + private boolean + addIccRecordToEf(int efType, String name, String number, String pin2) { + if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name + + ", number=" + number); + + boolean success = false; + + // TODO: do we need to call getAdnRecordsInEf() before calling + // updateAdnRecordsInEfBySearch()? In any case, we will leave + // the UI level logic to fill that prereq if necessary. But + // hopefully, we can remove this requirement. + + try { + IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( + ServiceManager.getService("simphonebook")); + if (iccIpb != null) { + success = iccIpb.updateAdnRecordsInEfBySearch(efType, "", "", + name, number, pin2); + } + } catch (RemoteException ex) { + // ignore it + } catch (SecurityException ex) { + if (DBG) log(ex.toString()); + } + if (DBG) log("addIccRecordToEf: " + success); + return success; + } + + private boolean + updateIccRecordInEf(int efType, String oldName, String oldNumber, + String newName, String newNumber,String pin2) { + if (DBG) log("updateIccRecordInEf: efType=" + efType + + ", oldname=" + oldName + ", oldnumber=" + oldNumber + + ", newname=" + newName + ", newnumber=" + newNumber); + boolean success = false; + + try { + IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( + ServiceManager.getService("simphonebook")); + if (iccIpb != null) { + success = iccIpb.updateAdnRecordsInEfBySearch(efType, + oldName, oldNumber, newName, newNumber, pin2); + } + } catch (RemoteException ex) { + // ignore it + } catch (SecurityException ex) { + if (DBG) log(ex.toString()); + } + if (DBG) log("updateIccRecordInEf: " + success); + return success; + } + + + private boolean deleteIccRecordFromEf(int efType, String name, String number, String pin2) { + if (DBG) log("deleteIccRecordFromEf: efType=" + efType + + ", name=" + name + ", number=" + number + ", pin2=" + pin2); + + boolean success = false; + + try { + IIccPhoneBook iccIpb = IIccPhoneBook.Stub.asInterface( + ServiceManager.getService("simphonebook")); + if (iccIpb != null) { + success = iccIpb.updateAdnRecordsInEfBySearch(efType, + name, number, "", "", pin2); + } + } catch (RemoteException ex) { + // ignore it + } catch (SecurityException ex) { + if (DBG) log(ex.toString()); + } + if (DBG) log("deleteIccRecordFromEf: " + success); + return success; + } + + /** + * Loads an AdnRecord into an ArrayList. Must be called with mLock held. + * + * @param record the ADN record to load from + * @param results the array list to put the results in + */ + private void loadRecord(AdnRecord record, + ArrayList results) { + if (!record.isEmpty()) { + ArrayList contact = new ArrayList(2); + String alphaTag = record.getAlphaTag(); + String number = record.getNumber(); + + if (DBG) log("loadRecord: " + alphaTag + ", " + number); + contact.add(alphaTag); + contact.add(number); + results.add(contact); + } + } + + private void log(String msg) { + Log.d(TAG, "[IccProvider] " + msg); + } + +} diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java new file mode 100644 index 0000000..114094b --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccRecords.java @@ -0,0 +1,237 @@ +/* + * 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.os.Registrant; +import android.os.RegistrantList; +import android.util.Log; + +import java.util.ArrayList; + +/** + * {@hide} + */ +public abstract class IccRecords extends Handler implements IccConstants { + + protected static final boolean DBG = true; + //***** Instance Variables + + protected PhoneBase phone; + protected RegistrantList recordsLoadedRegistrants = new RegistrantList(); + + protected int recordsToLoad; // number of pending load requests + + protected AdnRecordCache adnCache; + + //***** Cached SIM State; cleared on channel close + + protected boolean recordsRequested = false; // true if we've made requests for the sim records + + public String iccid; + protected String msisdn = null; // My mobile number + protected String msisdnTag = null; + protected String voiceMailNum = null; + protected String voiceMailTag = null; + protected String newVoiceMailNum = null; + protected String newVoiceMailTag = null; + protected boolean isVoiceMailFixed = false; + protected int countVoiceMessages = 0; + + protected int mncLength = 0; // 0 is used to indicate that the value + // is not initialized + protected int mailboxIndex = 0; // 0 is no mailbox dailing number associated + + protected String spn; + protected int spnDisplayCondition; + + //***** Constants + + // Bitmasks for SPN display rules. + protected static final int SPN_RULE_SHOW_SPN = 0x01; + protected static final int SPN_RULE_SHOW_PLMN = 0x02; + + //***** Event Constants + protected static final int EVENT_SET_MSISDN_DONE = 30; + + //***** Constructor + + public IccRecords(PhoneBase p) { + this.phone = p; + } + + protected abstract void onRadioOffOrNotAvailable(); + + //***** Public Methods + public AdnRecordCache getAdnCache() { + return adnCache; + } + + public void registerForRecordsLoaded(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + recordsLoadedRegistrants.add(r); + + if (recordsToLoad == 0 && recordsRequested == true) { + r.notifyRegistrant(new AsyncResult(null, null, null)); + } + } + + public void unregisterForRecordsLoaded(Handler h) { + recordsLoadedRegistrants.remove(h); + } + + public String getMsisdnNumber() { + return msisdn; + } + + /** + * Set subscriber number to SIM record + * + * The subscriber number is stored in EF_MSISDN (TS 51.011) + * + * When the operation is complete, onComplete will be sent to its handler + * + * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters) + * @param number dailing nubmer (up to 20 digits) + * if the number starts with '+', then set to international TOA + * @param onComplete + * onComplete.obj will be an AsyncResult + * ((AsyncResult)onComplete.obj).exception == null on success + * ((AsyncResult)onComplete.obj).exception != null on fail + */ + public void setMsisdnNumber(String alphaTag, String number, + Message onComplete) { + + msisdn = number; + msisdnTag = alphaTag; + + if(DBG) log("Set MSISDN: " + msisdnTag +" " + msisdn); + + + AdnRecord adn = new AdnRecord(msisdnTag, msisdn); + + new AdnRecordLoader(phone).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null, + obtainMessage(EVENT_SET_MSISDN_DONE, onComplete)); + } + + public String getMsisdnAlphaTag() { + return msisdnTag; + } + + public String getVoiceMailNumber() { + return voiceMailNum; + } + + /** + * Return Service Provider Name stored in SIM (EF_SPN=0x6F46) or in RUIM (EF_RUIM_SPN=0x6F41) + * @return null if SIM is not yet ready or no RUIM entry + */ + public String getServiceProviderName() { + return spn; + } + + /** + * Set voice mail number to SIM record + * + * The voice mail number can be stored either in EF_MBDN (TS 51.011) or + * EF_MAILBOX_CPHS (CPHS 4.2) + * + * If EF_MBDN is available, store the voice mail number to EF_MBDN + * + * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS + * + * So the voice mail number will be stored in both EFs if both are available + * + * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail. + * + * When the operation is complete, onComplete will be sent to its handler + * + * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters) + * @param voiceNumber dailing nubmer (upto 20 digits) + * if the number is start with '+', then set to international TOA + * @param onComplete + * onComplete.obj will be an AsyncResult + * ((AsyncResult)onComplete.obj).exception == null on success + * ((AsyncResult)onComplete.obj).exception != null on fail + */ + public abstract void setVoiceMailNumber(String alphaTag, String voiceNumber, + Message onComplete); + + public String getVoiceMailAlphaTag() { + return voiceMailTag; + } + + /** + * Sets the SIM voice message waiting indicator records + * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported + * @param countWaiting The number of messages waiting, if known. Use + * -1 to indicate that an unknown number of + * messages are waiting + */ + public abstract void setVoiceMessageWaiting(int line, int countWaiting); + + /** @return true if there are messages waiting, false otherwise. */ + public boolean getVoiceMessageWaiting() { + return countVoiceMessages != 0; + } + + /** + * Returns number of voice messages waiting, if available + * If not available (eg, on an older CPHS SIM) -1 is returned if + * getVoiceMessageWaiting() is true + */ + public int getCountVoiceMessages() { + return countVoiceMessages; + } + + /** + * Called by STK Service when REFRESH is received. + * @param fileChanged indicates whether any files changed + * @param fileList if non-null, a list of EF files that changed + */ + public abstract void onRefresh(boolean fileChanged, int[] fileList); + + + public boolean getRecordsLoaded() { + if (recordsToLoad == 0 && recordsRequested == true) { + return true; + } else { + return false; + } + } + + //***** Overridden from Handler + public abstract void handleMessage(Message msg); + + protected abstract void onRecordLoaded(); + + protected abstract void onAllRecordsLoaded(); + + /** + * Returns the SpnDisplayRule based on settings on the SIM and the + * specified plmn (currently-registered PLMN). See TS 22.101 Annex A + * and TS 51.011 10.3.11 for details. + * + * If the SPN is not found on the SIM, the rule is always PLMN_ONLY. + */ + protected abstract int getDisplayRule(String plmn); + + protected abstract void log(String s); +} + diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java new file mode 100644 index 0000000..620f2de --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.app.PendingIntent; +import android.content.Context; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; + +/** + * IccSmsInterfaceManager to provide an inter-process communication to + * access Sms in Icc. + */ +public abstract class IccSmsInterfaceManager extends ISms.Stub { + static final boolean DBG = true; + + protected PhoneBase mPhone; + protected Context mContext; + protected SMSDispatcher mDispatcher; + + protected IccSmsInterfaceManager(PhoneBase phone){ + mPhone = phone; + mContext = phone.getContext(); + } + + protected void enforceReceiveAndSend(String message) { + mContext.enforceCallingPermission( + "android.permission.RECEIVE_SMS", message); + mContext.enforceCallingPermission( + "android.permission.SEND_SMS", message); + } + + /** + * Send a Raw PDU SMS + * + * @param smsc the SMSC to send the message through, or NULL for the + * defatult SMSC + * @param pdu the raw PDU to send + * @param sentIntent if not NULL this Intent is + * broadcast when the message is sucessfully sent, or failed. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * @param deliveryIntent if not NULL this Intent is + * broadcast when the message is delivered to the recipient. The + * raw pdu of the status report is in the extended data ("pdu"). + */ + public void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, + PendingIntent deliveryIntent) { + Context context = mPhone.getContext(); + + context.enforceCallingPermission( + "android.permission.SEND_SMS", + "Sending SMS message"); + if (DBG) log("sendRawPdu: smsc=" + smsc + + " pdu="+ pdu + " sentIntent" + sentIntent + + " deliveryIntent" + deliveryIntent); + mDispatcher.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent); + } + + /** + * Send a multi-part 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 parts an ArrayList of strings that, in order, + * comprise the original message + * @param sentIntents if not null, an ArrayList of + * PendingIntents (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * @param deliveryIntents if not null, an ArrayList of + * PendingIntents (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"). + */ + public void sendMultipartText(String destinationAddress, String scAddress, List parts, + List sentIntents, List deliveryIntents) { + Context context = mPhone.getContext(); + + context.enforceCallingPermission( + "android.permission.SEND_SMS", + "Sending SMS message"); + if (DBG) log("sendMultipartText"); + mDispatcher.sendMultipartText(destinationAddress, scAddress, (ArrayList) parts, + (ArrayList) sentIntents, (ArrayList) deliveryIntents); + } + + /** + * create SmsRawData lists from all sms record byte[] + * Use null to indicate "free" record + * + * @param messages List of message records from EF_SMS. + * @return SmsRawData list of all in-used records + */ + protected ArrayList buildValidRawData(ArrayList messages) { + int count = messages.size(); + ArrayList ret; + + ret = new ArrayList(count); + + for (int i = 0; i < count; i++) { + byte[] ba = messages.get(i); + if (ba[0] == STATUS_ON_ICC_FREE) { + ret.add(null); + } else { + ret.add(new SmsRawData(messages.get(i))); + } + } + + return ret; + } + + /** + * Generates an EF_SMS record from status and raw PDU. + * + * @param status Message status. See TS 51.011 10.5.3. + * @param pdu Raw message PDU. + * @return byte array for the record. + */ + protected byte[] makeSmsRecordData(int status, byte[] pdu) { + byte[] data = new byte[IccConstants.SMS_RECORD_LENGTH]; + + // Status bits for this record. See TS 51.011 10.5.3 + data[0] = (byte)(status & 7); + + System.arraycopy(pdu, 0, data, 1, pdu.length); + + // Pad out with 0xFF's. + for (int j = pdu.length+1; j < IccConstants.SMS_RECORD_LENGTH; j++) { + data[j] = -1; + } + + return data; + } + + protected abstract void log(String msg); + +} + diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java new file mode 100644 index 0000000..a51d074 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.app.PendingIntent; +import android.os.ServiceManager; + +import java.util.List; + +public class IccSmsInterfaceManagerProxy extends ISms.Stub { + private IccSmsInterfaceManager mIccSmsInterfaceManager; + + public IccSmsInterfaceManagerProxy(IccSmsInterfaceManager + iccSmsInterfaceManager) { + this.mIccSmsInterfaceManager = iccSmsInterfaceManager; + if(ServiceManager.getService("isms") == null) { + ServiceManager.addService("isms", this); + } + } + + public void setmIccSmsInterfaceManager(IccSmsInterfaceManager iccSmsInterfaceManager) { + this.mIccSmsInterfaceManager = iccSmsInterfaceManager; + } + + public boolean + updateMessageOnIccEf(int index, int status, byte[] pdu) throws android.os.RemoteException { + return mIccSmsInterfaceManager.updateMessageOnIccEf(index, status, pdu); + } + + public boolean copyMessageToIccEf(int status, byte[] pdu, + byte[] smsc) throws android.os.RemoteException { + return mIccSmsInterfaceManager.copyMessageToIccEf(status, pdu, smsc); + } + + public List getAllMessagesFromIccEf() throws android.os.RemoteException { + return mIccSmsInterfaceManager.getAllMessagesFromIccEf(); + } + + public void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, + PendingIntent deliveryIntent) throws android.os.RemoteException { + mIccSmsInterfaceManager.sendRawPdu(smsc, pdu, sentIntent, + deliveryIntent); + } + + public void sendMultipartText(String destinationAddress, String scAddress, + List parts, List sentIntents, + List deliveryIntents) throws android.os.RemoteException { + mIccSmsInterfaceManager.sendMultipartText(destinationAddress, scAddress, + parts, sentIntents, deliveryIntents); + } + +} diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java new file mode 100644 index 0000000..881ed2d --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccUtils.java @@ -0,0 +1,487 @@ +/* + * 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.graphics.Bitmap; +import android.graphics.Color; +import android.util.Log; + +import com.android.internal.telephony.GsmAlphabet; + +import java.io.UnsupportedEncodingException; + +/** + * Various methods, useful for dealing with SIM data. + */ +public class IccUtils { + static final String LOG_TAG="IccUtils"; + + /** + * Many fields in GSM SIM's are stored as nibble-swizzled BCD + * + * Assumes left-justified field that may be padded right with 0xf + * values. + * + * Stops on invalid BCD value, returning string so far + */ + public static String + bcdToString(byte[] data, int offset, int length) { + StringBuilder ret = new StringBuilder(length*2); + + for (int i = offset ; i < offset + length ; i++) { + byte b; + int v; + + v = data[i] & 0xf; + if (v > 9) break; + ret.append((char)('0' + v)); + + v = (data[i] >> 4) & 0xf; + if (v > 9) break; + ret.append((char)('0' + v)); + } + + return ret.toString(); + } + + + /** + * Decodes a GSM-style BCD byte, returning an int ranging from 0-99. + * + * In GSM land, the least significant BCD digit is stored in the most + * significant nibble. + * + * Out-of-range digits are treated as 0 for the sake of the time stamp, + * because of this: + * + * TS 23.040 section 9.2.3.11 + * "if the MS receives a non-integer value in the SCTS, it shall + * assume the digit is set to 0 but shall store the entire field + * exactly as received" + */ + public static int + bcdByteToInt(byte b) { + int ret = 0; + + // treat out-of-range BCD values as 0 + if ((b & 0xf0) <= 0x90) { + ret = (b >> 4) & 0xf; + } + + if ((b & 0x0f) <= 0x09) { + ret += (b & 0xf) * 10; + } + + return ret; + } + + /** Decodes BCD byte like {@link bcdByteToInt}, but the most significant BCD + * digit is expected in the most significant nibble. + */ + public static int + beBcdByteToInt(byte b) { + int ret = 0; + + // treat out-of-range BCD values as 0 + if ((b & 0xf0) <= 0x90) { + ret = ((b >> 4) & 0xf) * 10; + } + + if ((b & 0x0f) <= 0x09) { + ret += (b & 0xf); + } + + return ret; + } + + /** + * Decodes a string field that's formatted like the EF[ADN] alpha + * identifier + * + * From TS 51.011 10.5.1: + * Coding: + * this alpha tagging shall use either + * - the SMS default 7 bit coded alphabet as defined in + * TS 23.038 [12] with bit 8 set to 0. The alpha identifier + * shall be left justified. Unused bytes shall be set to 'FF'; or + * - one of the UCS2 coded options as defined in annex B. + * + * Annex B from TS 11.11 V8.13.0: + * 1) If the first octet in the alpha string is '80', then the + * remaining octets are 16 bit UCS2 characters ... + * 2) if the first octet in the alpha string is '81', then the + * second octet contains a value indicating the number of + * characters in the string, and the third octet contains an + * 8 bit number which defines bits 15 to 8 of a 16 bit + * base pointer, where bit 16 is set to zero and bits 7 to 1 + * are also set to zero. These sixteen bits constitute a + * base pointer to a "half page" in the UCS2 code space, to be + * used with some or all of the remaining octets in the string. + * The fourth and subsequent octets contain codings as follows: + * If bit 8 of the octet is set to zero, the remaining 7 bits + * of the octet contain a GSM Default Alphabet character, + * whereas if bit 8 of the octet is set to one, then the + * remaining seven bits are an offset value added to the + * 16 bit base pointer defined earlier... + * 3) If the first octet of the alpha string is set to '82', then + * the second octet contains a value indicating the number of + * characters in the string, and the third and fourth octets + * contain a 16 bit number which defines the complete 16 bit + * base pointer to a "half page" in the UCS2 code space... + */ + public static String + adnStringFieldToString(byte[] data, int offset, int length) { + if (length >= 1) { + if (data[offset] == (byte) 0x80) { + int ucslen = (length - 1) / 2; + String ret = null; + + try { + ret = new String(data, offset + 1, ucslen * 2, "utf-16be"); + } catch (UnsupportedEncodingException ex) { + Log.e(LOG_TAG, "implausible UnsupportedEncodingException", + ex); + } + + if (ret != null) { + // trim off trailing FFFF characters + + ucslen = ret.length(); + while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF') + ucslen--; + + return ret.substring(0, ucslen); + } + } + } + + boolean isucs2 = false; + char base = '\0'; + int len = 0; + + if (length >= 3 && data[offset] == (byte) 0x81) { + len = data[offset + 1] & 0xFF; + if (len > length - 3) + len = length - 3; + + base = (char) ((data[offset + 2] & 0xFF) << 7); + offset += 3; + isucs2 = true; + } else if (length >= 4 && data[offset] == (byte) 0x82) { + len = data[offset + 1] & 0xFF; + if (len > length - 4) + len = length - 4; + + base = (char) (((data[offset + 2] & 0xFF) << 8) | + (data[offset + 3] & 0xFF)); + offset += 4; + isucs2 = true; + } + + if (isucs2) { + StringBuilder ret = new StringBuilder(); + + while (len > 0) { + // UCS2 subset case + + if (data[offset] < 0) { + ret.append((char) (base + (data[offset] & 0x7F))); + offset++; + len--; + } + + // GSM character set case + + int count = 0; + while (count < len && data[offset + count] >= 0) + count++; + + ret.append(GsmAlphabet.gsm8BitUnpackedToString(data, + offset, count)); + + offset += count; + len -= count; + } + + return ret.toString(); + } + + return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length); + } + + static int + hexCharToInt(char c) { + if (c >= '0' && c <= '9') return (c - '0'); + if (c >= 'A' && c <= 'F') return (c - 'A' + 10); + if (c >= 'a' && c <= 'f') return (c - 'a' + 10); + + throw new RuntimeException ("invalid hex char '" + c + "'"); + } + + /** + * Converts a hex String to a byte array. + * + * @param s A string of hexadecimal characters, must be an even number of + * chars long + * + * @return byte array representation + * + * @throws RuntimeException on invalid format + */ + public static byte[] + hexStringToBytes(String s) { + byte[] ret; + + if (s == null) return null; + + int sz = s.length(); + + ret = new byte[sz/2]; + + for (int i=0 ; i > 4); + + ret.append("0123456789abcdef".charAt(b)); + + b = 0x0f & bytes[i]; + + ret.append("0123456789abcdef".charAt(b)); + } + + return ret.toString(); + } + + + /** + * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string + * "offset" points to "octet 3", the coding scheme byte + * empty string returned on decode error + */ + public static String + networkNameToString(byte[] data, int offset, int length) { + String ret; + + if ((data[offset] & 0x80) != 0x80 || length < 1) { + return ""; + } + + switch ((data[offset] >>> 4) & 0x7) { + case 0: + // SMS character set + int countSeptets; + int unusedBits = data[offset] & 7; + countSeptets = (((length - 1) * 8) - unusedBits) / 7 ; + ret = GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets); + break; + case 1: + // UCS2 + try { + ret = new String(data, + offset + 1, length - 1, "utf-16"); + } catch (UnsupportedEncodingException ex) { + ret = ""; + Log.e(LOG_TAG,"implausible UnsupportedEncodingException", ex); + } + break; + + // unsupported encoding + default: + ret = ""; + break; + } + + // "Add CI" + // "The MS should add the letters for the Country's Initials and + // a separator (e.g. a space) to the text string" + + if ((data[offset] & 0x40) != 0) { + // FIXME(mkf) add country initials here + + } + + return ret; + } + + /** + * Convert a TS 131.102 image instance of code scheme '11' into Bitmap + * @param data The raw data + * @param length The length of image body + * @return The bitmap + */ + public static Bitmap parseToBnW(byte[] data, int length){ + int valueIndex = 0; + int width = data[valueIndex++] & 0xFF; + int height = data[valueIndex++] & 0xFF; + int numOfPixels = width*height; + + int[] pixels = new int[numOfPixels]; + + int pixelIndex = 0; + int bitIndex = 7; + byte currentByte = 0x00; + while (pixelIndex < numOfPixels) { + // reassign data and index for every byte (8 bits). + if (pixelIndex % 8 == 0) { + currentByte = data[valueIndex++]; + bitIndex = 7; + } + pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01); + }; + + if (pixelIndex != numOfPixels) { + Log.e(LOG_TAG, "parse end and size error"); + } + return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); + } + + private static int bitToRGB(int bit){ + if(bit == 1){ + return Color.WHITE; + } else { + return Color.BLACK; + } + } + + /** + * a TS 131.102 image instance of code scheme '11' into color Bitmap + * + * @param data The raw data + * @param length the length of image body + * @param transparency with or without transparency + * @return The color bitmap + */ + public static Bitmap parseToRGB(byte[] data, int length, + boolean transparency) { + int valueIndex = 0; + int width = data[valueIndex++] & 0xFF; + int height = data[valueIndex++] & 0xFF; + int bits = data[valueIndex++] & 0xFF; + int colorNumber = data[valueIndex++] & 0xFF; + int clutOffset = ((data[valueIndex++] & 0xFF) << 8) + | data[valueIndex++]; + length = length - 6; + + int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber); + if (true == transparency) { + colorIndexArray[colorNumber - 1] = Color.TRANSPARENT; + } + + int[] resultArray = null; + if (0 == (8 % bits)) { + resultArray = mapTo2OrderBitColor(data, valueIndex, + (width * height), colorIndexArray, bits); + } else { + resultArray = mapToNon2OrderBitColor(data, valueIndex, + (width * height), colorIndexArray, bits); + } + + return Bitmap.createBitmap(resultArray, width, height, + Bitmap.Config.RGB_565); + } + + private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex, + int length, int[] colorArray, int bits) { + if (0 != (8 % bits)) { + Log.e(LOG_TAG, "not event number of color"); + return mapToNon2OrderBitColor(data, valueIndex, length, colorArray, + bits); + } + + int mask = 0x01; + switch (bits) { + case 1: + mask = 0x01; + break; + case 2: + mask = 0x03; + break; + case 4: + mask = 0x0F; + break; + case 8: + mask = 0xFF; + break; + } + + int[] resultArray = new int[length]; + int resultIndex = 0; + int run = 8 / bits; + while (resultIndex < length) { + byte tempByte = data[valueIndex++]; + for (int runIndex = 0; runIndex < run; ++runIndex) { + int offset = run - runIndex - 1; + resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits)) + & mask]; + } + } + return resultArray; + } + + private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex, + int length, int[] colorArray, int bits) { + if (0 == (8 % bits)) { + Log.e(LOG_TAG, "not odd number of color"); + return mapTo2OrderBitColor(data, valueIndex, length, colorArray, + bits); + } + + int[] resultArray = new int[length]; + // TODO fix me: + return resultArray; + } + + private static int[] getCLUT(byte[] rawData, int offset, int number) { + if (null == rawData) { + return null; + } + + int[] result = new int[number]; + int endIndex = offset + (number * 3); // 1 color use 3 bytes + int valueIndex = offset; + int colorIndex = 0; + int alpha = 0xff << 24; + do { + result[colorIndex++] = alpha + | ((rawData[valueIndex++] & 0xFF) << 16) + | ((rawData[valueIndex++] & 0xFF) << 8) + | ((rawData[valueIndex++] & 0xFF)); + } while (valueIndex < endIndex); + return result; + } +} diff --git a/telephony/java/com/android/internal/telephony/IccVmFixedException.java b/telephony/java/com/android/internal/telephony/IccVmFixedException.java new file mode 100644 index 0000000..45679c1 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccVmFixedException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +/** + * {@hide} + */ +public final class IccVmFixedException extends IccException { + IccVmFixedException() + { + + } + + public IccVmFixedException(String s) + { + super(s); + } +} \ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java b/telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java new file mode 100644 index 0000000..7e90d24 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IccVmNotSupportedException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +/** + * {@hide} + */ +public final class IccVmNotSupportedException extends IccException { + IccVmNotSupportedException() + { + + } + + public IccVmNotSupportedException(String s) + { + super(s); + } +} diff --git a/telephony/java/com/android/internal/telephony/MmiCode.java b/telephony/java/com/android/internal/telephony/MmiCode.java index 925b06f..c71ff77 100644 --- a/telephony/java/com/android/internal/telephony/MmiCode.java +++ b/telephony/java/com/android/internal/telephony/MmiCode.java @@ -41,14 +41,14 @@ public interface MmiCode * @return Localized message for UI display, valid only in COMPLETE * or FAILED states. null otherwise */ - + public CharSequence getMessage(); /** * Cancels pending MMI request. * State becomes CANCELLED unless already COMPLETE or FAILED */ - public void cancel(); + public void cancel(); /** * @return true if the network response is a REQUEST for more user input. diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 05e61f2..ed90d32 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -17,10 +17,14 @@ package com.android.internal.telephony; import android.content.Context; +import android.content.SharedPreferences; import android.os.Handler; import android.os.Message; +import android.preference.PreferenceManager; import android.telephony.CellLocation; import android.telephony.ServiceState; + +import com.android.internal.telephony.DataConnection; import com.android.internal.telephony.gsm.NetworkInfo; import com.android.internal.telephony.gsm.PdpConnection; import com.android.internal.telephony.test.SimulatedRadioControl; @@ -38,13 +42,13 @@ public interface Phone { /** used to enable additional debug messages */ static final boolean DEBUG_PHONE = true; - - /** + + /** * The phone state. One of the following:

*

    *
  • IDLE = no phone activity
  • - *
  • RINGING = a phone call is ringing or call waiting. + *
  • RINGING = a phone call is ringing or call waiting. * In the latter case, another call is active as well
  • *
  • OFFHOOK = The phone is off hook. At least one call * exists that is dialing, active or holding and no calls are @@ -70,7 +74,7 @@ public interface Phone { CONNECTED, CONNECTING, DISCONNECTED, SUSPENDED; }; - enum DataActivityState { + public enum DataActivityState { /** * The state of a data activity. *
      @@ -131,6 +135,8 @@ public interface Phone { static final String REASON_DATA_ENABLED = "dataEnabled"; static final String REASON_GPRS_ATTACHED = "gprsAttached"; static final String REASON_GPRS_DETACHED = "gprsDetached"; + static final String REASON_CDMA_DATA_ATTACHED = "cdmaDataAttached"; + static final String REASON_CDMA_DATA_DETACHED = "cdmaDataDetached"; static final String REASON_APN_CHANGED = "apnChanged"; static final String REASON_APN_SWITCHED = "apnSwitched"; static final String REASON_RESTORE_DEFAULT_APN = "restoreDefaultApn"; @@ -152,12 +158,32 @@ public interface Phone { static final int BM_BOUNDARY = 6; // upper band boundary // Used for preferred network type - static final int NT_AUTO_TYPE = 0; // WCDMA preferred (auto mode) - static final int NT_GSM_TYPE = 1; // GSM only - static final int NT_WCDMA_TYPE = 2; // WCDMA only - - /** - * Get the current ServiceState. Use + // Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone + int NT_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */ + int NT_MODE_GSM_ONLY = 1; /* GSM only */ + int NT_MODE_WCDMA_ONLY = 2; /* WCDMA only */ + int NT_MODE_GSM_UMTS = 3; /* GSM/WCDMA (auto mode, according to PRL) + AVAILABLE Application Settings menu*/ + int NT_MODE_CDMA = 4; /* CDMA and EvDo (auto mode, according to PRL) + AVAILABLE Application Settings menu*/ + int NT_MODE_CDMA_NO_EVDO = 5; /* CDMA only */ + int NT_MODE_EVDO_NO_CDMA = 6; /* EvDo only */ + int NT_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL) + AVAILABLE Application Settings menu*/ + int PREFERRED_NT_MODE = NT_MODE_GLOBAL; + + + // Used for CDMA roaming mode + static final int CDMA_RM_HOME = 0; //Home Networks only, as defined in PRL + static final int CDMA_RM_AFFILIATED = 1; //Roaming an Affiliated networks, as defined in PRL + static final int CDMA_RM_ANY = 2; //Roaming on Any Network, as defined in PRL + + // Used for CDMA subscription mode + static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; //RUIM/SIM (default) + static final int CDMA_SUBSCRIPTION_NV = 1; //NV -> non-volatile memory + + /** + * Get the current ServiceState. Use * registerForServiceStateChanged to be informed of * updates. */ @@ -167,11 +193,12 @@ public interface Phone { * Get the current CellLocation. */ CellLocation getCellLocation(); - + /** * Get the current DataState. No change notification exists at this - * interface -- use - * {@link com.android.internal.telephony.PhoneStateIntentReceiver PhoneStateIntentReceiver} instead. + * interface -- use + * {@link com.android.internal.telephony.PhoneStateIntentReceiver PhoneStateIntentReceiver} + * instead. */ DataState getDataConnectionState(); @@ -181,58 +208,70 @@ public interface Phone { * {@link TelephonyManager} instead. */ DataActivityState getDataActivityState(); - + /** * Gets the context for the phone, as set at initialization time. */ Context getContext(); - /** + /** + * Disables the DNS check (i.e., allows "0.0.0.0"). + * Useful for lab testing environment. + * @param b true disables the check, false enables. + */ + void disableDnsCheck(boolean b); + + /** + * Returns true if the DNS check is currently disabled. + */ + boolean isDnsCheckDisabled(); + + /** * Get current coarse-grained voice call state. - * Use {@link #registerForPhoneStateChanged(Handler, int, Object) + * Use {@link #registerForPhoneStateChanged(Handler, int, Object) * registerForPhoneStateChanged()} for change notification.

      * If the phone has an active call and call waiting occurs, * then the phone state is RINGING not OFFHOOK - * Note: + * Note: * This registration point provides notification of finer-grained * changes.

      * */ State getState(); - /** + /** * Returns a string identifier for this phone interface for parties * outside the phone app process. * @return The string name. */ String getPhoneName(); - /** + /** * Returns an array of string identifiers for the APN types serviced by the * currently active or last connected APN. * @return The string array. */ String[] getActiveApnTypes(); - - /** + + /** * Returns a string identifier for currently active or last connected APN. * @return The string name. */ String getActiveApn(); - - /** + + /** * Get current signal strength. No change notification available on this * interface. Use PhoneStateNotifier or an equivalent. - * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu). + * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu). * The following special values are defined:

      *
      • 0 means "-113 dBm or less".
      • *
      • 31 means "-51 dBm or greater".
      - * + * * @return Current signal strength in ASU's. */ int getSignalStrengthASU(); - - /** + + /** * Notifies when a previously untracked non-ringing/waiting connection has appeared. * This is likely due to some other entity (eg, SIM card application) initiating a call. */ @@ -243,7 +282,7 @@ public interface Phone { */ void unregisterForUnknownConnection(Handler h); - /** + /** * Notifies when any aspect of the voice call state changes. * Resulting events will have an AsyncResult in Message.obj. * AsyncResult.userData will be set to the obj argument here. @@ -252,13 +291,13 @@ public interface Phone { void registerForPhoneStateChanged(Handler h, int what, Object obj); /** - * Unregisters for voice call state change notifications. + * Unregisters for voice call state change notifications. * Extraneous calls are tolerated silently. */ void unregisterForPhoneStateChanged(Handler h); - /** + /** * Notifies when a new ringing or waiting connection has appeared.

      * * Messages received from this: @@ -267,19 +306,19 @@ public interface Phone { * AsyncResult.result = a Connection.

      * Please check Connection.isRinging() to make sure the Connection * has not dropped since this message was posted. - * If Connection.isRinging() is true, then + * If Connection.isRinging() is true, then * Connection.getCall() == Phone.getRingingCall() */ void registerForNewRingingConnection(Handler h, int what, Object obj); /** - * Unregisters for new ringing connection notification. + * Unregisters for new ringing connection notification. * Extraneous calls are tolerated silently */ void unregisterForNewRingingConnection(Handler h); - /** + /** * Notifies when an incoming call rings.

      * * Messages received from this: @@ -288,29 +327,29 @@ public interface Phone { * AsyncResult.result = a Connection.

      */ void registerForIncomingRing(Handler h, int what, Object obj); - + /** - * Unregisters for ring notification. + * Unregisters for ring notification. * Extraneous calls are tolerated silently */ - + void unregisterForIncomingRing(Handler h); - - - /** + + + /** * Notifies when a voice connection has disconnected, either due to local * or remote hangup or error. - * + * * Messages received from this will have the following members:

      *

      • Message.obj will be an AsyncResult
      • *
      • AsyncResult.userObj = obj
      • - *
      • AsyncResult.result = a Connection object that is + *
      • AsyncResult.result = a Connection object that is * no longer connected.
      */ void registerForDisconnect(Handler h, int what, Object obj); /** - * Unregisters for voice disconnection notification. + * Unregisters for voice disconnection notification. * Extraneous calls are tolerated silently */ void unregisterForDisconnect(Handler h); @@ -330,7 +369,7 @@ public interface Phone { void registerForMmiInitiate(Handler h, int what, Object obj); /** - * Unregisters for new MMI initiate notification. + * Unregisters for new MMI initiate notification. * Extraneous calls are tolerated silently */ void unregisterForMmiInitiate(Handler h); @@ -346,7 +385,7 @@ public interface Phone { void registerForMmiComplete(Handler h, int what, Object obj); /** - * Unregisters for MMI complete notification. + * Unregisters for MMI complete notification. * Extraneous calls are tolerated silently */ void unregisterForMmiComplete(Handler h); @@ -355,7 +394,7 @@ public interface Phone { * Returns a list of MMI codes that are pending. (They have initiated * but have not yet completed). * Presently there is only ever one. - * Use registerForMmiInitiate + * Use registerForMmiInitiate * and registerForMmiComplete for change notification. */ public List getPendingMmiCodes(); @@ -370,14 +409,14 @@ public interface Phone { public void sendUssdResponse(String ussdMessge); /** - * Register for ServiceState changed. + * Register for ServiceState changed. * Message.obj will contain an AsyncResult. * AsyncResult.result will be a ServiceState instance */ void registerForServiceStateChanged(Handler h, int what, Object obj); /** - * Unregisters for ServiceStateChange notification. + * Unregisters for ServiceStateChange notification. * Extraneous calls are tolerated silently */ void unregisterForServiceStateChanged(Handler h); @@ -394,9 +433,9 @@ public interface Phone { void registerForSuppServiceNotification(Handler h, int what, Object obj); /** - * Unregisters for Supplementary Service notifications. + * Unregisters for Supplementary Service notifications. * Extraneous calls are tolerated silently - * + * * @param h Handler to be removed from the registrant list. */ void unregisterForSuppServiceNotification(Handler h); @@ -414,53 +453,85 @@ public interface Phone { /** * Unregister for notifications when a supplementary service attempt fails. * Extraneous calls are tolerated silently - * + * * @param h Handler to be removed from the registrant list. */ void unregisterForSuppServiceFailed(Handler h); - /** - * Returns SIM record load state. Use + /** + * Register for notifications when a sInCall VoicePrivacy is enabled + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj); + + /** + * Unegister for notifications when a sInCall VoicePrivacy is enabled + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForInCallVoicePrivacyOn(Handler h); + + /** + * Register for notifications when a sInCall VoicePrivacy is disabled + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj); + + /** + * Unegister for notifications when a sInCall VoicePrivacy is disabled + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForInCallVoicePrivacyOff(Handler h); + + /** + * Returns SIM record load state. Use * getSimCard().registerForReady() for change notification. * - * @return true if records from the SIM have been loaded and are + * @return true if records from the SIM have been loaded and are * available (if applicable). If not applicable to the underlying * technology, returns true as well. */ - boolean getSimRecordsLoaded(); + boolean getIccRecordsLoaded(); /** - * Returns the SIM card interface for this phone, or null + * Returns the ICC card interface for this phone, or null * if not applicable to underlying technology. */ - SimCard getSimCard(); + IccCard getIccCard(); /** - * Answers a ringing or waiting call. Active calls, if any, go on hold. + * Answers a ringing or waiting call. Active calls, if any, go on hold. * Answering occurs asynchronously, and final notification occurs via - * {@link #registerForPhoneStateChanged(android.os.Handler, int, + * {@link #registerForPhoneStateChanged(android.os.Handler, int, * java.lang.Object) registerForPhoneStateChanged()}. * * @exception CallStateException when no call is ringing or waiting */ void acceptCall() throws CallStateException; - /** - * Reject (ignore) a ringing call. In GSM, this means UDUB - * (User Determined User Busy). Reject occurs asynchronously, - * and final notification occurs via - * {@link #registerForPhoneStateChanged(android.os.Handler, int, + /** + * Reject (ignore) a ringing call. In GSM, this means UDUB + * (User Determined User Busy). Reject occurs asynchronously, + * and final notification occurs via + * {@link #registerForPhoneStateChanged(android.os.Handler, int, * java.lang.Object) registerForPhoneStateChanged()}. * * @exception CallStateException when no call is ringing or waiting */ void rejectCall() throws CallStateException; - /** + /** * Places any active calls on hold, and makes any held calls * active. Switch occurs asynchronously and may fail. - * Final notification occurs via - * {@link #registerForPhoneStateChanged(android.os.Handler, int, + * Final notification occurs via + * {@link #registerForPhoneStateChanged(android.os.Handler, int, * java.lang.Object) registerForPhoneStateChanged()}. * * @exception CallStateException if a call is ringing, waiting, or @@ -469,24 +540,40 @@ public interface Phone { void switchHoldingAndActive() throws CallStateException; /** - * Whether or not the phone can conference in the current phone + * Whether or not the phone can conference in the current phone * state--that is, one call holding and one call active. - * @return true if the phone can conference; false otherwise. + * @return true if the phone can conference; false otherwise. */ boolean canConference(); /** - * Conferences holding and active. Conference occurs asynchronously - * and may fail. Final notification occurs via - * {@link #registerForPhoneStateChanged(android.os.Handler, int, - * java.lang.Object) registerForPhoneStateChanged()}. - * + * Conferences holding and active. Conference occurs asynchronously + * and may fail. Final notification occurs via + * {@link #registerForPhoneStateChanged(android.os.Handler, int, + * java.lang.Object) registerForPhoneStateChanged()}. + * * @exception CallStateException if canConference() would return false. * In these cases, this operation may not be performed. */ void conference() throws CallStateException; /** + * Enable or disable enhanced Voice Privacy (VP). If enhanced VP is + * disabled, normal VP is enabled. + * + * @param enable whether true or false to enable or disable. + * @param onComplete a callback message when the action is completed. + */ + void enableEnhancedVoicePrivacy(boolean enable, Message onComplete); + + /** + * Get the currently set Voice Privacy (VP) mode. + * + * @param onComplete a callback message when the action is completed. + */ + void getEnhancedVoicePrivacy(Message onComplete); + + /** * Whether or not the phone can do explicit call transfer in the current * phone state--that is, one call holding and one call active. * @return true if the phone can do explicit call transfer; false otherwise. @@ -513,65 +600,65 @@ public interface Phone { void clearDisconnected(); - /** - * Gets the foreground call object, which represents all connections that - * are dialing or active (all connections + /** + * Gets the foreground call object, which represents all connections that + * are dialing or active (all connections * that have their audio path connected).

      * * The foreground call is a singleton object. It is constant for the life * of this phone. It is never null.

      - * + * * The foreground call will only ever be in one of these states: - * IDLE, ACTIVE, DIALING, ALERTING, or DISCONNECTED. + * IDLE, ACTIVE, DIALING, ALERTING, or DISCONNECTED. * * State change notification is available via - * {@link #registerForPhoneStateChanged(android.os.Handler, int, + * {@link #registerForPhoneStateChanged(android.os.Handler, int, * java.lang.Object) registerForPhoneStateChanged()}. */ Call getForegroundCall(); - /** + /** * Gets the background call object, which represents all connections that * are holding (all connections that have been accepted or connected, but * do not have their audio path connected).

      * * The background call is a singleton object. It is constant for the life * of this phone object . It is never null.

      - * + * * The background call will only ever be in one of these states: * IDLE, HOLDING or DISCONNECTED. * * State change notification is available via - * {@link #registerForPhoneStateChanged(android.os.Handler, int, + * {@link #registerForPhoneStateChanged(android.os.Handler, int, * java.lang.Object) registerForPhoneStateChanged()}. */ Call getBackgroundCall(); - /** - * Gets the ringing call object, which represents an incoming + /** + * Gets the ringing call object, which represents an incoming * connection (if present) that is pending answer/accept. (This connection * may be RINGING or WAITING, and there may be only one.)

      * The ringing call is a singleton object. It is constant for the life * of this phone. It is never null.

      - * + * * The ringing call will only ever be in one of these states: * IDLE, INCOMING, WAITING or DISCONNECTED. * * State change notification is available via - * {@link #registerForPhoneStateChanged(android.os.Handler, int, + * {@link #registerForPhoneStateChanged(android.os.Handler, int, * java.lang.Object) registerForPhoneStateChanged()}. */ Call getRingingCall(); - /** + /** * Initiate a new voice connection. This happens asynchronously, so you * cannot assume the audio path is connected (or a call index has been * assigned) until PhoneStateChanged notification has occurred. * * @exception CallStateException if a new outgoing call is not currently - * possible because no more call slots exist or a call exists that is - * dialing, alerting, ringing, or waiting. Other errors are + * possible because no more call slots exist or a call exists that is + * dialing, alerting, ringing, or waiting. Other errors are * handled asynchronously. */ Connection dial(String dialString) throws CallStateException; @@ -579,7 +666,7 @@ public interface Phone { /** * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated * without SEND (so dial is not appropriate). - * + * * @param dialString the MMI command to be executed. * @return true if MMI command is executed. */ @@ -597,7 +684,7 @@ public interface Phone { boolean handleInCallMmiCommands(String command) throws CallStateException; /** - * Play a DTMF tone on the active call. Ignored if there is no active call. + * Play a DTMF tone on the active call. Ignored if there is no active call. * @param c should be one of 0-9, '*' or '#'. Other values will be * silently ignored. */ @@ -619,20 +706,20 @@ public interface Phone { /** - * Sets the radio power on/off state (off is sometimes - * called "airplane mode"). Current state can be gotten via - * {@link #getServiceState()}.{@link + * Sets the radio power on/off state (off is sometimes + * called "airplane mode"). Current state can be gotten via + * {@link #getServiceState()}.{@link * android.telephony.ServiceState#getState() getState()}. - * Note: This request is asynchronous. + * Note: This request is asynchronous. * getServiceState().getState() will not change immediately after this call. - * registerForServiceStateChanged() to find out when the + * registerForServiceStateChanged() to find out when the * request is complete. * - * @param power true means "on", false means "off". + * @param power true means "on", false means "off". */ void setRadioPower(boolean power); - /** + /** * Get voice message waiting indicator status. No change notification * available on this interface. Use PhoneStateNotifier or similar instead. * @@ -674,8 +761,8 @@ public interface Phone { void setLine1Number(String alphaTag, String number, Message onComplete); /** - * Get the voice mail access phone number. Typically dialed when the - * user holds the "1" key in the phone app. May return null if not + * Get the voice mail access phone number. Typically dialed when the + * user holds the "1" key in the phone app. May return null if not * available or the SIM is not ready.

      */ String getVoiceMailNumber(); @@ -684,8 +771,8 @@ public interface Phone { * Returns the alpha tag associated with the voice mail number. * If there is no alpha tag associated or the record is not yet available, * returns a default localized string.

      - * - * Please use this value instead of some other localized string when + * + * Please use this value instead of some other localized string when * showing a name for this number in the UI. For example, call log * entries should show this alpha tag.

      * @@ -708,29 +795,29 @@ public interface Phone { /** * getCallForwardingOptions - * gets a call forwarding option. The return value of - * ((AsyncResult)onComplete.obj) is an array of CallForwardInfo. - * - * @param commandInterfaceCFReason is one of the valid call forwarding - * CF_REASONS, as defined in - * com.android.internal.telephony.gsm.CommandsInterface + * gets a call forwarding option. The return value of + * ((AsyncResult)onComplete.obj) is an array of CallForwardInfo. + * + * @param commandInterfaceCFReason is one of the valid call forwarding + * CF_REASONS, as defined in + * com.android.internal.telephony.CommandsInterface./code> * @param onComplete a callback message when the action is completed. - * @see com.android.internal.telephony.gsm.CallForwardInfo for details. + * @see com.android.internal.telephony.CallForwardInfo for details. */ void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete); - + /** * setCallForwardingOptions * sets a call forwarding option. - * - * @param commandInterfaceCFReason is one of the valid call forwarding - * CF_REASONS, as defined in - * com.android.internal.telephony.gsm.CommandsInterface - * @param commandInterfaceCFAction is one of the valid call forwarding - * CF_ACTIONS, as defined in - * com.android.internal.telephony.gsm.CommandsInterface - * @param dialingNumber is the target phone number to forward calls to + * + * @param commandInterfaceCFReason is one of the valid call forwarding + * CF_REASONS, as defined in + * com.android.internal.telephony.CommandsInterface./code> + * @param commandInterfaceCFAction is one of the valid call forwarding + * CF_ACTIONS, as defined in + * com.android.internal.telephony.CommandsInterface./code> + * @param dialingNumber is the target phone number to forward calls to * @param timerSeconds is used by CFNRy to indicate the timeout before * forwarding is attempted. * @param onComplete a callback message when the action is completed. @@ -740,83 +827,83 @@ public interface Phone { String dialingNumber, int timerSeconds, Message onComplete); - + /** * getOutgoingCallerIdDisplay - * gets outgoing caller id display. The return value of + * gets outgoing caller id display. The return value of * ((AsyncResult)onComplete.obj) is an array of int, with a length of 2. - * + * * @param onComplete a callback message when the action is completed. - * @see com.android.internal.telephony.gsm.CommandsInterface.getCLIR for details. + * @see com.android.internal.telephony.CommandsInterface.getCLIR for details. */ void getOutgoingCallerIdDisplay(Message onComplete); - + /** * setOutgoingCallerIdDisplay - * sets a call forwarding option. - * - * @param commandInterfaceCLIRMode is one of the valid call CLIR - * modes, as defined in - * com.android.internal.telephony.gsm.CommandsInterface + * sets a call forwarding option. + * + * @param commandInterfaceCLIRMode is one of the valid call CLIR + * modes, as defined in + * com.android.internal.telephony.CommandsInterface./code> * @param onComplete a callback message when the action is completed. */ void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete); - + /** * getCallWaiting - * gets call waiting activation state. The return value of + * gets call waiting activation state. The return value of * ((AsyncResult)onComplete.obj) is an array of int, with a length of 1. - * + * * @param onComplete a callback message when the action is completed. - * @see com.android.internal.telephony.gsm.CommandsInterface.queryCallWaiting for details. + * @see com.android.internal.telephony.CommandsInterface.queryCallWaiting for details. */ void getCallWaiting(Message onComplete); - + /** * setCallWaiting - * sets a call forwarding option. - * - * @param enable is a boolean representing the state that you are + * sets a call forwarding option. + * + * @param enable is a boolean representing the state that you are * requesting, true for enabled, false for disabled. * @param onComplete a callback message when the action is completed. */ void setCallWaiting(boolean enable, Message onComplete); - + /** * Scan available networks. This method is asynchronous; . * On completion, response.obj is set to an AsyncResult with * one of the following members:.

      *

        - *
      • response.obj.result will be a List of - * com.android.internal.telephony.gsm.NetworkInfo objects, or
      • - *
      • response.obj.exception will be set with an exception + *
      • response.obj.result will be a List of + * com.android.internal.telephony.gsm.NetworkInfo objects, or
      • + *
      • response.obj.exception will be set with an exception * on failure.
      • *
      */ - void getAvailableNetworks(Message response); + void getAvailableNetworks(Message response); /** * Switches network selection mode to "automatic", re-scanning and * re-selecting a network if appropriate. - * - * @param response The message to dispatch when the network selection + * + * @param response The message to dispatch when the network selection * is complete. - * - * @see #selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo, + * + * @see #selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo, * android.os.Message ) */ void setNetworkSelectionModeAutomatic(Message response); /** - * Manually selects a network. response is + * Manually selects a network. response is * dispatched when this is complete. response.obj will be * an AsyncResult, and response.obj.exception will be non-null * on failure. - * + * * @see #setNetworkSelectionModeAutomatic(Message) */ - void selectNetworkManually(NetworkInfo network, + void selectNetworkManually(NetworkInfo network, Message response); /** @@ -843,7 +930,7 @@ public interface Phone { * of available cell IDs. Cell IDs are in hexadecimal format. * * @param response callback message that is dispatched when the query - * completes. + * completes. */ void getNeighboringCids(Message response); @@ -856,28 +943,28 @@ public interface Phone { * AsyncResult. Message.obj.result will be * a Connection object.

      * - * Message.arg1 will be the post dial character being processed, + * Message.arg1 will be the post dial character being processed, * or 0 ('\0') if end of string.

      * - * If Connection.getPostDialState() == WAIT, - * the application must call - * {@link com.android.internal.telephony.Connection#proceedAfterWaitChar() - * Connection.proceedAfterWaitChar()} or - * {@link com.android.internal.telephony.Connection#cancelPostDial() + * If Connection.getPostDialState() == WAIT, + * the application must call + * {@link com.android.internal.telephony.Connection#proceedAfterWaitChar() + * Connection.proceedAfterWaitChar()} or + * {@link com.android.internal.telephony.Connection#cancelPostDial() * Connection.cancelPostDial()} - * for the telephony system to continue playing the post-dial + * for the telephony system to continue playing the post-dial * DTMF sequence.

      * - * If Connection.getPostDialState() == WILD, - * the application must call + * If Connection.getPostDialState() == WILD, + * the application must call * {@link com.android.internal.telephony.Connection#proceedAfterWildChar * Connection.proceedAfterWildChar()} - * or - * {@link com.android.internal.telephony.Connection#cancelPostDial() + * or + * {@link com.android.internal.telephony.Connection#cancelPostDial() * Connection.cancelPostDial()} - * for the telephony system to continue playing the + * for the telephony system to continue playing the * post-dial DTMF sequence.

      - * + * * Only one post dial character handler may be set.

      * Calling this method with "h" equal to null unsets this handler.

      */ @@ -885,19 +972,19 @@ public interface Phone { /** - * Mutes or unmutes the microphone for the active call. The microphone - * is automatically unmuted if a call is answered, dialed, or resumed + * Mutes or unmutes the microphone for the active call. The microphone + * is automatically unmuted if a call is answered, dialed, or resumed * from a holding state. - * - * @param muted true to mute the microphone, + * + * @param muted true to mute the microphone, * false to activate the microphone. */ void setMute(boolean muted); /** - * Gets current mute status. Use - * {@link #registerForPhoneStateChanged(android.os.Handler, int, + * Gets current mute status. Use + * {@link #registerForPhoneStateChanged(android.os.Handler, int, * java.lang.Object) registerForPhoneStateChanged()} * as a change notifcation, although presently phone state changed is not * fired when setMute() is called. @@ -908,12 +995,12 @@ public interface Phone { /** * Invokes RIL_REQUEST_OEM_HOOK_RAW on RIL implementation. - * + * * @param data The data for the request. - * @param response On success, + * @param response On success, * (byte[])(((AsyncResult)response.obj).result) - * On failure, - * (((AsyncResult)response.obj).result) == null and + * On failure, + * (((AsyncResult)response.obj).result) == null and * (((AsyncResult)response.obj).exception) being an instance of * com.android.internal.telephony.gsm.CommandException * @@ -923,13 +1010,13 @@ public interface Phone { /** * Invokes RIL_REQUEST_OEM_HOOK_Strings on RIL implementation. - * + * * @param strings The strings to make available as the request data. - * @param response On success, "response" bytes is + * @param response On success, "response" bytes is * made available as: * (String[])(((AsyncResult)response.obj).result). - * On failure, - * (((AsyncResult)response.obj).result) == null and + * On failure, + * (((AsyncResult)response.obj).result) == null and * (((AsyncResult)response.obj).exception) being an instance of * com.android.internal.telephony.gsm.CommandException * @@ -940,6 +1027,7 @@ public interface Phone { /** * Get the current active PDP context list * + * @deprecated * @param response On success, "response" bytes is * made available as: * (String[])(((AsyncResult)response.obj).result). @@ -951,13 +1039,34 @@ public interface Phone { void getPdpContextList(Message response); /** + * Get the current active Data Call list, substitutes getPdpContextList + * + * @param response On success, "response" bytes is + * made available as: + * (String[])(((AsyncResult)response.obj).result). + * On failure, + * (((AsyncResult)response.obj).result) == null and + * (((AsyncResult)response.obj).exception) being an instance of + * com.android.internal.telephony.gsm.CommandException + */ + void getDataCallList(Message response); + + /** * Get current mutiple PDP link status - * + * + * @deprecated * @return list of pdp link connections */ List getCurrentPdpList (); /** + * Get current mutiple data connection status + * + * @return list of data connections + */ + List getCurrentDataConnectionList (); + + /** * Udpate LAC and CID in service state for currnet GSM netowrk registration * * If get different LAC and/or CID, notifyServiceState will be sent @@ -981,11 +1090,11 @@ public interface Phone { void disableLocationUpdates(); /** - * For unit tests; don't send notifications to "Phone" + * For unit tests; don't send notifications to "Phone" * mailbox registrants if true. */ void setUnitTestMode(boolean f); - + /** * @return true If unit test mode is enabled */ @@ -1019,8 +1128,29 @@ public interface Phone { void setDataRoamingEnabled(boolean enable); /** + * Query the CDMA roaming preference setting + * + * @param response is callback message to report one of CDMA_RM_* + */ + void queryCdmaRoamingPreference(Message response); + + /** + * Requests to set the CDMA roaming preference + * @param cdmaRoamingType one of CDMA_RM_* + * @param response is callback message + */ + void setCdmaRoamingPreference(int cdmaRoamingType, Message response); + + /** + * Requests to set the CDMA subscription mode + * @param cdmaSubscriptionType one of CDMA_SUBSCRIPTION_* + * @param response is callback message + */ + void setCdmaSubscription(int cdmaSubscriptionType, Message response); + + /** * If this is a simulated phone interface, returns a SimulatedRadioControl. - * @ return A SimulatedRadioControl if this is a simulated interface; + * @ return A SimulatedRadioControl if this is a simulated interface; * otherwise, null. */ SimulatedRadioControl getSimulatedRadioControl(); @@ -1109,7 +1239,7 @@ public interface Phone { public String[] getDnsServers(String apnType); /** - * Retrieves the unique device ID, e.g., IMEI for GSM phones. + * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones. */ String getDeviceId(); @@ -1125,7 +1255,81 @@ public interface Phone { String getSubscriberId(); /** - * Retrieves the serial number of the SIM, if applicable. + * Retrieves the serial number of the ICC, if applicable. + */ + String getIccSerialNumber(); + + //***** CDMA support methods + + + /** + * Retrieves the ESN for CDMA phones. */ - String getSimSerialNumber(); + String getEsn(); + + /** + * Retrieves MEID for CDMA phones. + */ + String getMeid(); + + /** + * Retrieves the PhoneSubInfo of the Phone + */ + public PhoneSubInfo getPhoneSubInfo(); + + /** + * Retrieves the IccSmsInterfaceManager of the Phone + */ + public IccSmsInterfaceManager getIccSmsInterfaceManager(); + + /** + * Retrieves the IccPhoneBookInterfaceManager of the Phone + */ + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(); + + /** + * setTTYModeEnabled + * sets a TTY mode option. + * + * @param enable is a boolean representing the state that you are + * requesting, true for enabled, false for disabled. + * @param onComplete a callback message when the action is completed + */ + void setTTYModeEnabled(boolean enable, Message onComplete); + + /** + * queryTTYModeEnabled + * query the status of the TTY mode + * + * @param onComplete a callback message when the action is completed. + */ + void queryTTYModeEnabled(Message onComplete); + + /** + * Activate or deactivate cell broadcast SMS. + * + * @param activate + * 0 = activate, 1 = deactivate + * @param response + * Callback message is empty on completion + */ + void activateCellBroadcastSms(int activate, Message response); + + /** + * Query the current configuration of cdma cell broadcast SMS. + * + * @param response + * Callback message is empty on completion + */ + void getCellBroadcastSmsConfig(Message response); + + /** + * Configure cell broadcast SMS. + * + * @param response + * Callback message is empty on completion + */ + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response); + + public void notifyDataActivity(); } diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 4fb5f61..259de62 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -20,23 +20,30 @@ import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.content.Context; import android.content.res.Configuration; +import android.content.SharedPreferences; import android.os.AsyncResult; import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.os.RegistrantList; import android.os.SystemProperties; +import android.preference.PreferenceManager; import android.telephony.ServiceState; +import android.text.TextUtils; import android.util.Log; + import com.android.internal.R; +import com.android.internal.telephony.gsm.PdpConnection; import com.android.internal.telephony.test.SimulatedRadioControl; import java.util.List; import java.util.Locale; + /** - * (Not for SDK use) + * (Not for SDK use) * A base implementation for the com.android.internal.telephony.Phone interface. - * + * * Note that implementations of Phone.java are expected to be used * from a single application thread. This should be the same thread that * originally called PhoneFactory to obtain the interface. @@ -46,42 +53,101 @@ import java.util.Locale; */ public abstract class PhoneBase implements Phone { - private static final String LOG_TAG = "GSM"; + private static final String LOG_TAG = "PHONE"; + private static final boolean LOCAL_DEBUG = true; + + // Key used to read and write the saved network selection value + public static final String NETWORK_SELECTION_KEY = "network_selection_key"; + + // Key used to read/write "disable data connection on boot" pref (used for testing) + public static final String DATA_DISABLED_ON_BOOT_KEY = "disabled_on_boot_key"; + + //***** Event Constants + protected static final int EVENT_RADIO_AVAILABLE = 1; + /** Supplementary Service Notification received. */ + protected static final int EVENT_SSN = 2; + protected static final int EVENT_SIM_RECORDS_LOADED = 3; + protected static final int EVENT_MMI_DONE = 4; + protected static final int EVENT_RADIO_ON = 5; + protected static final int EVENT_GET_BASEBAND_VERSION_DONE = 6; + protected static final int EVENT_USSD = 7; + protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8; + protected static final int EVENT_GET_IMEI_DONE = 9; + protected static final int EVENT_GET_IMEISV_DONE = 10; + protected static final int EVENT_GET_SIM_STATUS_DONE = 11; + protected static final int EVENT_SET_CALL_FORWARD_DONE = 12; + protected static final int EVENT_GET_CALL_FORWARD_DONE = 13; + protected static final int EVENT_CALL_RING = 14; + // Used to intercept the carrier selection calls so that + // we can save the values. + protected static final int EVENT_SET_NETWORK_MANUAL_COMPLETE = 15; + protected static final int EVENT_SET_NETWORK_AUTOMATIC_COMPLETE = 16; + protected static final int EVENT_SET_CLIR_COMPLETE = 17; + protected static final int EVENT_REGISTERED_TO_NETWORK = 18; + protected static final int EVENT_SET_VM_NUMBER_DONE = 19; + // Events for CDMA support + protected static final int EVENT_GET_DEVICE_IDENTITY_DONE = 20; + protected static final int EVENT_RUIM_RECORDS_LOADED = 21; + protected static final int EVENT_NV_READY = 22; + protected static final int EVENT_SET_ENHANCED_VP = 23; + + // Key used to read/write current CLIR setting + public static final String CLIR_KEY = "clir_key"; + + // Key used to read/write "disable DNS server check" pref (used for testing) + public static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key"; + + //***** Instance Variables + public CommandsInterface mCM; + protected IccFileHandler mIccFileHandler; + boolean mDnsCheckDisabled = false; + + /** + * Set a system property, unless we're in unit test mode + */ + public void + setSystemProperty(String property, String value) { + if(getUnitTestMode()) { + return; + } + SystemProperties.set(property, value); + } - protected final RegistrantList mPhoneStateRegistrants + + protected final RegistrantList mPhoneStateRegistrants = new RegistrantList(); - protected final RegistrantList mNewRingingConnectionRegistrants + protected final RegistrantList mNewRingingConnectionRegistrants = new RegistrantList(); - protected final RegistrantList mIncomingRingRegistrants + protected final RegistrantList mIncomingRingRegistrants = new RegistrantList(); - - protected final RegistrantList mDisconnectRegistrants + + protected final RegistrantList mDisconnectRegistrants = new RegistrantList(); - protected final RegistrantList mServiceStateRegistrants + protected final RegistrantList mServiceStateRegistrants = new RegistrantList(); - - protected final RegistrantList mMmiCompleteRegistrants + + protected final RegistrantList mMmiCompleteRegistrants = new RegistrantList(); - protected final RegistrantList mMmiRegistrants + protected final RegistrantList mMmiRegistrants = new RegistrantList(); - protected final RegistrantList mUnknownConnectionRegistrants + protected final RegistrantList mUnknownConnectionRegistrants = new RegistrantList(); - - protected final RegistrantList mSuppServiceFailedRegistrants + + protected final RegistrantList mSuppServiceFailedRegistrants = new RegistrantList(); - + protected Looper mLooper; /* to insure registrants are in correct thread*/ protected Context mContext; - /** - * PhoneNotifier is an abstraction for all system-wide - * state change notification. DefaultPhoneNotifier is + /** + * PhoneNotifier is an abstraction for all system-wide + * state change notification. DefaultPhoneNotifier is * used here unless running we're inside a unit test. */ protected PhoneNotifier mNotifier; @@ -94,7 +160,7 @@ public abstract class PhoneBase implements Phone { * Constructs a PhoneBase in normal (non-unit test) mode. * * @param context Context object from hosting application - * @param notifier An instance of DefaultPhoneNotifier, + * @param notifier An instance of DefaultPhoneNotifier, * unless unit testing. */ protected PhoneBase(PhoneNotifier notifier, Context context) { @@ -105,13 +171,13 @@ public abstract class PhoneBase implements Phone { * Constructs a PhoneBase in normal (non-unit test) mode. * * @param context Context object from hosting application - * @param notifier An instance of DefaultPhoneNotifier, + * @param notifier An instance of DefaultPhoneNotifier, * unless unit testing. - * @param unitTestMode when true, prevents notifications + * @param unitTestMode when true, prevents notifications * of state change events */ - protected PhoneBase(PhoneNotifier notifier, Context context, - boolean unitTestMode) { + protected PhoneBase(PhoneNotifier notifier, Context context, + boolean unitTestMode) { this.mNotifier = notifier; this.mContext = context; mLooper = Looper.myLooper(); @@ -119,6 +185,9 @@ public abstract class PhoneBase implements Phone { setLocaleByCarrier(); setUnitTestMode(unitTestMode); + + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false); } // Inherited documentation suffices. @@ -126,6 +195,26 @@ public abstract class PhoneBase implements Phone { return mContext; } + /** + * Disables the DNS check (i.e., allows "0.0.0.0"). + * Useful for lab testing environment. + * @param b true disables the check, false enables. + */ + public void disableDnsCheck(boolean b) { + mDnsCheckDisabled = b; + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences.Editor editor = sp.edit(); + editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b); + editor.commit(); + } + + /** + * Returns true if the DNS check is currently disabled. + */ + public boolean isDnsCheckDisabled() { + return mDnsCheckDisabled; + } + // Inherited documentation suffices. public void registerForPhoneStateChanged(Handler h, int what, Object obj) { checkCorrectThread(h); @@ -137,29 +226,29 @@ public abstract class PhoneBase implements Phone { public void unregisterForPhoneStateChanged(Handler h) { mPhoneStateRegistrants.remove(h); } - + /** * Notify registrants of a PhoneStateChanged. - * Subclasses of Phone probably want to replace this with a + * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ protected void notifyCallStateChangedP() { AsyncResult ar = new AsyncResult(null, this, null); mPhoneStateRegistrants.notifyRegistrants(ar); } - + // Inherited documentation suffices. public void registerForUnknownConnection(Handler h, int what, Object obj) { checkCorrectThread(h); - + mUnknownConnectionRegistrants.addUnique(h, what, obj); } - + // Inherited documentation suffices. public void unregisterForUnknownConnection(Handler h) { mUnknownConnectionRegistrants.remove(h); } - + // Inherited documentation suffices. public void registerForNewRingingConnection( Handler h, int what, Object obj) { @@ -173,12 +262,33 @@ public abstract class PhoneBase implements Phone { mNewRingingConnectionRegistrants.remove(h); } + // Inherited documentation suffices. + public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){ + mCM.registerForInCallVoicePrivacyOn(h,what,obj); + } + + // Inherited documentation suffices. + public void unregisterForInCallVoicePrivacyOn(Handler h){ + mCM.unregisterForInCallVoicePrivacyOn(h); + } + + // Inherited documentation suffices. + public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){ + mCM.registerForInCallVoicePrivacyOff(h,what,obj); + } + + // Inherited documentation suffices. + public void unregisterForInCallVoicePrivacyOff(Handler h){ + mCM.unregisterForInCallVoicePrivacyOff(h); + } + + /** * Notifiy registrants of a new ringing Connection. - * Subclasses of Phone probably want to replace this with a + * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ - protected void notifyNewRingingConnectionP(Connection cn) { + protected void notifyNewRingingConnectionP(Connection cn) { AsyncResult ar = new AsyncResult(null, cn, null); mNewRingingConnectionRegistrants.notifyRegistrants(ar); } @@ -187,15 +297,15 @@ public abstract class PhoneBase implements Phone { public void registerForIncomingRing( Handler h, int what, Object obj) { checkCorrectThread(h); - + mIncomingRingRegistrants.addUnique(h, what, obj); } - + // Inherited documentation suffices. public void unregisterForIncomingRing(Handler h) { mIncomingRingRegistrants.remove(h); } - + // Inherited documentation suffices. public void registerForDisconnect(Handler h, int what, Object obj) { checkCorrectThread(h); @@ -211,15 +321,15 @@ public abstract class PhoneBase implements Phone { // Inherited documentation suffices. public void registerForSuppServiceFailed(Handler h, int what, Object obj) { checkCorrectThread(h); - + mSuppServiceFailedRegistrants.addUnique(h, what, obj); } - + // Inherited documentation suffices. public void unregisterForSuppServiceFailed(Handler h) { mSuppServiceFailedRegistrants.remove(h); } - + // Inherited documentation suffices. public void registerForMmiInitiate(Handler h, int what, Object obj) { checkCorrectThread(h); @@ -231,7 +341,7 @@ public abstract class PhoneBase implements Phone { public void unregisterForMmiInitiate(Handler h) { mMmiRegistrants.remove(h); } - + // Inherited documentation suffices. public void registerForMmiComplete(Handler h, int what, Object obj) { checkCorrectThread(h); @@ -247,10 +357,31 @@ public abstract class PhoneBase implements Phone { } /** - * Subclasses should override this. See documentation in superclass. + * Method to retrieve the saved operator id from the Shared Preferences */ - public abstract List getPendingMmiCodes(); - + private String getSavedNetworkSelection() { + // open the shared preferences and search with our key. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + return sp.getString(NETWORK_SELECTION_KEY, ""); + } + + /** + * Method to restore the previously saved operator id, or reset to + * automatic selection, all depending upon the value in the shared + * preferences. + */ + public void restoreSavedNetworkSelection(Message response) { + // retrieve the operator id + String networkSelection = getSavedNetworkSelection(); + + // set to auto if the id is empty, otherwise select the network. + if (TextUtils.isEmpty(networkSelection)) { + mCM.setNetworkSelectionModeAutomatic(response); + } else { + mCM.setNetworkSelectionModeManual(networkSelection, response); + } + } + // Inherited documentation suffices. public void setUnitTestMode(boolean f) { mUnitTestMode = f; @@ -260,11 +391,11 @@ public abstract class PhoneBase implements Phone { public boolean getUnitTestMode() { return mUnitTestMode; } - + /** * To be invoked when a voice call Connection disconnects. * - * Subclasses of Phone probably want to replace this with a + * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ protected void notifyDisconnectP(Connection cn) { @@ -286,7 +417,7 @@ public abstract class PhoneBase implements Phone { } /** - * Subclasses of Phone probably want to replace this with a + * Subclasses of Phone probably want to replace this with a * version scoped to their packages */ protected void notifyServiceStateChangedP(ServiceState ss) { @@ -312,7 +443,7 @@ public abstract class PhoneBase implements Phone { private void checkCorrectThread(Handler h) { if (h.getLooper() != mLooper) { throw new RuntimeException( - "com.android.internal.telephony.Phone must be used from within one thread"); + "com.android.internal.telephony.Phone must be used from within one thread"); } } @@ -401,4 +532,107 @@ public abstract class PhoneBase implements Phone { } } } + + /* + * Retrieves the Handler of the Phone instance + */ + public abstract Handler getHandler(); + + /** + * Retrieves the IccFileHandler of the Phone instance + */ + public abstract IccFileHandler getIccFileHandler(); + + + /** + * Query the status of the CDMA roaming preference + */ + public void queryCdmaRoamingPreference(Message response) { + mCM.queryCdmaRoamingPreference(response); + } + + /** + * Set the status of the CDMA roaming preference + */ + public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { + mCM.setCdmaRoamingPreference(cdmaRoamingType, response); + } + + /** + * Set the status of the CDMA subscription mode + */ + public void setCdmaSubscription(int cdmaSubscriptionType, Message response) { + mCM.setCdmaSubscription(cdmaSubscriptionType, response); + } + + /** + * Set the preferred Network Type: Global, CDMA only or GSM/UMTS only + */ + public void setPreferredNetworkType(int networkType, Message response) { + mCM.setPreferredNetworkType(networkType, response); + } + + /** + * Set the status of the preferred Network Type: Global, CDMA only or GSM/UMTS only + */ + public void getPreferredNetworkType(Message response) { + mCM.getPreferredNetworkType(response); + } + + public void setTTYModeEnabled(boolean enable, Message onComplete) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void queryTTYModeEnabled(Message onComplete) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + /** + * This should only be called in GSM mode. + * Only here for some backward compatibility + * issues concerning the GSMPhone class. + * @deprecated + */ + public List getCurrentPdpList() { + return null; + } + + public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void getEnhancedVoicePrivacy(Message onComplete) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void setBandMode(int bandMode, Message response) { + mCM.setBandMode(bandMode, response); + } + + public void queryAvailableBandMode(Message response) { + mCM.queryAvailableBandMode(response); + } + + public void invokeOemRilRequestRaw(byte[] data, Message response) { + mCM.invokeOemRilRequestRaw(data, response); + } + + public void invokeOemRilRequestStrings(String[] strings, Message response) { + mCM.invokeOemRilRequestStrings(strings, response); + } + + public void notifyDataActivity() { + mNotifier.notifyDataActivity(this); + } + + public void notifyDataConnection(String reason) { + mNotifier.notifyDataConnection(this, reason); + } + + public abstract String getPhoneName(); + } diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java index 0ca5f45..3db0499 100644 --- a/telephony/java/com/android/internal/telephony/PhoneFactory.java +++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java @@ -16,80 +16,55 @@ package com.android.internal.telephony; -import java.util.ArrayList; -import java.util.List; -import java.io.IOException; -import java.net.InetSocketAddress; - -import java.util.Collections; - -import android.util.Log; -import com.android.internal.telephony.gsm.GSMPhone; -import com.android.internal.telephony.gsm.RIL; -import com.android.internal.telephony.test.ModelInterpreter; -import com.android.internal.telephony.test.SimulatedCommands; -import android.os.Looper; -import android.os.SystemProperties; import android.content.Context; -import android.content.Intent; import android.net.LocalServerSocket; -import android.app.ActivityManagerNative; +import android.os.Looper; +import android.provider.Settings; +import android.util.Log; + +import com.android.internal.telephony.cdma.CDMAPhone; +import com.android.internal.telephony.gsm.GSMPhone; /** * {@hide} */ -public class PhoneFactory -{ - static final String LOG_TAG="GSM"; - +public class PhoneFactory { + static final String LOG_TAG = "PHONE"; static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000; static final int SOCKET_OPEN_MAX_RETRY = 3; - //***** Class Variables + //***** Class Variables - static private ArrayList sPhones = new ArrayList(); + static private Phone sProxyPhone = null; + static private CommandsInterface sCommandsInterface = null; static private boolean sMadeDefaults = false; static private PhoneNotifier sPhoneNotifier; static private Looper sLooper; + static private Context sContext; - static private Object testMailbox; - - //***** Class Methods + static final int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE; - private static void - useNewRIL(Context context) - { - ModelInterpreter mi = null; - GSMPhone phone; + static final int preferredCdmaSubscription = RILConstants.PREFERRED_CDMA_SUBSCRIPTION; - try { - if (false) { - mi = new ModelInterpreter(new InetSocketAddress("127.0.0.1", 6502)); - } - - phone = new GSMPhone(context, new RIL(context), sPhoneNotifier); + //***** Class Methods - registerPhone (phone); - } catch (IOException ex) { - Log.e(LOG_TAG, "Error creating ModelInterpreter", ex); - } + public static void makeDefaultPhones(Context context) { + makeDefaultPhone(context); } - /** * FIXME replace this with some other way of making these * instances */ - public static void - makeDefaultPhones(Context context) - { - synchronized(Phone.class) { - if (!sMadeDefaults) { + public static void makeDefaultPhone(Context context) { + synchronized(Phone.class) { + if (!sMadeDefaults) { sLooper = Looper.myLooper(); + sContext = context; if (sLooper == null) { throw new RuntimeException( - "PhoneFactory.makeDefaultPhones must be called from Looper thread"); + "PhoneFactory.makeDefaultPhone must be called from Looper thread"); } int retryCount = 0; @@ -109,7 +84,7 @@ public class PhoneFactory break; } else if (retryCount > SOCKET_OPEN_MAX_RETRY) { throw new RuntimeException("PhoneFactory probably already running"); - }else { + } else { try { Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); } catch (InterruptedException er) { @@ -119,44 +94,71 @@ public class PhoneFactory sPhoneNotifier = new DefaultPhoneNotifier(); - if ((SystemProperties.get("ro.radio.noril","")).equals("")) { - useNewRIL(context); - } else { - GSMPhone phone; - phone = new GSMPhone(context, new SimulatedCommands(), sPhoneNotifier); - registerPhone (phone); + //Get preferredNetworkMode from Settings.System + int networkMode = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode); + Log.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode)); + + //Get preferredNetworkMode from Settings.System + int cdmaSubscription = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PREFERRED_CDMA_SUBSCRIPTION, preferredCdmaSubscription); + Log.i(LOG_TAG, "Cdma Subscription set to " + Integer.toString(cdmaSubscription)); + + //reads the system properties and makes commandsinterface + sCommandsInterface = new RIL(context, networkMode, cdmaSubscription); + + switch(networkMode) { + case RILConstants.NETWORK_MODE_WCDMA_PREF: + case RILConstants.NETWORK_MODE_GSM_ONLY: + case RILConstants.NETWORK_MODE_WCDMA_ONLY: + case RILConstants.NETWORK_MODE_GSM_UMTS: + sProxyPhone = new PhoneProxy(new GSMPhone(context, + sCommandsInterface, sPhoneNotifier)); + Log.i(LOG_TAG, "Creating GSMPhone"); + break; + case RILConstants.NETWORK_MODE_CDMA: + case RILConstants.NETWORK_MODE_CDMA_NO_EVDO: + case RILConstants.NETWORK_MODE_EVDO_NO_CDMA: + sProxyPhone = new PhoneProxy(new CDMAPhone(context, + sCommandsInterface, sPhoneNotifier)); + Log.i(LOG_TAG, "Creating CDMAPhone"); + break; + case RILConstants.NETWORK_MODE_GLOBAL: + default: + sProxyPhone = new PhoneProxy(new CDMAPhone(context, + sCommandsInterface, sPhoneNotifier)); + Log.i(LOG_TAG, "Creating CDMAPhone"); } - sMadeDefaults = true; } } } - public static Phone getDefaultPhone() - { - if (!sMadeDefaults) { - throw new IllegalStateException("Default phones haven't been made yet!"); - } - + public static Phone getDefaultPhone() { if (sLooper != Looper.myLooper()) { throw new RuntimeException( "PhoneFactory.getDefaultPhone must be called from Looper thread"); } - synchronized (sPhones) { - return sPhones.isEmpty() ? null : sPhones.get(0); + if (!sMadeDefaults) { + throw new IllegalStateException("Default phones haven't been made yet!"); } + return sProxyPhone; } - - public static void registerPhone(Phone p) - { - if (sLooper != Looper.myLooper()) { - throw new RuntimeException( - "PhoneFactory.getDefaultPhone must be called from Looper thread"); + + public static Phone getCdmaPhone() { + synchronized(PhoneProxy.lockForRadioTechnologyChange) { + Phone phone = new CDMAPhone(sContext, sCommandsInterface, sPhoneNotifier); + return phone; } - synchronized (sPhones) { - sPhones.add(p); + } + + public static Phone getGsmPhone() { + synchronized(PhoneProxy.lockForRadioTechnologyChange) { + Phone phone = new GSMPhone(sContext, sCommandsInterface, sPhoneNotifier); + return phone; } } } + diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java new file mode 100644 index 0000000..dd36f0b --- /dev/null +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + + +import android.app.ActivityManagerNative; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Handler; +import android.os.Message; +import android.preference.PreferenceManager; +import android.telephony.CellLocation; +import android.telephony.ServiceState; +import android.util.Log; + +import com.android.internal.telephony.cdma.CDMAPhone; +import com.android.internal.telephony.gsm.GSMPhone; +import com.android.internal.telephony.gsm.NetworkInfo; +import com.android.internal.telephony.gsm.PdpConnection; +import com.android.internal.telephony.test.SimulatedRadioControl; + +import java.util.List; + +public class PhoneProxy extends Handler implements Phone { + public final static Object lockForRadioTechnologyChange = new Object(); +// private static boolean radioTechnologyChangeGsmToCdma = false; +// private static boolean radioTechnologyChangeCdmaToGsm = false; + + private Phone mActivePhone; + private String mOutgoingPhone; + private CommandsInterface mCommandsInterface; + private IccSmsInterfaceManagerProxy mIccSmsInterfaceManagerProxy; + private IccPhoneBookInterfaceManagerProxy mIccPhoneBookInterfaceManagerProxy; + private PhoneSubInfoProxy mPhoneSubInfoProxy; + + private static final int EVENT_RADIO_TECHNOLOGY_CHANGED = 1; + private static final String LOG_TAG = "PHONE"; + + //***** Class Methods + public PhoneProxy(Phone phone) { + mActivePhone = phone; + mIccSmsInterfaceManagerProxy = new IccSmsInterfaceManagerProxy( + phone.getIccSmsInterfaceManager()); + mIccPhoneBookInterfaceManagerProxy = new IccPhoneBookInterfaceManagerProxy( + phone.getIccPhoneBookInterfaceManager()); + mPhoneSubInfoProxy = new PhoneSubInfoProxy(phone.getPhoneSubInfo()); + mCommandsInterface = ((PhoneBase)mActivePhone).mCM; + mCommandsInterface.registerForRadioTechnologyChanged( + this, EVENT_RADIO_TECHNOLOGY_CHANGED, null); + } + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case EVENT_RADIO_TECHNOLOGY_CHANGED: + //switch Phone from CDMA to GSM or vice versa + mOutgoingPhone = ((PhoneBase)mActivePhone).getPhoneName(); + logd("Switching phone from " + mOutgoingPhone + "Phone to " + + (mOutgoingPhone.equals("GSM") ? "CDMAPhone" : "GSMPhone") ); + boolean oldPowerState = false; //old power state to off + if (mCommandsInterface.getRadioState().isOn()) { + oldPowerState = true; + logd("Setting Radio Power to Off"); + mCommandsInterface.setRadioPower(false, null); + } + if(mOutgoingPhone.equals("GSM")) { + logd("Make a new CDMAPhone and destroy the old GSMPhone."); + + ((GSMPhone)mActivePhone).dispose(); + Phone oldPhone = mActivePhone; + + //Give the garbage collector a hint to start the garbage collection asap + // NOTE this has been disabled since radio technology change could happen during + // e.g. a multimedia playing and could slow the system. Tests needs to be done + // to see the effects of the GC call here when system is busy. + //System.gc(); + + mActivePhone = PhoneFactory.getCdmaPhone(); + logd("Resetting Radio"); + mCommandsInterface.setRadioPower(oldPowerState, null); + ((GSMPhone)oldPhone).removeReferences(); + oldPhone = null; + } else { + logd("Make a new GSMPhone and destroy the old CDMAPhone."); + + ((CDMAPhone)mActivePhone).dispose(); + //mActivePhone = null; + Phone oldPhone = mActivePhone; + + // Give the GC a hint to start the garbage collection asap + // NOTE this has been disabled since radio technology change could happen during + // e.g. a multimedia playing and could slow the system. Tests needs to be done + // to see the effects of the GC call here when system is busy. + //System.gc(); + + mActivePhone = PhoneFactory.getGsmPhone(); + logd("Resetting Radio:"); + mCommandsInterface.setRadioPower(oldPowerState, null); + ((CDMAPhone)oldPhone).removeReferences(); + oldPhone = null; + } + + //Set the new interfaces in the proxy's + mIccSmsInterfaceManagerProxy.setmIccSmsInterfaceManager( + mActivePhone.getIccSmsInterfaceManager()); + mIccPhoneBookInterfaceManagerProxy.setmIccPhoneBookInterfaceManager( + mActivePhone.getIccPhoneBookInterfaceManager()); + mPhoneSubInfoProxy.setmPhoneSubInfo(this.mActivePhone.getPhoneSubInfo()); + mCommandsInterface = ((PhoneBase)mActivePhone).mCM; + + //Send an Intent to the PhoneApp that we had a radio technology change + Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); + intent.putExtra(Phone.PHONE_NAME_KEY, mActivePhone.getPhoneName()); + ActivityManagerNative.broadcastStickyIntent(intent, null); + + break; + default: + Log.e(LOG_TAG, "Error! This handler was not registered for this message type. Message: " + + msg.what); + break; + } + super.handleMessage(msg); + } + + private void logv(String msg) { + Log.v(LOG_TAG, "[PhoneProxy] " + msg); + } + + private void logd(String msg) { + Log.d(LOG_TAG, "[PhoneProxy] " + msg); + } + + private void logw(String msg) { + Log.w(LOG_TAG, "[PhoneProxy] " + msg); + } + + private void loge(String msg) { + Log.e(LOG_TAG, "[PhoneProxy] " + msg); + } + + + public ServiceState getServiceState() { + return mActivePhone.getServiceState(); + } + + public CellLocation getCellLocation() { + return mActivePhone.getCellLocation(); + } + + public DataState getDataConnectionState() { + return mActivePhone.getDataConnectionState(); + } + + public DataActivityState getDataActivityState() { + return mActivePhone.getDataActivityState(); + } + + public Context getContext() { + return mActivePhone.getContext(); + } + + public void disableDnsCheck(boolean b) { + mActivePhone.disableDnsCheck(b); + } + + public boolean isDnsCheckDisabled() { + return mActivePhone.isDnsCheckDisabled(); + } + + public State getState() { + return mActivePhone.getState(); + } + + public String getPhoneName() { + return mActivePhone.getPhoneName(); + } + + public String[] getActiveApnTypes() { + return mActivePhone.getActiveApnTypes(); + } + + public String getActiveApn() { + return mActivePhone.getActiveApn(); + } + + public int getSignalStrengthASU() { + return mActivePhone.getSignalStrengthASU(); + } + + public void registerForUnknownConnection(Handler h, int what, Object obj) { + mActivePhone.registerForUnknownConnection(h, what, obj); + } + + public void unregisterForUnknownConnection(Handler h) { + mActivePhone.unregisterForUnknownConnection(h); + } + + public void registerForPhoneStateChanged(Handler h, int what, Object obj) { + mActivePhone.registerForPhoneStateChanged(h, what, obj); + } + + public void unregisterForPhoneStateChanged(Handler h) { + mActivePhone.unregisterForPhoneStateChanged(h); + } + + public void registerForNewRingingConnection(Handler h, int what, Object obj) { + mActivePhone.registerForNewRingingConnection(h, what, obj); + } + + public void unregisterForNewRingingConnection(Handler h) { + mActivePhone.unregisterForNewRingingConnection(h); + } + + public void registerForIncomingRing(Handler h, int what, Object obj) { + mActivePhone.registerForIncomingRing(h, what, obj); + } + + public void unregisterForIncomingRing(Handler h) { + mActivePhone.unregisterForIncomingRing(h); + } + + public void registerForDisconnect(Handler h, int what, Object obj) { + mActivePhone.registerForDisconnect(h, what, obj); + } + + public void unregisterForDisconnect(Handler h) { + mActivePhone.unregisterForDisconnect(h); + } + + public void registerForMmiInitiate(Handler h, int what, Object obj) { + mActivePhone.registerForMmiInitiate(h, what, obj); + } + + public void unregisterForMmiInitiate(Handler h) { + mActivePhone.unregisterForMmiInitiate(h); + } + + public void registerForMmiComplete(Handler h, int what, Object obj) { + mActivePhone.registerForMmiComplete(h, what, obj); + } + + public void unregisterForMmiComplete(Handler h) { + mActivePhone.unregisterForMmiComplete(h); + } + + public List getPendingMmiCodes() { + return mActivePhone.getPendingMmiCodes(); + } + + public void sendUssdResponse(String ussdMessge) { + mActivePhone.sendUssdResponse(ussdMessge); + } + + public void registerForServiceStateChanged(Handler h, int what, Object obj) { + mActivePhone.registerForServiceStateChanged(h, what, obj); + } + + public void unregisterForServiceStateChanged(Handler h) { + mActivePhone.unregisterForServiceStateChanged(h); + } + + public void registerForSuppServiceNotification(Handler h, int what, Object obj) { + mActivePhone.registerForSuppServiceNotification(h, what, obj); + } + + public void unregisterForSuppServiceNotification(Handler h) { + mActivePhone.unregisterForSuppServiceNotification(h); + } + + public void registerForSuppServiceFailed(Handler h, int what, Object obj) { + mActivePhone.registerForSuppServiceFailed(h, what, obj); + } + + public void unregisterForSuppServiceFailed(Handler h) { + mActivePhone.unregisterForSuppServiceFailed(h); + } + + public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){ + mActivePhone.registerForInCallVoicePrivacyOn(h,what,obj); + } + + public void unregisterForInCallVoicePrivacyOn(Handler h){ + mActivePhone.unregisterForInCallVoicePrivacyOn(h); + } + + public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){ + mActivePhone.registerForInCallVoicePrivacyOff(h,what,obj); + } + + public void unregisterForInCallVoicePrivacyOff(Handler h){ + mActivePhone.unregisterForInCallVoicePrivacyOff(h); + } + + public boolean getIccRecordsLoaded() { + return mActivePhone.getIccRecordsLoaded(); + } + + public IccCard getIccCard() { + return mActivePhone.getIccCard(); + } + + public void acceptCall() throws CallStateException { + mActivePhone.acceptCall(); + } + + public void rejectCall() throws CallStateException { + mActivePhone.rejectCall(); + } + + public void switchHoldingAndActive() throws CallStateException { + mActivePhone.switchHoldingAndActive(); + } + + public boolean canConference() { + return mActivePhone.canConference(); + } + + public void conference() throws CallStateException { + mActivePhone.conference(); + } + + public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { + mActivePhone.enableEnhancedVoicePrivacy(enable, onComplete); + } + + public void getEnhancedVoicePrivacy(Message onComplete) { + mActivePhone.getEnhancedVoicePrivacy(onComplete); + } + + public boolean canTransfer() { + return mActivePhone.canTransfer(); + } + + public void explicitCallTransfer() throws CallStateException { + mActivePhone.explicitCallTransfer(); + } + + public void clearDisconnected() { + mActivePhone.clearDisconnected(); + } + + public Call getForegroundCall() { + return mActivePhone.getForegroundCall(); + } + + public Call getBackgroundCall() { + return mActivePhone.getBackgroundCall(); + } + + public Call getRingingCall() { + return mActivePhone.getRingingCall(); + } + + public Connection dial(String dialString) throws CallStateException { + return mActivePhone.dial(dialString); + } + + public boolean handlePinMmi(String dialString) { + return mActivePhone.handlePinMmi(dialString); + } + + public boolean handleInCallMmiCommands(String command) throws CallStateException { + return mActivePhone.handleInCallMmiCommands(command); + } + + public void sendDtmf(char c) { + mActivePhone.sendDtmf(c); + } + + public void startDtmf(char c) { + mActivePhone.startDtmf(c); + } + + public void stopDtmf() { + mActivePhone.stopDtmf(); + } + + public void setRadioPower(boolean power) { + mActivePhone.setRadioPower(power); + } + + public boolean getMessageWaitingIndicator() { + return mActivePhone.getMessageWaitingIndicator(); + } + + public boolean getCallForwardingIndicator() { + return mActivePhone.getCallForwardingIndicator(); + } + + public String getLine1Number() { + return mActivePhone.getLine1Number(); + } + + public String getLine1AlphaTag() { + return mActivePhone.getLine1AlphaTag(); + } + + public void setLine1Number(String alphaTag, String number, Message onComplete) { + mActivePhone.setLine1Number(alphaTag, number, onComplete); + } + + public String getVoiceMailNumber() { + return mActivePhone.getVoiceMailNumber(); + } + + public String getVoiceMailAlphaTag() { + return mActivePhone.getVoiceMailAlphaTag(); + } + + public void setVoiceMailNumber(String alphaTag,String voiceMailNumber, + Message onComplete) { + mActivePhone.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete); + } + + public void getCallForwardingOption(int commandInterfaceCFReason, + Message onComplete) { + mActivePhone.getCallForwardingOption(commandInterfaceCFReason, + onComplete); + } + + public void setCallForwardingOption(int commandInterfaceCFReason, + int commandInterfaceCFAction, String dialingNumber, + int timerSeconds, Message onComplete) { + mActivePhone.setCallForwardingOption(commandInterfaceCFReason, + commandInterfaceCFAction, dialingNumber, timerSeconds, onComplete); + } + + public void getOutgoingCallerIdDisplay(Message onComplete) { + mActivePhone.getOutgoingCallerIdDisplay(onComplete); + } + + public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, + Message onComplete) { + mActivePhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode, + onComplete); + } + + public void getCallWaiting(Message onComplete) { + mActivePhone.getCallWaiting(onComplete); + } + + public void setCallWaiting(boolean enable, Message onComplete) { + mActivePhone.setCallWaiting(enable, onComplete); + } + + public void getAvailableNetworks(Message response) { + mActivePhone.getAvailableNetworks(response); + } + + public void setNetworkSelectionModeAutomatic(Message response) { + mActivePhone.setNetworkSelectionModeAutomatic(response); + } + + public void selectNetworkManually(NetworkInfo network, Message response) { + mActivePhone.selectNetworkManually(network, response); + } + + public void setPreferredNetworkType(int networkType, Message response) { + mActivePhone.setPreferredNetworkType(networkType, response); + } + + public void getPreferredNetworkType(Message response) { + mActivePhone.getPreferredNetworkType(response); + } + + public void getNeighboringCids(Message response) { + mActivePhone.getNeighboringCids(response); + } + + public void setOnPostDialCharacter(Handler h, int what, Object obj) { + mActivePhone.setOnPostDialCharacter(h, what, obj); + } + + public void setMute(boolean muted) { + mActivePhone.setMute(muted); + } + + public boolean getMute() { + return mActivePhone.getMute(); + } + + public void invokeOemRilRequestRaw(byte[] data, Message response) { + mActivePhone.invokeOemRilRequestRaw(data, response); + } + + public void invokeOemRilRequestStrings(String[] strings, Message response) { + mActivePhone.invokeOemRilRequestStrings(strings, response); + } + + /** + * @deprecated + */ + public void getPdpContextList(Message response) { + mActivePhone.getPdpContextList(response); + } + + public void getDataCallList(Message response) { + mActivePhone.getDataCallList(response); + } + + /** + * @deprecated + */ + public List getCurrentPdpList() { + return mActivePhone.getCurrentPdpList(); + } + + public List getCurrentDataConnectionList() { + return mActivePhone.getCurrentDataConnectionList(); + } + + public void updateServiceLocation(Message response) { + mActivePhone.updateServiceLocation(response); + } + + public void enableLocationUpdates() { + mActivePhone.enableLocationUpdates(); + } + + public void disableLocationUpdates() { + mActivePhone.disableLocationUpdates(); + } + + public void setUnitTestMode(boolean f) { + mActivePhone.setUnitTestMode(f); + } + + public boolean getUnitTestMode() { + return mActivePhone.getUnitTestMode(); + } + + public void setBandMode(int bandMode, Message response) { + mActivePhone.setBandMode(bandMode, response); + } + + public void queryAvailableBandMode(Message response) { + mActivePhone.queryAvailableBandMode(response); + } + + public boolean getDataRoamingEnabled() { + return mActivePhone.getDataRoamingEnabled(); + } + + public void setDataRoamingEnabled(boolean enable) { + mActivePhone.setDataRoamingEnabled(enable); + } + + public void queryCdmaRoamingPreference(Message response) { + mActivePhone.queryCdmaRoamingPreference(response); + } + + public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { + mActivePhone.setCdmaRoamingPreference(cdmaRoamingType, response); + } + + public void setCdmaSubscription(int cdmaSubscriptionType, Message response) { + mActivePhone.setCdmaSubscription(cdmaSubscriptionType, response); + } + + public SimulatedRadioControl getSimulatedRadioControl() { + return mActivePhone.getSimulatedRadioControl(); + } + + public boolean enableDataConnectivity() { + return mActivePhone.enableDataConnectivity(); + } + + public boolean disableDataConnectivity() { + return mActivePhone.disableDataConnectivity(); + } + + public int enableApnType(String type) { + return mActivePhone.enableApnType(type); + } + + public int disableApnType(String type) { + return mActivePhone.disableApnType(type); + } + + public boolean isDataConnectivityPossible() { + return mActivePhone.isDataConnectivityPossible(); + } + + public String getInterfaceName(String apnType) { + return mActivePhone.getInterfaceName(apnType); + } + + public String getIpAddress(String apnType) { + return mActivePhone.getIpAddress(apnType); + } + + public String getGateway(String apnType) { + return mActivePhone.getGateway(apnType); + } + + public String[] getDnsServers(String apnType) { + return mActivePhone.getDnsServers(apnType); + } + + public String getDeviceId() { + return mActivePhone.getDeviceId(); + } + + public String getDeviceSvn() { + return mActivePhone.getDeviceSvn(); + } + + public String getSubscriberId() { + return mActivePhone.getSubscriberId(); + } + + public String getIccSerialNumber() { + return mActivePhone.getIccSerialNumber(); + } + + public String getEsn() { + return mActivePhone.getEsn(); + } + + public String getMeid() { + return mActivePhone.getMeid(); + } + + public PhoneSubInfo getPhoneSubInfo(){ + return mActivePhone.getPhoneSubInfo(); + } + + public IccSmsInterfaceManager getIccSmsInterfaceManager(){ + return mActivePhone.getIccSmsInterfaceManager(); + } + + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ + return mActivePhone.getIccPhoneBookInterfaceManager(); + } + + public void setTTYModeEnabled(boolean enable, Message onComplete) { + mActivePhone.setTTYModeEnabled(enable, onComplete); + } + + public void queryTTYModeEnabled(Message onComplete) { + mActivePhone.queryTTYModeEnabled(onComplete); + } + + public void activateCellBroadcastSms(int activate, Message response) { + mActivePhone.activateCellBroadcastSms(activate, response); + } + + public void getCellBroadcastSmsConfig(Message response) { + mActivePhone.getCellBroadcastSmsConfig(response); + } + + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) { + mActivePhone.setCellBroadcastSmsConfig(configValuesArray, response); + } + + public void notifyDataActivity() { + mActivePhone.notifyDataActivity(); + } +} + diff --git a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java index 61d4c9f..fd822cd 100644 --- a/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java +++ b/telephony/java/com/android/internal/telephony/PhoneStateIntentReceiver.java @@ -32,11 +32,11 @@ import android.util.Log; * * Use android.telephony.TelephonyManager and PhoneStateListener instead. * - * + * */ @Deprecated public final class PhoneStateIntentReceiver extends BroadcastReceiver { - private static final String LOG_TAG = "PhoneStateIntRecv"; + private static final String LOG_TAG = "PHONE"; private static final boolean DBG = false; public static final String INTENT_KEY_ASU = "asu"; @@ -182,7 +182,7 @@ public final class PhoneStateIntentReceiver extends BroadcastReceiver { if (TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED.equals(action)) { mAsu = intent.getIntExtra(INTENT_KEY_ASU, mAsu); if (DBG) Log.d(LOG_TAG, "onReceiveIntent: set asu=" + mAsu); - + if (mTarget != null && getNotifySignalStrength()) { Message message = Message.obtain(mTarget, mAsuEventWhat); mTarget.sendMessage(message); diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java index 644d1f4..4d1f7e5 100644 --- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java @@ -1,10 +1,25 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.internal.telephony; import android.content.Context; -import android.os.ServiceManager; -import com.android.internal.telephony.*; +import android.util.Log; public class PhoneSubInfo extends IPhoneSubInfo.Stub { + static final String LOG_TAG = "PHONE"; private Phone mPhone; private Context mContext; private static final String READ_PHONE_STATE = @@ -13,10 +28,17 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { public PhoneSubInfo(Phone phone) { mPhone = phone; mContext = phone.getContext(); - ServiceManager.addService("iphonesubinfo", this); } + + public void dispose() { + } + + protected void finalize() { + Log.d(LOG_TAG, "PhoneSubInfo finalized"); + } + /** - * Retrieves the unique device ID, e.g., IMEI for GSM phones. + * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones. */ public String getDeviceId() { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE"); @@ -41,11 +63,11 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { } /** - * Retrieves the serial number of the SIM, if applicable. + * Retrieves the serial number of the ICC, if applicable. */ - public String getSimSerialNumber() { + public String getIccSerialNumber() { mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE"); - return mPhone.getSimSerialNumber(); + return mPhone.getIccSerialNumber(); } /** diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java new file mode 100644 index 0000000..450b3a7 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java @@ -0,0 +1,85 @@ +/* + * 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.ServiceManager; + + +public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub { + private PhoneSubInfo mPhoneSubInfo; + + public PhoneSubInfoProxy(PhoneSubInfo phoneSubInfo) { + mPhoneSubInfo = phoneSubInfo; + if(ServiceManager.getService("iphonesubinfo") == null) { + ServiceManager.addService("iphonesubinfo", this); + } + } + + public void setmPhoneSubInfo(PhoneSubInfo phoneSubInfo) { + this.mPhoneSubInfo = phoneSubInfo; + } + + public String getDeviceId() { + return mPhoneSubInfo.getDeviceId(); + } + + public String getDeviceSvn() { + return mPhoneSubInfo.getDeviceSvn(); + } + + /** + * Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones. + */ + public String getSubscriberId() { + return mPhoneSubInfo.getSubscriberId(); + } + + /** + * Retrieves the serial number of the ICC, if applicable. + */ + public String getIccSerialNumber() { + return mPhoneSubInfo.getIccSerialNumber(); + } + + /** + * Retrieves the phone number string for line 1. + */ + public String getLine1Number() { + return mPhoneSubInfo.getLine1Number(); + } + + /** + * Retrieves the alpha identifier for line 1. + */ + public String getLine1AlphaTag() { + return mPhoneSubInfo.getLine1AlphaTag(); + } + + /** + * Retrieves the voice mail number. + */ + public String getVoiceMailNumber() { + return mPhoneSubInfo.getVoiceMailNumber(); + } + + /** + * Retrieves the alpha identifier associated with the voice mail number. + */ + public String getVoiceMailAlphaTag() { + return mPhoneSubInfo.getVoiceMailAlphaTag(); + } +} diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java new file mode 100644 index 0000000..f5d3b32 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -0,0 +1,3050 @@ +/* + * 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.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Parcel; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.telephony.PhoneNumberUtils; +import android.telephony.NeighboringCellInfo; +import android.telephony.SmsManager; +import android.telephony.SmsMessage; +import android.util.Log; +import android.util.Config; + +import static com.android.internal.telephony.RILConstants.*; + +import com.android.internal.telephony.CallForwardInfo; +import com.android.internal.telephony.CommandException; +import com.android.internal.telephony.gsm.NetworkInfo; +import com.android.internal.telephony.gsm.PDPContextState; +import com.android.internal.telephony.gsm.SuppServiceNotification; +import com.android.internal.telephony.IccCardApplication; +import com.android.internal.telephony.IccCardStatus; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.SmsResponse; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; + +/** + * {@hide} + */ +class RILRequest { + static final String LOG_TAG = "RILJ"; + + //***** Class Variables + static int sNextSerial = 0; + static Object sSerialMonitor = new Object(); + private static Object sPoolSync = new Object(); + private static RILRequest sPool = null; + private static int sPoolSize = 0; + private static final int MAX_POOL_SIZE = 4; + + //***** Instance Variables + int mSerial; + int mRequest; + Message mResult; + Parcel mp; + RILRequest mNext; + + /** + * Retrieves a new RILRequest instance from the pool. + * + * @param request RIL_REQUEST_* + * @param result sent when operation completes + * @return a RILRequest instance from the pool. + */ + static RILRequest obtain(int request, Message result) { + RILRequest rr = null; + + synchronized(sPoolSync) { + if (sPool != null) { + rr = sPool; + sPool = rr.mNext; + rr.mNext = null; + sPoolSize--; + } + } + + if (rr == null) { + rr = new RILRequest(); + } + + synchronized(sSerialMonitor) { + rr.mSerial = sNextSerial++; + } + rr.mRequest = request; + rr.mResult = result; + rr.mp = Parcel.obtain(); + + if (result != null && result.getTarget() == null) { + throw new NullPointerException("Message target must not be null"); + } + + // first elements in any RIL Parcel + rr.mp.writeInt(request); + rr.mp.writeInt(rr.mSerial); + + return rr; + } + + /** + * Returns a RILRequest instance to the pool. + * + * Note: This should only be called once per use. + */ + void release() { + synchronized (sPoolSync) { + if (sPoolSize < MAX_POOL_SIZE) { + this.mNext = sPool; + sPool = this; + sPoolSize++; + } + } + } + + private RILRequest() { + } + + static void + resetSerial() { + synchronized(sSerialMonitor) { + sNextSerial = 0; + } + } + + String + serialString() { + //Cheesy way to do %04d + StringBuilder sb = new StringBuilder(8); + String sn; + + sn = Integer.toString(mSerial); + + //sb.append("J["); + sb.append('['); + for (int i = 0, s = sn.length() ; i < 4 - s; i++) { + sb.append('0'); + } + + sb.append(sn); + sb.append(']'); + return sb.toString(); + } + + void + onError(int error) { + CommandException ex; + + ex = CommandException.fromRilErrno(error); + + if (RIL.RILJ_LOGD) Log.d(LOG_TAG, serialString() + "< " + + RIL.requestToString(mRequest) + + " error: " + ex); + + if (mResult != null) { + AsyncResult.forMessage(mResult, null, ex); + mResult.sendToTarget(); + } + + if (mp != null) { + mp.recycle(); + mp = null; + } + } +} + + +/** + * RIL implementation of the CommandsInterface. + * FIXME public only for testing + * + * {@hide} + */ +public final class RIL extends BaseCommands implements CommandsInterface { + static final String LOG_TAG = "RILJ"; + private static final boolean DBG = false; + static final boolean RILJ_LOGD = Config.LOGD; + static final boolean RILJ_LOGV = DBG ? Config.LOGD : Config.LOGV; + static int WAKE_LOCK_TIMEOUT = 5000; + + //***** Instance Variables + + LocalSocket mSocket; + HandlerThread mSenderThread; + RILSender mSender; + Thread mReceiverThread; + RILReceiver mReceiver; + private Context mContext; + WakeLock mWakeLock; + int mRequestMessagesPending; + + // Is this the first radio state change? + private boolean mInitialRadioStateChange = true; + + //I'd rather this be LinkedList or something + ArrayList mRequestsList = new ArrayList(); + + Object mLastNITZTimeInfo; + + //***** Events + + static final int EVENT_SEND = 1; + static final int EVENT_WAKE_LOCK_TIMEOUT = 2; + + //***** Constants + + // match with constant in ril.cpp + static final int RIL_MAX_COMMAND_BYTES = (8 * 1024); + static final int RESPONSE_SOLICITED = 0; + static final int RESPONSE_UNSOLICITED = 1; + + static final String SOCKET_NAME_RIL = "rild"; + + static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000; + + + BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + sendScreenState(true); + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + sendScreenState(false); + } else { + Log.w(LOG_TAG, "RIL received unexpected Intent: " + intent.getAction()); + } + } + }; + + class RILSender extends Handler implements Runnable { + public RILSender(Looper looper) { + super(looper); + } + + // Only allocated once + byte[] dataLength = new byte[4]; + + //***** Runnable implementation + public void + run() { + //setup if needed + } + + + //***** Handler implemementation + + public void + handleMessage(Message msg) { + RILRequest rr = (RILRequest)(msg.obj); + RILRequest req = null; + + switch (msg.what) { + case EVENT_SEND: + /** + * mRequestMessagePending++ already happened for every + * EVENT_SEND, thus we must make sure + * mRequestMessagePending-- happens once and only once + */ + boolean alreadySubtracted = false; + try { + LocalSocket s; + + s = mSocket; + + if (s == null) { + rr.onError(RADIO_NOT_AVAILABLE); + rr.release(); + mRequestMessagesPending--; + alreadySubtracted = true; + return; + } + + synchronized (mRequestsList) { + mRequestsList.add(rr); + } + + mRequestMessagesPending--; + alreadySubtracted = true; + + byte[] data; + + data = rr.mp.marshall(); + rr.mp.recycle(); + rr.mp = null; + + if (data.length > RIL_MAX_COMMAND_BYTES) { + throw new RuntimeException( + "Parcel larger than max bytes allowed! " + + data.length); + } + + // parcel length in big endian + dataLength[0] = dataLength[1] = 0; + dataLength[2] = (byte)((data.length >> 8) & 0xff); + dataLength[3] = (byte)((data.length) & 0xff); + + //Log.v(LOG_TAG, "writing packet: " + data.length + " bytes"); + + s.getOutputStream().write(dataLength); + s.getOutputStream().write(data); + } catch (IOException ex) { + Log.e(LOG_TAG, "IOException", ex); + req = findAndRemoveRequestFromList(rr.mSerial); + // make sure this request has not already been handled, + // eg, if RILReceiver cleared the list. + if (req != null || !alreadySubtracted) { + rr.onError(RADIO_NOT_AVAILABLE); + rr.release(); + } + } catch (RuntimeException exc) { + Log.e(LOG_TAG, "Uncaught exception ", exc); + req = findAndRemoveRequestFromList(rr.mSerial); + // make sure this request has not already been handled, + // eg, if RILReceiver cleared the list. + if (req != null || !alreadySubtracted) { + rr.onError(GENERIC_FAILURE); + rr.release(); + } + } + + if (!alreadySubtracted) { + mRequestMessagesPending--; + } + + break; + + case EVENT_WAKE_LOCK_TIMEOUT: + // Haven't heard back from the last request. Assume we're + // not getting a response and release the wake lock. + // TODO should we clean up mRequestList and mRequestPending + synchronized (mWakeLock) { + if (mWakeLock.isHeld()) { + if (RILJ_LOGD) { + synchronized (mRequestsList) { + int count = mRequestsList.size(); + Log.d(LOG_TAG, "WAKE_LOCK_TIMEOUT " + + " mReqPending=" + mRequestMessagesPending + + " mRequestList=" + count); + + for (int i = 0; i < count; i++) { + rr = mRequestsList.get(i); + Log.d(LOG_TAG, i + ": [" + rr.mSerial + "] " + + requestToString(rr.mRequest)); + + } + } + } + mWakeLock.release(); + } + } + + break; + } + } + } + + /** + * Reads in a single RIL message off the wire. A RIL message consists + * of a 4-byte little-endian length and a subsequent series of bytes. + * The final message (length header omitted) is read into + * buffer and the length of the final message (less header) + * is returned. A return value of -1 indicates end-of-stream. + * + * @param is non-null; Stream to read from + * @param buffer Buffer to fill in. Must be as large as maximum + * message size, or an ArrayOutOfBounds exception will be thrown. + * @return Length of message less header, or -1 on end of stream. + * @throws IOException + */ + private static int readRilMessage(InputStream is, byte[] buffer) + throws IOException { + int countRead; + int offset; + int remaining; + int messageLength; + + // First, read in the length of the message + offset = 0; + remaining = 4; + do { + countRead = is.read(buffer, offset, remaining); + + if (countRead < 0 ) { + Log.e(LOG_TAG, "Hit EOS reading message length"); + return -1; + } + + offset += countRead; + remaining -= countRead; + } while (remaining > 0); + + messageLength = ((buffer[0] & 0xff) << 24) + | ((buffer[1] & 0xff) << 16) + | ((buffer[2] & 0xff) << 8) + | (buffer[3] & 0xff); + + // Then, re-use the buffer and read in the message itself + offset = 0; + remaining = messageLength; + do { + countRead = is.read(buffer, offset, remaining); + + if (countRead < 0 ) { + Log.e(LOG_TAG, "Hit EOS reading message. messageLength=" + messageLength + + " remaining=" + remaining); + return -1; + } + + offset += countRead; + remaining -= countRead; + } while (remaining > 0); + + return messageLength; + } + + class RILReceiver implements Runnable { + byte[] buffer; + + RILReceiver() { + buffer = new byte[RIL_MAX_COMMAND_BYTES]; + } + + public void + run() { + int retryCount = 0; + + try {for (;;) { + LocalSocket s = null; + LocalSocketAddress l; + + try { + s = new LocalSocket(); + l = new LocalSocketAddress(SOCKET_NAME_RIL, + LocalSocketAddress.Namespace.RESERVED); + s.connect(l); + } catch (IOException ex){ + try { + if (s != null) { + s.close(); + } + } catch (IOException ex2) { + //ignore failure to close after failure to connect + } + + // don't print an error message after the the first time + // or after the 8th time + + if (retryCount == 8) { + Log.e (LOG_TAG, + "Couldn't find '" + SOCKET_NAME_RIL + + "' socket after " + retryCount + + " times, continuing to retry silently"); + } else if (retryCount > 0 && retryCount < 8) { + Log.i (LOG_TAG, + "Couldn't find '" + SOCKET_NAME_RIL + + "' socket; retrying after timeout"); + } + + try { + Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); + } catch (InterruptedException er) { + } + + retryCount++; + continue; + } + + retryCount = 0; + + mSocket = s; + Log.i(LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket"); + + int length = 0; + try { + InputStream is = mSocket.getInputStream(); + + for (;;) { + Parcel p; + + length = readRilMessage(is, buffer); + + if (length < 0) { + // End-of-stream reached + break; + } + + p = Parcel.obtain(); + p.unmarshall(buffer, 0, length); + p.setDataPosition(0); + + //Log.v(LOG_TAG, "Read packet: " + length + " bytes"); + + processResponse(p); + p.recycle(); + } + } catch (java.io.IOException ex) { + Log.i(LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed", + ex); + } catch (Throwable tr) { + Log.e(LOG_TAG, "Uncaught exception read length=" + length + + "Exception:" + tr.toString()); + } + + Log.i(LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL + + "' socket"); + + setRadioState (RadioState.RADIO_UNAVAILABLE); + + try { + mSocket.close(); + } catch (IOException ex) { + } + + mSocket = null; + RILRequest.resetSerial(); + + // Clear request list on close + synchronized (mRequestsList) { + for (int i = 0, sz = mRequestsList.size() ; i < sz ; i++) { + RILRequest rr = mRequestsList.get(i); + rr.onError(RADIO_NOT_AVAILABLE); + rr.release(); + } + + mRequestsList.clear(); + } + }} catch (Throwable tr) { + Log.e(LOG_TAG,"Uncaught exception", tr); + } + } + } + + + + //***** Constructors + public + RIL(Context context) { + this(context, RILConstants.PREFERRED_NETWORK_MODE, + RILConstants.PREFERRED_CDMA_SUBSCRIPTION); + } + + public RIL(Context context, int networkMode, int cdmaSubscription) { + super(context); + mCdmaSubscription = cdmaSubscription; + mNetworkMode = networkMode; + //At startup mPhoneType is first set from networkMode + switch(networkMode) { + case RILConstants.NETWORK_MODE_WCDMA_PREF: + case RILConstants.NETWORK_MODE_GSM_ONLY: + case RILConstants.NETWORK_MODE_WCDMA_ONLY: + case RILConstants.NETWORK_MODE_GSM_UMTS: + mPhoneType = RILConstants.GSM_PHONE; + break; + case RILConstants.NETWORK_MODE_CDMA: + case RILConstants.NETWORK_MODE_CDMA_NO_EVDO: + case RILConstants.NETWORK_MODE_EVDO_NO_CDMA: + mPhoneType = RILConstants.CDMA_PHONE; + break; + case RILConstants.NETWORK_MODE_GLOBAL: + mPhoneType = RILConstants.CDMA_PHONE; + break; + default: + mPhoneType = RILConstants.CDMA_PHONE; + } + + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); + mWakeLock.setReferenceCounted(false); + mRequestMessagesPending = 0; + + mContext = context; + + mSenderThread = new HandlerThread("RILSender"); + mSenderThread.start(); + + Looper looper = mSenderThread.getLooper(); + mSender = new RILSender(looper); + + mReceiver = new RILReceiver(); + mReceiverThread = new Thread(mReceiver, "RILReceiver"); + mReceiverThread.start(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + context.registerReceiver(mIntentReceiver, filter); + } + + //***** CommandsInterface implementation + + @Override public void + setOnNITZTime(Handler h, int what, Object obj) { + super.setOnNITZTime(h, what, obj); + + // Send the last NITZ time if we have it + if (mLastNITZTimeInfo != null) { + mNITZTimeRegistrant + .notifyRegistrant( + new AsyncResult (null, mLastNITZTimeInfo, null)); + mLastNITZTimeInfo = null; + } + } + + public void + getIccStatus(Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + supplyIccPin(String pin, Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(1); + rr.mp.writeString(pin); + + send(rr); + } + + public void + supplyIccPuk(String puk, String newPin, Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(2); + rr.mp.writeString(puk); + rr.mp.writeString(newPin); + + send(rr); + } + + public void + supplyIccPin2(String pin, Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN2, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(1); + rr.mp.writeString(pin); + + send(rr); + } + + public void + supplyIccPuk2(String puk, String newPin2, Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK2, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(2); + rr.mp.writeString(puk); + rr.mp.writeString(newPin2); + + send(rr); + } + + public void + changeIccPin(String oldPin, String newPin, Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(2); + rr.mp.writeString(oldPin); + rr.mp.writeString(newPin); + + send(rr); + } + + public void + changeIccPin2(String oldPin2, String newPin2, Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN2, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(2); + rr.mp.writeString(oldPin2); + rr.mp.writeString(newPin2); + + send(rr); + } + + public void + changeBarringPassword(String facility, String oldPwd, String newPwd, Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(3); + rr.mp.writeString(facility); + rr.mp.writeString(oldPwd); + rr.mp.writeString(newPwd); + + send(rr); + } + + public void + supplyNetworkDepersonalization(String netpin, Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeInt(1); + rr.mp.writeString(netpin); + + send(rr); + } + + public void + getCurrentCalls (Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getPDPContextList(Message result) { + getDataCallList(result); + } + + public void + getDataCallList(Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_DATA_CALL_LIST, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + dial (String address, int clirMode, Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result); + + rr.mp.writeString(address); + rr.mp.writeInt(clirMode); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getIMSI(Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + + "> getIMSI:RIL_REQUEST_GET_IMSI " + + RIL_REQUEST_GET_IMSI + + " " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getIMEI(Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEI, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getIMEISV(Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEISV, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + + public void + hangupConnection (int gsmIndex, Message result) { + if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex); + + RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + + gsmIndex); + + rr.mp.writeInt(1); + rr.mp.writeInt(gsmIndex); + + send(rr); + } + + public void + hangupWaitingOrBackground (Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, + result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + hangupForegroundResumeBackground (Message result) { + RILRequest rr + = RILRequest.obtain( + RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, + result); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + switchWaitingOrHoldingAndActive (Message result) { + RILRequest rr + = RILRequest.obtain( + RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, + result); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + conference (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_CONFERENCE, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + + public void setPreferredVoicePrivacy(boolean enable, Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE, + result); + + rr.mp.writeInt(1); + rr.mp.writeInt(enable ? 1:0); + + send(rr); + } + + public void getPreferredVoicePrivacy(Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE, + result); + send(rr); + } + + public void + separateConnection (int gsmIndex, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SEPARATE_CONNECTION, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + gsmIndex); + + rr.mp.writeInt(1); + rr.mp.writeInt(gsmIndex); + + send(rr); + } + + public void + acceptCall (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_ANSWER, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + rejectCall (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_UDUB, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + explicitCallTransfer (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getLastCallFailCause (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * @deprecated + */ + public void + getLastPdpFailCause (Message result) { + getLastDataCallFailCause (result); + } + + /** + * The preferred new alternative to getLastPdpFailCause + */ + public void + getLastDataCallFailCause (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + setMute (boolean enableMute, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_MUTE, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + enableMute); + + rr.mp.writeInt(1); + rr.mp.writeInt(enableMute ? 1 : 0); + + send(rr); + } + + public void + getMute (Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_GET_MUTE, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getSignalStrength (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SIGNAL_STRENGTH, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getRegistrationState (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_REGISTRATION_STATE, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getGPRSRegistrationState (Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_GPRS_REGISTRATION_STATE, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getOperator(Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_OPERATOR, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + sendDtmf(char c, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_DTMF, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeString(Character.toString(c)); + + send(rr); + } + + public void + startDtmf(char c, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_DTMF_START, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeString(Character.toString(c)); + + send(rr); + } + + public void + stopDtmf(Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_DTMF_STOP, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + + public void + sendSMS (String smscPDU, String pdu, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result); + + rr.mp.writeInt(2); + rr.mp.writeString(smscPDU); + rr.mp.writeString(pdu); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + sendCdmaSms(byte[] pdu, Message result) { + int address_nbr_of_digits; + int subaddr_nbr_of_digits; + int bearerDataLength; + ByteArrayInputStream bais = new ByteArrayInputStream(pdu); + DataInputStream dis = new DataInputStream(bais); + + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_CDMA_SEND_SMS, result); + + try { + rr.mp.writeInt(dis.readInt()); //teleServiceId + rr.mp.writeByte((byte) dis.readInt()); //servicePresent + rr.mp.writeInt(dis.readInt()); //serviceCategory + rr.mp.writeInt(dis.read()); //address_digit_mode + rr.mp.writeInt(dis.read()); //address_nbr_mode + rr.mp.writeInt(dis.read()); //address_ton + rr.mp.writeInt(dis.read()); //address_nbr_plan + address_nbr_of_digits = (byte) dis.read(); + rr.mp.writeByte((byte) address_nbr_of_digits); + for(int i=0; i < address_nbr_of_digits; i++){ + rr.mp.writeByte(dis.readByte()); // address_orig_bytes[i] + } + rr.mp.writeInt(dis.read()); //subaddressType + rr.mp.writeByte((byte) dis.read()); //subaddr_odd + subaddr_nbr_of_digits = (byte) dis.read(); + rr.mp.writeByte((byte) subaddr_nbr_of_digits); + for(int i=0; i < subaddr_nbr_of_digits; i++){ + rr.mp.writeByte(dis.readByte()); //subaddr_orig_bytes[i] + } + + bearerDataLength = dis.read(); + rr.mp.writeInt(bearerDataLength); + for(int i=0; i < bearerDataLength; i++){ + rr.mp.writeByte(dis.readByte()); //bearerData[i] + } + }catch (IOException ex){ + if (RILJ_LOGD) riljLog("sendSmsCdma: conversion from input stream to object failed: " + + ex); + } + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void deleteSmsOnSim(int index, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_DELETE_SMS_ON_SIM, + response); + + rr.mp.writeInt(1); + rr.mp.writeInt(index); + + if (Config.LOGD) { + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + + " " + index); + } + + send(rr); + } + + public void deleteSmsOnRuim(int index, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, + response); + + rr.mp.writeInt(1); + rr.mp.writeInt(index); + + if (Config.LOGD) { + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + + " " + index); + } + + send(rr); + } + + public void writeSmsToSim(int status, String smsc, String pdu, Message response) { + status = translateStatus(status); + + RILRequest rr = RILRequest.obtain(RIL_REQUEST_WRITE_SMS_TO_SIM, + response); + + rr.mp.writeInt(status); + rr.mp.writeString(pdu); + rr.mp.writeString(smsc); + + if (Config.LOGD) { + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + + " " + status); + } + + send(rr); + } + + public void writeSmsToRuim(int status, String pdu, Message response) { + status = translateStatus(status); + + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, + response); + + rr.mp.writeInt(status); + rr.mp.writeString(pdu); + + if (Config.LOGD) { + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + + " " + status); + } + + send(rr); + } + + /** + * Translates EF_SMS status bits to a status value compatible with + * SMS AT commands. See TS 27.005 3.1. + */ + private int translateStatus(int status) { + switch(status & 0x7) { + case SmsManager.STATUS_ON_ICC_READ: + return 1; + case SmsManager.STATUS_ON_ICC_UNREAD: + return 0; + case SmsManager.STATUS_ON_ICC_SENT: + return 3; + case SmsManager.STATUS_ON_ICC_UNSENT: + return 2; + } + + // Default to READ. + return 1; + } + + /** + * @deprecated + */ + public void + setupDefaultPDP(String apn, String user, String password, Message result) { + String radioTechnology = "1"; //0 for CDMA, 1 for GSM/UMTS + String profile = ""; //profile number, NULL for GSM/UMTS + setupDataCall(radioTechnology, profile, apn, user, + password, result); + + } + + /** + * @deprecated + */ + public void + deactivateDefaultPDP(int cid, Message result) { + deactivateDataCall(cid, result); + } + + /** + * The preferred new alternative to setupDefaultPDP that is + * CDMA-compatible. + * + */ + public void + setupDataCall(String radioTechnology, String profile, String apn, + String user, String password, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result); + + rr.mp.writeInt(5); + + rr.mp.writeString(radioTechnology); + rr.mp.writeString(profile); + rr.mp.writeString(apn); + rr.mp.writeString(user); + rr.mp.writeString(password); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + + apn); + + send(rr); + } + + public void + deactivateDataCall(int cid, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DATA_CALL, result); + + rr.mp.writeInt(1); + rr.mp.writeString(Integer.toString(cid)); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + " " + cid); + + send(rr); + } + + public void + setRadioPower(boolean on, Message result) { + //if radio is OFF set preferred NW type and cmda subscription + if(mInitialRadioStateChange) { + synchronized (mStateMonitor) { + if (!mState.isOn()) { + RILRequest rrPnt = RILRequest.obtain( + RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, null); + + rrPnt.mp.writeInt(1); + rrPnt.mp.writeInt(mNetworkMode); + if (RILJ_LOGD) riljLog(rrPnt.serialString() + "> " + + requestToString(rrPnt.mRequest) + " : " + mNetworkMode); + + send(rrPnt); + + RILRequest rrCs = RILRequest.obtain( + RIL_REQUEST_CDMA_SET_SUBSCRIPTION, null); + rrCs.mp.writeInt(1); + rrCs.mp.writeInt(mCdmaSubscription); + if (RILJ_LOGD) riljLog(rrCs.serialString() + "> " + + requestToString(rrCs.mRequest) + " : " + mCdmaSubscription); + send(rrCs); + } + } + } + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_RADIO_POWER, result); + + rr.mp.writeInt(1); + rr.mp.writeInt(on ? 1 : 0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + setSuppServiceNotifications(boolean enable, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result); + + rr.mp.writeInt(1); + rr.mp.writeInt(enable ? 1 : 0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest)); + + send(rr); + } + + public void + acknowledgeLastIncomingSMS(boolean success, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SMS_ACKNOWLEDGE, result); + + rr.mp.writeInt(1); + rr.mp.writeInt(success ? 1 : 0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + acknowledgeLastIncomingCdmaSms(boolean success, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result); + + rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass + // cause code according to X.S004-550E + rr.mp.writeInt(39); //39 means other terminal problem; is not interpreted for success. + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + + public void + iccIO (int command, int fileid, String path, int p1, int p2, int p3, + String data, String pin2, Message result) { + //Note: This RIL request has not been renamed to ICC, + // but this request is also valid for SIM and RUIM + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SIM_IO, result); + + rr.mp.writeInt(command); + rr.mp.writeInt(fileid); + rr.mp.writeString(path); + rr.mp.writeInt(p1); + rr.mp.writeInt(p2); + rr.mp.writeInt(p3); + rr.mp.writeString(data); + rr.mp.writeString(pin2); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: " + requestToString(rr.mRequest) + + " 0x" + Integer.toHexString(command) + + " 0x" + Integer.toHexString(fileid) + " " + + p1 + "," + p2 + "," + p3); + + send(rr); + } + + public void + getCLIR(Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_GET_CLIR, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + setCLIR(int clirMode, Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_CLIR, result); + + // count ints + rr.mp.writeInt(1); + + rr.mp.writeInt(clirMode); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + clirMode); + + send(rr); + } + + public void + queryCallWaiting(int serviceClass, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_WAITING, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(serviceClass); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + serviceClass); + + send(rr); + } + + public void + setCallWaiting(boolean enable, int serviceClass, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_CALL_WAITING, response); + + rr.mp.writeInt(2); + rr.mp.writeInt(enable ? 1 : 0); + rr.mp.writeInt(serviceClass); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + enable + ", " + serviceClass); + + send(rr); + } + + public void + setNetworkSelectionModeAutomatic(Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, + response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + setNetworkSelectionModeManual(String operatorNumeric, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, + response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + operatorNumeric); + + rr.mp.writeString(operatorNumeric); + + send(rr); + } + + public void + getNetworkSelectionMode(Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, + response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getAvailableNetworks(Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, + response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + setCallForward(int action, int cfReason, int serviceClass, + String number, int timeSeconds, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_CALL_FORWARD, response); + + rr.mp.writeInt(action); + rr.mp.writeInt(cfReason); + rr.mp.writeInt(serviceClass); + rr.mp.writeInt(PhoneNumberUtils.toaFromString(number)); + rr.mp.writeString(number); + rr.mp.writeInt (timeSeconds); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + action + " " + cfReason + " " + serviceClass + + timeSeconds); + + send(rr); + } + + public void + queryCallForwardStatus(int cfReason, int serviceClass, + String number, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, response); + + rr.mp.writeInt(2); // 2 is for query action, not in used anyway + rr.mp.writeInt(cfReason); + rr.mp.writeInt(serviceClass); + rr.mp.writeInt(PhoneNumberUtils.toaFromString(number)); + rr.mp.writeString(number); + rr.mp.writeInt (0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + cfReason + " " + serviceClass); + + send(rr); + } + + public void + queryCLIP(Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_QUERY_CLIP, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + + public void + getBasebandVersion (Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_BASEBAND_VERSION, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + queryFacilityLock (String facility, String password, int serviceClass, + Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_QUERY_FACILITY_LOCK, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + // count strings + rr.mp.writeInt(3); + + rr.mp.writeString(facility); + rr.mp.writeString(password); + + rr.mp.writeString(Integer.toString(serviceClass)); + + send(rr); + } + + public void + setFacilityLock (String facility, boolean lockState, String password, + int serviceClass, Message response) { + String lockString; + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_FACILITY_LOCK, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + // count strings + rr.mp.writeInt(4); + + rr.mp.writeString(facility); + lockString = (lockState)?"1":"0"; + rr.mp.writeString(lockString); + rr.mp.writeString(password); + rr.mp.writeString(Integer.toString(serviceClass)); + + send(rr); + + } + + public void + sendUSSD (String ussdString, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SEND_USSD, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + ussdString); + + rr.mp.writeString(ussdString); + + send(rr); + } + + // inherited javadoc suffices + public void cancelPendingUssd (Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_CANCEL_USSD, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + + public void resetRadio(Message result) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_RESET_RADIO, result); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void invokeOemRilRequestRaw(byte[] data, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_RAW, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + "[" + IccUtils.bytesToHexString(data) + "]"); + + rr.mp.writeByteArray(data); + + send(rr); + + } + + public void invokeOemRilRequestStrings(String[] strings, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_STRINGS, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeStringArray(strings); + + send(rr); + } + + /** + * Assign a specified band for RF configuration. + * + * @param bandMode one of BM_*_BAND + * @param response is callback message + */ + public void setBandMode (int bandMode, Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_SET_BAND_MODE, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(bandMode); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + bandMode); + + send(rr); + } + + /** + * Query the list of band mode supported by RF. + * + * @param response is callback message + * ((AsyncResult)response.obj).result is an int[] with every + * element representing one avialable BM_*_BAND + */ + public void queryAvailableBandMode (Message response) { + RILRequest rr + = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, + response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void sendTerminalResponse(String contents, Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeString(contents); + send(rr); + } + + /** + * {@inheritDoc} + */ + public void sendEnvelope(String contents, Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + rr.mp.writeString(contents); + send(rr); + } + + /** + * {@inheritDoc} + */ + public void handleCallSetupRequestFromSim( + boolean accept, Message response) { + + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM, + response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + int[] param = new int[1]; + param[0] = accept ? 1 : 0; + rr.mp.writeIntArray(param); + send(rr); + } + + /** + * {@inheritDoc} + */ + public void setPreferredNetworkType(int networkType , Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(networkType); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " : " + networkType); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void getPreferredNetworkType(Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void getNeighboringCids(Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void setLocationUpdates(boolean enable, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_LOCATION_UPDATES, response); + rr.mp.writeInt(1); + rr.mp.writeInt(enable ? 1 : 0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + ": " + enable); + + send(rr); + } + + //***** Private Methods + + private void sendScreenState(boolean on) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_SCREEN_STATE, null); + rr.mp.writeInt(1); + rr.mp.writeInt(on ? 1 : 0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + ": " + on); + + send(rr); + } + + protected void + onRadioAvailable() { + // In case screen state was lost (due to process crash), + // this ensures that the RIL knows the correct screen state. + + // TODO: Should query Power Manager and send the actual + // screen state. Just send true for now. + sendScreenState(true); + } + + private void setRadioStateFromRILInt(int state) { + RadioState newState; + + /* RIL_RadioState ril.h */ + switch(state) { + case 0: newState = RadioState.RADIO_OFF; break; + case 1: newState = RadioState.RADIO_UNAVAILABLE; break; + case 2: newState = RadioState.SIM_NOT_READY; break; + case 3: newState = RadioState.SIM_LOCKED_OR_ABSENT; break; + case 4: newState = RadioState.SIM_READY; break; + case 5: newState = RadioState.RUIM_NOT_READY; break; + case 6: newState = RadioState.RUIM_READY; break; + case 7: newState = RadioState.RUIM_LOCKED_OR_ABSENT; break; + case 8: newState = RadioState.NV_NOT_READY; break; + case 9: newState = RadioState.NV_READY; break; + + default: + throw new RuntimeException( + "Unrecognized RIL_RadioState: " +state); + } + + if (mInitialRadioStateChange) { + if (newState.isOn()) { + /* If this is our first notification, make sure the radio + * is powered off. This gets the radio into a known state, + * since it's possible for the phone proc to have restarted + * (eg, if it or the runtime crashed) without the RIL + * and/or radio knowing. + */ + if (RILJ_LOGD) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF"); + setRadioPower(false, null); + } else { + if (DBG) Log.d(LOG_TAG, "Radio OFF @ init"); + setRadioState(newState); + } + mInitialRadioStateChange = false; + } else { + setRadioState(newState); + } + } + + /** + * Holds a PARTIAL_WAKE_LOCK whenever + * a) There is outstanding RIL request sent to RIL deamon and no replied + * b) There is a request waiting to be sent out. + * + * There is a WAKE_LOCK_TIMEOUT to release the lock, though it shouldn't + * happen often. + */ + + private void + acquireWakeLock() { + synchronized (mWakeLock) { + mWakeLock.acquire(); + mRequestMessagesPending++; + + mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); + Message msg = mSender.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); + mSender.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT); + } + } + + private void + releaseWakeLockIfDone() { + synchronized (mWakeLock) { + if (mWakeLock.isHeld() && + (mRequestMessagesPending == 0) && + (mRequestsList.size() == 0)) { + mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); + mWakeLock.release(); + } + } + } + + private void + send(RILRequest rr) { + Message msg; + + msg = mSender.obtainMessage(EVENT_SEND, rr); + + acquireWakeLock(); + + msg.sendToTarget(); + } + + private void + processResponse (Parcel p) { + int type; + + type = p.readInt(); + + if (type == RESPONSE_UNSOLICITED) { + processUnsolicited (p); + } else if (type == RESPONSE_SOLICITED) { + processSolicited (p); + } + + releaseWakeLockIfDone(); + } + + private RILRequest findAndRemoveRequestFromList(int serial) { + synchronized (mRequestsList) { + for (int i = 0, s = mRequestsList.size() ; i < s ; i++) { + RILRequest rr = mRequestsList.get(i); + + if (rr.mSerial == serial) { + mRequestsList.remove(i); + return rr; + } + } + } + + return null; + } + + private void + processSolicited (Parcel p) { + int serial, error; + boolean found = false; + + serial = p.readInt(); + error = p.readInt(); + + RILRequest rr; + + rr = findAndRemoveRequestFromList(serial); + + if (rr == null) { + Log.w(LOG_TAG, "Unexpected solicited response! sn: " + + serial + " error: " + error); + return; + } + + if (error != 0) { + rr.onError(error); + rr.release(); + return; + } + + Object ret; + + try {switch (rr.mRequest) { +/* + cat libs/telephony/ril_commands.h \ + | egrep "^ *{RIL_" \ + | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/' +*/ + case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break; + case RIL_REQUEST_ENTER_SIM_PIN: ret = responseVoid(p); break; + case RIL_REQUEST_ENTER_SIM_PUK: ret = responseVoid(p); break; + case RIL_REQUEST_ENTER_SIM_PIN2: ret = responseVoid(p); break; + case RIL_REQUEST_ENTER_SIM_PUK2: ret = responseVoid(p); break; + case RIL_REQUEST_CHANGE_SIM_PIN: ret = responseVoid(p); break; + case RIL_REQUEST_CHANGE_SIM_PIN2: ret = responseVoid(p); break; + case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret = responseVoid(p); break; + case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break; + case RIL_REQUEST_DIAL: ret = responseVoid(p); break; + case RIL_REQUEST_GET_IMSI: ret = responseString(p); break; + case RIL_REQUEST_HANGUP: ret = responseVoid(p); break; + case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret = responseVoid(p); break; + case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: ret = responseVoid(p); break; + case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: ret = responseVoid(p); break; + case RIL_REQUEST_CONFERENCE: ret = responseVoid(p); break; + case RIL_REQUEST_UDUB: ret = responseVoid(p); break; + case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseInts(p); break; + case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseInts(p); break; + case RIL_REQUEST_REGISTRATION_STATE: ret = responseStrings(p); break; + case RIL_REQUEST_GPRS_REGISTRATION_STATE: ret = responseStrings(p); break; + case RIL_REQUEST_OPERATOR: ret = responseStrings(p); break; + case RIL_REQUEST_RADIO_POWER: ret = responseVoid(p); break; + case RIL_REQUEST_DTMF: ret = responseVoid(p); break; + case RIL_REQUEST_SEND_SMS: ret = responseSMS(p); break; + case RIL_REQUEST_SEND_SMS_EXPECT_MORE: ret = responseSMS(p); break; + case RIL_REQUEST_SETUP_DATA_CALL: ret = responseStrings(p); break; + case RIL_REQUEST_SIM_IO: ret = responseICC_IO(p); break; + case RIL_REQUEST_SEND_USSD: ret = responseVoid(p); break; + case RIL_REQUEST_CANCEL_USSD: ret = responseVoid(p); break; + case RIL_REQUEST_GET_CLIR: ret = responseInts(p); break; + case RIL_REQUEST_SET_CLIR: ret = responseVoid(p); break; + case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: ret = responseCallForward(p); break; + case RIL_REQUEST_SET_CALL_FORWARD: ret = responseVoid(p); break; + case RIL_REQUEST_QUERY_CALL_WAITING: ret = responseInts(p); break; + case RIL_REQUEST_SET_CALL_WAITING: ret = responseVoid(p); break; + case RIL_REQUEST_SMS_ACKNOWLEDGE: ret = responseVoid(p); break; + case RIL_REQUEST_GET_IMEI: ret = responseString(p); break; + case RIL_REQUEST_GET_IMEISV: ret = responseString(p); break; + case RIL_REQUEST_ANSWER: ret = responseVoid(p); break; + case RIL_REQUEST_DEACTIVATE_DATA_CALL: ret = responseVoid(p); break; + case RIL_REQUEST_QUERY_FACILITY_LOCK: ret = responseInts(p); break; + case RIL_REQUEST_SET_FACILITY_LOCK: ret = responseVoid(p); break; + case RIL_REQUEST_CHANGE_BARRING_PASSWORD: ret = responseVoid(p); break; + case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret = responseInts(p); break; + case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret = responseVoid(p); break; + case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: ret = responseVoid(p); break; + case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret = responseNetworkInfos(p); break; + case RIL_REQUEST_DTMF_START: ret = responseVoid(p); break; + case RIL_REQUEST_DTMF_STOP: ret = responseVoid(p); break; + case RIL_REQUEST_BASEBAND_VERSION: ret = responseString(p); break; + case RIL_REQUEST_SEPARATE_CONNECTION: ret = responseVoid(p); break; + case RIL_REQUEST_SET_MUTE: ret = responseVoid(p); break; + case RIL_REQUEST_GET_MUTE: ret = responseInts(p); break; + case RIL_REQUEST_QUERY_CLIP: ret = responseInts(p); break; + case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: ret = responseInts(p); break; + case RIL_REQUEST_DATA_CALL_LIST: ret = responseDataCallList(p); break; + case RIL_REQUEST_RESET_RADIO: ret = responseVoid(p); break; + case RIL_REQUEST_OEM_HOOK_RAW: ret = responseRaw(p); break; + case RIL_REQUEST_OEM_HOOK_STRINGS: ret = responseStrings(p); break; + case RIL_REQUEST_SCREEN_STATE: ret = responseVoid(p); break; + case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: ret = responseVoid(p); break; + case RIL_REQUEST_WRITE_SMS_TO_SIM: ret = responseInts(p); break; + case RIL_REQUEST_DELETE_SMS_ON_SIM: ret = responseVoid(p); break; + case RIL_REQUEST_SET_BAND_MODE: ret = responseVoid(p); break; + case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: ret = responseInts(p); break; + case RIL_REQUEST_STK_GET_PROFILE: ret = responseString(p); break; + case RIL_REQUEST_STK_SET_PROFILE: ret = responseVoid(p); break; + case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: ret = responseString(p); break; + case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: ret = responseVoid(p); break; + case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret = responseInts(p); break; + case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break; + case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break; + case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseInts(p); break; + case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break; + case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_SET_SUBSCRIPTION: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: ret = responseInts(p); break; + case RIL_REQUEST_SET_TTY_MODE: ret = responseVoid(p); break; + case RIL_REQUEST_QUERY_TTY_MODE: ret = responseInts(p); break; + case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: ret = responseInts(p); break; + case RIL_REQUEST_CDMA_FLASH: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_BURST_DTMF: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_SEND_SMS: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: ret = responseVoid(p); break; + case RIL_REQUEST_GET_BROADCAST_CONFIG: ret = responseBR_SMS_CNF(p); break; + case RIL_REQUEST_SET_BROADCAST_CONFIG: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: ret = responseCDMA_BR_CNF(p); break; + case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: ret = responseVoid(p); break; + case RIL_REQUEST_BROADCAST_ACTIVATION: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_VALIDATE_AKEY: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: ret = responseVoid(p); break; + case RIL_REQUEST_CDMA_SUBSCRIPTION: ret = responseStrings(p); break; + case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: ret = responseInts(p); break; + case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: ret = responseVoid(p); break; + case RIL_REQUEST_DEVICE_IDENTITY: ret = responseStrings(p); break; + default: + throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest); + //break; + }} catch (Throwable tr) { + // Exceptions here usually mean invalid RIL responses + + Log.w(LOG_TAG, rr.serialString() + "< " + + requestToString(rr.mRequest) + + " exception, possible invalid RIL response", tr); + + if (rr.mResult != null) { + AsyncResult.forMessage(rr.mResult, null, tr); + rr.mResult.sendToTarget(); + } + rr.release(); + return; + } + + if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + + " " + retToString(rr.mRequest, ret)); + + if (rr.mResult != null) { + AsyncResult.forMessage(rr.mResult, ret, null); + rr.mResult.sendToTarget(); + } + + rr.release(); + } + + private String + retToString(int req, Object ret) { + if (ret == null) return ""; + switch (req) { + // Don't log these return values, for privacy's sake. + case RIL_REQUEST_GET_IMSI: + case RIL_REQUEST_GET_IMEI: + case RIL_REQUEST_GET_IMEISV: + return ""; + } + + StringBuilder sb; + String s; + int length; + if (ret instanceof int[]){ + int[] intArray = (int[]) ret; + length = intArray.length; + sb = new StringBuilder("{"); + if (length > 0) { + int i = 0; + sb.append(intArray[i++]); + while ( i < length) { + sb.append(", ").append(intArray[i++]); + } + } + sb.append("}"); + s = sb.toString(); + } else if (ret instanceof String[]) { + String[] strings = (String[]) ret; + length = strings.length; + sb = new StringBuilder("{"); + if (length > 0) { + int i = 0; + sb.append(strings[i++]); + while ( i < length) { + sb.append(", ").append(strings[i++]); + } + } + sb.append("}"); + s = sb.toString(); + }else if (req == RIL_REQUEST_GET_CURRENT_CALLS) { + ArrayList calls = (ArrayList) ret; + sb = new StringBuilder(" "); + for (DriverCall dc : calls) { + sb.append("[").append(dc).append("] "); + } + s = sb.toString(); + } else if (req == RIL_REQUEST_GET_NEIGHBORING_CELL_IDS) { + ArrayList cells; + cells = (ArrayList) ret; + sb = new StringBuilder(" "); + for (NeighboringCellInfo cell : cells) { + sb.append(cell).append(" "); + } + s = sb.toString(); + } else { + s = ret.toString(); + } + return s; + } + + private void + processUnsolicited (Parcel p) { + int response; + Object ret; + + response = p.readInt(); + + try {switch(response) { +/* + cat libs/telephony/ril_unsol_commands.h \ + | egrep "^ *{RIL_" \ + | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/' +*/ + + case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret = responseVoid(p); break; + case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break; + case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: ret = responseVoid(p); break; + case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break; + case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret = responseString(p); break; + case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break; + case RIL_UNSOL_ON_USSD: ret = responseStrings(p); break; + case RIL_UNSOL_NITZ_TIME_RECEIVED: ret = responseString(p); break; + case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseInts(p); break; + case RIL_UNSOL_DATA_CALL_LIST_CHANGED: ret = responseDataCallList(p);break; + case RIL_UNSOL_SUPP_SVC_NOTIFICATION: ret = responseSuppServiceNotification(p); break; + case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break; + case RIL_UNSOL_STK_PROACTIVE_COMMAND: ret = responseString(p); break; + case RIL_UNSOL_STK_EVENT_NOTIFY: ret = responseString(p); break; + case RIL_UNSOL_STK_CALL_SETUP: ret = responseInts(p); break; + case RIL_UNSOL_SIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; + case RIL_UNSOL_SIM_REFRESH: ret = responseInts(p); break; + case RIL_UNSOL_CALL_RING: ret = responseVoid(p); break; + case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break; + case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: ret = responseVoid(p); break; + case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: ret = responseCdmaSms(p); break; + case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseString(p); break; + case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; + default: + throw new RuntimeException("Unrecognized unsol response: " + response); + //break; (implied) + }} catch (Throwable tr) { + Log.e(LOG_TAG, "Exception processing unsol response: " + response + + "Exception:" + tr.toString()); + return; + } + + switch(response) { + case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: + /* has bonus radio state int */ + setRadioStateFromRILInt(p.readInt()); + + if (RILJ_LOGD) unsljLogMore(response, mState.toString()); + break; + case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: + if (RILJ_LOGD) unsljLog(response); + + mCallStateRegistrants + .notifyRegistrants(new AsyncResult(null, null, null)); + break; + case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: + if (RILJ_LOGD) unsljLog(response); + + mNetworkStateRegistrants + .notifyRegistrants(new AsyncResult(null, null, null)); + break; + case RIL_UNSOL_RESPONSE_NEW_SMS: { + if (RILJ_LOGD) unsljLog(response); + + // FIXME this should move up a layer + String a[] = new String[2]; + + a[1] = (String)ret; + + SmsMessage sms; + + sms = SmsMessage.newFromCMT(a); + if (mSMSRegistrant != null) { + mSMSRegistrant + .notifyRegistrant(new AsyncResult(null, sms, null)); + } + break; + } + case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: + if (RILJ_LOGD) unsljLogRet(response, ret); + + if (mSmsStatusRegistrant != null) { + mSmsStatusRegistrant.notifyRegistrant( + new AsyncResult(null, ret, null)); + } + break; + case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: + if (RILJ_LOGD) unsljLogRet(response, ret); + + int[] smsIndex = (int[])ret; + + if(smsIndex.length == 1) { + if (mSmsOnSimRegistrant != null) { + mSmsOnSimRegistrant. + notifyRegistrant(new AsyncResult(null, smsIndex, null)); + } + } else { + if (RILJ_LOGD) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length " + + smsIndex.length); + } + break; + case RIL_UNSOL_ON_USSD: + String[] resp = (String[])ret; + + if (resp.length < 2) { + resp = new String[2]; + resp[0] = ((String[])ret)[0]; + resp[1] = null; + } + if (RILJ_LOGD) unsljLogMore(response, resp[0]); + if (mUSSDRegistrant != null) { + mUSSDRegistrant.notifyRegistrant( + new AsyncResult (null, resp, null)); + } + break; + case RIL_UNSOL_NITZ_TIME_RECEIVED: + if (RILJ_LOGD) unsljLogRet(response, ret); + + // has bonus long containing milliseconds since boot that the NITZ + // time was received + long nitzReceiveTime = p.readLong(); + + Object[] result = new Object[2]; + + result[0] = ret; + result[1] = Long.valueOf(nitzReceiveTime); + + if (mNITZTimeRegistrant != null) { + + mNITZTimeRegistrant + .notifyRegistrant(new AsyncResult (null, result, null)); + } else { + // in case NITZ time registrant isnt registered yet + mLastNITZTimeInfo = result; + } + break; + + case RIL_UNSOL_SIGNAL_STRENGTH: + // Note this is set to "verbose" because it happens + // frequently + if (RILJ_LOGV) unsljLogvRet(response, ret); + + if (mSignalStrengthRegistrant != null) { + mSignalStrengthRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + break; + case RIL_UNSOL_DATA_CALL_LIST_CHANGED: + if (RILJ_LOGD) unsljLogRet(response, ret); + + mDataConnectionRegistrants.notifyRegistrants(new AsyncResult(null, ret, null)); + break; + + case RIL_UNSOL_SUPP_SVC_NOTIFICATION: + if (RILJ_LOGD) unsljLogRet(response, ret); + + if (mSsnRegistrant != null) { + mSsnRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_STK_SESSION_END: + if (RILJ_LOGD) unsljLog(response); + + if (mStkSessionEndRegistrant != null) { + mStkSessionEndRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_STK_PROACTIVE_COMMAND: + if (RILJ_LOGD) unsljLogRet(response, ret); + + if (mStkProCmdRegistrant != null) { + mStkProCmdRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_STK_EVENT_NOTIFY: + if (RILJ_LOGD) unsljLogRet(response, ret); + + if (mStkEventRegistrant != null) { + mStkEventRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_STK_CALL_SETUP: + if (RILJ_LOGD) unsljLogRet(response, ret); + + if (mStkCallSetUpRegistrant != null) { + mStkCallSetUpRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_SIM_SMS_STORAGE_FULL: + if (RILJ_LOGD) unsljLog(response); + + if (mIccSmsFullRegistrant != null) { + mIccSmsFullRegistrant.notifyRegistrant(); + } + break; + + case RIL_UNSOL_SIM_REFRESH: + if (RILJ_LOGD) unsljLogRet(response, ret); + + if (mIccRefreshRegistrant != null) { + mIccRefreshRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + break; + + case RIL_UNSOL_CALL_RING: + if (RILJ_LOGD) unsljLog(response); + + if (mRingRegistrant != null) { + mRingRegistrant.notifyRegistrant(); + } + break; + + case RIL_UNSOL_RESTRICTED_STATE_CHANGED: + if (RILJ_LOGD) unsljLogvRet(response, ret); + if (mRestrictedStateRegistrant != null) { + mRestrictedStateRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); + } + + case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: + if (mIccStatusChangedRegistrants != null) { + mIccStatusChangedRegistrants.notifyRegistrants(); + } + break; + + case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: + SmsMessage sms = (SmsMessage) ret; + + if (mSMSRegistrant != null) { + mSMSRegistrant + .notifyRegistrant(new AsyncResult(null, sms, null)); + } + break; + + case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: + // TODO T: waiting for SMS BC feature + break; + + case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: + if (Config.LOGD) { + if (RILJ_LOGD) riljLog("[UNSL]< RUIM_SMS_STORAGE_FULL"); + } + + if (mIccSmsFullRegistrant != null) { + mIccSmsFullRegistrant.notifyRegistrant(); + } + break; + } + } + + private Object + responseInts(Parcel p) { + int numInts; + int response[]; + + numInts = p.readInt(); + + response = new int[numInts]; + + for (int i = 0 ; i < numInts ; i++) { + response[i] = p.readInt(); + } + + return response; + } + + + private Object + responseVoid(Parcel p) { + return null; + } + + private Object + responseCallForward(Parcel p) { + int numInfos; + CallForwardInfo infos[]; + + numInfos = p.readInt(); + + infos = new CallForwardInfo[numInfos]; + + for (int i = 0 ; i < numInfos ; i++) { + infos[i] = new CallForwardInfo(); + + infos[i].status = p.readInt(); + infos[i].reason = p.readInt(); + infos[i].serviceClass = p.readInt(); + infos[i].toa = p.readInt(); + infos[i].number = p.readString(); + infos[i].timeSeconds = p.readInt(); + } + + return infos; + } + + private Object + responseSuppServiceNotification(Parcel p) { + SuppServiceNotification notification = new SuppServiceNotification(); + + notification.notificationType = p.readInt(); + notification.code = p.readInt(); + notification.index = p.readInt(); + notification.type = p.readInt(); + notification.number = p.readString(); + + return notification; + } + + private Object + responseCdmaSms(Parcel p) { + SmsMessage sms; + sms = SmsMessage.newFromParcel(p); + + return sms; + } + + private Object + responseString(Parcel p) { + String response; + + response = p.readString(); + + return response; + } + + private Object + responseStrings(Parcel p) { + int num; + String response[]; + + response = p.readStringArray(); + + if (false) { + num = p.readInt(); + + response = new String[num]; + for (int i = 0; i < num; i++) { + response[i] = p.readString(); + } + } + + return response; + } + + private Object + responseRaw(Parcel p) { + int num; + byte response[]; + + response = p.createByteArray(); + + return response; + } + + private Object + responseSMS(Parcel p) { + int messageRef; + String ackPDU; + + messageRef = p.readInt(); + ackPDU = p.readString(); + + SmsResponse response = new SmsResponse(messageRef, ackPDU); + + return response; + } + + + private Object + responseICC_IO(Parcel p) { + int sw1, sw2; + byte data[] = null; + Message ret; + + sw1 = p.readInt(); + sw2 = p.readInt(); + + String s = p.readString(); + + return new IccIoResult(sw1, sw2, s); + } + + private Object + responseIccCardStatus(Parcel p) { + RadioState currentRadioState; + IccCardApplication ca; + + currentRadioState = getRadioState(); + + IccCardStatus status = new IccCardStatus(); + status.card_state = status.CardStateFromRILInt(p.readInt()); + status.universal_pin_state = status.PinStateFromRILInt(p.readInt()); + status.gsm_umts_subscription_app_index = p.readInt(); + status.cdma_subscription_app_index = p.readInt(); + status.num_applications = p.readInt(); + + // limit to maximum allowed applications + if (status.num_applications > IccCardStatus.CARD_MAX_APPS) { + status.num_applications = IccCardStatus.CARD_MAX_APPS; + } + + for (int i = 0 ; i < status.num_applications ; i++) { + ca = new IccCardApplication(); + ca.app_type = ca.AppTypeFromRILInt(p.readInt()); + ca.app_state = ca.AppStateFromRILInt(p.readInt()); + ca.perso_substate = ca.PersoSubstateFromRILInt(p.readInt()); + ca.aid = p.readString(); + ca.app_label = p.readString(); + ca.pin1_replaced = p.readInt(); + ca.pin1 = p.readInt(); + ca.pin2 = p.readInt(); + status.application.add(ca); + } + + // this is common for all radio technologies + if (!status.card_state.isCardPresent()) { + return IccStatus.ICC_ABSENT; + } + + // check radio technology + if( currentRadioState == RadioState.RADIO_OFF || + currentRadioState == RadioState.RADIO_UNAVAILABLE || + currentRadioState == RadioState.SIM_NOT_READY || + currentRadioState == RadioState.RUIM_NOT_READY || + currentRadioState == RadioState.NV_NOT_READY || + currentRadioState == RadioState.NV_READY ) { + return IccStatus.ICC_NOT_READY; + } + + if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT || + currentRadioState == RadioState.SIM_READY || + currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT || + currentRadioState == RadioState.RUIM_READY) { + + int index; + + // check for CDMA radio technology + if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT || + currentRadioState == RadioState.RUIM_READY) { + index = status.cdma_subscription_app_index; + } + else { + index = status.gsm_umts_subscription_app_index; + } + + // check if PIN required + if (status.application.get(index).app_state.isPinRequired()) { + return IccStatus.ICC_PIN; + } + if (status.application.get(index).app_state.isPukRequired()) { + return IccStatus.ICC_PUK; + } + if (status.application.get(index).app_state.isSubscriptionPersoEnabled()) { + return IccStatus.ICC_NETWORK_PERSONALIZATION; + } + if (status.application.get(index).app_state.isAppReady()) { + return IccStatus.ICC_READY; + } + if (status.application.get(index).app_state.isAppNotReady()) { + return IccStatus.ICC_NOT_READY; + } + return IccStatus.ICC_NOT_READY; + } + + // Unrecognized ICC status. Treat it like a missing ICC. + Log.e(LOG_TAG, "Unrecognized RIL_REQUEST_GET_SIM_STATUS result: " + status); + return IccStatus.ICC_ABSENT; + } + + private Object + responseCallList(Parcel p) { + int num; + int voiceSettings; + ArrayList response; + DriverCall dc; + + num = p.readInt(); + response = new ArrayList(num); + + for (int i = 0 ; i < num ; i++) { + dc = new DriverCall(); + + dc.state = DriverCall.stateFromCLCC(p.readInt()); + dc.index = p.readInt(); + dc.TOA = p.readInt(); + dc.isMpty = (0 != p.readInt()); + dc.isMT = (0 != p.readInt()); + dc.als = p.readInt(); + voiceSettings = p.readInt(); + dc.isVoice = (0 == voiceSettings) ? false : true; + dc.number = p.readString(); + dc.numberPresentation = DriverCall.presentationFromCLIP(p.readInt()); + + // Make sure there's a leading + on addresses with a TOA + // of 145 + + dc.number = PhoneNumberUtils.stringFromStringAndTOA( + dc.number, dc.TOA); + + response.add(dc); + + if ( RILConstants.CDMA_VOICE_PRIVACY == voiceSettings ) { + mVoicePrivacyOnRegistrants.notifyRegistrants(); + Log.d(LOG_TAG, "InCall VoicePrivacy is enabled: " + + Integer.toString(voiceSettings)); + } else { + mVoicePrivacyOffRegistrants.notifyRegistrants(); + Log.d(LOG_TAG, "InCall VoicePrivacy is disabled: " + + Integer.toString(voiceSettings)); + } + } + + Collections.sort(response); + + return response; + } + + private Object + responseDataCallList(Parcel p) { + int num; + ArrayList response; + + num = p.readInt(); + response = new ArrayList(num); + + for (int i = 0; i < num; i++) { + PDPContextState pdp = new PDPContextState(); + + pdp.cid = p.readInt(); + pdp.active = p.readInt() == 0 ? false : true; + pdp.type = p.readString(); + pdp.apn = p.readString(); + pdp.address = p.readString(); + + response.add(pdp); + } + + return response; + } + + private Object + responseNetworkInfos(Parcel p) { + String strings[] = (String [])responseStrings(p); + ArrayList ret; + + if (strings.length % 4 != 0) { + throw new RuntimeException( + "RIL_REQUEST_QUERY_AVAILABLE_NETWORKS: invalid response. Got " + + strings.length + " strings, expected multible of 4"); + } + + ret = new ArrayList(strings.length / 4); + + for (int i = 0 ; i < strings.length ; i += 4) { + ret.add ( + new NetworkInfo( + strings[i+0], + strings[i+1], + strings[i+2], + strings[i+3])); + } + + return ret; + } + + private Object + responseCellList(Parcel p) { + int num; + ArrayList response; + NeighboringCellInfo cell; + + num = p.readInt(); + response = new ArrayList(num); + + for (int i = 0 ; i < num ; i++) { + try { + int rssi = p.readInt(); + int cid = Integer.valueOf(p.readString(), 16); + cell = new NeighboringCellInfo(rssi, cid); + response.add(cell); + } catch ( Exception e) { + } + } + + return response; + } + + private Object + responseBR_SMS_CNF(Parcel p) { + // TODO + return null; + } + + private Object + responseCDMA_BR_CNF(Parcel p) { + int numInts; + int response[]; + + numInts = p.readInt(); + + response = new int[numInts]; + + response[0] = numInts; + for (int i = 1 ; i < numInts; i++) { + response[i] = p.readInt(); + } + + return response; + } + + static String + requestToString(int request) { +/* + cat libs/telephony/ril_commands.h \ + | egrep "^ *{RIL_" \ + | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/' +*/ + switch(request) { + case RIL_REQUEST_GET_SIM_STATUS: return "GET_SIM_STATUS"; + case RIL_REQUEST_ENTER_SIM_PIN: return "ENTER_SIM_PIN"; + case RIL_REQUEST_ENTER_SIM_PUK: return "ENTER_SIM_PUK"; + case RIL_REQUEST_ENTER_SIM_PIN2: return "ENTER_SIM_PIN2"; + case RIL_REQUEST_ENTER_SIM_PUK2: return "ENTER_SIM_PUK2"; + case RIL_REQUEST_CHANGE_SIM_PIN: return "CHANGE_SIM_PIN"; + case RIL_REQUEST_CHANGE_SIM_PIN2: return "CHANGE_SIM_PIN2"; + case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: return "ENTER_NETWORK_DEPERSONALIZATION"; + case RIL_REQUEST_GET_CURRENT_CALLS: return "GET_CURRENT_CALLS"; + case RIL_REQUEST_DIAL: return "DIAL"; + case RIL_REQUEST_GET_IMSI: return "GET_IMSI"; + case RIL_REQUEST_HANGUP: return "HANGUP"; + case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "HANGUP_WAITING_OR_BACKGROUND"; + case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "HANGUP_FOREGROUND_RESUME_BACKGROUND"; + case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: return "REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE"; + case RIL_REQUEST_CONFERENCE: return "CONFERENCE"; + case RIL_REQUEST_UDUB: return "UDUB"; + case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "LAST_CALL_FAIL_CAUSE"; + case RIL_REQUEST_SIGNAL_STRENGTH: return "SIGNAL_STRENGTH"; + case RIL_REQUEST_REGISTRATION_STATE: return "REGISTRATION_STATE"; + case RIL_REQUEST_GPRS_REGISTRATION_STATE: return "GPRS_REGISTRATION_STATE"; + case RIL_REQUEST_OPERATOR: return "OPERATOR"; + case RIL_REQUEST_RADIO_POWER: return "RADIO_POWER"; + case RIL_REQUEST_DTMF: return "DTMF"; + case RIL_REQUEST_SEND_SMS: return "SEND_SMS"; + case RIL_REQUEST_SEND_SMS_EXPECT_MORE: return "SEND_SMS_EXPECT_MORE"; + case RIL_REQUEST_SETUP_DATA_CALL: return "SETUP_DATA_CALL"; + case RIL_REQUEST_SIM_IO: return "SIM_IO"; + case RIL_REQUEST_SEND_USSD: return "SEND_USSD"; + case RIL_REQUEST_CANCEL_USSD: return "CANCEL_USSD"; + case RIL_REQUEST_GET_CLIR: return "GET_CLIR"; + case RIL_REQUEST_SET_CLIR: return "SET_CLIR"; + case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: return "QUERY_CALL_FORWARD_STATUS"; + case RIL_REQUEST_SET_CALL_FORWARD: return "SET_CALL_FORWARD"; + case RIL_REQUEST_QUERY_CALL_WAITING: return "QUERY_CALL_WAITING"; + case RIL_REQUEST_SET_CALL_WAITING: return "SET_CALL_WAITING"; + case RIL_REQUEST_SMS_ACKNOWLEDGE: return "SMS_ACKNOWLEDGE"; + case RIL_REQUEST_GET_IMEI: return "GET_IMEI"; + case RIL_REQUEST_GET_IMEISV: return "GET_IMEISV"; + case RIL_REQUEST_ANSWER: return "ANSWER"; + case RIL_REQUEST_DEACTIVATE_DATA_CALL: return "DEACTIVATE_DATA_CALL"; + case RIL_REQUEST_QUERY_FACILITY_LOCK: return "QUERY_FACILITY_LOCK"; + case RIL_REQUEST_SET_FACILITY_LOCK: return "SET_FACILITY_LOCK"; + case RIL_REQUEST_CHANGE_BARRING_PASSWORD: return "CHANGE_BARRING_PASSWORD"; + case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: return "QUERY_NETWORK_SELECTION_MODE"; + case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: return "SET_NETWORK_SELECTION_AUTOMATIC"; + case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: return "SET_NETWORK_SELECTION_MANUAL"; + case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : return "QUERY_AVAILABLE_NETWORKS "; + case RIL_REQUEST_DTMF_START: return "DTMF_START"; + case RIL_REQUEST_DTMF_STOP: return "DTMF_STOP"; + case RIL_REQUEST_BASEBAND_VERSION: return "BASEBAND_VERSION"; + case RIL_REQUEST_SEPARATE_CONNECTION: return "SEPARATE_CONNECTION"; + case RIL_REQUEST_SET_MUTE: return "SET_MUTE"; + case RIL_REQUEST_GET_MUTE: return "GET_MUTE"; + case RIL_REQUEST_QUERY_CLIP: return "QUERY_CLIP"; + case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: return "LAST_DATA_CALL_FAIL_CAUSE"; + case RIL_REQUEST_DATA_CALL_LIST: return "DATA_CALL_LIST"; + case RIL_REQUEST_RESET_RADIO: return "RESET_RADIO"; + case RIL_REQUEST_OEM_HOOK_RAW: return "OEM_HOOK_RAW"; + case RIL_REQUEST_OEM_HOOK_STRINGS: return "OEM_HOOK_STRINGS"; + case RIL_REQUEST_SCREEN_STATE: return "SCREEN_STATE"; + case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: return "SET_SUPP_SVC_NOTIFICATION"; + case RIL_REQUEST_WRITE_SMS_TO_SIM: return "WRITE_SMS_TO_SIM"; + case RIL_REQUEST_DELETE_SMS_ON_SIM: return "DELETE_SMS_ON_SIM"; + case RIL_REQUEST_SET_BAND_MODE: return "SET_BAND_MODE"; + case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: return "QUERY_AVAILABLE_BAND_MODE"; + case RIL_REQUEST_STK_GET_PROFILE: return "REQUEST_STK_GET_PROFILE"; + case RIL_REQUEST_STK_SET_PROFILE: return "REQUEST_STK_SET_PROFILE"; + case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "REQUEST_STK_SEND_ENVELOPE_COMMAND"; + case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "REQUEST_STK_SEND_TERMINAL_RESPONSE"; + case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM"; + case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "REQUEST_EXPLICIT_CALL_TRANSFER"; + case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "REQUEST_SET_PREFERRED_NETWORK_TYPE"; + case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "REQUEST_GET_PREFERRED_NETWORK_TYPE"; + case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "REQUEST_GET_NEIGHBORING_CELL_IDS"; + case RIL_REQUEST_SET_LOCATION_UPDATES: return "REQUEST_SET_LOCATION_UPDATES"; + case RIL_REQUEST_CDMA_SET_SUBSCRIPTION: return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION"; + case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE"; + case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE"; + case RIL_REQUEST_SET_TTY_MODE: return "RIL_REQUEST_SET_TTY_MODE"; + case RIL_REQUEST_QUERY_TTY_MODE: return "RIL_REQUEST_QUERY_TTY_MODE"; + case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE"; + case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE"; + case RIL_REQUEST_CDMA_FLASH: return "RIL_REQUEST_CDMA_FLASH"; + case RIL_REQUEST_CDMA_BURST_DTMF: return "RIL_REQUEST_CDMA_BURST_DTMF"; + case RIL_REQUEST_CDMA_SEND_SMS: return "RIL_REQUEST_CDMA_SEND_SMS"; + case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE"; + case RIL_REQUEST_GET_BROADCAST_CONFIG: return "RIL_REQUEST_GET_BROADCAST_CONFIG"; + case RIL_REQUEST_SET_BROADCAST_CONFIG: return "RIL_REQUEST_SET_BROADCAST_CONFIG"; + case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG"; + case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG"; + case RIL_REQUEST_BROADCAST_ACTIVATION: return "RIL_REQUEST_BROADCAST_ACTIVATION"; + case RIL_REQUEST_CDMA_VALIDATE_AKEY: return "RIL_REQUEST_CDMA_VALIDATE_AKEY"; + case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: return "RIL_REQUEST_CDMA_BROADCAST_ACTIVATION"; + case RIL_REQUEST_CDMA_SUBSCRIPTION: return "RIL_REQUEST_CDMA_SUBSCRIPTION"; + case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: return "RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM"; + case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: return "RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM"; + case RIL_REQUEST_DEVICE_IDENTITY: return "RIL_REQUEST_DEVICE_IDENTITY"; + default: return ""; + } + } + + static String + responseToString(int request) + { +/* + cat libs/telephony/ril_unsol_commands.h \ + | egrep "^ *{RIL_" \ + | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/' +*/ + switch(request) { + case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED"; + case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED"; + case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_NETWORK_STATE_CHANGED"; + case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS"; + case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT"; + case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM"; + case RIL_UNSOL_ON_USSD: return "UNSOL_ON_USSD"; + case RIL_UNSOL_ON_USSD_REQUEST: return "UNSOL_ON_USSD_REQUEST"; + case RIL_UNSOL_NITZ_TIME_RECEIVED: return "UNSOL_NITZ_TIME_RECEIVED"; + case RIL_UNSOL_SIGNAL_STRENGTH: return "UNSOL_SIGNAL_STRENGTH"; + case RIL_UNSOL_DATA_CALL_LIST_CHANGED: return "UNSOL_DATA_CALL_LIST_CHANGED"; + case RIL_UNSOL_SUPP_SVC_NOTIFICATION: return "UNSOL_SUPP_SVC_NOTIFICATION"; + case RIL_UNSOL_STK_SESSION_END: return "UNSOL_STK_SESSION_END"; + case RIL_UNSOL_STK_PROACTIVE_COMMAND: return "UNSOL_STK_PROACTIVE_COMMAND"; + case RIL_UNSOL_STK_EVENT_NOTIFY: return "UNSOL_STK_EVENT_NOTIFY"; + case RIL_UNSOL_STK_CALL_SETUP: return "UNSOL_STK_CALL_SETUP"; + case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FULL"; + case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH"; + case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING"; + case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "RIL_UNSOL_RESTRICTED_STATE_CHANGED"; + default: return ""; + } + } + + private void riljLog(String msg) { + Log.d(LOG_TAG, msg); + } + + private void riljLogv(String msg) { + Log.v(LOG_TAG, msg); + } + + private void unsljLog(int response) { + riljLog("[UNSL]< " + responseToString(response)); + } + + private void unsljLogMore(int response, String more) { + riljLog("[UNSL]< " + responseToString(response) + " " + more); + } + + private void unsljLogRet(int response, Object ret) { + riljLog("[UNSL]< " + responseToString(response) + " " + retToString(response, ret)); + } + + private void unsljLogvRet(int response, Object ret) { + riljLogv("[UNSL]< " + responseToString(response) + " " + retToString(response, ret)); + } + + + // ***** Methods for CDMA support + public void + getDeviceIdentity(Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_DEVICE_IDENTITY, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void + getCDMASubscription(Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SUBSCRIPTION, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + public void setPhoneType(int phoneType) { //Set by CDMAPhone and GSMPhone constructor + mPhoneType = phoneType; + } + + /** + * {@inheritDoc} + */ + public void queryCdmaRoamingPreference(Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, response); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(cdmaRoamingType); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " : " + cdmaRoamingType); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void setCdmaSubscription(int cdmaSubscription , Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_CDMA_SET_SUBSCRIPTION, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(cdmaSubscription); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " : " + cdmaSubscription); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void queryTTYModeEnabled(Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_QUERY_TTY_MODE, response); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void setTTYModeEnabled(boolean enable, Message response) { + RILRequest rr = RILRequest.obtain( + RILConstants.RIL_REQUEST_SET_TTY_MODE, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(enable ? 1 : 0); + + send(rr); + } + + /** + * {@inheritDoc} + */ + public void + sendCDMAFeatureCode(String FeatureCode, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_FLASH, response); + + rr.mp.writeInt(1); + rr.mp.writeString(FeatureCode); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " : " + FeatureCode); + + send(rr); + } + + public void getCdmaBroadcastConfig(Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG, response); + + send(rr); + } + + public void setCdmaBroadcastConfig(int[] configValuesArray, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, response); + + for(int i = 0; i < configValuesArray.length; i++) { + rr.mp.writeInt(configValuesArray[i]); + } + + send(rr); + } + + public void activateCdmaBroadcastSms(int activate, Message response) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, response); + + rr.mp.writeInt(1); + rr.mp.writeInt(activate); + + send(rr); + } +} diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java new file mode 100644 index 0000000..ba3b754 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -0,0 +1,236 @@ +/* + * 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 interface RILConstants { + // From the top of ril.cpp + int RIL_ERRNO_INVALID_RESPONSE = -1; + + // from RIL_Errno + int SUCCESS = 0; + int RADIO_NOT_AVAILABLE = 1; /* If radio did not start or is resetting */ + int GENERIC_FAILURE = 2; + int PASSWORD_INCORRECT = 3; /* for PIN/PIN2 methods only! */ + int SIM_PIN2 = 4; /* Operation requires SIM PIN2 to be entered */ + int SIM_PUK2 = 5; /* Operation requires SIM PIN2 to be entered */ + int REQUEST_NOT_SUPPORTED = 6; + int REQUEST_CANCELLED = 7; + int OP_NOT_ALLOWED_DURING_VOICE_CALL = 8; /* data operation is not allowed during voice call in + class C */ + int OP_NOT_ALLOWED_BEFORE_REG_NW = 9; /* request is not allowed before device registers to + network */ + int SMS_SEND_FAIL_RETRY = 10; /* send sms fail and need retry */ + + /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */ + int NETWORK_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */ + int NETWORK_MODE_GSM_ONLY = 1; /* GSM only */ + int NETWORK_MODE_WCDMA_ONLY = 2; /* WCDMA only */ + int NETWORK_MODE_GSM_UMTS = 3; /* GSM/WCDMA (auto mode, according to PRL) + AVAILABLE Application Settings menu*/ + int NETWORK_MODE_CDMA = 4; /* CDMA and EvDo (auto mode, according to PRL) + AVAILABLE Application Settings menu*/ + int NETWORK_MODE_CDMA_NO_EVDO = 5; /* CDMA only */ + int NETWORK_MODE_EVDO_NO_CDMA = 6; /* EvDo only */ + int NETWORK_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL) + AVAILABLE Application Settings menu*/ + int PREFERRED_NETWORK_MODE = NETWORK_MODE_GLOBAL; + + /* CDMA subscription source. See ril.h RIL_REQUEST_CDMA_SET_SUBSCRIPTION */ + int SUBSCRIPTION_FROM_RUIM = 0; /* CDMA subscription from RUIM when available */ + int SUBSCRIPTION_FROM_NV = 1; /* CDMA subscription from NV */ + int PREFERRED_CDMA_SUBSCRIPTION = SUBSCRIPTION_FROM_NV; + + int CDMA_CELL_BROADCAST_SMS_DISABLED = 1; + int CDMA_CELL_BROADCAST_SMS_ENABLED = 0; + + int CDMA_PHONE = 0; + int GSM_PHONE = 1; + + int CDM_TTY_MODE_DISABLED = 0; + int CDM_TTY_MODE_ENABLED = 1; + + byte CDMA_VOICE_PRIVACY = 0x70; /* "p" value used in Ril_Call.isVoice if Privacy + is active */ + +/* +cat include/telephony/ril.h | \ + egrep '^#define' | \ + sed -re 's/^#define +([^ ]+)* +([^ ]+)/ int \1 = \2;/' \ + >>java/android/com.android.internal.telephony/gsm/RILConstants.java +*/ + + + int RIL_SIM_ABSENT = 0; + int RIL_SIM_NOT_READY = 1; + int RIL_SIM_READY = 2; + int RIL_SIM_PIN = 3; + int RIL_SIM_PUK = 4; + int RIL_SIM_NETWORK_PERSONALIZATION = 5; + + /** + * No restriction at all including voice/SMS/USSD/SS/AV64 + * and packet data. + */ + int RIL_RESTRICTED_STATE_NONE = 0x00; + /** + * Block emergency call due to restriction. + * But allow all normal voice/SMS/USSD/SS/AV64. + */ + int RIL_RESTRICTED_STATE_CS_EMERGENCY = 0x01; + /** + * Block all normal voice/SMS/USSD/SS/AV64 due to restriction. + * Only Emergency call allowed. + */ + int RIL_RESTRICTED_STATE_CS_NORMAL = 0x02; + /** + * Block all voice/SMS/USSD/SS/AV64 + * including emergency call due to restriction. + */ + int RIL_RESTRICTED_STATE_CS_ALL = 0x04; + /** + * Block packet data access due to restriction. + */ + int RIL_RESTRICTED_STATE_PS_ALL = 0x10; + + int RIL_REQUEST_GET_SIM_STATUS = 1; + int RIL_REQUEST_ENTER_SIM_PIN = 2; + int RIL_REQUEST_ENTER_SIM_PUK = 3; + int RIL_REQUEST_ENTER_SIM_PIN2 = 4; + int RIL_REQUEST_ENTER_SIM_PUK2 = 5; + int RIL_REQUEST_CHANGE_SIM_PIN = 6; + int RIL_REQUEST_CHANGE_SIM_PIN2 = 7; + int RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION = 8; + int RIL_REQUEST_GET_CURRENT_CALLS = 9; + int RIL_REQUEST_DIAL = 10; + int RIL_REQUEST_GET_IMSI = 11; + int RIL_REQUEST_HANGUP = 12; + int RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND = 13; + int RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND = 14; + int RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE = 15; + int RIL_REQUEST_CONFERENCE = 16; + int RIL_REQUEST_UDUB = 17; + int RIL_REQUEST_LAST_CALL_FAIL_CAUSE = 18; + int RIL_REQUEST_SIGNAL_STRENGTH = 19; + int RIL_REQUEST_REGISTRATION_STATE = 20; + int RIL_REQUEST_GPRS_REGISTRATION_STATE = 21; + int RIL_REQUEST_OPERATOR = 22; + int RIL_REQUEST_RADIO_POWER = 23; + int RIL_REQUEST_DTMF = 24; + int RIL_REQUEST_SEND_SMS = 25; + int RIL_REQUEST_SEND_SMS_EXPECT_MORE = 26; + int RIL_REQUEST_SETUP_DATA_CALL = 27; + int RIL_REQUEST_SIM_IO = 28; + int RIL_REQUEST_SEND_USSD = 29; + int RIL_REQUEST_CANCEL_USSD = 30; + int RIL_REQUEST_GET_CLIR = 31; + int RIL_REQUEST_SET_CLIR = 32; + int RIL_REQUEST_QUERY_CALL_FORWARD_STATUS = 33; + int RIL_REQUEST_SET_CALL_FORWARD = 34; + int RIL_REQUEST_QUERY_CALL_WAITING = 35; + int RIL_REQUEST_SET_CALL_WAITING = 36; + int RIL_REQUEST_SMS_ACKNOWLEDGE = 37; + int RIL_REQUEST_GET_IMEI = 38; + int RIL_REQUEST_GET_IMEISV = 39; + int RIL_REQUEST_ANSWER = 40; + int RIL_REQUEST_DEACTIVATE_DATA_CALL = 41; + int RIL_REQUEST_QUERY_FACILITY_LOCK = 42; + int RIL_REQUEST_SET_FACILITY_LOCK = 43; + int RIL_REQUEST_CHANGE_BARRING_PASSWORD = 44; + int RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE = 45; + int RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC = 46; + int RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL = 47; + int RIL_REQUEST_QUERY_AVAILABLE_NETWORKS = 48; + int RIL_REQUEST_DTMF_START = 49; + int RIL_REQUEST_DTMF_STOP = 50; + int RIL_REQUEST_BASEBAND_VERSION = 51; + int RIL_REQUEST_SEPARATE_CONNECTION = 52; + int RIL_REQUEST_SET_MUTE = 53; + int RIL_REQUEST_GET_MUTE = 54; + int RIL_REQUEST_QUERY_CLIP = 55; + int RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE = 56; + int RIL_REQUEST_DATA_CALL_LIST = 57; + int RIL_REQUEST_RESET_RADIO = 58; + int RIL_REQUEST_OEM_HOOK_RAW = 59; + int RIL_REQUEST_OEM_HOOK_STRINGS = 60; + int RIL_REQUEST_SCREEN_STATE = 61; + int RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION = 62; + int RIL_REQUEST_WRITE_SMS_TO_SIM = 63; + int RIL_REQUEST_DELETE_SMS_ON_SIM = 64; + int RIL_REQUEST_SET_BAND_MODE = 65; + int RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE = 66; + int RIL_REQUEST_STK_GET_PROFILE = 67; + int RIL_REQUEST_STK_SET_PROFILE = 68; + int RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND = 69; + int RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE = 70; + int RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM = 71; + int RIL_REQUEST_EXPLICIT_CALL_TRANSFER = 72; + int RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE = 73; + int RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE = 74; + int RIL_REQUEST_GET_NEIGHBORING_CELL_IDS = 75; + int RIL_REQUEST_SET_LOCATION_UPDATES = 76; + int RIL_REQUEST_CDMA_SET_SUBSCRIPTION = 77; + int RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE = 78; + int RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE = 79; + int RIL_REQUEST_SET_TTY_MODE = 80; + int RIL_REQUEST_QUERY_TTY_MODE = 81; + int RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE = 82; + int RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE = 83; + int RIL_REQUEST_CDMA_FLASH = 84; + int RIL_REQUEST_CDMA_BURST_DTMF = 85; + int RIL_REQUEST_CDMA_VALIDATE_AKEY = 86; + int RIL_REQUEST_CDMA_SEND_SMS = 87; + int RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE = 88; + int RIL_REQUEST_GET_BROADCAST_CONFIG = 89; + int RIL_REQUEST_SET_BROADCAST_CONFIG = 90; + int RIL_REQUEST_BROADCAST_ACTIVATION = 91; + int RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG = 92; + int RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG = 93; + int RIL_REQUEST_CDMA_BROADCAST_ACTIVATION = 94; + int RIL_REQUEST_CDMA_SUBSCRIPTION = 99; + int RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM = 100; + int RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM = 101; + int RIL_REQUEST_DEVICE_IDENTITY = 102; + int RIL_UNSOL_RESPONSE_BASE = 1000; + int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000; + int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001; + int RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED = 1002; + int RIL_UNSOL_RESPONSE_NEW_SMS = 1003; + int RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT = 1004; + int RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM = 1005; + int RIL_UNSOL_ON_USSD = 1006; + int RIL_UNSOL_ON_USSD_REQUEST = 1007; + int RIL_UNSOL_NITZ_TIME_RECEIVED = 1008; + int RIL_UNSOL_SIGNAL_STRENGTH = 1009; + int RIL_UNSOL_DATA_CALL_LIST_CHANGED = 1010; + int RIL_UNSOL_SUPP_SVC_NOTIFICATION = 1011; + int RIL_UNSOL_STK_SESSION_END = 1012; + int RIL_UNSOL_STK_PROACTIVE_COMMAND = 1013; + int RIL_UNSOL_STK_EVENT_NOTIFY = 1014; + int RIL_UNSOL_STK_CALL_SETUP = 1015; + int RIL_UNSOL_SIM_SMS_STORAGE_FULL = 1016; + int RIL_UNSOL_SIM_REFRESH = 1017; + int RIL_UNSOL_CALL_RING = 1018; + int RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED = 1019; + int RIL_UNSOL_RESPONSE_CDMA_NEW_SMS = 1020; + int RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS = 1021; + int RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL = 1022; + int RIL_UNSOL_RESTRICTED_STATE_CHANGED = 1023; +} diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java new file mode 100644 index 0000000..f2bd361 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -0,0 +1,744 @@ +/* + * 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.app.Activity; +import android.app.PendingIntent; +import android.app.AlertDialog; +import android.app.PendingIntent.CanceledException; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.database.Cursor; +import android.database.SQLException; +import android.net.Uri; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.provider.Telephony; +import android.provider.Telephony.Sms.Intents; +import android.provider.Settings; +import android.telephony.SmsMessage; +import android.telephony.ServiceState; +import android.util.Config; +import android.util.Log; +import android.view.WindowManager; + +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.SmsResponse; +import com.android.internal.telephony.WapPushOverSms; +import com.android.internal.util.HexDump; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; + +import com.android.internal.R; + +import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; +import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; +import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; +import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; + + +public abstract class SMSDispatcher extends Handler { + private static final String TAG = "SMS"; + + /** Default checking period for SMS sent without user permit */ + private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; + + /** Default number of SMS sent in checking period without user permit */ + private static final int DEFAULT_SMS_MAX_COUNT = 100; + + /** Default timeout for SMS sent query */ + private static final int DEFAULT_SMS_TIMOUEOUT = 6000; + + protected static final String[] RAW_PROJECTION = new String[] { + "pdu", + "sequence", + }; + + static final int MAIL_SEND_SMS = 1; + + static final protected int EVENT_NEW_SMS = 1; + + static final protected int EVENT_SEND_SMS_COMPLETE = 2; + + /** Retry sending a previously failed SMS message */ + static final protected int EVENT_SEND_RETRY = 3; + + /** Status report received */ + static final protected int EVENT_NEW_SMS_STATUS_REPORT = 5; + + /** SIM/RUIM storage is full */ + static final protected int EVENT_ICC_FULL = 6; + + /** SMS confirm required */ + static final protected int EVENT_POST_ALERT = 7; + + /** Send the user confirmed SMS */ + static final protected int EVENT_SEND_CONFIRMED_SMS = 8; + + /** Alert is timeout */ + static final protected int EVENT_ALERT_TIMEOUT = 9; + + protected Phone mPhone; + protected Context mContext; + protected ContentResolver mResolver; + protected CommandsInterface mCm; + + protected final WapPushOverSms mWapPush; + + protected final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); + + /** Maximum number of times to retry sending a failed SMS. */ + private static final int MAX_SEND_RETRIES = 3; + /** Delay before next send attempt on a failed SMS, in milliseconds. */ + private static final int SEND_RETRY_DELAY = 2000; + /** single part SMS */ + private static final int SINGLE_PART_SMS = 1; + + /** + * Message reference for a CONCATENATED_8_BIT_REFERENCE or + * CONCATENATED_16_BIT_REFERENCE message set. Should be + * incremented for each set of concatenated messages. + */ + protected static int sConcatenatedRef; + + private SmsCounter mCounter; + + private SmsTracker mSTracker; + + private static SmsMessage mSmsMessage; + private static SmsMessageBase mSmsMessageBase; + private SmsMessageBase.SubmitPduBase mSubmitPduBase; + + /** + * Implement the per-application based SMS control, which only allows + * a limit on the number of SMS/MMS messages an app can send in checking + * period. + */ + private class SmsCounter { + private int mCheckPeriod; + private int mMaxAllowed; + private HashMap> mSmsStamp; + + /** + * Create SmsCounter + * @param mMax is the number of SMS allowed without user permit + * @param mPeriod is the checking period + */ + SmsCounter(int mMax, int mPeriod) { + mMaxAllowed = mMax; + mCheckPeriod = mPeriod; + mSmsStamp = new HashMap> (); + } + + /** + * Check to see if an application allow to send new SMS messages + * + * @param appName is the application sending sms + * @param smsWaiting is the number of new sms wants to be sent + * @return true if application is allowed to send the requested number + * of new sms messages + */ + boolean check(String appName, int smsWaiting) { + if (!mSmsStamp.containsKey(appName)) { + mSmsStamp.put(appName, new ArrayList()); + } + + return isUnderLimit(mSmsStamp.get(appName), smsWaiting); + } + + private boolean isUnderLimit(ArrayList sent, int smsWaiting) { + Long ct = System.currentTimeMillis(); + + Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct); + + while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) { + sent.remove(0); + } + + + if ( (sent.size() + smsWaiting) <= mMaxAllowed) { + for (int i = 0; i < smsWaiting; i++ ) { + sent.add(ct); + } + return true; + } + return false; + } + } + + protected SMSDispatcher(PhoneBase phone) { + mPhone = phone; + mWapPush = new WapPushOverSms(phone); + mContext = phone.getContext(); + mResolver = mContext.getContentResolver(); + mCm = phone.mCM; + mSTracker = null; + + int check_period = Settings.Gservices.getInt(mResolver, + Settings.Gservices.SMS_OUTGOING_CEHCK_INTERVAL_MS, + DEFAULT_SMS_CHECK_PERIOD); + int max_count = Settings.Gservices.getInt(mResolver, + Settings.Gservices.SMS_OUTGOING_CEHCK_MAX_COUNT, + DEFAULT_SMS_MAX_COUNT); + mCounter = new SmsCounter(max_count, check_period); + + mCm.setOnNewSMS(this, EVENT_NEW_SMS, null); + mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); + mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null); + + // Don't always start message ref at 0. + sConcatenatedRef = new Random().nextInt(256); + } + + public void dispose() { + mCm.unSetOnNewSMS(this); + mCm.unSetOnSmsStatus(this); + mCm.unSetOnIccSmsFull(this); + } + + protected void finalize() { + Log.d(TAG, "SMSDispatcher finalized"); + } + + + /* TODO: Need to figure out how to keep track of status report routing in a + * persistent manner. If the phone process restarts (reboot or crash), + * we will lose this list and any status reports that come in after + * will be dropped. + */ + /** Sent messages awaiting a delivery status report. */ + protected final ArrayList deliveryPendingList = new ArrayList(); + + /** + * Handles events coming from the phone stack. Overridden from handler. + * + * @param msg the message to handle + */ + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + + switch (msg.what) { + case EVENT_NEW_SMS: + // A new SMS has been received by the device + if (Config.LOGD) { + Log.d(TAG, "New SMS Message Received"); + } + + SmsMessage sms; + + ar = (AsyncResult) msg.obj; + + // FIXME only acknowledge on store + acknowledgeLastIncomingSms(true, null); + + if (ar.exception != null) { + Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); + return; + } + + sms = (SmsMessage) ar.result; + dispatchMessage(sms.mWrappedSmsMessage); + + break; + + case EVENT_SEND_SMS_COMPLETE: + // An outbound SMS has been successfully transferred, or failed. + handleSendComplete((AsyncResult) msg.obj); + break; + + case EVENT_SEND_RETRY: + sendSms((SmsTracker) msg.obj); + break; + + case EVENT_NEW_SMS_STATUS_REPORT: + handleStatusReport((AsyncResult)msg.obj); + break; + + case EVENT_ICC_FULL: + handleIccFull(); + break; + + case EVENT_POST_ALERT: + handleReachSentLimit((SmsTracker)(msg.obj)); + break; + + case EVENT_ALERT_TIMEOUT: + ((AlertDialog)(msg.obj)).dismiss(); + msg.obj = null; + mSTracker = null; + break; + + case EVENT_SEND_CONFIRMED_SMS: + if (mSTracker!=null) { + if (isMultipartTracker(mSTracker)) { + sendMultipartSms(mSTracker); + } else { + sendSms(mSTracker); + } + mSTracker = null; + } + break; + } + } + + /** + * Called when SIM_FULL message is received from the RIL. Notifies interested + * parties that SIM storage for SMS messages is full. + */ + private void handleIccFull(){ + // broadcast SIM_FULL intent + Intent intent = new Intent(Intents.SIM_FULL_ACTION); + mPhone.getContext().sendBroadcast(intent, "android.permission.RECEIVE_SMS"); + } + + /** + * Called when a status report is received. This should correspond to + * a previously successful SEND. + * + * @param ar AsyncResult passed into the message handler. ar.result should + * be a String representing the status report PDU, as ASCII hex. + */ + protected abstract void handleStatusReport(AsyncResult ar); + + /** + * Called when SMS send completes. Broadcasts a sentIntent on success. + * On failure, either sets up retries or broadcasts a sentIntent with + * the failure in the result code. + * + * @param ar AsyncResult passed into the message handler. ar.result should + * an SmsResponse instance if send was successful. ar.userObj + * should be an SmsTracker instance. + */ + protected void handleSendComplete(AsyncResult ar) { + SmsTracker tracker = (SmsTracker) ar.userObj; + PendingIntent sentIntent = tracker.mSentIntent; + + if (ar.exception == null) { + if (Config.LOGD) { + Log.d(TAG, "SMS send complete. Broadcasting " + + "intent: " + sentIntent); + } + + if (tracker.mDeliveryIntent != null) { + // Expecting a status report. Add it to the list. + int messageRef = ((SmsResponse)ar.result).messageRef; + tracker.mMessageRef = messageRef; + deliveryPendingList.add(tracker); + } + + if (sentIntent != null) { + try { + sentIntent.send(Activity.RESULT_OK); + } catch (CanceledException ex) {} + } + } else { + if (Config.LOGD) { + Log.d(TAG, "SMS send failed"); + } + + int ss = mPhone.getServiceState().getState(); + + if (ss != ServiceState.STATE_IN_SERVICE) { + handleNotInService(ss, tracker); + } else if ((((CommandException)(ar.exception)).getCommandError() + == CommandException.Error.SMS_FAIL_RETRY) && + tracker.mRetryCount < MAX_SEND_RETRIES) { + // Retry after a delay if needed. + // TODO: According to TS 23.040, 9.2.3.6, we should resend + // with the same TP-MR as the failed message, and + // TP-RD set to 1. However, we don't have a means of + // knowing the MR for the failed message (EF_SMSstatus + // may or may not have the MR corresponding to this + // message, depending on the failure). Also, in some + // implementations this retry is handled by the baseband. + tracker.mRetryCount++; + Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); + sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); + } else if (tracker.mSentIntent != null) { + // Done retrying; return an error to the app. + try { + tracker.mSentIntent.send(RESULT_ERROR_GENERIC_FAILURE); + } catch (CanceledException ex) {} + } + } + } + + /** + * Handles outbound message when the phone is not in service. + * + * @param ss Current service state. Valid values are: + * OUT_OF_SERVICE + * EMERGENCY_ONLY + * POWER_OFF + * @param tracker An SmsTracker for the current message. + */ + protected void handleNotInService(int ss, SmsTracker tracker) { + if (tracker.mSentIntent != null) { + try { + if (ss == ServiceState.STATE_POWER_OFF) { + tracker.mSentIntent.send(RESULT_ERROR_RADIO_OFF); + } else { + tracker.mSentIntent.send(RESULT_ERROR_NO_SERVICE); + } + } catch (CanceledException ex) {} + } + } + + /** + * Dispatches an incoming SMS messages. + * + * @param sms the incoming message from the phone + */ + protected abstract void dispatchMessage(SmsMessageBase sms); + + + /** + * If this is the last part send the parts out to the application, otherwise + * the part is stored for later processing. + */ + protected void processMessagePart(SmsMessageBase sms, int referenceNumber, + int sequence, int count, int destinationPort) { + // Lookup all other related parts + StringBuilder where = new StringBuilder("reference_number ="); + where.append(referenceNumber); + where.append(" AND address = ?"); + String[] whereArgs = new String[] {sms.getOriginatingAddress()}; + + byte[][] pdus = null; + Cursor cursor = null; + try { + cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); + int cursorCount = cursor.getCount(); + if (cursorCount != count - 1) { + // We don't have all the parts yet, store this one away + ContentValues values = new ContentValues(); + values.put("date", new Long(sms.getTimestampMillis())); + values.put("pdu", HexDump.toHexString(sms.getPdu())); + values.put("address", sms.getOriginatingAddress()); + values.put("reference_number", referenceNumber); + values.put("count", count); + values.put("sequence", sequence); + if (destinationPort != -1) { + values.put("destination_port", destinationPort); + } + mResolver.insert(mRawUri, values); + + return; + } + + // All the parts are in place, deal with them + int pduColumn = cursor.getColumnIndex("pdu"); + int sequenceColumn = cursor.getColumnIndex("sequence"); + + pdus = new byte[count][]; + for (int i = 0; i < cursorCount; i++) { + cursor.moveToNext(); + int cursorSequence = (int)cursor.getLong(sequenceColumn); + pdus[cursorSequence - 1] = HexDump.hexStringToByteArray( + cursor.getString(pduColumn)); + } + // This one isn't in the DB, so add it + pdus[sequence - 1] = sms.getPdu(); + + // Remove the parts from the database + mResolver.delete(mRawUri, where.toString(), whereArgs); + } catch (SQLException e) { + Log.e(TAG, "Can't access multipart SMS database", e); + return; // TODO: NACK the message or something, don't just discard. + } finally { + if (cursor != null) cursor.close(); + } + + // Dispatch the PDUs to applications + switch (destinationPort) { + case SmsHeader.PORT_WAP_PUSH: { + // Build up the data stream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + for (int i = 0; i < count; i++) { + SmsMessage msg = SmsMessage.createFromPdu(pdus[i]); + byte[] data = msg.getUserData(); + output.write(data, 0, data.length); + } + + // Handle the PUSH + mWapPush.dispatchWapPdu(output.toByteArray()); + break; + } + + case -1: + // The messages were not sent to a port + dispatchPdus(pdus); + break; + + default: + // The messages were sent to a port, so concoct a URI for it + dispatchPortAddressedPdus(pdus, destinationPort); + break; + } + } + + /** + * Dispatches standard PDUs to interested applications + * + * @param pdus The raw PDUs making up the message + */ + protected void dispatchPdus(byte[][] pdus) { + Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); + intent.putExtra("pdus", pdus); + mPhone.getContext().sendBroadcast( + intent, "android.permission.RECEIVE_SMS"); + } + + /** + * Dispatches port addressed PDUs to interested applications + * + * @param pdus The raw PDUs making up the message + * @param port The destination port of the messages + */ + protected void dispatchPortAddressedPdus(byte[][] pdus, int port) { + Uri uri = Uri.parse("sms://localhost:" + port); + Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); + intent.putExtra("pdus", pdus); + mPhone.getContext().sendBroadcast( + intent, "android.permission.RECEIVE_SMS"); + } + + + /** + * Send a multi-part 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 parts an ArrayList of strings that, in order, + * comprise the original message + * @param sentIntents if not null, an ArrayList of + * PendingIntents (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * 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 ArrayList of + * PendingIntents (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"). + */ + protected abstract void sendMultipartText(String destinationAddress, String scAddress, + ArrayList parts, ArrayList sentIntents, + ArrayList deliveryIntents); + + /** + * Send a SMS + * + * @param smsc the SMSC to send the message through, or NULL for the + * defatult SMSC + * @param pdu the raw PDU to send + * @param sentIntent if not NULL this Intent is + * broadcast when the message is sucessfully sent, or failed. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * 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 Intent is + * broadcast when the message is delivered to the recipient. The + * raw pdu of the status report is in the extended data ("pdu"). + */ + protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, + PendingIntent deliveryIntent) { + if (pdu == null) { + if (sentIntent != null) { + try { + sentIntent.send(RESULT_ERROR_NULL_PDU); + } catch (CanceledException ex) {} + } + return; + } + + HashMap map = new HashMap(); + map.put("smsc", smsc); + map.put("pdu", pdu); + + SmsTracker tracker = new SmsTracker(map, sentIntent, + deliveryIntent); + int ss = mPhone.getServiceState().getState(); + + if (ss != ServiceState.STATE_IN_SERVICE) { + handleNotInService(ss, tracker); + } else { + String appName = getAppNameByIntent(sentIntent); + if (mCounter.check(appName, SINGLE_PART_SMS)) { + sendSms(tracker); + } else { + sendMessage(obtainMessage(EVENT_POST_ALERT, tracker)); + } + } + } + + /** + * Post an alert while SMS needs user confirm. + * + * An SmsTracker for the current message. + */ + protected void handleReachSentLimit(SmsTracker tracker) { + + Resources r = Resources.getSystem(); + + String appName = getAppNameByIntent(tracker.mSentIntent); + + AlertDialog d = new AlertDialog.Builder(mContext) + .setTitle(r.getString(R.string.sms_control_title)) + .setMessage(appName + " " + r.getString(R.string.sms_control_message)) + .setPositiveButton(r.getString(R.string.sms_control_yes), mListener) + .setNegativeButton(r.getString(R.string.sms_control_no), null) + .create(); + + d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + d.show(); + + mSTracker = tracker; + sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d), + DEFAULT_SMS_TIMOUEOUT); + } + + protected String getAppNameByIntent(PendingIntent intent) { + Resources r = Resources.getSystem(); + return (intent != null) ? intent.getTargetPackage() + : r.getString(R.string.sms_control_default_app_name); + } + + /** + * Send the message along to the radio. + * + * @param tracker holds the SMS message to send + */ + protected abstract void sendSms(SmsTracker tracker); + + /** + * Send the multi-part SMS based on multipart Sms tracker + * + * @param tracker holds the multipart Sms tracker ready to be sent + */ + protected abstract void sendMultipartSms (SmsTracker tracker); + + /** + * Activate or deactivate cell broadcast SMS. + * + * @param activate + * 0 = activate, 1 = deactivate + * @param response + * Callback message is empty on completion + */ + protected abstract void activateCellBroadcastSms(int activate, Message response); + + /** + * Query the current configuration of cell broadcast SMS. + * + * @param response + * Callback message contains the configuration from the modem on completion + * @see #setCellBroadcastConfig + */ + protected abstract void getCellBroadcastSmsConfig(Message response); + + /** + * Configure cell broadcast SMS. + * + * @param configValuesArray + * The first element defines the number of triples that follow. + * A triple is made up of the service category, the language identifier + * and a boolean that specifies whether the category is set active. + * @param response + * Callback message is empty on completion + */ + protected abstract void setCellBroadcastConfig(int[] configValuesArray, Message response); + + /** + * Send an acknowledge message. + * @param success indicates that last message was successfully received. + * @param response callback message sent when operation completes. + */ + protected abstract void acknowledgeLastIncomingSms(boolean success, Message response); + + /** + * Check if a SmsTracker holds multi-part Sms + * + * @param tracker a SmsTracker could hold a multi-part Sms + * @return true for tracker holds Multi-parts Sms + */ + private boolean isMultipartTracker (SmsTracker tracker) { + HashMap map = tracker.mData; + return ( map.get("parts") != null); + } + + /** + * Keeps track of an SMS that has been sent to the RIL, until it it has + * successfully been sent, or we're done trying. + * + */ + static protected class SmsTracker { + // fields need to be public for derived SmsDispatchers + public HashMap mData; + public int mRetryCount; + public int mMessageRef; + + public PendingIntent mSentIntent; + public PendingIntent mDeliveryIntent; + + SmsTracker(HashMap data, PendingIntent sentIntent, + PendingIntent deliveryIntent) { + mData = data; + mSentIntent = sentIntent; + mDeliveryIntent = deliveryIntent; + mRetryCount = 0; + } + } + + protected SmsTracker SmsTrackerFactory(HashMap data, PendingIntent sentIntent, + PendingIntent deliveryIntent) { + return new SmsTracker(data, sentIntent, deliveryIntent); + } + + private DialogInterface.OnClickListener mListener = + new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + Log.d(TAG, "click YES to send out sms"); + sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS)); + } + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java new file mode 100644 index 0000000..a4bf0dd --- /dev/null +++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java @@ -0,0 +1,244 @@ +/* + * 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.os.Registrant; +import android.os.RegistrantList; +import android.telephony.ServiceState; + +/** + * {@hide} + */ +public abstract class ServiceStateTracker extends Handler { + /** + * The access technology currently in use: + * 0 = unknown + * 1 = GPRS only + * 2 = EDGE + * 3 = UMTS + */ + protected static final int DATA_ACCESS_UNKNOWN = 0; + protected static final int DATA_ACCESS_GPRS = 1; + protected static final int DATA_ACCESS_EDGE = 2; + protected static final int DATA_ACCESS_UMTS = 3; + protected static final int DATA_ACCESS_CDMA_IS95A = 4; + protected static final int DATA_ACCESS_CDMA_IS95B = 5; + protected static final int DATA_ACCESS_CDMA_1xRTT = 6; + protected static final int DATA_ACCESS_CDMA_EvDo_0 = 7; + protected static final int DATA_ACCESS_CDMA_EvDo_A = 8; + //***** Instance Variables + + protected CommandsInterface cm; + + public ServiceState ss; + protected ServiceState newSS; + + // Used as a unique identifier to track requests associated with a poll + // and ignore stale responses.The value is a count-down of expected responses + // in this pollingContext + protected int[] pollingContext; + protected boolean mDesiredPowerState; + + protected boolean dontPollSignalStrength = false; // Default is to poll strength + // If we're getting unsolicited signal strength updates from the radio, + // set value to true and don't bother polling any more + + protected RegistrantList networkAttachedRegistrants = new RegistrantList(); + protected RegistrantList roamingOnRegistrants = new RegistrantList(); + protected RegistrantList roamingOffRegistrants = new RegistrantList(); + + //***** Constants + + protected static final boolean DBG = true; + + // signal strength poll rate + protected static final int POLL_PERIOD_MILLIS = 20 * 1000; + + // waiting period before recheck gprs and voice registration + public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; + + public static final int MAX_NUM_DATA_STATE_READS = 15; + public static final int DATA_STATE_POLL_SLEEP_MS = 100; + + //*****GSM events + protected static final int EVENT_RADIO_STATE_CHANGED = 1; + protected static final int EVENT_NETWORK_STATE_CHANGED = 2; + protected static final int EVENT_GET_SIGNAL_STRENGTH = 3; + protected static final int EVENT_POLL_STATE_REGISTRATION = 4; + protected static final int EVENT_POLL_STATE_GPRS = 5; + protected static final int EVENT_POLL_STATE_OPERATOR = 6; + protected static final int EVENT_POLL_SIGNAL_STRENGTH = 10; + protected static final int EVENT_NITZ_TIME = 11; + protected static final int EVENT_SIGNAL_STRENGTH_UPDATE = 12; + protected static final int EVENT_RADIO_AVAILABLE = 13; + protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14; + protected static final int EVENT_GET_LOC_DONE = 15; + protected static final int EVENT_SIM_RECORDS_LOADED = 16; + protected static final int EVENT_SIM_READY = 17; + protected static final int EVENT_LOCATION_UPDATES_ENABLED = 18; + protected static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19; + protected static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20; + protected static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21; + protected static final int EVENT_CHECK_REPORT_GPRS = 22; + protected static final int EVENT_RESTRICTED_STATE_CHANGED = 23; + + //*****CDMA events: + protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA = 24; + protected static final int EVENT_POLL_STATE_OPERATOR_CDMA = 25; + protected static final int EVENT_RUIM_READY = 26; + protected static final int EVENT_RUIM_RECORDS_LOADED = 27; + protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA = 28; + protected static final int EVENT_POLL_SIGNAL_STRENGTH_CDMA = 29; + protected static final int EVENT_GET_SIGNAL_STRENGTH_CDMA = 30; + protected static final int EVENT_NETWORK_STATE_CHANGED_CDMA = 31; + protected static final int EVENT_GET_LOC_DONE_CDMA = 32; + protected static final int EVENT_SIGNAL_STRENGTH_UPDATE_CDMA = 33; + protected static final int EVENT_NV_LOADED = 34; + + // Event Log Tags + protected static final int EVENT_LOG_CGREG_FAIL = 50107; + protected static final int EVENT_DATA_STATE_RADIO_OFF = 50108; + + //***** Time Zones + protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; + + // List of ISO codes for countries that can have an offset of GMT+0 + // when not in daylight savings time. This ignores some small places + // such as the Canary Islands (Spain) and Danmarkshavn (Denmark). + // The list must be sorted by code. + protected static final String[] GMT_COUNTRY_CODES = { + "bf", // Burkina Faso + "ci", // Cote d'Ivoire + "eh", // Western Sahara + "fo", // Faroe Islands, Denmark + "gh", // Ghana + "gm", // Gambia + "gn", // Guinea + "gw", // Guinea Bissau + "ie", // Ireland + "lr", // Liberia + "is", // Iceland + "ma", // Morocco + "ml", // Mali + "mr", // Mauritania + "pt", // Portugal + "sl", // Sierra Leone + "sn", // Senegal + "st", // Sao Tome and Principe + "tg", // Togo + "uk", // U.K + }; + + + //***** Constructors + public ServiceStateTracker() { + + } + + + /** + * Registration point for combined roaming on + * combined roaming is true when roaming is true and ONS differs SPN + * + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + public void registerForRoamingOn(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + roamingOnRegistrants.add(r); + + if (ss.getRoaming()) { + r.notifyRegistrant(); + } + } + + public void unregisterForRoamingOn(Handler h) { + roamingOnRegistrants.remove(h); + } + + /** + * Registration point for combined roaming off + * combined roaming is true when roaming is true and ONS differs SPN + * + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + public void registerForRoamingOff(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + roamingOffRegistrants.add(r); + + if (!ss.getRoaming()) { + r.notifyRegistrant(); + } + } + + public void unregisterForRoamingOff(Handler h) { + roamingOffRegistrants.remove(h); + } + + /** + * Reregister network through toggle perferred network type + * This is a work aorund to deregister and register network since there is + * no ril api to set COPS=2 (deregister) only. + * + * @param onComplete is dispatched when this is complete. it will be + * an AsyncResult, and onComplete.obj.exception will be non-null + * on failure. + */ + public void reRegisterNetwork(Message onComplete) { + cm.getPreferredNetworkType( + obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete)); + } + + + //***** Called from Phone + public void + setRadioPower(boolean power) { + mDesiredPowerState = power; + + setPowerStateToDesired(); + } + + + public void enableLocationUpdates() { + cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); + } + + public void disableLocationUpdates() { + cm.setLocationUpdates(false, null); + } + + //***** Overridden from Handler + public abstract void handleMessage(Message msg); + + //***** Protected abstract Methods + protected abstract void handlePollStateResult(int what, AsyncResult ar); + protected abstract void updateSpnDisplay(); + protected abstract void setPowerStateToDesired(); + + /** Cancel a pending (if any) pollState() operation */ + protected void cancelPollState() { + // This will effectively cancel the rest of the poll requests + pollingContext = new int[1]; + } +} + diff --git a/telephony/java/com/android/internal/telephony/SimCard.java b/telephony/java/com/android/internal/telephony/SimCard.java deleted file mode 100644 index 03b366f..0000000 --- a/telephony/java/com/android/internal/telephony/SimCard.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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.Message; -import android.os.Handler; - -/** - * {@hide} - */ -public interface SimCard -{ - /* The extra data for broacasting intent INTENT_SIM_STATE_CHANGE */ - static public final String INTENT_KEY_SIM_STATE = "ss"; - /* NOT_READY means the SIM interface is not ready (eg, radio is off or powering on) */ - static public final String INTENT_VALUE_SIM_NOT_READY = "NOT_READY"; - /* ABSENT means SIM is missing */ - static public final String INTENT_VALUE_SIM_ABSENT = "ABSENT"; - /* LOCKED means SIM is locked by pin or by network */ - static public final String INTENT_VALUE_SIM_LOCKED = "LOCKED"; - /* READY means SIM is ready to access */ - static public final String INTENT_VALUE_SIM_READY = "READY"; - /* IMSI means SIM IMSI is ready in property */ - static public final String INTENT_VALUE_SIM_IMSI = "IMSI"; - /* LOADED means all SIM records, including IMSI, are loaded */ - static public final String INTENT_VALUE_SIM_LOADED = "LOADED"; - /* The extra data for broacasting intent INTENT_SIM_STATE_CHANGE */ - static public final String INTENT_KEY_LOCKED_REASON = "reason"; - /* PIN means SIM is locked on PIN1 */ - static public final String INTENT_VALUE_LOCKED_ON_PIN = "PIN"; - /* PUK means SIM is locked on PUK1 */ - static public final String INTENT_VALUE_LOCKED_ON_PUK = "PUK"; - /* NETWORK means SIM is locked on NETWORK PERSONALIZATION */ - static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK"; - - - /* - UNKNOWN is a transient state, for example, after uesr inputs sim pin under - PIN_REQUIRED state, the query for sim status returns UNKNOWN before it - turns to READY - */ - public enum State { - UNKNOWN, - ABSENT, - PIN_REQUIRED, - PUK_REQUIRED, - NETWORK_LOCKED, - READY; - - public boolean isPinLocked() { - return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)); - } - } - - State getState(); - - - /** - * Notifies handler of any transition into State.ABSENT - */ - void registerForAbsent(Handler h, int what, Object obj); - void unregisterForAbsent(Handler h); - - /** - * Notifies handler of any transition into State.isPinLocked() - */ - void registerForLocked(Handler h, int what, Object obj); - void unregisterForLocked(Handler h); - - /** - * Notifies handler of any transition into State.NETWORK_LOCKED - */ - void registerForNetworkLocked(Handler h, int what, Object obj); - void unregisterForNetworkLocked(Handler h); - - /** - * Supply the SIM PIN to the SIM - * - * When the operation is complete, onComplete will be sent to it's - * Handler. - * - * onComplete.obj will be an AsyncResult - * - * ((AsyncResult)onComplete.obj).exception == null on success - * ((AsyncResult)onComplete.obj).exception != null on fail - * - * If the supplied PIN is incorrect: - * ((AsyncResult)onComplete.obj).exception != null - * && ((AsyncResult)onComplete.obj).exception - * instanceof com.android.internal.telephony.gsm.CommandException) - * && ((CommandException)(((AsyncResult)onComplete.obj).exception)) - * .getCommandError() == CommandException.Error.PASSWORD_INCORRECT - * - * - */ - - void supplyPin (String pin, Message onComplete); - void supplyPuk (String puk, String newPin, Message onComplete); - void supplyPin2 (String pin2, Message onComplete); - void supplyPuk2 (String puk2, String newPin2, Message onComplete); - - /** - * Check whether sim pin lock is enabled - * This is a sync call which returns the cached pin enabled state - * - * @return true for sim locked enabled - * false for sim locked disabled - */ - boolean getSimLockEnabled (); - - /** - * Set the sim pin lock enabled or disabled - * When the operation is complete, onComplete will be sent to its handler - * - * @param enabled "true" for locked "false" for unlocked. - * @param password needed to change the sim pin state, aka. Pin1 - * @param onComplete - * onComplete.obj will be an AsyncResult - * ((AsyncResult)onComplete.obj).exception == null on success - * ((AsyncResult)onComplete.obj).exception != null on fail - */ - void setSimLockEnabled(boolean enabled, String password, Message onComplete); - - - /** - * Change the sim password used in sim pin lock - * When the operation is complete, onComplete will be sent to its handler - * - * @param oldPassword is the old password - * @param newPassword is the new password - * @param onComplete - * onComplete.obj will be an AsyncResult - * ((AsyncResult)onComplete.obj).exception == null on success - * ((AsyncResult)onComplete.obj).exception != null on fail - */ - void changeSimLockPassword(String oldPassword, String newPassword, - Message onComplete); - - /** - * Check whether sim fdn (fixed dialing number) is enabled - * This is a sync call which returns the cached pin enabled state - * - * @return true for sim fdn enabled - * false for sim fdn disabled - */ - boolean getSimFdnEnabled (); - - /** - * Set the sim fdn enabled or disabled - * When the operation is complete, onComplete will be sent to its handler - * - * @param enabled "true" for locked "false" for unlocked. - * @param password needed to change the sim fdn enable, aka Pin2 - * @param onComplete - * onComplete.obj will be an AsyncResult - * ((AsyncResult)onComplete.obj).exception == null on success - * ((AsyncResult)onComplete.obj).exception != null on fail - */ - void setSimFdnEnabled(boolean enabled, String password, Message onComplete); - - /** - * Change the sim password used in sim fdn enable - * When the operation is complete, onComplete will be sent to its handler - * - * @param oldPassword is the old password - * @param newPassword is the new password - * @param onComplete - * onComplete.obj will be an AsyncResult - * ((AsyncResult)onComplete.obj).exception == null on success - * ((AsyncResult)onComplete.obj).exception != null on fail - */ - void changeSimFdnPassword(String oldPassword, String newPassword, - Message onComplete); - - void supplyNetworkDepersonalization (String pin, Message onComplete); - - /** - * Returns service provider name stored in SIM card. - * If there is no service provider name associated or the record is not - * yet available, null will be returned

      - * - * Please use this value when display Service Provider Name in idle mode

      - * - * Usage of this provider name in the UI is a common carrier requirement. - * - * Also available via Android property "gsm.sim.operator.alpha" - * - * @return Service Provider Name stored in SIM card - * null if no service provider name associated or the record is not - * yet available - * - */ - String getServiceProviderName(); -} diff --git a/telephony/java/com/android/internal/telephony/SmsAddress.java b/telephony/java/com/android/internal/telephony/SmsAddress.java new file mode 100644 index 0000000..b3892cb --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsAddress.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +public abstract class SmsAddress { + // From TS 23.040 9.1.2.5 and TS 24.008 table 10.5.118 + // and C.S0005-D table 2.7.1.3.2.4-2 + public static final int TON_UNKNOWN = 0; + public static final int TON_INTERNATIONAL = 1; + public static final int TON_NATIONAL = 2; + public static final int TON_NETWORK = 3; + public static final int TON_SUBSCRIBER = 4; + public static final int TON_ALPHANUMERIC = 5; + public static final int TON_ABBREVIATED = 6; + + public int ton; + public String address; + public byte[] origBytes; + + /** + * Returns the address of the SMS message in String form or null if unavailable + */ + public String getAddressString() { + return address; + } + + /** + * Returns true if this is an alphanumeric address + */ + public boolean isAlphanumeric() { + return ton == TON_ALPHANUMERIC; + } + + /** + * Returns true if this is a network address + */ + public boolean isNetworkSpecific() { + return ton == TON_NETWORK; + } + + public boolean couldBeEmailGateway() { + // Some carriers seems to send email gateway messages in this form: + // from: an UNKNOWN TON, 3 or 4 digits long, beginning with a 5 + // PID: 0x00, Data coding scheme 0x03 + // So we just attempt to treat any message from an address length <= 4 + // as an email gateway + + return address.length() <= 4; + } + +} diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java new file mode 100644 index 0000000..64b884e --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsHeader.java @@ -0,0 +1,242 @@ +/* + * 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 com.android.internal.util.HexDump; + +import java.util.ArrayList; + +/** + * This class represents a SMS user data header. + * + */ +public class SmsHeader { + /** See TS 23.040 9.2.3.24 for description of this element ID. */ + public static final int CONCATENATED_8_BIT_REFERENCE = 0x00; + /** See TS 23.040 9.2.3.24 for description of this element ID. */ + public static final int SPECIAL_SMS_MESSAGE_INDICATION = 0x01; + /** See TS 23.040 9.2.3.24 for description of this element ID. */ + public static final int APPLICATION_PORT_ADDRESSING_8_BIT = 0x04; + /** See TS 23.040 9.2.3.24 for description of this element ID. */ + public static final int APPLICATION_PORT_ADDRESSING_16_BIT= 0x05; + /** See TS 23.040 9.2.3.24 for description of this element ID. */ + public static final int CONCATENATED_16_BIT_REFERENCE = 0x08; + + public static final int PORT_WAP_PUSH = 2948; + public static final int PORT_WAP_WSP = 9200; + + private byte[] m_data; + private ArrayList m_elements = new ArrayList(); + public int nbrOfHeaders; + + /** + * Creates an SmsHeader object from raw user data header bytes. + * + * @param data is user data header bytes + * @return an SmsHeader object + */ + public static SmsHeader parse(byte[] data) { + SmsHeader header = new SmsHeader(); + header.m_data = data; + + int index = 0; + header.nbrOfHeaders = 0; + while (index < data.length) { + int id = data[index++] & 0xff; + int length = data[index++] & 0xff; + byte[] elementData = new byte[length]; + System.arraycopy(data, index, elementData, 0, length); + header.add(new Element(id, elementData)); + index += length; + header.nbrOfHeaders++; + } + + return header; + } + + public SmsHeader() { } + + /** + * Returns the list of SmsHeader Elements that make up the header. + * + * @return the list of SmsHeader Elements. + */ + public ArrayList getElements() { + return m_elements; + } + + /** + * Add an element to the SmsHeader. + * + * @param element to add. + */ + public void add(Element element) { + m_elements.add(element); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + builder.append("UDH LENGTH: " + m_data.length + " octets"); + builder.append("UDH: "); + builder.append(HexDump.toHexString(m_data)); + builder.append("\n"); + + for (Element e : getElements()) { + builder.append(" 0x" + HexDump.toHexString((byte)e.getID()) + " - "); + switch (e.getID()) { + case CONCATENATED_8_BIT_REFERENCE: { + builder.append("Concatenated Short Message 8bit ref\n"); + byte[] data = e.getData(); + builder.append(" " + data.length + " (0x"); + builder.append(HexDump.toHexString((byte)data.length) + + ") Bytes - Information Element\n"); + builder.append(" " + data[0] + " : SM reference number\n"); + builder.append(" " + data[1] + " : number of messages\n"); + builder.append(" " + data[2] + " : this SM sequence number\n"); + break; + } + + case CONCATENATED_16_BIT_REFERENCE: { + builder.append("Concatenated Short Message 16bit ref\n"); + byte[] data = e.getData(); + builder.append(" " + data.length + " (0x"); + builder.append(HexDump.toHexString((byte)data.length) + + ") Bytes - Information Element\n"); + builder.append(" " + (data[0] & 0xff) * 256 + (data[1] & 0xff) + + " : SM reference number\n"); + builder.append(" " + data[2] + " : number of messages\n"); + builder.append(" " + data[3] + " : this SM sequence number\n"); + break; + } + + case APPLICATION_PORT_ADDRESSING_8_BIT: + { + builder.append("Application port addressing 8bit\n"); + byte[] data = e.getData(); + + builder.append(" " + data.length + " (0x"); + builder.append(HexDump.toHexString( + (byte)data.length) + ") Bytes - Information Element\n"); + + int source = (data[0] & 0xff); + builder.append(" " + source + " : DESTINATION port\n"); + + int dest = (data[1] & 0xff); + builder.append(" " + dest + " : SOURCE port\n"); + break; + } + + case APPLICATION_PORT_ADDRESSING_16_BIT: { + builder.append("Application port addressing 16bit\n"); + byte[] data = e.getData(); + + builder.append(" " + data.length + " (0x"); + builder.append(HexDump.toHexString((byte)data.length) + + ") Bytes - Information Element\n"); + + int source = (data[0] & 0xff) << 8; + source |= (data[1] & 0xff); + builder.append(" " + source + " : DESTINATION port\n"); + + int dest = (data[2] & 0xff) << 8; + dest |= (data[3] & 0xff); + builder.append(" " + dest + " : SOURCE port\n"); + break; + } + + default: { + builder.append("Unknown element\n"); + break; + } + } + } + + return builder.toString(); + } + + private int calcSize() { + int size = 1; // +1 for the UDHL field + for (Element e : m_elements) { + size += e.getData().length; + size += 2; // 1 byte ID, 1 byte length + } + + return size; + } + + /** + * Converts SmsHeader object to a byte array as specified in TS 23.040 9.2.3.24. + * @return Byte array representing the SmsHeader + */ + public byte[] toByteArray() { + if (m_elements.size() == 0) return null; + + if (m_data == null) { + int size = calcSize(); + int cur = 1; + m_data = new byte[size]; + + m_data[0] = (byte) (size-1); // UDHL does not include itself + + for (Element e : m_elements) { + int length = e.getData().length; + m_data[cur++] = (byte) e.getID(); + m_data[cur++] = (byte) length; + System.arraycopy(e.getData(), 0, m_data, cur, length); + cur += length; + } + } + + return m_data; + } + + /** + * A single Element in the SMS User Data Header. + * + * See TS 23.040 9.2.3.24. + * + */ + public static class Element { + private byte[] m_data; + private int m_id; + + public Element(int id, byte[] data) { + m_id = id; + m_data = data; + } + + /** + * Returns the Information Element Identifier for this element. + * + * @return the IE identifier. + */ + public int getID() { + return m_id; + } + + /** + * Returns the data portion of this element. + * + * @return element data. + */ + public byte[] getData() { + return m_data; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java new file mode 100644 index 0000000..7c32451 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.util.Log; +import com.android.internal.telephony.SmsHeader; +import java.util.Arrays; + +import static android.telephony.SmsMessage.ENCODING_7BIT; +import static android.telephony.SmsMessage.ENCODING_16BIT; +import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES; +import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; +import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS; +import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER; +import static android.telephony.SmsMessage.MessageClass; +import static com.android.internal.telephony.SmsAddress.TON_ABBREVIATED; +import static com.android.internal.telephony.SmsAddress.TON_ALPHANUMERIC; +import static com.android.internal.telephony.SmsAddress.TON_INTERNATIONAL; +import static com.android.internal.telephony.SmsAddress.TON_NATIONAL; +import static com.android.internal.telephony.SmsAddress.TON_NETWORK; +import static com.android.internal.telephony.SmsAddress.TON_SUBSCRIBER; +import static com.android.internal.telephony.SmsAddress.TON_UNKNOWN; + +/** + * Base class declaring the specific methods and members for SmsMessage. + * {@hide} + */ +public abstract class SmsMessageBase { + private static final String LOG_TAG = "SMS"; + + /** {@hide} The address of the SMSC. May be null */ + protected String scAddress; + + /** {@hide} The address of the sender */ + protected SmsAddress originatingAddress; + + /** {@hide} The message body as a string. May be null if the message isn't text */ + protected String messageBody; + + /** {@hide} */ + protected String pseudoSubject; + + /** {@hide} Non-null if this is an email gateway message */ + protected String emailFrom; + + /** {@hide} Non-null if this is an email gateway message */ + protected String emailBody; + + /** {@hide} */ + protected boolean isEmail; + + /** {@hide} */ + protected long scTimeMillis; + + /** {@hide} The raw PDU of the message */ + protected byte[] mPdu; + + /** {@hide} The raw bytes for the user data section of the message */ + protected byte[] userData; + + /** {@hide} */ + protected SmsHeader userDataHeader; + + // "Message Waiting Indication Group" + // 23.038 Section 4 + /** {@hide} */ + protected boolean isMwi; + + /** {@hide} */ + protected boolean mwiSense; + + /** {@hide} */ + protected boolean mwiDontStore; + + /** + * Indicates status for messages stored on the ICC. + */ + protected int statusOnIcc = -1; + + /** + * Record index of message in the EF. + */ + protected int indexOnIcc = -1; + + /** TP-Message-Reference - Message Reference of sent message. @hide */ + public int messageRef; + + public static abstract class SubmitPduBase { + public byte[] encodedScAddress; // Null if not applicable. + public byte[] encodedMessage; + + public String toString() { + return "SubmitPdu: encodedScAddress = " + + Arrays.toString(encodedScAddress) + + ", encodedMessage = " + + Arrays.toString(encodedMessage); + } + } + + /** + * Returns the address of the SMS service center that relayed this message + * or null if there is none. + */ + public String getServiceCenterAddress() { + return scAddress; + } + + /** + * Returns the originating address (sender) of this SMS message in String + * form or null if unavailable + */ + public String getOriginatingAddress() { + if (originatingAddress == null) { + return null; + } + + return originatingAddress.getAddressString(); + } + + /** + * 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() { + if (isEmail) { + return emailFrom; + } else { + return getOriginatingAddress(); + } + } + + /** + * 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 messageBody; + } + + /** + * Returns the class of this message. + */ + public abstract MessageClass getMessageClass(); + + /** + * 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() { + if (isEmail) { + return emailBody; + } else { + return getMessageBody(); + } + } + + /** + * Unofficial convention of a subject line enclosed in parens empty string + * if not present + */ + public String getPseudoSubject() { + return pseudoSubject == null ? "" : pseudoSubject; + } + + /** + * Returns the service centre timestamp in currentTimeMillis() format + */ + public long getTimestampMillis() { + return scTimeMillis; + } + + /** + * 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 isEmail; + } + + /** + * @return if isEmail() is true, body of the email sent through the gateway. + * null otherwise + */ + public String getEmailBody() { + return emailBody; + } + + /** + * @return if isEmail() is true, email from address of email sent through + * the gateway. null otherwise + */ + public String getEmailFrom() { + return emailFrom; + } + + /** + * Get protocol identifier. + */ + public abstract int getProtocolIdentifier(); + + /** + * See TS 23.040 9.2.3.9 returns true if this is a "replace short message" + * SMS + */ + public abstract boolean 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 abstract boolean isCphsMwiMessage(); + + /** + * returns true if this message is a CPHS voicemail / message waiting + * indicator (MWI) clear message + */ + public abstract boolean isMWIClearMessage(); + + /** + * returns true if this message is a CPHS voicemail / message waiting + * indicator (MWI) set message + */ + public abstract boolean isMWISetMessage(); + + /** + * returns true if this message is a "Message Waiting Indication Group: + * Discard Message" notification and should not be stored. + */ + public abstract boolean isMwiDontStore(); + + /** + * returns the user data section minus the user data header if one was + * present. + */ + public byte[] getUserData() { + return userData; + } + + /** + * Returns an object representing the user data header + * + * @return an object representing the user data header + * + * {@hide} + */ + public SmsHeader getUserDataHeader() { + return userDataHeader; + } + + /** + * Returns the raw PDU for the message. + * + * @return the raw PDU for the message. + */ + public byte[] getPdu() { + return mPdu; + } + + /** + * 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. + * + * @return 0 indicates the previously sent message was received. + * See TS 23.040, 9.9.2.3.15 for a description of other possible + * values. + */ + public abstract int getStatus(); + + /** + * Return true iff the message is a SMS-STATUS-REPORT message. + */ + public abstract boolean isStatusReportMessage(); + + /** + * Returns true iff the TP-Reply-Path bit is set in + * this message. + */ + public abstract boolean isReplyPathPresent(); + + /** + * 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 statusOnIcc; + } + + /** + * 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 indexOnIcc; + } + + protected void parseMessageBody() { + if (originatingAddress.couldBeEmailGateway()) { + extractEmailAddressFromMessageBody(); + } + } + + /** + * Try to parse this message as an email gateway message -> Neither + * of the standard ways are currently supported: There are two ways + * specified in TS 23.040 Section 3.8 (not supported via this mechanism) - + * SMS message "may have its TP-PID set for internet electronic mail - MT + * SMS format: [] - "Depending on the + * nature of the gateway, the destination/origination address is either + * derived from the content of the SMS TP-OA or TP-DA field, or the + * TP-OA/TP-DA field contains a generic gateway address and the to/from + * address is added at the beginning as shown above." - multiple addreses + * separated by commas, no spaces - subject field delimited by '()' or '##' + * and '#' Section 9.2.3.24.11 + */ + protected void extractEmailAddressFromMessageBody() { + + /* + * a little guesswork here. I haven't found doc for this. + * the format could be either + * + * 1. [x@y][ ]/[subject][ ]/[body] + * -or- + * 2. [x@y][ ]/[body] + */ + int slash = 0, slash2 = 0, atSymbol = 0; + + try { + slash = messageBody.indexOf(" /"); + if (slash == -1) { + return; + } + + atSymbol = messageBody.indexOf('@'); + if (atSymbol == -1 || atSymbol > slash) { + return; + } + + emailFrom = messageBody.substring(0, slash); + + slash2 = messageBody.indexOf(" /", slash + 2); + + if (slash2 == -1) { + pseudoSubject = null; + emailBody = messageBody.substring(slash + 2); + } else { + pseudoSubject = messageBody.substring(slash + 2, slash2); + emailBody = messageBody.substring(slash2 + 2); + } + + isEmail = true; + } catch (Exception ex) { + Log.w(LOG_TAG, + "extractEmailAddressFromMessageBody: exception slash=" + + slash + ", atSymbol=" + atSymbol + ", slash2=" + + slash2, ex); + } + } + +} + diff --git a/telephony/java/com/android/internal/telephony/SmsRawData.aidl b/telephony/java/com/android/internal/telephony/SmsRawData.aidl new file mode 100644 index 0000000..b0b3e4f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsRawData.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.internal.telephony; + +parcelable SmsRawData; diff --git a/telephony/java/com/android/internal/telephony/SmsRawData.java b/telephony/java/com/android/internal/telephony/SmsRawData.java new file mode 100644 index 0000000..891d942 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsRawData.java @@ -0,0 +1,62 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +package com.android.internal.telephony; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A parcelable holder class of byte[] for ISms aidl implementation + */ +public class SmsRawData implements Parcelable { + byte[] data; + + //Static Methods + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator (){ + public SmsRawData createFromParcel(Parcel source) { + int size; + size = source.readInt(); + byte[] data = new byte[size]; + source.readByteArray(data); + return new SmsRawData(data); + } + + public SmsRawData[] newArray(int size) { + return new SmsRawData[size]; + } + }; + + // Constructor + public SmsRawData(byte[] data) { + this.data = data; + } + + public byte[] getBytes() { + return data; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(data.length); + dest.writeByteArray(data); + } +} diff --git a/telephony/java/com/android/internal/telephony/SmsResponse.java b/telephony/java/com/android/internal/telephony/SmsResponse.java new file mode 100644 index 0000000..3c4df56 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsResponse.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +/** + * Object returned by the RIL upon successful completion of sendSMS. + * Contains message reference and ackPdu. + * + */ +public class SmsResponse { + /** Message reference of the just-sent SMS. */ + int messageRef; + /** ackPdu for the just-sent SMS. */ + String ackPdu; + + public SmsResponse(int messageRef, String ackPdu) { + this.messageRef = messageRef; + this.ackPdu = ackPdu; + } +} diff --git a/telephony/java/com/android/internal/telephony/TelephonyEventLog.java b/telephony/java/com/android/internal/telephony/TelephonyEventLog.java new file mode 100644 index 0000000..1ef3c6c --- /dev/null +++ b/telephony/java/com/android/internal/telephony/TelephonyEventLog.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +/* This class contains the details related to Telephony Event Logging */ +public final class TelephonyEventLog { + + /* Event log tags */ + public static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; + public static final int EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED = 50101; + public static final int EVENT_LOG_RADIO_RESET = 50102; + public static final int EVENT_LOG_PDP_RESET = 50103; + public static final int EVENT_LOG_REREGISTER_NETWORK = 50104; + public static final int EVENT_LOG_RADIO_PDP_SETUP_FAIL = 50105; + public static final int EVENT_LOG_CALL_DROP = 50106; + public static final int EVENT_LOG_CGREG_FAIL = 50107; + public static final int EVENT_DATA_STATE_RADIO_OFF = 50108; + public static final int EVENT_LOG_PDP_NETWORK_DROP = 50109; +} diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 9219e7a..c342233 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -35,6 +35,24 @@ public class TelephonyIntents { */ public static final String ACTION_SERVICE_STATE_CHANGED = "android.intent.action.SERVICE_STATE"; + /** + *

      Broadcast Action: The radio technology has changed. The intent will have the following + * extra values:

      + *
        + *
      • phoneName - A string version of the new phone name.
      • + *
      + * + *

      + * You can not receive this through components declared + * in manifests, only by explicitly registering for it with + * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, + * android.content.IntentFilter) Context.registerReceiver()}. + * + *

      + * Requires no permission. + */ + public static final String ACTION_RADIO_TECHNOLOGY_CHANGED + = "android.intent.action.RADIO_TECHNOLOGY"; /** * Broadcast Action: The phone's signal strength has changed. The intent will have the @@ -47,7 +65,7 @@ public class TelephonyIntents { *

      • 0 means "-113 dBm or less".
      • 31 means "-51 dBm or greater".
      * *
    - * + * *

    * You can not receive this through components declared * in manifests, only by exlicitly registering for it with diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index 6aa90f1..396b42d 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -26,8 +26,11 @@ public interface TelephonyProperties { //****** Baseband and Radio Interface version - /** - * Baseband version + //TODO T: property strings do not have to be gsm specific + // change gsm.*operator.*" properties to "operator.*" properties + + /** + * Baseband version * Availability: property is available any time radio is on */ static final String PROPERTY_BASEBAND_VERSION = "gsm.version.baseband"; @@ -47,6 +50,12 @@ public interface TelephonyProperties */ static final String PROPERTY_OPERATOR_NUMERIC = "gsm.operator.numeric"; + /** 'true' if the device is on a manually selected network + * + * Availability: when registered to a network + */ + static final String PROPERTY_OPERATOR_ISMANUAL = "operator.ismanual"; + /** 'true' if the device is considered roaming on this network for GSM * purposes. * Availability: when registered to a network @@ -60,7 +69,7 @@ public interface TelephonyProperties static final String PROPERTY_OPERATOR_ISO_COUNTRY = "gsm.operator.iso-country"; //****** SIM Card - /** + /** * One of "UNKNOWN" "ABSENT" "PIN_REQUIRED" * "PUK_REQUIRED" "NETWORK_LOCKED" or "READY" */ @@ -70,15 +79,15 @@ public interface TelephonyProperties * provider of the SIM. 5 or 6 decimal digits. * Availablity: SIM state must be "READY" */ - static String PROPERTY_SIM_OPERATOR_NUMERIC = "gsm.sim.operator.numeric"; + static String PROPERTY_ICC_OPERATOR_NUMERIC = "gsm.sim.operator.numeric"; - /** PROPERTY_SIM_OPERATOR_ALPHA is also known as the SPN, or Service Provider Name. + /** PROPERTY_ICC_OPERATOR_ALPHA is also known as the SPN, or Service Provider Name. * Availablity: SIM state must be "READY" */ - static String PROPERTY_SIM_OPERATOR_ALPHA = "gsm.sim.operator.alpha"; + static String PROPERTY_ICC_OPERATOR_ALPHA = "gsm.sim.operator.alpha"; /** ISO country code equivalent for the SIM provider's country code*/ - static String PROPERTY_SIM_OPERATOR_ISO_COUNTRY = "gsm.sim.operator.iso-country"; + static String PROPERTY_ICC_OPERATOR_ISO_COUNTRY = "gsm.sim.operator.iso-country"; /** * Indicates the available radio technology. Values include: "unknown", diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java index 2b70162..98899c9 100644 --- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java +++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java @@ -22,7 +22,6 @@ import android.os.PowerManager; import android.provider.Telephony.Sms.Intents; import android.util.Config; import android.util.Log; -import com.android.internal.telephony.gsm.SimUtils; /** @@ -44,6 +43,7 @@ public class WapPushOverSms { private final int WAKE_LOCK_TIMEOUT = 5000; public WapPushOverSms(Phone phone) { + mContext = phone.getContext(); createWakelock(); } @@ -56,7 +56,7 @@ public class WapPushOverSms { */ public void dispatchWapPdu(byte[] pdu) { - if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + SimUtils.bytesToHexString(pdu)); + if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu)); int index = 0; int transactionId = pdu[index++] & 0xFF; @@ -225,3 +225,4 @@ public class WapPushOverSms { mContext.sendBroadcast(intent, permission); } } + diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java new file mode 100644 index 0000000..ef2f548 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -0,0 +1,916 @@ +/* + * 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.cdma; + +import android.content.Context; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Registrant; +import android.os.RegistrantList; +import android.os.SystemProperties; +import android.provider.Settings; +import android.telephony.CellLocation; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; +import android.text.TextUtils; +import android.util.Log; + +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION; + +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.IccPhoneBookInterfaceManager; +import com.android.internal.telephony.IccSmsInterfaceManager; +import com.android.internal.telephony.MmiCode; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneBase; +import com.android.internal.telephony.PhoneNotifier; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.PhoneSubInfo; +import com.android.internal.telephony.RILConstants; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@hide} + */ +public class CDMAPhone extends PhoneBase { + static final String LOG_TAG = "CDMA"; + private static final boolean LOCAL_DEBUG = true; + + //***** Instance Variables + CdmaCallTracker mCT; + CdmaSMSDispatcher mSMS; + CdmaServiceStateTracker mSST; + CdmaDataConnectionTracker mDataConnection; + RuimFileHandler mRuimFileHandler; + RuimRecords mRuimRecords; + RuimCard mRuimCard; + MyHandler h; + ArrayList mPendingMMIs = new ArrayList(); + RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager; + RuimSmsInterfaceManager mRuimSmsInterfaceManager; + PhoneSubInfo mSubInfo; + + protected RegistrantList mNvLoadedRegistrants = new RegistrantList(); + private String mEsn; + private String mMeid; + + Registrant mPostDialHandler; + + + //***** Constructors + public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) { + this(context,ci,notifier, false); + } + + public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, + boolean unitTestMode) { + super(notifier, context, unitTestMode); + + h = new MyHandler(); + mCM = ci; + + mCM.setPhoneType(RILConstants.CDMA_PHONE); + mCT = new CdmaCallTracker(this); + mSST = new CdmaServiceStateTracker (this); + mSMS = new CdmaSMSDispatcher(this); + mIccFileHandler = new RuimFileHandler(this); + mRuimRecords = new RuimRecords(this); + mDataConnection = new CdmaDataConnectionTracker (this); + mRuimCard = new RuimCard(this); + mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this); + mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this); + mSubInfo = new PhoneSubInfo(this); + + mCM.registerForAvailable(h, EVENT_RADIO_AVAILABLE, null); + mRuimRecords.registerForRecordsLoaded(h, EVENT_RUIM_RECORDS_LOADED, null); + mCM.registerForOffOrNotAvailable(h, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); + mCM.registerForOn(h, EVENT_RADIO_ON, null); + mCM.setOnSuppServiceNotification(h, EVENT_SSN, null); + mCM.setOnCallRing(h, EVENT_CALL_RING, null); + mSST.registerForNetworkAttach(h, EVENT_REGISTERED_TO_NETWORK, null); + mCM.registerForNVReady(h, EVENT_NV_READY, null); + + //Change the system setting + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.CDMA_PHONE); + } + + public void dispose() { + synchronized(PhoneProxy.lockForRadioTechnologyChange) { + + //Unregister from all former registered events + mRuimRecords.unregisterForRecordsLoaded(h); //EVENT_RUIM_RECORDS_LOADED + mCM.unregisterForAvailable(h); //EVENT_RADIO_AVAILABLE + mCM.unregisterForOffOrNotAvailable(h); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE + mCM.unregisterForOn(h); //EVENT_RADIO_ON + mCM.unregisterForNVReady(h); //EVENT_NV_READY + mSST.unregisterForNetworkAttach(h); //EVENT_REGISTERED_TO_NETWORK + mCM.unSetOnSuppServiceNotification(h); + mCM.unSetOnCallRing(h); + + //Force all referenced classes to unregister their former registered events + mCT.dispose(); + mDataConnection.dispose(); + mSST.dispose(); + mSMS.dispose(); + mIccFileHandler.dispose(); // instance of RuimFileHandler + mRuimRecords.dispose(); + mRuimCard.dispose(); + mRuimPhoneBookInterfaceManager.dispose(); + mRuimSmsInterfaceManager.dispose(); + mSubInfo.dispose(); + } + } + + public void removeReferences() { + this.mRuimPhoneBookInterfaceManager = null; + this.mRuimSmsInterfaceManager = null; + this.mSMS = null; + this.mSubInfo = null; + this.mRuimRecords = null; + this.mIccFileHandler = null; + this.mRuimCard = null; + this.mDataConnection = null; + this.mCT = null; + this.mSST = null; + } + + protected void finalize() { + if(LOCAL_DEBUG) Log.d(LOG_TAG, "CDMAPhone finalized"); + } + + + //***** Overridden from Phone + public ServiceState getServiceState() { + return mSST.ss; + } + + public Phone.State + getState() { + return mCT.state; + } + + public String + getPhoneName() { + return "CDMA"; + } + + public boolean canTransfer() { + Log.e(LOG_TAG, "canTransfer: not possible in CDMA"); + return false; + } + + public CdmaCall + getRingingCall() { + return mCT.ringingCall; + } + + public void setMute(boolean muted) { + mCT.setMute(muted); + } + + public boolean getMute() { + return mCT.getMute(); + } + + public void conference() throws CallStateException { + // three way calls in CDMA will be handled by feature codes + Log.e(LOG_TAG, "conference: not possible in CDMA"); + } + + public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) { + this.mCM.setPreferredVoicePrivacy(enable, onComplete); + } + + public void getEnhancedVoicePrivacy(Message onComplete) { + this.mCM.getPreferredVoicePrivacy(onComplete); + } + + public void clearDisconnected() { + mCT.clearDisconnected(); + } + + public DataActivityState getDataActivityState() { + DataActivityState ret = DataActivityState.NONE; + + if (mSST.getCurrentCdmaDataConnectionState() != ServiceState.RADIO_TECHNOLOGY_UNKNOWN) { + + switch (mDataConnection.getActivity()) { + case DATAIN: + ret = DataActivityState.DATAIN; + break; + + case DATAOUT: + ret = DataActivityState.DATAOUT; + break; + + case DATAINANDOUT: + ret = DataActivityState.DATAINANDOUT; + break; + } + } + return ret; + } + + /*package*/ void + notifySignalStrength() { + mNotifier.notifySignalStrength(this); + } + + public Connection + dial (String dialString) throws CallStateException { + // Need to make sure dialString gets parsed properly + String newDialString = PhoneNumberUtils.stripSeparators(dialString); + + FeatureCode fc = FeatureCode.newFromDialString(newDialString, this); + if (LOCAL_DEBUG) Log.d(LOG_TAG, + "dialing w/ fc '" + fc + "'..."); + // check for feature code + if (fc == null) { + // check if call in progress + if (!mCT.foregroundCall.isIdle()) { + FeatureCode digits = new FeatureCode(this); + // use dial number as poundString + digits.poundString = newDialString; + mPendingMMIs.add(fc); + mMmiRegistrants.notifyRegistrants(new AsyncResult(null, fc, null)); + digits.processCode(); + return null; + } else { + return mCT.dial(newDialString); + } + } else { + mPendingMMIs.add(fc); + mMmiRegistrants.notifyRegistrants(new AsyncResult(null, fc, null)); + fc.processCode(); + + // FIXME should this return null or something else? + return null; + } + } + + public int getSignalStrengthASU() { + return mSST.rssi == 99 ? -1 : mSST.rssi; + } + + public boolean + getMessageWaitingIndicator() { + Log.e(LOG_TAG, "method getMessageWaitingIndicator is NOT supported in CDMA!"); + return false; + } + + public List + getPendingMmiCodes() { + Log.e(LOG_TAG, "method getPendingMmiCodes is NOT supported in CDMA!"); + return null; + } + + public void registerForSuppServiceNotification( + Handler h, int what, Object obj) { + Log.e(LOG_TAG, "method registerForSuppServiceNotification is NOT supported in CDMA!"); + } + + public CdmaCall getBackgroundCall() { + return mCT.backgroundCall; + } + + public String getGateway(String apnType) { + return mDataConnection.getGateway(); + } + + public boolean handleInCallMmiCommands(String dialString) { + Log.e(LOG_TAG, "method handleInCallMmiCommands is NOT supported in CDMA!"); + return false; + } + + public int enableApnType(String type) { + // This request is mainly used to enable MMS APN + // In CDMA there is no need to enable/disable a different APN for MMS + Log.d(LOG_TAG, "Request to enableApnType("+type+")"); + if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + return Phone.APN_ALREADY_ACTIVE; + } else { + return Phone.APN_REQUEST_FAILED; + } + } + + public int disableApnType(String type) { + // This request is mainly used to disable MMS APN + // In CDMA there is no need to enable/disable a different APN for MMS + Log.d(LOG_TAG, "Request to disableApnType("+type+")"); + if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + return Phone.APN_REQUEST_STARTED; + } else { + return Phone.APN_REQUEST_FAILED; + } + } + + public String getActiveApn() { + Log.d(LOG_TAG, "Request to getActiveApn()"); + return null; + } + + public void + setNetworkSelectionModeAutomatic(Message response) { + Log.e(LOG_TAG, "method setNetworkSelectionModeAutomatic is NOT supported in CDMA!"); + } + + public void unregisterForSuppServiceNotification(Handler h) { + Log.e(LOG_TAG, "method unregisterForSuppServiceNotification is NOT supported in CDMA!"); + } + + public void + acceptCall() throws CallStateException { + mCT.acceptCall(); + } + + public void + rejectCall() throws CallStateException { + mCT.rejectCall(); + } + + public void + switchHoldingAndActive() throws CallStateException { + mCT.switchWaitingOrHoldingAndActive(); + } + + public String getLine1Number() { + return mRuimRecords.getMdnNumber(); + } + + public void getCallWaiting(Message onComplete) { + mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete); + } + + public void + setRadioPower(boolean power) { + mSST.setRadioPower(power); + } + + public String getEsn() { + return mEsn; + } + + public String getMeid() { + return mMeid; + } + + //returns MEID in CDMA + public String getDeviceId() { + return getMeid(); + } + + public String getDeviceSvn() { + Log.d(LOG_TAG, "getDeviceSvn(): return 0"); + return "0"; + } + + public String getSubscriberId() { + Log.e(LOG_TAG, "method getSubscriberId for IMSI is NOT supported in CDMA!"); + return null; + } + + public boolean canConference() { + Log.e(LOG_TAG, "canConference: not possible in CDMA"); + return false; + } + + public String getInterfaceName(String apnType) { + return mDataConnection.getInterfaceName(); + } + + public CellLocation getCellLocation() { + return mSST.cellLoc; + } + + public boolean disableDataConnectivity() { + return mDataConnection.setDataEnabled(false); + } + + public CdmaCall getForegroundCall() { + return mCT.foregroundCall; + } + + public void + selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo network, + Message response) { + Log.e(LOG_TAG, "selectNetworkManually: not possible in CDMA"); + } + + public void setOnPostDialCharacter(Handler h, int what, Object obj) { + Log.e(LOG_TAG, "setOnPostDialCharacter: not possible in CDMA"); + } + + public boolean handlePinMmi(String dialString) { + Log.e(LOG_TAG, "method handlePinMmi is NOT supported in CDMA!"); + return false; + } + + public boolean isDataConnectivityPossible() { + boolean noData = mDataConnection.getDataEnabled() && + getDataConnectionState() == DataState.DISCONNECTED; + return !noData && getIccCard().getState() == IccCard.State.READY && + getServiceState().getState() == ServiceState.STATE_IN_SERVICE && + (mDataConnection.getDataOnRoamingEnabled() || !getServiceState().getRoaming()); + } + + public void setLine1Number(String alphaTag, String number, Message onComplete) { + Log.e(LOG_TAG, "setLine1Number: not possible in CDMA"); + } + + public String[] getDnsServers(String apnType) { + return mDataConnection.getDnsServers(); + } + + public IccCard getIccCard() { + return mRuimCard; + } + + public String getIccSerialNumber() { + return mRuimRecords.iccid; + } + + public void setCallWaiting(boolean enable, Message onComplete) { + Log.e(LOG_TAG, "method setCallWaiting is NOT supported in CDMA!"); + } + + public void updateServiceLocation(Message response) { + mSST.getLacAndCid(response); + } + + public void setDataRoamingEnabled(boolean enable) { + mDataConnection.setDataOnRoamingEnabled(enable); + } + + public String getIpAddress(String apnType) { + return mDataConnection.getIpAddress(); + } + + public void + getNeighboringCids(Message response) { + // WINK:TODO: implement after Cupcake merge + mCM.getNeighboringCids(response); // workaround. + } + + public DataState getDataConnectionState() { + DataState ret = DataState.DISCONNECTED; + + if ((SystemProperties.get("adb.connected", "").length() > 0) + && (SystemProperties.get("android.net.use-adb-networking", "") + .length() > 0)) { + // We're connected to an ADB host and we have USB networking + // turned on. No matter what the radio state is, + // we report data connected + + ret = DataState.CONNECTED; + } else if (mSST.getCurrentCdmaDataConnectionState() + == ServiceState.RADIO_TECHNOLOGY_UNKNOWN) { + // If we're out of service, open TCP sockets may still work + // but no data will flow + ret = DataState.DISCONNECTED; + } else { + switch (mDataConnection.getState()) { + case FAILED: + case IDLE: + ret = DataState.DISCONNECTED; + break; + + case CONNECTED: + case DISCONNECTING: + if ( mCT.state != Phone.State.IDLE + && !mSST.isConcurrentVoiceAndData()) { + ret = DataState.SUSPENDED; + } else { + ret = DataState.CONNECTED; + } + break; + + case INITING: + case CONNECTING: + case SCANNING: + ret = DataState.CONNECTING; + break; + } + } + + return ret; + } + + public void sendUssdResponse(String ussdMessge) { + Log.e(LOG_TAG, "sendUssdResponse: not possible in CDMA"); + } + + public void sendDtmf(char c) { + if (!PhoneNumberUtils.is12Key(c)) { + Log.e(LOG_TAG, + "sendDtmf called with invalid character '" + c + "'"); + } else { + if (mCT.state == Phone.State.OFFHOOK) { + mCM.sendDtmf(c, null); + } + } + } + + public void startDtmf(char c) { + if (!PhoneNumberUtils.is12Key(c)) { + Log.e(LOG_TAG, + "startDtmf called with invalid character '" + c + "'"); + } else { + mCM.startDtmf(c, null); + } + } + + public void stopDtmf() { + mCM.stopDtmf(null); + } + + public void getAvailableNetworks(Message response) { + Log.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA"); + } + + public String[] getActiveApnTypes() { + String[] result; + Log.d(LOG_TAG, "Request to getActiveApn()"); + result = new String[1]; + result[0] = Phone.APN_TYPE_DEFAULT; + return result; + } + + public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { + Log.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA"); + } + + public void enableLocationUpdates() { + mSST.enableLocationUpdates(); + } + + /** + * @deprecated + */ + public void getPdpContextList(Message response) { + getDataCallList(response); + } + + public void getDataCallList(Message response) { + mCM.getDataCallList(response); + } + + public boolean getDataRoamingEnabled() { + return mDataConnection.getDataOnRoamingEnabled(); + } + + public List getCurrentDataConnectionList () { + return mDataConnection.getAllDataConnections(); + } + + public void setVoiceMailNumber(String alphaTag, + String voiceMailNumber, + Message onComplete) { + //mSIMRecords.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete); + //TODO: Where do we have to store this value has to be clarified with QC + } + + public String getVoiceMailNumber() { + //TODO: Where can we get this value has to be clarified with QC + //return mSIMRecords.getVoiceMailNumber(); +// throw new RuntimeException(); + return "12345"; + } + + public String getVoiceMailAlphaTag() { + // TODO: Where can we get this value has to be clarified with QC. + String ret = "";//TODO: Remove = "", if we know where to get this value. + + //ret = mSIMRecords.getVoiceMailAlphaTag(); + + if (ret == null || ret.length() == 0) { + return mContext.getText( + com.android.internal.R.string.defaultVoiceMailAlphaTag).toString(); + } + + return ret; + } + + public boolean enableDataConnectivity() { + return mDataConnection.setDataEnabled(true); + } + + public void disableLocationUpdates() { + mSST.disableLocationUpdates(); + } + + public boolean getIccRecordsLoaded() { + return mRuimRecords.getRecordsLoaded(); + } + + public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) { + Log.e(LOG_TAG, "getCallForwardingOption: not possible in CDMA"); + } + + public void setCallForwardingOption(int commandInterfaceCFAction, + int commandInterfaceCFReason, + String dialingNumber, + int timerSeconds, + Message onComplete) { + Log.e(LOG_TAG, "setCallForwardingOption: not possible in CDMA"); + } + + public void + getOutgoingCallerIdDisplay(Message onComplete) { + Log.e(LOG_TAG, "getOutgoingCallerIdDisplay: not possible in CDMA"); + } + + public boolean + getCallForwardingIndicator() { + Log.e(LOG_TAG, "getCallForwardingIndicator: not possible in CDMA"); + return false; + } + + public void explicitCallTransfer() { + Log.e(LOG_TAG, "explicitCallTransfer: not possible in CDMA"); + } + + public String getLine1AlphaTag() { + Log.e(LOG_TAG, "getLine1AlphaTag: not possible in CDMA"); + return null; + } + + /** + * Notify any interested party of a Phone state change. + */ + /*package*/ void notifyPhoneStateChanged() { + mNotifier.notifyPhoneState(this); + } + + /** + * Notifies registrants (ie, activities in the Phone app) about + * changes to call state (including Phone and Connection changes). + */ + /*package*/ void notifyCallStateChanged() { + /* we'd love it if this was package-scoped*/ + super.notifyCallStateChangedP(); + } + + void notifyServiceStateChanged(ServiceState ss) { + super.notifyServiceStateChangedP(ss); + } + + void notifyLocationChanged() { + mNotifier.notifyCellLocation(this); + } + + /*package*/ void notifyNewRingingConnection(Connection c) { + /* we'd love it if this was package-scoped*/ + super.notifyNewRingingConnectionP(c); + } + + /** + * Notifiy registrants of a RING event. + */ + void notifyIncomingRing() { + AsyncResult ar = new AsyncResult(null, this, null); + mIncomingRingRegistrants.notifyRegistrants(ar); + } + + /*package*/ void notifyDisconnect(Connection cn) { + mDisconnectRegistrants.notifyResult(cn); + } + + void notifyUnknownConnection() { + mUnknownConnectionRegistrants.notifyResult(this); + } + + /*package*/ void + updateMessageWaitingIndicator(boolean mwi) { + // this also calls notifyMessageWaitingIndicator() + mRuimRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0); + } + + + /** + * Removes the given FC from the pending list and notifies + * registrants that it is complete. + * @param fc FC that is done + */ + /*package*/ void onMMIDone(FeatureCode fc) { + /* Only notify complete if it's on the pending list. + * Otherwise, it's already been handled (eg, previously canceled). + * The exception is cancellation of an incoming USSD-REQUEST, which is + * not on the list. + */ + if (mPendingMMIs.remove(fc)) { + mMmiCompleteRegistrants.notifyRegistrants( + new AsyncResult(null, fc, null)); + } + } + + //***** Inner Classes + class MyHandler extends Handler { + MyHandler() { + } + + MyHandler(Looper l) { + super(l); + } + + public void handleMessage(Message msg) { + AsyncResult ar; + Message onComplete; + + switch(msg.what) { + case EVENT_RADIO_AVAILABLE: { + mCM.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE)); + + mCM.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE)); + } + break; + + case EVENT_GET_BASEBAND_VERSION_DONE:{ + ar = (AsyncResult)msg.obj; + + if (ar.exception != null) { + break; + } + + if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result); + setSystemProperty(PROPERTY_BASEBAND_VERSION, (String)ar.result); + } + break; + + case EVENT_GET_DEVICE_IDENTITY_DONE:{ + ar = (AsyncResult)msg.obj; + + if (ar.exception != null) { + break; + } + String[] respId = (String[])ar.result; + mEsn = respId[2]; + mMeid = respId[3]; + } + break; + + case EVENT_RUIM_RECORDS_LOADED:{ + Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received"); + } + break; + + case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:{ + Log.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received"); + } + break; + + case EVENT_RADIO_ON:{ + Log.d(LOG_TAG, "Event EVENT_RADIO_ON Received"); + } + break; + + case EVENT_SSN:{ + Log.d(LOG_TAG, "Event EVENT_SSN Received"); + } + break; + + case EVENT_CALL_RING:{ + Log.d(LOG_TAG, "Event EVENT_CALL_RING Received"); + } + break; + + case EVENT_REGISTERED_TO_NETWORK:{ + Log.d(LOG_TAG, "Event EVENT_REGISTERED_TO_NETWORK Received"); + } + break; + + case EVENT_NV_READY:{ + Log.d(LOG_TAG, "Event EVENT_NV_READY Received"); + //Inform the Service State Tracker + mNvLoadedRegistrants.notifyRegistrants(); + } + break; + + default:{ + throw new RuntimeException("unexpected event not handled"); + } + } + } + } + + /** + * Retrieves the PhoneSubInfo of the CDMAPhone + */ + public PhoneSubInfo getPhoneSubInfo(){ + return mSubInfo; + } + + /** + * Retrieves the IccSmsInterfaceManager of the CDMAPhone + */ + public IccSmsInterfaceManager getIccSmsInterfaceManager(){ + return mRuimSmsInterfaceManager; + } + + /** + * Retrieves the IccPhoneBookInterfaceManager of the CDMAPhone + */ + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ + return mRuimPhoneBookInterfaceManager; + } + + public void registerForNvLoaded(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mNvLoadedRegistrants.add(r); + } + + public void unregisterForNvLoaded(Handler h) { + mNvLoadedRegistrants.remove(h); + } + + // override for allowing access from other classes of this package + /** + * {@inheritDoc} + */ + public final void setSystemProperty(String property, String value) { + super.setSystemProperty(property, value); + } + + /** + * {@inheritDoc} + */ + public Handler getHandler(){ + return h; + } + + /** + * {@inheritDoc} + */ + public IccFileHandler getIccFileHandler(){ + return this.mIccFileHandler; + } + + /** + * Set the TTY mode of the CDMAPhone + */ + public void setTTYModeEnabled(boolean enable, Message onComplete) { + this.mCM.setTTYModeEnabled(enable, onComplete); +} + + /** + * Queries the TTY mode of the CDMAPhone + */ + public void queryTTYModeEnabled(Message onComplete) { + this.mCM.queryTTYModeEnabled(onComplete); + } + + /** + * Activate or deactivate cell broadcast SMS. + * + * @param activate + * 0 = activate, 1 = deactivate + * @param response + * Callback message is empty on completion + */ + public void activateCellBroadcastSms(int activate, Message response) { + mSMS.activateCellBroadcastSms(activate, response); + } + + /** + * Query the current configuration of cdma cell broadcast SMS. + * + * @param response + * Callback message is empty on completion + */ + public void getCellBroadcastSmsConfig(Message response){ + mSMS.getCellBroadcastSmsConfig(response); + } + + /** + * Configure cdma cell broadcast SMS. + * + * @param response + * Callback message is empty on completion + */ + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){ + mSMS.setCellBroadcastConfig(configValuesArray, response); + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java b/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java new file mode 100644 index 0000000..ea557b2 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CallFailCause.java @@ -0,0 +1,50 @@ +/* + * 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.cdma; + +/** + * Call fail causes from TS 24.008 . + * These are mostly the cause codes we need to distinguish for the UI. + * See 22.001 Annex F.4 for mapping of cause codes to local tones. + * + * {@hide} + * + */ +public interface CallFailCause { + static final int NORMAL_CLEARING = 16; + // Busy Tone + static final int USER_BUSY = 17; + +// // No Tone +// static final int NUMBER_CHANGED = 22; +// static final int STATUS_ENQUIRY = 30; + static final int NORMAL_UNSPECIFIED = 31; +// +// // Congestion Tone +// static final int NO_CIRCUIT_AVAIL = 34; +// static final int TEMPORARY_FAILURE = 41; +// static final int SWITCHING_CONGESTION = 42; +// static final int CHANNEL_NOT_AVAIL = 44; +// static final int QOS_NOT_AVAIL = 49; +// static final int BEARER_NOT_AVAIL = 58; +// +// // others +// static final int ACM_LIMIT_EXCEEDED = 68; +// static final int CALL_BARRED = 240; +// static final int FDN_BLOCKED = 241; + static final int ERROR_UNSPECIFIED = 0xffff; +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java new file mode 100644 index 0000000..34514d9 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java @@ -0,0 +1,209 @@ +/* + * 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.cdma; + +import java.util.ArrayList; +import java.util.List; + +import com.android.internal.telephony.Call; +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.DriverCall; +import com.android.internal.telephony.Phone; + +/** + * {@hide} + */ +public final class CdmaCall extends Call { + /*************************** Instance Variables **************************/ + + /*package*/ ArrayList connections = new ArrayList(); + /*package*/ State state = State.IDLE; + /*package*/ CdmaCallTracker owner; + + /***************************** Class Methods *****************************/ + + static State + stateFromDCState (DriverCall.State dcState) { + switch (dcState) { + case ACTIVE: return State.ACTIVE; + case HOLDING: return State.HOLDING; + case DIALING: return State.DIALING; + case ALERTING: return State.ALERTING; + case INCOMING: return State.INCOMING; + case WAITING: return State.WAITING; + default: throw new RuntimeException ("illegal call state:" + dcState); + } + } + + + /****************************** Constructors *****************************/ + /*package*/ + CdmaCall (CdmaCallTracker owner) { + this.owner = owner; + } + + public void dispose() { + } + + /************************** Overridden from Call *************************/ + public List + getConnections() { + // FIXME should return Collections.unmodifiableList(); + return connections; + } + + public State + getState() { + return state; + } + + public Phone + getPhone() { + //TODO, see GsmCall + return null; + } + + public boolean isMultiparty() { + return connections.size() > 1; + } + + /** Please note: if this is the foreground call and a + * background call exists, the background call will be resumed + * because an AT+CHLD=1 will be sent + */ + public void + hangup() throws CallStateException { + owner.hangup(this); + } + + public String + toString() { + return state.toString(); + } + + //***** Called from CdmaConnection + + /*package*/ void + attach(Connection conn, DriverCall dc) { + connections.add(conn); + + state = stateFromDCState (dc.state); + } + + /*package*/ void + attachFake(Connection conn, State state) { + connections.add(conn); + + this.state = state; + } + + /** + * Called by CdmaConnection when it has disconnected + */ + void + connectionDisconnected(CdmaConnection conn) { + if (state != State.DISCONNECTED) { + /* If only disconnected connections remain, we are disconnected*/ + + boolean hasOnlyDisconnectedConnections = true; + + for (int i = 0, s = connections.size() ; i < s; i ++) { + if (connections.get(i).getState() + != State.DISCONNECTED + ) { + hasOnlyDisconnectedConnections = false; + break; + } + } + + if (hasOnlyDisconnectedConnections) { + state = State.DISCONNECTED; + } + } + } + + + /*package*/ void + detach(CdmaConnection conn) { + connections.remove(conn); + + if (connections.size() == 0) { + state = State.IDLE; + } + } + + /*package*/ boolean + update (CdmaConnection conn, DriverCall dc) { + State newState; + boolean changed = false; + + newState = stateFromDCState(dc.state); + + if (newState != state) { + state = newState; + changed = true; + } + + return changed; + } + + /** + * @return true if there's no space in this call for additional + * connections to be added via "conference" + */ + /*package*/ boolean + isFull() { + return connections.size() == CdmaCallTracker.MAX_CONNECTIONS_PER_CALL; + } + + //***** Called from CdmaCallTracker + + + /** + * Called when this Call is being hung up locally (eg, user pressed "end") + * Note that at this point, the hangup request has been dispatched to the radio + * but no response has yet been received so update() has not yet been called + */ + void + onHangupLocal() { + for (int i = 0, s = connections.size() + ; i < s; i++ + ) { + CdmaConnection cn = (CdmaConnection)connections.get(i); + + cn.onHangupLocal(); + } + } + + /** + * Called when it's time to clean up disconnected Connection objects + */ + void clearDisconnected() { + for (int i = connections.size() - 1 ; i >= 0 ; i--) { + CdmaConnection cn = (CdmaConnection)connections.get(i); + + if (cn.getState() == State.DISCONNECTED) { + connections.remove(i); + } + } + + if (connections.size() == 0) { + state = State.IDLE; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java new file mode 100644 index 0000000..a1d362f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -0,0 +1,860 @@ +/* + * 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.cdma; + +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.Registrant; +import android.os.RegistrantList; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; +import android.util.Log; + +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.CallTracker; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.DriverCall; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneProxy; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@hide} + */ +public final class CdmaCallTracker extends CallTracker { + static final String LOG_TAG = "CDMA"; + + private static final boolean REPEAT_POLLING = false; + + private static final boolean DBG_POLL = false; + + //***** Constants + + static final int MAX_CONNECTIONS = 1; // only 1 connection allowed in CDMA + static final int MAX_CONNECTIONS_PER_CALL = 1; // only 1 connection allowed per call + + //***** Instance Variables + + CdmaConnection connections[] = new CdmaConnection[MAX_CONNECTIONS]; + RegistrantList voiceCallEndedRegistrants = new RegistrantList(); + RegistrantList voiceCallStartedRegistrants = new RegistrantList(); + + + // connections dropped durin last poll + ArrayList droppedDuringPoll + = new ArrayList(MAX_CONNECTIONS); + + CdmaCall ringingCall = new CdmaCall(this); + // A call that is ringing or (call) waiting + CdmaCall foregroundCall = new CdmaCall(this); + CdmaCall backgroundCall = new CdmaCall(this); + + CdmaConnection pendingMO; + boolean hangupPendingMO; + + CDMAPhone phone; + + boolean desiredMute = false; // false = mute off + + Phone.State state = Phone.State.IDLE; + + +// boolean needsPoll; + + + + //***** Events + + //***** Constructors + CdmaCallTracker(CDMAPhone phone) { + this.phone = phone; + cm = phone.mCM; + cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); + cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null); + cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); + } + + public void dispose() { + cm.unregisterForCallStateChanged(this); + cm.unregisterForOn(this); + cm.unregisterForNotAvailable(this); + + for(CdmaConnection c : connections) { + try { + if(c != null) hangup(c); + } catch (CallStateException ex) { + Log.e(LOG_TAG, "unexpected error on hangup during dispose"); + } + } + + try { + if(pendingMO != null) hangup(pendingMO); + } catch (CallStateException ex) { + Log.e(LOG_TAG, "unexpected error on hangup during dispose"); + } + + clearDisconnected(); + + } + + protected void finalize() { + Log.d(LOG_TAG, "CdmaCallTracker finalized"); + } + + //***** Instance Methods + + //***** Public Methods + public void registerForVoiceCallStarted(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + voiceCallStartedRegistrants.add(r); + } + public void unregisterForVoiceCallStarted(Handler h) { + voiceCallStartedRegistrants.remove(h); + } + + public void registerForVoiceCallEnded(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + voiceCallEndedRegistrants.add(r); + } + + public void unregisterForVoiceCallEnded(Handler h) { + voiceCallEndedRegistrants.remove(h); + } + + private void + fakeHoldForegroundBeforeDial() { + List connCopy; + + // We need to make a copy here, since fakeHoldBeforeDial() + // modifies the lists, and we don't want to reverse the order + connCopy = (List) foregroundCall.connections.clone(); + + for (int i = 0, s = connCopy.size() ; i < s ; i++) { + CdmaConnection conn = (CdmaConnection)connCopy.get(i); + + conn.fakeHoldBeforeDial(); + } + } + + /** + * clirMode is one of the CLIR_ constants + */ + Connection + dial (String dialString, int clirMode) throws CallStateException { + // note that this triggers call state changed notif + clearDisconnected(); + + if (!canDial()) { + throw new CallStateException("cannot dial in current state"); + } + + // The new call must be assigned to the foreground call. + // That call must be idle, so place anything that's + // there on hold + if (foregroundCall.getState() == CdmaCall.State.ACTIVE) { + // this will probably be done by the radio anyway + // but the dial might fail before this happens + // and we need to make sure the foreground call is clear + // for the newly dialed connection + switchWaitingOrHoldingAndActive(); + + // Fake local state so that + // a) foregroundCall is empty for the newly dialed connection + // b) hasNonHangupStateChanged remains false in the + // next poll, so that we don't clear a failed dialing call + fakeHoldForegroundBeforeDial(); + } + + if (foregroundCall.getState() != CdmaCall.State.IDLE) { + //we should have failed in !canDial() above before we get here + throw new CallStateException("cannot dial in current state"); + } + + pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall); + hangupPendingMO = false; + + if (pendingMO.address == null || pendingMO.address.length() == 0 + || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0 + ) { + // Phone number is invalid + pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER; + + // handlePollCalls() will notice this call not present + // and will mark it as dropped. + pollCallsWhenSafe(); + } else { + // Always unmute when initiating a new call + setMute(false); + + cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + } + + updatePhoneState(); + phone.notifyCallStateChanged(); + + return pendingMO; + } + + + Connection + dial (String dialString) throws CallStateException { + return dial(dialString, CommandsInterface.CLIR_DEFAULT); + } + + void + acceptCall () throws CallStateException { + // FIXME if SWITCH fails, should retry with ANSWER + // in case the active/holding call disappeared and this + // is no longer call waiting + + if (ringingCall.getState() == CdmaCall.State.INCOMING) { + Log.i("phone", "acceptCall: incoming..."); + // Always unmute when answering a new call + setMute(false); + cm.acceptCall(obtainCompleteMessage()); + } else if (ringingCall.getState() == CdmaCall.State.WAITING) { + setMute(false); + switchWaitingOrHoldingAndActive(); + } else { + throw new CallStateException("phone not ringing"); + } + } + + void + rejectCall () throws CallStateException { + // AT+CHLD=0 means "release held or UDUB" + // so if the phone isn't ringing, this could hang up held + if (ringingCall.getState().isRinging()) { + cm.rejectCall(obtainCompleteMessage()); + } else { + throw new CallStateException("phone not ringing"); + } + } + + void + switchWaitingOrHoldingAndActive() throws CallStateException { + // Should we bother with this check? + if (ringingCall.getState() == CdmaCall.State.INCOMING) { + throw new CallStateException("cannot be in the incoming state"); + } else { + cm.sendCDMAFeatureCode("", obtainCompleteMessage(EVENT_SWITCH_RESULT)); + } + } + + void + conference() throws CallStateException { + // three way calls in CDMA will be handled by feature codes + Log.e(LOG_TAG, "conference: not possible in CDMA"); + } + + void + explicitCallTransfer() throws CallStateException { + cm.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); + } + + void + clearDisconnected() { + internalClearDisconnected(); + + updatePhoneState(); + phone.notifyCallStateChanged(); + } + + boolean + canConference() { + return foregroundCall.getState() == CdmaCall.State.ACTIVE + && backgroundCall.getState() == CdmaCall.State.HOLDING + && !backgroundCall.isFull() + && !foregroundCall.isFull(); + } + + boolean + canDial() { + boolean ret; + int serviceState = phone.getServiceState().getState(); + + ret = (serviceState != ServiceState.STATE_POWER_OFF) && + pendingMO == null + && !ringingCall.isRinging() + && (!foregroundCall.getState().isAlive() + || !backgroundCall.getState().isAlive()); + + return ret; + } + + boolean + canTransfer() { + Log.e(LOG_TAG, "canTransfer: not possible in CDMA"); + return false; + } + + //***** Private Instance Methods + + private void + internalClearDisconnected() { + ringingCall.clearDisconnected(); + foregroundCall.clearDisconnected(); + backgroundCall.clearDisconnected(); + } + + /** + * Obtain a message to use for signalling "invoke getCurrentCalls() when + * this operation and all other pending operations are complete + */ + private Message + obtainCompleteMessage() { + return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); + } + + /** + * Obtain a message to use for signalling "invoke getCurrentCalls() when + * this operation and all other pending operations are complete + */ + private Message + obtainCompleteMessage(int what) { + pendingOperations++; + lastRelevantPoll = null; + needsPoll = true; + + if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + + pendingOperations + ", needsPoll=" + needsPoll); + + return obtainMessage(what); + } + + private void + operationComplete() { + pendingOperations--; + + if (DBG_POLL) log("operationComplete: pendingOperations=" + + pendingOperations + ", needsPoll=" + needsPoll); + + if (pendingOperations == 0 && needsPoll) { + lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); + cm.getCurrentCalls(lastRelevantPoll); + } else if (pendingOperations < 0) { + // this should never happen + Log.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0"); + pendingOperations = 0; + } + } + + + + private void + updatePhoneState() { + Phone.State oldState = state; + + if (ringingCall.isRinging()) { + state = Phone.State.RINGING; + } else if (pendingMO != null || + !(foregroundCall.isIdle() && backgroundCall.isIdle())) { + state = Phone.State.OFFHOOK; + } else { + state = Phone.State.IDLE; + } + + if (state == Phone.State.IDLE && oldState != state) { + voiceCallEndedRegistrants.notifyRegistrants( + new AsyncResult(null, null, null)); + } else if (oldState == Phone.State.IDLE && oldState != state) { + voiceCallStartedRegistrants.notifyRegistrants ( + new AsyncResult(null, null, null)); + } + + if (state != oldState) { + phone.notifyPhoneStateChanged(); + } + } + + // ***** Overwritten from CallTracker + + protected void + handlePollCalls(AsyncResult ar) { + List polledCalls; + + if (ar.exception == null) { + polledCalls = (List)ar.result; + } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { + // just a dummy empty ArrayList to cause the loop + // to hang up all the calls + polledCalls = new ArrayList(); + } else { + // Radio probably wasn't ready--try again in a bit + // But don't keep polling if the channel is closed + pollCallsAfterDelay(); + return; + } + + Connection newRinging = null; //or waiting + boolean hasNonHangupStateChanged = false; // Any change besides + // a dropped connection + boolean needsPollDelay = false; + boolean unknownConnectionAppeared = false; + + for (int i = 0, curDC = 0, dcSize = polledCalls.size() + ; i < connections.length; i++) { + CdmaConnection conn = connections[i]; + DriverCall dc = null; + + // polledCall list is sparse + if (curDC < dcSize) { + dc = (DriverCall) polledCalls.get(curDC); + + if (dc.index == i+1) { + curDC++; + } else { + dc = null; + } + } + + if (DBG_POLL) log("poll: conn[i=" + i + "]=" + + conn+", dc=" + dc); + + if (conn == null && dc != null) { + // Connection appeared in CLCC response that we don't know about + if (pendingMO != null && pendingMO.compareTo(dc)) { + + if (DBG_POLL) log("poll: pendingMO=" + pendingMO); + + // It's our pending mobile originating call + connections[i] = pendingMO; + pendingMO.index = i; + pendingMO.update(dc); + pendingMO = null; + + // Someone has already asked to hangup this call + if (hangupPendingMO) { + hangupPendingMO = false; + try { + if (Phone.DEBUG_PHONE) log( + "poll: hangupPendingMO, hangup conn " + i); + hangup(connections[i]); + } catch (CallStateException ex) { + Log.e(LOG_TAG, "unexpected error on hangup"); + } + + // Do not continue processing this poll + // Wait for hangup and repoll + return; + } + } else { + connections[i] = new CdmaConnection(phone.getContext(), dc, this, i); + + // it's a ringing call + if (connections[i].getCall() == ringingCall) { + newRinging = connections[i]; + } else { + // Something strange happened: a call appeared + // which is neither a ringing call or one we created. + // Either we've crashed and re-attached to an existing + // call, or something else (eg, SIM) initiated the call. + + Log.i(LOG_TAG,"Phantom call appeared " + dc); + + // If it's a connected call, set the connect time so that + // it's non-zero. It may not be accurate, but at least + // it won't appear as a Missed Call. + if (dc.state != DriverCall.State.ALERTING + && dc.state != DriverCall.State.DIALING) { + connections[i].connectTime = System.currentTimeMillis(); + } + + unknownConnectionAppeared = true; + } + } + hasNonHangupStateChanged = true; + } else if (conn != null && dc == null) { + // Connection missing in CLCC response that we were + // tracking. + droppedDuringPoll.add(conn); + // Dropped connections are removed from the CallTracker + // list but kept in the Call list + connections[i] = null; + } else if (conn != null && dc != null && !conn.compareTo(dc)) { + // Connection in CLCC response does not match what + // we were tracking. Assume dropped call and new call + + droppedDuringPoll.add(conn); + connections[i] = new CdmaConnection (phone.getContext(), dc, this, i); + + if (connections[i].getCall() == ringingCall) { + newRinging = connections[i]; + } // else something strange happened + hasNonHangupStateChanged = true; + } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ + boolean changed; + changed = conn.update(dc); + hasNonHangupStateChanged = hasNonHangupStateChanged || changed; + } + + if (REPEAT_POLLING) { + if (dc != null) { + // FIXME with RIL, we should not need this anymore + if ((dc.state == DriverCall.State.DIALING + /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) + || (dc.state == DriverCall.State.ALERTING + /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) + || (dc.state == DriverCall.State.INCOMING + /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) + || (dc.state == DriverCall.State.WAITING + /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/) + ) { + // Sometimes there's no unsolicited notification + // for state transitions + needsPollDelay = true; + } + } + } + } + + // This is the first poll after an ATD. + // We expect the pending call to appear in the list + // If it does not, we land here + if (pendingMO != null) { + Log.d(LOG_TAG,"Pending MO dropped before poll fg state:" + + foregroundCall.getState()); + + droppedDuringPoll.add(pendingMO); + pendingMO = null; + hangupPendingMO = false; + } + + if (newRinging != null) { + phone.notifyNewRingingConnection(newRinging); + } + + // clear the "local hangup" and "missed/rejected call" + // cases from the "dropped during poll" list + // These cases need no "last call fail" reason + for (int i = droppedDuringPoll.size() - 1; i >= 0 ; i--) { + CdmaConnection conn = droppedDuringPoll.get(i); + + if (conn.isIncoming() && conn.getConnectTime() == 0) { + // Missed or rejected call + Connection.DisconnectCause cause; + if (conn.cause == Connection.DisconnectCause.LOCAL) { + cause = Connection.DisconnectCause.INCOMING_REJECTED; + } else { + cause = Connection.DisconnectCause.INCOMING_MISSED; + } + + if (Phone.DEBUG_PHONE) { + log("missed/rejected call, conn.cause=" + conn.cause); + log("setting cause to " + cause); + } + droppedDuringPoll.remove(i); + conn.onDisconnect(cause); + } else if (conn.cause == Connection.DisconnectCause.LOCAL) { + // Local hangup + droppedDuringPoll.remove(i); + conn.onDisconnect(Connection.DisconnectCause.LOCAL); + } else if (conn.cause == Connection.DisconnectCause.INVALID_NUMBER) { + droppedDuringPoll.remove(i); + conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER); + } + } + + // Any non-local disconnects: determine cause + if (droppedDuringPoll.size() > 0) { + cm.getLastCallFailCause( + obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); + } + + if (needsPollDelay) { + pollCallsAfterDelay(); + } + + // Cases when we can no longer keep disconnected Connection's + // with their previous calls + // 1) the phone has started to ring + // 2) A Call/Connection object has changed state... + // we may have switched or held or answered (but not hung up) + if (newRinging != null || hasNonHangupStateChanged) { + internalClearDisconnected(); + } + + updatePhoneState(); + + if (unknownConnectionAppeared) { + phone.notifyUnknownConnection(); + } + + if (hasNonHangupStateChanged || newRinging != null) { + phone.notifyCallStateChanged(); + } + + //dumpState(); + } + + //***** Called from CdmaConnection + /*package*/ void + hangup (CdmaConnection conn) throws CallStateException { + if (conn.owner != this) { + throw new CallStateException ("CdmaConnection " + conn + + "does not belong to CdmaCallTracker " + this); + } + + if (conn == pendingMO) { + // We're hanging up an outgoing call that doesn't have it's + // GSM index assigned yet + + if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); + hangupPendingMO = true; + } else { + try { + cm.hangupConnection (conn.getCDMAIndex(), obtainCompleteMessage()); + } catch (CallStateException ex) { + // Ignore "connection not found" + // Call may have hung up already + Log.w(LOG_TAG,"CdmaCallTracker WARN: hangup() on absent connection " + + conn); + } + } + + conn.onHangupLocal(); + } + + /*package*/ void + separate (CdmaConnection conn) throws CallStateException { + if (conn.owner != this) { + throw new CallStateException ("CdmaConnection " + conn + + "does not belong to CdmaCallTracker " + this); + } + try { + cm.separateConnection (conn.getCDMAIndex(), + obtainCompleteMessage(EVENT_SEPARATE_RESULT)); + } catch (CallStateException ex) { + // Ignore "connection not found" + // Call may have hung up already + Log.w(LOG_TAG,"CdmaCallTracker WARN: separate() on absent connection " + + conn); + } + } + + //***** Called from CDMAPhone + + /*package*/ void + setMute(boolean mute) { + desiredMute = mute; + cm.setMute(desiredMute, null); + } + + /*package*/ boolean + getMute() { + return desiredMute; + } + + + //***** Called from CdmaCall + + /* package */ void + hangup (CdmaCall call) throws CallStateException { + if (call.getConnections().size() == 0) { + throw new CallStateException("no connections in call"); + } + + if (call == ringingCall) { + if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); + cm.hangupWaitingOrBackground(obtainCompleteMessage()); + } else if (call == foregroundCall) { + if (call.isDialingOrAlerting()) { + if (Phone.DEBUG_PHONE) { + log("(foregnd) hangup dialing or alerting..."); + } + hangup((CdmaConnection)(call.getConnections().get(0))); + } else { + hangupForegroundResumeBackground(); + } + } else if (call == backgroundCall) { + if (ringingCall.isRinging()) { + if (Phone.DEBUG_PHONE) { + log("hangup all conns in background call"); + } + hangupAllConnections(call); + } else { + hangupWaitingOrBackground(); + } + } else { + throw new RuntimeException ("CdmaCall " + call + + "does not belong to CdmaCallTracker " + this); + } + + call.onHangupLocal(); + } + + /* package */ + void hangupWaitingOrBackground() { + if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); + cm.hangupWaitingOrBackground(obtainCompleteMessage()); + } + + /* package */ + void hangupForegroundResumeBackground() { + if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); + cm.hangupForegroundResumeBackground(obtainCompleteMessage()); + } + + void hangupConnectionByIndex(CdmaCall call, int index) + throws CallStateException { + int count = call.connections.size(); + for (int i = 0; i < count; i++) { + CdmaConnection cn = (CdmaConnection)call.connections.get(i); + if (cn.getCDMAIndex() == index) { + cm.hangupConnection(index, obtainCompleteMessage()); + return; + } + } + + throw new CallStateException("no gsm index found"); + } + + void hangupAllConnections(CdmaCall call) throws CallStateException{ + try { + int count = call.connections.size(); + for (int i = 0; i < count; i++) { + CdmaConnection cn = (CdmaConnection)call.connections.get(i); + cm.hangupConnection(cn.getCDMAIndex(), obtainCompleteMessage()); + } + } catch (CallStateException ex) { + Log.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); + } + } + + /* package */ + CdmaConnection getConnectionByIndex(CdmaCall call, int index) + throws CallStateException { + int count = call.connections.size(); + for (int i = 0; i < count; i++) { + CdmaConnection cn = (CdmaConnection)call.connections.get(i); + if (cn.getCDMAIndex() == index) { + return cn; + } + } + + return null; + } + + private Phone.SuppService getFailedService(int what) { + switch (what) { + case EVENT_SWITCH_RESULT: + return Phone.SuppService.SWITCH; + case EVENT_CONFERENCE_RESULT: + return Phone.SuppService.CONFERENCE; + case EVENT_SEPARATE_RESULT: + return Phone.SuppService.SEPARATE; + case EVENT_ECT_RESULT: + return Phone.SuppService.TRANSFER; + } + return Phone.SuppService.UNKNOWN; + } + + private void handleRadioNotAvailable() { + // handlePollCalls will clear out its + // call list when it gets the CommandException + // error result from this + pollCallsWhenSafe(); + } + + //****** Overridden from Handler + + public void + handleMessage (Message msg) { + AsyncResult ar; + + switch (msg.what) { + case EVENT_POLL_CALLS_RESULT:{ + Log.d(LOG_TAG, "Event EVENT_POLL_CALLS_RESULT Received"); + ar = (AsyncResult)msg.obj; + + if(msg == lastRelevantPoll) { + if(DBG_POLL) log( + "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); + needsPoll = false; + lastRelevantPoll = null; + handlePollCalls((AsyncResult)msg.obj); + } + } + break; + + case EVENT_OPERATION_COMPLETE: + ar = (AsyncResult)msg.obj; + operationComplete(); + break; + + case EVENT_SWITCH_RESULT: + ar = (AsyncResult)msg.obj; + operationComplete(); + break; + + case EVENT_GET_LAST_CALL_FAIL_CAUSE: + int causeCode; + ar = (AsyncResult)msg.obj; + + operationComplete(); + + if (ar.exception != null) { + // An exception occurred...just treat the disconnect + // cause as "normal" + causeCode = CallFailCause.NORMAL_CLEARING; + Log.i(LOG_TAG, + "Exception during getLastCallFailCause, assuming normal disconnect"); + } else { + causeCode = ((int[])ar.result)[0]; + } + + for (int i = 0, s = droppedDuringPoll.size() + ; i < s ; i++ + ) { + CdmaConnection conn = droppedDuringPoll.get(i); + + conn.onRemoteDisconnect(causeCode); + } + + updatePhoneState(); + + phone.notifyCallStateChanged(); + droppedDuringPoll.clear(); + break; + + case EVENT_CALL_STATE_CHANGE: + pollCallsWhenSafe(); + break; + + case EVENT_RADIO_AVAILABLE: + handleRadioAvailable(); + break; + + case EVENT_RADIO_NOT_AVAILABLE: + handleRadioNotAvailable(); + break; + + default:{ + throw new RuntimeException("unexpected event not handled"); + } + } + } + + protected void log(String msg) { + Log.d(LOG_TAG, "[CdmaCallTracker] " + msg); + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java new file mode 100644 index 0000000..cdad4a7 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -0,0 +1,705 @@ +/* + * 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.cdma; + +import com.android.internal.telephony.*; +import android.content.Context; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.Registrant; +import android.os.SystemClock; +import android.util.Config; +import android.util.Log; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; + + +/** + * {@hide} + */ +public class CdmaConnection extends Connection { + static final String LOG_TAG = "CDMA"; + + //***** Instance Variables + + CdmaCallTracker owner; + CdmaCall parent; + + + String address; // MAY BE NULL!!! + String dialString; // outgoing calls only + String postDialString; // outgoing calls only + boolean isIncoming; + boolean disconnected; + + int index; // index in CdmaCallTracker.connections[], -1 if unassigned + + /* + * These time/timespan values are based on System.currentTimeMillis(), + * i.e., "wall clock" time. + */ + long createTime; + long connectTime; + long disconnectTime; + + /* + * These time/timespan values are based on SystemClock.elapsedRealTime(), + * i.e., time since boot. They are appropriate for comparison and + * calculating deltas. + */ + long connectTimeReal; + long duration; + long holdingStartTime; // The time when the Connection last transitioned + // into HOLDING + + int nextPostDialChar; // index into postDialString + + DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; + PostDialState postDialState = PostDialState.NOT_STARTED; + int numberPresentation = Connection.PRESENTATION_ALLOWED; + + Handler h; + + private PowerManager.WakeLock mPartialWakeLock; + + //***** Event Constants + static final int EVENT_DTMF_DONE = 1; + static final int EVENT_PAUSE_DONE = 2; + static final int EVENT_NEXT_POST_DIAL = 3; + static final int EVENT_WAKE_LOCK_TIMEOUT = 4; + + //***** Constants + static final int PAUSE_DELAY_FIRST_MILLIS = 100; + static final int PAUSE_DELAY_MILLIS = 3 * 1000; + static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; + + //***** Inner Classes + + class MyHandler extends Handler { + MyHandler(Looper l) {super(l);} + + public void + handleMessage(Message msg) { + + switch (msg.what) { + case EVENT_NEXT_POST_DIAL: + case EVENT_DTMF_DONE: + case EVENT_PAUSE_DONE: + processNextPostDialChar(); + break; + case EVENT_WAKE_LOCK_TIMEOUT: + releaseWakeLock(); + break; + } + } + } + + //***** Constructors + + /** This is probably an MT call that we first saw in a CLCC response */ + /*package*/ + CdmaConnection (Context context, DriverCall dc, CdmaCallTracker ct, int index) { + createWakeLock(context); + acquireWakeLock(); + + owner = ct; + h = new MyHandler(owner.getLooper()); + + address = dc.number; + + isIncoming = dc.isMT; + createTime = System.currentTimeMillis(); + numberPresentation = dc.numberPresentation; + + this.index = index; + + parent = parentFromDCState (dc.state); + parent.attach(this, dc); + } + + /** This is an MO call, created when dialing */ + /*package*/ + CdmaConnection (Context context, String dialString, CdmaCallTracker ct, CdmaCall parent) { + createWakeLock(context); + acquireWakeLock(); + + owner = ct; + h = new MyHandler(owner.getLooper()); + + this.dialString = dialString; + + this.address = PhoneNumberUtils.extractNetworkPortion(dialString); + this.postDialString = PhoneNumberUtils.extractPostDialPortion(dialString); + + index = -1; + + isIncoming = false; + createTime = System.currentTimeMillis(); + + this.parent = parent; + parent.attachFake(this, CdmaCall.State.DIALING); + } + + public void dispose() { + } + + static boolean + equalsHandlesNulls (Object a, Object b) { + return (a == null) ? (b == null) : a.equals (b); + } + + /*package*/ boolean + compareTo(DriverCall c) { + // On mobile originated (MO) calls, the phone number may have changed + // due to a SIM Toolkit call control modification. + // + // We assume we know when MO calls are created (since we created them) + // and therefore don't need to compare the phone number anyway. + if (! (isIncoming || c.isMT)) return true; + + // ... but we can compare phone numbers on MT calls, and we have + // no control over when they begin, so we might as well + + String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA); + return isIncoming == c.isMT && equalsHandlesNulls(address, cAddress); + } + + public String + toString() { + return (isIncoming ? "incoming" : "outgoing"); + } + + public String getAddress() { + return address; + } + + public CdmaCall getCall() { + return parent; + } + + public long getCreateTime() { + return createTime; + } + + public long getConnectTime() { + return connectTime; + } + + public long getDisconnectTime() { + return disconnectTime; + } + + public long getDurationMillis() { + if (connectTimeReal == 0) { + return 0; + } else if (duration == 0) { + return SystemClock.elapsedRealtime() - connectTimeReal; + } else { + return duration; + } + } + + public long getHoldDurationMillis() { + if (getState() != CdmaCall.State.HOLDING) { + // If not holding, return 0 + return 0; + } else { + return SystemClock.elapsedRealtime() - holdingStartTime; + } + } + + public DisconnectCause getDisconnectCause() { + return cause; + } + + public boolean isIncoming() { + return isIncoming; + } + + public CdmaCall.State getState() { + if (disconnected) { + return CdmaCall.State.DISCONNECTED; + } else { + return super.getState(); + } + } + + public void hangup() throws CallStateException { + if (!disconnected) { + owner.hangup(this); + } else { + throw new CallStateException ("disconnected"); + } + } + + public void separate() throws CallStateException { + if (!disconnected) { + owner.separate(this); + } else { + throw new CallStateException ("disconnected"); + } + } + + public PostDialState getPostDialState() { + return postDialState; + } + + public void proceedAfterWaitChar() { + if (postDialState != PostDialState.WAIT) { + Log.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected " + + "getPostDialState() to be WAIT but was " + postDialState); + return; + } + + setPostDialState(PostDialState.STARTED); + + processNextPostDialChar(); + } + + public void proceedAfterWildChar(String str) { + if (postDialState != PostDialState.WILD) { + Log.w(LOG_TAG, "CdmaConnection.proceedAfterWaitChar(): Expected " + + "getPostDialState() to be WILD but was " + postDialState); + return; + } + + setPostDialState(PostDialState.STARTED); + + if (false) { + boolean playedTone = false; + int len = (str != null ? str.length() : 0); + + for (int i=0; i"); + } + + /** + * Setup a data connection + * + * @param onCompleted + * notify success or not after down + */ + void connect(Message onCompleted) { + if (DBG) log("CdmaDataConnection Connecting..."); + + state = State.ACTIVATING; + onConnectCompleted = onCompleted; + createTime = -1; + lastFailTime = -1; + lastFailCause = FailCause.NONE; + receivedDisconnectReq = false; + phone.mCM.setupDataCall(Integer.toString(RILConstants.CDMA_PHONE), null, null, null, + null, obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE)); + } + + protected void disconnect(Message msg) { + onDisconnect = msg; + if (state == State.ACTIVE) { + if (phone.mCM.getRadioState().isOn()) { + phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); + } + } else if (state == State.ACTIVATING) { + receivedDisconnectReq = true; + } else { + // state == INACTIVE. Nothing to do, so notify immediately. + notifyDisconnect(msg); + } + } + + + public String toString() { + return "State=" + state + " create=" + createTime + " lastFail=" + + lastFailTime + " lastFailCause=" + lastFailCause; + } + + + protected void notifyFail(FailCause cause, Message onCompleted) { + if (onCompleted == null) { + return; + } + state = State.INACTIVE; + lastFailCause = cause; + lastFailTime = System.currentTimeMillis(); + onConnectCompleted = null; + + if(DBG) { + log("Notify data connection fail at " + lastFailTime + + " due to " + lastFailCause); + } + + AsyncResult.forMessage(onCompleted, cause, new Exception()); + onCompleted.sendToTarget(); + } + + protected void notifySuccess(Message onCompleted) { + if (onCompleted == null) { + return; + } + + state = State.ACTIVE; + createTime = System.currentTimeMillis(); + onConnectCompleted = null; + onCompleted.arg1 = cid; + + if (DBG) log("Notify data connection success at " + createTime); + + AsyncResult.forMessage(onCompleted); + onCompleted.sendToTarget(); + } + + protected void notifyDisconnect(Message msg) { + if (DBG) log("Notify data connection disconnect"); + + if (msg != null) { + AsyncResult.forMessage(msg); + msg.sendToTarget(); + } + clearSettings(); + } + + protected void onLinkStateChanged(DataLink.LinkState linkState) { + switch (linkState) { + case LINK_UP: + notifySuccess(onConnectCompleted); + break; + + case LINK_DOWN: + case LINK_EXITED: + phone.mCM.getLastDataCallFailCause(obtainMessage(EVENT_GET_LAST_FAIL_DONE)); + break; + } + } + + protected FailCause getFailCauseFromRequest(int rilCause) { + FailCause cause; + + switch (rilCause) { + case PS_NET_DOWN_REASON_OPERATOR_DETERMINED_BARRING: + cause = FailCause.BARRED; + break; + case PS_NET_DOWN_REASON_AUTH_FAILED: + cause = FailCause.USER_AUTHENTICATION; + break; + case PS_NET_DOWN_REASON_OPTION_NOT_SUPPORTED: + cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED; + break; + case PS_NET_DOWN_REASON_OPTION_UNSUBSCRIBED: + cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED; + break; + default: + cause = FailCause.UNKNOWN; + } + return cause; + } + + protected void log(String s) { + Log.d(LOG_TAG, "[CdmaDataConnection] " + s); + } + + @Override + protected void onDeactivated(AsyncResult ar) { + notifyDisconnect((Message) ar.userObj); + if (DBG) log("CDMA Connection Deactivated"); + } + + @Override + protected void onSetupConnectionCompleted(AsyncResult ar) { + if (ar.exception != null) { + Log.e(LOG_TAG, "CdmaDataConnection Init failed " + ar.exception); + + if (receivedDisconnectReq) { + // Don't bother reporting the error if there's already a + // pending disconnect request, since DataConnectionTracker + // has already updated its state. + notifyDisconnect(onDisconnect); + } else { + if (ar.exception instanceof CommandException + && ((CommandException) (ar.exception)).getCommandError() + == CommandException.Error.RADIO_NOT_AVAILABLE) { + notifyFail(FailCause.RADIO_NOT_AVAILABLE, onConnectCompleted); + } else { + phone.mCM.getLastDataCallFailCause(obtainMessage(EVENT_GET_LAST_FAIL_DONE)); + } + } + } else { + if (receivedDisconnectReq) { + // Don't bother reporting success if there's already a + // pending disconnect request, since DataConnectionTracker + // has already updated its state. + disconnect(onDisconnect); + } else { + String[] response = ((String[]) ar.result); + cid = Integer.parseInt(response[0]); + + if (response.length > 2) { + interfaceName = response[1]; + ipAddress = response[2]; + String prefix = "net." + interfaceName + "."; + gatewayAddress = SystemProperties.get(prefix + "gw"); + dnsServers[0] = SystemProperties.get(prefix + "dns1"); + dnsServers[1] = SystemProperties.get(prefix + "dns2"); + if (DBG) { + log("interface=" + interfaceName + " ipAddress=" + ipAddress + + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0] + + " DNS2=" + dnsServers[1]); + } + + if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1]) + && !((CDMAPhone) phone).isDnsCheckDisabled()) { + // Work around a race condition where QMI does not fill in DNS: + // Deactivate PDP and let DataConnectionTracker retry. + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_BAD_DNS_ADDRESS, + dnsServers[0]); + phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_FORCE_RETRY)); + return; + } + } + + onLinkStateChanged(DataLink.LinkState.LINK_UP); + + if (DBG) log("CdmaDataConnection setup on cid = " + cid); + } + } + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java new file mode 100644 index 0000000..84febf3 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -0,0 +1,929 @@ +/* + * 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.cdma; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.net.NetworkInfo; +import android.net.wifi.WifiManager; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.INetStatService; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.preference.PreferenceManager; +import android.provider.Checkin; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.EventLog; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.DataConnection.FailCause; +import com.android.internal.telephony.DataConnectionTracker; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.TelephonyEventLog; + +import java.util.ArrayList; + +/** + * WINK:TODO: In GsmDataConnectionTracker there are + * EventLog's used quite a few places maybe + * more need to be added in this file? + * + * {@hide} + */ +public final class CdmaDataConnectionTracker extends DataConnectionTracker { + private static final String LOG_TAG = "CDMA"; + private static final boolean DBG = true; + + //***** Instance Variables + + // Indicates baseband will not auto-attach + private boolean noAutoAttach = false; + long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + private boolean mIsScreenOn = true; + + //useful for debugging + boolean failNextConnect = false; + + /** + * dataConnectionList holds all the Data connection + */ + private ArrayList dataConnectionList; + + /** Currently active CdmaDataConnection */ + private CdmaDataConnection mActiveDataConnection; + + /** Defined cdma connection profiles */ + private static int EXTERNAL_NETWORK_DEFAULT_ID = 0; + private static int EXTERNAL_NETWORK_NUM_TYPES = 1; + + private boolean[] dataEnabled = new boolean[EXTERNAL_NETWORK_NUM_TYPES]; + + //***** Constants + + /** + * Pool size of CdmaDataConnection objects. + */ + private static final int DATA_CONNECTION_POOL_SIZE = 1; + + private static final int POLL_CONNECTION_MILLIS = 5 * 1000; + private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.cdma-reconnect"; + private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; + + // Possibly promoate to base class, the only difference is + // the INTENT_RECONNECT_ALARM action is a different string. + // Do consider technology changes if it is promoted. + BroadcastReceiver mIntentReceiver = new BroadcastReceiver () + { + @Override + public void onReceive(Context context, Intent intent) + { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_SCREEN_ON)) { + mIsScreenOn = true; + stopNetStatPoll(); + startNetStatPoll(); + } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + mIsScreenOn = false; + stopNetStatPoll(); + startNetStatPoll(); + } else if (action.equals((INTENT_RECONNECT_ALARM))) { + Log.d(LOG_TAG, "Data reconnect alarm. Previous state was " + state); + + String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); + if (state == State.FAILED) { + cleanUpConnection(false, reason); + } + trySetupData(reason); + } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + final android.net.NetworkInfo networkInfo = (NetworkInfo) + intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); + } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; + + if (!enabled) { + // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION + // quit and wont report disconnected til next enalbing. + mIsWifiConnected = false; + } + } + } + }; + + + //***** Constructor + + CdmaDataConnectionTracker(CDMAPhone p) { + super(p); + + p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); + p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); + p.mRuimRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); + p.mCM.registerForNVReady(this, EVENT_NV_READY, null); + p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null); + p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); + p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); + p.mSST.registerForCdmaDataConnectionAttached(this, EVENT_TRY_SETUP_DATA, null); + p.mSST.registerForCdmaDataConnectionDetached(this, EVENT_CDMA_DATA_DETACHED, null); + p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null); + p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null); + + this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat")); + + IntentFilter filter = new IntentFilter(); + filter.addAction(INTENT_RECONNECT_ALARM); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + + p.getContext().registerReceiver(mIntentReceiver, filter, null, p.h); + + mDataConnectionTracker = this; + + createAllDataConnectionList(); + + // This preference tells us 1) initial condition for "dataEnabled", + // and 2) whether the RIL will setup the baseband to auto-PS attach. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); + + dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID] = + !sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false); + noAutoAttach = !dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID]; + } + + public void dispose() { + //Unregister from all events + phone.mCM.unregisterForAvailable(this); + phone.mCM.unregisterForOffOrNotAvailable(this); + ((CDMAPhone) phone).mRuimRecords.unregisterForRecordsLoaded(this); + phone.mCM.unregisterForNVReady(this); + phone.mCM.unregisterForDataStateChanged(this); + ((CDMAPhone) phone).mCT.unregisterForVoiceCallEnded(this); + ((CDMAPhone) phone).mCT.unregisterForVoiceCallStarted(this); + ((CDMAPhone) phone).mSST.unregisterForCdmaDataConnectionAttached(this); + ((CDMAPhone) phone).mSST.unregisterForCdmaDataConnectionDetached(this); + ((CDMAPhone) phone).mSST.unregisterForRoamingOn(this); + ((CDMAPhone) phone).mSST.unregisterForRoamingOff(this); + + phone.getContext().unregisterReceiver(this.mIntentReceiver); + destroyAllDataConnectionList(); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "CdmaDataConnectionTracker finalized"); + } + + void setState(State s) { + if (DBG) log ("setState: " + s); + if (state != s) { + if (s == State.INITING) { // request Data connection context + Checkin.updateStats(phone.getContext().getContentResolver(), + Checkin.Stats.Tag.PHONE_CDMA_DATA_ATTEMPTED, 1, 0.0); + } + + if (s == State.CONNECTED) { // pppd is up + Checkin.updateStats(phone.getContext().getContentResolver(), + Checkin.Stats.Tag.PHONE_CDMA_DATA_CONNECTED, 1, 0.0); + } + } + + state = s; + } + + public int enableApnType(String type) { + // This request is mainly used to enable MMS APN + // In CDMA there is no need to enable/disable a different APN for MMS + Log.d(LOG_TAG, "Request to enableApnType("+type+")"); + if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + return Phone.APN_ALREADY_ACTIVE; + } else { + return Phone.APN_REQUEST_FAILED; + } + } + + public int disableApnType(String type) { + // This request is mainly used to disable MMS APN + // In CDMA there is no need to enable/disable a different APN for MMS + Log.d(LOG_TAG, "Request to disableApnType("+type+")"); + if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + return Phone.APN_REQUEST_STARTED; + } else { + return Phone.APN_REQUEST_FAILED; + } + } + + private boolean isEnabled(int cdmaDataProfile) { + return dataEnabled[cdmaDataProfile]; + } + + private void setEnabled(int cdmaDataProfile, boolean enable) { + Log.d(LOG_TAG, "setEnabled(" + cdmaDataProfile + ", " + enable + ')'); + dataEnabled[cdmaDataProfile] = enable; + Log.d(LOG_TAG, "dataEnabled[DEFAULT_PROFILE]=" + dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID]); + } + + /** + * Simply tear down data connections due to radio off + * and don't setup again. + */ + public void cleanConnectionBeforeRadioOff() { + cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); + } + + /** + * The data connection is expected to be setup while device + * 1. has ruim card or non-volatile data store + * 2. registered to data connection service + * 3. user doesn't explicitly disable data service + * 4. wifi is not on + * + * @return false while no data connection if all above requirements are met. + */ + public boolean isDataConnectionAsDesired() { + boolean roaming = phone.getServiceState().getRoaming(); + + if ( ((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) || + ((CDMAPhone) phone).mRuimRecords.getRecordsLoaded()) && + ((CDMAPhone) phone).mSST.getCurrentCdmaDataConnectionState() == + ServiceState.STATE_IN_SERVICE && + (!roaming || getDataOnRoamingEnabled()) && + !mIsWifiConnected ) { + return (state == State.CONNECTED); + } + return true; + } + + /** + * Prevent mobile data connections from being established, + * or once again allow mobile data connections. If the state + * toggles, then either tear down or set up data, as + * appropriate to match the new state. + *

    This operation only affects the default connection + * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data + * @return {@code true} if the operation succeeded + */ + public boolean setDataEnabled(boolean enable) { + + boolean isEnabled = isEnabled(EXTERNAL_NETWORK_DEFAULT_ID); + + Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled); + if (!isEnabled && enable) { + setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, true); + return trySetupData(Phone.REASON_DATA_ENABLED); + } else if (!enable) { + setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, false); + return false; + } else // isEnabled && enable + + return true; + } + + /** + * Report the current state of data connectivity (enabled or disabled) + * @return {@code false} if data connectivity has been explicitly disabled, + * {@code true} otherwise. + */ + public boolean getDataEnabled() { + return dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID]; + } + + /** + * Report on whether data connectivity is enabled + * @return {@code false} if data connectivity has been explicitly disabled, + * {@code true} otherwise. + */ + public boolean getAnyDataEnabled() { + for (int i=0; i < EXTERNAL_NETWORK_NUM_TYPES; i++) { + if (isEnabled(i)) return true; + } + return false; + } + + private boolean isDataAllowed() { + boolean roaming = phone.getServiceState().getRoaming(); + return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()); + } + + private boolean trySetupData(String reason) { + if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); + + if (phone.getSimulatedRadioControl() != null) { + // Assume data is connected on the simulator + // FIXME this can be improved + setState(State.CONNECTED); + phone.notifyDataConnection(reason); + + Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); + return true; + } + + int psState = ((CDMAPhone) phone).mSST.getCurrentCdmaDataConnectionState(); + boolean roaming = phone.getServiceState().getRoaming(); + + if ((state == State.IDLE || state == State.SCANNING) + && (psState == ServiceState.RADIO_TECHNOLOGY_1xRTT || + psState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 || + psState == ServiceState.RADIO_TECHNOLOGY_EVDO_A) + && ((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) || + ((CDMAPhone) phone).mRuimRecords.getRecordsLoaded()) + && (((CDMAPhone) phone).mSST.isConcurrentVoiceAndData() || + phone.getState() == Phone.State.IDLE ) + && isDataAllowed()) { + + return setupData(reason); + + } else { + if (DBG) { + log("trySetupData: Not ready for data: " + + " dataState=" + state + + " PS state=" + psState + + " radio state=" + phone.mCM.getRadioState() + + " ruim=" + ((CDMAPhone) phone).mRuimRecords.getRecordsLoaded() + + " concurrentVoice&Data=" + ((CDMAPhone) phone).mSST.isConcurrentVoiceAndData() + + " phoneState=" + phone.getState() + + " dataEnabled=" + getAnyDataEnabled() + + " roaming=" + roaming + + " dataOnRoamingEnable=" + getDataOnRoamingEnabled()); + } + return false; + } + } + + /** + * If tearDown is true, this only tears down a CONNECTED session. Presently, + * there is no mechanism for abandoning an INITING/CONNECTING session, + * but would likely involve cancelling pending async requests or + * setting a flag or new state to ignore them when they came in + * @param tearDown true if the underlying DataConnection should be + * disconnected. + * @param reason reason for the clean up. + */ + private void cleanUpConnection(boolean tearDown, String reason) { + if (DBG) log("Clean up connection due to " + reason); + + // Clear the reconnect alarm, if set. + if (mReconnectIntent != null) { + AlarmManager am = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + am.cancel(mReconnectIntent); + mReconnectIntent = null; + } + + for (DataConnection connBase : dataConnectionList) { + CdmaDataConnection conn = (CdmaDataConnection) connBase; + + if(conn != null) { + if (tearDown) { + Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason); + conn.disconnect(msg); + } else { + conn.clearSettings(); + } + } + } + + stopNetStatPoll(); + + /* + * If we've been asked to tear down the connection, + * set the state to DISCONNECTING. However, there's + * a race that can occur if for some reason we were + * already in the IDLE state. In that case, the call + * to conn.disconnect() above will immediately post + * a message to the handler thread that the disconnect + * is done, and if the handler runs before the code + * below does, the handler will have set the state to + * IDLE before the code below runs. If we didn't check + * for that, future calls to trySetupData would fail, + * and we would never get out of the DISCONNECTING state. + */ + if (!tearDown) { + setState(State.IDLE); + phone.notifyDataConnection(reason); + } else if (state != State.IDLE) { + setState(State.DISCONNECTING); + } + } + + private CdmaDataConnection findFreeDataConnection() { + for (DataConnection connBase : dataConnectionList) { + CdmaDataConnection conn = (CdmaDataConnection) connBase; + if (conn.getState() == DataConnection.State.INACTIVE) { + return conn; + } + } + return null; + } + + private boolean setupData(String reason) { + + CdmaDataConnection conn = findFreeDataConnection(); + + if (conn == null) { + if (DBG) log("setupData: No free CdmaDataConnectionfound!"); + return false; + } + + mActiveDataConnection = conn; + + Message msg = obtainMessage(); + msg.what = EVENT_DATA_SETUP_COMPLETE; + msg.obj = reason; + conn.connect(msg); + + setState(State.INITING); + phone.notifyDataConnection(reason); + return true; + } + + private void notifyDefaultData(String reason) { + setState(State.CONNECTED); + phone.notifyDataConnection(reason); + startNetStatPoll(); + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + } + + private void resetPollStats() { + txPkts = -1; + rxPkts = -1; + sentSinceLastRecv = 0; + netStatPollPeriod = POLL_NETSTAT_MILLIS; + mNoRecvPollCount = 0; + } + + protected void startNetStatPoll() { + if (state == State.CONNECTED && netStatPollEnabled == false) { + Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); + resetPollStats(); + netStatPollEnabled = true; + mPollNetStat.run(); + } + } + + protected void stopNetStatPoll() { + netStatPollEnabled = false; + removeCallbacks(mPollNetStat); + Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); + } + + protected void restartRadio() { + Log.d(LOG_TAG, "************TURN OFF RADIO**************"); + cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); + phone.mCM.setRadioPower(false, null); + /* Note: no need to call setRadioPower(true). Assuming the desired + * radio power state is still ON (as tracked by ServiceStateTracker), + * ServiceStateTracker will call setRadioPower when it receives the + * RADIO_STATE_CHANGED notification for the power off. And if the + * desired power state has changed in the interim, we don't want to + * override it with an unconditional power on. + */ + } + + private Runnable mPollNetStat = new Runnable() { + + public void run() { + long sent, received; + long preTxPkts = -1, preRxPkts = -1; + + Activity newActivity; + + preTxPkts = txPkts; + preRxPkts = rxPkts; + + // check if netstat is still valid to avoid NullPointerException after NTC + if (netstat != null) { + try { + txPkts = netstat.getMobileTxPackets(); + rxPkts = netstat.getMobileRxPackets(); + } catch (RemoteException e) { + txPkts = 0; + rxPkts = 0; + } + + //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); + + if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { + sent = txPkts - preTxPkts; + received = rxPkts - preRxPkts; + + if ( sent > 0 && received > 0 ) { + sentSinceLastRecv = 0; + newActivity = Activity.DATAINANDOUT; + } else if (sent > 0 && received == 0) { + if (phone.getState() == Phone.State.IDLE) { + sentSinceLastRecv += sent; + } else { + sentSinceLastRecv = 0; + } + newActivity = Activity.DATAOUT; + } else if (sent == 0 && received > 0) { + sentSinceLastRecv = 0; + newActivity = Activity.DATAIN; + } else if (sent == 0 && received == 0) { + newActivity = Activity.NONE; + } else { + sentSinceLastRecv = 0; + newActivity = Activity.NONE; + } + + if (activity != newActivity) { + activity = newActivity; + phone.notifyDataActivity(); + } + } + + if (sentSinceLastRecv >= NUMBER_SENT_PACKETS_OF_HANG) { + // we already have NUMBER_SENT_PACKETS sent without ack + if (mNoRecvPollCount < NO_RECV_POLL_LIMIT) { + mNoRecvPollCount++; + // Slow down the poll interval to let things happen + netStatPollPeriod = POLL_NETSTAT_SLOW_MILLIS; + } else { + if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + + " pkts since last received"); + // We've exceeded the threshold. Restart the radio. + netStatPollEnabled = false; + stopNetStatPoll(); + restartRadio(); + } + } else { + mNoRecvPollCount = 0; + netStatPollPeriod = POLL_NETSTAT_MILLIS; + } + + if (netStatPollEnabled) { + mDataConnectionTracker.postDelayed(this, netStatPollPeriod); + } + } + } + }; + + /** + * Returns true if the last fail cause is something that + * seems like it deserves an error notification. + * Transient errors are ignored + */ + private boolean + shouldPostNotification(FailCause cause) { + return (cause != FailCause.UNKNOWN); + } + + /** + * Return true if data connection need to be setup after disconnected due to + * reason. + * + * @param reason the reason why data is disconnected + * @return true if try setup data connection is need for this reason + */ + private boolean retryAfterDisconnected(String reason) { + boolean retry = true; + + if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) || + Phone.REASON_DATA_DISABLED.equals(reason) ) { + retry = false; + } + return retry; + } + + private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { + if (state == State.FAILED) { + Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for " + + (nextReconnectDelay / 1000) + "s"); + + AlarmManager am = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(INTENT_RECONNECT_ALARM); + intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); + mReconnectIntent = PendingIntent.getBroadcast( + phone.getContext(), 0, intent, 0); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + nextReconnectDelay, + mReconnectIntent); + + // double it for next time + nextReconnectDelay *= 2; + if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { + nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; + } + + if (!shouldPostNotification(lastFailCauseCode)) { + Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification " + + "-- likely transient error"); + } else { + notifyNoData(lastFailCauseCode); + } + } + } + + private void notifyNoData(FailCause lastFailCauseCode) { + setState(State.FAILED); + } + + protected void onRecordsLoaded() { + if (state == State.FAILED) { + cleanUpConnection(false, null); + } + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + } + + protected void onNVReady() { + if (state == State.FAILED) { + cleanUpConnection(false, null); + } + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onTrySetupData() { + trySetupData(null); + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onRoamingOff() { + trySetupData(Phone.REASON_ROAMING_OFF); + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onRoamingOn() { + if (getDataOnRoamingEnabled()) { + trySetupData(Phone.REASON_ROAMING_ON); + } else { + if (DBG) log("Tear down data connection on roaming."); + cleanUpConnection(true, Phone.REASON_ROAMING_ON); + } + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onRadioAvailable() { + if (phone.getSimulatedRadioControl() != null) { + // Assume data is connected on the simulator + // FIXME this can be improved + setState(State.CONNECTED); + phone.notifyDataConnection(null); + + Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); + } + + if (state != State.IDLE) { + cleanUpConnection(true, null); + } + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onRadioOffOrNotAvailable() { + // Make sure our reconnect delay starts at the initial value + // next time the radio comes on + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + + if (phone.getSimulatedRadioControl() != null) { + // Assume data is connected on the simulator + // FIXME this can be improved + Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); + } else { + if (DBG) log("Radio is off and clean up all connection"); + cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); + } + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onDataSetupComplete(AsyncResult ar) { + String reason = null; + if (ar.userObj instanceof String) { + reason = (String) ar.userObj; + } + + if (ar.exception == null) { + // everything is setup + notifyDefaultData(reason); + } else { + FailCause cause = (FailCause) (ar.result); + if(DBG) log("Data Connection setup failed " + cause); + + // No try for permanent failure + if (cause.isPermanentFail()) { + notifyNoData(cause); + } + + if (tryAgain(cause)) { + trySetupData(reason); + } else { + startDelayedRetry(cause, reason); + } + } + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onDisconnectDone(AsyncResult ar) { + if(DBG) log("EVENT_DISCONNECT_DONE"); + String reason = null; + if (ar.userObj instanceof String) { + reason = (String) ar.userObj; + } + setState(State.IDLE); + phone.notifyDataConnection(reason); + if (retryAfterDisconnected(reason)) { + trySetupData(reason); + } + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onVoiceCallStarted() { + if (state == State.CONNECTED && !((CDMAPhone) phone).mSST.isConcurrentVoiceAndData()) { + stopNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); + } + } + + /** + * @override com.android.internal.telephony.DataConnectionTracker + */ + protected void onVoiceCallEnded() { + if (state == State.CONNECTED) { + if (!((CDMAPhone) phone).mSST.isConcurrentVoiceAndData()) { + startNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); + } else { + // clean slate after call end. + resetPollStats(); + } + } else { + // in case data setup was attempted when we were on a voice call + trySetupData(Phone.REASON_VOICE_CALL_ENDED); + } + } + + private boolean tryAgain(FailCause cause) { + return (cause != FailCause.RADIO_NOT_AVAILABLE) + && (cause != FailCause.RADIO_OFF) + && (cause != FailCause.RADIO_ERROR_RETRY) + && (cause != FailCause.NO_SIGNAL) + && (cause != FailCause.SIM_LOCKED); + } + + private void createAllDataConnectionList() { + dataConnectionList = new ArrayList(); + CdmaDataConnection dataConn; + + for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) { + dataConn = new CdmaDataConnection(((CDMAPhone) phone)); + dataConnectionList.add(dataConn); + } + } + + private void destroyAllDataConnectionList() { + if(dataConnectionList != null) { + CdmaDataConnection pdp; + dataConnectionList.removeAll(dataConnectionList); + } + } + + private void onCdmaDataAttached() { + if (state == State.CONNECTED) { + startNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED); + } else { + if (state == State.FAILED) { + cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + } + trySetupData(Phone.REASON_CDMA_DATA_DETACHED); + } + } + + protected void onDataStateChanged (AsyncResult ar) { + if (ar.exception != null) { + // This is probably "radio not available" or something + // of that sort. If so, the whole connection is going + // to come down soon anyway + return; + } + + if (state == State.CONNECTED) { + Log.i(LOG_TAG, "Data connection has changed."); + + int cid = -1; + EventLog.List val = new EventLog.List(cid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); + + cleanUpConnection(true, null); + } + Log.i(LOG_TAG, "Data connection has changed."); + } + + String getInterfaceName() { + if (mActiveDataConnection != null) { + return mActiveDataConnection.getInterface(); + } + return null; + } + + protected String getIpAddress() { + if (mActiveDataConnection != null) { + return mActiveDataConnection.getIpAddress(); + } + return null; + } + + String getGateway() { + if (mActiveDataConnection != null) { + return mActiveDataConnection.getGatewayAddress(); + } + return null; + } + + protected String[] getDnsServers() { + if (mActiveDataConnection != null) { + return mActiveDataConnection.getDnsServers(); + } + return null; + } + + public ArrayList getAllDataConnections() { + return dataConnectionList; + } + + private void startDelayedRetry(FailCause cause, String reason) { + notifyNoData(cause); + reconnectAfterFail(cause, reason); + } + + public void handleMessage (Message msg) { + + switch (msg.what) { + case EVENT_RECORDS_LOADED: + onRecordsLoaded(); + break; + + case EVENT_NV_READY: + onNVReady(); + break; + + case EVENT_CDMA_DATA_DETACHED: + onCdmaDataAttached(); + break; + + case EVENT_DATA_STATE_CHANGED: + onDataStateChanged((AsyncResult) msg.obj); + break; + + default: + // handle the message in the super class DataConnectionTracker + super.handleMessage(msg); + break; + } + } + + protected void log(String s) { + Log.d(LOG_TAG, "[CdmaDataConnectionTracker] " + s); + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java new file mode 100644 index 0000000..42c0583 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + + +import android.app.PendingIntent; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.SQLException; +import android.os.AsyncResult; +import android.os.Message; +import android.util.Config; +import android.util.Log; + +import com.android.internal.telephony.SmsHeader; +import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.SMSDispatcher; +//import com.android.internal.telephony.SMSDispatcher.SmsTracker; +import com.android.internal.telephony.cdma.SmsMessage; +import com.android.internal.telephony.cdma.sms.SmsEnvelope; +import com.android.internal.util.HexDump; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.HashMap; + + +final class CdmaSMSDispatcher extends SMSDispatcher { + private static final String TAG = "CDMA"; + + CdmaSMSDispatcher(CDMAPhone phone) { + super(phone); + } + + /** + * Called when a status report is received. This should correspond to + * a previously successful SEND. + * Is a special GSM function, should never be called in CDMA!! + * + * @param ar AsyncResult passed into the message handler. ar.result should + * be a String representing the status report PDU, as ASCII hex. + */ + protected void handleStatusReport(AsyncResult ar) { + Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!"); + } + + /** + * Dispatches an incoming SMS messages. + * + * @param smsb the incoming message from the phone + */ + protected void dispatchMessage(SmsMessageBase smsb) { + + // If sms is null, means there was a parsing error. + // TODO: Should NAK this. + if (smsb == null) { + return; + } + SmsMessage sms = (SmsMessage) smsb; + int teleService; + boolean handled = false; + + // Decode BD stream and set sms variables. + sms.parseSms(); + teleService = sms.getTeleService(); + + // Teleservices W(E)MT and VMN are handled together: + if ((SmsEnvelope.TELESERVICE_WMT == teleService) + ||(SmsEnvelope.TELESERVICE_WEMT == teleService) + ||(SmsEnvelope.TELESERVICE_VMN == teleService)){ + // From here on we need decoded BD. + // Special case the message waiting indicator messages + if (sms.isMWISetMessage()) { + ((CDMAPhone) mPhone).updateMessageWaitingIndicator(true); + + if (sms.isMwiDontStore()) { + handled = true; + } + + if (Config.LOGD) { + Log.d(TAG, + "Received voice mail indicator set SMS shouldStore=" + !handled); + } + } else if (sms.isMWIClearMessage()) { + ((CDMAPhone) mPhone).updateMessageWaitingIndicator(false); + + if (sms.isMwiDontStore()) { + handled = true; + } + + if (Config.LOGD) { + Log.d(TAG, + "Received voice mail indicator clear SMS shouldStore=" + !handled); + } + } + } + + if (null == sms.getUserData()){ + handled = true; + if (Config.LOGD) { + Log.d(TAG, "Received SMS without user data"); + } + } + + if (handled) return; + + if (SmsEnvelope.TELESERVICE_WAP == teleService){ + processCdmaWapPdu(sms.getUserData(), sms.messageRef, sms.getOriginatingAddress()); + return; + } + + // Parse the headers to see if this is partial, or port addressed + int referenceNumber = -1; + int count = 0; + int sequence = 0; + int destPort = -1; + // From here on we need BD distributed to SMS member variables. + + SmsHeader header = sms.getUserDataHeader(); + if (header != null) { + for (SmsHeader.Element element : header.getElements()) { + try { + switch (element.getID()) { + case SmsHeader.CONCATENATED_8_BIT_REFERENCE: { + byte[] data = element.getData(); + + referenceNumber = data[0] & 0xff; + count = data[1] & 0xff; + sequence = data[2] & 0xff; + + // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence + // is zero, or sequence > count, ignore the entire element + if (count == 0 || sequence == 0 || sequence > count) { + referenceNumber = -1; + } + break; + } + + case SmsHeader.CONCATENATED_16_BIT_REFERENCE: { + byte[] data = element.getData(); + + referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff); + count = data[2] & 0xff; + sequence = data[3] & 0xff; + + // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence + // is zero, or sequence > count, ignore the entire element + if (count == 0 || sequence == 0 || sequence > count) { + referenceNumber = -1; + } + break; + } + + case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: { + byte[] data = element.getData(); + + destPort = (data[0] & 0xff) << 8; + destPort |= (data[1] & 0xff); + + break; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + Log.e(TAG, "Bad element in header", e); + return; // TODO: NACK the message or something, don't just discard. + } + } + } + + if (referenceNumber == -1) { + // notify everyone of the message if it isn't partial + byte[][] pdus = new byte[1][]; + pdus[0] = sms.getPdu(); + + if (destPort != -1) {// GSM-style WAP indication + if (destPort == SmsHeader.PORT_WAP_PUSH) { + mWapPush.dispatchWapPdu(sms.getUserData()); + } + // The message was sent to a port, so concoct a URI for it + dispatchPortAddressedPdus(pdus, destPort); + } else { + // It's a normal message, dispatch it + dispatchPdus(pdus); + } + } else { + // Process the message part + processMessagePart(sms, referenceNumber, sequence, count, destPort); + } + } + + /** + * Processes inbound messages that are in the WAP-WDP PDU format. See + * wap-259-wdp-20010614-a section 6.5 for details on the WAP-WDP PDU format. + * WDP segments are gathered until a datagram completes and gets dispatched. + * + * @param pdu The WAP-WDP PDU segment + */ + protected void processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) { + int segment; + int totalSegments; + int index = 0; + int msgType; + + int sourcePort; + int destinationPort; + + msgType = pdu[index++]; + if (msgType != 0){ + Log.w(TAG, "Received a WAP SMS which is not WDP. Discard."); + return; + } + totalSegments = pdu[index++]; // >=1 + segment = pdu[index++]; // >=0 + + //process WDP segment + sourcePort = (0xFF & pdu[index++]) << 8; + sourcePort |= 0xFF & pdu[index++]; + destinationPort = (0xFF & pdu[index++]) << 8; + destinationPort |= 0xFF & pdu[index++]; + + // Lookup all other related parts + StringBuilder where = new StringBuilder("reference_number ="); + where.append(referenceNumber); + where.append(" AND address = ?"); + String[] whereArgs = new String[] {address}; + + Log.i(TAG, "Received WAP PDU. Type = " + msgType + ", originator = " + address + + ", src-port = " + sourcePort + ", dst-port = " + destinationPort + + ", ID = " + referenceNumber + ", segment# = " + segment + "/" + totalSegments); + + byte[][] pdus = null; + Cursor cursor = null; + try { + cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); + int cursorCount = cursor.getCount(); + if (cursorCount != totalSegments - 1) { + // We don't have all the parts yet, store this one away + ContentValues values = new ContentValues(); + values.put("date", new Long(0)); + values.put("pdu", HexDump.toHexString(pdu, index, pdu.length - index)); + values.put("address", address); + values.put("reference_number", referenceNumber); + values.put("count", totalSegments); + values.put("sequence", segment); + values.put("destination_port", destinationPort); + + mResolver.insert(mRawUri, values); + + return; + } + + // All the parts are in place, deal with them + int pduColumn = cursor.getColumnIndex("pdu"); + int sequenceColumn = cursor.getColumnIndex("sequence"); + + pdus = new byte[totalSegments][]; + for (int i = 0; i < cursorCount; i++) { + cursor.moveToNext(); + int cursorSequence = (int)cursor.getLong(sequenceColumn); + pdus[cursorSequence] = HexDump.hexStringToByteArray( + cursor.getString(pduColumn)); + } + // The last part will be added later + + // Remove the parts from the database + mResolver.delete(mRawUri, where.toString(), whereArgs); + } catch (SQLException e) { + Log.e(TAG, "Can't access multipart SMS database", e); + return; // TODO: NACK the message or something, don't just discard. + } finally { + if (cursor != null) cursor.close(); + } + + // Build up the data stream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + for (int i = 0; i < totalSegments-1; i++) { + // reassemble the (WSP-)pdu + output.write(pdus[i], 0, pdus[i].length); + } + + // This one isn't in the DB, so add it + output.write(pdu, index, pdu.length - index); + + byte[] datagram = output.toByteArray(); + // Dispatch the PDU to applications + switch (destinationPort) { + case SmsHeader.PORT_WAP_PUSH: + // Handle the PUSH + mWapPush.dispatchWapPdu(datagram); + break; + + default:{ + pdus = new byte[1][]; + pdus[0] = datagram; + // The messages were sent to any other WAP port + dispatchPortAddressedPdus(pdus, destinationPort); + break; + } + } + } + + /** {@inheritDoc} */ + protected void sendMultipartText(String destinationAddress, String scAddress, + ArrayList parts, ArrayList sentIntents, + ArrayList deliveryIntents) { + + int ref = ++sConcatenatedRef & 0xff; + + for (int i = 0, count = parts.size(); i < count; i++) { + // build SmsHeader data + byte[] data = new byte[5]; + data[0] = (byte) SmsHeader.CONCATENATED_8_BIT_REFERENCE; + data[1] = (byte) 3; // 3 bytes follow + data[2] = (byte) ref; // reference #, unique per message + data[3] = (byte) count; // total part count + data[4] = (byte) (i + 1); // 1-based sequence + + PendingIntent sentIntent = null; + PendingIntent deliveryIntent = null; + + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + if (deliveryIntents != null && deliveryIntents.size() > i) { + deliveryIntent = deliveryIntents.get(i); + } + + SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, + parts.get(i), deliveryIntent != null, data); + + sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); + } + } + + protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, + PendingIntent deliveryIntent) { + super.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent); + } + + /** {@inheritDoc} */ + protected void sendSms(SmsTracker tracker) { + HashMap map = tracker.mData; + + byte smsc[] = (byte[]) map.get("smsc"); + byte pdu[] = (byte[]) map.get("pdu"); + + Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); + + mCm.sendCdmaSms(pdu, reply); + } + + /** {@inheritDoc} */ + protected void sendMultipartSms (SmsTracker tracker) { + Log.d(TAG, "TODO: CdmaSMSDispatcher.sendMultipartSms not implemented"); + } + + /** {@inheritDoc} */ + protected void acknowledgeLastIncomingSms(boolean success, Message response){ + // FIXME unit test leaves cm == null. this should change + if (mCm != null) { + mCm.acknowledgeLastIncomingCdmaSms(success, response); + } + } + + /** {@inheritDoc} */ + protected void activateCellBroadcastSms(int activate, Message response) { + mCm.activateCdmaBroadcastSms(activate, response); + } + + /** {@inheritDoc} */ + protected void getCellBroadcastSmsConfig(Message response) { + mCm.getCdmaBroadcastConfig(response); + } + + /** {@inheritDoc} */ + protected void setCellBroadcastConfig(int[] configValuesArray, Message response) { + mCm.setCdmaBroadcastConfig(configValuesArray, response); + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java new file mode 100644 index 0000000..ca40e76 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -0,0 +1,1016 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +import android.app.AlarmManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.Registrant; +import android.os.RegistrantList; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.provider.Checkin; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.provider.Telephony.Intents; +import android.telephony.ServiceState; +import android.telephony.cdma.CdmaCellLocation; +import android.text.TextUtils; +import android.util.EventLog; +import android.util.Log; +import android.util.TimeUtils; + +import com.android.internal.telephony.CommandException; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.DataConnectionTracker; +// pretty sure importing stuff from GSM is bad: +import com.android.internal.telephony.gsm.MccTable; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.ServiceStateTracker; +import com.android.internal.telephony.TelephonyEventLog; +import com.android.internal.telephony.TelephonyIntents; + +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISMANUAL; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; + +import java.util.Arrays; +import java.util.Date; +import java.util.TimeZone; + +/** + * {@hide} + */ +final class CdmaServiceStateTracker extends ServiceStateTracker { + //***** Instance Variables + CDMAPhone phone; + CdmaCellLocation cellLoc; + CdmaCellLocation newCellLoc; + + int rssi = 99; // signal strength 0-31, 99=unknown + // That's "received signal strength indication" fyi + + /** + * The access technology currently in use: DATA_ACCESS_ + */ + private int networkType = 0; + private int newNetworkType = 0; + + private boolean mCdmaRoaming = false; + + private int cdmaDataConnectionState = -1;//Initial we assume no data connection + private int newCdmaDataConnectionState = -1;//Initial we assume no data connection + private int mRegistrationState = -1; + private RegistrantList cdmaDataConnectionAttachedRegistrants = new RegistrantList(); + private RegistrantList cdmaDataConnectionDetachedRegistrants = new RegistrantList(); + + private boolean mGotCountryCode = false; + + // We can't register for SIM_RECORDS_LOADED immediately because the + // SIMRecords object may not be instantiated yet. + private boolean mNeedToRegForRuimLoaded; + + // Keep track of SPN display rules, so we only broadcast intent if something changes. + private String curSpn = null; + private String curPlmn = null; + private int curSpnRule = 0; + + //***** Constants + static final String LOG_TAG = "CDMA"; + static final String TMUK = "23430"; + + private ContentResolver cr; + + private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + Log.i("CdmaServiceStateTracker", "Auto time state called ..."); + //NOTE in CDMA NITZ is not used + } + }; + + + //***** Constructors + + public CdmaServiceStateTracker(CDMAPhone phone) { + super(); + + this.phone = phone; + cm = phone.mCM; + ss = new ServiceState(); + newSS = new ServiceState(); + cellLoc = new CdmaCellLocation(); + newCellLoc = new CdmaCellLocation(); + + cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); + cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); + + cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null); + cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); + + cm.registerForRUIMReady(this, EVENT_RUIM_READY, null); + + phone.registerForNvLoaded(this, EVENT_NV_LOADED,null); + + // system setting property AIRPLANE_MODE_ON is set in Settings. + int airplaneMode = Settings.System.getInt( + phone.getContext().getContentResolver(), + Settings.System.AIRPLANE_MODE_ON, 0); + mDesiredPowerState = ! (airplaneMode > 0); + + cr = phone.getContext().getContentResolver(); + cr.registerContentObserver( + Settings.System.getUriFor(Settings.System.AUTO_TIME), true, + mAutoTimeObserver); + setRssiDefaultValues(); + + mNeedToRegForRuimLoaded = true; + } + + public void dispose() { + //Unregister for all events + cm.unregisterForAvailable(this); + cm.unregisterForRadioStateChanged(this); + cm.unregisterForNetworkStateChanged(this); + cm.unregisterForRUIMReady(this); + phone.unregisterForNvLoaded(this); + phone.mRuimRecords.unregisterForRecordsLoaded(this); + cm.unSetOnSignalStrengthUpdate(this); + cr.unregisterContentObserver(this.mAutoTimeObserver); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "CdmaServiceStateTracker finalized"); + } + + void registerForNetworkAttach(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + networkAttachedRegistrants.add(r); + + if (ss.getState() == ServiceState.STATE_IN_SERVICE) { + r.notifyRegistrant(); + } + } + + void unregisterForNetworkAttach(Handler h) { + networkAttachedRegistrants.remove(h); + } + + /** + * Registration point for transition into Data attached. + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + /*protected*/ void + registerForCdmaDataConnectionAttached(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + cdmaDataConnectionAttachedRegistrants.add(r); + + if (cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT + || cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 + || cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A) { + r.notifyRegistrant(); + } + } + void unregisterForCdmaDataConnectionAttached(Handler h) { + cdmaDataConnectionAttachedRegistrants.remove(h); + } + + /** + * Registration point for transition into Data detached. + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + /*protected*/ void + registerForCdmaDataConnectionDetached(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + cdmaDataConnectionDetachedRegistrants.add(r); + + if (cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT + && cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0 + && cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A) { + r.notifyRegistrant(); + } + } + void unregisterForCdmaDataConnectionDetached(Handler h) { + cdmaDataConnectionDetachedRegistrants.remove(h); + } + + //***** Called from CDMAPhone + public void + getLacAndCid(Message onComplete) { + cm.getRegistrationState(obtainMessage( + EVENT_GET_LOC_DONE_CDMA, onComplete)); + } + + + //***** Overridden from ServiceStateTracker + public void + handleMessage (Message msg) { + AsyncResult ar; + int[] ints; + String[] strings; + + switch (msg.what) { + case EVENT_RADIO_AVAILABLE: + //this is unnecessary + //setPowerStateToDesired(); + break; + + case EVENT_RUIM_READY: + // The RUIM is now ready i.e if it was locked + // it has been unlocked. At this stage, the radio is already + // powered on. + if (mNeedToRegForRuimLoaded) { + phone.mRuimRecords.registerForRecordsLoaded(this, + EVENT_RUIM_RECORDS_LOADED, null); + mNeedToRegForRuimLoaded = false; + } + // restore the previous network selection. + phone.restoreSavedNetworkSelection(null); + pollState(); + // Signal strength polling stops when radio is off + queueNextSignalStrengthPoll(); + break; + + case EVENT_RADIO_STATE_CHANGED: + // This will do nothing in the radio not + // available case + setPowerStateToDesired(); + pollState(); + break; + + case EVENT_NETWORK_STATE_CHANGED_CDMA: + pollState(); + break; + + case EVENT_GET_SIGNAL_STRENGTH: + // This callback is called when signal strength is polled + // all by itself + + if (!(cm.getRadioState().isOn()) || (cm.getRadioState().isGsm())) { + // Polling will continue when radio turns back on + return; + } + ar = (AsyncResult) msg.obj; + onSignalStrengthResult(ar); + queueNextSignalStrengthPoll(); + + break; + + case EVENT_GET_LOC_DONE_CDMA: + ar = (AsyncResult) msg.obj; + + if (ar.exception == null) { + String states[] = (String[])ar.result; + int baseStationId = -1; + int baseStationLongitude = -1; + int baseStationLatitude = -1; + + int baseStationData[] = { + -1, // baseStationId + -1, // baseStationLatitude + -1 // baseStationLongitude + }; + + if (states.length == 3) { + for(int i = 0; i < states.length; i++) { + try { + if (states[i] != null && states[i].length() > 0) { + baseStationData[i] = Integer.parseInt(states[i], 16); + } + } catch (NumberFormatException ex) { + Log.w(LOG_TAG, "error parsing cell location data: " + ex); + } + } + } + + // only update if cell location really changed + if (cellLoc.getBaseStationId() != baseStationData[0] + || cellLoc.getBaseStationLatitude() != baseStationData[1] + || cellLoc.getBaseStationLongitude() != baseStationData[2]) { + cellLoc.setCellLocationData(baseStationData[0], + baseStationData[1], + baseStationData[2]); + phone.notifyLocationChanged(); + } + } + + if (ar.userObj != null) { + AsyncResult.forMessage(((Message) ar.userObj)).exception + = ar.exception; + ((Message) ar.userObj).sendToTarget(); + } + break; + + case EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA: //Fall through + case EVENT_POLL_STATE_REGISTRATION_CDMA: //Fall through + case EVENT_POLL_STATE_OPERATOR_CDMA: + ar = (AsyncResult) msg.obj; + handlePollStateResult(msg.what, ar); + break; + + case EVENT_POLL_SIGNAL_STRENGTH: + // Just poll signal strength...not part of pollState() + + cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH)); + break; + + case EVENT_SIGNAL_STRENGTH_UPDATE: + // This is a notification from + // CommandsInterface.setOnSignalStrengthUpdate + + ar = (AsyncResult) msg.obj; + + // The radio is telling us about signal strength changes + // we don't have to ask it + dontPollSignalStrength = true; + + onSignalStrengthResult(ar); + break; + + case EVENT_RUIM_RECORDS_LOADED: + case EVENT_NV_LOADED: + updateSpnDisplay(); + break; + + case EVENT_LOCATION_UPDATES_ENABLED: + ar = (AsyncResult) msg.obj; + + if (ar.exception == null) { + getLacAndCid(null); + } + break; + + default: + Log.e(LOG_TAG, "Unhandled message with number: " + msg.what); + break; + } + } + + //***** Private Instance Methods + + protected void setPowerStateToDesired() + { + // If we want it on and it's off, turn it on + if (mDesiredPowerState + && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) { + cm.setRadioPower(true, null); + } else if (!mDesiredPowerState && cm.getRadioState().isOn()) { + DataConnectionTracker dcTracker = phone.mDataConnection; + if (! dcTracker.isDataConnectionAsDesired()) { + + EventLog.List val = new EventLog.List( + dcTracker.getStateInString(), + (dcTracker.getAnyDataEnabled() ? 1 : 0) ); + EventLog.writeEvent(TelephonyEventLog.EVENT_DATA_STATE_RADIO_OFF, val); + } + dcTracker.cleanConnectionBeforeRadioOff(); + + // poll data state up to 15 times, with a 100ms delay + // totaling 1.5 sec. Normal data disable action will finish in 100ms. + for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) { + if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED + && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) { + Log.d(LOG_TAG, "Data shutdown complete."); + break; + } + SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS); + } + // If it's on and available and we want it off.. + cm.setRadioPower(false, null); + } // Otherwise, we're in the desired state + } + + protected void updateSpnDisplay() { + + // TODO Check this method again, because it is not sure at the moment how + // the RUIM handles the SIM stuff + + //int rule = phone.mRuimRecords.getDisplayRule(ss.getOperatorNumeric()); + String spn = null; //phone.mRuimRecords.getServiceProviderName(); + String plmn = ss.getOperatorAlphaLong(); + + if (!TextUtils.equals(this.curPlmn, plmn)) { + //TODO (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN; + boolean showSpn = false; + //TODO (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN; + boolean showPlmn = true; + Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION); + intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn); + intent.putExtra(Intents.EXTRA_SPN, spn); + intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn); + intent.putExtra(Intents.EXTRA_PLMN, plmn); + phone.getContext().sendStickyBroadcast(intent); + } + + //curSpnRule = rule; + //curSpn = spn; + this.curPlmn = plmn; + } + + /** + * Handle the result of one of the pollState()-related requests + */ + + protected void + handlePollStateResult (int what, AsyncResult ar) { + int ints[]; + String states[]; + + // Ignore stale requests from last poll + if (ar.userObj != pollingContext) return; + + if (ar.exception != null) { + CommandException.Error err=null; + + if (ar.exception instanceof CommandException) { + err = ((CommandException)(ar.exception)).getCommandError(); + } + + if (err == CommandException.Error.RADIO_NOT_AVAILABLE) { + // Radio has crashed or turned off + cancelPollState(); + return; + } + + if (!cm.getRadioState().isOn()) { + // Radio has crashed or turned off + cancelPollState(); + return; + } + + if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW && + err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) { + Log.e(LOG_TAG, + "RIL implementation has returned an error where it must succeed", + ar.exception); + } + } else try { + switch (what) { + case EVENT_POLL_STATE_REGISTRATION_CDMA: + //offset, because we don't want the first 3 values in the int-array + final int offset = 3; + states = (String[])ar.result; + + int responseValuesRegistrationState[] = { + -1, //[0] radioTechnology + -1, //[1] baseStationId + -1, //[2] baseStationLatitude + -1, //[3] baseStationLongitude + 0, //[4] cssIndicator; init with 0, because it is treated as a boolean + -1, //[5] systemId + -1 //[6] networkId + }; + + if (states.length > 0) { + try { + this.mRegistrationState = Integer.parseInt(states[0]); + if (states.length == 10) { + for(int i = 0; i < states.length - offset; i++) { + if (states[i + offset] != null + && states[i + offset].length() > 0) { + try { + responseValuesRegistrationState[i] = + Integer.parseInt(states[i + offset], 16); + } + catch(NumberFormatException ex) { + Log.w(LOG_TAG, "Warning! There is an unexpected value" + + "returned as response from " + + "RIL_REQUEST_REGISTRATION_STATE."); + } + } + } + } + else { + Log.e(LOG_TAG, "Too less parameters returned from" + + " RIL_REQUEST_REGISTRATION_STATE"); + } + } catch (NumberFormatException ex) { + Log.w(LOG_TAG, "error parsing RegistrationState: " + ex); + } + } + + mCdmaRoaming = regCodeIsRoaming(this.mRegistrationState); + this.newCdmaDataConnectionState = + radioTechnologyToServiceState(responseValuesRegistrationState[0]); + newSS.setState (regCodeToServiceState(this.mRegistrationState)); + newSS.setRadioTechnology(responseValuesRegistrationState[0]); + newSS.setCssIndicator(responseValuesRegistrationState[4]); + newSS.setSystemAndNetworkId(responseValuesRegistrationState[5], + responseValuesRegistrationState[6]); + + newNetworkType = responseValuesRegistrationState[0]; + + // values are -1 if not available + newCellLoc.setCellLocationData(responseValuesRegistrationState[1], + responseValuesRegistrationState[2], + responseValuesRegistrationState[3]); + break; + + case EVENT_POLL_STATE_OPERATOR_CDMA: + String opNames[] = (String[])ar.result; + + if (opNames != null && opNames.length >= 4) { + newSS.setOperatorName (opNames[0], opNames[1], opNames[2]); + } + break; + + case EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA: + ints = (int[])ar.result; + newSS.setIsManualSelection(ints[0] == 1); + break; + default: + Log.e(LOG_TAG, "RIL response handle in wrong phone!" + + " Expected CDMA RIL request and get GSM RIL request."); + break; + } + + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "Exception while polling service state. " + + "Probably malformed RIL response.", ex); + } + + pollingContext[0]--; + + if (pollingContext[0] == 0) { + newSS.setRoaming(isRoamingBetweenOperators(mCdmaRoaming, newSS)); + + switch(this.mRegistrationState) { + case ServiceState.REGISTRATION_STATE_HOME_NETWORK: + newSS.setExtendedCdmaRoaming(ServiceState.REGISTRATION_STATE_HOME_NETWORK); + break; + case ServiceState.REGISTRATION_STATE_ROAMING: + newSS.setExtendedCdmaRoaming(ServiceState.REGISTRATION_STATE_ROAMING); + break; + case ServiceState.REGISTRATION_STATE_ROAMING_AFFILIATE: + newSS.setExtendedCdmaRoaming(ServiceState.REGISTRATION_STATE_ROAMING_AFFILIATE); + break; + default: + Log.w(LOG_TAG, "Received a different registration state, " + + "but don't changed the extended cdma roaming mode."); + } + pollStateDone(); + } + + } + + private void setRssiDefaultValues() { + rssi = 99; + } + + /** + * A complete "service state" from our perspective is + * composed of a handful of separate requests to the radio. + * + * We make all of these requests at once, but then abandon them + * and start over again if the radio notifies us that some + * event has changed + */ + + private void + pollState() { + pollingContext = new int[1]; + pollingContext[0] = 0; + + switch (cm.getRadioState()) { + case RADIO_UNAVAILABLE: + newSS.setStateOutOfService(); + newCellLoc.setStateInvalid(); + setRssiDefaultValues(); + mGotCountryCode = false; + + pollStateDone(); + break; + + case RADIO_OFF: + newSS.setStateOff(); + newCellLoc.setStateInvalid(); + setRssiDefaultValues(); + mGotCountryCode = false; + + pollStateDone(); + break; + + case SIM_NOT_READY: + case SIM_LOCKED_OR_ABSENT: + case SIM_READY: + log("Radio Technology Change ongoing, setting SS to off"); + newSS.setStateOff(); + newCellLoc.setStateInvalid(); + setRssiDefaultValues(); + mGotCountryCode = false; + + pollStateDone(); + break; + + default: + // Issue all poll-related commands at once + // then count down the responses, which + // are allowed to arrive out-of-order + + pollingContext[0]++; + //RIL_REQUEST_OPERATOR is necessary for CDMA + cm.getOperator( + obtainMessage(EVENT_POLL_STATE_OPERATOR_CDMA, pollingContext)); + + pollingContext[0]++; + //RIL_REQUEST_REGISTRATION_STATE is necessary for CDMA + cm.getRegistrationState( + obtainMessage(EVENT_POLL_STATE_REGISTRATION_CDMA, pollingContext)); + + pollingContext[0]++; + //RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE necessary for CDMA + cm.getNetworkSelectionMode( + obtainMessage(EVENT_POLL_STATE_NETWORK_SELECTION_MODE_CDMA, pollingContext)); + break; + } + } + + private static String networkTypeToString(int type) { + String ret = "unknown"; + + switch (type) { + case DATA_ACCESS_CDMA_IS95A: + case DATA_ACCESS_CDMA_IS95B: + ret = "CDMA"; + break; + case DATA_ACCESS_CDMA_1xRTT: + ret = "CDMA - 1xRTT"; + break; + case DATA_ACCESS_CDMA_EvDo_0: + ret = "CDMA - EvDo rev. 0"; + break; + case DATA_ACCESS_CDMA_EvDo_A: + ret = "CDMA - EvDo rev. A"; + break; + default: + if (DBG) { + Log.e(LOG_TAG, "Wrong network. Can not return a string."); + } + break; + } + + return ret; + } + + private void + pollStateDone() { + if (DBG) { + Log.d(LOG_TAG, "Poll ServiceState done: " + + " oldSS=[" + ss ); + Log.d(LOG_TAG, "Poll ServiceState done: " + + " newSS=[" + newSS); + } + + boolean hasRegistered = + ss.getState() != ServiceState.STATE_IN_SERVICE + && newSS.getState() == ServiceState.STATE_IN_SERVICE; + + boolean hasDeregistered = + ss.getState() == ServiceState.STATE_IN_SERVICE + && newSS.getState() != ServiceState.STATE_IN_SERVICE; + + boolean hasCdmaDataConnectionAttached = + (this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT + && this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0 + && this.cdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A) + && (this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT + || this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 + || this.newCdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A); + + boolean hasCdmaDataConnectionDetached = + (this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_1xRTT + || this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_0 + || this.cdmaDataConnectionState == ServiceState.RADIO_TECHNOLOGY_EVDO_A) + && (this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_1xRTT + && this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_0 + && this.newCdmaDataConnectionState != ServiceState.RADIO_TECHNOLOGY_EVDO_A); + + boolean hasCdmaDataConnectionChanged = + cdmaDataConnectionState != newCdmaDataConnectionState; + + boolean hasNetworkTypeChanged = networkType != newNetworkType; + + boolean hasChanged = !newSS.equals(ss); + + boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); + + boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); + + boolean hasLocationChanged = !newCellLoc.equals(cellLoc); + + ServiceState tss; + tss = ss; + ss = newSS; + newSS = tss; + // clean slate for next time + newSS.setStateOutOfService(); + + CdmaCellLocation tcl = cellLoc; + cellLoc = newCellLoc; + newCellLoc = tcl; + + cdmaDataConnectionState = newCdmaDataConnectionState; + networkType = newNetworkType; + + newSS.setStateOutOfService(); // clean slate for next time + + if (hasNetworkTypeChanged) { + phone.setSystemProperty(PROPERTY_DATA_NETWORK_TYPE, + networkTypeToString(networkType)); + } + + if (hasRegistered) { + Checkin.updateStats(phone.getContext().getContentResolver(), + Checkin.Stats.Tag.PHONE_CDMA_REGISTERED, 1, 0.0); + networkAttachedRegistrants.notifyRegistrants(); + } + + if (hasChanged) { + String operatorNumeric; + + phone.setSystemProperty(PROPERTY_OPERATOR_ALPHA, + ss.getOperatorAlphaLong()); + + operatorNumeric = ss.getOperatorNumeric(); + phone.setSystemProperty(PROPERTY_OPERATOR_NUMERIC, operatorNumeric); + + if (operatorNumeric == null) { + phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, ""); + } else { + String iso = ""; + try{ + iso = MccTable.countryCodeForMcc(Integer.parseInt( + operatorNumeric.substring(0,3))); + } catch ( NumberFormatException ex){ + Log.w(LOG_TAG, "countryCodeForMcc error" + ex); + } catch ( StringIndexOutOfBoundsException ex) { + Log.w(LOG_TAG, "countryCodeForMcc error" + ex); + } + + phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, iso); + mGotCountryCode = true; + } + + phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING, + ss.getRoaming() ? "true" : "false"); + phone.setSystemProperty(PROPERTY_OPERATOR_ISMANUAL, + ss.getIsManualSelection() ? "true" : "false"); + + updateSpnDisplay(); + phone.notifyServiceStateChanged(ss); + } + + if (hasCdmaDataConnectionAttached) { + cdmaDataConnectionAttachedRegistrants.notifyRegistrants(); + } + + if (hasCdmaDataConnectionDetached) { + cdmaDataConnectionDetachedRegistrants.notifyRegistrants(); + } + + if (hasCdmaDataConnectionChanged) { + phone.notifyDataConnection(null); + } + + if (hasRoamingOn) { + roamingOnRegistrants.notifyRegistrants(); + } + + if (hasRoamingOff) { + roamingOffRegistrants.notifyRegistrants(); + } + + if (hasLocationChanged) { + phone.notifyLocationChanged(); + } + } + + /** + * Returns a TimeZone object based only on parameters from the NITZ string. + */ + private TimeZone getNitzTimeZone(int offset, boolean dst, long when) { + TimeZone guess = findTimeZone(offset, dst, when); + if (guess == null) { + // Couldn't find a proper timezone. Perhaps the DST data is wrong. + guess = findTimeZone(offset, !dst, when); + } + if (DBG) { + Log.d(LOG_TAG, "getNitzTimeZone returning " + + (guess == null ? guess : guess.getID())); + } + return guess; + } + + private TimeZone findTimeZone(int offset, boolean dst, long when) { + int rawOffset = offset; + if (dst) { + rawOffset -= 3600000; + } + String[] zones = TimeZone.getAvailableIDs(rawOffset); + TimeZone guess = null; + Date d = new Date(when); + for (String zone : zones) { + TimeZone tz = TimeZone.getTimeZone(zone); + if (tz.getOffset(when) == offset && + tz.inDaylightTime(d) == dst) { + guess = tz; + break; + } + } + + return guess; + } + + private void + queueNextSignalStrengthPoll() { + if (dontPollSignalStrength || (cm.getRadioState().isGsm())) { + // The radio is telling us about signal strength changes + // we don't have to ask it + return; + } + + Message msg; + + msg = obtainMessage(); + msg.what = EVENT_POLL_SIGNAL_STRENGTH; + + // TODO Done't poll signal strength if screen is off + sendMessageDelayed(msg, POLL_PERIOD_MILLIS); + } + + /** + * send signal-strength-changed notification if rssi changed + * Called both for solicited and unsolicited signal stength updates + */ + private void + onSignalStrengthResult(AsyncResult ar) { + int oldRSSI = rssi; + + if (ar.exception != null) { + // 99 = unknown + // most likely radio is resetting/disconnected + rssi = 99; + } else { + int[] ints = (int[])ar.result; + + // bug 658816 seems to be a case where the result is 0-length + if (ints.length != 0) { + rssi = ints[0]; + } else { + Log.e(LOG_TAG, "Bogus signal strength response"); + rssi = 99; + } + } + + if (rssi != oldRSSI) { + try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after + // POLL_PERIOD_MILLIS) during Radio Technology Change) + phone.notifySignalStrength(); + } catch (NullPointerException ex) { + log("onSignalStrengthResult() Phone already destroyed: " + ex + + "Signal Stranth not notified"); + } + } + } + + + private int radioTechnologyToServiceState(int code) { + int retVal = ServiceState.RADIO_TECHNOLOGY_UNKNOWN; + switch(code) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + break; + case 6: + retVal = ServiceState.RADIO_TECHNOLOGY_1xRTT; + break; + case 7: + retVal = ServiceState.RADIO_TECHNOLOGY_EVDO_0; + break; + case 8: + retVal = ServiceState.RADIO_TECHNOLOGY_EVDO_A; + break; + default: + Log.e(LOG_TAG, "Wrong radioTechnology code."); + break; + } + return(retVal); + } + + /** code is registration state 0-5 from TS 27.007 7.2 */ + private int + regCodeToServiceState(int code) { + switch (code) { + case 0: // Not searching and not registered + return ServiceState.STATE_OUT_OF_SERVICE; + case 1: + return ServiceState.STATE_IN_SERVICE; + case 2: // 2 is "searching", fall through + case 3: // 3 is "registration denied", fall through + case 4: // 4 is "unknown" no vaild in current baseband + return ServiceState.STATE_OUT_OF_SERVICE; + case 5:// fall through + case 6: + // Registered and: roaming (5) or roaming affiliates (6) + return ServiceState.STATE_IN_SERVICE; + + default: + Log.w(LOG_TAG, "unexpected service state " + code); + return ServiceState.STATE_OUT_OF_SERVICE; + } + } + + /** + * @return The current CDMA data connection state. ServiceState.RADIO_TECHNOLOGY_1xRTT or + * ServiceState.RADIO_TECHNOLOGY_EVDO is the same as "attached" and + * ServiceState.RADIO_TECHNOLOGY_UNKNOWN is the same as detached. + */ + /*package*/ int getCurrentCdmaDataConnectionState() { + return cdmaDataConnectionState; + } + + /** + * code is registration state 0-5 from TS 27.007 7.2 + * returns true if registered roam, false otherwise + */ + private boolean + regCodeIsRoaming (int code) { + // 5 is "in service -- roam" + return 5 == code; + } + + /** + * Set roaming state when cdmaRoaming is true and ons is different from spn + * @param cdmaRoaming TS 27.007 7.2 CREG registered roaming + * @param s ServiceState hold current ons + * @return true for roaming state set + */ + private + boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) { + String spn = SystemProperties.get(PROPERTY_ICC_OPERATOR_ALPHA, "empty"); + + String onsl = s.getOperatorAlphaLong(); + String onss = s.getOperatorAlphaShort(); + + boolean equalsOnsl = onsl != null && spn.equals(onsl); + boolean equalsOnss = onss != null && spn.equals(onss); + + return cdmaRoaming && !(equalsOnsl || equalsOnss); + } + + private boolean getAutoTime() { + try { + return Settings.System.getInt(phone.getContext().getContentResolver(), + Settings.System.AUTO_TIME) > 0; + } catch (SettingNotFoundException snfe) { + return true; + } + } + + /** + * @return true if phone is camping on a technology + * that could support voice and data simultaneously. + */ + boolean isConcurrentVoiceAndData() { + + // Note: it needs to be confirmed which CDMA network types + // can support voice and data calls concurrently. + // For the time-being, the return value will be false. + return false; + } + + protected void log(String s) { + Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s); + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java b/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java new file mode 100644 index 0000000..65b7336 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java @@ -0,0 +1,312 @@ +/* + * 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.cdma; + +import android.content.Context; +import android.os.*; +import android.util.Log; + +import com.android.internal.telephony.*; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * {@hide} + * + */ +public final class FeatureCode extends Handler implements MmiCode { + static final String LOG_TAG = "CDMA"; + + //***** Constants + + // Call Forwarding + static final String FC_CF_ACTIVATE = "72"; + static final String FC_CF_DEACTIVATE = "73"; + static final String FC_CF_FORWARD_TO_NUMBER = "56"; + + // Call Forwarding Busy Line + static final String FC_CFBL_ACTIVATE = "90"; + static final String FC_CFBL_DEACTIVATE = "91"; + static final String FC_CFBL_FORWARD_TO_NUMBER = "40"; + + // Call Forwarding Don't Answer + static final String FC_CFDA_ACTIVATE = "92"; + static final String FC_CFDA_DEACTIVATE = "93"; + static final String FC_CFDA_FORWARD_TO_NUMBER = "42"; + + // Cancel Call Waiting + static final String FC_CCW = "70"; + + // Usage Sensitive Three-way Calling + static final String FC_3WC = "71"; + + // Do Not Disturb + static final String FC_DND_ACTIVATE = "78"; + static final String FC_DND_DEACTIVATE = "79"; + + // Who Called Me? + static final String FC_WHO = "51"; + + // Rejection of Undesired Annoying Calls + static final String FC_RUAC_ACTIVATE = "60"; + static final String FC_RUAC_DEACTIVATE = "80"; + + // Calling Number Delivery + // Calling Number Identification Presentation + static final String FC_CNIP = "65"; + // Calling Number Identification Restriction + static final String FC_CNIR = "85"; + + + //***** Event Constants + + static final int EVENT_SET_COMPLETE = 1; + static final int EVENT_CDMA_FLASH_COMPLETED = 2; + + + //***** Instance Variables + + CDMAPhone phone; + Context context; + + String action; // '*' in CDMA + String sc; // Service Code + String poundString; // Entire Flash string + String dialingNumber; + + /** Set to true in processCode, not at newFromDialString time */ + + State state = State.PENDING; + CharSequence message; + + //***** Class Variables + + + // Flash Code Pattern + + static Pattern sPatternSuppService = Pattern.compile( + "((\\*)(\\d{2,3})(#?)([^*#]*)?)(.*)"); +/* 1 2 3 4 5 6 + + 1 = Full string up to and including # + 2 = action + 3 = service code + 4 = separator + 5 = dialing number +*/ + + static final int MATCH_GROUP_POUND_STRING = 1; + static final int MATCH_GROUP_ACTION_STRING = 2; + static final int MATCH_GROUP_SERVICE_CODE = 3; + static final int MATCH_GROUP_DIALING_NUMBER = 5; + + + //***** Public Class methods + + /** + * Some dial strings in CDMA are defined to do non-call setup + * things, such as set supplementary service settings (eg, call + * forwarding). These are generally referred to as "Feature Codes". + * We look to see if the dial string contains a valid Feature code (potentially + * with a dial string at the end as well) and return info here. + * + * If the dial string contains no Feature code, we return an instance with + * only "dialingNumber" set + * + * Please see also S.R0006-000-A v2.0 "Wireless Features Description" + */ + + static FeatureCode newFromDialString(String dialString, CDMAPhone phone) { + Matcher m; + FeatureCode ret = null; + + m = sPatternSuppService.matcher(dialString); + + // Is this formatted like a standard supplementary service code? + if (m.matches()) { + ret = new FeatureCode(phone); + ret.poundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING)); + ret.action = makeEmptyNull(m.group(MATCH_GROUP_ACTION_STRING)); + ret.sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); + ret.dialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER)); + } + + return ret; + } + + //***** Private Class methods + + /** make empty strings be null. + * Java regexp returns empty strings for empty groups + */ + private static String makeEmptyNull (String s) { + if (s != null && s.length() == 0) return null; + + return s; + } + + /** returns true of the string is empty or null */ + private static boolean isEmptyOrNull(CharSequence s) { + return s == null || (s.length() == 0); + } + + static boolean isServiceCodeCallForwarding(String sc) { + return sc != null && + (sc.equals(FC_CF_ACTIVATE) + || sc.equals(FC_CF_DEACTIVATE) || sc.equals(FC_CF_FORWARD_TO_NUMBER) + || sc.equals(FC_CFBL_ACTIVATE) || sc.equals(FC_CFBL_DEACTIVATE) + || sc.equals(FC_CFBL_FORWARD_TO_NUMBER) || sc.equals(FC_CFDA_ACTIVATE) + || sc.equals(FC_CFDA_DEACTIVATE) || sc.equals(FC_CFDA_FORWARD_TO_NUMBER)); + } + + static boolean isServiceCodeCallWaiting(String sc) { + return sc != null && sc.equals(FC_CCW); + } + + static boolean isServiceCodeThreeWayCalling(String sc) { + return sc != null && sc.equals(FC_3WC); + } + + static boolean isServiceCodeAnnoyingCalls(String sc) { + return sc != null && + (sc.equals(FC_RUAC_ACTIVATE) + || sc.equals(FC_RUAC_DEACTIVATE)); + } + + static boolean isServiceCodeCallingNumberDelivery(String sc) { + return sc != null && + (sc.equals(FC_CNIP) + || sc.equals(FC_CNIR)); + } + + static boolean isServiceCodeDoNotDisturb(String sc) { + return sc != null && + (sc.equals(FC_DND_ACTIVATE) + || sc.equals(FC_DND_DEACTIVATE)); + } + + + //***** Constructor + + FeatureCode (CDMAPhone phone) { + super(phone.getHandler().getLooper()); + this.phone = phone; + this.context = phone.getContext(); + } + + + //***** MmiCode implementation + + public State getState() { + return state; + } + + public CharSequence getMessage() { + return message; + } + + // inherited javadoc suffices + public void cancel() { + //Not used here + } + + public boolean isCancelable() { + Log.e(LOG_TAG, "isCancelable: not used in CDMA"); + return false; + } + + public boolean isUssdRequest() { + Log.e(LOG_TAG, "isUssdRequest: not used in CDMA"); + return false; + } + + /** Process a Flash Code...anything that isn't a dialing number */ + void processCode () { + Log.d(LOG_TAG, "send feature code..."); + phone.mCM.sendCDMAFeatureCode(this.poundString, + obtainMessage(EVENT_CDMA_FLASH_COMPLETED)); + } + + /** Called from CDMAPhone.handleMessage; not a Handler subclass */ + public void handleMessage (Message msg) { + AsyncResult ar; + + switch (msg.what) { + case EVENT_SET_COMPLETE: + ar = (AsyncResult) (msg.obj); + onSetComplete(ar); + break; + case EVENT_CDMA_FLASH_COMPLETED: + ar = (AsyncResult) (msg.obj); + + if (ar.exception != null) { + state = State.FAILED; + message = context.getText(com.android.internal.R.string.mmiError); + } else { + state = State.COMPLETE; + message = context.getText(com.android.internal.R.string.mmiComplete); + } + phone.onMMIDone(this); + break; + } + } + + + //***** Private instance methods + + private CharSequence getScString() { + if (sc != null) { + if (isServiceCodeCallForwarding(sc)) { + return context.getText(com.android.internal.R.string.CfMmi); + } else if (isServiceCodeCallWaiting(sc)) { + return context.getText(com.android.internal.R.string.CwMmi); + } else if (sc.equals(FC_CNIP)) { + return context.getText(com.android.internal.R.string.CnipMmi); + } else if (sc.equals(FC_CNIR)) { + return context.getText(com.android.internal.R.string.CnirMmi); + } else if (isServiceCodeThreeWayCalling(sc)) { + return context.getText(com.android.internal.R.string.ThreeWCMmi); + } else if (isServiceCodeAnnoyingCalls(sc)) { + return context.getText(com.android.internal.R.string.RuacMmi); + } else if (isServiceCodeCallingNumberDelivery(sc)) { + return context.getText(com.android.internal.R.string.CndMmi); + } else if (isServiceCodeDoNotDisturb(sc)) { + return context.getText(com.android.internal.R.string.DndMmi); + } + } + + return ""; + } + + private void onSetComplete(AsyncResult ar){ + StringBuilder sb = new StringBuilder(getScString()); + sb.append("\n"); + + if (ar.exception != null) { + state = State.FAILED; + sb.append(context.getText(com.android.internal.R.string.mmiError)); + } else { + state = State.FAILED; + sb.append(context.getText(com.android.internal.R.string.mmiError)); + } + + message = sb; + phone.onMMIDone(this); + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimCard.java b/telephony/java/com/android/internal/telephony/cdma/RuimCard.java new file mode 100644 index 0000000..9d9f479 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/RuimCard.java @@ -0,0 +1,522 @@ +/* + * 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.cdma; + +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.Registrant; +import android.os.RegistrantList; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.TelephonyProperties; + +import android.app.ActivityManagerNative; +import android.content.Intent; +import android.content.res.Configuration; + +import static android.Manifest.permission.READ_PHONE_STATE; + +/** + * Note: this class shares common code with SimCard, consider a base class to minimize code + * duplication. + * {@hide} + */ +public final class RuimCard extends Handler implements IccCard { + static final String LOG_TAG="CDMA"; + + //***** Instance Variables + private static final boolean DBG = true; + + private CDMAPhone phone; + + private CommandsInterface.IccStatus status = null; + private boolean mDesiredPinLocked; + private boolean mDesiredFdnEnabled; + private boolean mRuimPinLocked = true; // default to locked + private boolean mRuimFdnEnabled = false; // Default to disabled. + // Will be updated when RUIM_READY. +// //***** Constants + +// // FIXME I hope this doesn't conflict with the Dialer's notifications +// Nobody is using this at the moment +// static final int NOTIFICATION_ID_ICC_STATUS = 33456; + + //***** Event Constants + + static final int EVENT_RUIM_LOCKED_OR_ABSENT = 1; + static final int EVENT_GET_RUIM_STATUS_DONE = 2; + static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3; + static final int EVENT_PINPUK_DONE = 4; + static final int EVENT_REPOLL_STATUS_DONE = 5; + static final int EVENT_RUIM_READY = 6; + static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7; + static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8; + static final int EVENT_CHANGE_RUIM_PASSWORD_DONE = 9; + static final int EVENT_QUERY_FACILITY_FDN_DONE = 10; + static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11; + + + //***** Constructor + + RuimCard(CDMAPhone phone) { + this.phone = phone; + + phone.mCM.registerForRUIMLockedOrAbsent( + this, EVENT_RUIM_LOCKED_OR_ABSENT, null); + + phone.mCM.registerForOffOrNotAvailable( + this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); + + phone.mCM.registerForRUIMReady( + this, EVENT_RUIM_READY, null); + + updateStateProperty(); + } + + //***** RuimCard implementation + + public State + getState() { + if (status == null) { + switch(phone.mCM.getRadioState()) { + /* This switch block must not return anything in + * State.isLocked() or State.ABSENT. + * If it does, handleSimStatus() may break + */ + case RADIO_OFF: + case RADIO_UNAVAILABLE: + case RUIM_NOT_READY: + return State.UNKNOWN; + case RUIM_LOCKED_OR_ABSENT: + //this should be transient-only + return State.UNKNOWN; + case RUIM_READY: + return State.READY; + case NV_READY: + case NV_NOT_READY: + return State.ABSENT; + } + } else { + switch (status) { + case ICC_ABSENT: return State.ABSENT; + case ICC_NOT_READY: return State.UNKNOWN; + case ICC_READY: return State.READY; + case ICC_PIN: return State.PIN_REQUIRED; + case ICC_PUK: return State.PUK_REQUIRED; + case ICC_NETWORK_PERSONALIZATION: return State.NETWORK_LOCKED; + } + } + + Log.e(LOG_TAG, "RuimCard.getState(): case should never be reached"); + return State.UNKNOWN; + } + + public void dispose() { + //Unregister for all events + phone.mCM.unregisterForRUIMLockedOrAbsent(this); + phone.mCM.unregisterForOffOrNotAvailable(this); + phone.mCM.unregisterForRUIMReady(this); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "RuimCard finalized"); + } + + private RegistrantList absentRegistrants = new RegistrantList(); + private RegistrantList pinLockedRegistrants = new RegistrantList(); + private RegistrantList networkLockedRegistrants = new RegistrantList(); + + + public void registerForAbsent(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + absentRegistrants.add(r); + + if (getState() == State.ABSENT) { + r.notifyRegistrant(); + } + } + + public void unregisterForAbsent(Handler h) { + absentRegistrants.remove(h); + } + + public void registerForNetworkLocked(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + networkLockedRegistrants.add(r); + + if (getState() == State.NETWORK_LOCKED) { + r.notifyRegistrant(); + } + } + + public void unregisterForNetworkLocked(Handler h) { + networkLockedRegistrants.remove(h); + } + + public void registerForLocked(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + pinLockedRegistrants.add(r); + + if (getState().isPinLocked()) { + r.notifyRegistrant(); + } + } + + public void unregisterForLocked(Handler h) { + pinLockedRegistrants.remove(h); + } + + public void supplyPin (String pin, Message onComplete) { + phone.mCM.supplyIccPin(pin, obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public void supplyPuk (String puk, String newPin, Message onComplete) { + phone.mCM.supplyIccPuk(puk, newPin, obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public void supplyPin2 (String pin2, Message onComplete) { + phone.mCM.supplyIccPin2(pin2, obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public void supplyPuk2 (String puk2, String newPin2, Message onComplete) { + phone.mCM.supplyIccPuk2(puk2, newPin2, obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public void supplyNetworkDepersonalization (String pin, Message onComplete) { + if(DBG) log("Network Despersonalization: " + pin); + phone.mCM.supplyNetworkDepersonalization(pin, + obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public boolean getIccLockEnabled() { + return mRuimPinLocked; + } + + public boolean getIccFdnEnabled() { + return mRuimFdnEnabled; + } + + public void setIccLockEnabled (boolean enabled, + String password, Message onComplete) { + int serviceClassX; + serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + + CommandsInterface.SERVICE_CLASS_DATA + + CommandsInterface.SERVICE_CLASS_FAX; + + mDesiredPinLocked = enabled; + + phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM, + enabled, password, serviceClassX, + obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete)); + } + + public void setIccFdnEnabled (boolean enabled, + String password, Message onComplete) { + int serviceClassX; + serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + + CommandsInterface.SERVICE_CLASS_DATA + + CommandsInterface.SERVICE_CLASS_FAX + + CommandsInterface.SERVICE_CLASS_SMS; + + mDesiredFdnEnabled = enabled; + + phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD, + enabled, password, serviceClassX, + obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete)); + } + + public void changeIccLockPassword(String oldPassword, String newPassword, + Message onComplete) { + if(DBG) log("Change Pin1 old: " + oldPassword + " new: " + newPassword); + phone.mCM.changeIccPin(oldPassword, newPassword, + obtainMessage(EVENT_CHANGE_RUIM_PASSWORD_DONE, onComplete)); + } + + public void changeIccFdnPassword(String oldPassword, String newPassword, + Message onComplete) { + if(DBG) log("Change Pin2 old: " + oldPassword + " new: " + newPassword); + phone.mCM.changeIccPin2(oldPassword, newPassword, + obtainMessage(EVENT_CHANGE_RUIM_PASSWORD_DONE, onComplete)); + } + + public String getServiceProviderName() { + return phone.mRuimRecords.getServiceProviderName(); + } + + //***** Handler implementation + @Override + public void handleMessage(Message msg){ + AsyncResult ar; + int serviceClassX; + + serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + + CommandsInterface.SERVICE_CLASS_DATA + + CommandsInterface.SERVICE_CLASS_FAX; + + switch (msg.what) { + case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: + Log.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received"); + status = null; + updateStateProperty(); + broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_NOT_READY, null); + break; + case EVENT_RUIM_READY: + Log.d(LOG_TAG, "Event EVENT_RUIM_READY Received"); + //TODO: put facility read in SIM_READY now, maybe in REG_NW + phone.mCM.getIccStatus(obtainMessage(EVENT_GET_RUIM_STATUS_DONE)); + phone.mCM.queryFacilityLock ( + CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, + obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); + phone.mCM.queryFacilityLock ( + CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX, + obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE)); + break; + case EVENT_RUIM_LOCKED_OR_ABSENT: + Log.d(LOG_TAG, "Event EVENT_RUIM_LOCKED_OR_ABSENT Received"); + phone.mCM.getIccStatus(obtainMessage(EVENT_GET_RUIM_STATUS_DONE)); + phone.mCM.queryFacilityLock ( + CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, + obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); + break; + case EVENT_GET_RUIM_STATUS_DONE: + Log.d(LOG_TAG, "Event EVENT_GET_RUIM_STATUS_DONE Received"); + ar = (AsyncResult)msg.obj; + + getRuimStatusDone(ar); + break; + case EVENT_PINPUK_DONE: + Log.d(LOG_TAG, "Event EVENT_PINPUK_DONE Received"); + // a PIN/PUK/PIN2/PUK2/Network Personalization + // request has completed. ar.userObj is the response Message + // Repoll before returning + ar = (AsyncResult)msg.obj; + // TODO should abstract these exceptions + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + phone.mCM.getIccStatus( + obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj)); + break; + case EVENT_REPOLL_STATUS_DONE: + Log.d(LOG_TAG, "Event EVENT_REPOLL_STATUS_DONE Received"); + // Finished repolling status after PIN operation + // ar.userObj is the response messaeg + // ar.userObj.obj is already an AsyncResult with an + // appropriate exception filled in if applicable + + ar = (AsyncResult)msg.obj; + getRuimStatusDone(ar); + ((Message)ar.userObj).sendToTarget(); + break; + case EVENT_QUERY_FACILITY_LOCK_DONE: + Log.d(LOG_TAG, "Event EVENT_QUERY_FACILITY_LOCK_DONE Received"); + ar = (AsyncResult)msg.obj; + onQueryFacilityLock(ar); + break; + case EVENT_QUERY_FACILITY_FDN_DONE: + Log.d(LOG_TAG, "Event EVENT_QUERY_FACILITY_FDN_DONE Received"); + ar = (AsyncResult)msg.obj; + onQueryFdnEnabled(ar); + break; + case EVENT_CHANGE_FACILITY_LOCK_DONE: + Log.d(LOG_TAG, "Event EVENT_CHANGE_FACILITY_LOCK_DONE Received"); + ar = (AsyncResult)msg.obj; + if (ar.exception == null) { + mRuimPinLocked = mDesiredPinLocked; + if (DBG) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " + + "mRuimPinLocked= " + mRuimPinLocked); + } else { + Log.e(LOG_TAG, "Error change facility lock with exception " + + ar.exception); + } + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + ((Message)ar.userObj).sendToTarget(); + break; + case EVENT_CHANGE_FACILITY_FDN_DONE: + Log.d(LOG_TAG, "Event EVENT_CHANGE_FACILITY_FDN_DONE Received"); + ar = (AsyncResult)msg.obj; + + if (ar.exception == null) { + mRuimFdnEnabled = mDesiredFdnEnabled; + if (DBG) log("EVENT_CHANGE_FACILITY_FDN_DONE: " + + "mRuimFdnEnabled=" + mRuimFdnEnabled); + } else { + Log.e(LOG_TAG, "Error change facility fdn with exception " + + ar.exception); + } + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + ((Message)ar.userObj).sendToTarget(); + break; + case EVENT_CHANGE_RUIM_PASSWORD_DONE: + Log.d(LOG_TAG, "Event EVENT_CHANGE_RUIM_PASSWORD_DONE Received"); + ar = (AsyncResult)msg.obj; + if(ar.exception != null) { + Log.e(LOG_TAG, "Error in change sim password with exception" + + ar.exception); + } + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + ((Message)ar.userObj).sendToTarget(); + break; + default: + Log.e(LOG_TAG, "[CdmaRuimCard] Unknown Event " + msg.what); + } + } + + //***** Private methods + + /** + * Interpret EVENT_QUERY_FACILITY_LOCK_DONE + * @param ar is asyncResult of Query_Facility_Locked + */ + private void onQueryFacilityLock(AsyncResult ar) { + if(ar.exception != null) { + if (DBG) log("Error in querying facility lock:" + ar.exception); + return; + } + + int[] ints = (int[])ar.result; + if(ints.length != 0) { + mRuimPinLocked = (0!=ints[0]); + if(DBG) log("Query facility lock : " + mRuimPinLocked); + } else { + Log.e(LOG_TAG, "[CdmaRuimCard] Bogus facility lock response"); + } + } + + /** + * Interpret EVENT_QUERY_FACILITY_LOCK_DONE + * @param ar is asyncResult of Query_Facility_Locked + */ + private void onQueryFdnEnabled(AsyncResult ar) { + if(ar.exception != null) { + if(DBG) log("Error in querying facility lock:" + ar.exception); + return; + } + + int[] ints = (int[])ar.result; + if(ints.length != 0) { + mRuimFdnEnabled = (0!=ints[0]); + if(DBG) log("Query facility lock : " + mRuimFdnEnabled); + } else { + Log.e(LOG_TAG, "[CdmaRuimCard] Bogus facility lock response"); + } + } + + private void + getRuimStatusDone(AsyncResult ar) { + if (ar.exception != null) { + Log.e(LOG_TAG,"Error getting SIM status. " + + "RIL_REQUEST_GET_SIM_STATUS should " + + "never return an error", ar.exception); + return; + } + + CommandsInterface.IccStatus newStatus + = (CommandsInterface.IccStatus) ar.result; + + handleRuimStatus(newStatus); + } + + private void + handleRuimStatus(CommandsInterface.IccStatus newStatus) { + boolean transitionedIntoPinLocked; + boolean transitionedIntoAbsent; + boolean transitionedIntoNetworkLocked; + + RuimCard.State oldState, newState; + + oldState = getState(); + status = newStatus; + newState = getState(); + + updateStateProperty(); + + transitionedIntoPinLocked = ( + (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED) + || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED)); + transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT); + transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED + && newState == State.NETWORK_LOCKED); + + if (transitionedIntoPinLocked) { + if(DBG) log("Notify RUIM pin or puk locked."); + pinLockedRegistrants.notifyRegistrants(); + broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_LOCKED, + (newState == State.PIN_REQUIRED) ? + INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK); + } else if (transitionedIntoAbsent) { + if(DBG) log("Notify RUIM missing."); + absentRegistrants.notifyRegistrants(); + broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_ABSENT, null); + } else if (transitionedIntoNetworkLocked) { + if(DBG) log("Notify RUIM network locked."); + networkLockedRegistrants.notifyRegistrants(); + broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_LOCKED, + INTENT_VALUE_LOCKED_NETWORK); + } + } + + public void broadcastRuimStateChangedIntent(String value, String reason) { + Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Phone.PHONE_NAME_KEY, phone.getPhoneName()); + intent.putExtra(RuimCard.INTENT_KEY_ICC_STATE, value); + intent.putExtra(RuimCard.INTENT_KEY_LOCKED_REASON, reason); + if(DBG) log("Broadcasting intent SIM_STATE_CHANGED_ACTION " + value + + " reason " + reason); + ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE); + } + + public void updateImsiConfiguration(String imsi) { + if (imsi.length() >= 6) { + Configuration config = new Configuration(); + config.mcc = ((imsi.charAt(0)-'0')*100) + + ((imsi.charAt(1)-'0')*10) + + (imsi.charAt(2)-'0'); + config.mnc = ((imsi.charAt(3)-'0')*100) + + ((imsi.charAt(4)-'0')*10) + + (imsi.charAt(5)-'0'); + try { + ActivityManagerNative.getDefault().updateConfiguration(config); + } catch (RemoteException e) { + } + } + } + + private void + updateStateProperty() { + phone.setSystemProperty( + TelephonyProperties.PROPERTY_SIM_STATE, + getState().toString()); + } + + private void log(String msg) { + Log.d(LOG_TAG, "[RuimCard] " + msg); + } +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java new file mode 100644 index 0000000..7d392f0 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/RuimFileHandler.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +import android.os.*; +import android.os.AsyncResult; +import android.util.Log; + +import com.android.internal.telephony.IccConstants; +import com.android.internal.telephony.IccException; +import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.IccFileTypeMismatch; +import com.android.internal.telephony.IccIoResult; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.PhoneProxy; + +import java.util.ArrayList; + +/** + * {@hide} + */ +public final class RuimFileHandler extends IccFileHandler { + static final String LOG_TAG = "CDMA"; + + //***** Instance Variables + + //***** Constructor + RuimFileHandler(CDMAPhone phone) { + super(phone); + } + + public void dispose() { + } + + protected void finalize() { + Log.d(LOG_TAG, "RuimFileHandler finalized"); + } + + //***** Overridden from IccFileHandler + + @Override + public void loadEFImgTransparent(int fileid, int highOffset, int lowOffset, + int length, Message onLoaded) { + Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0, + onLoaded); + + phone.mCM.iccIO(COMMAND_GET_RESPONSE, fileid, "img", 0, 0, + GET_RESPONSE_EF_IMG_SIZE_BYTES, null, null, response); + } + + @Override + public void handleMessage(Message msg) { + + super.handleMessage(msg); + } + + protected void logd(String msg) { + Log.d(LOG_TAG, "[RuimFileHandler] " + msg); + } + + protected void loge(String msg) { + Log.e(LOG_TAG, "[RuimFileHandler] " + msg); + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java new file mode 100644 index 0000000..78e89d5 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java @@ -0,0 +1,101 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.internal.telephony.cdma; + +import android.content.pm.PackageManager; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.ServiceManager; +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import com.android.internal.telephony.AdnRecord; +import com.android.internal.telephony.AdnRecordCache; +import com.android.internal.telephony.IccPhoneBookInterfaceManager; +import com.android.internal.telephony.PhoneProxy; + +import java.util.ArrayList; +import java.util.List; + +/** + * RuimPhoneBookInterfaceManager to provide an inter-process communication to + * access ADN-like SIM records. + */ + + +public class RuimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager { + static final String LOG_TAG = "CDMA"; + + + Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + + switch(msg.what) { + default: + mBaseHandler.handleMessage(msg); + break; + } + } + }; + + public RuimPhoneBookInterfaceManager(CDMAPhone phone) { + super(phone); + adnCache = phone.mRuimRecords.getAdnCache(); + //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy + } + + public void dispose() { + super.dispose(); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "RuimPhoneBookInterfaceManager finalized"); + } + + public int[] getAdnRecordsSize(int efid) { + if (DBG) logd("getAdnRecordsSize: efid=" + efid); + synchronized(mLock) { + checkThread(); + recordSize = new int[3]; + + //Using mBaseHandler, no difference in EVENT_GET_SIZE_DONE handling + Message response = mBaseHandler.obtainMessage(EVENT_GET_SIZE_DONE); + + phone.getIccFileHandler().getEFLinearRecordSize(efid, response); + try { + mLock.wait(); + } catch (InterruptedException e) { + logd("interrupted while trying to load from the RUIM"); + } + } + + return recordSize; + } + + protected void logd(String msg) { + Log.d(LOG_TAG, "[RuimPbInterfaceManager] " + msg); + } + + protected void loge(String msg) { + Log.e(LOG_TAG, "[RuimPbInterfaceManager] " + msg); + } +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java new file mode 100644 index 0000000..7776f8b --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.Registrant; +import android.util.Log; + +import static com.android.internal.telephony.TelephonyProperties.*; +import com.android.internal.telephony.AdnRecord; +import com.android.internal.telephony.AdnRecordCache; +import com.android.internal.telephony.AdnRecordLoader; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.cdma.RuimCard; +import com.android.internal.telephony.gsm.MccTable; + +// can't be used since VoiceMailConstants is not public +//import com.android.internal.telephony.gsm.VoiceMailConstants; +import com.android.internal.telephony.IccException; +import com.android.internal.telephony.IccRecords; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.PhoneProxy; + + +/** + * {@hide} + */ +public final class RuimRecords extends IccRecords { + static final String LOG_TAG = "CDMA"; + + private static final boolean DBG = true; + + //***** Instance Variables + String imsi_m; + String mdn = null; // My mobile number + String h_sid; + String h_nid; + + // is not initialized + + //***** Event Constants + + private static final int EVENT_RUIM_READY = 1; + private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2; + private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4; + private static final int EVENT_GET_ICCID_DONE = 5; + private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10; + private static final int EVENT_UPDATE_DONE = 14; + private static final int EVENT_GET_SST_DONE = 17; + private static final int EVENT_GET_ALL_SMS_DONE = 18; + private static final int EVENT_MARK_SMS_READ_DONE = 19; + + private static final int EVENT_SMS_ON_RUIM = 21; + private static final int EVENT_GET_SMS_DONE = 22; + + private static final int EVENT_RUIM_REFRESH = 31; + + //***** Constructor + + RuimRecords(CDMAPhone p) { + super(p); + + adnCache = new AdnRecordCache(phone); + + recordsRequested = false; // No load request is made till SIM ready + + // recordsToLoad is set to 0 because no requests are made yet + recordsToLoad = 0; + + + p.mCM.registerForRUIMReady(this, EVENT_RUIM_READY, null); + p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); + // NOTE the EVENT_SMS_ON_RUIM is not registered + p.mCM.setOnIccRefresh(this, EVENT_RUIM_REFRESH, null); + + // Start off by setting empty state + onRadioOffOrNotAvailable(); + + } + + public void dispose() { + //Unregister for all events + phone.mCM.unregisterForRUIMReady(this); + phone.mCM.unregisterForOffOrNotAvailable( this); + phone.mCM.unSetOnIccRefresh(this); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "RuimRecords finalized"); + } + + protected void onRadioOffOrNotAvailable() { + countVoiceMessages = 0; + mncLength = 0; + iccid = null; + + adnCache.reset(); + + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null); + + // recordsRequested is set to false indicating that the SIM + // read requests made so far are not valid. This is set to + // true only when fresh set of read requests are made. + recordsRequested = false; + } + + //***** Public Methods + + /** Returns null if RUIM is not yet ready */ + public String getIMSI_M() { + return imsi_m; + } + + public String getMdnNumber() { + return mdn; + } + + public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){ + // In CDMA this is Operator/OEM dependent + AsyncResult.forMessage((onComplete)).exception = + new IccException("setVoiceMailNumber not implemented"); + onComplete.sendToTarget(); + Log.e(LOG_TAG, "method setVoiceMailNumber is not implemented"); + } + + /** + * Called by CCAT Service when REFRESH is received. + * @param fileChanged indicates whether any files changed + * @param fileList if non-null, a list of EF files that changed + */ + public void onRefresh(boolean fileChanged, int[] fileList) { + if (fileChanged) { + // A future optimization would be to inspect fileList and + // only reload those files that we care about. For now, + // just re-fetch all RUIM records that we cache. + fetchRuimRecords(); + } + } + + /** Returns the 5 or 6 digit MCC/MNC of the operator that + * provided the RUIM card. Returns null of RUIM is not yet ready + */ + String getRUIMOperatorNumeric() { + if (imsi_m == null) { + return null; + } + + if (mncLength != 0) { + // Length = length of MCC + length of MNC + // TODO: change spec name + // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3) + return imsi_m.substring(0, 3 + mncLength); + } + + // Guess the MNC length based on the MCC if we don't + // have a valid value in ef[ad] + + int mcc; + + mcc = Integer.parseInt(imsi_m.substring(0,3)); + + return imsi_m.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); + } + + //***** Overridden from Handler + public void handleMessage(Message msg) { + AsyncResult ar; + + byte data[]; + + boolean isRecordLoadResponse = false; + + try { switch (msg.what) { + case EVENT_RUIM_READY: + onRuimReady(); + break; + + case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: + onRadioOffOrNotAvailable(); + break; + + case EVENT_GET_DEVICE_IDENTITY_DONE: + Log.d(LOG_TAG, "Event EVENT_GET_DEVICE_IDENTITY_DONE Received"); + break; + + /* IO events */ + + case EVENT_GET_CDMA_SUBSCRIPTION_DONE: + ar = (AsyncResult)msg.obj; + String localTemp[] = (String[])ar.result; + if (ar.exception != null) { + break; + } + + mdn = localTemp[0]; + h_sid = localTemp[1]; + h_nid = localTemp[2]; + + Log.d(LOG_TAG, "MDN: " + mdn); + + break; + + case EVENT_GET_ICCID_DONE: + isRecordLoadResponse = true; + + ar = (AsyncResult)msg.obj; + data = (byte[])ar.result; + + if (ar.exception != null) { + break; + } + + iccid = IccUtils.bcdToString(data, 0, data.length); + + Log.d(LOG_TAG, "iccid: " + iccid); + + break; + + case EVENT_UPDATE_DONE: + ar = (AsyncResult)msg.obj; + if (ar.exception != null) { + Log.i(LOG_TAG, "RuimRecords update failed", ar.exception); + } + break; + + case EVENT_GET_ALL_SMS_DONE: + case EVENT_MARK_SMS_READ_DONE: + case EVENT_SMS_ON_RUIM: + case EVENT_GET_SMS_DONE: + Log.w(LOG_TAG, "Event not supported: " + msg.what); + break; + + // TODO: probably EF_CST should be read instead + case EVENT_GET_SST_DONE: + Log.d(LOG_TAG, "Event EVENT_GET_SST_DONE Received"); + break; + + case EVENT_RUIM_REFRESH: + isRecordLoadResponse = false; + ar = (AsyncResult)msg.obj; + if (ar.exception == null) { + handleRuimRefresh((int[])(ar.result)); + } + break; + + }}catch (RuntimeException exc) { + // I don't want these exceptions to be fatal + Log.w(LOG_TAG, "Exception parsing RUIM record", exc); + } finally { + // Count up record load responses even if they are fails + if (isRecordLoadResponse) { + onRecordLoaded(); + } + } + } + + protected void onRecordLoaded() { + // One record loaded successfully or failed, In either case + // we need to update the recordsToLoad count + recordsToLoad -= 1; + + if (recordsToLoad == 0 && recordsRequested == true) { + onAllRecordsLoaded(); + } else if (recordsToLoad < 0) { + Log.e(LOG_TAG, "RuimRecords: recordsToLoad <0, programmer error suspected"); + recordsToLoad = 0; + } + } + + protected void onAllRecordsLoaded() { + Log.d(LOG_TAG, "RuimRecords: record load complete"); + + // Further records that can be inserted are Operator/OEM dependent + + recordsLoadedRegistrants.notifyRegistrants( + new AsyncResult(null, null, null)); + ((CDMAPhone) phone).mRuimCard.broadcastRuimStateChangedIntent( + RuimCard.INTENT_VALUE_ICC_LOADED, null); + } + + + //***** Private Methods + + private void onRuimReady() { + /* broadcast intent ICC_READY here so that we can make sure + READY is sent before IMSI ready + */ + + ((CDMAPhone) phone).mRuimCard.broadcastRuimStateChangedIntent( + RuimCard.INTENT_VALUE_ICC_READY, null); + + fetchRuimRecords(); + + phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); + + } + + private void fetchRuimRecords() { + recordsRequested = true; + + Log.v(LOG_TAG, "RuimRecords:fetchRuimRecords " + recordsToLoad); + + phone.getIccFileHandler().loadEFTransparent(EF_ICCID, + obtainMessage(EVENT_GET_ICCID_DONE)); + recordsToLoad++; + + // Further records that can be inserted are Operator/OEM dependent + } + + @Override + protected int getDisplayRule(String plmn) { + // TODO together with spn + return 0; + } + + @Override + public void setVoiceMessageWaiting(int line, int countWaiting) { + Log.i(LOG_TAG, "RuimRecords: setVoiceMessageWaiting not supported."); + } + + private void handleRuimRefresh(int[] result) { + if (result == null || result.length == 0) { + if (DBG) log("handleRuimRefresh without input"); + return; + } + + switch ((result[0])) { + case CommandsInterface.SIM_REFRESH_FILE_UPDATED: + if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); + adnCache.reset(); + fetchRuimRecords(); + break; + case CommandsInterface.SIM_REFRESH_INIT: + if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); + // need to reload all files (that we care about) + fetchRuimRecords(); + break; + case CommandsInterface.SIM_REFRESH_RESET: + if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); + phone.mCM.setRadioPower(false, null); + /* Note: no need to call setRadioPower(true). Assuming the desired + * radio power state is still ON (as tracked by ServiceStateTracker), + * ServiceStateTracker will call setRadioPower when it receives the + * RADIO_STATE_CHANGED notification for the power off. And if the + * desired power state has changed in the interim, we don't want to + * override it with an unconditional power on. + */ + break; + default: + // unknown refresh operation + if (DBG) log("handleRuimRefresh with unknown operation"); + break; + } + } + + protected void log(String s) { + Log.d(LOG_TAG, "[RuimRecords] " + s); + } + +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java new file mode 100644 index 0000000..9439359 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.internal.telephony.cdma; + +import android.content.Context; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import com.android.internal.telephony.IccConstants; +import com.android.internal.telephony.IccSmsInterfaceManager; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.SmsRawData; + +import java.util.ArrayList; +import java.util.List; + +import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; + +/** + * RuimSmsInterfaceManager to provide an inter-process communication to + * access Sms in Ruim. + */ +public class RuimSmsInterfaceManager extends IccSmsInterfaceManager { + static final String LOG_TAG = "CDMA"; + static final boolean DBG = true; + + private final Object mLock = new Object(); + private boolean mSuccess; + private List mSms; + + private static final int EVENT_LOAD_DONE = 1; + private static final int EVENT_UPDATE_DONE = 2; + + Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + + switch (msg.what) { + case EVENT_UPDATE_DONE: + ar = (AsyncResult) msg.obj; + synchronized (mLock) { + mSuccess = (ar.exception == null); + mLock.notifyAll(); + } + break; + case EVENT_LOAD_DONE: + ar = (AsyncResult)msg.obj; + synchronized (mLock) { + if (ar.exception == null) { + mSms = (List) + buildValidRawData((ArrayList) ar.result); + } else { + if(DBG) log("Cannot load Sms records"); + if (mSms != null) + mSms.clear(); + } + mLock.notifyAll(); + } + break; + } + } + }; + + public RuimSmsInterfaceManager(CDMAPhone phone) { + super(phone); + mDispatcher = new CdmaSMSDispatcher(phone); + } + + public void dispose() { + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "RuimSmsInterfaceManager finalized"); + } + + /** + * Update the specified message on the RUIM. + * + * @param index record index of message to update + * @param status 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 success or not + * + */ + public boolean + updateMessageOnIccEf(int index, int status, byte[] pdu) { + if (DBG) log("updateMessageOnIccEf: index=" + index + + " status=" + status + " ==> " + + "("+ pdu + ")"); + enforceReceiveAndSend("Updating message on RUIM"); + synchronized(mLock) { + mSuccess = false; + Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); + + if (status == STATUS_ON_ICC_FREE) { + // Special case FREE: call deleteSmsOnRuim instead of + // manipulating the RUIM record + mPhone.mCM.deleteSmsOnRuim(index, response); + } else { + byte[] record = makeSmsRecordData(status, pdu); + mPhone.getIccFileHandler().updateEFLinearFixed( + IccConstants.EF_SMS, index, record, null, response); + } + try { + mLock.wait(); + } catch (InterruptedException e) { + log("interrupted while trying to update by index"); + } + } + return mSuccess; + } + + /** + * Copy a raw SMS PDU to the RUIM. + * + * @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 success or not + * + */ + public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) { + //NOTE smsc not used in RUIM + if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + + "pdu=("+ pdu + ")"); + enforceReceiveAndSend("Copying message to RUIM"); + synchronized(mLock) { + mSuccess = false; + Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); + + mPhone.mCM.writeSmsToRuim(status, IccUtils.bytesToHexString(pdu), + response); + + try { + mLock.wait(); + } catch (InterruptedException e) { + log("interrupted while trying to update by index"); + } + } + return mSuccess; + } + + /** + * Retrieves all messages currently stored on RUIM. + */ + public List getAllMessagesFromIccEf() { + if (DBG) log("getAllMessagesFromEF"); + + Context context = mPhone.getContext(); + + context.enforceCallingPermission( + "android.permission.RECEIVE_SMS", + "Reading messages from RUIM"); + synchronized(mLock) { + Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); + mPhone.getIccFileHandler().loadEFLinearFixedAll(IccConstants.EF_SMS, response); + + try { + mLock.wait(); + } catch (InterruptedException e) { + log("interrupted while trying to load from the RUIM"); + } + } + return mSms; + } + + protected void log(String msg) { + Log.d(LOG_TAG, "[RuimSmsInterfaceManager] " + msg); + } +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java new file mode 100644 index 0000000..e4b474a --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -0,0 +1,905 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +import android.os.Parcel; +import android.text.format.Time; +import android.util.Config; +import android.util.Log; +import com.android.internal.telephony.EncodeException; +import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.SmsHeader; +import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.cdma.sms.BearerData; +import com.android.internal.telephony.cdma.sms.CdmaSmsAddress; +import com.android.internal.telephony.cdma.sms.SmsDataCoding; +import com.android.internal.telephony.cdma.sms.SmsEnvelope; +import com.android.internal.telephony.cdma.sms.UserData; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Random; + +import static android.telephony.SmsMessage.ENCODING_7BIT; +import static android.telephony.SmsMessage.ENCODING_8BIT; +import static android.telephony.SmsMessage.ENCODING_16BIT; +import static android.telephony.SmsMessage.ENCODING_UNKNOWN; +import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES; +import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; +import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS; +import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER; +import static android.telephony.SmsMessage.MessageClass; +import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_NONE; +import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_TEMPORARY; +import static com.android.internal.telephony.cdma.sms.BearerData.ERROR_PERMANENT; +import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_DELIVER; +import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_SUBMIT; +import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_CANCELLATION; +import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_DELIVERY_ACK; +import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_USER_ACK; +import static com.android.internal.telephony.cdma.sms.BearerData.MESSAGE_TYPE_READ_ACK; +import static com.android.internal.telephony.cdma.sms.CdmaSmsAddress.SMS_ADDRESS_MAX; +import static com.android.internal.telephony.cdma.sms.CdmaSmsAddress.SMS_SUBADDRESS_MAX; +import static com.android.internal.telephony.cdma.sms.SmsEnvelope.SMS_BEARER_DATA_MAX; +import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_7BIT_ASCII; +import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_GSM_7BIT_ALPHABET; +import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_IA5; +import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_OCTET; +import static com.android.internal.telephony.cdma.sms.UserData.UD_ENCODING_UNICODE_16; + +/** + * A Short Message Service message. + * + */ +public class SmsMessage extends SmsMessageBase { + static final String LOG_TAG = "CDMA"; + + /** + * Status of a previously submitted SMS. + * This field applies to SMS Delivery Acknowledge messages. 0 indicates success; + * Here, the error class is defined by the bits from 9-8, the status code by the bits from 7-0. + * See C.S0015-B, v2.0, 4.5.21 for a detailed description of possible values. + */ + private int status; + + /** The next message ID for the BearerData. Shall be a random value on first use. + * (See C.S0015-B, v2.0, 4.3.1.5) + */ + private static int nextMessageId = 0; + + /** Specifies if this is the first SMS message submit */ + private static boolean firstSMS = true; + + /** Specifies if a return of an acknowledgment is requested for send SMS */ + private static final int RETURN_NO_ACK = 0; + private static final int RETURN_ACK = 1; + + private SmsEnvelope mEnvelope; + private BearerData mBearerData; + + public static class SubmitPdu extends SubmitPduBase { + } + + /** + * Create an SmsMessage from a raw PDU. + * Note: In CDMA the PDU is just a byte representation of the received Sms. + */ + public static SmsMessage createFromPdu(byte[] pdu) { + SmsMessage msg = new SmsMessage(); + + try { + msg.parsePdu(pdu); + return msg; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex); + return null; + } + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public static SmsMessage newFromCMT(String[] lines) { + Log.w(LOG_TAG, "newFromCMT: is not supported in CDMA mode."); + return null; + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public static SmsMessage newFromCMTI(String line) { + Log.w(LOG_TAG, "newFromCMTI: is not supported in CDMA mode."); + return null; + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public static SmsMessage newFromCDS(String line) { + Log.w(LOG_TAG, "newFromCDS: is not supported in CDMA mode."); + return null; + } + + /** + * Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp. + * Note: Only primitive fields are set. + */ + public static SmsMessage newFromParcel(Parcel p) { + // Note: Parcel.readByte actually reads one Int and masks to byte + SmsMessage msg = new SmsMessage(); + SmsEnvelope env = new SmsEnvelope(); + CdmaSmsAddress addr = new CdmaSmsAddress(); + byte[] data; + byte count; + int countInt; + + //currently not supported by the modem-lib: env.mMessageType + env.teleService = p.readInt(); //p_cur->uTeleserviceID + + if (0 != p.readByte()) { //p_cur->bIsServicePresent + env.messageType = SmsEnvelope.MESSAGE_TYPE_BROADCAST; + } + else { + if (SmsEnvelope.TELESERVICE_NOT_SET == env.teleService) { + // assume type ACK + env.messageType = SmsEnvelope.MESSAGE_TYPE_ACKNOWLEDGE; + } else { + env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; + } + } + env.serviceCategory = p.readInt(); //p_cur->uServicecategory + + // address + addr.digitMode = (byte) (0xFF & p.readInt()); //p_cur->sAddress.digit_mode + addr.numberMode = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_mode + addr.ton = p.readInt(); //p_cur->sAddress.number_type + addr.numberPlan = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_plan + count = p.readByte(); //p_cur->sAddress.number_of_digits + addr.numberOfDigits = count; + data = new byte[count]; + //p_cur->sAddress.digits[digitCount] + for (int index=0; index < count; index++) { + data[index] = p.readByte(); + } + addr.origBytes = data; + + // ignore subaddress + p.readInt(); //p_cur->sSubAddress.subaddressType + p.readByte(); //p_cur->sSubAddress.odd + count = p.readByte(); //p_cur->sSubAddress.number_of_digits + //p_cur->sSubAddress.digits[digitCount] : + for (int index=0; index < count; index++) { + p.readByte(); + } + + /* currently not supported by the modem-lib: + env.bearerReply + env.replySeqNo + env.errorClass + env.causeCode + */ + + // bearer data + countInt = p.readInt(); //p_cur->uBearerDataLen + if (countInt >0) { + data = new byte[countInt]; + //p_cur->aBearerData[digitCount] : + for (int index=0; index < countInt; index++) { + data[index] = p.readByte(); + } + env.bearerData = data; + // BD gets further decoded when accessed in SMSDispatcher + } + + // link the the filled objects to the SMS + env.origAddress = addr; + msg.originatingAddress = addr; + msg.mEnvelope = env; + + // create byte stream representation for transportation through the layers. + msg.createPdu(); + + return msg; + } + + /** + * Create an SmsMessage from an SMS EF record. + * + * @param index Index of SMS record. This should be index in ArrayList + * returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1. + * @param data Record data. + * @return An SmsMessage representing the record. + * + * @hide + */ + public static SmsMessage createFromEfRecord(int index, byte[] data) { + try { + SmsMessage msg = new SmsMessage(); + + msg.indexOnIcc = index; + + // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT, + // or STORED_UNSENT + // See 3GPP2 C.S0023 3.4.27 + if ((data[0] & 1) == 0) { + Log.w(LOG_TAG, "SMS parsing failed: Trying to parse a free record"); + return null; + } else { + msg.statusOnIcc = data[0] & 0x07; + } + + // Second byte is the MSG_LEN, length of the message + // See 3GPP2 C.S0023 3.4.27 + int size = data[1]; + + // Note: Data may include trailing FF's. That's OK; message + // should still parse correctly. + byte[] pdu = new byte[size]; + System.arraycopy(data, 2, pdu, 0, size); + // the message has to be parsed before it can be displayed + // see gsm.SmsMessage + return msg; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex); + return null; + } + + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public static int getTPLayerLengthForPDU(String pdu) { + Log.w(LOG_TAG, "getTPLayerLengthForPDU: is not supported in CDMA mode."); + return 0; + } + + /** + * Get an SMS-SUBMIT PDU for a destination address and a message + * + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress Address of the recipient. + * @param message String representation of the message payload. + * @param statusReportRequested Indicates whether a report is requested for this message. + * @param headerData Array containing the data for the User Data Header, preceded + * by the Element Identifiers. + * @return a SubmitPdu containing the encoded SC + * address, if applicable, and the encoded message. + * Returns null on encode error. + * @hide + */ + public static SubmitPdu getSubmitPdu(String scAddress, + String destinationAddress, String message, + boolean statusReportRequested, byte[] headerData) { + + SmsMessage sms = new SmsMessage(); + SubmitPdu ret = new SubmitPdu(); + UserData uData = new UserData(); + SmsHeader smsHeader; + + // Perform null parameter checks. + if (message == null || destinationAddress == null) { + return null; + } + + // ** Set UserData + SmsHeader ** + try { + // First, try encoding it with the GSM alphabet + int septetCount = GsmAlphabet.countGsmSeptets(message, true); + // User Data (and length) + + uData.userData = message.getBytes(); + + if (uData.userData.length > MAX_USER_DATA_SEPTETS) { + // Message too long + return null; + } + + // desired TP-Data-Coding-Scheme + uData.userDataEncoding = UserData.UD_ENCODING_GSM_7BIT_ALPHABET; + + // paddingBits not needed for UD_ENCODING_GSM_7BIT_ALPHABET + + // sms header + if(headerData != null) { + smsHeader = SmsHeader.parse(headerData); + uData.userDataHeader = smsHeader; + } else { + // no user data header available! + } + + } catch (EncodeException ex) { + byte[] textPart; + // Encoding to the 7-bit alphabet failed. Let's see if we can + // send it as a UCS-2 encoded message + + try { + textPart = message.getBytes("utf-16be"); + } catch (UnsupportedEncodingException uex) { + Log.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); + return null; + } + + uData.userData = textPart; + + if (uData.userData.length > MAX_USER_DATA_BYTES) { + // Message too long + return null; + } + + // TP-Data-Coding-Scheme + uData.userDataEncoding = UserData.UD_ENCODING_UNICODE_16; + + // sms header + if(headerData != null) { + smsHeader = SmsHeader.parse(headerData); + uData.userDataHeader = smsHeader; + } else { + // no user data header available! + } + } + + byte[] data = sms.getEnvelope(destinationAddress, statusReportRequested, uData, + (headerData != null), (null == headerData)); + + if (null == data) return null; + + ret.encodedMessage = data; + ret.encodedScAddress = null; + return ret; + } + + + /** + * Get an SMS-SUBMIT PDU for a destination address and a message + * + * @param scAddress Service Centre address. Null means use default. + * @return a SubmitPdu 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) { + return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null); + } + + /** + * Get an SMS-SUBMIT PDU for a data message to a destination address & 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 data for the message + * @return a SubmitPdu 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) { + + SmsMessage sms = new SmsMessage(); + SubmitPdu ret = new SubmitPdu(); + UserData uData = new UserData(); + SmsHeader smsHeader = new SmsHeader(); + + if (data.length > (MAX_USER_DATA_BYTES - 7 /* UDH size */)) { + Log.e(LOG_TAG, "SMS data message may only contain " + + (MAX_USER_DATA_BYTES - 7) + " bytes"); + return null; + } + + byte[] destPort = new byte[4]; + destPort[0] = (byte) ((destinationPort >> 8) & 0xFF); // MSB of destination port + destPort[1] = (byte) (destinationPort & 0xFF); // LSB of destination port + destPort[2] = 0x00; // MSB of originating port + destPort[3] = 0x00; // LSB of originating port + smsHeader.add( + new SmsHeader.Element(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT, destPort)); + + smsHeader.nbrOfHeaders = smsHeader.getElements().size(); + uData.userDataHeader = smsHeader; + + // TP-Data-Coding-Scheme + // No class, 8 bit data + uData.userDataEncoding = UserData.UD_ENCODING_OCTET; + uData.userData = data; + + byte[] msgData = sms.getEnvelope(destinationAddress, statusReportRequested, uData, + true, true); + + ret.encodedMessage = msgData; + ret.encodedScAddress = null; + return ret; + } + + static class PduParser { + + PduParser() { + } + + /** + * Parses an SC timestamp and returns a currentTimeMillis()-style + * timestamp + */ + static long getSCTimestampMillis(byte[] timestamp) { + // TP-Service-Centre-Time-Stamp + int year = IccUtils.beBcdByteToInt(timestamp[0]); + int month = IccUtils.beBcdByteToInt(timestamp[1]); + int day = IccUtils.beBcdByteToInt(timestamp[2]); + int hour = IccUtils.beBcdByteToInt(timestamp[3]); + int minute = IccUtils.beBcdByteToInt(timestamp[4]); + int second = IccUtils.beBcdByteToInt(timestamp[5]); + + Time time = new Time(Time.TIMEZONE_UTC); + + // C.S0015-B v2.0, 4.5.4: range is 1996-2095 + time.year = year >= 96 ? year + 1900 : year + 2000; + time.month = month - 1; + time.monthDay = day; + time.hour = hour; + time.minute = minute; + time.second = second; + + return time.toMillis(true); + } + + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public int getProtocolIdentifier() { + Log.w(LOG_TAG, "getProtocolIdentifier: is not supported in CDMA mode."); + // (3GPP TS 23.040): "no interworking, but SME to SME protocol": + return 0; + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public boolean isReplace() { + Log.w(LOG_TAG, "isReplace: is not supported in CDMA mode."); + return false; + } + + /** + * {@inheritDoc} + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public boolean isCphsMwiMessage() { + Log.w(LOG_TAG, "isCphsMwiMessage: is not supported in CDMA mode."); + return false; + } + + /** + * {@inheritDoc} + */ + public boolean isMWIClearMessage() { + if ((mBearerData != null) && (0 == mBearerData.numberOfMessages)) { + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + public boolean isMWISetMessage() { + if ((mBearerData != null) && (mBearerData.numberOfMessages >0)) { + return true; + } + return false; + } + + /** + * {@inheritDoc} + */ + public boolean isMwiDontStore() { + if ((mBearerData != null) && (mBearerData.numberOfMessages >0) + && (null == mBearerData.userData)) { + return true; + } + return false; + } + + /** + * Returns the status for a previously submitted message. + * For not interfering with status codes from GSM, this status code is + * shifted to the bits 31-16. + */ + public int getStatus() { + return(status<<16); + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public boolean isStatusReportMessage() { + Log.w(LOG_TAG, "isStatusReportMessage: is not supported in CDMA mode."); + return false; + } + + /** + * Note: This function is a GSM specific functionality which is not supported in CDMA mode. + */ + public boolean isReplyPathPresent() { + Log.w(LOG_TAG, "isReplyPathPresent: is not supported in CDMA mode."); + return false; + } + + /** + * Returns the teleservice type of the message. + * @return the teleservice: + * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET}, + * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WMT}, + * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WEMT}, + * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN}, + * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP} + */ + public int getTeleService() { + return mEnvelope.teleService; + } + + /** + * Decodes pdu to an empty SMS object. + * In the CDMA case the pdu is just an internal byte stream representation + * of the SMS Java-object. + * @see #createPdu() + */ + private void parsePdu(byte[] pdu) { + ByteArrayInputStream bais = new ByteArrayInputStream(pdu); + DataInputStream dis = new DataInputStream(new BufferedInputStream(bais)); + byte length; + SmsEnvelope env = new SmsEnvelope(); + CdmaSmsAddress addr = new CdmaSmsAddress(); + + try { + env.messageType = dis.readInt(); + env.teleService = dis.readInt(); + env.serviceCategory = dis.readInt(); + + addr.digitMode = dis.readByte(); + addr.numberMode = dis.readByte(); + addr.ton = dis.readByte(); + addr.numberPlan = dis.readByte(); + + length = dis.readByte(); + addr.numberOfDigits = length; + addr.origBytes = new byte[length]; + dis.read(addr.origBytes, 0, length); // digits + + env.bearerReply = dis.readInt(); + // CauseCode values: + env.replySeqNo = dis.readByte(); + env.errorClass = dis.readByte(); + env.causeCode = dis.readByte(); + + //encoded BearerData: + length = dis.readByte(); + env.bearerData = new byte[length]; + dis.read(env.bearerData, 0, length); + dis.close(); + } catch (Exception ex) { + Log.e(LOG_TAG, "createFromPdu: conversion from byte array to object failed: " + ex); + } + + // link the filled objects to this SMS + originatingAddress = addr; + env.origAddress = addr; + mEnvelope = env; + + parseSms(); + } + + /** + * Parses a SMS message from its BearerData stream. (mobile-terminated only) + */ + protected void parseSms() { + mBearerData = SmsDataCoding.decodeCdmaSms(mEnvelope.bearerData); + messageRef = mBearerData.messageID; + + // TP-Message-Type-Indicator + // (See 3GPP2 C.S0015-B, v2, 4.5.1) + int messageType = mBearerData.messageType; + + switch (messageType) { + case MESSAGE_TYPE_USER_ACK: + case MESSAGE_TYPE_READ_ACK: + case MESSAGE_TYPE_DELIVER: + // Deliver (mobile-terminated only) + parseSmsDeliver(); + break; + case MESSAGE_TYPE_DELIVERY_ACK: + parseSmsDeliveryAck(); + break; + + default: + // the rest of these + throw new RuntimeException("Unsupported message type: " + messageType); + } + } + + /** + * Parses a SMS-DELIVER message. (mobile-terminated only) + * See 3GPP2 C.S0015-B, v2, 4.4.1 + */ + private void parseSmsDeliver() { + if (originatingAddress != null) { + originatingAddress.address = new String(originatingAddress.origBytes); + if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: " + + originatingAddress.address); + } + + if (mBearerData.timeStamp != null) { + scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp); + } + + if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis); + + parseUserData(mBearerData.userData); + } + + /** + * Parses a SMS-DELIVER message. (mobile-terminated only) + * See 3GPP2 C.S0015-B, v2, 4.4.1 + */ + private void parseSmsDeliveryAck() { + if (originatingAddress != null) { + originatingAddress.address = new String(originatingAddress.origBytes); + if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: " + + originatingAddress.address); + } + + if (mBearerData.timeStamp != null) { + scTimeMillis = PduParser.getSCTimestampMillis(mBearerData.timeStamp); + } + + if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis); + + if (mBearerData.errorClass != BearerData.ERROR_UNDEFINED) { + status = mBearerData.errorClass << 8; + status |= mBearerData.messageStatus; + } + + parseUserData(mBearerData.userData); + + } + + /** + * Parses the User Data of an SMS. + */ + private void parseUserData(UserData uData) { + int encodingType; + + if (null == uData) { + return; + } + + encodingType = uData.userDataEncoding; + + // insert DCS-decoding here when type is supported by ril-library + + userData = uData.userData; + userDataHeader = uData.userDataHeader; + + switch (encodingType) { + case UD_ENCODING_GSM_7BIT_ALPHABET: + case UD_ENCODING_UNICODE_16: + // user data was already decoded by wmsts-library + messageBody = new String(userData); + break; + + // data and unsupported encodings: + case UD_ENCODING_OCTET: + default: + messageBody = null; + break; + } + + if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'"); + + if (messageBody != null) { + parseMessageBody(); + } + } + + /** + * {@inheritDoc} + */ + public MessageClass getMessageClass() { + if (BearerData.DISPLAY_IMMEDIATE == mBearerData.displayMode ) { + return MessageClass.CLASS_0; + } else { + return MessageClass.UNKNOWN; + } + } + + /** + * Creates BearerData and Envelope from parameters for a Submit SMS. + * @return byte stream for SubmitPdu. + */ + private byte[] getEnvelope(String destinationAddress, boolean statusReportRequested, + UserData userData, boolean hasHeaders, boolean useNewId) { + + BearerData mBearerData = new BearerData(); + SmsEnvelope env = new SmsEnvelope(); + CdmaSmsAddress mSmsAddress = new CdmaSmsAddress(); + + // ** set SmsAddress ** + mSmsAddress.digitMode = CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR; + try { + mSmsAddress.origBytes = destinationAddress.getBytes("UTF-8"); + } catch (Exception e) { + Log.e(LOG_TAG, "doGetSubmitPdu: conversion of destinationAddress from string to byte[]" + + " failed: " + e.getMessage()); + return null; + } + mSmsAddress.numberOfDigits = (byte)mSmsAddress.origBytes.length; + mSmsAddress.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK; + // see C.S0015-B, v2.0, 3.4.3.3 + mSmsAddress.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY; + mSmsAddress.ton = CdmaSmsAddress.TON_INTERNATIONAL_OR_IP; + + // ** set BearerData ** + mBearerData.userData = userData; + mBearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT; + + if (useNewId) { + setNextMessageId(); + } + mBearerData.messageID = nextMessageId; + + // Set the reply options (See C.S0015-B, v2.0, 4.5.11) + if(statusReportRequested) { + mBearerData.deliveryAckReq = true; + } else { + mBearerData.deliveryAckReq = false; + } + // Currently settings applications do not support this + mBearerData.userAckReq = false; + mBearerData.readAckReq = false; + mBearerData.reportReq = false; + + // Set the display mode (See C.S0015-B, v2.0, 4.5.16) + mBearerData.displayMode = BearerData.DISPLAY_DEFAULT; + + // number of messages: not needed for encoding! + + // indicate whether a user data header is available + mBearerData.hasUserDataHeader = hasHeaders; + + // ** encode BearerData ** + byte[] encodedBearerData = null; + try { + encodedBearerData = SmsDataCoding.encodeCdmaSms(mBearerData); + } catch (Exception e) { + Log.e(LOG_TAG, "doGetSubmitPdu: EncodeCdmaSMS function in JNI interface failed: " + + e.getMessage()); + return null; + } + + // ** SmsEnvelope ** + env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; + env.teleService = SmsEnvelope.TELESERVICE_WEMT; + env.destAddress = mSmsAddress; + env.bearerReply = RETURN_ACK; + env.bearerData = encodedBearerData; + mEnvelope = env; + + // get byte array output stream from SmsAddress object and SmsEnvelope member. + return serialize(mSmsAddress); + } + + /** + * Set the nextMessageId to a random value between 0 and 65536 + * See C.S0015-B, v2.0, 4.3.1.5 + */ + private void setNextMessageId() { + // Message ID, modulo 65536 + if(firstSMS) { + Random generator = new Random(); + nextMessageId = generator.nextInt(65536); + firstSMS = false; + } else { + nextMessageId = ++nextMessageId & 0xFFFF; + } + } + + /** + * Creates ByteArrayOutputStream from CdmaSmsAddress and SmsEnvelope objects + * + * @param address CdmaSmsAddress object + * @return ByteArrayOutputStream + */ + private byte[] serialize(CdmaSmsAddress destAddress) { + SmsEnvelope env = mEnvelope; + ByteArrayOutputStream baos = new ByteArrayOutputStream(100); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos)); + + try { + dos.writeInt(env.teleService); + dos.writeInt(0); //servicePresent + dos.writeInt(0); //serviceCategory + dos.write(destAddress.digitMode); + dos.write(destAddress.numberMode); + dos.write(destAddress.ton); // number_type + dos.write(destAddress.numberPlan); + dos.write(destAddress.numberOfDigits); + dos.write(destAddress.origBytes, 0, destAddress.origBytes.length); // digits + // Subaddress is not supported. + dos.write(0); //subaddressType + dos.write(0); //subaddr_odd + dos.write(0); //subaddr_nbr_of_digits + dos.write(env.bearerData.length); + dos.write(env.bearerData, 0, env.bearerData.length); + dos.close(); + return baos.toByteArray(); + } catch(IOException ex) { + Log.e(LOG_TAG, "serialize: conversion from object to data output stream failed: " + ex); + return null; + } + } + + /** + * Creates byte array (pseudo pdu) from SMS object. + * Note: Do not call this method more than once per object! + */ + private void createPdu() { + SmsEnvelope env = mEnvelope; + CdmaSmsAddress addr = env.origAddress; + ByteArrayOutputStream baos = new ByteArrayOutputStream(100); + DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos)); + + try { + dos.writeInt(env.messageType); + dos.writeInt(env.teleService); + dos.writeInt(env.serviceCategory); + + dos.writeByte(addr.digitMode); + dos.writeByte(addr.numberMode); + dos.writeByte(addr.ton); + dos.writeByte(addr.numberPlan); + dos.writeByte(addr.numberOfDigits); + dos.write(addr.origBytes, 0, addr.origBytes.length); // digits + + dos.writeInt(env.bearerReply); + // CauseCode values: + dos.writeByte(env.replySeqNo); + dos.writeByte(env.errorClass); + dos.writeByte(env.causeCode); + //encoded BearerData: + dos.writeByte(env.bearerData.length); + dos.write(env.bearerData, 0, env.bearerData.length); + dos.close(); + + mPdu = baos.toByteArray(); + } catch (IOException ex) { + Log.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex); + } + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/TtyIntent.java b/telephony/java/com/android/internal/telephony/cdma/TtyIntent.java new file mode 100644 index 0000000..f27f79c --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/TtyIntent.java @@ -0,0 +1,46 @@ +/* + * 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.cdma; + +public class TtyIntent { + + private static final String TAG = "TtyIntent"; + + + /** Event for TTY mode change */ + + /** + * Broadcast intent action indicating that the TTY has either been + * enabled or disabled. An intent extra provides this state as a boolean, + * where {@code true} means enabled. + * @see #TTY_ENABLED + * + * {@hide} + */ + public static final String TTY_ENABLED_CHANGE_ACTION = + "com.android.internal.telephony.cdma.intent.action.TTY_ENABLED_CHANGE"; + + /** + * The lookup key for a boolean that indicates whether TTY mode is enabled or + * disabled. {@code true} means TTY mode is enabled. Retrieve it with + * {@link android.content.Intent#getBooleanExtra(String,boolean)}. + * + * {@hide} + */ + public static final String TTY_ENABLED = "ttyEnabled"; + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/package.html b/telephony/java/com/android/internal/telephony/cdma/package.html new file mode 100644 index 0000000..cf1ad4a --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/package.html @@ -0,0 +1,6 @@ + + +Provides classes to control or read data from CDMA phones. +@hide + + diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java new file mode 100644 index 0000000..fec9529 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma.sms; + +public final class BearerData{ + + // For completeness the following fields are listed, though not used yet. + /** + * Supported priority modes for CDMA SMS messages + * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1) + */ + //public static final int PRIORITY_NORMAL = 0x0; + //public static final int PRIORITY_INTERACTIVE = 0x1; + //public static final int PRIORITY_URGENT = 0x2; + //public static final int PRIORITY_EMERGENCY = 0x3; + + /** + * Supported privacy modes for CDMA SMS messages + * (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1) + */ + //public static final int PRIVACY_NOT_RESTRICTED = 0x0; + //public static final int PRIVACY_RESTRICTED = 0x1; + //public static final int PRIVACY_CONFIDENTIAL = 0x2; + //public static final int PRIVACY_SECRET = 0x3; + + /** + * Supported alert modes for CDMA SMS messages + * (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1) + */ + //public static final int ALERT_DEFAULT = 0x0; + //public static final int ALERT_LOW_PRIO = 0x1; + //public static final int ALERT_MEDIUM_PRIO = 0x2; + //public static final int ALERT_HIGH_PRIO = 0x3; + + /** + * Supported display modes for CDMA SMS messages + * (See 3GPP2 C.S0015-B, v2.0, table 4.5.16-1) + */ + public static final int DISPLAY_IMMEDIATE = 0x0; + public static final int DISPLAY_DEFAULT = 0x1; + public static final int DISPLAY_USER = 0x2; + + /** + * Supported message types for CDMA SMS messages + * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1) + */ + public static final int MESSAGE_TYPE_DELIVER = 0x01; + public static final int MESSAGE_TYPE_SUBMIT = 0x02; + public static final int MESSAGE_TYPE_CANCELLATION = 0x03; + public static final int MESSAGE_TYPE_DELIVERY_ACK = 0x04; + public static final int MESSAGE_TYPE_USER_ACK = 0x05; + public static final int MESSAGE_TYPE_READ_ACK = 0x06; + public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07; + public static final int MESSAGE_TYPE_SUBMIT_REPORT = 0x08; + + /** + * SMS Message Status Codes + * (See 3GPP2 C.S0015-B, v2.0, table 4.5.21-1) + */ + /* no-error codes */ + public static final int ERROR_NONE = 0x00; + public static final int STATUS_ACCEPTED = 0x00; + public static final int STATUS_DEPOSITED_TO_INTERNET = 0x01; + public static final int STATUS_DELIVERED = 0x02; + public static final int STATUS_CANCELLED = 0x03; + /* temporary-error and permanent-error codes */ + public static final int ERROR_TEMPORARY = 0x02; + public static final int STATUS_NETWORK_CONGESTION = 0x04; + public static final int STATUS_NETWORK_ERROR = 0x05; + public static final int STATUS_UNKNOWN_ERROR = 0x1F; + /* permanent-error codes */ + public static final int ERROR_PERMANENT = 0x03; + public static final int STATUS_CANCEL_FAILED = 0x06; + public static final int STATUS_BLOCKED_DESTINATION = 0x07; + public static final int STATUS_TEXT_TOO_LONG = 0x08; + public static final int STATUS_DUPLICATE_MESSAGE = 0x09; + public static final int STATUS_INVALID_DESTINATION = 0x0A; + public static final int STATUS_MESSAGE_EXPIRED = 0x0D; + /* undefined-status codes */ + public static final int ERROR_UNDEFINED = 0xFF; + public static final int STATUS_UNDEFINED = 0xFF; + + /** Bit-mask indicating used fields for SmsDataCoding */ + public int mask; + + /** + * 4-bit value indicating the message type in accordance to + * table 4.5.1-1 + * (See 3GPP2 C.S0015-B, v2, 4.5.1) + */ + public byte messageType; + + /** + * 16-bit value indicating the message ID, which increments modulo 65536. + * (Special rules apply for WAP-messages.) + * (See 3GPP2 C.S0015-B, v2, 4.5.1) + */ + public int messageID; + + /** + * 1-bit value that indicates whether a User Data Header is present. + * (See 3GPP2 C.S0015-B, v2, 4.5.1) + */ + public boolean hasUserDataHeader; + + /** + * provides the information for the user data + * (e.g. padding bits, user data, user data header, etc) + * (See 3GPP2 C.S.0015-B, v2, 4.5.2) + */ + public UserData userData; + + //public UserResponseCode userResponseCode; + + /** + * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4 + * year, month, day, hours, minutes, seconds; + */ + public byte[] timeStamp; + + //public SmsTime validityPeriodAbsolute; + //public SmsRelTime validityPeriodRelative; + //public SmsTime deferredDeliveryTimeAbsolute; + //public SmsRelTime deferredDeliveryTimeRelative; + //public byte priority; + //public byte privacy; + + /** + * Reply Option + * 1-bit values which indicate whether SMS acknowledgment is requested or not. + * (See 3GPP2 C.S0015-B, v2, 4.5.11) + */ + public boolean userAckReq; + public boolean deliveryAckReq; + public boolean readAckReq; + public boolean reportReq; + + /** + * The number of Messages element (8-bit value) is a decimal number in the 0 to 99 range + * representing the number of messages stored at the Voice Mail System. This element is + * used by the Voice Mail Notification service. + * (See 3GPP2 C.S0015-B, v2, 4.5.12) + */ + public int numberOfMessages; + + //public int alert; + //public int language; + + /** + * 4-bit or 8-bit value that indicates the number to be dialed in reply to a + * received SMS message. + * (See 3GPP2 C.S0015-B, v2, 4.5.15) + */ + public CdmaSmsAddress callbackNumber; + + /** + * 2-bit value that is used to indicate to the mobile station when to display + * the received message. + * (See 3GPP2 C.S0015-B, v2, 4.5.16) + */ + public byte displayMode = DISPLAY_DEFAULT; + + /** + * First component of the Message status, that indicates if an error has occurred + * and whether the error is considered permanent or temporary. + * (See 3GPP2 C.S0015-B, v2, 4.5.21) + */ + public int errorClass = ERROR_UNDEFINED; + + /** + * Second component of the Message status, that indicates if an error has occurred + * and the cause of the error. + * (See 3GPP2 C.S0015-B, v2, 4.5.21) + */ + public int messageStatus = STATUS_UNDEFINED; + +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java new file mode 100644 index 0000000..1643cab --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma.sms; + +import com.android.internal.telephony.SmsAddress; + +public class CdmaSmsAddress extends SmsAddress { + /** + * digit mode indicators + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + */ + static public final int DIGIT_MODE_4BIT_DTMF = 0x00; + static public final int DIGIT_MODE_8BIT_CHAR = 0x01; + + /** + * number mode indicators + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + */ + static public final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00; + static public final int NUMBER_MODE_DATA_NETWORK = 0x01; + + /** + * number types for data networks + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + */ + static public final int TON_UNKNOWN = 0x00; + static public final int TON_INTERNATIONAL_OR_IP = 0x01; + static public final int TON_NATIONAL_OR_EMAIL = 0x02; + static public final int TON_NETWORK = 0x03; + static public final int TON_SUBSCRIBER = 0x04; + static public final int TON_ALPHANUMERIC = 0x05; + static public final int TON_ABBREVIATED = 0x06; + static public final int TON_RESERVED = 0x07; + + /** + * maximum lengths for fields as defined in ril_cdma_sms.h + */ + static public final int SMS_ADDRESS_MAX = 36; + static public final int SMS_SUBADDRESS_MAX = 36; + + /** + * Supported numbering plan identification + * (See C.S005-D, v1.0, table 2.7.1.3.2.4-3) + */ + static public final int NUMBERING_PLAN_UNKNOWN = 0x0; + static public final int NUMBERING_PLAN_ISDN_TELEPHONY = 0x1; + //static protected final int NUMBERING_PLAN_DATA = 0x3; + //static protected final int NUMBERING_PLAN_TELEX = 0x4; + //static protected final int NUMBERING_PLAN_PRIVATE = 0x9; + + /** + * 1-bit value that indicates whether the address digits are 4-bit DTMF codes + * or 8-bit codes. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + */ + public byte digitMode; + + /** + * 1-bit value that indicates whether the address type is a data network address or not. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + */ + public byte numberMode; + + // use parent class member ton instead public byte numberType; + + /** + * 0 or 4-bit value that indicates which numbering plan identification is set. + * (See 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3) + */ + public byte numberPlan; + + /** + * This field shall be set to the number of address digits + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) + */ + public byte numberOfDigits; + + // use parent class member orig_bytes instead of public byte[] digits; + + // Constructor + public CdmaSmsAddress(){ + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsDataCoding.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsDataCoding.java new file mode 100644 index 0000000..6ba7463 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsDataCoding.java @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma.sms; + +import com.android.internal.telephony.SmsHeader; + +/* + * The SMSDataCoding class encodes and decodes CDMA SMS messages. + */ +public class SmsDataCoding { + private final static String TAG = "CDMA_SMS_JNI"; + + private final static int CDMA_SMS_WMS_MASK_BD_NULL = 0x00000000; + private final static int CDMA_SMS_WMS_MASK_BD_MSG_ID = 0x00000001; + private final static int CDMA_SMS_WMS_MASK_BD_USER_DATA = 0x00000002; +// private final static int CDMA_SMS_WMS_MASK_BD_USER_RESP = 0x00000004; + private final static int CDMA_SMS_WMS_MASK_BD_MC_TIME = 0x00000008; +// private final static int CDMA_SMS_WMS_MASK_BD_VALID_ABS = 0x00000010; +// private final static int CDMA_SMS_WMS_MASK_BD_VALID_REL = 0x00000020; +// private final static int CDMA_SMS_WMS_MASK_BD_DEFER_ABS = 0x00000040; +// private final static int CDMA_SMS_WMS_MASK_BD_DEFER_REL = 0x00000080; +// private final static int CDMA_SMS_WMS_MASK_BD_PRIORITY = 0x00000100; +// private final static int CDMA_SMS_WMS_MASK_BD_PRIVACY = 0x00000200; +// private final static int CDMA_SMS_WMS_MASK_BD_REPLY_OPTION = 0x00000400; + private final static int CDMA_SMS_WMS_MASK_BD_NUM_OF_MSGS = 0x00000800; +// private final static int CDMA_SMS_WMS_MASK_BD_ALERT = 0x00001000; +// private final static int CDMA_SMS_WMS_MASK_BD_LANGUAGE = 0x00002000; + private final static int CDMA_SMS_WMS_MASK_BD_CALLBACK = 0x00004000; + private final static int CDMA_SMS_WMS_MASK_BD_DISPLAY_MODE = 0x00008000; +// private final static int CDMA_SMS_WMS_MASK_BD_SCPT_DATA = 0x00010000; +// private final static int CDMA_SMS_WMS_MASK_BD_SCPT_RESULT = 0x00020000; +// private final static int CDMA_SMS_WMS_MASK_BD_DEPOSIT_INDEX = 0x00040000; +// private final static int CDMA_SMS_WMS_MASK_BD_DELIVERY_STATUS = 0x00080000; +// private final static int CDMA_SMS_WMS_MASK_BD_IP_ADDRESS = 0x10000000; +// private final static int CDMA_SMS_WMS_MASK_BD_RSN_NO_NOTIFY = 0x20000000; +// private final static int CDMA_SMS_WMS_MASK_BD_OTHER = 0x40000000; + + /** + * Successful operation. + */ + private static final int JNI_CDMA_SMS_SUCCESS = 0; + + /** + * General failure. + */ + private static final int JNI_CDMA_SMS_FAILURE = 1; + + /** + * Data length is out of length. + */ + private static final int JNI_CDMA_SMS_DATA_LEN_OUT_OF_RANGE = 2; + + /** + * Class name unknown. + */ + private static final int JNI_CDMA_SMS_CLASS_UNKNOWN = 3; + + /** + * Field ID unknown. + */ + private static final int JNI_CDMA_SMS_FIELD_ID_UNKNOWN = 4; + + /** + * Memory allocation failed. + */ + private static final int JNI_CDMA_SMS_OUT_OF_MEMORY = 5; + + /** + * Encode SMS. + * + * @param bearerData an instance of BearerData. + * + * @return the encoded SMS as byte[]. + */ + public static byte[] encodeCdmaSms(BearerData bearerData) { + byte[] encodedSms; + + if( nativeCdmaSmsConstructClientBD() == JNI_CDMA_SMS_FAILURE){ + return null; + } + + // check bearer data and generate bit mask + generateBearerDataBitMask(bearerData); + encodedSms = startEncoding(bearerData); + + if( nativeCdmaSmsDestructClientBD() == JNI_CDMA_SMS_FAILURE){ + return null; + } + return encodedSms; + } + + /** + * Decode SMS. + * + * @param SmsData the encoded SMS. + * + * @return an instance of BearerData. + */ + public static BearerData decodeCdmaSms(byte[] SmsData) { + BearerData bearerData; + + if( nativeCdmaSmsConstructClientBD() == JNI_CDMA_SMS_FAILURE){ + return null; + } + + bearerData = startDecoding(SmsData); + + if( nativeCdmaSmsDestructClientBD() == JNI_CDMA_SMS_FAILURE){ + return null; + } + return bearerData; + } + + private static void generateBearerDataBitMask(BearerData bearerData) { + // initial + bearerData.mask = CDMA_SMS_WMS_MASK_BD_NULL; + + // check message type + if (bearerData.messageType != 0){ + bearerData.mask |= CDMA_SMS_WMS_MASK_BD_MSG_ID; + } + + // check mUserData + if (bearerData.userData != null){ + bearerData.mask |= CDMA_SMS_WMS_MASK_BD_USER_DATA; + } + + // check mTimeStamp + if (bearerData.timeStamp != null){ + bearerData.mask |= CDMA_SMS_WMS_MASK_BD_MC_TIME; + } + + // check mNumberOfMessages + if (bearerData.numberOfMessages > 0){ + bearerData.mask |= CDMA_SMS_WMS_MASK_BD_NUM_OF_MSGS; + } + + // check mCallbackNumber + if(bearerData.callbackNumber != null){ + bearerData.mask |= CDMA_SMS_WMS_MASK_BD_CALLBACK; + } + + // check DisplayMode + if(bearerData.displayMode == BearerData.DISPLAY_DEFAULT || + bearerData.displayMode == BearerData.DISPLAY_IMMEDIATE || + bearerData.displayMode == BearerData.DISPLAY_USER){ + bearerData.mask |= CDMA_SMS_WMS_MASK_BD_DISPLAY_MODE; + } + } + + private static byte[] startEncoding(BearerData bearerData) { + int m_id; + byte[] m_data; + int dataLength; + byte[] encodedSms; + int nbrOfHeaders = 0; + + if( nativeCdmaSmsSetBearerDataPrimitives(bearerData) == JNI_CDMA_SMS_FAILURE){ + return null; + } + + if ((bearerData.mask & CDMA_SMS_WMS_MASK_BD_USER_DATA) == CDMA_SMS_WMS_MASK_BD_USER_DATA){ + if( nativeCdmaSmsSetUserData(bearerData.userData) == JNI_CDMA_SMS_FAILURE){ + return null; + } + + if (bearerData.userData.userDataHeader != null){ + nbrOfHeaders = bearerData.userData.userDataHeader.nbrOfHeaders; + } + + for (int i = 0; i < nbrOfHeaders; i++) { + m_id = bearerData.userData.userDataHeader.getElements().get(i).getID(); + m_data = bearerData.userData.userDataHeader.getElements().get(i).getData(); + dataLength = m_data.length; + if( nativeCdmaSmsSetUserDataHeader(m_id, m_data, dataLength, i) + == JNI_CDMA_SMS_FAILURE){ + return null; + } + } + } + + if ((bearerData.mask & CDMA_SMS_WMS_MASK_BD_CALLBACK) == CDMA_SMS_WMS_MASK_BD_CALLBACK) { + if( nativeCdmaSmsSetSmsAddress(bearerData.callbackNumber) == JNI_CDMA_SMS_FAILURE){ + return null; + } + } + + /* call native method to encode SMS */ + encodedSms = nativeCdmaSmsEncodeSms(); + + return encodedSms; + } + + private static BearerData startDecoding(byte[] SmsData) { + BearerData bData = new BearerData(); + byte[] udhData; + + /* call native method to decode SMS */ + if( nativeCdmaSmsDecodeSms(SmsData) == JNI_CDMA_SMS_FAILURE){ + return null; + } + + if( nativeCdmaSmsGetBearerDataPrimitives(bData) == JNI_CDMA_SMS_FAILURE){ + return null; + } + + if ((bData.mask & CDMA_SMS_WMS_MASK_BD_USER_DATA) == CDMA_SMS_WMS_MASK_BD_USER_DATA) { + bData.userData = new UserData(); + if( nativeCdmaSmsGetUserData(bData.userData) == JNI_CDMA_SMS_FAILURE){ + return null; + } + + udhData = nativeCdmaSmsGetUserDataHeader(); + if (udhData != null) { + bData.userData.userDataHeader = SmsHeader.parse(udhData); + } + } + + if ((bData.mask & CDMA_SMS_WMS_MASK_BD_CALLBACK) == CDMA_SMS_WMS_MASK_BD_CALLBACK) { + bData.callbackNumber = new CdmaSmsAddress(); + if( nativeCdmaSmsGetSmsAddress(bData.callbackNumber) == JNI_CDMA_SMS_FAILURE){ + return null; + } + } + + return bData; + } + + // native methods + + /** + * native method: Allocate memory for clientBD structure + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsConstructClientBD(); + + /** + * native method: Free memory used for clientBD structure + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsDestructClientBD(); + + /** + * native method: fill clientBD structure with bearerData primitives + * + * @param bearerData an instance of BearerData. + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsSetBearerDataPrimitives(BearerData bearerData); + + /** + * native method: fill bearerData primitives with clientBD variables + * + * @param bearerData an instance of BearerData. + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsGetBearerDataPrimitives(BearerData bearerData); + + /** + * native method: fill clientBD.user_data with UserData primitives + * + * @param userData an instance of UserData. + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsSetUserData(UserData userData); + + /** + * native method: fill UserData primitives with clientBD.user_data + * + * @param userData an instance of UserData. + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsGetUserData(UserData userData); + + /** + * native method: fill clientBD.user_data.headers with UserDataHeader primitives + * + * @param ID ID of element. + * @param data element data. + * @param dataLength data length + * @param index index of element + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsSetUserDataHeader( + int ID, byte[] data, int dataLength, int index); + + /** + * native method: fill UserDataHeader primitives with clientBD.user_data.headers + * + * @return user data headers + */ + private static native byte[] nativeCdmaSmsGetUserDataHeader(); + + /** + * native method: fill clientBD.callback with SmsAddress primitives + * + * @param smsAddr an instance of SmsAddress. + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsSetSmsAddress(CdmaSmsAddress smsAddr); + + /** + * native method: fill SmsAddress primitives with clientBD.callback + * + * @param smsAddr an instance of SmsAddress. + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsGetSmsAddress(CdmaSmsAddress smsAddr); + + /** + * native method: call encoding functions and get encoded SMS + * + * @return the encoded SMS + */ + private static native byte[] nativeCdmaSmsEncodeSms(); + + /** + * native method: call decode functions + * + * @param encodedSMS encoded SMS. + * + * @return #JNI_CDMA_SMS_SUCCESS if succeed. + * #JNI_CDMA_SMS_FAILURE if fail. + */ + private static native int nativeCdmaSmsDecodeSms(byte[] encodedSMS); + + /** + * Load the shared library to link the native methods. + */ + static { + try { + System.loadLibrary("cdma_sms_jni"); + } + catch (UnsatisfiedLinkError ule) { + System.err.println("WARNING: Could not load cdma_sms_jni.so"); + } + } +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java new file mode 100644 index 0000000..f80e8c0 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma.sms; + + +public final class SmsEnvelope{ + /** + * Message Types + * (See 3GPP2 C.S0015-B 3.4.1) + */ + static public final int MESSAGE_TYPE_POINT_TO_POINT = 0x00; + static public final int MESSAGE_TYPE_BROADCAST = 0x01; + static public final int MESSAGE_TYPE_ACKNOWLEDGE = 0x02; + + /** + * Supported Teleservices + * (See 3GPP2 N.S0005 and TIA-41) + */ + static public final int TELESERVICE_NOT_SET = 0x0000; + static public final int TELESERVICE_WMT = 0x1002; + static public final int TELESERVICE_VMN = 0x1003; + static public final int TELESERVICE_WAP = 0x1004; + static public final int TELESERVICE_WEMT = 0x1005; + + // ServiceCategories for Cell Broadcast, see 3GPP2 C.R1001 table 9.3.1-1 + //static public final int SERVICECATEGORY_EMERGENCY = 0x0010; + //... + + /** + * maximum lengths for fields as defined in ril_cdma_sms.h + */ + static public final int SMS_BEARER_DATA_MAX = 255; + + /** + * Provides the type of a SMS message like point to point, broadcast or acknowledge + */ + public int messageType; + + /** + * The 16-bit Teleservice parameter identifies which upper layer service access point is sending + * or receiving the message. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.1) + */ + public int teleService = TELESERVICE_NOT_SET; + + /** + * The 16-bit service category parameter identifies the type of service provided + * by the SMS message. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.2) + */ + public int serviceCategory; + + /** + * The origination address identifies the originator of the SMS message. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.4) + */ + public CdmaSmsAddress origAddress; + + /** + * The destination address identifies the target of the SMS message. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.4) + */ + public CdmaSmsAddress destAddress; + + /** + * The 6-bit bearer reply parameter is used to request the return of a + * SMS Acknowledge Message. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.5) + */ + public int bearerReply; + + /** + * Cause Code values: + * The cause code parameters are an indication whether an SMS error has occurred and if so, + * whether the condition is considered temporary or permanent. + * ReplySeqNo 6-bit value, + * ErrorClass 2-bit value, + * CauseCode 0-bit or 8-bit value + * (See 3GPP2 C.S0015-B, v2, 3.4.3.6) + */ + public byte replySeqNo; + public byte errorClass; + public byte causeCode; + + /** + * encoded bearer data + * (See 3GPP2 C.S0015-B, v2, 3.4.3.7) + */ + public byte[] bearerData; + + public SmsEnvelope() { + // nothing to see here + } + +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java new file mode 100644 index 0000000..e761469 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma.sms; + +import com.android.internal.telephony.SmsHeader; + +public class UserData{ + + /** + * Supported user data encoding types + * (See 3GPP2 C.R1001-F, v1.0, table 9.1-1) + */ + public static final int UD_ENCODING_OCTET = 0x00; + //public static final int UD_ENCODING_EXTENDED_PROTOCOL = 0x01; + public static final int UD_ENCODING_7BIT_ASCII = 0x02; + public static final int UD_ENCODING_IA5 = 0x03; + public static final int UD_ENCODING_UNICODE_16 = 0x04; + //public static final int UD_ENCODING_SHIFT_JIS = 0x05; + //public static final int UD_ENCODING_KOREAN = 0x06; + //public static final int UD_ENCODING_LATIN_HEBREW = 0x07; + //public static final int UD_ENCODING_LATIN = 0x08; + public static final int UD_ENCODING_GSM_7BIT_ALPHABET = 0x09; + //public static final int UD_ENCODING_GSM_DCS = 0x0A; + + /** + * Contains the data header of the user data + */ + public SmsHeader userDataHeader; + + /** + * Contains the data encoding type for the SMS message + */ + public int userDataEncoding; + + // needed when encoding is IS91 or DCS (not supported yet): + //public int messageType; + + /** + * Number of invalid bits in the last byte of data. + */ + public int paddingBits; + + /** + * Contains the user data of a SMS message + * (See 3GPP2 C.S0015-B, v2, 4.5.2) + */ + public byte[] userData; + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/package.html b/telephony/java/com/android/internal/telephony/cdma/sms/package.html new file mode 100644 index 0000000..48e1034 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/package.html @@ -0,0 +1,6 @@ + + +Provides CDMA-specific features for text/data/PDU SMS messages +@hide + + diff --git a/telephony/java/com/android/internal/telephony/gsm/AdnRecord.aidl b/telephony/java/com/android/internal/telephony/gsm/AdnRecord.aidl deleted file mode 100644 index 68d9a7c..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/AdnRecord.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package com.android.internal.telephony.gsm; - -parcelable AdnRecord; diff --git a/telephony/java/com/android/internal/telephony/gsm/AdnRecord.java b/telephony/java/com/android/internal/telephony/gsm/AdnRecord.java deleted file mode 100644 index 30df699..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/AdnRecord.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * 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.gsm; - -import com.android.internal.telephony.*; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.Message; -import android.os.Handler; -import android.os.Looper; -import android.os.AsyncResult; -import android.util.Log; -import android.telephony.PhoneNumberUtils; -import java.util.ArrayList; - -class AdnRecordLoader extends Handler -{ - static final String LOG_TAG = "GSM"; - - //***** Instance Variables - - GSMPhone phone; - int ef; - int extensionEF; - int pendingExtLoads; - Message userResponse; - String pin2; - - // For "load one" - int recordNumber; - - // for "load all" - ArrayList 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 - - AdnRecordLoader(GSMPhone phone) - { - // The telephony unit-test cases may create AdnRecords - // in secondary threads - super(phone.h.getLooper()); - - this.phone = phone; - } - - /** - * Resulting AdnRecord is placed in response.obj.result - * or response.obj.exception is set - */ - void - loadFromEF(int ef, int extensionEF, int recordNumber, - Message response) - { - this.ef = ef; - this.extensionEF = extensionEF; - this.recordNumber = recordNumber; - this.userResponse = response; - - phone.mSIMFileHandler.loadEFLinearFixed( - ef, recordNumber, - obtainMessage(EVENT_ADN_LOAD_DONE)); - - } - - - /** - * Resulting ArrayList<adnRecord> is placed in response.obj.result - * or response.obj.exception is set - */ - void - loadAllFromEF(int ef, int extensionEF, - Message response) - { - this.ef = ef; - this.extensionEF = extensionEF; - this.userResponse = response; - - phone.mSIMFileHandler.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 phoneNubmer - * @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 - */ - 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; - - phone.mSIMFileHandler.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("worong ADN format", - ar.exception); - } - - phone.mSIMFileHandler.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" + SimUtils.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; - - phone.mSIMFileHandler.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 extention EF: 0x" - + Integer.toHexString(extensionEF) - + ":" + adn.extRecord - + "\n" + SimUtils.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 datas = (ArrayList)(ar.result); - - if (ar.exception != null) { - throw new RuntimeException("load failed", ar.exception); - } - - adns = new ArrayList(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++; - - phone.mSIMFileHandler.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; - } - } - - -} - -/** - * - * 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 = ""; - String number = ""; - 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_DAILING_NUMBER_START = 2; - static final int ADN_DAILING_NUMBER_END = 11; - static final int ADN_CAPABILITY_ID = 12; - static final int ADN_EXTENSION_ID = 13; - - //***** Static Methods - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() - { - public AdnRecord createFromParcel(Parcel source) - { - int efid; - int recordNumber; - String alphaTag; - String number; - - efid = source.readInt(); - recordNumber = source.readInt(); - alphaTag = source.readString(); - number = source.readString(); - - return new AdnRecord(efid, recordNumber, alphaTag, number); - } - - 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 (int efid, int recordNumber, String alphaTag, String number) - { - this.efid = efid; - this.recordNumber = recordNumber; - this.alphaTag = alphaTag; - this.number = number; - } - - //***** Instance Methods - - public String getAlphaTag() - { - return alphaTag; - } - - public String getNumber() - { - return number; - } - - public String toString() - { - return "ADN Record '" + alphaTag + "' '" + number + "'"; - } - - public boolean isEmpty() - { - return alphaTag.equals("") && number.equals(""); - } - - public boolean hasExtendedRecord() - { - return extRecord != 0 && extRecord != 0xff; - } - - public boolean isEqual(AdnRecord adn) { - return ( alphaTag.equals(adn.getAlphaTag()) && - number.equals(adn.getNumber()) ); - } - //***** 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); - } - - /** - * 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 nulll for wrong format of dialing nubmer or tag - */ - public byte[] buildAdnString(int recordSize) { - byte[] bcdNumber; - byte[] byteTag; - byte[] adnString = null; - int footerOffset = recordSize - FOOTER_SIZE_BYTES; - - if (number == null || number.equals("") || - alphaTag == null || alphaTag.equals("")) { - - Log.w(LOG_TAG, "[buildAdnString] Empty alpha tag or number"); - adnString = new byte[recordSize]; - for (int i = 0; i < recordSize; i++) { - adnString[i] = (byte) 0xFF; - } - } else if (number.length() - > (ADN_DAILING_NUMBER_END - ADN_DAILING_NUMBER_START + 1) * 2) { - Log.w(LOG_TAG, - "[buildAdnString] Max length of dailing number is 20"); - } else if (alphaTag.length() > footerOffset) { - Log.w(LOG_TAG, - "[buildAdnString] Max length of tag is " + footerOffset); - } else { - - adnString = new byte[recordSize]; - for (int i = 0; i < recordSize; i++) { - adnString[i] = (byte) 0xFF; - } - - 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; // Capacility Id - adnString[footerOffset + ADN_EXTENSION_ID] - = (byte) 0xFF; // Extension Record Id - - 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 = SimUtils.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]; - - } catch (RuntimeException ex) { - Log.w(LOG_TAG, "Error parsing AdnRecord", ex); - number = ""; - alphaTag = ""; - } - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/AdnRecordCache.java b/telephony/java/com/android/internal/telephony/gsm/AdnRecordCache.java deleted file mode 100644 index 9da18e3..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/AdnRecordCache.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * 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.gsm; - -import android.util.SparseArray; -import android.util.Log; -import android.os.Message; -import android.os.Handler; -import android.os.AsyncResult; -import java.util.ArrayList; -import java.util.Iterator; - -/** - * {@hide} - */ -public final class AdnRecordCache extends Handler implements SimConstants -{ - //***** Instance Variables - - GSMPhone phone; - - // Indexed by EF ID - SparseArray> adnLikeFiles - = new SparseArray>(); - - // People waiting for ADN-like files to be loaded - SparseArray> adnLikeWaiters - = new SparseArray>(); - - // People waiting for adn record to be updated - SparseArray userWriteResponse = new SparseArray(); - - //***** Event Constants - - static final int EVENT_LOAD_ALL_ADN_LIKE_DONE = 1; - static final int EVENT_UPDATE_ADN_DONE = 2; - - //***** Constructor - - - /*package*/ - AdnRecordCache(GSMPhone phone) - { - this.phone = phone; - } - - //***** Called from SIMRecords - - /** - * Called from SIMRecords.onRadioNotAvailable and SIMRecords.handleSimRefresh. - */ - /*package*/ void - reset() - { - adnLikeFiles.clear(); - - clearWaiters(); - clearUserWriters(); - - } - - private void clearWaiters() { - int size = adnLikeWaiters.size(); - for (int i = 0; i < size; i++) { - ArrayList 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 - */ - /*package*/ ArrayList - 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 - */ - private 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; - 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 - */ - 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(phone).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 - */ - 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 oldAdnList; - oldAdnList = getRecordsIfLoaded(efid); - - if (oldAdnList == null) { - sendErrorResponse(response, "Adn list not exist for EF:" + efid); - return; - } - - int index = -1; - int count = 1; - for (Iterator 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; - } - - Message pendingResponse = userWriteResponse.get(efid); - - if (pendingResponse != null) { - sendErrorResponse(response, "Have pending update for EF:" + efid); - return; - } - - userWriteResponse.put(efid, response); - - new AdnRecordLoader(phone).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 - */ - /*package*/ void - requestLoadAllAdnLike (int efid, Message response) - { - ArrayList waiters; - ArrayList result; - - 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(); - waiters.add(response); - - adnLikeWaiters.put(efid, waiters); - - int extensionEF = extensionEfForEf(efid); - - 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(phone).loadAllFromEF(efid, extensionEF, - obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0)); - } - - //***** Private methods - - private void - notifyWaiters(ArrayList 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*/ - ar = (AsyncResult) msg.obj; - efid = msg.arg1; - ArrayList waiters; - - waiters = adnLikeWaiters.get(efid); - adnLikeWaiters.delete(efid); - - if (ar.exception == null) { - adnLikeFiles.put(efid, (ArrayList) (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); - } - - Message response = userWriteResponse.get(efid); - userWriteResponse.delete(efid); - - AsyncResult.forMessage(response, null, ar.exception); - response.sendToTarget(); - break; - } - - } - - -} diff --git a/telephony/java/com/android/internal/telephony/gsm/BaseCommands.java b/telephony/java/com/android/internal/telephony/gsm/BaseCommands.java deleted file mode 100644 index 8e14b43..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/BaseCommands.java +++ /dev/null @@ -1,367 +0,0 @@ -/* - * 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.gsm; - -import android.content.Context; -import android.os.RegistrantList; -import android.os.Registrant; -import android.os.Handler; -import android.os.AsyncResult; -import android.os.SystemProperties; -import android.provider.Checkin; -import android.util.Config; -import android.util.Log; - -/** - * {@hide} - */ -public abstract class BaseCommands implements CommandsInterface -{ - static final String LOG_TAG = "GSM"; - - //***** Instance Variables - protected Context mContext; - protected RadioState mState = RadioState.RADIO_UNAVAILABLE; - protected Object mStateMonitor = new Object(); - - protected RegistrantList mRadioStateChangedRegistrants = new RegistrantList(); - protected RegistrantList mOnRegistrants = new RegistrantList(); - protected RegistrantList mAvailRegistrants = new RegistrantList(); - protected RegistrantList mOffOrNotAvailRegistrants = new RegistrantList(); - protected RegistrantList mNotAvailRegistrants = new RegistrantList(); - protected RegistrantList mSIMReadyRegistrants = new RegistrantList(); - protected RegistrantList mSIMLockedRegistrants = new RegistrantList(); - protected RegistrantList mCallStateRegistrants = new RegistrantList(); - protected RegistrantList mNetworkStateRegistrants = new RegistrantList(); - protected RegistrantList mPDPRegistrants = new RegistrantList(); - protected Registrant mSMSRegistrant; - protected Registrant mNITZTimeRegistrant; - protected Registrant mSignalStrengthRegistrant; - protected Registrant mUSSDRegistrant; - protected Registrant mSmsOnSimRegistrant; - /** Registrant for handling SMS Status Reports */ - protected Registrant mSmsStatusRegistrant; - /** Registrant for handling Supplementary Service Notifications */ - protected Registrant mSsnRegistrant; - protected Registrant mStkSessionEndRegistrant; - protected Registrant mStkProCmdRegistrant; - protected Registrant mStkEventRegistrant; - protected Registrant mStkCallSetUpRegistrant; - /** Registrant for handling SIM SMS storage full messages */ - protected Registrant mSimSmsFullRegistrant; - /** Registrant for handling SIM Refresh notifications */ - protected Registrant mSimRefreshRegistrant; - /** Registrant for handling RING notifications */ - protected Registrant mRingRegistrant; - /** Registrant for handling RESTRICTED STATE changed notification */ - protected Registrant mRestrictedStateRegistrant; - - public BaseCommands(Context context) { - mContext = context; // May be null (if so we won't log statistics) - } - - //***** CommandsInterface implementation - - public RadioState - getRadioState() - { - return mState; - } - - - public void - registerForRadioStateChanged(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - synchronized (mStateMonitor) { - mRadioStateChangedRegistrants.add(r); - r.notifyRegistrant(); - } - } - - public void - registerForOn(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - synchronized (mStateMonitor) { - mOnRegistrants.add(r); - - if (mState.isOn()) { - r.notifyRegistrant(new AsyncResult(null, null, null)); - } - } - } - - - public void - registerForAvailable(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - synchronized (mStateMonitor) { - mAvailRegistrants.add(r); - - if (mState.isAvailable()) { - r.notifyRegistrant(new AsyncResult(null, null, null)); - } - } - } - - public void - registerForNotAvailable(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - synchronized (mStateMonitor) { - mNotAvailRegistrants.add(r); - - if (!mState.isAvailable()) { - r.notifyRegistrant(new AsyncResult(null, null, null)); - } - } - } - - public void - registerForOffOrNotAvailable(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - synchronized (mStateMonitor) { - mOffOrNotAvailRegistrants.add(r); - - if (mState == RadioState.RADIO_OFF || !mState.isAvailable()) { - r.notifyRegistrant(new AsyncResult(null, null, null)); - } - } - } - - - /** Any transition into SIM_READY */ - public void - registerForSIMReady(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - synchronized (mStateMonitor) { - mSIMReadyRegistrants.add(r); - - if (mState.isSIMReady()) { - r.notifyRegistrant(new AsyncResult(null, null, null)); - } - } - } - - public void - registerForSIMLockedOrAbsent(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - synchronized (mStateMonitor) { - mSIMLockedRegistrants.add(r); - - if (mState == RadioState.SIM_LOCKED_OR_ABSENT) { - r.notifyRegistrant(new AsyncResult(null, null, null)); - } - } - } - - public void - registerForCallStateChanged(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - mCallStateRegistrants.add(r); - } - - public void - registerForNetworkStateChanged(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - mNetworkStateRegistrants.add(r); - } - - public void - registerForPDPStateChanged(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - mPDPRegistrants.add(r); - } - - public void - setOnNewSMS(Handler h, int what, Object obj) - { - mSMSRegistrant = new Registrant (h, what, obj); - } - - public void - setOnSmsOnSim(Handler h, int what, Object obj) - { - mSmsOnSimRegistrant = new Registrant (h, what, obj); - } - - public void setOnSmsStatus(Handler h, int what, Object obj) { - mSmsStatusRegistrant = new Registrant (h, what, obj); - } - - public void - setOnSignalStrengthUpdate(Handler h, int what, Object obj) - { - mSignalStrengthRegistrant = new Registrant (h, what, obj); - } - - public void - setOnNITZTime(Handler h, int what, Object obj) - { - mNITZTimeRegistrant = new Registrant (h, what, obj); - } - - public void - setOnUSSD(Handler h, int what, Object obj) - { - mUSSDRegistrant = new Registrant (h, what, obj); - } - - public void - setOnSuppServiceNotification(Handler h, int what, Object obj) - { - mSsnRegistrant = new Registrant (h, what, obj); - } - - public void - setOnStkSessionEnd(Handler h, int what, Object obj) - { - mStkSessionEndRegistrant = new Registrant (h, what, obj); - } - - public void - setOnStkProactiveCmd(Handler h, int what, Object obj) - { - mStkProCmdRegistrant = new Registrant (h, what, obj); - } - - public void - setOnStkEvent(Handler h, int what, Object obj) - { - mStkEventRegistrant = new Registrant (h, what, obj); - } - - public void - setOnStkCallSetUp(Handler h, int what, Object obj) - { - mStkCallSetUpRegistrant = new Registrant (h, what, obj); - } - - public void setOnSimSmsFull(Handler h, int what, Object obj) { - mSimSmsFullRegistrant = new Registrant (h, what, obj); - } - - public void setOnSimRefresh(Handler h, int what, Object obj) { - mSimRefreshRegistrant = new Registrant (h, what, obj); - } - - public void setOnCallRing(Handler h, int what, Object obj) { - mRingRegistrant = new Registrant (h, what, obj); - } - - public void - setOnRestrictedStateChanged(Handler h, int what, Object obj) - { - mRestrictedStateRegistrant = new Registrant (h, what, obj); - } - - //***** Protected Methods - /** - * Store new RadioState and send notification based on the changes - * - * This function is called only by RIL.java when receiving unsolicited - * RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED - * - * RadioState has 5 values : RADIO_OFF, RADIO_UNAVAILABLE, SIM_NOT_READY, - * SIM_LOCKED_OR_ABSENT, and SIM_READY. - * - * @param newState new RadioState decoded from RIL_UNSOL_RADIO_STATE_CHANGED - */ - protected void setRadioState(RadioState newState) { - RadioState oldState; - - synchronized (mStateMonitor) { - if (Config.LOGV) { - Log.v(LOG_TAG, "setRadioState old: " + mState - + " new " + newState); - } - - oldState = mState; - mState = newState; - - if (oldState == mState) { - // no state transition - return; - } - - if (mContext != null && - newState == RadioState.RADIO_UNAVAILABLE && - oldState != RadioState.RADIO_OFF) { - Checkin.updateStats(mContext.getContentResolver(), - Checkin.Stats.Tag.PHONE_RADIO_RESETS, 1, 0.0); - } - - mRadioStateChangedRegistrants.notifyRegistrants(); - - if (mState.isAvailable() && !oldState.isAvailable()) { - Log.d(LOG_TAG,"Notifying: radio available"); - mAvailRegistrants.notifyRegistrants(); - onRadioAvailable(); - } - - if (!mState.isAvailable() && oldState.isAvailable()) { - Log.d(LOG_TAG,"Notifying: radio not available"); - mNotAvailRegistrants.notifyRegistrants(); - } - - if (mState.isSIMReady() && !oldState.isSIMReady()) { - Log.d(LOG_TAG,"Notifying: SIM ready"); - mSIMReadyRegistrants.notifyRegistrants(); - } - - if (mState == RadioState.SIM_LOCKED_OR_ABSENT) { - Log.d(LOG_TAG,"Notifying: SIM locked or absent"); - mSIMLockedRegistrants.notifyRegistrants(); - } - - if (mState.isOn() && !oldState.isOn()) { - Log.d(LOG_TAG,"Notifying: Radio On"); - mOnRegistrants.notifyRegistrants(); - } - - if ((!mState.isOn() || !mState.isAvailable()) - && !((!oldState.isOn() || !oldState.isAvailable())) - ) { - Log.d(LOG_TAG,"Notifying: radio off or not available"); - mOffOrNotAvailRegistrants.notifyRegistrants(); - } - } - } - - protected void - onRadioAvailable() - { - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/CallForwardInfo.java b/telephony/java/com/android/internal/telephony/gsm/CallForwardInfo.java deleted file mode 100644 index bf31b13..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/CallForwardInfo.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.gsm; - -import android.telephony.PhoneNumberUtils; - -/** - * See also RIL_CallForwardInfo in include/telephony/ril.h - * - * {@hide} - */ -public class CallForwardInfo -{ - public int status; /*1 = active, 0 = not active */ - public int reason; /* from TS 27.007 7.11 "reason" */ - public int serviceClass; /* Sum of CommandsInterface.SERVICE_CLASS */ - public int toa; /* "type" from TS 27.007 7.11 */ - public String number; /* "number" from TS 27.007 7.11 */ - public int timeSeconds; /* for CF no reply only */ - - public String toString() - { - return super.toString() + (status == 0 ? " not active " : " active ") - + " reason: " + reason - + " serviceClass: " + serviceClass - + " \"" + PhoneNumberUtils.stringFromStringAndTOA(number, toa) + "\" " - + timeSeconds + " seconds"; - - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/CallTracker.java b/telephony/java/com/android/internal/telephony/gsm/CallTracker.java deleted file mode 100644 index 2d716bb..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/CallTracker.java +++ /dev/null @@ -1,984 +0,0 @@ -/* - * 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.gsm; - -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_EDGE; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_GPRS; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UMTS; -import static com.android.internal.telephony.gsm.ServiceStateTracker.DATA_ACCESS_UNKNOWN; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.Message; -import android.os.Registrant; -import android.os.RegistrantList; -import android.os.SystemProperties; -import android.telephony.PhoneNumberUtils; -import android.telephony.ServiceState; -import android.telephony.TelephonyManager; -import android.telephony.gsm.GsmCellLocation; -import android.util.EventLog; -import android.util.Log; - -import com.android.internal.telephony.Call; -import com.android.internal.telephony.CallStateException; -import com.android.internal.telephony.Connection; -import com.android.internal.telephony.Phone; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@hide} - */ -public final class CallTracker extends Handler -{ - static final String LOG_TAG = "GSM"; - private static final boolean REPEAT_POLLING = false; - - private static final boolean DBG_POLL = false; - - //***** Constants - - static final int POLL_DELAY_MSEC = 250; - static final int MAX_CONNECTIONS = 7; // only 7 connections allowed in GSM - static final int MAX_CONNECTIONS_PER_CALL = 5; // only 5 connections allowed per call - - //***** Instance Variables - - GSMConnection connections[] = new GSMConnection[MAX_CONNECTIONS]; - RegistrantList voiceCallEndedRegistrants = new RegistrantList(); - RegistrantList voiceCallStartedRegistrants = new RegistrantList(); - - - // connections dropped durin last poll - ArrayList droppedDuringPoll - = new ArrayList(MAX_CONNECTIONS); - - GSMCall ringingCall = new GSMCall(this); - // A call that is ringing or (call) waiting - GSMCall foregroundCall = new GSMCall(this); - GSMCall backgroundCall = new GSMCall(this); - - GSMConnection pendingMO; - boolean hangupPendingMO; - - GSMPhone phone; - CommandsInterface cm; - boolean desiredMute = false; // false = mute off - - Phone.State state = Phone.State.IDLE; - - int pendingOperations; - boolean needsPoll; - Message lastRelevantPoll; - - - //***** Events - - static final int EVENT_POLL_CALLS_RESULT = 1; - static final int EVENT_CALL_STATE_CHANGE = 2; - static final int EVENT_REPOLL_AFTER_DELAY = 3; - static final int EVENT_OPERATION_COMPLETE = 4; - static final int EVENT_GET_LAST_CALL_FAIL_CAUSE = 5; - - static final int EVENT_SWITCH_RESULT = 8; - static final int EVENT_RADIO_AVAILABLE = 9; - static final int EVENT_RADIO_NOT_AVAILABLE = 10; - static final int EVENT_CONFERENCE_RESULT = 11; - static final int EVENT_SEPARATE_RESULT = 12; - static final int EVENT_ECT_RESULT = 13; - - //***** Constructors - - CallTracker (GSMPhone phone) - { - this.phone = phone; - cm = phone.mCM; - - cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); - - cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null); - cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); - } - - //***** Instance Methods - - //***** Public Methods - public void registerForVoiceCallStarted(Handler h, int what, Object obj) - { - Registrant r = new Registrant(h, what, obj); - voiceCallStartedRegistrants.add(r); - } - - public void registerForVoiceCallEnded(Handler h, int what, Object obj) - { - Registrant r = new Registrant(h, what, obj); - voiceCallEndedRegistrants.add(r); - } - - private void - fakeHoldForegroundBeforeDial() - { - List connCopy; - - // We need to make a copy here, since fakeHoldBeforeDial() - // modifies the lists, and we don't want to reverse the order - connCopy = (List) foregroundCall.connections.clone(); - - for (int i = 0, s = connCopy.size() ; i < s ; i++) { - GSMConnection conn = (GSMConnection)connCopy.get(i); - - conn.fakeHoldBeforeDial(); - } - } - - /** - * clirMode is one of the CLIR_ constants - */ - Connection - dial (String dialString, int clirMode) throws CallStateException { - // note that this triggers call state changed notif - clearDisconnected(); - - if (!canDial()) { - throw new CallStateException("cannot dial in current state"); - } - - // The new call must be assigned to the foreground call. - // That call must be idle, so place anything that's - // there on hold - if (foregroundCall.getState() == Call.State.ACTIVE) { - // this will probably be done by the radio anyway - // but the dial might fail before this happens - // and we need to make sure the foreground call is clear - // for the newly dialed connection - switchWaitingOrHoldingAndActive(); - - // Fake local state so that - // a) foregroundCall is empty for the newly dialed connection - // b) hasNonHangupStateChanged remains false in the - // next poll, so that we don't clear a failed dialing call - fakeHoldForegroundBeforeDial(); - } - - if (foregroundCall.getState() != Call.State.IDLE) { - //we should have failed in !canDial() above before we get here - throw new CallStateException("cannot dial in current state"); - } - - pendingMO = new GSMConnection(phone.getContext(), dialString, this, foregroundCall); - hangupPendingMO = false; - - if (pendingMO.address == null || pendingMO.address.length() == 0 - || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0 - ) { - // Phone number is invalid - pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER; - - // handlePollCalls() will notice this call not present - // and will mark it as dropped. - pollCallsWhenSafe(); - } else { - // Always unmute when initiating a new call - setMute(false); - - cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); - } - - updatePhoneState(); - phone.notifyCallStateChanged(); - - return pendingMO; - } - - - Connection - dial (String dialString) throws CallStateException - { - return dial(dialString, CommandsInterface.CLIR_DEFAULT); - } - - void - acceptCall () throws CallStateException - { - // FIXME if SWITCH fails, should retry with ANSWER - // in case the active/holding call disappeared and this - // is no longer call waiting - - if (ringingCall.getState() == Call.State.INCOMING) { - Log.i("phone", "acceptCall: incoming..."); - // Always unmute when answering a new call - setMute(false); - cm.acceptCall(obtainCompleteMessage()); - } else if (ringingCall.getState() == Call.State.WAITING) { - setMute(false); - switchWaitingOrHoldingAndActive(); - } else { - throw new CallStateException("phone not ringing"); - } - } - - void - rejectCall () throws CallStateException - { - // AT+CHLD=0 means "release held or UDUB" - // so if the phone isn't ringing, this could hang up held - if (ringingCall.getState().isRinging()) { - cm.rejectCall(obtainCompleteMessage()); - } else { - throw new CallStateException("phone not ringing"); - } - } - - void - switchWaitingOrHoldingAndActive() throws CallStateException { - // Should we bother with this check? - if (ringingCall.getState() == Call.State.INCOMING) { - throw new CallStateException("cannot be in the incoming state"); - } else { - cm.switchWaitingOrHoldingAndActive( - obtainCompleteMessage(EVENT_SWITCH_RESULT)); - } - } - - void - conference() throws CallStateException - { - cm.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT)); - } - - void - explicitCallTransfer() throws CallStateException - { - cm.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); - } - - void - clearDisconnected() - { - internalClearDisconnected(); - - updatePhoneState(); - phone.notifyCallStateChanged(); - } - - boolean - canConference() - { - return foregroundCall.getState() == Call.State.ACTIVE - && backgroundCall.getState() == Call.State.HOLDING - && !backgroundCall.isFull() - && !foregroundCall.isFull(); - } - - boolean - canDial() - { - boolean ret; - int serviceState = phone.getServiceState().getState(); - - ret = (serviceState != ServiceState.STATE_POWER_OFF) && - pendingMO == null - && !ringingCall.isRinging() - && (!foregroundCall.getState().isAlive() - || !backgroundCall.getState().isAlive()); - - return ret; - } - - boolean - canTransfer() - { - return foregroundCall.getState() == Call.State.ACTIVE - && backgroundCall.getState() == Call.State.HOLDING; - } - - //***** Private Instance Methods - - private void - internalClearDisconnected() - { - ringingCall.clearDisconnected(); - foregroundCall.clearDisconnected(); - backgroundCall.clearDisconnected(); - } - - /** - * @return true if we're idle or there's a call to getCurrentCalls() pending - * but nothing else - */ - private boolean - checkNoOperationsPending() - { - if (DBG_POLL) log("checkNoOperationsPending: pendingOperations=" + - pendingOperations); - return pendingOperations == 0; - } - - - /** - * Obtain a message to use for signalling "invoke getCurrentCalls() when - * this operation and all other pending operations are complete - */ - private Message - obtainCompleteMessage() - { - return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); - } - - /** - * Obtain a message to use for signalling "invoke getCurrentCalls() when - * this operation and all other pending operations are complete - */ - private Message - obtainCompleteMessage(int what) - { - pendingOperations++; - lastRelevantPoll = null; - needsPoll = true; - - if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + - pendingOperations + ", needsPoll=" + needsPoll); - - return obtainMessage(what); - } - - /** - * Obtain a complete message that indicates that this operation - * does not require polling of getCurrentCalls(). However, if other - * operations that do need getCurrentCalls() are pending or are - * scheduled while this operation is pending, the invocatoin - * of getCurrentCalls() will be postponed until this - * operation is also complete. - */ - private Message - obtainNoPollCompleteMessage(int what) - { - pendingOperations++; - lastRelevantPoll = null; - return obtainMessage(what); - } - - - private void - operationComplete() - { - pendingOperations--; - - if (DBG_POLL) log("operationComplete: pendingOperations=" + - pendingOperations + ", needsPoll=" + needsPoll); - - if (pendingOperations == 0 && needsPoll) { - lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); - cm.getCurrentCalls(lastRelevantPoll); - } else if (pendingOperations < 0) { - // this should never happen - Log.e(LOG_TAG,"CallTracker.pendingOperations < 0"); - pendingOperations = 0; - } - } - - private void - pollCallsWhenSafe() - { - needsPoll = true; - - if (checkNoOperationsPending()) { - lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); - cm.getCurrentCalls(lastRelevantPoll); - } - } - - private void - pollCallsAfterDelay() - { - Message msg = obtainMessage(); - - msg.what = EVENT_REPOLL_AFTER_DELAY; - sendMessageDelayed(msg, POLL_DELAY_MSEC); - } - - private boolean - isCommandExceptionRadioNotAvailable(Throwable e) - { - return e != null && e instanceof CommandException - && ((CommandException)e).getCommandError() - == CommandException.Error.RADIO_NOT_AVAILABLE; - } - - private void - updatePhoneState() - { - Phone.State oldState = state; - - if (ringingCall.isRinging()) { - state = Phone.State.RINGING; - } else if (pendingMO != null || - !(foregroundCall.isIdle() && backgroundCall.isIdle())) { - state = Phone.State.OFFHOOK; - } else { - state = Phone.State.IDLE; - } - - if (state == Phone.State.IDLE && oldState != state) { - voiceCallEndedRegistrants.notifyRegistrants( - new AsyncResult(null, null, null)); - } else if (oldState == Phone.State.IDLE && oldState != state) { - voiceCallStartedRegistrants.notifyRegistrants ( - new AsyncResult(null, null, null)); - } - - if (state != oldState) { - phone.notifyPhoneStateChanged(); - } - } - - private void - handlePollCalls(AsyncResult ar) - { - List polledCalls; - - if (ar.exception == null) { - polledCalls = (List)ar.result; - } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { - // just a dummy empty ArrayList to cause the loop - // to hang up all the calls - polledCalls = new ArrayList(); - } else { - // Radio probably wasn't ready--try again in a bit - // But don't keep polling if the channel is closed - pollCallsAfterDelay(); - return; - } - - Connection newRinging = null; //or waiting - boolean hasNonHangupStateChanged = false; // Any change besides - // a dropped connection - boolean needsPollDelay = false; - boolean unknownConnectionAppeared = false; - - for (int i = 0, curDC = 0, dcSize = polledCalls.size() - ; i < connections.length; i++) { - GSMConnection conn = connections[i]; - DriverCall dc = null; - - // polledCall list is sparse - if (curDC < dcSize) { - dc = (DriverCall) polledCalls.get(curDC); - - if (dc.index == i+1) { - curDC++; - } else { - dc = null; - } - } - - if (DBG_POLL) log("poll: conn[i=" + i + "]=" + - conn+", dc=" + dc); - - if (conn == null && dc != null) { - // Connection appeared in CLCC response that we don't know about - if (pendingMO != null && pendingMO.compareTo(dc)) { - - if (DBG_POLL) log("poll: pendingMO=" + pendingMO); - - // It's our pending mobile originating call - connections[i] = pendingMO; - pendingMO.index = i; - pendingMO.update(dc); - pendingMO = null; - - // Someone has already asked to hangup this call - if (hangupPendingMO) { - hangupPendingMO = false; - try { - if (Phone.DEBUG_PHONE) log( - "poll: hangupPendingMO, hangup conn " + i); - hangup(connections[i]); - } catch (CallStateException ex) { - Log.e(LOG_TAG, "unexpected error on hangup"); - } - - // Do not continue processing this poll - // Wait for hangup and repoll - return; - } - } else { - connections[i] = new GSMConnection(phone.getContext(), dc, this, i); - - // it's a ringing call - if (connections[i].getCall() == ringingCall) { - newRinging = connections[i]; - } else { - // Something strange happened: a call appeared - // which is neither a ringing call or one we created. - // Either we've crashed and re-attached to an existing - // call, or something else (eg, SIM) initiated the call. - - Log.i(LOG_TAG,"Phantom call appeared " + dc); - - // If it's a connected call, set the connect time so that - // it's non-zero. It may not be accurate, but at least - // it won't appear as a Missed Call. - if (dc.state != DriverCall.State.ALERTING - && dc.state != DriverCall.State.DIALING) { - connections[i].connectTime = System.currentTimeMillis(); - } - - unknownConnectionAppeared = true; - } - } - hasNonHangupStateChanged = true; - } else if (conn != null && dc == null) { - // Connection missing in CLCC response that we were - // tracking. - droppedDuringPoll.add(conn); - // Dropped connections are removed from the CallTracker - // list but kept in the GSMCall list - connections[i] = null; - } else if (conn != null && dc != null && !conn.compareTo(dc)) { - // Connection in CLCC response does not match what - // we were tracking. Assume dropped call and new call - - droppedDuringPoll.add(conn); - connections[i] = new GSMConnection (phone.getContext(), dc, this, i); - - if (connections[i].getCall() == ringingCall) { - newRinging = connections[i]; - } // else something strange happened - hasNonHangupStateChanged = true; - } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ - boolean changed; - changed = conn.update(dc); - hasNonHangupStateChanged = hasNonHangupStateChanged || changed; - } - - if (REPEAT_POLLING) { - if (dc != null) { - // FIXME with RIL, we should not need this anymore - if ((dc.state == DriverCall.State.DIALING - /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) - || (dc.state == DriverCall.State.ALERTING - /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) - || (dc.state == DriverCall.State.INCOMING - /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) - || (dc.state == DriverCall.State.WAITING - /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/) - ) { - // Sometimes there's no unsolicited notification - // for state transitions - needsPollDelay = true; - } - } - } - } - - // This is the first poll after an ATD. - // We expect the pending call to appear in the list - // If it does not, we land here - if (pendingMO != null) { - Log.d(LOG_TAG,"Pending MO dropped before poll fg state:" - + foregroundCall.getState()); - - droppedDuringPoll.add(pendingMO); - pendingMO = null; - hangupPendingMO = false; - } - - if (newRinging != null) { - phone.notifyNewRingingConnection(newRinging); - } - - // clear the "local hangup" and "missed/rejected call" - // cases from the "dropped during poll" list - // These cases need no "last call fail" reason - for (int i = droppedDuringPoll.size() - 1; i >= 0 ; i--) { - GSMConnection conn = droppedDuringPoll.get(i); - - if (conn.isIncoming() && conn.getConnectTime() == 0) { - // Missed or rejected call - Connection.DisconnectCause cause; - if (conn.cause == Connection.DisconnectCause.LOCAL) { - cause = Connection.DisconnectCause.INCOMING_REJECTED; - } else { - cause = Connection.DisconnectCause.INCOMING_MISSED; - } - - if (Phone.DEBUG_PHONE) { - log("missed/rejected call, conn.cause=" + conn.cause); - log("setting cause to " + cause); - } - droppedDuringPoll.remove(i); - conn.onDisconnect(cause); - } else if (conn.cause == Connection.DisconnectCause.LOCAL) { - // Local hangup - droppedDuringPoll.remove(i); - conn.onDisconnect(Connection.DisconnectCause.LOCAL); - } else if (conn.cause == - Connection.DisconnectCause.INVALID_NUMBER) { - droppedDuringPoll.remove(i); - conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER); - } - } - - // Any non-local disconnects: determine cause - if (droppedDuringPoll.size() > 0) { - cm.getLastCallFailCause( - obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); - } - - if (needsPollDelay) { - pollCallsAfterDelay(); - } - - // Cases when we can no longer keep disconnected Connection's - // with their previous calls - // 1) the phone has started to ring - // 2) A Call/Connection object has changed state... - // we may have switched or held or answered (but not hung up) - if (newRinging != null || hasNonHangupStateChanged) { - internalClearDisconnected(); - } - - updatePhoneState(); - - if (unknownConnectionAppeared) { - phone.notifyUnknownConnection(); - } - - if (hasNonHangupStateChanged || newRinging != null) { - phone.notifyCallStateChanged(); - } - - //dumpState(); - } - - private void - handleRadioAvailable() - { - pollCallsWhenSafe(); - } - - private void - handleRadioNotAvailable() - { - // handlePollCalls will clear out its - // call list when it gets the CommandException - // error result from this - pollCallsWhenSafe(); - } - - private void - dumpState() - { - List l; - - Log.i(LOG_TAG,"Phone State:" + state); - - Log.i(LOG_TAG,"Ringing call: " + ringingCall.toString()); - - l = ringingCall.getConnections(); - for (int i = 0, s = l.size(); i < s; i++) { - Log.i(LOG_TAG,l.get(i).toString()); - } - - Log.i(LOG_TAG,"Foreground call: " + foregroundCall.toString()); - - l = foregroundCall.getConnections(); - for (int i = 0, s = l.size(); i < s; i++) { - Log.i(LOG_TAG,l.get(i).toString()); - } - - Log.i(LOG_TAG,"Background call: " + backgroundCall.toString()); - - l = backgroundCall.getConnections(); - for (int i = 0, s = l.size(); i < s; i++) { - Log.i(LOG_TAG,l.get(i).toString()); - } - - } - - //***** Called from GSMConnection - - /*package*/ void - hangup (GSMConnection conn) throws CallStateException - { - if (conn.owner != this) { - throw new CallStateException ("Connection " + conn - + "does not belong to CallTracker " + this); - } - - if (conn == pendingMO) { - // We're hanging up an outgoing call that doesn't have it's - // GSM index assigned yet - - if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); - hangupPendingMO = true; - } else { - try { - cm.hangupConnection (conn.getGSMIndex(), obtainCompleteMessage()); - } catch (CallStateException ex) { - // Ignore "connection not found" - // Call may have hung up already - Log.w(LOG_TAG,"CallTracker WARN: hangup() on absent connection " - + conn); - } - } - - conn.onHangupLocal(); - } - - /*package*/ void - separate (GSMConnection conn) throws CallStateException - { - if (conn.owner != this) { - throw new CallStateException ("Connection " + conn - + "does not belong to CallTracker " + this); - } - try { - cm.separateConnection (conn.getGSMIndex(), - obtainCompleteMessage(EVENT_SEPARATE_RESULT)); - } catch (CallStateException ex) { - // Ignore "connection not found" - // Call may have hung up already - Log.w(LOG_TAG,"CallTracker WARN: separate() on absent connection " - + conn); - } - } - - //***** Called from GSMPhone - - /*package*/ void - setMute(boolean mute) - { - desiredMute = mute; - cm.setMute(desiredMute, null); - } - - /*package*/ boolean - getMute() - { - return desiredMute; - } - - - //***** Called from GSMCall - - /* package */ void - hangup (GSMCall call) throws CallStateException - { - if (call.getConnections().size() == 0) { - throw new CallStateException("no connections in call"); - } - - if (call == ringingCall) { - if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); - cm.hangupWaitingOrBackground(obtainCompleteMessage()); - } else if (call == foregroundCall) { - if (call.isDialingOrAlerting()) { - if (Phone.DEBUG_PHONE) { - log("(foregnd) hangup dialing or alerting..."); - } - hangup((GSMConnection)(call.getConnections().get(0))); - } else { - hangupForegroundResumeBackground(); - } - } else if (call == backgroundCall) { - if (ringingCall.isRinging()) { - if (Phone.DEBUG_PHONE) { - log("hangup all conns in background call"); - } - hangupAllConnections(call); - } else { - hangupWaitingOrBackground(); - } - } else { - throw new RuntimeException ("Call " + call + - "does not belong to CallTracker " + this); - } - - call.onHangupLocal(); - } - - /* package */ - void hangupWaitingOrBackground() { - if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); - cm.hangupWaitingOrBackground(obtainCompleteMessage()); - } - - /* package */ - void hangupForegroundResumeBackground() { - if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); - cm.hangupForegroundResumeBackground(obtainCompleteMessage()); - } - - void hangupConnectionByIndex(GSMCall call, int index) - throws CallStateException { - int count = call.connections.size(); - for (int i = 0; i < count; i++) { - GSMConnection cn = (GSMConnection)call.connections.get(i); - if (cn.getGSMIndex() == index) { - cm.hangupConnection(index, obtainCompleteMessage()); - return; - } - } - - throw new CallStateException("no gsm index found"); - } - - void hangupAllConnections(GSMCall call) throws CallStateException{ - try { - int count = call.connections.size(); - for (int i = 0; i < count; i++) { - GSMConnection cn = (GSMConnection)call.connections.get(i); - cm.hangupConnection(cn.getGSMIndex(), obtainCompleteMessage()); - } - } catch (CallStateException ex) { - Log.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); - } - } - - /* package */ - GSMConnection getConnectionByIndex(GSMCall call, int index) - throws CallStateException { - int count = call.connections.size(); - for (int i = 0; i < count; i++) { - GSMConnection cn = (GSMConnection)call.connections.get(i); - if (cn.getGSMIndex() == index) { - return cn; - } - } - - return null; - } - - private Phone.SuppService getFailedService(int what) { - switch (what) { - case EVENT_SWITCH_RESULT: - return Phone.SuppService.SWITCH; - case EVENT_CONFERENCE_RESULT: - return Phone.SuppService.CONFERENCE; - case EVENT_SEPARATE_RESULT: - return Phone.SuppService.SEPARATE; - case EVENT_ECT_RESULT: - return Phone.SuppService.TRANSFER; - } - return Phone.SuppService.UNKNOWN; - } - - //****** Overridden from Handler - - public void - handleMessage (Message msg) - { - AsyncResult ar; - - switch (msg.what) { - case EVENT_POLL_CALLS_RESULT: - ar = (AsyncResult)msg.obj; - - if (msg == lastRelevantPoll) { - if (DBG_POLL) log( - "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); - needsPoll = false; - lastRelevantPoll = null; - handlePollCalls((AsyncResult)msg.obj); - } - break; - - case EVENT_OPERATION_COMPLETE: - ar = (AsyncResult)msg.obj; - operationComplete(); - break; - - case EVENT_SWITCH_RESULT: - case EVENT_CONFERENCE_RESULT: - case EVENT_SEPARATE_RESULT: - case EVENT_ECT_RESULT: - ar = (AsyncResult)msg.obj; - if (ar.exception != null) { - phone.notifySuppServiceFailed(getFailedService(msg.what)); - } - operationComplete(); - break; - - case EVENT_GET_LAST_CALL_FAIL_CAUSE: - int causeCode; - ar = (AsyncResult)msg.obj; - - operationComplete(); - - if (ar.exception != null) { - // An exception occurred...just treat the disconnect - // cause as "normal" - causeCode = CallFailCause.NORMAL_CLEARING; - Log.i(LOG_TAG, - "Exception during getLastCallFailCause, assuming normal disconnect"); - } else { - causeCode = ((int[])ar.result)[0]; - } - // Log the causeCode if its not normal - if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || - causeCode == CallFailCause.TEMPORARY_FAILURE || - causeCode == CallFailCause.SWITCHING_CONGESTION || - causeCode == CallFailCause.CHANNEL_NOT_AVAIL || - causeCode == CallFailCause.QOS_NOT_AVAIL || - causeCode == CallFailCause.BEARER_NOT_AVAIL || - causeCode == CallFailCause.ERROR_UNSPECIFIED) { - int cid = -1; - GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); - if (loc != null) cid = loc.getCid(); - - EventLog.List val = new EventLog.List(causeCode, cid, - TelephonyManager.getDefault().getNetworkType()); - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CALL_DROP, val); - } - - for (int i = 0, s = droppedDuringPoll.size() - ; i < s ; i++ - ) { - GSMConnection conn = droppedDuringPoll.get(i); - - conn.onRemoteDisconnect(causeCode); - } - - updatePhoneState(); - - phone.notifyCallStateChanged(); - droppedDuringPoll.clear(); - break; - - case EVENT_REPOLL_AFTER_DELAY: - case EVENT_CALL_STATE_CHANGE: - pollCallsWhenSafe(); - break; - - case EVENT_RADIO_AVAILABLE: - handleRadioAvailable(); - break; - - case EVENT_RADIO_NOT_AVAILABLE: - handleRadioNotAvailable(); - break; - } - } - - private void log(String msg) { - Log.d(LOG_TAG, "[CallTracker] " + msg); - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/CommandException.java b/telephony/java/com/android/internal/telephony/gsm/CommandException.java deleted file mode 100644 index 5cf48f3..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/CommandException.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.gsm; - -import android.util.Log; - -/** - * {@hide} - */ -public class CommandException extends RuntimeException -{ - private Error e; - - public enum Error { - INVALID_RESPONSE, - RADIO_NOT_AVAILABLE, - GENERIC_FAILURE, - PASSWORD_INCORRECT, - SIM_PIN2, - SIM_PUK2, - REQUEST_NOT_SUPPORTED, - OP_NOT_ALLOWED_DURING_VOICE_CALL, - OP_NOT_ALLOWED_BEFORE_REG_NW, - SMS_FAIL_RETRY, - } - - public CommandException(Error e) - { - super(e.toString()); - this.e = e; - } - - public static CommandException - fromRilErrno(int ril_errno) - { - switch(ril_errno) { - case RILConstants.SUCCESS: return null; - case RILConstants.RIL_ERRNO_INVALID_RESPONSE: - return new CommandException(Error.INVALID_RESPONSE); - case RILConstants.RADIO_NOT_AVAILABLE: - return new CommandException(Error.RADIO_NOT_AVAILABLE); - case RILConstants.GENERIC_FAILURE: - return new CommandException(Error.GENERIC_FAILURE); - case RILConstants.PASSWORD_INCORRECT: - return new CommandException(Error.PASSWORD_INCORRECT); - case RILConstants.SIM_PIN2: - return new CommandException(Error.SIM_PIN2); - case RILConstants.SIM_PUK2: - return new CommandException(Error.SIM_PUK2); - case RILConstants.REQUEST_NOT_SUPPORTED: - return new CommandException(Error.REQUEST_NOT_SUPPORTED); - case RILConstants.OP_NOT_ALLOWED_DURING_VOICE_CALL: - return new CommandException(Error.OP_NOT_ALLOWED_DURING_VOICE_CALL); - case RILConstants.OP_NOT_ALLOWED_BEFORE_REG_NW: - return new CommandException(Error.OP_NOT_ALLOWED_BEFORE_REG_NW); - case RILConstants.SMS_SEND_FAIL_RETRY: - return new CommandException(Error.SMS_FAIL_RETRY); - default: - Log.e("GSM", "Unrecognized RIL errno " + ril_errno); - return new CommandException(Error.INVALID_RESPONSE); - } - } - - public Error getCommandError() - { - return e; - } - - - -} diff --git a/telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java b/telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java deleted file mode 100644 index 7915798..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/CommandsInterface.java +++ /dev/null @@ -1,926 +0,0 @@ -/* - * 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.gsm; -import com.android.internal.telephony.*; -import android.os.Message; -import android.os.Handler; - -/** - * {@hide} - */ -public interface CommandsInterface -{ - enum RadioState { - RADIO_OFF, /* Radio explictly powered off (eg CFUN=0) */ - RADIO_UNAVAILABLE, /* Radio unavailable (eg, resetting or not booted) */ - SIM_NOT_READY, /* Radio is on, but the SIM interface is not ready */ - SIM_LOCKED_OR_ABSENT, /* SIM PIN locked, PUK required, network - personalization, or SIM absent */ - SIM_READY; /* Radio is on and SIM interface is available */ - - boolean isOn() /* and available...*/ - { - return this == SIM_NOT_READY - || this == SIM_LOCKED_OR_ABSENT - || this == SIM_READY; - } - - boolean isAvailable() - { - return this != RADIO_UNAVAILABLE; - } - - boolean isSIMReady() - { - // if you add new states after SIM_READY, include them too - return this == SIM_READY; - } - } - - enum SimStatus { - SIM_ABSENT, - SIM_NOT_READY, - SIM_READY, - SIM_PIN, - SIM_PUK, - SIM_NETWORK_PERSONALIZATION - } - - //***** Constants - - // Used as parameter to dial() and setCLIR() below - static final int CLIR_DEFAULT = 0; // "use subscription default value" - static final int CLIR_INVOCATION = 1; // (restrict CLI presentation) - static final int CLIR_SUPPRESSION = 2; // (allow CLI presentation) - - - // Used as parameters for call forward methods below - static final int CF_ACTION_DISABLE = 0; - static final int CF_ACTION_ENABLE = 1; -// static final int CF_ACTION_UNUSED = 2; - static final int CF_ACTION_REGISTRATION = 3; - static final int CF_ACTION_ERASURE = 4; - - static final int CF_REASON_UNCONDITIONAL = 0; - static final int CF_REASON_BUSY = 1; - static final int CF_REASON_NO_REPLY = 2; - static final int CF_REASON_NOT_REACHABLE = 3; - static final int CF_REASON_ALL = 4; - static final int CF_REASON_ALL_CONDITIONAL = 5; - - // Used for call barring methods below - static final String CB_FACILITY_BAOC = "AO"; - static final String CB_FACILITY_BAOIC = "OI"; - static final String CB_FACILITY_BAOICxH = "OX"; - static final String CB_FACILITY_BAIC = "AI"; - static final String CB_FACILITY_BAICr = "IR"; - static final String CB_FACILITY_BA_ALL = "AB"; - static final String CB_FACILITY_BA_MO = "AG"; - static final String CB_FACILITY_BA_MT = "AC"; - static final String CB_FACILITY_BA_SIM = "SC"; - static final String CB_FACILITY_BA_FD = "FD"; - - - // Used for various supp services apis - // See 27.007 +CCFC or +CLCK - static final int SERVICE_CLASS_NONE = 0; // no user input - static final int SERVICE_CLASS_VOICE = (1 << 0); - static final int SERVICE_CLASS_DATA = (1 << 1); //synoym for 16+32+64+128 - static final int SERVICE_CLASS_FAX = (1 << 2); - static final int SERVICE_CLASS_SMS = (1 << 3); - static final int SERVICE_CLASS_DATA_SYNC = (1 << 4); - static final int SERVICE_CLASS_DATA_ASYNC = (1 << 5); - static final int SERVICE_CLASS_PACKET = (1 << 6); - static final int SERVICE_CLASS_PAD = (1 << 7); - static final int SERVICE_CLASS_MAX = (1 << 7); // Max SERVICE_CLASS value - - // Numeric representation of string values returned - // by messages sent to setOnUSSD handler - static final int USSD_MODE_NOTIFY = 0; - static final int USSD_MODE_REQUEST = 1; - - // SIM Refresh results, passed up from RIL. - static final int SIM_REFRESH_FILE_UPDATED = 0; // Single file updated - static final int SIM_REFRESH_INIT = 1; // SIM initialized; reload all - static final int SIM_REFRESH_RESET = 2; // SIM reset; may be locked - - //***** Methods - - RadioState getRadioState(); - - /** - * Fires on any RadioState transition - * Always fires immediately as well - * - * do not attempt to calculate transitions by storing getRadioState() values - * on previous invocations of this notification. Instead, use the other - * registration methods - */ - void registerForRadioStateChanged(Handler h, int what, Object obj); - - /** - * Fires on any transition into RadioState.isOn() - * Fires immediately if currently in that state - * In general, actions should be idempotent. State may change - * before event is received. - */ - void registerForOn(Handler h, int what, Object obj); - - /** - * Fires on any transition out of RadioState.isAvailable() - * Fires immediately if currently in that state - * In general, actions should be idempotent. State may change - * before event is received. - */ - void registerForAvailable(Handler h, int what, Object obj); - //void unregisterForAvailable(Handler h); - /** - * Fires on any transition into !RadioState.isAvailable() - * Fires immediately if currently in that state - * In general, actions should be idempotent. State may change - * before event is received. - */ - void registerForNotAvailable(Handler h, int what, Object obj); - //void unregisterForNotAvailable(Handler h); - /** - * Fires on any transition into RADIO_OFF or !RadioState.isAvailable() - * Fires immediately if currently in that state - * In general, actions should be idempotent. State may change - * before event is received. - */ - void registerForOffOrNotAvailable(Handler h, int what, Object obj); - //void unregisterForNotAvailable(Handler h); - - /** - * Fires on any transition into SIM_READY - * Fires immediately if if currently in that state - * In general, actions should be idempotent. State may change - * before event is received. - */ - void registerForSIMReady(Handler h, int what, Object obj); - //void unregisterForSIMReady(Handler h); - /** Any transition into SIM_LOCKED_OR_ABSENT */ - void registerForSIMLockedOrAbsent(Handler h, int what, Object obj); - //void unregisterForSIMLockedOrAbsent(Handler h); - - void registerForCallStateChanged(Handler h, int what, Object obj); - //void unregisterForCallStateChanged(Handler h); - void registerForNetworkStateChanged(Handler h, int what, Object obj); - //void unregisterForNetworkStateChanged(Handler h); - void registerForPDPStateChanged(Handler h, int what, Object obj); - //void unregisterForPDPStateChanged(Handler h); - - /** - * unlike the register* methods, there's only one new SMS handler - * if you need to unregister, you should also tell the radio to stop - * sending SMS's to you (via AT+CNMI) - * - * AsyncResult.result is a String containing the SMS PDU - */ - void setOnNewSMS(Handler h, int what, Object obj); - - /** - * Register for NEW_SMS_ON_SIM unsolicited message - * - * AsyncResult.result is an int array containing the index of new SMS - */ - void setOnSmsOnSim(Handler h, int what, Object obj); - - /** - * Register for NEW_SMS_STATUS_REPORT unsolicited message - * - * AsyncResult.result is a String containing the status report PDU - */ - void setOnSmsStatus(Handler h, int what, Object obj); - - /** - * unlike the register* methods, there's only one NITZ time handler - * - * AsyncResult.result is an Object[] - * ((Object[])AsyncResult.result)[0] is a String containing the NITZ time string - * ((Object[])AsyncResult.result)[1] is a Long containing the milliseconds since boot as - * returned by elapsedRealtime() when this NITZ time - * was posted. - * - * Please note that the delivery of this message may be delayed several - * seconds on system startup - */ - void setOnNITZTime(Handler h, int what, Object obj); - - /** - * unlike the register* methods, there's only one USSD notify handler - * - * Represents the arrival of a USSD "notify" message, which may - * or may not have been triggered by a previous USSD send - * - * AsyncResult.result is a String[] - * ((String[])(AsyncResult.result))[0] contains status code - * "0" USSD-Notify -- text in ((const char **)data)[1] - * "1" USSD-Request -- text in ((const char **)data)[1] - * "2" Session terminated by network - * "3" other local client (eg, SIM Toolkit) has responded - * "4" Operation not supported - * "5" Network timeout - * - * ((String[])(AsyncResult.result))[1] contains the USSD message - * The numeric representations of these are in USSD_MODE_* - */ - - void setOnUSSD(Handler h, int what, Object obj); - - /** - * unlike the register* methods, there's only one signal strength handler - * AsyncResult.result is an int[2] - * response.obj.result[0] is received signal strength (0-31, 99) - * response.obj.result[1] is bit error rate (0-7, 99) - * as defined in TS 27.007 8.5 - */ - - void setOnSignalStrengthUpdate(Handler h, int what, Object obj); - - /** - * Sets the handler for SIM SMS storage full unsolicited message. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnSimSmsFull(Handler h, int what, Object obj); - - /** - * Sets the handler for SIM Refresh notifications. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnSimRefresh(Handler h, int what, Object obj); - - /** - * Sets the handler for RING notifications. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnCallRing(Handler h, int what, Object obj); - - /** - * Sets the handler for RESTRICTED_STATE changed notification, - * eg, for Domain Specific Access Control - * unlike the register* methods, there's only one signal strength handler - * - * AsyncResult.result is an int[1] - * response.obj.result[0] is a bitmask of RIL_RESTRICTED_STATE_* values - */ - - void setOnRestrictedStateChanged(Handler h, int what, Object obj); - - /** - * Sets the handler for Supplementary Service Notifications. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnSuppServiceNotification(Handler h, int what, Object obj); - - /** - * Sets the handler for Session End Notifications for STK. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnStkSessionEnd(Handler h, int what, Object obj); - - /** - * Sets the handler for Proactive Commands for STK. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnStkProactiveCmd(Handler h, int what, Object obj); - - /** - * Sets the handler for Event Notifications for STK. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnStkEvent(Handler h, int what, Object obj); - - /** - * Sets the handler for Call Set Up Notifications for STK. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setOnStkCallSetUp(Handler h, int what, Object obj); - - /** - * Enables/disbables supplementary service related notifications from - * the network. - * - * @param enable true to enable notifications, false to disable. - * @param result Message to be posted when command completes. - */ - void setSuppServiceNotifications(boolean enable, Message result); - - /** - * Returns current SIM status. - * - * AsyncResult.result is SimStatus - * - */ - - void getSimStatus(Message result); - - /** - * Supply the SIM PIN to the SIM card - * - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * This exception is CommandException with an error of PASSWORD_INCORRECT - * if the password is incorrect - * - * ar.exception and ar.result are null on success - */ - - void supplySimPin(String pin, Message result); - - /** - * Supply the SIM PUK to the SIM card - * - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * This exception is CommandException with an error of PASSWORD_INCORRECT - * if the password is incorrect - * - * ar.exception and ar.result are null on success - */ - - void supplySimPuk(String puk, String newPin, Message result); - - /** - * Supply the SIM PIN2 to the SIM card - * Only called following operation where SIM_PIN2 was - * returned as a a failure from a previous operation - * - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * This exception is CommandException with an error of PASSWORD_INCORRECT - * if the password is incorrect - * - * ar.exception and ar.result are null on success - */ - - void supplySimPin2(String pin2, Message result); - - /** - * Supply the SIM PUK2 to the SIM card - * Only called following operation where SIM_PUK2 was - * returned as a a failure from a previous operation - * - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * This exception is CommandException with an error of PASSWORD_INCORRECT - * if the password is incorrect - * - * ar.exception and ar.result are null on success - */ - - void supplySimPuk2(String puk2, String newPin2, Message result); - - void changeSimPin(String oldPin, String newPin, Message result); - void changeSimPin2(String oldPin2, String newPin2, Message result); - - void changeBarringPassword(String facility, String oldPwd, String newPwd, Message result); - - void supplyNetworkDepersonalization(String netpin, Message result); - - /** - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result contains a List of DriverCall - * The ar.result List is sorted by DriverCall.index - */ - void getCurrentCalls (Message result); - - /** - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result contains a List of PDPContextState - */ - void getPDPContextList(Message result); - - /** - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - * - * CLIR_DEFAULT == on "use subscription default value" - * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation) - * CLIR_INVOCATION == on "CLIR invocation" (restrict CLI presentation) - */ - void dial (String address, int clirMode, Message result); - - /** - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is String containing IMSI on success - */ - void getIMSI(Message result); - - /** - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is String containing IMEI on success - */ - void getIMEI(Message result); - - /** - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is String containing IMEISV on success - */ - void getIMEISV(Message result); - - /** - * Hang up one individual connection. - * returned message - * retMsg.obj = AsyncResult ar - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - * - * 3GPP 22.030 6.5.5 - * "Releases a specific active call X" - */ - void hangupConnection (int gsmIndex, Message result); - - /** - * 3GPP 22.030 6.5.5 - * "Releases all held calls or sets User Determined User Busy (UDUB) - * for a waiting call." - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void hangupWaitingOrBackground (Message result); - - /** - * 3GPP 22.030 6.5.5 - * "Releases all active calls (if any exist) and accepts - * the other (held or waiting) call." - * - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void hangupForegroundResumeBackground (Message result); - - /** - * 3GPP 22.030 6.5.5 - * "Places all active calls (if any exist) on hold and accepts - * the other (held or waiting) call." - * - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void switchWaitingOrHoldingAndActive (Message result); - - /** - * 3GPP 22.030 6.5.5 - * "Adds a held call to the conversation" - * - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void conference (Message result); - - /** - * 3GPP 22.030 6.5.5 - * "Places all active calls on hold except call X with which - * communication shall be supported." - */ - void separateConnection (int gsmIndex, Message result); - - /** - * - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void acceptCall (Message result); - - /** - * also known as UDUB - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void rejectCall (Message result); - - /** - * 3GPP 22.030 6.5.5 - * "Connects the two calls and disconnects the subscriber from both calls" - * - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void explicitCallTransfer (Message result); - - /** - * cause code returned as int[0] in Message.obj.response - * Returns integer cause code defined in TS 24.008 - * Annex H or closest approximation. - * Most significant codes: - * - Any defined in 22.001 F.4 (for generating busy/congestion) - * - Cause 68: ACM >= ACMMax - */ - void getLastCallFailCause (Message result); - - - /** - * Reason for last PDP context deactivate or failure to activate - * cause code returned as int[0] in Message.obj.response - * returns an integer cause code defined in TS 24.008 - * section 6.1.3.1.3 or close approximation - */ - void getLastPdpFailCause (Message result); - - void setMute (boolean enableMute, Message response); - - void getMute (Message response); - - /** - * response.obj is an AsyncResult - * response.obj.result is an int[2] - * response.obj.result[0] is received signal strength (0-31, 99) - * response.obj.result[1] is bit error rate (0-7, 99) - * as defined in TS 27.007 8.5 - */ - void getSignalStrength (Message response); - - - /** - * response.obj.result is an int[3] - * response.obj.result[0] is registration state 0-5 from TS 27.007 7.2 - * response.obj.result[1] is LAC if registered or -1 if not - * response.obj.result[2] is CID if registered or -1 if not - * valid LAC and CIDs are 0x0000 - 0xffff - * - * Please note that registration state 4 ("unknown") is treated - * as "out of service" above - */ - void getRegistrationState (Message response); - - /** - * response.obj.result is an int[3] - * response.obj.result[0] is registration state 0-5 from TS 27.007 7.2 - * response.obj.result[1] is LAC if registered or -1 if not - * response.obj.result[2] is CID if registered or -1 if not - * valid LAC and CIDs are 0x0000 - 0xffff - * - * Please note that registration state 4 ("unknown") is treated - * as "out of service" above - */ - void getGPRSRegistrationState (Message response); - - /** - * response.obj.result is a String[3] - * response.obj.result[0] is long alpha or null if unregistered - * response.obj.result[1] is short alpha or null if unregistered - * response.obj.result[2] is numeric or null if unregistered - */ - void getOperator(Message response); - - /** - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void sendDtmf(char c, Message result); - - - /** - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void startDtmf(char c, Message result); - - /** - * ar.exception carries exception on failure - * ar.userObject contains the orignal value of result.obj - * ar.result is null on success and failure - */ - void stopDtmf(Message result); - - - /** - * smscPDU is smsc address in PDU form GSM BCD format prefixed - * by a length byte (as expected by TS 27.005) or NULL for default SMSC - * pdu is SMS in PDU format as an ASCII hex string - * less the SMSC address - */ - void sendSMS (String smscPDU, String pdu, Message response); - - /** - * Deletes the specified SMS record from SIM memory (EF_SMS). - * - * @param index index of the SMS record to delete - * @param response sent when operation completes - */ - void deleteSmsOnSim(int index, Message response); - - /** - * Writes an SMS message to SIM memory (EF_SMS). - * - * @param status status of message on SIM. One of: - * SmsManger.STATUS_ON_SIM_READ - * SmsManger.STATUS_ON_SIM_UNREAD - * SmsManger.STATUS_ON_SIM_SENT - * SmsManger.STATUS_ON_SIM_UNSENT - * @param pdu message PDU, as hex string - * @param response sent when operation completes. - * response.obj will be an AsyncResult, and will indicate - * any error that may have occurred (eg, out of memory). - */ - void writeSmsToSim(int status, String smsc, String pdu, Message response); - - void setupDefaultPDP(String apn, String user, String password, Message response); - - void deactivateDefaultPDP(int cid, Message response); - - void setRadioPower(boolean on, Message response); - - void acknowledgeLastIncomingSMS(boolean success, Message response); - - /** - * parameters equivilient to 27.007 AT+CRSM command - * response.obj will be an AsyncResult - * response.obj.userObj will be a SimIoResult on success - */ - void simIO (int command, int fileid, String path, int p1, int p2, int p3, - String data, String pin2, Message response); - - /** - * (AsyncResult)response.obj).result is an int[] with element [0] set to - * 1 for "CLIP is provisioned", and 0 for "CLIP is not provisioned". - * - * @param response is callback message - */ - - void queryCLIP(Message response); - - /** - * response.obj will be a an int[2] - * - * response.obj[0] will be TS 27.007 +CLIR parameter 'n' - * 0 presentation indicator is used according to the subscription of the CLIR service - * 1 CLIR invocation - * 2 CLIR suppression - * - * response.obj[1] will be TS 27.007 +CLIR parameter 'm' - * 0 CLIR not provisioned - * 1 CLIR provisioned in permanent mode - * 2 unknown (e.g. no network, etc.) - * 3 CLIR temporary mode presentation restricted - * 4 CLIR temporary mode presentation allowed - */ - - void getCLIR(Message response); - - /** - * clirMode is one of the CLIR_* constants above - * - * response.obj is null - */ - - void setCLIR(int clirMode, Message response); - - /** - * (AsyncResult)response.obj).result is an int[] with element [0] set to - * 0 for disabled, 1 for enabled. - * - * @param serviceClass is a sum of SERVICE_CLASS_* - * @param response is callback message - */ - - void queryCallWaiting(int serviceClass, Message response); - - /** - * @param enable is true to enable, false to disable - * @param serviceClass is a sum of SERVICE_CLASS_* - * @param response is callback message - */ - - void setCallWaiting(boolean enable, int serviceClass, Message response); - - /** - * @param action is one of CF_ACTION_* - * @param cfReason is one of CF_REASON_* - * @param serviceClass is a sum of SERVICE_CLASSS_* - */ - void setCallForward(int action, int cfReason, int serviceClass, - String number, int timeSeconds, Message response); - - /** - * cfReason is one of CF_REASON_* - * - * ((AsyncResult)response.obj).result will be an array of - * CallForwardInfo's - * - * An array of length 0 means "disabled for all codes" - */ - void queryCallForwardStatus(int cfReason, int serviceClass, - String number, Message response); - - void setNetworkSelectionModeAutomatic(Message response); - - void setNetworkSelectionModeManual(String operatorNumeric, Message response); - - /** - * Queries whether the current network selection mode is automatic - * or manual - * - * ((AsyncResult)response.obj).result is an int[] with element [0] being - * a 0 for automatic selection and a 1 for manual selection - */ - - void getNetworkSelectionMode(Message response); - - /** - * Queries the currently available networks - * - * ((AsyncResult)response.obj).result is a List of NetworkInfo objects - */ - void getAvailableNetworks(Message response); - - void getBasebandVersion (Message response); - - - /** - * (AsyncResult)response.obj).result will be an Integer representing - * the sum of enabled serivice classes (sum of SERVICE_CLASS_*) - * - * @param facility one of CB_FACILTY_* - * @param password password or "" if not required - * @param serviceClass is a sum of SERVICE_CLASS_* - * @param response is callback message - */ - - void queryFacilityLock (String facility, String password, int serviceClass, - Message response); - - /** - * @param facility one of CB_FACILTY_* - * @param lockState true means lock, false means unlock - * @param password password or "" if not required - * @param serviceClass is a sum of SERVICE_CLASS_* - * @param response is callback message - */ - void setFacilityLock (String facility, boolean lockState, String password, - int serviceClass, Message response); - - - void sendUSSD (String ussdString, Message response); - - /** - * Cancels a pending USSD session if one exists. - * @param response callback message - */ - void cancelPendingUssd (Message response); - - void resetRadio(Message result); - - /** - * Assign a specified band for RF configuration. - * - * @param bandMode one of BM_*_BAND - * @param response is callback message - */ - void setBandMode (int bandMode, Message response); - - /** - * Query the list of band mode supported by RF. - * - * @param response is callback message - * ((AsyncResult)response.obj).result is an int[] with every - * element representing one avialable BM_*_BAND - */ - void queryAvailableBandMode (Message response); - - /** - * Requests to set the preferred network type for searching and registering - * (CS/PS domain, RAT, and operation mode) - * @param networkType one of NT_*_TYPE - * @param response is callback message - */ - void setPreferredNetworkType(int networkType , Message response); - - /** - * Query the preferred network type setting - * - * @param response is callback message to report one of NT_*_TYPE - */ - void getPreferredNetworkType(Message response); - - /** - * Query neighboring cell ids - * - * @param response s callback message to cell ids - */ - void getNeighboringCids(Message response); - - /** - * Request to enable/disable network state change notifications when - * location informateion (lac and/or cid) has changed. - * - * @param enable true to enable, false to disable - * @param response callback message - */ - void setLocationUpdates(boolean enable, Message response); - - - void invokeOemRilRequestRaw(byte[] data, Message response); - - void invokeOemRilRequestStrings(String[] strings, Message response); - - - /** - * Send TERMINAL RESPONSE to the SIM, after processing a proactive command - * sent by the SIM. - * - * @param contents String containing SAT/USAT response in hexadecimal - * format starting with first byte of response data. See - * TS 102 223 for details. - * @param response Callback message - */ - public void sendTerminalResponse(String contents, Message response); - - /** - * Send ENVELOPE to the SIM, after processing a proactive command sent by - * the SIM. - * - * @param contents String containing SAT/USAT response in hexadecimal - * format starting with command tag. See TS 102 223 for - * details. - * @param response Callback message - */ - public void sendEnvelope(String contents, Message response); - - /** - * Accept or reject the call setup request from SIM. - * - * @param accept true if the call is to be accepted, false otherwise. - * @param response Callback message - */ - public void handleCallSetupRequestFromSim(boolean accept, Message response); -} diff --git a/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java deleted file mode 100644 index 02a6841..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/DataConnectionTracker.java +++ /dev/null @@ -1,1836 +0,0 @@ -/* - * 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.gsm; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.database.ContentObserver; -import android.database.Cursor; -import android.net.NetworkInfo; -import android.net.wifi.WifiManager; -import android.net.Uri; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.INetStatService; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.preference.PreferenceManager; -import android.provider.Checkin; -import android.provider.Settings; -import android.provider.Telephony; -import android.provider.Settings.SettingNotFoundException; -import android.telephony.ServiceState; -import android.telephony.TelephonyManager; -import android.telephony.gsm.GsmCellLocation; -import android.text.TextUtils; -import android.util.EventLog; -import android.util.Log; - -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.gsm.PdpConnection.PdpFailCause; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * {@hide} - */ -final class DataConnectionTracker extends Handler -{ - private static final String LOG_TAG = "GSM"; - private static final boolean DBG = true; - - /** - * IDLE: ready to start data connection setup, default state - * INITING: state of issued setupDefaultPDP() but not finish yet - * CONNECTING: state of issued startPppd() but not finish yet - * SCANNING: data connection fails with one apn but other apns are available - * ready to start data connection on other apns (before INITING) - * CONNECTED: IP connection is setup - * DISCONNECTING: PdpConnection.disconnect() has been called, but PDP - * context is not yet deactivated - * FAILED: data connection fail for all apns settings - * - * getDataConnectionState() maps State to DataState - * FAILED or IDLE : DISCONNECTED - * INITING or CONNECTING or SCANNING: CONNECTING - * CONNECTED : CONNECTED or DISCONNECTING - */ - enum State { - IDLE, - INITING, - CONNECTING, - SCANNING, - CONNECTED, - DISCONNECTING, - FAILED - } - - enum Activity { - NONE, - DATAIN, - DATAOUT, - DATAINANDOUT - } - - /** - * Handles changes to the APN db. - */ - private class ApnChangeObserver extends ContentObserver { - public ApnChangeObserver () { - super(mDataConnectionTracker); - } - - @Override - public void onChange(boolean selfChange) { - sendMessage(obtainMessage(EVENT_APN_CHANGED)); - } - } - - //***** Instance Variables - - GSMPhone phone; - INetStatService netstat; - State state = State.IDLE; - Activity activity = Activity.NONE; - boolean netStatPollEnabled = false; - // Indicates baseband will not auto-attach - private boolean noAutoAttach = false; - long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; - Handler mDataConnectionTracker = null; - private ContentResolver mResolver; - - long txPkts, rxPkts, sentSinceLastRecv; - int netStatPollPeriod; - private int mNoRecvPollCount = 0; - private boolean mPingTestActive = false; - // Count of PDP reset attempts; reset when we see incoming, - // call reRegisterNetwork, or pingTest succeeds. - private int mPdpResetCount = 0; - private boolean mIsScreenOn = true; - - //useful for debugging - boolean failNextConnect = false; - - /** - * allApns holds all apns for this sim spn, retrieved from - * the Carrier DB. - * - * Create once after simcard info is loaded - */ - private ArrayList allApns = null; - - /** - * waitingApns holds all apns that are waiting to be connected - * - * It is a subset of allApns and has the same format - */ - private ArrayList waitingApns = null; - - private ApnSetting preferredApn = null; - - /** - * pdpList holds all the PDP connection, i.e. IP Link in GPRS - */ - private ArrayList pdpList; - - /** CID of active PDP */ - int cidActive; - - /** Currently requested APN type */ - private String mRequestedApnType = Phone.APN_TYPE_DEFAULT; - - /** Currently active APN */ - private ApnSetting mActiveApn; - - /** Currently active PdpConnection */ - private PdpConnection mActivePdp; - - private static int APN_DEFAULT_ID = 0; - private static int APN_MMS_ID = 1; - private static int APN_NUM_TYPES = 2; - - private boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; - - /** wifi connection status will be updated by sticky intent */ - private boolean mIsWifiConnected = false; - - /** Intent sent when the reconnect alarm fires. */ - private PendingIntent mReconnectIntent = null; - - /** Is packet service restricted by network */ - private boolean mIsPsRestricted = false; - - //***** Constants - - // TODO: Increase this to match the max number of simultaneous - // PDP contexts we plan to support. - /** - * Pool size of PdpConnection objects. - */ - private static final int PDP_CONNECTION_POOL_SIZE = 1; - - private static final int POLL_PDP_MILLIS = 5 * 1000; - private static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000; - /** Cap out with 1 hour retry interval. */ - private static final int RECONNECT_DELAY_MAX_MILLIS = 60 * 60 * 1000; - - /** Slow poll when attempting connection recovery. */ - private static final int POLL_NETSTAT_SLOW_MILLIS = 5000; - - /** Default ping deadline, in seconds. */ - private static final int DEFAULT_PING_DEADLINE = 5; - /** Default max failure count before attempting to network re-registration. */ - private static final int DEFAULT_MAX_PDP_RESET_FAIL = 3; - - /** - * After detecting a potential connection problem, this is the max number - * of subsequent polls before attempting a radio reset. At this point, - * poll interval is 5 seconds (POLL_NETSTAT_SLOW_MILLIS), so set this to - * poll for about 2 more minutes. - */ - private static final int NO_RECV_POLL_LIMIT = 24; - - // 1 sec. default polling interval when screen is on. - private static final int POLL_NETSTAT_MILLIS = 1000; - // 10 min. default polling interval when screen is off. - private static final int POLL_NETSTAT_SCREEN_OFF_MILLIS = 1000*60*10; - // 2 min for round trip time - private static final int POLL_LONGEST_RTT = 120 * 1000; - // 10 for packets without ack - private static final int NUMBER_SENT_PACKETS_OF_HANG = 10; - // how long to wait before switching back to default APN - private static final int RESTORE_DEFAULT_APN_DELAY = 1 * 60 * 1000; - // system property that can override the above value - private static final String APN_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; - // represents an invalid IP address - private static final String NULL_IP = "0.0.0.0"; - - private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; - private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; - - - //***** Event Codes - static final int EVENT_DATA_SETUP_COMPLETE = 1; - static final int EVENT_RADIO_AVAILABLE = 3; - static final int EVENT_RECORDS_LOADED = 4; - static final int EVENT_TRY_SETUP_DATA = 5; - static final int EVENT_PDP_STATE_CHANGED = 6; - static final int EVENT_POLL_PDP = 7; - static final int EVENT_GET_PDP_LIST_COMPLETE = 11; - static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 12; - static final int EVENT_VOICE_CALL_STARTED = 14; - static final int EVENT_VOICE_CALL_ENDED = 15; - static final int EVENT_GPRS_DETACHED = 19; - static final int EVENT_LINK_STATE_CHANGED = 20; - static final int EVENT_ROAMING_ON = 21; - static final int EVENT_ROAMING_OFF = 22; - static final int EVENT_ENABLE_NEW_APN = 23; - static final int EVENT_RESTORE_DEFAULT_APN = 24; - static final int EVENT_DISCONNECT_DONE = 25; - static final int EVENT_GPRS_ATTACHED = 26; - static final int EVENT_START_NETSTAT_POLL = 27; - static final int EVENT_START_RECOVERY = 28; - static final int EVENT_APN_CHANGED = 29; - static final int EVENT_PS_RESTRICT_ENABLED = 30; - static final int EVENT_PS_RESTRICT_DISABLED = 31; - - static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn"); - static final String APN_ID = "apn_id"; - private boolean canSetPreferApn = false; - - BroadcastReceiver mIntentReceiver = new BroadcastReceiver () - { - @Override - public void onReceive(Context context, Intent intent) - { - String action = intent.getAction(); - if (action.equals(Intent.ACTION_SCREEN_ON)) { - mIsScreenOn = true; - stopNetStatPoll(); - startNetStatPoll(); - } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { - mIsScreenOn = false; - stopNetStatPoll(); - startNetStatPoll(); - } else if (action.equals((INTENT_RECONNECT_ALARM))) { - Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state); - - String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); - if (state == State.FAILED) { - cleanUpConnection(false, reason); - } - trySetupData(reason); - } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - final android.net.NetworkInfo networkInfo = (NetworkInfo) - intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); - } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; - - if (!enabled) { - // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION - // quit and wont report disconnected til next enalbing. - mIsWifiConnected = false; - } - } - } - }; - - /** Watches for changes to the APN db. */ - private ApnChangeObserver apnObserver; - - //***** Constructor - - DataConnectionTracker(GSMPhone phone) - { - this.phone = phone; - phone.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); - phone.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); - phone.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); - phone.mCM.registerForPDPStateChanged (this, EVENT_PDP_STATE_CHANGED, null); - phone.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); - phone.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); - phone.mSST.registerForGprsAttached(this, EVENT_GPRS_ATTACHED, null); - phone.mSST.registerForGprsDetached(this, EVENT_GPRS_DETACHED, null); - phone.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null); - phone.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null); - phone.mSST.registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null); - phone.mSST.registerForPsRestrictedDisabled(this, EVENT_PS_RESTRICT_DISABLED, null); - - this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat")); - - IntentFilter filter = new IntentFilter(); - filter.addAction(INTENT_RECONNECT_ALARM); - filter.addAction(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - - phone.getContext().registerReceiver(mIntentReceiver, filter, null, phone.h); - - - mDataConnectionTracker = this; - mResolver = phone.getContext().getContentResolver(); - - apnObserver = new ApnChangeObserver(); - phone.getContext().getContentResolver().registerContentObserver( - Telephony.Carriers.CONTENT_URI, true, apnObserver); - - createAllPdpList(); - - // This preference tells us 1) initial condition for "dataEnabled", - // and 2) whether the RIL will setup the baseband to auto-PS attach. - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); - dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false); - noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; - } - - void setState(State s) { - if (DBG) log ("setState: " + s); - if (state != s) { - if (s == State.INITING) { // request PDP context - Checkin.updateStats( - phone.getContext().getContentResolver(), - Checkin.Stats.Tag.PHONE_GPRS_ATTEMPTED, 1, 0.0); - } - - if (s == State.CONNECTED) { // pppd is up - Checkin.updateStats( - phone.getContext().getContentResolver(), - Checkin.Stats.Tag.PHONE_GPRS_CONNECTED, 1, 0.0); - } - } - - state = s; - - if (state == State.FAILED) { - if (waitingApns != null) - waitingApns.clear(); // when teardown the connection and set to IDLE - } - } - - String getStateInString() { - switch (state) { - case IDLE: return "IDLE"; - case INITING: return "INIT"; - case CONNECTING: return "CING"; - case SCANNING: return "SCAN"; - case CONNECTED: return "CNTD"; - case DISCONNECTING: return "DING"; - case FAILED: return "FAIL"; - default: return "ERRO"; - } - } - - String[] getActiveApnTypes() { - String[] result; - if (mActiveApn != null) { - result = mActiveApn.types; - } else { - result = new String[1]; - result[0] = Phone.APN_TYPE_DEFAULT; - } - return result; - } - - String getActiveApnString() { - String result = null; - if (mActiveApn != null) { - result = mActiveApn.apn; - } - return result; - } - - /** - * Ensure that we are connected to an APN of the specified type. - * @param type the APN type (currently the only valid value - * is {@link Phone#APN_TYPE_MMS}) - * @return the result of the operation. Success is indicated by - * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or - * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast - * will be sent by the ConnectivityManager when a connection to - * the APN has been established. - */ - int enableApnType(String type) { - if (!TextUtils.equals(type, Phone.APN_TYPE_MMS)) { - return Phone.APN_REQUEST_FAILED; - } - // If already active, return - Log.d(LOG_TAG, "enableApnType("+type+")"); - if (isApnTypeActive(type)) { - setEnabled(type, true); - removeMessages(EVENT_RESTORE_DEFAULT_APN); - /** - * We're being asked to enable a non-default APN that's already in use. - * This means we should restart the timer that will automatically - * switch back to the default APN and disable the non-default APN - * when it expires. - */ - sendMessageDelayed( - obtainMessage(EVENT_RESTORE_DEFAULT_APN), - getRestoreDefaultApnDelay()); - if (state == State.INITING) return Phone.APN_REQUEST_STARTED; - else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; - } - - if (!isApnTypeAvailable(type)) { - return Phone.APN_TYPE_NOT_AVAILABLE; - } - - setEnabled(type, true); - mRequestedApnType = type; - sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN)); - return Phone.APN_REQUEST_STARTED; - } - - /** - * The APN of the specified type is no longer needed. Ensure that if - * use of the default APN has not been explicitly disabled, we are connected - * to the default APN. - * @param type the APN type. The only valid value currently is {@link Phone#APN_TYPE_MMS}. - * @return - */ - int disableApnType(String type) { - Log.d(LOG_TAG, "disableApnType("+type+")"); - if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { - removeMessages(EVENT_RESTORE_DEFAULT_APN); - setEnabled(type, false); - if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { - if (dataEnabled[APN_DEFAULT_ID]) { - return Phone.APN_ALREADY_ACTIVE; - } else { - cleanUpConnection(true, Phone.REASON_DATA_DISABLED); - return Phone.APN_REQUEST_STARTED; - } - } else { - /* - * Note that if default data is disabled, the following - * has the effect of disabling the MMS APN, and then - * ignoring the request to enable the default APN. - * The net result is that data is completely disabled. - */ - sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN)); - return Phone.APN_REQUEST_STARTED; - } - } else { - return Phone.APN_REQUEST_FAILED; - } - } - - /** - * The data connection is expected to be setup while device - * 1. has sim card - * 2. registered to gprs service - * 3. user doesn't explicitly disable data service - * 4. wifi is not on - * 5. packet service is not restricted - * - * @return false while no data connection if all above requirements are met. - */ - boolean isDataConnectionAsDesired() { - boolean roaming = phone.getServiceState().getRoaming(); - - if (phone.mSIMRecords.getRecordsLoaded() && - phone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && - (!roaming || getDataOnRoamingEnabled()) && - !mIsWifiConnected && - !mIsPsRestricted ) { - return (state == State.CONNECTED); - } - return true; - } - - private boolean isApnTypeActive(String type) { - // TODO: to support simultaneous, mActiveApn can be a List instead. - return mActiveApn != null && mActiveApn.canHandleType(type); - } - - private boolean isApnTypeAvailable(String type) { - if (allApns != null) { - for (ApnSetting apn : allApns) { - if (apn.canHandleType(type)) { - return true; - } - } - } - return false; - } - - private boolean isEnabled(String apnType) { - if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) { - return dataEnabled[APN_DEFAULT_ID]; - } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) { - return dataEnabled[APN_MMS_ID]; - } else { - return false; - } - } - - private void setEnabled(String apnType, boolean enable) { - Log.d(LOG_TAG, "setEnabled(" + apnType + ", " + enable + ')'); - if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) { - dataEnabled[APN_DEFAULT_ID] = enable; - } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) { - dataEnabled[APN_MMS_ID] = enable; - } - Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] + - " dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID]); - } - - /** - * Prevent mobile data connections from being established, - * or once again allow mobile data connections. If the state - * toggles, then either tear down or set up data, as - * appropriate to match the new state. - *

    This operation only affects the default APN, and if the same APN is - * currently being used for MMS traffic, the teardown will not happen - * even when {@code enable} is {@code false}.

    - * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data - * @return {@code true} if the operation succeeded - */ - public boolean setDataEnabled(boolean enable) { - boolean isEnabled = isEnabled(Phone.APN_TYPE_DEFAULT); - Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled); - if (!isEnabled && enable) { - setEnabled(Phone.APN_TYPE_DEFAULT, true); - // trySetupData() will be a no-op if we are currently - // connected to the MMS APN - return trySetupData(Phone.REASON_DATA_ENABLED); - } else if (!enable) { - setEnabled(Phone.APN_TYPE_DEFAULT, false); - // Don't tear down if there is an active APN and it handles MMS. - // TODO: This isn't very general. - if (!isApnTypeActive(Phone.APN_TYPE_MMS) || !isEnabled(Phone.APN_TYPE_MMS)) { - cleanUpConnection(true, Phone.REASON_DATA_DISABLED); - return true; - } - return false; - } else // isEnabled && enable - return true; - } - - /** - * Simply tear down data connections due to radio off - * and don't setup again. - */ - public void cleanConnectionBeforeRadioOff() { - cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); - } - - /** - * Report the current state of data connectivity (enabled or disabled) for - * the default APN. - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - public boolean getDataEnabled() { - return dataEnabled[APN_DEFAULT_ID]; - } - - /** - * Report on whether data connectivity is enabled for any APN. - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - public boolean getAnyDataEnabled() { - return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID]; - } - - //The data roaming setting is now located in the shared preferences. - // See if the requested preference value is the same as that stored in - // the shared values. If it is not, then update it. - public void setDataOnRoamingEnabled(boolean enabled) { - if (getDataOnRoamingEnabled() != enabled) { - Settings.Secure.putInt(phone.getContext().getContentResolver(), - Settings.Secure.DATA_ROAMING, enabled ? 1 : 0); - } - Message roamingMsg = phone.getServiceState().getRoaming() ? - obtainMessage(EVENT_ROAMING_ON) : obtainMessage(EVENT_ROAMING_OFF); - sendMessage(roamingMsg); - } - - //Retrieve the data roaming setting from the shared preferences. - public boolean getDataOnRoamingEnabled() { - try { - return Settings.Secure.getInt(phone.getContext().getContentResolver(), - Settings.Secure.DATA_ROAMING) > 0; - } catch (SettingNotFoundException snfe) { - return false; - } - } - - public ArrayList getAllPdps() { - ArrayList pdps = (ArrayList)pdpList.clone(); - return pdps; - } - - private boolean isDataAllowed() { - boolean roaming = phone.getServiceState().getRoaming(); - return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()); - } - - //****** Called from ServiceStateTracker - /** - * Invoked when ServiceStateTracker observes a transition from GPRS - * attach to detach. - */ - private void onGprsDetached() - { - /* - * We presently believe it is unnecessary to tear down the PDP context - * when GPRS detaches, but we should stop the network polling. - */ - stopNetStatPoll(); - phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED); - } - - private void onGprsAttached() { - if (state == State.CONNECTED) { - startNetStatPoll(); - phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED); - } else { - if (state == State.FAILED) { - cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED); - nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; - } - trySetupData(Phone.REASON_GPRS_ATTACHED); - } - } - - private boolean trySetupData(String reason) - { - if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); - - Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted); - - if (phone.getSimulatedRadioControl() != null) { - // Assume data is connected on the simulator - // FIXME this can be improved - setState(State.CONNECTED); - phone.notifyDataConnection(reason); - - Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); - return true; - } - - int gprsState = phone.mSST.getCurrentGprsState(); - boolean roaming = phone.getServiceState().getRoaming(); - - if ((state == State.IDLE || state == State.SCANNING) - && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) - && phone.mSIMRecords.getRecordsLoaded() - && phone.getState() == Phone.State.IDLE - && isDataAllowed() - && !mIsPsRestricted ) { - - if (state == State.IDLE) { - waitingApns = buildWaitingApns(); - if (waitingApns.isEmpty()) { - if (DBG) log("No APN found"); - notifyNoData(PdpConnection.PdpFailCause.BAD_APN); - return false; - } else { - log ("Create from allApns : " + apnListToString(allApns)); - } - } - - if (DBG) { - log ("Setup watingApns : " + apnListToString(waitingApns)); - } - return setupData(reason); - } else { - if (DBG) - log("trySetupData: Not ready for data: " + - " dataState=" + state + - " gprsState=" + gprsState + - " sim=" + phone.mSIMRecords.getRecordsLoaded() + - " UMTS=" + phone.mSST.isConcurrentVoiceAndData() + - " phoneState=" + phone.getState() + - " dataEnabled=" + getAnyDataEnabled() + - " roaming=" + roaming + - " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + - " ps restricted=" + mIsPsRestricted); - return false; - } - } - - /** - * If tearDown is true, this only tears down a CONNECTED session. Presently, - * there is no mechanism for abandoning an INITING/CONNECTING session, - * but would likely involve cancelling pending async requests or - * setting a flag or new state to ignore them when they came in - * @param tearDown true if the underlying PdpConnection should be - * disconnected. - * @param reason reason for the clean up. - */ - private void cleanUpConnection(boolean tearDown, String reason) { - if (DBG) log("Clean up connection due to " + reason); - - // Clear the reconnect alarm, if set. - if (mReconnectIntent != null) { - AlarmManager am = - (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); - am.cancel(mReconnectIntent); - mReconnectIntent = null; - } - - for (PdpConnection pdp : pdpList) { - if (tearDown) { - Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason); - pdp.disconnect(msg); - } else { - pdp.clearSettings(); - } - } - stopNetStatPoll(); - - /* - * If we've been asked to tear down the connection, - * set the state to DISCONNECTING. However, there's - * a race that can occur if for some reason we were - * already in the IDLE state. In that case, the call - * to pdp.disconnect() above will immediately post - * a message to the handler thread that the disconnect - * is done, and if the handler runs before the code - * below does, the handler will have set the state to - * IDLE before the code below runs. If we didn't check - * for that, future calls to trySetupData would fail, - * and we would never get out of the DISCONNECTING state. - */ - if (!tearDown) { - setState(State.IDLE); - phone.notifyDataConnection(reason); - mActiveApn = null; - } else if (state != State.IDLE) { - setState(State.DISCONNECTING); - } - } - - /** - * @param types comma delimited list of APN types - * @return array of APN types - */ - private String[] parseTypes(String types) { - String[] result; - // If unset, set to DEFAULT. - if (types == null || types.equals("")) { - result = new String[1]; - result[0] = Phone.APN_TYPE_ALL; - } else { - result = types.split(","); - } - return result; - } - - private ArrayList createApnList(Cursor cursor) { - ArrayList result = new ArrayList(); - if (cursor.moveToFirst()) { - do { - String[] types = parseTypes( - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); - ApnSetting apn = new ApnSetting( - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), - types); - result.add(apn); - } while (cursor.moveToNext()); - } - return result; - } - - private PdpConnection findFreePdp() { - for (PdpConnection pdp : pdpList) { - if (pdp.getState() == PdpConnection.PdpState.INACTIVE) { - return pdp; - } - } - return null; - } - - private boolean setupData(String reason) { - ApnSetting apn; - PdpConnection pdp; - - apn = getNextApn(); - if (apn == null) return false; - pdp = findFreePdp(); - if (pdp == null) { - if (DBG) log("setupData: No free PdpConnection found!"); - return false; - } - mActiveApn = apn; - mActivePdp = pdp; - - Message msg = obtainMessage(); - msg.what = EVENT_DATA_SETUP_COMPLETE; - msg.obj = reason; - pdp.connect(apn, msg); - - setState(State.INITING); - phone.notifyDataConnection(reason); - return true; - } - - String getInterfaceName(String apnType) { - if (mActivePdp != null - && (apnType == null || mActiveApn.canHandleType(apnType))) { - return mActivePdp.getInterface(); - } - return null; - } - - String getIpAddress(String apnType) { - if (mActivePdp != null - && (apnType == null || mActiveApn.canHandleType(apnType))) { - return mActivePdp.getIpAddress(); - } - return null; - } - - String getGateway(String apnType) { - if (mActivePdp != null - && (apnType == null || mActiveApn.canHandleType(apnType))) { - return mActivePdp.getGatewayAddress(); - } - return null; - } - - String[] getDnsServers(String apnType) { - if (mActivePdp != null - && (apnType == null || mActiveApn.canHandleType(apnType))) { - return mActivePdp.getDnsServers(); - } - return null; - } - - private boolean - pdpStatesHasCID (ArrayList states, int cid) - { - for (int i = 0, s = states.size() ; i < s ; i++) { - if (states.get(i).cid == cid) return true; - } - - return false; - } - - private boolean - pdpStatesHasActiveCID (ArrayList states, int cid) - { - for (int i = 0, s = states.size() ; i < s ; i++) { - if (states.get(i).cid == cid) return states.get(i).active; - } - - return false; - } - - /** - * Handles changes to the APN database. - */ - private void onApnChanged() { - boolean isConnected; - - isConnected = (state != State.IDLE && state != State.FAILED); - - // The "current" may no longer be valid. MMS depends on this to send properly. - phone.updateCurrentCarrierInProvider(); - - // TODO: It'd be nice to only do this if the changed entrie(s) - // match the current operator. - createAllApnList(); - if (state != State.DISCONNECTING) { - cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); - if (!isConnected) { - trySetupData(Phone.REASON_APN_CHANGED); - } - } - } - - /** - * @param explicitPoll if true, indicates that *we* polled for this - * update while state == CONNECTED rather than having it delivered - * via an unsolicited response (which could have happened at any - * previous state - */ - private void - onPdpStateChanged (AsyncResult ar, boolean explicitPoll) - { - ArrayList pdpStates; - - pdpStates = (ArrayList)(ar.result); - - if (ar.exception != null) { - // This is probably "radio not available" or something - // of that sort. If so, the whole connection is going - // to come down soon anyway - return; - } - - - // This is how things are supposed to work: - // The PDP list is supposed to be empty of the CID - // when it disconnects - - if (state == State.CONNECTED - && !pdpStatesHasCID(pdpStates, cidActive)) { - - // It looks like the PDP context has deactivated - // Tear everything down and try to reconnect - - Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); - - // Add an event log when the network drops PDP - int cid = -1; - GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); - if (loc != null) cid = loc.getCid(); - - EventLog.List val = new EventLog.List(cid, - TelephonyManager.getDefault().getNetworkType()); - - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); - - cleanUpConnection(true, null); - - return; - } - - if (true) { - // - // Workaround for issue #655426 - // - - // -------------------------- - - // This is how some things work now: the PDP context is still - // listed with active = false, which makes it hard to - // distinguish an activating context from an activated-and-then - // deactivated one. - // - // Here, we only consider this authoritative if we asked for the - // PDP list. If it was an unsolicited response, we poll again - // to make sure everyone agrees on the initial state - - if (state == State.CONNECTED - && !pdpStatesHasActiveCID(pdpStates, cidActive)) { - - if (!explicitPoll) { - // We think it disconnected but aren't sure...poll from our side - phone.mCM.getPDPContextList( - this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); - } else { - Log.i(LOG_TAG, "PDP connection has dropped (active=false case). " - + " Reconnecting"); - - // Log the network drop on the event log. - int cid = -1; - GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); - if (loc != null) cid = loc.getCid(); - - EventLog.List val = new EventLog.List(cid, - TelephonyManager.getDefault().getNetworkType()); - - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); - - cleanUpConnection(true, null); - } - } - } - } - - private void notifyDefaultData(String reason) { - setupDnsProperties(); - setState(State.CONNECTED); - phone.notifyDataConnection(reason); - startNetStatPoll(); - // reset reconnect timer - nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; - } - - private void setupDnsProperties() { - int mypid = android.os.Process.myPid(); - String[] servers = getDnsServers(null); - String propName; - String propVal; - int count; - - count = 0; - for (int i = 0; i < servers.length; i++) { - String serverAddr = servers[i]; - if (!TextUtils.equals(serverAddr, "0.0.0.0")) { - SystemProperties.set("net.dns" + (i+1) + "." + mypid, serverAddr); - count++; - } - } - for (int i = count+1; i <= 4; i++) { - propName = "net.dns" + i + "." + mypid; - propVal = SystemProperties.get(propName); - if (propVal.length() != 0) { - SystemProperties.set(propName, ""); - } - } - /* - * Bump the property that tells the name resolver library - * to reread the DNS server list from the properties. - */ - propVal = SystemProperties.get("net.dnschange"); - if (propVal.length() != 0) { - try { - int n = Integer.parseInt(propVal); - SystemProperties.set("net.dnschange", "" + (n+1)); - } catch (NumberFormatException e) { - } - } - } - - /** - * This is a kludge to deal with the fact that - * the PDP state change notification doesn't always work - * with certain RIL impl's/basebands - * - */ - private void - startPeriodicPdpPoll() - { - removeMessages(EVENT_POLL_PDP); - - sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); - } - - private void resetPollStats() { - txPkts = -1; - rxPkts = -1; - sentSinceLastRecv = 0; - mNoRecvPollCount = 0; - } - - private void doRecovery() { - if (state == State.CONNECTED) { - int maxPdpReset = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, - DEFAULT_MAX_PDP_RESET_FAIL); - if (mPdpResetCount < maxPdpReset) { - mPdpResetCount++; - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, sentSinceLastRecv); - cleanUpConnection(true, Phone.REASON_PDP_RESET); - } else { - mPdpResetCount = 0; - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); - phone.mSST.reRegisterNetwork(null); - } - // TODO: Add increasingly drastic recovery steps, eg, - // reset the radio, reset the device. - } - } - - private void - startNetStatPoll() - { - if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) { - Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); - resetPollStats(); - netStatPollEnabled = true; - mPollNetStat.run(); - } - } - - private void - stopNetStatPoll() - { - netStatPollEnabled = false; - removeCallbacks(mPollNetStat); - Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); - } - - private void - restartRadio() - { - Log.d(LOG_TAG, "************TURN OFF RADIO**************"); - cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); - phone.mCM.setRadioPower(false, null); - /* Note: no need to call setRadioPower(true). Assuming the desired - * radio power state is still ON (as tracked by ServiceStateTracker), - * ServiceStateTracker will call setRadioPower when it receives the - * RADIO_STATE_CHANGED notification for the power off. And if the - * desired power state has changed in the interim, we don't want to - * override it with an unconditional power on. - */ - - int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); - SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); - } - - Runnable mPollNetStat = new Runnable() - { - - public void run() { - long sent, received; - long preTxPkts = -1, preRxPkts = -1; - - Activity newActivity; - - preTxPkts = txPkts; - preRxPkts = rxPkts; - - try { - txPkts = netstat.getMobileTxPackets(); - rxPkts = netstat.getMobileRxPackets(); - } catch (RemoteException e) { - txPkts = 0; - rxPkts = 0; - } - - //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); - - if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { - sent = txPkts - preTxPkts; - received = rxPkts - preRxPkts; - - if ( sent > 0 && received > 0 ) { - sentSinceLastRecv = 0; - newActivity = Activity.DATAINANDOUT; - mPdpResetCount = 0; - } else if (sent > 0 && received == 0) { - if (phone.mCT.state == Phone.State.IDLE) { - sentSinceLastRecv += sent; - } else { - sentSinceLastRecv = 0; - } - newActivity = Activity.DATAOUT; - } else if (sent == 0 && received > 0) { - sentSinceLastRecv = 0; - newActivity = Activity.DATAIN; - mPdpResetCount = 0; - } else if (sent == 0 && received == 0) { - newActivity = Activity.NONE; - } else { - sentSinceLastRecv = 0; - newActivity = Activity.NONE; - } - - if (activity != newActivity && mIsScreenOn) { - activity = newActivity; - phone.notifyDataActivity(); - } - } - - int watchdogTrigger = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, NUMBER_SENT_PACKETS_OF_HANG); - - if (sentSinceLastRecv >= watchdogTrigger) { - // we already have NUMBER_SENT_PACKETS sent without ack - if (mNoRecvPollCount == 0) { - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, - sentSinceLastRecv); - } - - int noRecvPollLimit = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); - - if (mNoRecvPollCount < noRecvPollLimit) { - // It's possible the PDP context went down and we weren't notified. - // Start polling the context list in an attempt to recover. - if (DBG) log("no DATAIN in a while; polling PDP"); - phone.mCM.getPDPContextList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); - - mNoRecvPollCount++; - - // Slow down the poll interval to let things happen - netStatPollPeriod = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, POLL_NETSTAT_SLOW_MILLIS); - } else { - if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + - " pkts since last received"); - // We've exceeded the threshold. Run ping test as a final check; - // it will proceed with recovery if ping fails. - stopNetStatPoll(); - Thread pingTest = new Thread() { - public void run() { - runPingTest(); - } - }; - mPingTestActive = true; - pingTest.start(); - } - } else { - mNoRecvPollCount = 0; - if (mIsScreenOn) { - netStatPollPeriod = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); - } else { - netStatPollPeriod = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, - POLL_NETSTAT_SCREEN_OFF_MILLIS); - } - } - - if (netStatPollEnabled) { - mDataConnectionTracker.postDelayed(this, netStatPollPeriod); - } - } - }; - - private void runPingTest () { - int status = -1; - try { - String address = Settings.Gservices.getString(mResolver, - Settings.Gservices.PDP_WATCHDOG_PING_ADDRESS); - int deadline = Settings.Gservices.getInt(mResolver, - Settings.Gservices.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE); - if (DBG) log("pinging " + address + " for " + deadline + "s"); - if (address != null && !NULL_IP.equals(address)) { - Process p = Runtime.getRuntime() - .exec("ping -c 1 -i 1 -w "+ deadline + " " + address); - status = p.waitFor(); - } - } catch (IOException e) { - Log.w(LOG_TAG, "ping failed: IOException"); - } catch (Exception e) { - Log.w(LOG_TAG, "exception trying to ping"); - } - - if (status == 0) { - // ping succeeded. False alarm. Reset netStatPoll. - // ("-1" for this event indicates a false alarm) - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, -1); - mPdpResetCount = 0; - sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL)); - } else { - // ping failed. Proceed with recovery. - sendMessage(obtainMessage(EVENT_START_RECOVERY)); - } - } - - /** - * Returns true if the last fail cause is something that - * seems like it deserves an error notification. - * Transient errors are ignored - */ - private boolean - shouldPostNotification(PdpConnection.PdpFailCause cause) - { - boolean shouldPost = true; - // TODO CHECK - // if (dataLink != null) { - // shouldPost = dataLink.getLastLinkExitCode() != DataLink.EXIT_OPEN_FAILED; - //} - return (shouldPost && cause != PdpConnection.PdpFailCause.UNKNOWN); - } - - /** - * Return true if data connection need to be setup after disconnected due to - * reason. - * - * @param reason the reason why data is disconnected - * @return true if try setup data connection is need for this reason - */ - private boolean retryAfterDisconnected(String reason) { - boolean retry = true; - - if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) || - Phone.REASON_DATA_DISABLED.equals(reason) || - Phone.REASON_PS_RESTRICT_ENABLED.equals(reason)) { - retry = false; - } - return retry; - } - - private void reconnectAfterFail(PdpFailCause lastFailCauseCode, String reason) { - if (state == State.FAILED) { - Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " - + (nextReconnectDelay / 1000) + "s"); - - AlarmManager am = - (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(INTENT_RECONNECT_ALARM); - intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); - mReconnectIntent = PendingIntent.getBroadcast( - phone.getContext(), 0, intent, 0); - am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + nextReconnectDelay, - mReconnectIntent); - - // double it for next time - nextReconnectDelay *= 2; - if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { - nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; - } - - if (!shouldPostNotification(lastFailCauseCode)) { - Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " - + "-- likely transient error"); - } else { - notifyNoData(lastFailCauseCode); - } - } - } - - private void notifyNoData(PdpConnection.PdpFailCause lastFailCauseCode) { - setState(State.FAILED); - } - - - private void log(String s) { - Log.d(LOG_TAG, "[DataConnectionTracker] " + s); - } - - //***** Overridden from Handler - public void - handleMessage (Message msg) - { - AsyncResult ar; - String reason = null; - - switch (msg.what) { - case EVENT_RECORDS_LOADED: - createAllApnList(); - if (state == State.FAILED) { - cleanUpConnection(false, null); - } - sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); - break; - - case EVENT_ENABLE_NEW_APN: - // TODO: To support simultaneous PDP contexts, this should really only call - // cleanUpConnection if it needs to free up a PdpConnection. - reason = Phone.REASON_APN_SWITCHED; - cleanUpConnection(true, reason); - break; - - case EVENT_TRY_SETUP_DATA: - if (msg.obj instanceof String) { - reason = (String)msg.obj; - } - - trySetupData(reason); - break; - - case EVENT_RESTORE_DEFAULT_APN: - if (DBG) Log.d(LOG_TAG, "Restore default APN"); - setEnabled(Phone.APN_TYPE_MMS, false); - if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { - cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN); - mRequestedApnType = Phone.APN_TYPE_DEFAULT; - } - break; - - case EVENT_ROAMING_OFF: - trySetupData(Phone.REASON_ROAMING_OFF); - break; - - case EVENT_GPRS_DETACHED: - onGprsDetached(); - break; - - case EVENT_GPRS_ATTACHED: - onGprsAttached(); - break; - - case EVENT_ROAMING_ON: - if (getDataOnRoamingEnabled()) { - trySetupData(Phone.REASON_ROAMING_ON); - } else { - if (DBG) log("Tear down data connection on roaming."); - cleanUpConnection(true, Phone.REASON_ROAMING_ON); - } - break; - - case EVENT_RADIO_AVAILABLE: - if (phone.getSimulatedRadioControl() != null) { - // Assume data is connected on the simulator - // FIXME this can be improved - setState(State.CONNECTED); - phone.notifyDataConnection(null); - - - Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); - } - - if (state != State.IDLE) { - cleanUpConnection(true, null); - } - break; - - case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: - // Make sure our reconnect delay starts at the initial value - // next time the radio comes on - nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; - - if (phone.getSimulatedRadioControl() != null) { - // Assume data is connected on the simulator - // FIXME this can be improved - Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); - } else { - if (DBG) log("Radio is off and clean up all connection"); - // TODO: Should we reset mRequestedApnType to "default"? - cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); - } - break; - - case EVENT_DATA_SETUP_COMPLETE: - ar = (AsyncResult) msg.obj; - if (ar.userObj instanceof String) { - reason = (String) ar.userObj; - } - - if (ar.exception == null) { - // everything is setup - - // arg1 contains CID for this PDP context - cidActive = msg.arg1; - /* - * We may have switched away from the default PDP context - * in order to enable a "special" APN (e.g., for MMS - * traffic). Set a timer to switch back and/or disable the - * special APN, so that a negligient application doesn't - * permanently prevent data connectivity. What we are - * protecting against here is not malicious apps, but - * rather an app that inadvertantly fails to reset to the - * default APN, or that dies before doing so. - */ - if (dataEnabled[APN_MMS_ID]) { - removeMessages(EVENT_RESTORE_DEFAULT_APN); - sendMessageDelayed( - obtainMessage(EVENT_RESTORE_DEFAULT_APN), - getRestoreDefaultApnDelay()); - } - if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { - SystemProperties.set("gsm.defaultpdpcontext.active", "true"); - if (canSetPreferApn && preferredApn == null) { - Log.d(LOG_TAG, "PREFERED APN is null"); - preferredApn = mActiveApn; - setPreferredApn(preferredApn.id); - } - } else { - SystemProperties.set("gsm.defaultpdpcontext.active", "false"); - } - notifyDefaultData(reason); - - // TODO: For simultaneous PDP support, we need to build another - // trigger another TRY_SETUP_DATA for the next APN type. (Note - // that the existing connection may service that type, in which - // case we should try the next type, etc. - } else { - PdpConnection.PdpFailCause cause; - cause = (PdpConnection.PdpFailCause) (ar.result); - if(DBG) - log("PDP setup failed " + cause); - // Log this failure to the Event Logs. - if (cause == PdpConnection.PdpFailCause.BAD_APN || - cause == PdpConnection.PdpFailCause.BAD_PAP_SECRET || - cause == PdpConnection.PdpFailCause.BARRED || - cause == PdpConnection.PdpFailCause.RADIO_ERROR_RETRY || - cause == PdpConnection.PdpFailCause.SUSPENED_TEMPORARY || - cause == PdpConnection.PdpFailCause.UNKNOWN || - cause == PdpConnection.PdpFailCause.USER_AUTHENTICATION) { - int cid = -1; - GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); - if (loc != null) cid = loc.getCid(); - - EventLog.List val = new EventLog.List( - cause.ordinal(), cid, - TelephonyManager.getDefault().getNetworkType()); - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); - } - // No try for permanent failure - if (cause.isPermanentFail()) { - notifyNoData(cause); - } - - if (tryNextApn(cause)) { - waitingApns.remove(0); - if (waitingApns.isEmpty()) { - // No more to try, start delayed retry - startDelayedRetry(cause, reason); - } else { - // we still have more apns to try - setState(State.SCANNING); - // Wait a bit before trying the next APN, so that - // we're not tying up the RIL command channel - sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), - RECONNECT_DELAY_INITIAL_MILLIS); - } - } else { - startDelayedRetry(cause, reason); - } - } - break; - - case EVENT_DISCONNECT_DONE: - if(DBG) log("EVENT_DISCONNECT_DONE"); - ar = (AsyncResult) msg.obj; - if (ar.userObj instanceof String) { - reason = (String) ar.userObj; - } - setState(State.IDLE); - phone.notifyDataConnection(reason); - mActiveApn = null; - if ( retryAfterDisconnected(reason) ) { - trySetupData(reason); - } - break; - - case EVENT_PDP_STATE_CHANGED: - ar = (AsyncResult) msg.obj; - - onPdpStateChanged(ar, false); - break; - - case EVENT_GET_PDP_LIST_COMPLETE: - ar = (AsyncResult) msg.obj; - - onPdpStateChanged(ar, true); - break; - - case EVENT_POLL_PDP: - /* See comment in startPeriodicPdpPoll */ - ar = (AsyncResult) msg.obj; - - if (!(state == State.CONNECTED)) { - // not connected; don't poll anymore - break; - } - - phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); - - sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), - POLL_PDP_MILLIS); - break; - - case EVENT_VOICE_CALL_STARTED: - if (state == State.CONNECTED && - !phone.mSST.isConcurrentVoiceAndData()) { - stopNetStatPoll(); - phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); - } - break; - - case EVENT_VOICE_CALL_ENDED: - if (state == State.CONNECTED) { - if (!phone.mSST.isConcurrentVoiceAndData()) { - startNetStatPoll(); - phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); - } else { - // clean slate after call end. - resetPollStats(); - } - } else { - // in case data setup was attempted when we were on a voice call - trySetupData(Phone.REASON_VOICE_CALL_ENDED); - } - break; - - case EVENT_START_NETSTAT_POLL: - mPingTestActive = false; - startNetStatPoll(); - break; - - case EVENT_START_RECOVERY: - mPingTestActive = false; - doRecovery(); - break; - - case EVENT_APN_CHANGED: - onApnChanged(); - break; - - case EVENT_PS_RESTRICT_ENABLED: - /** - * We don't need to explicitly to tear down the PDP context - * when PS restricted is enabled. The base band will deactive - * PDP context and notify us with PDP_CONTEXT_CHANGED. - * But we should stop the network polling and prevent reset PDP. - */ - Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); - stopNetStatPoll(); - mIsPsRestricted = true; - break; - - case EVENT_PS_RESTRICT_DISABLED: - /** - * When PS restrict is removed, we need setup PDP connection if - * PDP connection is down. - */ - Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); - mIsPsRestricted = false; - if (state == State.CONNECTED) { - startNetStatPoll(); - } else { - if (state == State.FAILED) { - cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED); - nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; - } - trySetupData(Phone.REASON_PS_RESTRICT_ENABLED); - } - break; - - } - } - - private boolean tryNextApn(PdpFailCause cause) { - return (cause != PdpFailCause.RADIO_NOT_AVIALABLE) - && (cause != PdpFailCause.RADIO_OFF) - && (cause != PdpFailCause.RADIO_ERROR_RETRY) - && (cause != PdpFailCause.NO_SIGNAL) - && (cause != PdpFailCause.SIM_LOCKED); - } - - private int getRestoreDefaultApnDelay() { - String restoreApnDelayStr = SystemProperties.get(APN_RESTORE_DELAY_PROP_NAME); - - if (restoreApnDelayStr != null && restoreApnDelayStr.length() != 0) { - try { - return Integer.valueOf(restoreApnDelayStr); - } catch (NumberFormatException e) { - } - } - return RESTORE_DEFAULT_APN_DELAY; - } - - /** - * Based on the sim operator numeric, create a list for all possible pdps - * with all apns associated with that pdp - * - * - */ - private void createAllApnList() { - allApns = new ArrayList(); - String operator = phone.mSIMRecords.getSIMOperatorNumeric(); - - if (operator != null) { - String selection = "numeric = '" + operator + "'"; - - Cursor cursor = phone.getContext().getContentResolver().query( - Telephony.Carriers.CONTENT_URI, null, selection, null, null); - - if (cursor != null) { - if (cursor.getCount() > 0) { - allApns = createApnList(cursor); - // TODO: Figure out where this fits in. This basically just - // writes the pap-secrets file. No longer tied to PdpConnection - // object. Not used on current platform (no ppp). - //PdpConnection pdp = pdpList.get(pdp_name); - //if (pdp != null && pdp.dataLink != null) { - // pdp.dataLink.setPasswordInfo(cursor); - //} - } - cursor.close(); - } - } - - if (allApns.isEmpty()) { - if (DBG) log("No APN found for carrier: " + operator); - preferredApn = null; - notifyNoData(PdpConnection.PdpFailCause.BAD_APN); - } else { - preferredApn = getPreferredApn(); - Log.d(LOG_TAG, "Get PreferredAPN"); - if (preferredApn != null && !preferredApn.numeric.equals(operator)) { - preferredApn = null; - setPreferredApn(-1); - } - } - } - - private void createAllPdpList() { - pdpList = new ArrayList(); - PdpConnection pdp; - - for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { - pdp = new PdpConnection(phone); - pdpList.add(pdp); - } - } - - /** - * - * @return waitingApns list to be used to create PDP - * error when waitingApns.isEmpty() - */ - private ArrayList buildWaitingApns() { - ArrayList apnList = new ArrayList(); - String operator = phone.mSIMRecords.getSIMOperatorNumeric(); - - if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { - if (canSetPreferApn && preferredApn != null) { - Log.i(LOG_TAG, "Preferred APN:" + operator + ":" - + preferredApn.numeric + ":" + preferredApn); - if (preferredApn.numeric.equals(operator)) { - Log.i(LOG_TAG, "Waiting APN set to preferred APN"); - apnList.add(preferredApn); - return apnList; - } else { - setPreferredApn(-1); - preferredApn = null; - } - } - } - - if (allApns != null) { - for (ApnSetting apn : allApns) { - if (apn.canHandleType(mRequestedApnType)) { - apnList.add(apn); - } - } - } - return apnList; - } - - /** - * Get next apn in waitingApns - * @return the first apn found in waitingApns, null if none - */ - private ApnSetting getNextApn() { - ArrayList list = waitingApns; - ApnSetting apn = null; - - if (list != null) { - if (!list.isEmpty()) { - apn = list.get(0); - } - } - return apn; - } - - private String apnListToString (ArrayList apns) { - StringBuilder result = new StringBuilder(); - for (int i = 0, size = apns.size(); i < size; i++) { - result.append('[') - .append(apns.get(i).toString()) - .append(']'); - } - return result.toString(); - } - - private void startDelayedRetry(PdpConnection.PdpFailCause cause, String reason) { - notifyNoData(cause); - if (mRequestedApnType != Phone.APN_TYPE_DEFAULT) { - sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN)); - } - else { - reconnectAfterFail(cause, reason); - } - } - - private void setPreferredApn(int pos) { - if (!canSetPreferApn) { - return; - } - - ContentResolver resolver = phone.getContext().getContentResolver(); - resolver.delete(PREFERAPN_URI, null, null); - - if (pos >= 0) { - ContentValues values = new ContentValues(); - values.put(APN_ID, pos); - resolver.insert(PREFERAPN_URI, values); - } - } - - private ApnSetting getPreferredApn() { - if (allApns.isEmpty()) { - return null; - } - - Cursor cursor = phone.getContext().getContentResolver().query( - PREFERAPN_URI, new String[] { "_id", "name", "apn" }, - null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); - - if (cursor != null) { - canSetPreferApn = true; - } else { - canSetPreferApn = false; - } - - if (canSetPreferApn && cursor.getCount() > 0) { - int pos; - cursor.moveToFirst(); - pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); - for(ApnSetting p:allApns) { - if (p.id == pos && p.canHandleType(mRequestedApnType)) { - cursor.close(); - return p; - } - } - } - - if (cursor != null) { - cursor.close(); - } - - return null; - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/DataLink.java b/telephony/java/com/android/internal/telephony/gsm/DataLink.java deleted file mode 100644 index b822ab4..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/DataLink.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.gsm; - -import android.os.Handler; -import android.os.Registrant; - -/** - * Base class representing the data link layer (eg, PPP). - * - * {@hide} - */ -abstract class DataLink extends Handler implements DataLinkInterface { - - /** Registrant for link status change notifications. */ - Registrant mLinkChangeRegistrant; - - protected DataConnectionTracker dataConnection; - - DataLink(DataConnectionTracker dc) { - dataConnection = dc; - } - - public void setOnLinkChange(Handler h, int what, Object obj) { - mLinkChangeRegistrant = new Registrant(h, what, obj); - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/DataLinkInterface.java b/telephony/java/com/android/internal/telephony/gsm/DataLinkInterface.java deleted file mode 100644 index bca63f2..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/DataLinkInterface.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.gsm; - -import android.database.Cursor; -import android.os.Handler; - -/** - * Data link interface. - * - * {@hide} - */ -interface DataLinkInterface { - /** - * Link state enumeration. - * - */ - enum LinkState { - LINK_UNKNOWN, - LINK_UP, - LINK_DOWN, - LINK_EXITED - } - - /** Normal exit */ - final static int EXIT_OK = 0; - /** Open failed */ - final static int EXIT_OPEN_FAILED = 7; - - /** - * Sets the handler for link state change events. - * - * @param h Handler - * @param what User-defined message code - * @param obj User object - */ - void setOnLinkChange(Handler h, int what, Object obj); - - /** - * Sets up the data link. - */ - void connect(); - - /** - * Tears down the data link. - */ - void disconnect(); - - /** - * Returns the exit code for a data link failure. - * - * @return exit code - */ - int getLastLinkExitCode(); - - /** - * Sets password information that may be required by the data link - * (eg, PAP secrets). - * - * @param cursor cursor to carriers table - */ - void setPasswordInfo(Cursor cursor); -} diff --git a/telephony/java/com/android/internal/telephony/gsm/DriverCall.java b/telephony/java/com/android/internal/telephony/gsm/DriverCall.java deleted file mode 100644 index aab885a..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/DriverCall.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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.gsm; -import com.android.internal.telephony.*; - -import android.util.Log; -import java.lang.Comparable; -import android.telephony.PhoneNumberUtils; - -/** - * {@hide} - */ -public class DriverCall implements Comparable -{ - static final String LOG_TAG = "GSM"; - - public enum State { - ACTIVE, - HOLDING, - DIALING, // MO call only - ALERTING, // MO call only - INCOMING, // MT call only - WAITING; // MT call only - // If you add a state, make sure to look for the switch() - // statements that use this enum - } - - public int index; - public boolean isMT; - public State state; // May be null if unavail - public boolean isMpty; - public String number; - public int TOA; - public boolean isVoice; - public int als; - public int numberPresentation; - - /** returns null on error */ - static DriverCall - fromCLCCLine(String line) - { - DriverCall ret = new DriverCall(); - - //+CLCC: 1,0,2,0,0,\"+18005551212\",145 - // index,isMT,state,mode,isMpty(,number,TOA)? - ATResponseParser p = new ATResponseParser(line); - - try { - ret.index = p.nextInt(); - ret.isMT = p.nextBoolean(); - ret.state = stateFromCLCC(p.nextInt()); - - ret.isVoice = (0 == p.nextInt()); - ret.isMpty = p.nextBoolean(); - - // use ALLOWED as default presentation while parsing CLCC - ret.numberPresentation = Connection.PRESENTATION_ALLOWED; - - if (p.hasMore()) { - // Some lame implementations return strings - // like "NOT AVAILABLE" in the CLCC line - ret.number = PhoneNumberUtils.extractNetworkPortion( - p.nextString()); - - if (ret.number.length() == 0) { - ret.number = null; - } - - ret.TOA = p.nextInt(); - - // Make sure there's a leading + on addresses with a TOA - // of 145 - - ret.number = PhoneNumberUtils.stringFromStringAndTOA( - ret.number, ret.TOA); - - } - } catch (ATParseEx ex) { - Log.e(LOG_TAG,"Invalid CLCC line: '" + line + "'"); - return null; - } - - return ret; - } - - public - DriverCall() - { - } - - public String - toString() - { - return "id=" + index + "," - + (isMT ? "mt" : "mo") + "," - + state + "," - + (isVoice ? "voice" : "no_voc") + "," - + (isMpty ? "conf" : "norm") + "," - + TOA + "," + als + ",cli " + numberPresentation; - } - - public static State - stateFromCLCC(int state) throws ATParseEx - { - switch(state) { - case 0: return State.ACTIVE; - case 1: return State.HOLDING; - case 2: return State.DIALING; - case 3: return State.ALERTING; - case 4: return State.INCOMING; - case 5: return State.WAITING; - default: - throw new ATParseEx("illegal call state " + state); - } - } - - public static int - presentationFromCLIP(int cli) throws ATParseEx - { - switch(cli) { - case 0: return Connection.PRESENTATION_ALLOWED; - case 1: return Connection.PRESENTATION_RESTRICTED; - case 2: return Connection.PRESENTATION_UNKNOWN; - case 3: return Connection.PRESENTATION_PAYPHONE; - default: - throw new ATParseEx("illegal presentation " + cli); - } - } - - //***** Comparable Implementation - - /** For sorting by index */ - public int - compareTo (Object o) - { - DriverCall dc; - - dc = (DriverCall)o; - - if (index < dc.index) { - return -1; - } else if (index == dc.index) { - return 0; - } else { /*index > dc.index*/ - return 1; - } - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/EncodeException.java b/telephony/java/com/android/internal/telephony/gsm/EncodeException.java deleted file mode 100644 index d546cef..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/EncodeException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.gsm; - -/** - * {@hide} - */ -public class EncodeException extends Exception -{ - public EncodeException() - { - super(); - } - - public EncodeException(String s) - { - super(s); - } - - public EncodeException(char c) - { - super("Unencodable char: '" + c + "'"); - } -} - diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMCall.java b/telephony/java/com/android/internal/telephony/gsm/GSMCall.java deleted file mode 100644 index 4feaf21..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/GSMCall.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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.gsm; -import com.android.internal.telephony.*; -import java.util.ArrayList; -import java.util.List; - -/** - * {@hide} - */ -class GSMCall extends Call -{ - /*************************** Instance Variables **************************/ - - /*package*/ ArrayList connections = new ArrayList(); - /*package*/ State state = State.IDLE; - /*package*/ CallTracker owner; - - /***************************** Class Methods *****************************/ - - static State - stateFromDCState (DriverCall.State dcState) - { - switch (dcState) { - case ACTIVE: return State.ACTIVE; - case HOLDING: return State.HOLDING; - case DIALING: return State.DIALING; - case ALERTING: return State.ALERTING; - case INCOMING: return State.INCOMING; - case WAITING: return State.WAITING; - default: throw new RuntimeException ("illegal call state:" + dcState); - } - } - - - /****************************** Constructors *****************************/ - /*package*/ - GSMCall (CallTracker owner) - { - this.owner = owner; - } - - /************************** Overridden from Call *************************/ - - public List - getConnections() - { - // FIXME should return Collections.unmodifiableList(); - return connections; - } - - public State - getState() - { - return state; - } - - public Phone - getPhone() - { - //TODO - return null; - } - - public boolean - isMultiparty() - { - return connections.size() > 1; - } - - /** Please note: if this is the foreground call and a - * background call exists, the background call will be resumed - * because an AT+CHLD=1 will be sent - */ - public void - hangup() throws CallStateException - { - owner.hangup(this); - } - - public String - toString() - { - return state.toString(); - } - - //***** Called from GSMConnection - - /*package*/ void - attach(GSMConnection conn, DriverCall dc) - { - connections.add(conn); - - state = stateFromDCState (dc.state); - } - - /*package*/ void - attachFake(GSMConnection conn, State state) - { - connections.add(conn); - - this.state = state; - } - - /** - * Called by GSMConnection when it has disconnected - */ - void - connectionDisconnected(GSMConnection conn) - { - if (state != State.DISCONNECTED) { - /* If only disconnected connections remain, we are disconnected*/ - - boolean hasOnlyDisconnectedConnections = true; - - for (int i = 0, s = connections.size() ; i < s; i ++) { - if (connections.get(i).getState() - != State.DISCONNECTED - ) { - hasOnlyDisconnectedConnections = false; - break; - } - } - - if (hasOnlyDisconnectedConnections) { - state = State.DISCONNECTED; - } - } - } - - - /*package*/ void - detach(GSMConnection conn) - { - connections.remove(conn); - - if (connections.size() == 0) { - state = State.IDLE; - } - } - - /*package*/ boolean - update (GSMConnection conn, DriverCall dc) - { - State newState; - boolean changed = false; - - newState = stateFromDCState(dc.state); - - if (newState != state) { - state = newState; - changed = true; - } - - return changed; - } - - /** - * @return true if there's no space in this call for additional - * connections to be added via "conference" - */ - /*package*/ boolean - isFull() - { - return connections.size() == CallTracker.MAX_CONNECTIONS_PER_CALL; - } - - //***** Called from CallTracker - - - /** - * Called when this Call is being hung up locally (eg, user pressed "end") - * Note that at this point, the hangup request has been dispatched to the radio - * but no response has yet been received so update() has not yet been called - */ - void - onHangupLocal() - { - for (int i = 0, s = connections.size() - ; i < s; i++ - ) { - GSMConnection cn = (GSMConnection)connections.get(i); - - cn.onHangupLocal(); - } - } - - /** - * Called when it's time to clean up disconnected Connection objects - */ - void - clearDisconnected() - { - for (int i = connections.size() - 1 ; i >= 0 ; i--) { - GSMConnection cn = (GSMConnection)connections.get(i); - - if (cn.getState() == State.DISCONNECTED) { - connections.remove(i); - } - } - - if (connections.size() == 0) { - state = State.IDLE; - } - } -} - diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java b/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java deleted file mode 100644 index 4777892..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/GSMConnection.java +++ /dev/null @@ -1,767 +0,0 @@ -/* - * 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.gsm; -import com.android.internal.telephony.*; - -import android.content.Context; -import android.os.Handler; -import android.os.PowerManager; -import android.os.Registrant; -import android.os.Looper; -import android.os.Message; -import android.os.AsyncResult; -import android.os.SystemClock; -import android.util.Log; -import android.util.Config; -import android.telephony.PhoneNumberUtils; -import android.telephony.ServiceState; - -/** - * {@hide} - */ -public class GSMConnection extends Connection { - static final String LOG_TAG = "GSM"; - - //***** Instance Variables - - CallTracker owner; - GSMCall parent; - - String address; // MAY BE NULL!!! - String dialString; // outgoing calls only - String postDialString; // outgoing calls only - boolean isIncoming; - boolean disconnected; - - int index; // index in CallTracker.connections[], -1 if unassigned - // The GSM index is 1 + this - - /* - * These time/timespan values are based on System.currentTimeMillis(), - * i.e., "wall clock" time. - */ - long createTime; - long connectTime; - long disconnectTime; - - /* - * These time/timespan values are based on SystemClock.elapsedRealTime(), - * i.e., time since boot. They are appropriate for comparison and - * calculating deltas. - */ - long connectTimeReal; - long duration; - long holdingStartTime; // The time when the Connection last transitioned - // into HOLDING - - int nextPostDialChar; // index into postDialString - - DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; - PostDialState postDialState = PostDialState.NOT_STARTED; - int numberPresentation = Connection.PRESENTATION_ALLOWED; - - Handler h; - - private PowerManager.WakeLock mPartialWakeLock; - - //***** Event Constants - static final int EVENT_DTMF_DONE = 1; - static final int EVENT_PAUSE_DONE = 2; - static final int EVENT_NEXT_POST_DIAL = 3; - static final int EVENT_WAKE_LOCK_TIMEOUT = 4; - - //***** Constants - static final int PAUSE_DELAY_FIRST_MILLIS = 100; - static final int PAUSE_DELAY_MILLIS = 3 * 1000; - static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; - - //***** Inner Classes - - class MyHandler extends Handler { - MyHandler(Looper l) {super(l);} - - public void - handleMessage(Message msg) - { - switch (msg.what) { - case EVENT_NEXT_POST_DIAL: - case EVENT_DTMF_DONE: - case EVENT_PAUSE_DONE: - processNextPostDialChar(); - break; - case EVENT_WAKE_LOCK_TIMEOUT: - releaseWakeLock(); - break; - } - } - } - - //***** Constructors - - /** This is probably an MT call that we first saw in a CLCC response */ - /*package*/ - GSMConnection (Context context, DriverCall dc, CallTracker ct, int index) - { - createWakeLock(context); - acquireWakeLock(); - - owner = ct; - h = new MyHandler(owner.getLooper()); - - address = dc.number; - - isIncoming = dc.isMT; - createTime = System.currentTimeMillis(); - numberPresentation = dc.numberPresentation; - - this.index = index; - - parent = parentFromDCState (dc.state); - parent.attach(this, dc); - } - - /** This is an MO call, created when dialing */ - /*package*/ - GSMConnection (Context context, String dialString, CallTracker ct, GSMCall parent) - { - createWakeLock(context); - acquireWakeLock(); - - owner = ct; - h = new MyHandler(owner.getLooper()); - - this.dialString = dialString; - - this.address = PhoneNumberUtils.extractNetworkPortion(dialString); - this.postDialString = PhoneNumberUtils.extractPostDialPortion(dialString); - - index = -1; - - isIncoming = false; - createTime = System.currentTimeMillis(); - - this.parent = parent; - parent.attachFake(this, Call.State.DIALING); - } - - static boolean - equalsHandlesNulls (Object a, Object b) - { - return (a == null) ? (b == null) : a.equals (b); - } - - /*package*/ boolean - compareTo(DriverCall c) - { - // On mobile originated (MO) calls, the phone number may have changed - // due to a SIM Toolkit call control modification. - // - // We assume we know when MO calls are created (since we created them) - // and therefore don't need to compare the phone number anyway. - if (! (isIncoming || c.isMT)) return true; - - // ... but we can compare phone numbers on MT calls, and we have - // no control over when they begin, so we might as well - - String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA); - return isIncoming == c.isMT && equalsHandlesNulls(address, cAddress); - } - - public String - toString() - { - return (isIncoming ? "incoming" : "outgoing"); - } - - public String getAddress() - { - return address; - } - - - public Call getCall() - { - return parent; - } - - public long getCreateTime() - { - return createTime; - } - - public long getConnectTime() - { - return connectTime; - } - - public long getDisconnectTime() - { - return disconnectTime; - } - - public long getDurationMillis() - { - if (connectTimeReal == 0) { - return 0; - } else if (duration == 0) { - return SystemClock.elapsedRealtime() - connectTimeReal; - } else { - return duration; - } - } - - public long getHoldDurationMillis() - { - if (getState() != Call.State.HOLDING) { - // If not holding, return 0 - return 0; - } else { - return SystemClock.elapsedRealtime() - holdingStartTime; - } - } - - public DisconnectCause getDisconnectCause() - { - return cause; - } - - public boolean isIncoming() - { - return isIncoming; - } - - public Call.State getState() - { - if (disconnected) { - return Call.State.DISCONNECTED; - } else { - return super.getState(); - } - } - - public void hangup() throws CallStateException - { - if (!disconnected) { - owner.hangup(this); - } else { - throw new CallStateException ("disconnected"); - } - } - - public void separate() throws CallStateException - { - if (!disconnected) { - owner.separate(this); - } else { - throw new CallStateException ("disconnected"); - } - } - - public PostDialState getPostDialState() - { - return postDialState; - } - - public void proceedAfterWaitChar() - { - if (postDialState != PostDialState.WAIT) { - Log.w(LOG_TAG, "Connection.proceedAfterWaitChar(): Expected " - + "getPostDialState() to be WAIT but was " + postDialState); - return; - } - - setPostDialState(PostDialState.STARTED); - - processNextPostDialChar(); - } - - public void proceedAfterWildChar(String str) { - if (postDialState != PostDialState.WILD) { - Log.w(LOG_TAG, "Connection.proceedAfterWaitChar(): Expected " - + "getPostDialState() to be WILD but was " + postDialState); - return; - } - - setPostDialState(PostDialState.STARTED); - - if (false) { - boolean playedTone = false; - int len = (str != null ? str.length() : 0); - - for (int i=0; i mPendingMMIs = new ArrayList(); SimPhoneBookInterfaceManager mSimPhoneBookIntManager; SimSmsInterfaceManager mSimSmsIntManager; PhoneSubInfo mSubInfo; - boolean mDnsCheckDisabled = false; Registrant mPostDialHandler; @@ -129,44 +126,17 @@ public class GSMPhone extends PhoneBase { private String mImeiSv; private String mVmNumber; - //***** Event Constants - - static final int EVENT_RADIO_AVAILABLE = 1; - /** Supplemnetary Service Notification received. */ - static final int EVENT_SSN = 2; - static final int EVENT_SIM_RECORDS_LOADED = 3; - static final int EVENT_MMI_DONE = 4; - static final int EVENT_RADIO_ON = 5; - static final int EVENT_GET_BASEBAND_VERSION_DONE = 6; - static final int EVENT_USSD = 7; - static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 8; - static final int EVENT_GET_IMEI_DONE = 9; - static final int EVENT_GET_IMEISV_DONE = 10; - static final int EVENT_GET_SIM_STATUS_DONE = 11; - static final int EVENT_SET_CALL_FORWARD_DONE = 12; - static final int EVENT_GET_CALL_FORWARD_DONE = 13; - static final int EVENT_CALL_RING = 14; - // Used to intercept the carriere selection calls so that - // we can save the values. - static final int EVENT_SET_NETWORK_MANUAL_COMPLETE = 15; - static final int EVENT_SET_NETWORK_AUTOMATIC_COMPLETE = 16; - static final int EVENT_SET_CLIR_COMPLETE = 17; - static final int EVENT_REGISTERED_TO_NETWORK = 18; - static final int EVENT_SET_VM_NUMBER_DONE = 19; //***** Constructors public - GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier) - { + GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier) { this(context,ci,notifier, false); } public - GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) - { + GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super(notifier, context, unitTestMode); - h = new MyHandler(); mCM = ci; @@ -174,34 +144,31 @@ public class GSMPhone extends PhoneBase { mSimulatedRadioControl = (SimulatedRadioControl) ci; } - mCT = new CallTracker(this); - mSST = new ServiceStateTracker (this); - mSMS = new SMSDispatcher(this); - mSIMFileHandler = new SIMFileHandler(this); + mCM.setPhoneType(RILConstants.GSM_PHONE); + mCT = new GsmCallTracker(this); + mSST = new GsmServiceStateTracker (this); + mSMS = new GsmSMSDispatcher(this); + mIccFileHandler = new SIMFileHandler(this); mSIMRecords = new SIMRecords(this); - mDataConnection = new DataConnectionTracker (this); - mSimCard = new GsmSimCard(this); + mDataConnection = new GsmDataConnectionTracker (this); + mSimCard = new SimCard(this); if (!unitTestMode) { mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this); mSimSmsIntManager = new SimSmsInterfaceManager(this); mSubInfo = new PhoneSubInfo(this); } mStkService = StkService.getInstance(mCM, mSIMRecords, mContext, - mSIMFileHandler, mSimCard); - + (SIMFileHandler)mIccFileHandler, mSimCard); + mCM.registerForAvailable(h, EVENT_RADIO_AVAILABLE, null); mSIMRecords.registerForRecordsLoaded(h, EVENT_SIM_RECORDS_LOADED, null); - mCM.registerForOffOrNotAvailable(h, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, - null); + mCM.registerForOffOrNotAvailable(h, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); mCM.registerForOn(h, EVENT_RADIO_ON, null); mCM.setOnUSSD(h, EVENT_USSD, null); mCM.setOnSuppServiceNotification(h, EVENT_SSN, null); mCM.setOnCallRing(h, EVENT_CALL_RING, null); mSST.registerForNetworkAttach(h, EVENT_REGISTERED_TO_NETWORK, null); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false); - if (false) { try { //debugSocket = new LocalServerSocket("com.android.internal.telephony.debug"); @@ -221,7 +188,7 @@ public class GSMPhone extends PhoneBase { mCM.resetRadio(null); sock.close(); } catch (IOException ex) { - Log.w(LOG_TAG, + Log.w(LOG_TAG, "Exception accepting socket", ex); } } @@ -235,13 +202,64 @@ public class GSMPhone extends PhoneBase { Log.w(LOG_TAG, "Failure to open com.android.internal.telephony.debug socket", ex); } } + + //Change the system setting + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.CURRENT_ACTIVE_PHONE, RILConstants.GSM_PHONE); + } + + public void dispose() { + synchronized(PhoneProxy.lockForRadioTechnologyChange) { + //Unregister from all former registered events + mCM.unregisterForAvailable(h); //EVENT_RADIO_AVAILABLE + mSIMRecords.unregisterForRecordsLoaded(h); //EVENT_SIM_RECORDS_LOADED + mCM.unregisterForOffOrNotAvailable(h); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE + mCM.unregisterForOn(h); //EVENT_RADIO_ON + mSST.unregisterForNetworkAttach(h); //EVENT_REGISTERED_TO_NETWORK + mCM.unSetOnUSSD(h); + mCM.unSetOnSuppServiceNotification(h); + mCM.unSetOnCallRing(h); + + mPendingMMIs.clear(); + + //Force all referenced classes to unregister their former registered events + mStkService.dispose(); + mCT.dispose(); + mDataConnection.dispose(); + mSST.dispose(); + mIccFileHandler.dispose(); // instance of SimFileHandler + mSIMRecords.dispose(); + mSimCard.dispose(); + mSimPhoneBookIntManager.dispose(); + mSimSmsIntManager.dispose(); + mSubInfo.dispose(); + } } - + + public void removeReferences() { + this.mSimulatedRadioControl = null; + this.mStkService = null; + this.mSimPhoneBookIntManager = null; + this.mSimSmsIntManager = null; + this.mSMS = null; + this.mSubInfo = null; + this.mSIMRecords = null; + this.mIccFileHandler = null; + this.mSimCard = null; + this.mDataConnection = null; + this.mCT = null; + this.mSST = null; + } + + protected void finalize() { + if(LOCAL_DEBUG) Log.d(LOG_TAG, "GSMPhone finalized"); + } + + //***** Overridden from Phone - public ServiceState - getServiceState() - { + public ServiceState + getServiceState() { return mSST.ss; } @@ -249,15 +267,13 @@ public class GSMPhone extends PhoneBase { return mSST.cellLoc; } - public Phone.State - getState() - { + public Phone.State + getState() { return mCT.state; } public String - getPhoneName() - { + getPhoneName() { return "GSM"; } @@ -270,14 +286,12 @@ public class GSMPhone extends PhoneBase { } public int - getSignalStrengthASU() - { + getSignalStrengthASU() { return mSST.rssi == 99 ? -1 : mSST.rssi; } public boolean - getMessageWaitingIndicator() - { + getMessageWaitingIndicator() { return mSIMRecords.getVoiceMessageWaiting(); } @@ -287,8 +301,7 @@ public class GSMPhone extends PhoneBase { } public List - getPendingMmiCodes() - { + getPendingMmiCodes() { return mPendingMMIs; } @@ -309,27 +322,27 @@ public class GSMPhone extends PhoneBase { // but no data will flow ret = DataState.DISCONNECTED; } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */ - switch (mDataConnection.state) { - case FAILED: - case IDLE: - ret = DataState.DISCONNECTED; - break; + switch (mDataConnection.getState()) { + case FAILED: + case IDLE: + ret = DataState.DISCONNECTED; + break; - case CONNECTED: - case DISCONNECTING: - if ( mCT.state != Phone.State.IDLE - && !mSST.isConcurrentVoiceAndData()) { - ret = DataState.SUSPENDED; - } else { - ret = DataState.CONNECTED; - } - break; + case CONNECTED: + case DISCONNECTING: + if ( mCT.state != Phone.State.IDLE + && !mSST.isConcurrentVoiceAndData()) { + ret = DataState.SUSPENDED; + } else { + ret = DataState.CONNECTED; + } + break; - case INITING: - case CONNECTING: - case SCANNING: - ret = DataState.CONNECTING; - break; + case INITING: + case CONNECTING: + case SCANNING: + ret = DataState.CONNECTING; + break; } } @@ -340,19 +353,18 @@ public class GSMPhone extends PhoneBase { DataActivityState ret = DataActivityState.NONE; if (mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE) { - switch (mDataConnection.activity) { - - case DATAIN: - ret = DataActivityState.DATAIN; - break; + switch (mDataConnection.getActivity()) { + case DATAIN: + ret = DataActivityState.DATAIN; + break; - case DATAOUT: - ret = DataActivityState.DATAOUT; - break; + case DATAOUT: + ret = DataActivityState.DATAOUT; + break; - case DATAINANDOUT: - ret = DataActivityState.DATAINANDOUT; - break; + case DATAINANDOUT: + ret = DataActivityState.DATAINANDOUT; + break; } } @@ -371,15 +383,13 @@ public class GSMPhone extends PhoneBase { * changes to call state (including Phone and Connection changes). */ /*package*/ void - notifyCallStateChanged() - { + notifyCallStateChanged() { /* we'd love it if this was package-scoped*/ super.notifyCallStateChangedP(); } /*package*/ void - notifyNewRingingConnection(Connection c) - { + notifyNewRingingConnection(Connection c) { /* we'd love it if this was package-scoped*/ super.notifyNewRingingConnectionP(c); } @@ -387,28 +397,26 @@ public class GSMPhone extends PhoneBase { /** * Notifiy registrants of a RING event. */ - void notifyIncomingRing() { + void notifyIncomingRing() { AsyncResult ar = new AsyncResult(null, this, null); mIncomingRingRegistrants.notifyRegistrants(ar); } - + /*package*/ void - notifyDisconnect(Connection cn) - { + notifyDisconnect(Connection cn) { mDisconnectRegistrants.notifyResult(cn); } void notifyUnknownConnection() { mUnknownConnectionRegistrants.notifyResult(this); } - + void notifySuppServiceFailed(SuppService code) { mSuppServiceFailedRegistrants.notifyResult(code); } /*package*/ void - notifyServiceStateChanged(ServiceState ss) - { + notifyServiceStateChanged(ServiceState ss) { super.notifyServiceStateChangedP(ss); } @@ -418,55 +426,38 @@ public class GSMPhone extends PhoneBase { } /*package*/ void - notifySignalStrength() - { + notifySignalStrength() { mNotifier.notifySignalStrength(this); } /*package*/ void - notifyDataConnection(String reason) { - mNotifier.notifyDataConnection(this, reason); - } - - /*package*/ void notifyDataConnectionFailed(String reason) { mNotifier.notifyDataConnectionFailed(this, reason); } /*package*/ void - notifyDataActivity() { - mNotifier.notifyDataActivity(this); - } - - /*package*/ void - updateMessageWaitingIndicator(boolean mwi) - { + updateMessageWaitingIndicator(boolean mwi) { // this also calls notifyMessageWaitingIndicator() mSIMRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0); } - /*package*/ void - notifyMessageWaitingIndicator() - { + public void + notifyMessageWaitingIndicator() { mNotifier.notifyMessageWaitingChanged(this); } - /*package*/ void + public void notifyCallForwardingIndicator() { mNotifier.notifyCallForwardingChanged(this); } + // override for allowing access from other classes of this package /** - * Set a system property, unless we're in unit test mode + * {@inheritDoc} */ - - /*package*/ void - setSystemProperty(String property, String value) - { - if(getUnitTestMode()) { - return; - } - SystemProperties.set(property, value); + public final void + setSystemProperty(String property, String value) { + super.setSystemProperty(property, value); } public void registerForSuppServiceNotification( @@ -480,71 +471,57 @@ public class GSMPhone extends PhoneBase { if (mSsnRegistrants.size() == 0) mCM.setSuppServiceNotifications(false, null); } - public void - acceptCall() throws CallStateException - { + public void + acceptCall() throws CallStateException { mCT.acceptCall(); } - public void - rejectCall() throws CallStateException - { + public void + rejectCall() throws CallStateException { mCT.rejectCall(); } public void - switchHoldingAndActive() throws CallStateException - { + switchHoldingAndActive() throws CallStateException { mCT.switchWaitingOrHoldingAndActive(); } - - public boolean canConference() - { + public boolean canConference() { return mCT.canConference(); } - public boolean canDial() - { + public boolean canDial() { return mCT.canDial(); } - public void conference() throws CallStateException - { + public void conference() throws CallStateException { mCT.conference(); } - public void clearDisconnected() - { - + public void clearDisconnected() { mCT.clearDisconnected(); } - public boolean canTransfer() - { + public boolean canTransfer() { return mCT.canTransfer(); } - public void explicitCallTransfer() throws CallStateException - { + public void explicitCallTransfer() throws CallStateException { mCT.explicitCallTransfer(); } - public Call - getForegroundCall() - { + public GsmCall + getForegroundCall() { return mCT.foregroundCall; } - public Call - getBackgroundCall() - { + public GsmCall + getBackgroundCall() { return mCT.backgroundCall; } - public Call - getRingingCall() - { + public GsmCall + getRingingCall() { return mCT.ringingCall; } @@ -554,7 +531,7 @@ public class GSMPhone extends PhoneBase { return false; } - if (getRingingCall().getState() != Call.State.IDLE) { + if (getRingingCall().getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 0: rejectCall"); try { mCT.rejectCall(); @@ -563,7 +540,7 @@ public class GSMPhone extends PhoneBase { "reject failed", e); notifySuppServiceFailed(Phone.SuppService.REJECT); } - } else if (getBackgroundCall().getState() != Call.State.IDLE) { + } else if (getBackgroundCall().getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 0: hangupWaitingOrBackground"); mCT.hangupWaitingOrBackground(); @@ -580,21 +557,21 @@ public class GSMPhone extends PhoneBase { return false; } - GSMCall call = (GSMCall) getForegroundCall(); + GsmCall call = (GsmCall) getForegroundCall(); try { if (len > 1) { char ch = dialString.charAt(1); int callIndex = ch - '0'; - if (callIndex >= 1 && callIndex <= CallTracker.MAX_CONNECTIONS) { + if (callIndex >= 1 && callIndex <= GsmCallTracker.MAX_CONNECTIONS) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 1: hangupConnectionByIndex " + callIndex); mCT.hangupConnectionByIndex(call, callIndex); } } else { - if (call.getState() != Call.State.IDLE) { + if (call.getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 1: hangup foreground"); //mCT.hangupForegroundResumeBackground(); @@ -622,16 +599,16 @@ public class GSMPhone extends PhoneBase { return false; } - GSMCall call = (GSMCall) getForegroundCall(); + GsmCall call = (GsmCall) getForegroundCall(); if (len > 1) { try { char ch = dialString.charAt(1); int callIndex = ch - '0'; - GSMConnection conn = mCT.getConnectionByIndex(call, callIndex); - + GsmConnection conn = mCT.getConnectionByIndex(call, callIndex); + // gsm index starts at 1, up to 5 connections in a call, - if (conn != null && callIndex >= 1 && callIndex <= CallTracker.MAX_CONNECTIONS) { + if (conn != null && callIndex >= 1 && callIndex <= GsmCallTracker.MAX_CONNECTIONS) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 2: separate call "+ callIndex); mCT.separate(conn); @@ -647,7 +624,7 @@ public class GSMPhone extends PhoneBase { } } else { try { - if (getRingingCall().getState() != Call.State.IDLE) { + if (getRingingCall().getState() != GsmCall.State.IDLE) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "MmiCode 2: accept ringing call"); mCT.acceptCall(); @@ -756,9 +733,9 @@ public class GSMPhone extends PhoneBase { } boolean isInCall() { - Call.State foregroundCallState = getForegroundCall().getState(); - Call.State backgroundCallState = getBackgroundCall().getState(); - Call.State ringingCallState = getRingingCall().getState(); + GsmCall.State foregroundCallState = getForegroundCall().getState(); + GsmCall.State backgroundCallState = getBackgroundCall().getState(); + GsmCall.State ringingCallState = getRingingCall().getState(); return (foregroundCallState.isAlive() || backgroundCallState.isAlive() || @@ -797,15 +774,15 @@ public class GSMPhone extends PhoneBase { public boolean handlePinMmi(String dialString) { GsmMmiCode mmi = GsmMmiCode.newFromDialString(dialString, this); - + if (mmi != null && mmi.isPinCommand()) { mPendingMMIs.add(mmi); mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); mmi.processCode(); return true; } - - return false; + + return false; } public void sendUssdResponse(String ussdMessge) { @@ -814,11 +791,11 @@ public class GSMPhone extends PhoneBase { mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null)); mmi.sendUssd(ussdMessge); } - + public void sendDtmf(char c) { if (!PhoneNumberUtils.is12Key(c)) { - Log.e(LOG_TAG, + Log.e(LOG_TAG, "sendDtmf called with invalid character '" + c + "'"); } else { if (mCT.state == Phone.State.OFFHOOK) { @@ -887,7 +864,7 @@ public class GSMPhone extends PhoneBase { com.android.internal.R.string.defaultVoiceMailAlphaTag).toString(); } - return ret; + return ret; } public String getDeviceId() { @@ -898,11 +875,21 @@ public class GSMPhone extends PhoneBase { return mImeiSv; } + public String getEsn() { + Log.e(LOG_TAG, "[GSMPhone] getEsn() is a CDMA method"); + return "0"; + } + + public String getMeid() { + Log.e(LOG_TAG, "[GSMPhone] getMeid() is a CDMA method"); + return "0"; + } + public String getSubscriberId() { return mSIMRecords.imsi; } - public String getSimSerialNumber() { + public String getIccSerialNumber() { return mSIMRecords.iccid; } @@ -939,37 +926,35 @@ public class GSMPhone extends PhoneBase { private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) { switch (commandInterfaceCFReason) { - case CF_REASON_UNCONDITIONAL: - case CF_REASON_BUSY: - case CF_REASON_NO_REPLY: - case CF_REASON_NOT_REACHABLE: - case CF_REASON_ALL: - case CF_REASON_ALL_CONDITIONAL: - return true; - default: - return false; + case CF_REASON_UNCONDITIONAL: + case CF_REASON_BUSY: + case CF_REASON_NO_REPLY: + case CF_REASON_NOT_REACHABLE: + case CF_REASON_ALL: + case CF_REASON_ALL_CONDITIONAL: + return true; + default: + return false; } } private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) { switch (commandInterfaceCFAction) { - case CF_ACTION_DISABLE: - case CF_ACTION_ENABLE: - case CF_ACTION_REGISTRATION: - case CF_ACTION_ERASURE: - return true; - default: - return false; + case CF_ACTION_DISABLE: + case CF_ACTION_ENABLE: + case CF_ACTION_REGISTRATION: + case CF_ACTION_ERASURE: + return true; + default: + return false; } } - - private boolean isCfEnable(int action) { + + protected boolean isCfEnable(int action) { return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION); } - - public void getCallForwardingOption(int commandInterfaceCFReason, - Message onComplete) { - + + public void getCallForwardingOption(int commandInterfaceCFReason, Message onComplete) { if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "requesting call forwarding query."); Message resp; @@ -983,14 +968,13 @@ public class GSMPhone extends PhoneBase { } public void setCallForwardingOption(int commandInterfaceCFAction, - int commandInterfaceCFReason, - String dialingNumber, - int timerSeconds, - Message onComplete) { - - if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && - (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { - + int commandInterfaceCFReason, + String dialingNumber, + int timerSeconds, + Message onComplete) { + if ( (isValidCommandInterfaceCFAction(commandInterfaceCFAction)) && + (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) { + Message resp; if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) { resp = h.obtainMessage(EVENT_SET_CALL_FORWARD_DONE, @@ -1006,12 +990,12 @@ public class GSMPhone extends PhoneBase { resp); } } - + public void getOutgoingCallerIdDisplay(Message onComplete) { mCM.getCLIR(onComplete); } - - public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, + + public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { mCM.setCLIR(commandInterfaceCLIRMode, h.obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete)); @@ -1020,162 +1004,111 @@ public class GSMPhone extends PhoneBase { public void getCallWaiting(Message onComplete) { mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete); } - + public void setCallWaiting(boolean enable, Message onComplete) { mCM.setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete); } - + public boolean - getSimRecordsLoaded() { + getIccRecordsLoaded() { return mSIMRecords.getRecordsLoaded(); } - public SimCard - getSimCard() { + public IccCard getIccCard() { return mSimCard; } - public void + public void getAvailableNetworks(Message response) { mCM.getAvailableNetworks(response); } /** - * Small container class used to hold information relevant to + * Small container class used to hold information relevant to * the carrier selection process. operatorNumeric can be "" - * if we are looking for automatic selection. + * if we are looking for automatic selection. */ private static class NetworkSelectMessage { public Message message; public String operatorNumeric; } - - public void + + public void setNetworkSelectionModeAutomatic(Message response) { // wrap the response message in our own message along with - // an empty string (to indicate automatic selection) for the + // an empty string (to indicate automatic selection) for the // operator's id. NetworkSelectMessage nsm = new NetworkSelectMessage(); nsm.message = response; nsm.operatorNumeric = ""; - + // get the message Message msg = h.obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm); - if (LOCAL_DEBUG) + if (LOCAL_DEBUG) Log.d(LOG_TAG, "wrapping and sending message to connect automatically"); mCM.setNetworkSelectionModeAutomatic(msg); } - public void + public void selectNetworkManually(com.android.internal.telephony.gsm.NetworkInfo network, - Message response) { + Message response) { // wrap the response message in our own message along with // the operator's id. NetworkSelectMessage nsm = new NetworkSelectMessage(); nsm.message = response; nsm.operatorNumeric = network.operatorNumeric; - + // get the message Message msg = h.obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm); mCM.setNetworkSelectionModeManual(network.operatorNumeric, msg); } - - /** - * Method to retrieve the saved operator id from the Shared Preferences - */ - private String getSavedNetworkSelection() { - // open the shared preferences and search with our key. - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); - return sp.getString(NETWORK_SELECTION_KEY, ""); - } - - /** - * Method to restore the previously saved operator id, or reset to - * automatic selection, all depending upon the value in the shared - * preferences. - */ - void restoreSavedNetworkSelection(Message response) { - // retrieve the operator id - String networkSelection = getSavedNetworkSelection(); - - // set to auto if the id is empty, otherwise select the network. - if (TextUtils.isEmpty(networkSelection)) { - mCM.setNetworkSelectionModeAutomatic(response); - } else { - mCM.setNetworkSelectionModeManual(networkSelection, response); - } - } - - public void - setPreferredNetworkType(int networkType, Message response) { - mCM.setPreferredNetworkType(networkType, response); - } - - public void - getPreferredNetworkType(Message response) { - mCM.getPreferredNetworkType(response); - } public void getNeighboringCids(Message response) { mCM.getNeighboringCids(response); } - - public void setOnPostDialCharacter(Handler h, int what, Object obj) - { + + public void setOnPostDialCharacter(Handler h, int what, Object obj) { mPostDialHandler = new Registrant(h, what, obj); } - - public void setMute(boolean muted) - { + public void setMute(boolean muted) { mCT.setMute(muted); } - - public boolean getMute() - { - return mCT.getMute(); - } - - public void invokeOemRilRequestRaw(byte[] data, Message response) - { - mCM.invokeOemRilRequestRaw(data, response); - } - - public void invokeOemRilRequestStrings(String[] strings, Message response) - { - mCM.invokeOemRilRequestStrings(strings, response); + public boolean getMute() { + return mCT.getMute(); } + /** + * @deprecated + */ public void getPdpContextList(Message response) { - mCM.getPDPContextList(response); + getDataCallList(response); } - public List getCurrentPdpList () { - return mDataConnection.getAllPdps(); + public void getDataCallList(Message response) { + mCM.getDataCallList(response); } /** - * Disables the DNS check (i.e., allows "0.0.0.0"). - * Useful for lab testing environment. - * @param b true disables the check, false enables. + * @deprecated */ - public void disableDnsCheck(boolean b) { - mDnsCheckDisabled = b; - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); - SharedPreferences.Editor editor = sp.edit(); - editor.putBoolean(DNS_SERVER_CHECK_DISABLED_KEY, b); - editor.commit(); + public List getCurrentPdpList() { + ArrayList connections = new ArrayList(); + ArrayList pdp_list = new ArrayList(); + + for(int n = 0; n < connections.size(); n++) { + pdp_list.add((PdpConnection) connections.get(n)); + } + + return pdp_list; } - /** - * Returns true if the DNS check is currently disabled. - */ - public boolean isDnsCheckDisabled() { - return mDnsCheckDisabled; + public List getCurrentDataConnectionList () { + return mDataConnection.getAllDataConnections(); } public void updateServiceLocation(Message response) { @@ -1190,14 +1123,6 @@ public class GSMPhone extends PhoneBase { mSST.disableLocationUpdates(); } - public void setBandMode(int bandMode, Message response) { - mCM.setBandMode(bandMode, response); - } - - public void queryAvailableBandMode(Message response) { - mCM.queryAvailableBandMode(response); - } - public boolean getDataRoamingEnabled() { return mDataConnection.getDataOnRoamingEnabled(); } @@ -1254,7 +1179,7 @@ public class GSMPhone extends PhoneBase { // check for "default"? boolean noData = mDataConnection.getDataEnabled() && getDataConnectionState() == DataState.DISCONNECTED; - return !noData && getSimCard().getState() == SimCard.State.READY && + return !noData && getIccCard().getState() == SimCard.State.READY && getServiceState().getState() == ServiceState.STATE_IN_SERVICE && (mDataConnection.getDataOnRoamingEnabled() || !getServiceState().getRoaming()); } @@ -1265,9 +1190,8 @@ public class GSMPhone extends PhoneBase { * @param mmi MMI that is done */ /*package*/ void - onMMIDone(GsmMmiCode mmi) - { - /* Only notify complete if it's on the pending list. + onMMIDone(GsmMmiCode mmi) { + /* Only notify complete if it's on the pending list. * Otherwise, it's already been handled (eg, previously canceled). * The exception is cancellation of an incoming USSD-REQUEST, which is * not on the list. @@ -1279,9 +1203,8 @@ public class GSMPhone extends PhoneBase { } - private void - onNetworkInitiatedUssd(GsmMmiCode mmi) - { + private void + onNetworkInitiatedUssd(GsmMmiCode mmi) { mMmiCompleteRegistrants.notifyRegistrants( new AsyncResult(null, mmi, null)); } @@ -1289,18 +1212,17 @@ public class GSMPhone extends PhoneBase { /** ussdMode is one of CommandsInterface.USSD_MODE_* */ private void - onIncomingUSSD (int ussdMode, String ussdMessage) - { + onIncomingUSSD (int ussdMode, String ussdMessage) { boolean isUssdError; boolean isUssdRequest; - - isUssdRequest + + isUssdRequest = (ussdMode == CommandsInterface.USSD_MODE_REQUEST); - isUssdError + isUssdError = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY && ussdMode != CommandsInterface.USSD_MODE_REQUEST); - + // See comments in GsmMmiCode.java // USSD requests aren't finished until one // of these two events happen @@ -1327,7 +1249,7 @@ public class GSMPhone extends PhoneBase { // also, discard if there is no message to present if (!isUssdError && ussdMessage != null) { GsmMmiCode mmi; - mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage, + mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage, isUssdRequest, GSMPhone.this); onNetworkInitiatedUssd(mmi); @@ -1338,7 +1260,7 @@ public class GSMPhone extends PhoneBase { /** * Make sure the network knows our preferred setting. */ - private void syncClirSetting() { + protected void syncClirSetting() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); int clirSetting = sp.getInt(CLIR_KEY, -1); if (clirSetting >= 0) { @@ -1348,20 +1270,16 @@ public class GSMPhone extends PhoneBase { //***** Inner Classes - class MyHandler extends Handler - { - MyHandler() - { + class MyHandler extends Handler { + MyHandler() { } - MyHandler(Looper l) - { + MyHandler(Looper l) { super(l); } public void - handleMessage (Message msg) - { + handleMessage (Message msg) { AsyncResult ar; Message onComplete; @@ -1422,11 +1340,10 @@ public class GSMPhone extends PhoneBase { if (ar.exception != null) { break; } - + mImeiSv = (String)ar.result; break; - case EVENT_USSD: ar = (AsyncResult)msg.obj; @@ -1441,7 +1358,7 @@ public class GSMPhone extends PhoneBase { } break; - case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: + case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: // Some MMI requests (eg USSD) are not completed // within the course of a CommandsInterface request // If the radio shuts off or resets while one of these @@ -1450,10 +1367,10 @@ public class GSMPhone extends PhoneBase { for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) { if (mPendingMMIs.get(i).isPendingUSSD()) { mPendingMMIs.get(i).onUssdFinishedError(); - } + } } break; - + case EVENT_SSN: ar = (AsyncResult)msg.obj; SuppServiceNotification not = (SuppServiceNotification) ar.result; @@ -1474,7 +1391,7 @@ public class GSMPhone extends PhoneBase { case EVENT_SET_VM_NUMBER_DONE: ar = (AsyncResult)msg.obj; - if (SimVmNotSupportedException.class.isInstance(ar.exception)) { + if (IccVmNotSupportedException.class.isInstance(ar.exception)) { storeVoiceMailNumber(mVmNumber); ar.exception = null; } @@ -1497,15 +1414,15 @@ public class GSMPhone extends PhoneBase { onComplete.sendToTarget(); } break; - + case EVENT_CALL_RING: ar = (AsyncResult)msg.obj; if (ar.exception == null) { notifyIncomingRing(); } break; - - // handle the select network completion callbacks. + + // handle the select network completion callbacks. case EVENT_SET_NETWORK_MANUAL_COMPLETE: case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE: handleSetSelectNetwork((AsyncResult) msg.obj); @@ -1550,29 +1467,29 @@ public class GSMPhone extends PhoneBase { * Used to track the settings upon completion of the network change. */ private void handleSetSelectNetwork(AsyncResult ar) { - // look for our wrapper within the asyncresult, skip the rest if it - // is null. + // look for our wrapper within the asyncresult, skip the rest if it + // is null. if (!(ar.userObj instanceof NetworkSelectMessage)) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "unexpected result from user object."); return; } - + NetworkSelectMessage nsm = (NetworkSelectMessage) ar.userObj; - + // found the object, now we send off the message we had originally - // attached to the request. + // attached to the request. if (nsm.message != null) { if (LOCAL_DEBUG) Log.d(LOG_TAG, "sending original message to recipient"); AsyncResult.forMessage(nsm.message, ar.result, ar.exception); nsm.message.sendToTarget(); } - + // open the shared preferences editor, and write the value. // nsm.operatorNumeric is "" if we're in automatic.selection. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putString(NETWORK_SELECTION_KEY, nsm.operatorNumeric); - + // commit and log the result. if (! editor.commit()) { Log.e(LOG_TAG, "failed to commit network selection preference"); @@ -1584,17 +1501,16 @@ public class GSMPhone extends PhoneBase { * Saves CLIR setting so that we can re-apply it as necessary * (in case the RIL resets it across reboots). */ - /* package */ void saveClirSetting(int commandInterfaceCLIRMode) { + public void saveClirSetting(int commandInterfaceCLIRMode) { // open the shared preferences editor, and write the value. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences.Editor editor = sp.edit(); editor.putInt(CLIR_KEY, commandInterfaceCLIRMode); - + // commit and log the result. if (! editor.commit()) { Log.e(LOG_TAG, "failed to commit CLIR preference"); } - } private void handleCfuQueryResult(CallForwardInfo[] infos) { @@ -1618,7 +1534,7 @@ public class GSMPhone extends PhoneBase { * simulates various data connection states. This messes with * DataConnectionTracker's internal states, but doesn't actually change * the underlying radio connection states. - * + * * @param state Phone.DataState enum. */ public void simulateDataConnection(Phone.DataState state) { @@ -1642,4 +1558,52 @@ public class GSMPhone extends PhoneBase { mDataConnection.setState(dcState); notifyDataConnection(null); } + + /** + * Retrieves the PhoneSubInfo of the GSMPhone + */ + public PhoneSubInfo getPhoneSubInfo(){ + return mSubInfo; + } + + /** + * Retrieves the IccSmsInterfaceManager of the GSMPhone + */ + public IccSmsInterfaceManager getIccSmsInterfaceManager(){ + return mSimSmsIntManager; + } + + /** + * Retrieves the IccPhoneBookInterfaceManager of the GSMPhone + */ + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ + return mSimPhoneBookIntManager; + } + + /** + * {@inheritDoc} + */ + public Handler getHandler(){ + return h; + } + + /** + * {@inheritDoc} + */ + public IccFileHandler getIccFileHandler(){ + return this.mIccFileHandler; + } + + public void activateCellBroadcastSms(int activate, Message response) { + Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM."); + } + + public void getCellBroadcastSmsConfig(Message response) { + Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM."); + } + + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){ + Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM."); + } + } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java deleted file mode 100644 index df34897..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/GsmAlphabet.java +++ /dev/null @@ -1,813 +0,0 @@ -/* - * 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.gsm; - -import android.telephony.gsm.SmsMessage; -import android.util.SparseIntArray; - -import android.util.Log; - -/** - * This class implements the character set mapping between - * the GSM SMS 7-bit alphabet specifed in TS 23.038 6.2.1 - * and UTF-16 - * - * {@hide} - */ -public class GsmAlphabet -{ - static final String LOG_TAG = "GSM"; - - - - //***** Constants - - /** - * This escapes extended characters, and when present indicates that the - * following character should - * be looked up in the "extended" table - * - * gsmToChar(GSM_EXTENDED_ESCAPE) returns 0xffff - */ - - public static final byte GSM_EXTENDED_ESCAPE = 0x1B; - - - /** - * char to GSM alphabet char - * Returns ' ' in GSM alphabet if there's no possible match - * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table - * In this case, you must call charToGsmExtended() for the value that - * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string - */ - public static int - charToGsm(char c) - { - try { - return charToGsm(c, false); - } catch (EncodeException ex) { - // this should never happen - return sGsmSpaceChar; - } - } - - /** - * char to GSM alphabet char - * @param throwException If true, throws EncodeException on invalid char. - * If false, returns GSM alphabet ' ' char. - * - * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table - * In this case, you must call charToGsmExtended() for the value that - * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string - */ - - public static int - charToGsm(char c, boolean throwException) throws EncodeException - { - int ret; - - ret = charToGsm.get(c, -1); - - if (ret == -1) { - ret = charToGsmExtended.get(c, -1); - - if (ret == -1) { - if (throwException) { - throw new EncodeException(c); - } else { - return sGsmSpaceChar; - } - } else { - return GSM_EXTENDED_ESCAPE; - } - } - - return ret; - - } - - - /** - * char to extended GSM alphabet char - * - * Extended chars should be escaped with GSM_EXTENDED_ESCAPE - * - * Returns ' ' in GSM alphabet if there's no possible match - * - */ - public static int - charToGsmExtended(char c) - { - int ret; - - ret = charToGsmExtended.get(c, -1); - - if (ret == -1) { - return sGsmSpaceChar; - } - - return ret; - } - - /** - * Converts a character in the GSM alphabet into a char - * - * if GSM_EXTENDED_ESCAPE is passed, 0xffff is returned. In this case, - * the following character in the stream should be decoded with - * gsmExtendedToChar() - * - * If an unmappable value is passed (one greater than 127), ' ' is returned - */ - - public static char - gsmToChar(int gsmChar) - { - return (char)gsmToChar.get(gsmChar, ' '); - } - - /** - * Converts a character in the extended GSM alphabet into a char - * - * if GSM_EXTENDED_ESCAPE is passed, ' ' is returned since no second - * extension page has yet been defined (see Note 1 in table 6.2.1.1 of - * TS 23.038 v7.00) - * - * If an unmappable value is passed , ' ' is returned - */ - - public static char - gsmExtendedToChar(int gsmChar) - { - int ret; - - ret = gsmExtendedToChar.get(gsmChar, -1); - - if (ret == -1) { - return ' '; - } - - return (char)ret; - } - - /** - * Converts a String into a byte array containing the 7-bit packed - * GSM Alphabet representation of the string. If a header is provided, - * this is included in the returned byte array and padded to a septet - * boundary. - * - * Unencodable chars are encoded as spaces - * - * Byte 0 in the returned byte array is the count of septets used, - * including the header and header padding. The returned byte array is - * the minimum size required to store the packed septets. The returned - * array cannot contain more than 255 septets. - * - * @param data The text string to encode. - * @param header Optional header (includeing length byte) that precedes - * the encoded data, padded to septet boundary. - * @return Byte array containing header and encoded data. - */ - public static byte[] stringToGsm7BitPackedWithHeader(String data, byte[] header) - throws EncodeException { - - if (header == null || header.length == 0) { - return stringToGsm7BitPacked(data); - } - - int headerBits = header.length * 8; - int headerSeptets = headerBits / 7; - headerSeptets += (headerBits % 7) > 0 ? 1 : 0; - - int sz = data.length(); - int septetCount; - septetCount = countGsmSeptets(data, true) + headerSeptets; - - byte[] ret = stringToGsm7BitPacked(data, 0, septetCount, - (headerSeptets*7), true); - - // Paste in the header - System.arraycopy(header, 0, ret, 1, header.length); - return ret; - } - - /** - * Converts a String into a byte array containing - * the 7-bit packed GSM Alphabet representation of the string. - * - * Unencodable chars are encoded as spaces - * - * Byte 0 in the returned byte array is the count of septets used - * The returned byte array is the minimum size required to store - * the packed septets. The returned array cannot contain more than 255 - * septets. - * - * @param data the data string to endcode - * @throws EncodeException if String is too large to encode - */ - public static byte[] stringToGsm7BitPacked(String data) - throws EncodeException { - return stringToGsm7BitPacked(data, 0, -1, 0, true); - } - - /** - * Converts a String into a byte array containing - * the 7-bit packed GSM Alphabet representation of the string. - * - * Byte 0 in the returned byte array is the count of septets used - * The returned byte array is the minimum size required to store - * the packed septets. The returned array cannot contain more than 255 - * septets. - * - * @param data the text to convert to septets - * @param dataOffset the character offset in data to start the encoding from - * @param maxSeptets the maximum number of septets to convert, or -1 for no - * enforced maximum. - * @param startingBitOffset the number of padding bits to put before - * the start of the first septet at the begining of the array - * @param throwException If true, throws EncodeException on invalid char. - * If false, replaces unencodable char with GSM alphabet space char. - * - * @throws EncodeException if String is too large to encode - */ - public static byte[] stringToGsm7BitPacked(String data, int dataOffset, - int maxSeptets, int startingBitOffset, boolean throwException) - throws EncodeException { - - int sz = data.length(); - int septetCount; - if (maxSeptets == -1) { - septetCount = countGsmSeptets(data, true); - } else { - septetCount = maxSeptets; - } - - if(septetCount > 0xff) { - throw new EncodeException("Payload cannot exceed " + Short.MAX_VALUE - + " septets"); - } - - // Enough for all the septets and the length 2 byte prefix - byte[] ret = new byte[1 + (((septetCount * 7) + 7) / 8)]; - - int bitOffset = startingBitOffset; - int septets = startingBitOffset/7; - for (int i = dataOffset; i < sz && septets < septetCount; i++, bitOffset += 7) { - char c = data.charAt(i); - - int v = GsmAlphabet.charToGsm(c, throwException); - if (v == GSM_EXTENDED_ESCAPE) { - // Lookup the extended char - v = GsmAlphabet.charToGsmExtended(c); - - packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE); - bitOffset += 7; - septets++; - } - - packSmsChar(ret, bitOffset, v); - septets++; - } - - // See check for > 0xff above - ret[0] = (byte)septets; - - return ret; - } - - /** - * Pack a 7-bit char into its appropirate place in a byte array - * - * @param bitOffset the bit offset that the septet should be packed at - * (septet index * 7) - */ - private static void - packSmsChar(byte[] packedChars, int bitOffset, int value) - { - int byteOffset = bitOffset / 8; - int shift = bitOffset % 8; - - packedChars[++byteOffset] |= value << shift; - - if (shift > 1) { - packedChars[++byteOffset] = (byte)(value >> (8 - shift)); - } - } - - /** - * Convert a GSM alphabet 7 bit packed string (SMS string) into a - * {@link java.lang.String}. - * - * See TS 23.038 6.1.2.1 for SMS Character Packing - * - * @param pdu the raw data from the pdu - * @param offset the byte offset of - * @param lengthSeptets string length in septets, not bytes - * @return String representation or null on decoding exception - */ - public static String gsm7BitPackedToString(byte[] pdu, int offset, - int lengthSeptets) { - return gsm7BitPackedToString(pdu, offset, lengthSeptets, 0); - } - - /** - * Convert a GSM alphabet 7 bit packed string (SMS string) into a - * {@link java.lang.String}. - * - * See TS 23.038 6.1.2.1 for SMS Character Packing - * - * @param pdu the raw data from the pdu - * @param offset the byte offset of - * @param lengthSeptets string length in septets, not bytes - * @param numPaddingBits the number of padding bits before the start of the - * string in the first byte - * @return String representation or null on decoding exception - */ - public static String gsm7BitPackedToString(byte[] pdu, int offset, - int lengthSeptets, int numPaddingBits) - { - StringBuilder ret = new StringBuilder(lengthSeptets); - boolean prevCharWasEscape; - - try { - prevCharWasEscape = false; - - for (int i = 0 ; i < lengthSeptets ; i++) { - int bitOffset = (7 * i) + numPaddingBits; - - int byteOffset = bitOffset / 8; - int shift = bitOffset % 8; - int gsmVal; - - gsmVal = (0x7f & (pdu[offset + byteOffset] >> shift)); - - // if it crosses a byte boundry - if (shift > 1) { - // set msb bits to 0 - gsmVal &= 0x7f >> (shift - 1); - - gsmVal |= 0x7f & (pdu[offset + byteOffset + 1] << (8 - shift)); - } - - if (prevCharWasEscape) { - ret.append(GsmAlphabet.gsmExtendedToChar(gsmVal)); - prevCharWasEscape = false; - } else if (gsmVal == GSM_EXTENDED_ESCAPE) { - prevCharWasEscape = true; - } else { - ret.append(GsmAlphabet.gsmToChar(gsmVal)); - } - } - } catch (RuntimeException ex) { - Log.e(LOG_TAG, "Error GSM 7 bit packed: ", ex); - return null; - } - - return ret.toString(); - } - - - /** - * Convert a GSM alphabet string that's stored in 8-bit unpacked - * format (as it often appears in SIM records) into a String - * - * Field may be padded with trailing 0xff's. The decode stops - * at the first 0xff encountered. - */ - public static String - gsm8BitUnpackedToString(byte[] data, int offset, int length) - { - boolean prevWasEscape; - StringBuilder ret = new StringBuilder(length); - - prevWasEscape = false; - for (int i = offset ; i < offset + length ; i++) { - // Never underestimate the pain that can be caused - // by signed bytes - int c = data[i] & 0xff; - - if (c == 0xff) { - break; - } else if (c == GSM_EXTENDED_ESCAPE) { - if (prevWasEscape) { - // Two escape chars in a row - // We treat this as a space - // See Note 1 in table 6.2.1.1 of TS 23.038 v7.00 - ret.append(' '); - prevWasEscape = false; - } else { - prevWasEscape = true; - } - } else { - if (prevWasEscape) { - ret.append((char)gsmExtendedToChar.get(c, ' ')); - } else { - ret.append((char)gsmToChar.get(c, ' ')); - } - prevWasEscape = false; - } - } - - return ret.toString(); - } - - /** - * Convert a string into an 8-bit unpacked GSM alphabet byte - * array - */ - public static byte[] - stringToGsm8BitPacked(String s) - { - byte[] ret; - - int septets = 0; - - septets = countGsmSeptets(s); - - // Enough for all the septets and the length byte prefix - ret = new byte[septets]; - - stringToGsm8BitUnpackedField(s, ret, 0, ret.length); - - return ret; - } - - - /** - * Write a String into a GSM 8-bit unpacked field of - * @param length size at @param offset in @param dest - * - * Field is padded with 0xff's, string is truncated if necessary - */ - - public static void - stringToGsm8BitUnpackedField(String s, byte dest[], int offset, int length) - { - int outByteIndex = offset; - - // Septets are stored in byte-aligned octets - for (int i = 0, sz = s.length() - ; i < sz && (outByteIndex - offset) < length - ; i++ - ) { - char c = s.charAt(i); - - int v = GsmAlphabet.charToGsm(c); - - if (v == GSM_EXTENDED_ESCAPE) { - // make sure we can fit an escaped char - if (! (outByteIndex + 1 - offset < length)) { - break; - } - - dest[outByteIndex++] = GSM_EXTENDED_ESCAPE; - - v = GsmAlphabet.charToGsmExtended(c); - } - - dest[outByteIndex++] = (byte)v; - } - - // pad with 0xff's - while((outByteIndex - offset) < length) { - dest[outByteIndex++] = (byte)0xff; - } - } - - /** - * Returns the count of 7-bit GSM alphabet characters - * needed to represent this character. Counts unencodable char as 1 septet. - */ - public static int - countGsmSeptets(char c) - { - try { - return countGsmSeptets(c, false); - } catch (EncodeException ex) { - // This should never happen. - return 0; - } - } - - /** - * Returns the count of 7-bit GSM alphabet characters - * needed to represent this character - * @param throwsException If true, throws EncodeException if unencodable - * char. Otherwise, counts invalid char as 1 septet - */ - public static int - countGsmSeptets(char c, boolean throwsException) throws EncodeException - { - if (charToGsm.get(c, -1) != -1) { - return 1; - } - - if (charToGsmExtended.get(c, -1) != -1) { - return 2; - } - - if (throwsException) { - throw new EncodeException(c); - } else { - // count as a space char - return 1; - } - } - - /** - * Returns the count of 7-bit GSM alphabet characters - * needed to represent this string. Counts unencodable char as 1 septet. - */ - public static int - countGsmSeptets(CharSequence s) - { - try { - return countGsmSeptets(s, false); - } catch (EncodeException ex) { - // this should never happen - return 0; - } - } - - /** - * Returns the count of 7-bit GSM alphabet characters - * needed to represent this string. - * @param throwsException If true, throws EncodeException if unencodable - * char. Otherwise, counts invalid char as 1 septet - */ - public static int - countGsmSeptets(CharSequence s, boolean throwsException) throws EncodeException - { - int charIndex = 0; - int sz = s.length(); - int count = 0; - - while (charIndex < sz) { - count += countGsmSeptets(s.charAt(charIndex), throwsException); - charIndex++; - } - - return count; - } - - /** - * Returns the index into s of the first character - * after limit septets have been reached, starting at - * index start. This is used when dividing messages - * into units within the SMS message size limit. - * - * @param s source string - * @param start index of where to start counting septets - * @param limit maximum septets to include, - * e.g. MAX_USER_DATA_SEPTETS - * @return index of first character that won't fit, or the length - * of the entire string if everything fits - */ - public static int - findGsmSeptetLimitIndex(String s, int start, int limit) { - int accumulator = 0; - int size = s.length(); - - for (int i = start; i < size; i++) { - accumulator += countGsmSeptets(s.charAt(i)); - if (accumulator > limit) { - return i; - } - } - return size; - } - - /** - * Returns the index into s of the first character - * after limit octets have been reached, starting at - * index start. This is used when dividing messages - * in UCS2 encoding into units within the SMS message size limit. - * - * @param s source string - * @param start index of where to start counting septets - * @param limit maximum septets to include, - * e.g. MAX_USER_DATA_BYTES - * @return index of first character that won't fit, or the length - * of the entire string if everything fits - */ - public static int - findUCS2LimitIndex(String s, int start, int limit) { - int numCharToBeEncoded = s.length() - start; - return ((numCharToBeEncoded*2 > limit)? limit/2: numCharToBeEncoded) + start; - } - - /** - * Returns the index into s of the first character - * after limit septets/octets have been reached - * according to the encodingType, starting at - * index start. This is used when dividing messages - * units within the SMS message size limit. - * - * @param s source string - * @param start index of where to start counting septets - * @param limit maximum septets to include, - * e.g. MAX_USER_DATA_BYTES - * @return index of first character that won't fit, or the length - * of the entire string if everything fits - */ - public static int - findLimitIndex(String s, int start, int limit, int encodingType) throws EncodeException { - if (encodingType == SmsMessage.ENCODING_7BIT) { - return findGsmSeptetLimitIndex(s, start, limit); - } - else if (encodingType == SmsMessage.ENCODING_16BIT) { - return findUCS2LimitIndex(s, start, limit); - } - else { - throw new EncodeException("Unsupported encoding type: " + encodingType); - } - } - - // Set in the static initializer - private static int sGsmSpaceChar; - - private static final SparseIntArray charToGsm = new SparseIntArray(); - private static final SparseIntArray gsmToChar = new SparseIntArray(); - private static final SparseIntArray charToGsmExtended = new SparseIntArray(); - private static final SparseIntArray gsmExtendedToChar = new SparseIntArray(); - - static { - int i = 0; - - charToGsm.put('@', i++); - charToGsm.put('\u00a3', i++); - charToGsm.put('$', i++); - charToGsm.put('\u00a5', i++); - charToGsm.put('\u00e8', i++); - charToGsm.put('\u00e9', i++); - charToGsm.put('\u00f9', i++); - charToGsm.put('\u00ec', i++); - charToGsm.put('\u00f2', i++); - charToGsm.put('\u00c7', i++); - charToGsm.put('\n', i++); - charToGsm.put('\u00d8', i++); - charToGsm.put('\u00f8', i++); - charToGsm.put('\r', i++); - charToGsm.put('\u00c5', i++); - charToGsm.put('\u00e5', i++); - - charToGsm.put('\u0394', i++); - charToGsm.put('_', i++); - charToGsm.put('\u03a6', i++); - charToGsm.put('\u0393', i++); - charToGsm.put('\u039b', i++); - charToGsm.put('\u03a9', i++); - charToGsm.put('\u03a0', i++); - charToGsm.put('\u03a8', i++); - charToGsm.put('\u03a3', i++); - charToGsm.put('\u0398', i++); - charToGsm.put('\u039e', i++); - charToGsm.put('\uffff', i++); - charToGsm.put('\u00c6', i++); - charToGsm.put('\u00e6', i++); - charToGsm.put('\u00df', i++); - charToGsm.put('\u00c9', i++); - - charToGsm.put(' ', i++); - charToGsm.put('!', i++); - charToGsm.put('"', i++); - charToGsm.put('#', i++); - charToGsm.put('\u00a4', i++); - charToGsm.put('%', i++); - charToGsm.put('&', i++); - charToGsm.put('\'', i++); - charToGsm.put('(', i++); - charToGsm.put(')', i++); - charToGsm.put('*', i++); - charToGsm.put('+', i++); - charToGsm.put(',', i++); - charToGsm.put('-', i++); - charToGsm.put('.', i++); - charToGsm.put('/', i++); - - charToGsm.put('0', i++); - charToGsm.put('1', i++); - charToGsm.put('2', i++); - charToGsm.put('3', i++); - charToGsm.put('4', i++); - charToGsm.put('5', i++); - charToGsm.put('6', i++); - charToGsm.put('7', i++); - charToGsm.put('8', i++); - charToGsm.put('9', i++); - charToGsm.put(':', i++); - charToGsm.put(';', i++); - charToGsm.put('<', i++); - charToGsm.put('=', i++); - charToGsm.put('>', i++); - charToGsm.put('?', i++); - - charToGsm.put('\u00a1', i++); - charToGsm.put('A', i++); - charToGsm.put('B', i++); - charToGsm.put('C', i++); - charToGsm.put('D', i++); - charToGsm.put('E', i++); - charToGsm.put('F', i++); - charToGsm.put('G', i++); - charToGsm.put('H', i++); - charToGsm.put('I', i++); - charToGsm.put('J', i++); - charToGsm.put('K', i++); - charToGsm.put('L', i++); - charToGsm.put('M', i++); - charToGsm.put('N', i++); - charToGsm.put('O', i++); - - charToGsm.put('P', i++); - charToGsm.put('Q', i++); - charToGsm.put('R', i++); - charToGsm.put('S', i++); - charToGsm.put('T', i++); - charToGsm.put('U', i++); - charToGsm.put('V', i++); - charToGsm.put('W', i++); - charToGsm.put('X', i++); - charToGsm.put('Y', i++); - charToGsm.put('Z', i++); - charToGsm.put('\u00c4', i++); - charToGsm.put('\u00d6', i++); - charToGsm.put('\u0147', i++); - charToGsm.put('\u00dc', i++); - charToGsm.put('\u00a7', i++); - - charToGsm.put('\u00bf', i++); - charToGsm.put('a', i++); - charToGsm.put('b', i++); - charToGsm.put('c', i++); - charToGsm.put('d', i++); - charToGsm.put('e', i++); - charToGsm.put('f', i++); - charToGsm.put('g', i++); - charToGsm.put('h', i++); - charToGsm.put('i', i++); - charToGsm.put('j', i++); - charToGsm.put('k', i++); - charToGsm.put('l', i++); - charToGsm.put('m', i++); - charToGsm.put('n', i++); - charToGsm.put('o', i++); - - charToGsm.put('p', i++); - charToGsm.put('q', i++); - charToGsm.put('r', i++); - charToGsm.put('s', i++); - charToGsm.put('t', i++); - charToGsm.put('u', i++); - charToGsm.put('v', i++); - charToGsm.put('w', i++); - charToGsm.put('x', i++); - charToGsm.put('y', i++); - charToGsm.put('z', i++); - charToGsm.put('\u00e4', i++); - charToGsm.put('\u00f6', i++); - charToGsm.put('\u00f1', i++); - charToGsm.put('\u00fc', i++); - charToGsm.put('\u00e0', i++); - - - charToGsmExtended.put('\f', 10); - charToGsmExtended.put('^', 20); - charToGsmExtended.put('{', 40); - charToGsmExtended.put('}', 41); - charToGsmExtended.put('\\', 47); - charToGsmExtended.put('[', 60); - charToGsmExtended.put('~', 61); - charToGsmExtended.put(']', 62); - charToGsmExtended.put('|', 64); - charToGsmExtended.put('\u20ac', 101); - - int size = charToGsm.size(); - for (int j=0; j connections = new ArrayList(); + /*package*/ GsmCallTracker owner; + + + /***************************** Class Methods *****************************/ + + static State + stateFromDCState (DriverCall.State dcState) { + switch (dcState) { + case ACTIVE: return State.ACTIVE; + case HOLDING: return State.HOLDING; + case DIALING: return State.DIALING; + case ALERTING: return State.ALERTING; + case INCOMING: return State.INCOMING; + case WAITING: return State.WAITING; + default: throw new RuntimeException ("illegal call state:" + dcState); + } + } + + + /****************************** Constructors *****************************/ + /*package*/ + GsmCall (GsmCallTracker owner) { + this.owner = owner; + } + + public void dispose() { + } + + /************************** Overridden from Call *************************/ + + public List + getConnections() { + // FIXME should return Collections.unmodifiableList(); + return connections; + } + + public Phone + getPhone() { + //TODO + return null; + } + + public boolean + isMultiparty() { + return connections.size() > 1; + } + + /** Please note: if this is the foreground call and a + * background call exists, the background call will be resumed + * because an AT+CHLD=1 will be sent + */ + public void + hangup() throws CallStateException { + owner.hangup(this); + } + + public String + toString() { + return state.toString(); + } + + //***** Called from GsmConnection + + /*package*/ void + attach(Connection conn, DriverCall dc) { + connections.add(conn); + + state = stateFromDCState (dc.state); + } + + /*package*/ void + attachFake(Connection conn, State state) { + connections.add(conn); + + this.state = state; + } + + /** + * Called by GsmConnection when it has disconnected + */ + void + connectionDisconnected(GsmConnection conn) { + if (state != State.DISCONNECTED) { + /* If only disconnected connections remain, we are disconnected*/ + + boolean hasOnlyDisconnectedConnections = true; + + for (int i = 0, s = connections.size() ; i < s; i ++) { + if (connections.get(i).getState() + != State.DISCONNECTED + ) { + hasOnlyDisconnectedConnections = false; + break; + } + } + + if (hasOnlyDisconnectedConnections) { + state = State.DISCONNECTED; + } + } + } + + + /*package*/ void + detach(GsmConnection conn) { + connections.remove(conn); + + if (connections.size() == 0) { + state = State.IDLE; + } + } + + /*package*/ boolean + update (GsmConnection conn, DriverCall dc) { + State newState; + boolean changed = false; + + newState = stateFromDCState(dc.state); + + if (newState != state) { + state = newState; + changed = true; + } + + return changed; + } + + /** + * @return true if there's no space in this call for additional + * connections to be added via "conference" + */ + /*package*/ boolean + isFull() { + return connections.size() == GsmCallTracker.MAX_CONNECTIONS_PER_CALL; + } + + //***** Called from GsmCallTracker + + + /** + * Called when this Call is being hung up locally (eg, user pressed "end") + * Note that at this point, the hangup request has been dispatched to the radio + * but no response has yet been received so update() has not yet been called + */ + void + onHangupLocal() { + for (int i = 0, s = connections.size() + ; i < s; i++ + ) { + GsmConnection cn = (GsmConnection)connections.get(i); + + cn.onHangupLocal(); + } + } + + /** + * Called when it's time to clean up disconnected Connection objects + */ + void + clearDisconnected() { + for (int i = connections.size() - 1 ; i >= 0 ; i--) { + GsmConnection cn = (GsmConnection)connections.get(i); + + if (cn.getState() == State.DISCONNECTED) { + connections.remove(i); + } + } + + if (connections.size() == 0) { + state = State.IDLE; + } + } +} + diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java new file mode 100644 index 0000000..5c5090f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java @@ -0,0 +1,908 @@ +/* + * 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.gsm; + +import android.os.*; +import android.telephony.gsm.GsmCellLocation; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.EventLog; +import android.util.Log; + +import com.android.internal.telephony.CallStateException; +import com.android.internal.telephony.CallTracker; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.Connection; +import com.android.internal.telephony.DriverCall; +import com.android.internal.telephony.gsm.CallFailCause; +import com.android.internal.telephony.gsm.GsmCall; +import com.android.internal.telephony.gsm.GsmConnection; +import com.android.internal.telephony.gsm.GSMPhone; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.TelephonyEventLog; +import com.android.internal.telephony.*; + +import java.util.List; +import java.util.ArrayList; + +/** + * {@hide} + */ +public final class GsmCallTracker extends CallTracker { + static final String LOG_TAG = "GSM"; + private static final boolean REPEAT_POLLING = false; + + private static final boolean DBG_POLL = false; + + //***** Constants + + static final int MAX_CONNECTIONS = 7; // only 7 connections allowed in GSM + static final int MAX_CONNECTIONS_PER_CALL = 5; // only 5 connections allowed per call + + //***** Instance Variables + GsmConnection connections[] = new GsmConnection[MAX_CONNECTIONS]; + RegistrantList voiceCallEndedRegistrants = new RegistrantList(); + RegistrantList voiceCallStartedRegistrants = new RegistrantList(); + + + // connections dropped durin last poll + ArrayList droppedDuringPoll + = new ArrayList(MAX_CONNECTIONS); + + GsmCall ringingCall = new GsmCall(this); + // A call that is ringing or (call) waiting + GsmCall foregroundCall = new GsmCall(this); + GsmCall backgroundCall = new GsmCall(this); + + GsmConnection pendingMO; + boolean hangupPendingMO; + + GSMPhone phone; + + boolean desiredMute = false; // false = mute off + + Phone.State state = Phone.State.IDLE; + + + + //***** Events + + + //***** Constructors + + GsmCallTracker (GSMPhone phone) { + this.phone = phone; + cm = phone.mCM; + + cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null); + + cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null); + cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null); + } + + public void dispose() { + //Unregister for all events + cm.unregisterForCallStateChanged(this); + cm.unregisterForOn(this); + cm.unregisterForNotAvailable(this); + + for(GsmConnection c : connections) { + try { + if(c != null) hangup(c); + } catch (CallStateException ex) { + Log.e(LOG_TAG, "unexpected error on hangup during dispose"); + } + } + + try { + if(pendingMO != null) hangup(pendingMO); + } catch (CallStateException ex) { + Log.e(LOG_TAG, "unexpected error on hangup during dispose"); + } + + clearDisconnected(); + } + + protected void finalize() { + Log.d(LOG_TAG, "GsmCallTracker finalized"); + } + + //***** Instance Methods + + //***** Public Methods + public void registerForVoiceCallStarted(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + voiceCallStartedRegistrants.add(r); + } + + public void unregisterForVoiceCallStarted(Handler h) { + voiceCallStartedRegistrants.remove(h); + } + + public void registerForVoiceCallEnded(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + voiceCallEndedRegistrants.add(r); + } + + public void unregisterForVoiceCallEnded(Handler h) { + voiceCallEndedRegistrants.remove(h); + } + + private void + fakeHoldForegroundBeforeDial() { + List connCopy; + + // We need to make a copy here, since fakeHoldBeforeDial() + // modifies the lists, and we don't want to reverse the order + connCopy = (List) foregroundCall.connections.clone(); + + for (int i = 0, s = connCopy.size() ; i < s ; i++) { + GsmConnection conn = (GsmConnection)connCopy.get(i); + + conn.fakeHoldBeforeDial(); + } + } + + /** + * clirMode is one of the CLIR_ constants + */ + Connection + dial (String dialString, int clirMode) throws CallStateException { + // note that this triggers call state changed notif + clearDisconnected(); + + if (!canDial()) { + throw new CallStateException("cannot dial in current state"); + } + + // The new call must be assigned to the foreground call. + // That call must be idle, so place anything that's + // there on hold + if (foregroundCall.getState() == GsmCall.State.ACTIVE) { + // this will probably be done by the radio anyway + // but the dial might fail before this happens + // and we need to make sure the foreground call is clear + // for the newly dialed connection + switchWaitingOrHoldingAndActive(); + + // Fake local state so that + // a) foregroundCall is empty for the newly dialed connection + // b) hasNonHangupStateChanged remains false in the + // next poll, so that we don't clear a failed dialing call + fakeHoldForegroundBeforeDial(); + } + + if (foregroundCall.getState() != GsmCall.State.IDLE) { + //we should have failed in !canDial() above before we get here + throw new CallStateException("cannot dial in current state"); + } + + pendingMO = new GsmConnection(phone.getContext(), dialString, this, foregroundCall); + hangupPendingMO = false; + + if (pendingMO.address == null || pendingMO.address.length() == 0 + || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0 + ) { + // Phone number is invalid + pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER; + + // handlePollCalls() will notice this call not present + // and will mark it as dropped. + pollCallsWhenSafe(); + } else { + // Always unmute when initiating a new call + setMute(false); + + cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + } + + updatePhoneState(); + phone.notifyCallStateChanged(); + + return pendingMO; + } + + + Connection + dial (String dialString) throws CallStateException { + return dial(dialString, CommandsInterface.CLIR_DEFAULT); + } + + void + acceptCall () throws CallStateException { + // FIXME if SWITCH fails, should retry with ANSWER + // in case the active/holding call disappeared and this + // is no longer call waiting + + if (ringingCall.getState() == GsmCall.State.INCOMING) { + Log.i("phone", "acceptCall: incoming..."); + // Always unmute when answering a new call + setMute(false); + cm.acceptCall(obtainCompleteMessage()); + } else if (ringingCall.getState() == GsmCall.State.WAITING) { + setMute(false); + switchWaitingOrHoldingAndActive(); + } else { + throw new CallStateException("phone not ringing"); + } + } + + void + rejectCall () throws CallStateException { + // AT+CHLD=0 means "release held or UDUB" + // so if the phone isn't ringing, this could hang up held + if (ringingCall.getState().isRinging()) { + cm.rejectCall(obtainCompleteMessage()); + } else { + throw new CallStateException("phone not ringing"); + } + } + + void + switchWaitingOrHoldingAndActive() throws CallStateException { + // Should we bother with this check? + if (ringingCall.getState() == GsmCall.State.INCOMING) { + throw new CallStateException("cannot be in the incoming state"); + } else { + cm.switchWaitingOrHoldingAndActive( + obtainCompleteMessage(EVENT_SWITCH_RESULT)); + } + } + + void + conference() throws CallStateException { + cm.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT)); + } + + void + explicitCallTransfer() throws CallStateException { + cm.explicitCallTransfer(obtainCompleteMessage(EVENT_ECT_RESULT)); + } + + void + clearDisconnected() { + internalClearDisconnected(); + + updatePhoneState(); + phone.notifyCallStateChanged(); + } + + boolean + canConference() { + return foregroundCall.getState() == GsmCall.State.ACTIVE + && backgroundCall.getState() == GsmCall.State.HOLDING + && !backgroundCall.isFull() + && !foregroundCall.isFull(); + } + + boolean + canDial() { + boolean ret; + int serviceState = phone.getServiceState().getState(); + + ret = (serviceState != ServiceState.STATE_POWER_OFF) && + pendingMO == null + && !ringingCall.isRinging() + && (!foregroundCall.getState().isAlive() + || !backgroundCall.getState().isAlive()); + + return ret; + } + + boolean + canTransfer() { + return foregroundCall.getState() == GsmCall.State.ACTIVE + && backgroundCall.getState() == GsmCall.State.HOLDING; + } + + //***** Private Instance Methods + + private void + internalClearDisconnected() { + ringingCall.clearDisconnected(); + foregroundCall.clearDisconnected(); + backgroundCall.clearDisconnected(); + } + + /** + * Obtain a message to use for signalling "invoke getCurrentCalls() when + * this operation and all other pending operations are complete + */ + private Message + obtainCompleteMessage() { + return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); + } + + /** + * Obtain a message to use for signalling "invoke getCurrentCalls() when + * this operation and all other pending operations are complete + */ + private Message + obtainCompleteMessage(int what) { + pendingOperations++; + lastRelevantPoll = null; + needsPoll = true; + + if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + + pendingOperations + ", needsPoll=" + needsPoll); + + return obtainMessage(what); + } + + private void + operationComplete() { + pendingOperations--; + + if (DBG_POLL) log("operationComplete: pendingOperations=" + + pendingOperations + ", needsPoll=" + needsPoll); + + if (pendingOperations == 0 && needsPoll) { + lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); + cm.getCurrentCalls(lastRelevantPoll); + } else if (pendingOperations < 0) { + // this should never happen + Log.e(LOG_TAG,"GsmCallTracker.pendingOperations < 0"); + pendingOperations = 0; + } + } + + private void + updatePhoneState() { + Phone.State oldState = state; + + if (ringingCall.isRinging()) { + state = Phone.State.RINGING; + } else if (pendingMO != null || + !(foregroundCall.isIdle() && backgroundCall.isIdle())) { + state = Phone.State.OFFHOOK; + } else { + state = Phone.State.IDLE; + } + + if (state == Phone.State.IDLE && oldState != state) { + voiceCallEndedRegistrants.notifyRegistrants( + new AsyncResult(null, null, null)); + } else if (oldState == Phone.State.IDLE && oldState != state) { + voiceCallStartedRegistrants.notifyRegistrants ( + new AsyncResult(null, null, null)); + } + + if (state != oldState) { + phone.notifyPhoneStateChanged(); + } + } + + protected void + handlePollCalls(AsyncResult ar) { + List polledCalls; + + if (ar.exception == null) { + polledCalls = (List)ar.result; + } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { + // just a dummy empty ArrayList to cause the loop + // to hang up all the calls + polledCalls = new ArrayList(); + } else { + // Radio probably wasn't ready--try again in a bit + // But don't keep polling if the channel is closed + pollCallsAfterDelay(); + return; + } + + Connection newRinging = null; //or waiting + boolean hasNonHangupStateChanged = false; // Any change besides + // a dropped connection + boolean needsPollDelay = false; + boolean unknownConnectionAppeared = false; + + for (int i = 0, curDC = 0, dcSize = polledCalls.size() + ; i < connections.length; i++) { + GsmConnection conn = connections[i]; + DriverCall dc = null; + + // polledCall list is sparse + if (curDC < dcSize) { + dc = (DriverCall) polledCalls.get(curDC); + + if (dc.index == i+1) { + curDC++; + } else { + dc = null; + } + } + + if (DBG_POLL) log("poll: conn[i=" + i + "]=" + + conn+", dc=" + dc); + + if (conn == null && dc != null) { + // Connection appeared in CLCC response that we don't know about + if (pendingMO != null && pendingMO.compareTo(dc)) { + + if (DBG_POLL) log("poll: pendingMO=" + pendingMO); + + // It's our pending mobile originating call + connections[i] = pendingMO; + pendingMO.index = i; + pendingMO.update(dc); + pendingMO = null; + + // Someone has already asked to hangup this call + if (hangupPendingMO) { + hangupPendingMO = false; + try { + if (Phone.DEBUG_PHONE) log( + "poll: hangupPendingMO, hangup conn " + i); + hangup(connections[i]); + } catch (CallStateException ex) { + Log.e(LOG_TAG, "unexpected error on hangup"); + } + + // Do not continue processing this poll + // Wait for hangup and repoll + return; + } + } else { + connections[i] = new GsmConnection(phone.getContext(), dc, this, i); + + // it's a ringing call + if (connections[i].getCall() == ringingCall) { + newRinging = connections[i]; + } else { + // Something strange happened: a call appeared + // which is neither a ringing call or one we created. + // Either we've crashed and re-attached to an existing + // call, or something else (eg, SIM) initiated the call. + + Log.i(LOG_TAG,"Phantom call appeared " + dc); + + // If it's a connected call, set the connect time so that + // it's non-zero. It may not be accurate, but at least + // it won't appear as a Missed Call. + if (dc.state != DriverCall.State.ALERTING + && dc.state != DriverCall.State.DIALING) { + connections[i].connectTime = System.currentTimeMillis(); + } + + unknownConnectionAppeared = true; + } + } + hasNonHangupStateChanged = true; + } else if (conn != null && dc == null) { + // Connection missing in CLCC response that we were + // tracking. + droppedDuringPoll.add(conn); + // Dropped connections are removed from the CallTracker + // list but kept in the GsmCall list + connections[i] = null; + } else if (conn != null && dc != null && !conn.compareTo(dc)) { + // Connection in CLCC response does not match what + // we were tracking. Assume dropped call and new call + + droppedDuringPoll.add(conn); + connections[i] = new GsmConnection (phone.getContext(), dc, this, i); + + if (connections[i].getCall() == ringingCall) { + newRinging = connections[i]; + } // else something strange happened + hasNonHangupStateChanged = true; + } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ + boolean changed; + changed = conn.update(dc); + hasNonHangupStateChanged = hasNonHangupStateChanged || changed; + } + + if (REPEAT_POLLING) { + if (dc != null) { + // FIXME with RIL, we should not need this anymore + if ((dc.state == DriverCall.State.DIALING + /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/) + || (dc.state == DriverCall.State.ALERTING + /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/) + || (dc.state == DriverCall.State.INCOMING + /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/) + || (dc.state == DriverCall.State.WAITING + /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/) + ) { + // Sometimes there's no unsolicited notification + // for state transitions + needsPollDelay = true; + } + } + } + } + + // This is the first poll after an ATD. + // We expect the pending call to appear in the list + // If it does not, we land here + if (pendingMO != null) { + Log.d(LOG_TAG,"Pending MO dropped before poll fg state:" + + foregroundCall.getState()); + + droppedDuringPoll.add(pendingMO); + pendingMO = null; + hangupPendingMO = false; + } + + if (newRinging != null) { + phone.notifyNewRingingConnection(newRinging); + } + + // clear the "local hangup" and "missed/rejected call" + // cases from the "dropped during poll" list + // These cases need no "last call fail" reason + for (int i = droppedDuringPoll.size() - 1; i >= 0 ; i--) { + GsmConnection conn = droppedDuringPoll.get(i); + + if (conn.isIncoming() && conn.getConnectTime() == 0) { + // Missed or rejected call + Connection.DisconnectCause cause; + if (conn.cause == Connection.DisconnectCause.LOCAL) { + cause = Connection.DisconnectCause.INCOMING_REJECTED; + } else { + cause = Connection.DisconnectCause.INCOMING_MISSED; + } + + if (Phone.DEBUG_PHONE) { + log("missed/rejected call, conn.cause=" + conn.cause); + log("setting cause to " + cause); + } + droppedDuringPoll.remove(i); + conn.onDisconnect(cause); + } else if (conn.cause == Connection.DisconnectCause.LOCAL) { + // Local hangup + droppedDuringPoll.remove(i); + conn.onDisconnect(Connection.DisconnectCause.LOCAL); + } else if (conn.cause == + Connection.DisconnectCause.INVALID_NUMBER) { + droppedDuringPoll.remove(i); + conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER); + } + } + + // Any non-local disconnects: determine cause + if (droppedDuringPoll.size() > 0) { + cm.getLastCallFailCause( + obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); + } + + if (needsPollDelay) { + pollCallsAfterDelay(); + } + + // Cases when we can no longer keep disconnected Connection's + // with their previous calls + // 1) the phone has started to ring + // 2) A Call/Connection object has changed state... + // we may have switched or held or answered (but not hung up) + if (newRinging != null || hasNonHangupStateChanged) { + internalClearDisconnected(); + } + + updatePhoneState(); + + if (unknownConnectionAppeared) { + phone.notifyUnknownConnection(); + } + + if (hasNonHangupStateChanged || newRinging != null) { + phone.notifyCallStateChanged(); + } + + //dumpState(); + } + + private void + handleRadioNotAvailable() { + // handlePollCalls will clear out its + // call list when it gets the CommandException + // error result from this + pollCallsWhenSafe(); + } + + private void + dumpState() { + List l; + + Log.i(LOG_TAG,"Phone State:" + state); + + Log.i(LOG_TAG,"Ringing call: " + ringingCall.toString()); + + l = ringingCall.getConnections(); + for (int i = 0, s = l.size(); i < s; i++) { + Log.i(LOG_TAG,l.get(i).toString()); + } + + Log.i(LOG_TAG,"Foreground call: " + foregroundCall.toString()); + + l = foregroundCall.getConnections(); + for (int i = 0, s = l.size(); i < s; i++) { + Log.i(LOG_TAG,l.get(i).toString()); + } + + Log.i(LOG_TAG,"Background call: " + backgroundCall.toString()); + + l = backgroundCall.getConnections(); + for (int i = 0, s = l.size(); i < s; i++) { + Log.i(LOG_TAG,l.get(i).toString()); + } + + } + + //***** Called from GsmConnection + + /*package*/ void + hangup (GsmConnection conn) throws CallStateException { + if (conn.owner != this) { + throw new CallStateException ("GsmConnection " + conn + + "does not belong to GsmCallTracker " + this); + } + + if (conn == pendingMO) { + // We're hanging up an outgoing call that doesn't have it's + // GSM index assigned yet + + if (Phone.DEBUG_PHONE) log("hangup: set hangupPendingMO to true"); + hangupPendingMO = true; + } else { + try { + cm.hangupConnection (conn.getGSMIndex(), obtainCompleteMessage()); + } catch (CallStateException ex) { + // Ignore "connection not found" + // Call may have hung up already + Log.w(LOG_TAG,"GsmCallTracker WARN: hangup() on absent connection " + + conn); + } + } + + conn.onHangupLocal(); + } + + /*package*/ void + separate (GsmConnection conn) throws CallStateException { + if (conn.owner != this) { + throw new CallStateException ("GsmConnection " + conn + + "does not belong to GsmCallTracker " + this); + } + try { + cm.separateConnection (conn.getGSMIndex(), + obtainCompleteMessage(EVENT_SEPARATE_RESULT)); + } catch (CallStateException ex) { + // Ignore "connection not found" + // Call may have hung up already + Log.w(LOG_TAG,"GsmCallTracker WARN: separate() on absent connection " + + conn); + } + } + + //***** Called from GSMPhone + + /*package*/ void + setMute(boolean mute) { + desiredMute = mute; + cm.setMute(desiredMute, null); + } + + /*package*/ boolean + getMute() { + return desiredMute; + } + + + //***** Called from GsmCall + + /* package */ void + hangup (GsmCall call) throws CallStateException { + if (call.getConnections().size() == 0) { + throw new CallStateException("no connections in call"); + } + + if (call == ringingCall) { + if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); + cm.hangupWaitingOrBackground(obtainCompleteMessage()); + } else if (call == foregroundCall) { + if (call.isDialingOrAlerting()) { + if (Phone.DEBUG_PHONE) { + log("(foregnd) hangup dialing or alerting..."); + } + hangup((GsmConnection)(call.getConnections().get(0))); + } else { + hangupForegroundResumeBackground(); + } + } else if (call == backgroundCall) { + if (ringingCall.isRinging()) { + if (Phone.DEBUG_PHONE) { + log("hangup all conns in background call"); + } + hangupAllConnections(call); + } else { + hangupWaitingOrBackground(); + } + } else { + throw new RuntimeException ("GsmCall " + call + + "does not belong to GsmCallTracker " + this); + } + + call.onHangupLocal(); + } + + /* package */ + void hangupWaitingOrBackground() { + if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground"); + cm.hangupWaitingOrBackground(obtainCompleteMessage()); + } + + /* package */ + void hangupForegroundResumeBackground() { + if (Phone.DEBUG_PHONE) log("hangupForegroundResumeBackground"); + cm.hangupForegroundResumeBackground(obtainCompleteMessage()); + } + + void hangupConnectionByIndex(GsmCall call, int index) + throws CallStateException { + int count = call.connections.size(); + for (int i = 0; i < count; i++) { + GsmConnection cn = (GsmConnection)call.connections.get(i); + if (cn.getGSMIndex() == index) { + cm.hangupConnection(index, obtainCompleteMessage()); + return; + } + } + + throw new CallStateException("no gsm index found"); + } + + void hangupAllConnections(GsmCall call) throws CallStateException{ + try { + int count = call.connections.size(); + for (int i = 0; i < count; i++) { + GsmConnection cn = (GsmConnection)call.connections.get(i); + cm.hangupConnection(cn.getGSMIndex(), obtainCompleteMessage()); + } + } catch (CallStateException ex) { + Log.e(LOG_TAG, "hangupConnectionByIndex caught " + ex); + } + } + + /* package */ + GsmConnection getConnectionByIndex(GsmCall call, int index) + throws CallStateException { + int count = call.connections.size(); + for (int i = 0; i < count; i++) { + GsmConnection cn = (GsmConnection)call.connections.get(i); + if (cn.getGSMIndex() == index) { + return cn; + } + } + + return null; + } + + private Phone.SuppService getFailedService(int what) { + switch (what) { + case EVENT_SWITCH_RESULT: + return Phone.SuppService.SWITCH; + case EVENT_CONFERENCE_RESULT: + return Phone.SuppService.CONFERENCE; + case EVENT_SEPARATE_RESULT: + return Phone.SuppService.SEPARATE; + case EVENT_ECT_RESULT: + return Phone.SuppService.TRANSFER; + } + return Phone.SuppService.UNKNOWN; + } + + //****** Overridden from Handler + + public void + handleMessage (Message msg) { + AsyncResult ar; + + switch (msg.what) { + case EVENT_POLL_CALLS_RESULT: + ar = (AsyncResult)msg.obj; + + if (msg == lastRelevantPoll) { + if (DBG_POLL) log( + "handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); + needsPoll = false; + lastRelevantPoll = null; + handlePollCalls((AsyncResult)msg.obj); + } + break; + + case EVENT_OPERATION_COMPLETE: + ar = (AsyncResult)msg.obj; + operationComplete(); + break; + + case EVENT_SWITCH_RESULT: + case EVENT_CONFERENCE_RESULT: + case EVENT_SEPARATE_RESULT: + case EVENT_ECT_RESULT: + ar = (AsyncResult)msg.obj; + if (ar.exception != null) { + phone.notifySuppServiceFailed(getFailedService(msg.what)); + } + operationComplete(); + break; + + case EVENT_GET_LAST_CALL_FAIL_CAUSE: + int causeCode; + ar = (AsyncResult)msg.obj; + + operationComplete(); + + if (ar.exception != null) { + // An exception occurred...just treat the disconnect + // cause as "normal" + causeCode = CallFailCause.NORMAL_CLEARING; + Log.i(LOG_TAG, + "Exception during getLastCallFailCause, assuming normal disconnect"); + } else { + causeCode = ((int[])ar.result)[0]; + } + // Log the causeCode if its not normal + if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || + causeCode == CallFailCause.TEMPORARY_FAILURE || + causeCode == CallFailCause.SWITCHING_CONGESTION || + causeCode == CallFailCause.CHANNEL_NOT_AVAIL || + causeCode == CallFailCause.QOS_NOT_AVAIL || + causeCode == CallFailCause.BEARER_NOT_AVAIL || + causeCode == CallFailCause.ERROR_UNSPECIFIED) { + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(causeCode, cid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CALL_DROP, val); + } + + for (int i = 0, s = droppedDuringPoll.size() + ; i < s ; i++ + ) { + GsmConnection conn = droppedDuringPoll.get(i); + + conn.onRemoteDisconnect(causeCode); + } + + updatePhoneState(); + + phone.notifyCallStateChanged(); + droppedDuringPoll.clear(); + break; + + case EVENT_REPOLL_AFTER_DELAY: + case EVENT_CALL_STATE_CHANGE: + pollCallsWhenSafe(); + break; + + case EVENT_RADIO_AVAILABLE: + handleRadioAvailable(); + break; + + case EVENT_RADIO_NOT_AVAILABLE: + handleRadioNotAvailable(); + break; + } + } + + protected void log(String msg) { + Log.d(LOG_TAG, "[GsmCallTracker] " + msg); + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java new file mode 100644 index 0000000..2b2f077 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java @@ -0,0 +1,732 @@ +/* + * 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.gsm; +import android.content.Context; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.Registrant; +import android.os.SystemClock; +import android.util.Config; +import android.util.Log; +import android.telephony.PhoneNumberUtils; +import android.telephony.ServiceState; + +import com.android.internal.telephony.*; + +/** + * {@hide} + */ +public class GsmConnection extends Connection { + static final String LOG_TAG = "GSM"; + + //***** Instance Variables + + GsmCallTracker owner; + GsmCall parent; + + String address; // MAY BE NULL!!! + String dialString; // outgoing calls only + String postDialString; // outgoing calls only + boolean isIncoming; + boolean disconnected; + + int index; // index in GsmCallTracker.connections[], -1 if unassigned + // The GSM index is 1 + this + + /* + * These time/timespan values are based on System.currentTimeMillis(), + * i.e., "wall clock" time. + */ + long createTime; + long connectTime; + long disconnectTime; + + /* + * These time/timespan values are based on SystemClock.elapsedRealTime(), + * i.e., time since boot. They are appropriate for comparison and + * calculating deltas. + */ + long connectTimeReal; + long duration; + long holdingStartTime; // The time when the Connection last transitioned + // into HOLDING + + int nextPostDialChar; // index into postDialString + + DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; + PostDialState postDialState = PostDialState.NOT_STARTED; + int numberPresentation = Connection.PRESENTATION_ALLOWED; + + Handler h; + + private PowerManager.WakeLock mPartialWakeLock; + + //***** Event Constants + static final int EVENT_DTMF_DONE = 1; + static final int EVENT_PAUSE_DONE = 2; + static final int EVENT_NEXT_POST_DIAL = 3; + static final int EVENT_WAKE_LOCK_TIMEOUT = 4; + + //***** Constants + static final int PAUSE_DELAY_FIRST_MILLIS = 100; + static final int PAUSE_DELAY_MILLIS = 3 * 1000; + static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; + + //***** Inner Classes + + class MyHandler extends Handler { + MyHandler(Looper l) {super(l);} + + public void + handleMessage(Message msg) { + + switch (msg.what) { + case EVENT_NEXT_POST_DIAL: + case EVENT_DTMF_DONE: + case EVENT_PAUSE_DONE: + processNextPostDialChar(); + break; + case EVENT_WAKE_LOCK_TIMEOUT: + releaseWakeLock(); + break; + } + } + } + + //***** Constructors + + /** This is probably an MT call that we first saw in a CLCC response */ + /*package*/ + GsmConnection (Context context, DriverCall dc, GsmCallTracker ct, int index) { + createWakeLock(context); + acquireWakeLock(); + + owner = ct; + h = new MyHandler(owner.getLooper()); + + address = dc.number; + + isIncoming = dc.isMT; + createTime = System.currentTimeMillis(); + numberPresentation = dc.numberPresentation; + + this.index = index; + + parent = parentFromDCState (dc.state); + parent.attach(this, dc); + } + + /** This is an MO call, created when dialing */ + /*package*/ + GsmConnection (Context context, String dialString, GsmCallTracker ct, GsmCall parent) { + createWakeLock(context); + acquireWakeLock(); + + owner = ct; + h = new MyHandler(owner.getLooper()); + + this.dialString = dialString; + + this.address = PhoneNumberUtils.extractNetworkPortion(dialString); + this.postDialString = PhoneNumberUtils.extractPostDialPortion(dialString); + + index = -1; + + isIncoming = false; + createTime = System.currentTimeMillis(); + + this.parent = parent; + parent.attachFake(this, GsmCall.State.DIALING); + } + + public void dispose() { + } + + static boolean + equalsHandlesNulls (Object a, Object b) { + return (a == null) ? (b == null) : a.equals (b); + } + + /*package*/ boolean + compareTo(DriverCall c) { + // On mobile originated (MO) calls, the phone number may have changed + // due to a SIM Toolkit call control modification. + // + // We assume we know when MO calls are created (since we created them) + // and therefore don't need to compare the phone number anyway. + if (! (isIncoming || c.isMT)) return true; + + // ... but we can compare phone numbers on MT calls, and we have + // no control over when they begin, so we might as well + + String cAddress = PhoneNumberUtils.stringFromStringAndTOA(c.number, c.TOA); + return isIncoming == c.isMT && equalsHandlesNulls(address, cAddress); + } + + public String + toString() { + return (isIncoming ? "incoming" : "outgoing"); + } + + public String getAddress() { + return address; + } + + public GsmCall getCall() { + return parent; + } + + public long getCreateTime() { + return createTime; + } + + public long getConnectTime() { + return connectTime; + } + + public long getDisconnectTime() { + return disconnectTime; + } + + public long getDurationMillis() { + if (connectTimeReal == 0) { + return 0; + } else if (duration == 0) { + return SystemClock.elapsedRealtime() - connectTimeReal; + } else { + return duration; + } + } + + public long getHoldDurationMillis() { + if (getState() != GsmCall.State.HOLDING) { + // If not holding, return 0 + return 0; + } else { + return SystemClock.elapsedRealtime() - holdingStartTime; + } + } + + public DisconnectCause getDisconnectCause() { + return cause; + } + + public boolean isIncoming() { + return isIncoming; + } + + public GsmCall.State getState() { + if (disconnected) { + return GsmCall.State.DISCONNECTED; + } else { + return super.getState(); + } + } + + public void hangup() throws CallStateException { + if (!disconnected) { + owner.hangup(this); + } else { + throw new CallStateException ("disconnected"); + } + } + + public void separate() throws CallStateException { + if (!disconnected) { + owner.separate(this); + } else { + throw new CallStateException ("disconnected"); + } + } + + public PostDialState getPostDialState() { + return postDialState; + } + + public void proceedAfterWaitChar() { + if (postDialState != PostDialState.WAIT) { + Log.w(LOG_TAG, "GsmConnection.proceedAfterWaitChar(): Expected " + + "getPostDialState() to be WAIT but was " + postDialState); + return; + } + + setPostDialState(PostDialState.STARTED); + + processNextPostDialChar(); + } + + public void proceedAfterWildChar(String str) { + if (postDialState != PostDialState.WILD) { + Log.w(LOG_TAG, "GsmConnection.proceedAfterWaitChar(): Expected " + + "getPostDialState() to be WILD but was " + postDialState); + return; + } + + setPostDialState(PostDialState.STARTED); + + if (false) { + boolean playedTone = false; + int len = (str != null ? str.length() : 0); + + for (int i=0; i allApns = null; + + /** + * waitingApns holds all apns that are waiting to be connected + * + * It is a subset of allApns and has the same format + */ + private ArrayList waitingApns = null; + + private ApnSetting preferredApn = null; + + /** + * pdpList holds all the PDP connection, i.e. IP Link in GPRS + */ + private ArrayList pdpList; + + /** Currently requested APN type */ + private String mRequestedApnType = Phone.APN_TYPE_DEFAULT; + + /** Currently active APN */ + private ApnSetting mActiveApn; + + /** Currently active PdpConnection */ + private PdpConnection mActivePdp; + + private static int APN_DEFAULT_ID = 0; + private static int APN_MMS_ID = 1; + private static int APN_NUM_TYPES = 2; + + private boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; + + /** Is packet service restricted by network */ + private boolean mIsPsRestricted = false; + + //***** Constants + + // TODO: Increase this to match the max number of simultaneous + // PDP contexts we plan to support. + /** + * Pool size of PdpConnection objects. + */ + private static final int PDP_CONNECTION_POOL_SIZE = 1; + + private static final int POLL_PDP_MILLIS = 5 * 1000; + + //WINK:TODO: Teleca, is this really gsm specific, what about CDMA? + private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; + private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; + + //***** Tag IDs for EventLog + private static final int EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED = 50101; + private static final int EVENT_LOG_RADIO_RESET = 50102; + private static final int EVENT_LOG_PDP_RESET = 50103; + private static final int EVENT_LOG_REREGISTER_NETWORK = 50104; + private static final int EVENT_LOG_RADIO_PDP_SETUP_FAIL = 50105; + + static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn"); + static final String APN_ID = "apn_id"; + private boolean canSetPreferApn = false; + + BroadcastReceiver mIntentReceiver = new BroadcastReceiver () + { + @Override + public void onReceive(Context context, Intent intent) + { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_SCREEN_ON)) { + mIsScreenOn = true; + stopNetStatPoll(); + startNetStatPoll(); + } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + mIsScreenOn = false; + stopNetStatPoll(); + startNetStatPoll(); + } else if (action.equals((INTENT_RECONNECT_ALARM))) { + Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state); + + String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); + if (state == State.FAILED) { + cleanUpConnection(false, reason); + } + trySetupData(reason); + } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + final android.net.NetworkInfo networkInfo = (NetworkInfo) + intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); + mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); + } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, + WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; + + if (!enabled) { + // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION + // quit and wont report disconnected til next enalbing. + mIsWifiConnected = false; + } + } + } + }; + + /** Watches for changes to the APN db. */ + private ApnChangeObserver apnObserver; + + //***** Constructor + + GsmDataConnectionTracker(GSMPhone p) { + super(p); + + p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); + p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); + p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); + p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null); + p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); + p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); + p.mSST.registerForGprsAttached(this, EVENT_GPRS_ATTACHED, null); + p.mSST.registerForGprsDetached(this, EVENT_GPRS_DETACHED, null); + p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null); + p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null); + p.mSST.registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null); + p.mSST.registerForPsRestrictedDisabled(this, EVENT_PS_RESTRICT_DISABLED, null); + + this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat")); + + IntentFilter filter = new IntentFilter(); + filter.addAction(INTENT_RECONNECT_ALARM); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + + p.getContext().registerReceiver(mIntentReceiver, filter, null, p.h); + + + mDataConnectionTracker = this; + mResolver = phone.getContext().getContentResolver(); + + apnObserver = new ApnChangeObserver(); + p.getContext().getContentResolver().registerContentObserver( + Telephony.Carriers.CONTENT_URI, true, apnObserver); + + createAllPdpList(); + + // This preference tells us 1) initial condition for "dataEnabled", + // and 2) whether the RIL will setup the baseband to auto-PS attach. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); + dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false); + noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; + } + + public void dispose() { + //Unregister for all events + phone.mCM.unregisterForAvailable(this); + phone.mCM.unregisterForOffOrNotAvailable(this); + ((GSMPhone) phone).mSIMRecords.unregisterForRecordsLoaded(this); + phone.mCM.unregisterForDataStateChanged(this); + ((GSMPhone) phone).mCT.unregisterForVoiceCallEnded(this); + ((GSMPhone) phone).mCT.unregisterForVoiceCallStarted(this); + ((GSMPhone) phone).mSST.unregisterForGprsAttached(this); + ((GSMPhone) phone).mSST.unregisterForGprsDetached(this); + ((GSMPhone) phone).mSST.unregisterForRoamingOn(this); + ((GSMPhone) phone).mSST.unregisterForRoamingOff(this); + ((GSMPhone) phone).mSST.unregisterForPsRestrictedEnabled(this); + ((GSMPhone) phone).mSST.unregisterForPsRestrictedDisabled(this); + + phone.getContext().unregisterReceiver(this.mIntentReceiver); + phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver); + + destroyAllPdpList(); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized"); + } + + void setState(State s) { + if (DBG) log ("setState: " + s); + if (state != s) { + if (s == State.INITING) { // request PDP context + Checkin.updateStats( + phone.getContext().getContentResolver(), + Checkin.Stats.Tag.PHONE_GPRS_ATTEMPTED, 1, 0.0); + } + + if (s == State.CONNECTED) { // pppd is up + Checkin.updateStats( + phone.getContext().getContentResolver(), + Checkin.Stats.Tag.PHONE_GPRS_CONNECTED, 1, 0.0); + } + } + + state = s; + + if (state == State.FAILED) { + if (waitingApns != null) + waitingApns.clear(); // when teardown the connection and set to IDLE + } + } + + String[] getActiveApnTypes() { + String[] result; + if (mActiveApn != null) { + result = mActiveApn.types; + } else { + result = new String[1]; + result[0] = Phone.APN_TYPE_DEFAULT; + } + return result; + } + + protected String getActiveApnString() { + String result = null; + if (mActiveApn != null) { + result = mActiveApn.apn; + } + return result; + } + + /** + * Ensure that we are connected to an APN of the specified type. + * @param type the APN type (currently the only valid value + * is {@link Phone#APN_TYPE_MMS}) + * @return the result of the operation. Success is indicated by + * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or + * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast + * will be sent by the ConnectivityManager when a connection to + * the APN has been established. + */ + protected int enableApnType(String type) { + if (!TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + return Phone.APN_REQUEST_FAILED; + } + // If already active, return + Log.d(LOG_TAG, "enableApnType("+type+")"); + if (isApnTypeActive(type)) { + setEnabled(type, true); + removeMessages(EVENT_RESTORE_DEFAULT_APN); + /** + * We're being asked to enable a non-default APN that's already in use. + * This means we should restart the timer that will automatically + * switch back to the default APN and disable the non-default APN + * when it expires. + */ + sendMessageDelayed( + obtainMessage(EVENT_RESTORE_DEFAULT_APN), + getRestoreDefaultApnDelay()); + if (state == State.INITING) return Phone.APN_REQUEST_STARTED; + else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; + } + + if (!isApnTypeAvailable(type)) { + return Phone.APN_TYPE_NOT_AVAILABLE; + } + + setEnabled(type, true); + mRequestedApnType = type; + sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN)); + return Phone.APN_REQUEST_STARTED; + } + + /** + * The APN of the specified type is no longer needed. Ensure that if + * use of the default APN has not been explicitly disabled, we are connected + * to the default APN. + * @param type the APN type. The only valid value currently is {@link Phone#APN_TYPE_MMS}. + * @return + */ + protected int disableApnType(String type) { + Log.d(LOG_TAG, "disableApnType("+type+")"); + if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + removeMessages(EVENT_RESTORE_DEFAULT_APN); + setEnabled(type, false); + if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { + if (dataEnabled[APN_DEFAULT_ID]) { + return Phone.APN_ALREADY_ACTIVE; + } else { + cleanUpConnection(true, Phone.REASON_DATA_DISABLED); + return Phone.APN_REQUEST_STARTED; + } + } else { + /* + * Note that if default data is disabled, the following + * has the effect of disabling the MMS APN, and then + * ignoring the request to enable the default APN. + * The net result is that data is completely disabled. + */ + sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN)); + return Phone.APN_REQUEST_STARTED; + } + } else { + return Phone.APN_REQUEST_FAILED; + } + } + + /** + * The data connection is expected to be setup while device + * 1. has sim card + * 2. registered to gprs service + * 3. user doesn't explicitly disable data service + * 4. wifi is not on + * + * @return false while no data connection if all above requirements are met. + */ + public boolean isDataConnectionAsDesired() { + boolean roaming = phone.getServiceState().getRoaming(); + + if (((GSMPhone) phone).mSIMRecords.getRecordsLoaded() && + ((GSMPhone) phone).mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && + (!roaming || getDataOnRoamingEnabled()) && + !mIsWifiConnected && + !mIsPsRestricted ) { + return (state == State.CONNECTED); + } + return true; + } + + private boolean isApnTypeActive(String type) { + // TODO: to support simultaneous, mActiveApn can be a List instead. + return mActiveApn != null && mActiveApn.canHandleType(type); + } + + private boolean isApnTypeAvailable(String type) { + if (allApns != null) { + for (ApnSetting apn : allApns) { + if (apn.canHandleType(type)) { + return true; + } + } + } + return false; + } + + private boolean isEnabled(String apnType) { + if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) { + return dataEnabled[APN_DEFAULT_ID]; + } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) { + return dataEnabled[APN_MMS_ID]; + } else { + return false; + } + } + + private void setEnabled(String apnType, boolean enable) { + Log.d(LOG_TAG, "setEnabled(" + apnType + ", " + enable + ')'); + if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) { + dataEnabled[APN_DEFAULT_ID] = enable; + } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) { + dataEnabled[APN_MMS_ID] = enable; + } + Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] + + " dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID]); + } + + /** + * Prevent mobile data connections from being established, + * or once again allow mobile data connections. If the state + * toggles, then either tear down or set up data, as + * appropriate to match the new state. + *

    This operation only affects the default APN, and if the same APN is + * currently being used for MMS traffic, the teardown will not happen + * even when {@code enable} is {@code false}.

    + * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data + * @return {@code true} if the operation succeeded + */ + public boolean setDataEnabled(boolean enable) { + boolean isEnabled = isEnabled(Phone.APN_TYPE_DEFAULT); + Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled); + if (!isEnabled && enable) { + setEnabled(Phone.APN_TYPE_DEFAULT, true); + // trySetupData() will be a no-op if we are currently + // connected to the MMS APN + return trySetupData(Phone.REASON_DATA_ENABLED); + } else if (!enable) { + setEnabled(Phone.APN_TYPE_DEFAULT, false); + // Don't tear down if there is an active APN and it handles MMS. + // TODO: This isn't very general. + if (!isApnTypeActive(Phone.APN_TYPE_MMS) || !isEnabled(Phone.APN_TYPE_MMS)) { + cleanUpConnection(true, Phone.REASON_DATA_DISABLED); + return true; + } + return false; + } else { + // isEnabled && enable + return true; + } + } + + /** + * Simply tear down data connections due to radio off + * and don't setup again. + */ + public void cleanConnectionBeforeRadioOff() { + cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); + } + + /** + * Report the current state of data connectivity (enabled or disabled) for + * the default APN. + * @return {@code false} if data connectivity has been explicitly disabled, + * {@code true} otherwise. + */ + public boolean getDataEnabled() { + return dataEnabled[APN_DEFAULT_ID]; + } + + /** + * Report on whether data connectivity is enabled for any APN. + * @return {@code false} if data connectivity has been explicitly disabled, + * {@code true} otherwise. + */ + public boolean getAnyDataEnabled() { + return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID]; + } + + /** + * Formerly this method was ArrayList getAllPdps() + */ + public ArrayList getAllDataConnections() { + ArrayList pdps = (ArrayList)pdpList.clone(); + return pdps; + } + + private boolean isDataAllowed() { + boolean roaming = phone.getServiceState().getRoaming(); + return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()); + } + + //****** Called from ServiceStateTracker + /** + * Invoked when ServiceStateTracker observes a transition from GPRS + * attach to detach. + */ + protected void onGprsDetached() { + /* + * We presently believe it is unnecessary to tear down the PDP context + * when GPRS detaches, but we should stop the network polling. + */ + stopNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED); + } + + private void onGprsAttached() { + if (state == State.CONNECTED) { + startNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED); + } else { + if (state == State.FAILED) { + cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + } + trySetupData(Phone.REASON_GPRS_ATTACHED); + } + } + + private boolean trySetupData(String reason) { + if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); + + Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted); + + if (phone.getSimulatedRadioControl() != null) { + // Assume data is connected on the simulator + // FIXME this can be improved + setState(State.CONNECTED); + phone.notifyDataConnection(reason); + + Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); + return true; + } + + int gprsState = ((GSMPhone) phone).mSST.getCurrentGprsState(); + boolean roaming = phone.getServiceState().getRoaming(); + + if ((state == State.IDLE || state == State.SCANNING) + && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) + && ((GSMPhone) phone).mSIMRecords.getRecordsLoaded() + && ( ((GSMPhone) phone).mSST.isConcurrentVoiceAndData() || + phone.getState() == Phone.State.IDLE ) + && isDataAllowed() + && !mIsPsRestricted ) { + + if (state == State.IDLE) { + waitingApns = buildWaitingApns(); + if (waitingApns.isEmpty()) { + if (DBG) log("No APN found"); + notifyNoData(PdpConnection.FailCause.BAD_APN); + return false; + } else { + log ("Create from allApns : " + apnListToString(allApns)); + } + } + + if (DBG) { + log ("Setup watingApns : " + apnListToString(waitingApns)); + } + return setupData(reason); + } else { + if (DBG) + log("trySetupData: Not ready for data: " + + " dataState=" + state + + " gprsState=" + gprsState + + " sim=" + ((GSMPhone) phone).mSIMRecords.getRecordsLoaded() + + " UMTS=" + ((GSMPhone) phone).mSST.isConcurrentVoiceAndData() + + " phoneState=" + phone.getState() + + " dataEnabled=" + getAnyDataEnabled() + + " roaming=" + roaming + + " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + + " ps restricted=" + mIsPsRestricted); + return false; + } + } + + /** + * If tearDown is true, this only tears down a CONNECTED session. Presently, + * there is no mechanism for abandoning an INITING/CONNECTING session, + * but would likely involve cancelling pending async requests or + * setting a flag or new state to ignore them when they came in + * @param tearDown true if the underlying PdpConnection should be + * disconnected. + * @param reason reason for the clean up. + */ + private void cleanUpConnection(boolean tearDown, String reason) { + if (DBG) log("Clean up connection due to " + reason); + + // Clear the reconnect alarm, if set. + if (mReconnectIntent != null) { + AlarmManager am = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + am.cancel(mReconnectIntent); + mReconnectIntent = null; + } + + for (DataConnection conn : pdpList) { + PdpConnection pdp = (PdpConnection) conn; + if (tearDown) { + Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason); + pdp.disconnect(msg); + } else { + pdp.clearSettings(); + } + } + stopNetStatPoll(); + + /* + * If we've been asked to tear down the connection, + * set the state to DISCONNECTING. However, there's + * a race that can occur if for some reason we were + * already in the IDLE state. In that case, the call + * to pdp.disconnect() above will immediately post + * a message to the handler thread that the disconnect + * is done, and if the handler runs before the code + * below does, the handler will have set the state to + * IDLE before the code below runs. If we didn't check + * for that, future calls to trySetupData would fail, + * and we would never get out of the DISCONNECTING state. + */ + if (!tearDown) { + setState(State.IDLE); + phone.notifyDataConnection(reason); + mActiveApn = null; + } else if (state != State.IDLE) { + setState(State.DISCONNECTING); + } + } + + /** + * @param types comma delimited list of APN types + * @return array of APN types + */ + private String[] parseTypes(String types) { + String[] result; + // If unset, set to DEFAULT. + if (types == null || types.equals("")) { + result = new String[1]; + result[0] = Phone.APN_TYPE_ALL; + } else { + result = types.split(","); + } + return result; + } + + private ArrayList createApnList(Cursor cursor) { + ArrayList result = new ArrayList(); + if (cursor.moveToFirst()) { + do { + String[] types = parseTypes( + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); + ApnSetting apn = new ApnSetting( + cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), + cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), + types); + result.add(apn); + } while (cursor.moveToNext()); + } + return result; + } + + private PdpConnection findFreePdp() { + for (DataConnection conn : pdpList) { + PdpConnection pdp = (PdpConnection) conn; + if (pdp.getState() == DataConnection.State.INACTIVE) { + return pdp; + } + } + return null; + } + + private boolean setupData(String reason) { + ApnSetting apn; + PdpConnection pdp; + + apn = getNextApn(); + if (apn == null) return false; + pdp = findFreePdp(); + if (pdp == null) { + if (DBG) log("setupData: No free PdpConnection found!"); + return false; + } + mActiveApn = apn; + mActivePdp = pdp; + + Message msg = obtainMessage(); + msg.what = EVENT_DATA_SETUP_COMPLETE; + msg.obj = reason; + pdp.connect(apn, msg); + + setState(State.INITING); + phone.notifyDataConnection(reason); + return true; + } + + String getInterfaceName(String apnType) { + if (mActivePdp != null + && (apnType == null || mActiveApn.canHandleType(apnType))) { + return mActivePdp.getInterface(); + } + return null; + } + + protected String getIpAddress(String apnType) { + if (mActivePdp != null + && (apnType == null || mActiveApn.canHandleType(apnType))) { + return mActivePdp.getIpAddress(); + } + return null; + } + + String getGateway(String apnType) { + if (mActivePdp != null + && (apnType == null || mActiveApn.canHandleType(apnType))) { + return mActivePdp.getGatewayAddress(); + } + return null; + } + + protected String[] getDnsServers(String apnType) { + if (mActivePdp != null + && (apnType == null || mActiveApn.canHandleType(apnType))) { + return mActivePdp.getDnsServers(); + } + return null; + } + + private boolean + pdpStatesHasCID (ArrayList states, int cid) { + for (int i = 0, s = states.size() ; i < s ; i++) { + if (states.get(i).cid == cid) return true; + } + + return false; + } + + private boolean + pdpStatesHasActiveCID (ArrayList states, int cid) { + for (int i = 0, s = states.size() ; i < s ; i++) { + if (states.get(i).cid == cid) return states.get(i).active; + } + + return false; + } + + /** + * Handles changes to the APN database. + */ + private void onApnChanged() { + boolean isConnected; + + isConnected = (state != State.IDLE && state != State.FAILED); + + // The "current" may no longer be valid. MMS depends on this to send properly. + ((GSMPhone) phone).updateCurrentCarrierInProvider(); + + // TODO: It'd be nice to only do this if the changed entrie(s) + // match the current operator. + createAllApnList(); + if (state != State.DISCONNECTING) { + cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); + if (!isConnected) { + trySetupData(Phone.REASON_APN_CHANGED); + } + } + } + + /** + * @param explicitPoll if true, indicates that *we* polled for this + * update while state == CONNECTED rather than having it delivered + * via an unsolicited response (which could have happened at any + * previous state + */ + protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) { + ArrayList pdpStates; + + pdpStates = (ArrayList)(ar.result); + + if (ar.exception != null) { + // This is probably "radio not available" or something + // of that sort. If so, the whole connection is going + // to come down soon anyway + return; + } + + + // This is how things are supposed to work: + // The PDP list is supposed to be empty of the CID + // when it disconnects + + if (state == State.CONNECTED + && !pdpStatesHasCID(pdpStates, cidActive)) { + + // It looks like the PDP context has deactivated + // Tear everything down and try to reconnect + + Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); + + // Add an event log when the network drops PDP + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(cid, + TelephonyManager.getDefault().getNetworkType()); + + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); + + cleanUpConnection(true, null); + + return; + } + + if (true) { + // + // Workaround for issue #655426 + // + + // -------------------------- + + // This is how some things work now: the PDP context is still + // listed with active = false, which makes it hard to + // distinguish an activating context from an activated-and-then + // deactivated one. + // + // Here, we only consider this authoritative if we asked for the + // PDP list. If it was an unsolicited response, we poll again + // to make sure everyone agrees on the initial state + + if (state == State.CONNECTED + && !pdpStatesHasActiveCID(pdpStates, cidActive)) { + + if (!explicitPoll) { + // We think it disconnected but aren't sure...poll from our side + phone.mCM.getPDPContextList( + this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); + } else { + Log.i(LOG_TAG, "PDP connection has dropped (active=false case). " + + " Reconnecting"); + + // Log the network drop on the event log. + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(cid, + TelephonyManager.getDefault().getNetworkType()); + + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); + + cleanUpConnection(true, null); + } + } + } + } + + private void notifyDefaultData(String reason) { + setupDnsProperties(); + setState(State.CONNECTED); + phone.notifyDataConnection(reason); + startNetStatPoll(); + // reset reconnect timer + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + } + + private void setupDnsProperties() { + int mypid = android.os.Process.myPid(); + String[] servers = getDnsServers(null); + String propName; + String propVal; + int count; + + count = 0; + for (int i = 0; i < servers.length; i++) { + String serverAddr = servers[i]; + if (!TextUtils.equals(serverAddr, "0.0.0.0")) { + SystemProperties.set("net.dns" + (i+1) + "." + mypid, serverAddr); + count++; + } + } + for (int i = count+1; i <= 4; i++) { + propName = "net.dns" + i + "." + mypid; + propVal = SystemProperties.get(propName); + if (propVal.length() != 0) { + SystemProperties.set(propName, ""); + } + } + /* + * Bump the property that tells the name resolver library + * to reread the DNS server list from the properties. + */ + propVal = SystemProperties.get("net.dnschange"); + if (propVal.length() != 0) { + try { + int n = Integer.parseInt(propVal); + SystemProperties.set("net.dnschange", "" + (n+1)); + } catch (NumberFormatException e) { + } + } + } + + /** + * This is a kludge to deal with the fact that + * the PDP state change notification doesn't always work + * with certain RIL impl's/basebands + * + */ + private void startPeriodicPdpPoll() { + removeMessages(EVENT_POLL_PDP); + + sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); + } + + private void resetPollStats() { + txPkts = -1; + rxPkts = -1; + sentSinceLastRecv = 0; + netStatPollPeriod = POLL_NETSTAT_MILLIS; + mNoRecvPollCount = 0; + } + + private void doRecovery() { + if (state == State.CONNECTED) { + int maxPdpReset = Settings.Gservices.getInt(mResolver, + Settings.Gservices.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, + DEFAULT_MAX_PDP_RESET_FAIL); + if (mPdpResetCount < maxPdpReset) { + mPdpResetCount++; + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, sentSinceLastRecv); + cleanUpConnection(true, Phone.REASON_PDP_RESET); + } else { + mPdpResetCount = 0; + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); + ((GSMPhone) phone).mSST.reRegisterNetwork(null); + } + // TODO: Add increasingly drastic recovery steps, eg, + // reset the radio, reset the device. + } + } + + protected void startNetStatPoll() { + if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) { + Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); + resetPollStats(); + netStatPollEnabled = true; + mPollNetStat.run(); + } + } + + protected void stopNetStatPoll() { + netStatPollEnabled = false; + removeCallbacks(mPollNetStat); + Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); + } + + protected void restartRadio() { + Log.d(LOG_TAG, "************TURN OFF RADIO**************"); + cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); + phone.mCM.setRadioPower(false, null); + /* Note: no need to call setRadioPower(true). Assuming the desired + * radio power state is still ON (as tracked by ServiceStateTracker), + * ServiceStateTracker will call setRadioPower when it receives the + * RADIO_STATE_CHANGED notification for the power off. And if the + * desired power state has changed in the interim, we don't want to + * override it with an unconditional power on. + */ + + int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); + SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); + } + + private Runnable mPollNetStat = new Runnable() + { + + public void run() { + long sent, received; + long preTxPkts = -1, preRxPkts = -1; + + Activity newActivity; + + preTxPkts = txPkts; + preRxPkts = rxPkts; + + try { + txPkts = netstat.getMobileTxPackets(); + rxPkts = netstat.getMobileRxPackets(); + } catch (RemoteException e) { + txPkts = 0; + rxPkts = 0; + } + + //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); + + if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { + sent = txPkts - preTxPkts; + received = rxPkts - preRxPkts; + + if ( sent > 0 && received > 0 ) { + sentSinceLastRecv = 0; + newActivity = Activity.DATAINANDOUT; + mPdpResetCount = 0; + } else if (sent > 0 && received == 0) { + if (phone.getState() == Phone.State.IDLE) { + sentSinceLastRecv += sent; + } else { + sentSinceLastRecv = 0; + } + newActivity = Activity.DATAOUT; + } else if (sent == 0 && received > 0) { + sentSinceLastRecv = 0; + newActivity = Activity.DATAIN; + mPdpResetCount = 0; + } else if (sent == 0 && received == 0) { + newActivity = Activity.NONE; + } else { + sentSinceLastRecv = 0; + newActivity = Activity.NONE; + } + + if (activity != newActivity && mIsScreenOn) { + activity = newActivity; + phone.notifyDataActivity(); + } + } + + int watchdogTrigger = Settings.Gservices.getInt(mResolver, + Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, + NUMBER_SENT_PACKETS_OF_HANG); + + if (sentSinceLastRecv >= watchdogTrigger) { + // we already have NUMBER_SENT_PACKETS sent without ack + if (mNoRecvPollCount == 0) { + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, + sentSinceLastRecv); + } + + int noRecvPollLimit = Settings.Gservices.getInt(mResolver, + Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); + + if (mNoRecvPollCount < noRecvPollLimit) { + // It's possible the PDP context went down and we weren't notified. + // Start polling the context list in an attempt to recover. + if (DBG) log("no DATAIN in a while; polling PDP"); + phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); + + mNoRecvPollCount++; + + // Slow down the poll interval to let things happen + netStatPollPeriod = Settings.Gservices.getInt(mResolver, + Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, + POLL_NETSTAT_SLOW_MILLIS); + } else { + if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + + " pkts since last received"); + // We've exceeded the threshold. Run ping test as a final check; + // it will proceed with recovery if ping fails. + stopNetStatPoll(); + Thread pingTest = new Thread() { + public void run() { + runPingTest(); + } + }; + mPingTestActive = true; + pingTest.start(); + } + } else { + mNoRecvPollCount = 0; + if (mIsScreenOn) { + netStatPollPeriod = Settings.Gservices.getInt(mResolver, + Settings.Gservices.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); + } else { + netStatPollPeriod = Settings.Gservices.getInt(mResolver, + Settings.Gservices.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, + POLL_NETSTAT_SCREEN_OFF_MILLIS); + } + } + + if (netStatPollEnabled) { + mDataConnectionTracker.postDelayed(this, netStatPollPeriod); + } + } + }; + + private void runPingTest () { + int status = -1; + try { + String address = Settings.Gservices.getString(mResolver, + Settings.Gservices.PDP_WATCHDOG_PING_ADDRESS); + int deadline = Settings.Gservices.getInt(mResolver, + Settings.Gservices.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE); + if (DBG) log("pinging " + address + " for " + deadline + "s"); + if (address != null && !NULL_IP.equals(address)) { + Process p = Runtime.getRuntime() + .exec("ping -c 1 -i 1 -w "+ deadline + " " + address); + status = p.waitFor(); + } + } catch (IOException e) { + Log.w(LOG_TAG, "ping failed: IOException"); + } catch (Exception e) { + Log.w(LOG_TAG, "exception trying to ping"); + } + + if (status == 0) { + // ping succeeded. False alarm. Reset netStatPoll. + // ("-1" for this event indicates a false alarm) + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, -1); + mPdpResetCount = 0; + sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL)); + } else { + // ping failed. Proceed with recovery. + sendMessage(obtainMessage(EVENT_START_RECOVERY)); + } + } + + /** + * Returns true if the last fail cause is something that + * seems like it deserves an error notification. + * Transient errors are ignored + */ + private boolean shouldPostNotification(PdpConnection.FailCause cause) { + boolean shouldPost = true; + // TODO CHECK + // if (dataLink != null) { + // shouldPost = dataLink.getLastLinkExitCode() != DataLink.EXIT_OPEN_FAILED; + //} + return (shouldPost && cause != PdpConnection.FailCause.UNKNOWN); + } + + /** + * Return true if data connection need to be setup after disconnected due to + * reason. + * + * @param reason the reason why data is disconnected + * @return true if try setup data connection is need for this reason + */ + private boolean retryAfterDisconnected(String reason) { + boolean retry = true; + + if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) || + Phone.REASON_DATA_DISABLED.equals(reason) ) { + retry = false; + } + return retry; + } + + private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { + if (state == State.FAILED) { + Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " + + (nextReconnectDelay / 1000) + "s"); + + AlarmManager am = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(INTENT_RECONNECT_ALARM); + intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); + mReconnectIntent = PendingIntent.getBroadcast( + phone.getContext(), 0, intent, 0); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + nextReconnectDelay, + mReconnectIntent); + + // double it for next time + nextReconnectDelay *= 2; + if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) { + nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS; + } + + if (!shouldPostNotification(lastFailCauseCode)) { + Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " + + "-- likely transient error"); + } else { + notifyNoData(lastFailCauseCode); + } + } + } + + private void notifyNoData(PdpConnection.FailCause lastFailCauseCode) { + setState(State.FAILED); + } + + protected void onRecordsLoaded() { + createAllApnList(); + if (state == State.FAILED) { + cleanUpConnection(false, null); + } + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + } + + protected void onEnableNewApn() { + // TODO: To support simultaneous PDP contexts, this should really only call + // cleanUpConnection if it needs to free up a PdpConnection. + cleanUpConnection(true, Phone.REASON_APN_SWITCHED); + } + + protected void onTrySetupData() { + trySetupData(null); + } + + protected void onRestoreDefaultApn() { + if (DBG) Log.d(LOG_TAG, "Restore default APN"); + setEnabled(Phone.APN_TYPE_MMS, false); + + if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { + cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN); + mRequestedApnType = Phone.APN_TYPE_DEFAULT; + } + } + + protected void onRoamingOff() { + trySetupData(Phone.REASON_ROAMING_OFF); + } + + protected void onRoamingOn() { + if (getDataOnRoamingEnabled()) { + trySetupData(Phone.REASON_ROAMING_ON); + } else { + if (DBG) log("Tear down data connection on roaming."); + cleanUpConnection(true, Phone.REASON_ROAMING_ON); + } + } + + protected void onRadioAvailable() { + if (phone.getSimulatedRadioControl() != null) { + // Assume data is connected on the simulator + // FIXME this can be improved + setState(State.CONNECTED); + phone.notifyDataConnection(null); + + Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); + } + + if (state != State.IDLE) { + cleanUpConnection(true, null); + } + } + + protected void onRadioOffOrNotAvailable() { + // Make sure our reconnect delay starts at the initial value + // next time the radio comes on + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + + if (phone.getSimulatedRadioControl() != null) { + // Assume data is connected on the simulator + // FIXME this can be improved + Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); + } else { + if (DBG) log("Radio is off and clean up all connection"); + // TODO: Should we reset mRequestedApnType to "default"? + cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); + } + } + + protected void onDataSetupComplete(AsyncResult ar) { + String reason = null; + if (ar.userObj instanceof String) { + reason = (String) ar.userObj; + } + + if (ar.exception == null) { + // everything is setup + + /* + * We may have switched away from the default PDP context + * in order to enable a "special" APN (e.g., for MMS + * traffic). Set a timer to switch back and/or disable the + * special APN, so that a negligient application doesn't + * permanently prevent data connectivity. What we are + * protecting against here is not malicious apps, but + * rather an app that inadvertantly fails to reset to the + * default APN, or that dies before doing so. + */ + if (dataEnabled[APN_MMS_ID]) { + removeMessages(EVENT_RESTORE_DEFAULT_APN); + sendMessageDelayed(obtainMessage(EVENT_RESTORE_DEFAULT_APN), + getRestoreDefaultApnDelay()); + } + if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { + SystemProperties.set("gsm.defaultpdpcontext.active", "true"); + if (canSetPreferApn && preferredApn == null) { + Log.d(LOG_TAG, "PREFERED APN is null"); + preferredApn = mActiveApn; + setPreferredApn(preferredApn.id); + } + } else { + SystemProperties.set("gsm.defaultpdpcontext.active", "false"); + } + notifyDefaultData(reason); + + // TODO: For simultaneous PDP support, we need to build another + // trigger another TRY_SETUP_DATA for the next APN type. (Note + // that the existing connection may service that type, in which + // case we should try the next type, etc. + } else { + PdpConnection.FailCause cause; + cause = (PdpConnection.FailCause) (ar.result); + if(DBG) log("PDP setup failed " + cause); + // Log this failure to the Event Logs. + if (cause == PdpConnection.FailCause.BAD_APN || + cause == PdpConnection.FailCause.BAD_PAP_SECRET || + cause == PdpConnection.FailCause.BARRED || + cause == PdpConnection.FailCause.RADIO_ERROR_RETRY || + cause == PdpConnection.FailCause.SUSPENED_TEMPORARY || + cause == PdpConnection.FailCause.UNKNOWN || + cause == PdpConnection.FailCause.USER_AUTHENTICATION) { + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List( + cause.ordinal(), cid, + TelephonyManager.getDefault().getNetworkType()); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); + } + + // No try for permanent failure + if (cause.isPermanentFail()) { + notifyNoData(cause); + } + + if (tryNextApn(cause)) { + waitingApns.remove(0); + if (waitingApns.isEmpty()) { + // No more to try, start delayed retry + startDelayedRetry(cause, reason); + } else { + // we still have more apns to try + setState(State.SCANNING); + trySetupData(reason); + } + } else { + startDelayedRetry(cause, reason); + } + } + } + + protected void onDisconnectDone(AsyncResult ar) { + String reason = null; + if(DBG) log("EVENT_DISCONNECT_DONE"); + if (ar.userObj instanceof String) { + reason = (String) ar.userObj; + } + setState(State.IDLE); + phone.notifyDataConnection(reason); + mActiveApn = null; + if (retryAfterDisconnected(reason)) { + trySetupData(reason); + } + } + + protected void onPollPdp() { + if (state == State.CONNECTED) { + // only poll when connected + phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); + sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); + } + } + + protected void onVoiceCallStarted() { + if (state == State.CONNECTED && !((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) { + stopNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); + } + } + + protected void onVoiceCallEnded() { + if (state == State.CONNECTED) { + if (!((GSMPhone) phone).mSST.isConcurrentVoiceAndData()) { + startNetStatPoll(); + phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); + } else { + // clean slate after call end. + resetPollStats(); + } + } else { + // in case data setup was attempted when we were on a voice call + trySetupData(Phone.REASON_VOICE_CALL_ENDED); + } + } + + private boolean tryNextApn(FailCause cause) { + return (cause != FailCause.RADIO_NOT_AVAILABLE) + && (cause != FailCause.RADIO_OFF) + && (cause != FailCause.RADIO_ERROR_RETRY) + && (cause != FailCause.NO_SIGNAL) + && (cause != FailCause.SIM_LOCKED); + } + + private int getRestoreDefaultApnDelay() { + String restoreApnDelayStr = SystemProperties.get(APN_RESTORE_DELAY_PROP_NAME); + + if (restoreApnDelayStr != null && restoreApnDelayStr.length() != 0) { + try { + return Integer.valueOf(restoreApnDelayStr); + } catch (NumberFormatException e) { + } + } + return RESTORE_DEFAULT_APN_DELAY; + } + + /** + * Based on the sim operator numeric, create a list for all possible pdps + * with all apns associated with that pdp + * + * + */ + private void createAllApnList() { + allApns = new ArrayList(); + String operator = ((GSMPhone) phone).mSIMRecords.getSIMOperatorNumeric(); + + if (operator != null) { + String selection = "numeric = '" + operator + "'"; + + Cursor cursor = phone.getContext().getContentResolver().query( + Telephony.Carriers.CONTENT_URI, null, selection, null, null); + + if (cursor != null) { + if (cursor.getCount() > 0) { + allApns = createApnList(cursor); + // TODO: Figure out where this fits in. This basically just + // writes the pap-secrets file. No longer tied to PdpConnection + // object. Not used on current platform (no ppp). + //PdpConnection pdp = pdpList.get(pdp_name); + //if (pdp != null && pdp.dataLink != null) { + // pdp.dataLink.setPasswordInfo(cursor); + //} + } + cursor.close(); + } + } + + if (allApns.isEmpty()) { + if (DBG) log("No APN found for carrier: " + operator); + preferredApn = null; + notifyNoData(PdpConnection.FailCause.BAD_APN); + } else { + preferredApn = getPreferredApn(); + Log.d(LOG_TAG, "Get PreferredAPN"); + if (preferredApn != null && !preferredApn.numeric.equals(operator)) { + preferredApn = null; + setPreferredApn(-1); + } + } + } + + private void createAllPdpList() { + pdpList = new ArrayList(); + DataConnection pdp; + + for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { + pdp = new PdpConnection((GSMPhone) phone); + pdpList.add(pdp); + } + } + + private void destroyAllPdpList() { + if(pdpList != null) { + PdpConnection pdp; + pdpList.removeAll(pdpList); + } + } + + /** + * + * @return waitingApns list to be used to create PDP + * error when waitingApns.isEmpty() + */ + private ArrayList buildWaitingApns() { + ArrayList apnList = new ArrayList(); + String operator = ((GSMPhone )phone).mSIMRecords.getSIMOperatorNumeric(); + + if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { + if (canSetPreferApn && preferredApn != null) { + Log.i(LOG_TAG, "Preferred APN:" + operator + ":" + + preferredApn.numeric + ":" + preferredApn); + if (preferredApn.numeric.equals(operator)) { + Log.i(LOG_TAG, "Waiting APN set to preferred APN"); + apnList.add(preferredApn); + return apnList; + } else { + setPreferredApn(-1); + preferredApn = null; + } + } + } + + if (allApns != null) { + for (ApnSetting apn : allApns) { + if (apn.canHandleType(mRequestedApnType)) { + apnList.add(apn); + } + } + } + return apnList; + } + + /** + * Get next apn in waitingApns + * @return the first apn found in waitingApns, null if none + */ + private ApnSetting getNextApn() { + ArrayList list = waitingApns; + ApnSetting apn = null; + + if (list != null) { + if (!list.isEmpty()) { + apn = list.get(0); + } + } + return apn; + } + + private String apnListToString (ArrayList apns) { + StringBuilder result = new StringBuilder(); + for (int i = 0, size = apns.size(); i < size; i++) { + result.append('[') + .append(apns.get(i).toString()) + .append(']'); + } + return result.toString(); + } + + private void startDelayedRetry(PdpConnection.FailCause cause, String reason) { + notifyNoData(cause); + if (mRequestedApnType != Phone.APN_TYPE_DEFAULT) { + sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN)); + } + else { + reconnectAfterFail(cause, reason); + } + } + + private void setPreferredApn(int pos) { + if (!canSetPreferApn) { + return; + } + + ContentResolver resolver = phone.getContext().getContentResolver(); + resolver.delete(PREFERAPN_URI, null, null); + + if (pos >= 0) { + ContentValues values = new ContentValues(); + values.put(APN_ID, pos); + resolver.insert(PREFERAPN_URI, values); + } + } + + private ApnSetting getPreferredApn() { + if (allApns.isEmpty()) { + return null; + } + + Cursor cursor = phone.getContext().getContentResolver().query( + PREFERAPN_URI, new String[] { "_id", "name", "apn" }, + null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); + + if (cursor != null) { + canSetPreferApn = true; + } else { + canSetPreferApn = false; + } + + if (canSetPreferApn && cursor.getCount() > 0) { + int pos; + cursor.moveToFirst(); + pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); + for(ApnSetting p:allApns) { + if (p.id == pos && p.canHandleType(mRequestedApnType)) { + cursor.close(); + return p; + } + } + } + + if (cursor != null) { + cursor.close(); + } + + return null; + } + + public void handleMessage (Message msg) { + + switch (msg.what) { + case EVENT_RECORDS_LOADED: + onRecordsLoaded(); + break; + + case EVENT_ENABLE_NEW_APN: + onEnableNewApn(); + break; + + case EVENT_RESTORE_DEFAULT_APN: + onRestoreDefaultApn(); + break; + + case EVENT_GPRS_DETACHED: + onGprsDetached(); + break; + + case EVENT_GPRS_ATTACHED: + onGprsAttached(); + break; + + case EVENT_DATA_STATE_CHANGED: + onPdpStateChanged((AsyncResult) msg.obj, false); + break; + + case EVENT_GET_PDP_LIST_COMPLETE: + onPdpStateChanged((AsyncResult) msg.obj, true); + break; + + case EVENT_POLL_PDP: + onPollPdp(); + break; + + case EVENT_START_NETSTAT_POLL: + mPingTestActive = false; + startNetStatPoll(); + break; + + case EVENT_START_RECOVERY: + mPingTestActive = false; + doRecovery(); + break; + + case EVENT_APN_CHANGED: + onApnChanged(); + break; + + case EVENT_PS_RESTRICT_ENABLED: + /** + * We don't need to explicitly to tear down the PDP context + * when PS restricted is enabled. The base band will deactive + * PDP context and notify us with PDP_CONTEXT_CHANGED. + * But we should stop the network polling and prevent reset PDP. + */ + Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); + stopNetStatPoll(); + mIsPsRestricted = true; + break; + + case EVENT_PS_RESTRICT_DISABLED: + /** + * When PS restrict is removed, we need setup PDP connection if + * PDP connection is down. + */ + Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); + mIsPsRestricted = false; + if (state == State.CONNECTED) { + startNetStatPoll(); + } else { + if (state == State.FAILED) { + cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED); + nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS; + } + trySetupData(Phone.REASON_PS_RESTRICT_ENABLED); + } + break; + + default: + // handle the message in the super class DataConnectionTracker + super.handleMessage(msg); + break; + } + } + + protected void log(String s) { + Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s); + } + +} diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java index 04f8332..4db8fc6 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java @@ -18,39 +18,40 @@ package com.android.internal.telephony.gsm; import android.content.Context; import com.android.internal.telephony.*; + import android.os.*; -import android.os.AsyncResult; +import android.telephony.PhoneNumberUtils; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.util.Log; + +import static com.android.internal.telephony.CommandsInterface.*; + import java.util.regex.Pattern; import java.util.regex.Matcher; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.telephony.PhoneNumberUtils; -import static com.android.internal.telephony.gsm.CommandsInterface.*; /** * The motto for this file is: * - * "NOTE: By using the # as a separator, most cases are expected to be unambiguous." + * "NOTE: By using the # as a separator, most cases are expected to be unambiguous." * -- TS 22.030 6.5.2 * * {@hide} * */ -public final class GsmMmiCode extends Handler implements MmiCode -{ +public final class GsmMmiCode extends Handler implements MmiCode { static final String LOG_TAG = "GSM"; //***** Constants - + // From TS 22.030 6.5.2 static final String ACTION_ACTIVATE = "*"; static final String ACTION_DEACTIVATE = "#"; static final String ACTION_INTERROGATE = "*#"; static final String ACTION_REGISTER = "**"; static final String ACTION_ERASURE = "##"; - - // Supp Service cocdes from TS 22.030 Annex B + + // Supp Service cocdes from TS 22.030 Annex B //Called line presentation static final String SC_CLIP = "30"; @@ -102,25 +103,24 @@ public final class GsmMmiCode extends Handler implements MmiCode GSMPhone phone; Context context; - + String action; // One of ACTION_* String sc; // Service Code String sia, sib, sic; // Service Info a,b,c String poundString; // Entire MMI string up to and including # String dialingNumber; String pwd; // For password registration - - /** Set to true in processCode, not at newFromDialString time */ + /** Set to true in processCode, not at newFromDialString time */ private boolean isPendingUSSD; private boolean isUssdRequest; - State state = State.PENDING; + State state = State.PENDING; CharSequence message; - + //***** Class Variables - + // See TS 22.030 6.5.2 "Structure of the MMI" @@ -137,9 +137,9 @@ public final class GsmMmiCode extends Handler implements MmiCode 10 = dialing number */ - static final int MATCH_GROUP_POUND_STRING = 1; + static final int MATCH_GROUP_POUND_STRING = 1; - static final int MATCH_GROUP_ACTION = 2; + static final int MATCH_GROUP_ACTION = 2; //(activation/interrogation/registration/erasure) static final int MATCH_GROUP_SERVICE_CODE = 3; @@ -151,7 +151,7 @@ public final class GsmMmiCode extends Handler implements MmiCode //***** Public Class methods - + /** * Some dial strings in GSM are defined to do non-call setup * things, such as modify or query supplementry service settings (eg, call @@ -165,9 +165,8 @@ public final class GsmMmiCode extends Handler implements MmiCode * Please see flow chart in TS 22.030 6.5.3.2 */ - static GsmMmiCode - newFromDialString(String dialString, GSMPhone phone) - { + static GsmMmiCode + newFromDialString(String dialString, GSMPhone phone) { Matcher m; GsmMmiCode ret = null; @@ -187,7 +186,7 @@ public final class GsmMmiCode extends Handler implements MmiCode } else if (dialString.endsWith("#")) { // TS 22.030 sec 6.5.3.2 - // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet + // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND". ret = new GsmMmiCode(phone); @@ -202,16 +201,15 @@ public final class GsmMmiCode extends Handler implements MmiCode } static GsmMmiCode - newNetworkInitiatedUssd (String ussdMessage, - boolean isUssdRequest, GSMPhone phone) - { + newNetworkInitiatedUssd (String ussdMessage, + boolean isUssdRequest, GSMPhone phone) { GsmMmiCode ret; ret = new GsmMmiCode(phone); ret.message = ussdMessage; ret.isUssdRequest = isUssdRequest; - + // If it's a request, set to PENDING so that it's cancelable. if (isUssdRequest) { ret.isPendingUSSD = true; @@ -225,42 +223,39 @@ public final class GsmMmiCode extends Handler implements MmiCode static GsmMmiCode newFromUssdUserInput(String ussdMessge, GSMPhone phone) { GsmMmiCode ret = new GsmMmiCode(phone); - + ret.message = ussdMessge; ret.state = State.PENDING; ret.isPendingUSSD = true; - + return ret; } //***** Private Class methods - /** make empty strings be null. - * Regexp returns empty strings for empty groups + /** make empty strings be null. + * Regexp returns empty strings for empty groups */ private static String - makeEmptyNull (String s) - { + makeEmptyNull (String s) { if (s != null && s.length() == 0) return null; return s; } /** returns true of the string is empty or null */ - private static boolean - isEmptyOrNull(CharSequence s) - { + private static boolean + isEmptyOrNull(CharSequence s) { return s == null || (s.length() == 0); } private static int - scToCallForwardReason(String sc) - { - if (sc == null) { + scToCallForwardReason(String sc) { + if (sc == null) { throw new RuntimeException ("invalid call forward sc"); } - + if (sc.equals(SC_CF_All)) { return CommandsInterface.CF_REASON_ALL; } else if (sc.equals(SC_CFU)) { @@ -279,8 +274,7 @@ public final class GsmMmiCode extends Handler implements MmiCode } private static int - siToServiceClass(String si) - { + siToServiceClass(String si) { if (si == null || si.length() == 0) { return SERVICE_CLASS_NONE; } else { @@ -299,7 +293,7 @@ public final class GsmMmiCode extends Handler implements MmiCode /* Note for code 20: From TS 22.030 Annex C: - "All GPRS bearer services" are not included in "All tele and bearer services" + "All GPRS bearer services" are not included in "All tele and bearer services" and "All bearer services"." ....so SERVICE_CLASS_DATA, which (according to 27.007) includes GPRS */ @@ -319,8 +313,7 @@ public final class GsmMmiCode extends Handler implements MmiCode } private static int - siToTime (String si) - { + siToTime (String si) { if (si == null || si.length() == 0) { return 0; } else { @@ -330,33 +323,30 @@ public final class GsmMmiCode extends Handler implements MmiCode } static boolean - isServiceCodeCallForwarding(String sc) - { - return sc != null && - (sc.equals(SC_CFU) - || sc.equals(SC_CFB) || sc.equals(SC_CFNRy) - || sc.equals(SC_CFNR) || sc.equals(SC_CF_All) + isServiceCodeCallForwarding(String sc) { + return sc != null && + (sc.equals(SC_CFU) + || sc.equals(SC_CFB) || sc.equals(SC_CFNRy) + || sc.equals(SC_CFNR) || sc.equals(SC_CF_All) || sc.equals(SC_CF_All_Conditional)); } static boolean - isServiceCodeCallBarring(String sc) - { + isServiceCodeCallBarring(String sc) { return sc != null && - (sc.equals(SC_BAOC) + (sc.equals(SC_BAOC) || sc.equals(SC_BAOIC) || sc.equals(SC_BAOICxH) || sc.equals(SC_BAIC) || sc.equals(SC_BAICr) || sc.equals(SC_BA_ALL) || sc.equals(SC_BA_MO) - || sc.equals(SC_BA_MT)); + || sc.equals(SC_BA_MT)); } static String - scToBarringFacility(String sc) - { - if (sc == null) { + scToBarringFacility(String sc) { + if (sc == null) { throw new RuntimeException ("invalid call barring sc"); } @@ -383,11 +373,10 @@ public final class GsmMmiCode extends Handler implements MmiCode //***** Constructor - GsmMmiCode (GSMPhone phone) - { + GsmMmiCode (GSMPhone phone) { // The telephony unit-test cases may create GsmMmiCode's // in secondary threads - super(phone.h.getLooper()); + super(phone.getHandler().getLooper()); this.phone = phone; this.context = phone.getContext(); } @@ -395,21 +384,18 @@ public final class GsmMmiCode extends Handler implements MmiCode //***** MmiCode implementation public State - getState() - { + getState() { return state; } public CharSequence - getMessage() - { + getMessage() { return message; } // inherited javadoc suffices public void - cancel() - { + cancel() { // Complete or failed cannot be cancelled if (state == State.COMPLETE || state == State.FAILED) { return; @@ -423,7 +409,7 @@ public final class GsmMmiCode extends Handler implements MmiCode * cancel it. */ phone.mCM.cancelPendingUssd(obtainMessage(EVENT_USSD_CANCEL_COMPLETE, this)); - + /* * Don't call phone.onMMIDone here; wait for CANCEL_COMPLETE notice * from RIL. @@ -436,7 +422,6 @@ public final class GsmMmiCode extends Handler implements MmiCode phone.onMMIDone (this); } - } public boolean isCancelable() { @@ -445,20 +430,17 @@ public final class GsmMmiCode extends Handler implements MmiCode } //***** Instance Methods - /** Does this dial string contain a structured or unstructured MMI code? */ boolean - isMMI() - { + isMMI() { return poundString != null; } /* Is this a 1 or 2 digit "short code" as defined in TS 22.030 sec 6.5.3.2? */ boolean - isShortCode() - { - return poundString == null + isShortCode() { + return poundString == null && dialingNumber != null && dialingNumber.length() <= 2; } @@ -479,7 +461,7 @@ public final class GsmMmiCode extends Handler implements MmiCode * for treating "0" and "00" as call setup strings. */ || dialString.equals("0") - || dialString.equals("00")))); + || dialString.equals("00")))); } /** * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related @@ -489,17 +471,16 @@ public final class GsmMmiCode extends Handler implements MmiCode || sc.equals(SC_PUK) || sc.equals(SC_PUK2)); } - /** + /** * *See TS 22.030 Annex B - * In temporary mode, to suppress CLIR for a single call, enter: + * In temporary mode, to suppress CLIR for a single call, enter: * " * 31 # SEND " - * In temporary mode, to invoke CLIR for a single call enter: + * In temporary mode, to invoke CLIR for a single call enter: * " # 31 # SEND " */ - - boolean - isTemporaryModeCLIR() - { + + boolean + isTemporaryModeCLIR() { return sc != null && sc.equals(SC_CLIR) && dialingNumber != null && (isActivate() || isDeactivate()); } @@ -509,50 +490,43 @@ public final class GsmMmiCode extends Handler implements MmiCode * See also isTemporaryModeCLIR() */ int - getCLIRMode() - { + getCLIRMode() { if (sc != null && sc.equals(SC_CLIR)) { if (isActivate()) { return CommandsInterface.CLIR_SUPPRESSION; } else if (isDeactivate()) { - return CommandsInterface.CLIR_INVOCATION; + return CommandsInterface.CLIR_INVOCATION; } } - + return CommandsInterface.CLIR_DEFAULT; } - - boolean isActivate() - { + + boolean isActivate() { return action != null && action.equals(ACTION_ACTIVATE); } - boolean isDeactivate() - { + boolean isDeactivate() { return action != null && action.equals(ACTION_DEACTIVATE); } - - boolean isInterrogate() - { + + boolean isInterrogate() { return action != null && action.equals(ACTION_INTERROGATE); } - boolean isRegister() - { + boolean isRegister() { return action != null && action.equals(ACTION_REGISTER); } - boolean isErasure() - { + boolean isErasure() { return action != null && action.equals(ACTION_ERASURE); } - /** + /** * Returns true if this is a USSD code that's been submitted to the * network...eg, after processCode() is called */ - public boolean isPendingUSSD() - { + public boolean isPendingUSSD() { return isPendingUSSD; } @@ -562,8 +536,7 @@ public final class GsmMmiCode extends Handler implements MmiCode /** Process a MMI code or short code...anything that isn't a dialing number */ void - processCode () - { + processCode () { try { if (isShortCode()) { Log.d(LOG_TAG, "isShortCode"); @@ -573,7 +546,7 @@ public final class GsmMmiCode extends Handler implements MmiCode // We should have no dialing numbers here throw new RuntimeException ("Invalid or Unsupported MMI Code"); } else if (sc != null && sc.equals(SC_CLIP)) { - Log.d(LOG_TAG, "is CLIP"); + Log.d(LOG_TAG, "is CLIP"); if (isInterrogate()) { phone.mCM.queryCLIP( obtainMessage(EVENT_QUERY_COMPLETE, this)); @@ -581,7 +554,7 @@ public final class GsmMmiCode extends Handler implements MmiCode throw new RuntimeException ("Invalid or Unsupported MMI Code"); } } else if (sc != null && sc.equals(SC_CLIR)) { - Log.d(LOG_TAG, "is CLIR"); + Log.d(LOG_TAG, "is CLIR"); if (isActivate()) { phone.mCM.setCLIR(CommandsInterface.CLIR_INVOCATION, obtainMessage(EVENT_SET_COMPLETE, this)); @@ -688,13 +661,13 @@ public final class GsmMmiCode extends Handler implements MmiCode // sia = basic service group int serviceClass = siToServiceClass(sia); - if (isActivate() || isDeactivate()) { + if (isActivate() || isDeactivate()) { phone.mCM.setCallWaiting(isActivate(), serviceClass, obtainMessage(EVENT_SET_COMPLETE, this)); - } else if (isInterrogate()) { + } else if (isInterrogate()) { phone.mCM.queryCallWaiting(serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this)); - } else { + } else { throw new RuntimeException ("Invalid or Unsupported MMI Code"); } } else if (isPinCommand()) { @@ -718,16 +691,16 @@ public final class GsmMmiCode extends Handler implements MmiCode } else { // pre-checks OK if (sc.equals(SC_PIN)) { - phone.mCM.changeSimPin(oldPinOrPuk, newPin, + phone.mCM.changeIccPin(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (sc.equals(SC_PIN2)) { - phone.mCM.changeSimPin2(oldPinOrPuk, newPin, + phone.mCM.changeIccPin2(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (sc.equals(SC_PUK)) { - phone.mCM.supplySimPuk(oldPinOrPuk, newPin, + phone.mCM.supplyIccPuk(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (sc.equals(SC_PUK2)) { - phone.mCM.supplySimPuk2(oldPinOrPuk, newPin, + phone.mCM.supplyIccPuk2(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } } @@ -743,7 +716,7 @@ public final class GsmMmiCode extends Handler implements MmiCode state = State.FAILED; message = context.getText(com.android.internal.R.string.mmiError); phone.onMMIDone(this); - } + } } private void handlePasswordError(int res) { @@ -755,8 +728,8 @@ public final class GsmMmiCode extends Handler implements MmiCode phone.onMMIDone(this); } - /** - * Called from GSMPhone + /** + * Called from GSMPhone * * An unsolicited USSD NOTIFY or REQUEST has come in matching * up with this pending USSD request @@ -765,8 +738,7 @@ public final class GsmMmiCode extends Handler implements MmiCode * active (ie, the network expects user input). */ void - onUssdFinished(String ussdMessage, boolean isUssdRequest) - { + onUssdFinished(String ussdMessage, boolean isUssdRequest) { if (state == State.PENDING) { if (ussdMessage == null) { message = context.getText(com.android.internal.R.string.mmiComplete); @@ -783,15 +755,14 @@ public final class GsmMmiCode extends Handler implements MmiCode } } - /** - * Called from GSMPhone + /** + * Called from GSMPhone * * The radio has reset, and this is still pending */ void - onUssdFinishedError() - { + onUssdFinishedError() { if (state == State.PENDING) { state = State.FAILED; message = context.getText(com.android.internal.R.string.mmiError); @@ -808,15 +779,14 @@ public final class GsmMmiCode extends Handler implements MmiCode // response does not complete this MMI code...we wait for // an unsolicited USSD "Notify" or "Request". // The matching up of this is doene in GSMPhone. - - phone.mCM.sendUSSD(ussdMessage, + + phone.mCM.sendUSSD(ussdMessage, obtainMessage(EVENT_USSD_COMPLETE, this)); } /** Called from GSMPhone.handleMessage; not a Handler subclass */ public void - handleMessage (Message msg) - { + handleMessage (Message msg) { AsyncResult ar; switch (msg.what) { @@ -865,13 +835,13 @@ public final class GsmMmiCode extends Handler implements MmiCode com.android.internal.R.string.mmiError); phone.onMMIDone(this); - } + } // Note that unlike most everything else, the USSD complete // response does not complete this MMI code...we wait for // an unsolicited USSD "Notify" or "Request". // The matching up of this is done in GSMPhone. - + break; case EVENT_USSD_CANCEL_COMPLETE: @@ -976,8 +946,7 @@ public final class GsmMmiCode extends Handler implements MmiCode } private void - onGetClirComplete(AsyncResult ar) - { + onGetClirComplete(AsyncResult ar) { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); @@ -996,35 +965,35 @@ public final class GsmMmiCode extends Handler implements MmiCode com.android.internal.R.string.serviceNotProvisioned)); state = State.COMPLETE; break; - + case 1: // CLIR provisioned in permanent mode sb.append(context.getText( com.android.internal.R.string.CLIRPermanent)); state = State.COMPLETE; break; - case 2: // unknown (e.g. no network, etc.) + case 2: // unknown (e.g. no network, etc.) sb.append(context.getText( com.android.internal.R.string.mmiError)); state = State.FAILED; break; - case 3: // CLIR temporary mode presentation restricted + case 3: // CLIR temporary mode presentation restricted // the 'n' parameter from TS 27.007 7.7 switch (clirArgs[0]) { default: case 0: // Default sb.append(context.getText( - com.android.internal.R.string.CLIRDefaultOnNextCallOn)); + com.android.internal.R.string.CLIRDefaultOnNextCallOn)); break; case 1: // CLIR invocation sb.append(context.getText( - com.android.internal.R.string.CLIRDefaultOnNextCallOn)); + com.android.internal.R.string.CLIRDefaultOnNextCallOn)); break; case 2: // CLIR suppression sb.append(context.getText( - com.android.internal.R.string.CLIRDefaultOnNextCallOff)); + com.android.internal.R.string.CLIRDefaultOnNextCallOff)); break; } state = State.COMPLETE; @@ -1036,21 +1005,21 @@ public final class GsmMmiCode extends Handler implements MmiCode default: case 0: // Default sb.append(context.getText( - com.android.internal.R.string.CLIRDefaultOffNextCallOff)); + com.android.internal.R.string.CLIRDefaultOffNextCallOff)); break; case 1: // CLIR invocation sb.append(context.getText( - com.android.internal.R.string.CLIRDefaultOffNextCallOn)); + com.android.internal.R.string.CLIRDefaultOffNextCallOn)); break; case 2: // CLIR suppression sb.append(context.getText( - com.android.internal.R.string.CLIRDefaultOffNextCallOff)); + com.android.internal.R.string.CLIRDefaultOffNextCallOff)); break; } state = State.COMPLETE; break; - } + } } message = sb; @@ -1059,23 +1028,29 @@ public final class GsmMmiCode extends Handler implements MmiCode /** * @param serviceClass 1 bit of the service class bit vectory - * @return String to be used for call forward query MMI response text. + * @return String to be used for call forward query MMI response text. * Returns null if unrecognized */ private CharSequence - serviceClassToCFString (int serviceClass) - { + serviceClassToCFString (int serviceClass) { switch (serviceClass) { - case SERVICE_CLASS_VOICE: return context.getText(com.android.internal.R.string.serviceClassVoice); - case SERVICE_CLASS_DATA: return context.getText(com.android.internal.R.string.serviceClassData); - case SERVICE_CLASS_FAX: return context.getText(com.android.internal.R.string.serviceClassFAX); - case SERVICE_CLASS_SMS: return context.getText(com.android.internal.R.string.serviceClassSMS); - case SERVICE_CLASS_DATA_SYNC: return context.getText(com.android.internal.R.string.serviceClassDataSync); - case SERVICE_CLASS_DATA_ASYNC: return context.getText(com.android.internal.R.string.serviceClassDataAsync); - case SERVICE_CLASS_PACKET: return context.getText(com.android.internal.R.string.serviceClassPacket); - case SERVICE_CLASS_PAD: return context.getText(com.android.internal.R.string.serviceClassPAD); - + case SERVICE_CLASS_VOICE: + return context.getText(com.android.internal.R.string.serviceClassVoice); + case SERVICE_CLASS_DATA: + return context.getText(com.android.internal.R.string.serviceClassData); + case SERVICE_CLASS_FAX: + return context.getText(com.android.internal.R.string.serviceClassFAX); + case SERVICE_CLASS_SMS: + return context.getText(com.android.internal.R.string.serviceClassSMS); + case SERVICE_CLASS_DATA_SYNC: + return context.getText(com.android.internal.R.string.serviceClassDataSync); + case SERVICE_CLASS_DATA_ASYNC: + return context.getText(com.android.internal.R.string.serviceClassDataAsync); + case SERVICE_CLASS_PACKET: + return context.getText(com.android.internal.R.string.serviceClassPacket); + case SERVICE_CLASS_PAD: + return context.getText(com.android.internal.R.string.serviceClassPAD); default: return null; } @@ -1084,8 +1059,7 @@ public final class GsmMmiCode extends Handler implements MmiCode /** one CallForwardInfo + serviceClassMask -> one line of text */ private CharSequence - makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) - { + makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { CharSequence template; String sources[] = {"{0}", "{1}", "{2}"}; CharSequence destinations[] = new CharSequence[3]; @@ -1094,7 +1068,7 @@ public final class GsmMmiCode extends Handler implements MmiCode // CF_REASON_NO_REPLY also has a time value associated with // it. All others don't. - needTimeTemplate = + needTimeTemplate = (info.reason == CommandsInterface.CF_REASON_NO_REPLY); if (info.status == 1) { @@ -1122,8 +1096,8 @@ public final class GsmMmiCode extends Handler implements MmiCode } // In the template (from strings.xmls) - // {0} is one of "bearerServiceCode*" - // {1} is dialing number + // {0} is one of "bearerServiceCode*" + // {1} is dialing number // {2} is time in seconds destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); @@ -1142,8 +1116,7 @@ public final class GsmMmiCode extends Handler implements MmiCode private void - onQueryCfComplete(AsyncResult ar) - { + onQueryCfComplete(AsyncResult ar) { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); @@ -1152,7 +1125,7 @@ public final class GsmMmiCode extends Handler implements MmiCode sb.append(context.getText(com.android.internal.R.string.mmiError)); } else { CallForwardInfo infos[]; - + infos = (CallForwardInfo[]) ar.result; if (infos.length == 0) { @@ -1166,18 +1139,18 @@ public final class GsmMmiCode extends Handler implements MmiCode SpannableStringBuilder tb = new SpannableStringBuilder(); // Each bit in the service class gets its own result line - // The service classes may be split up over multiple + // The service classes may be split up over multiple // CallForwardInfos. So, for each service classs, find out // which CallForwardInfo represents it and then build // the response text based on that - for (int serviceClassMask = 1 + for (int serviceClassMask = 1 ; serviceClassMask <= SERVICE_CLASS_MAX - ; serviceClassMask <<= 1 + ; serviceClassMask <<= 1 ) { for (int i = 0, s = infos.length; i < s ; i++) { if ((serviceClassMask & infos[i].serviceClass) != 0) { - tb.append(makeCFQueryResultMessage(infos[i], + tb.append(makeCFQueryResultMessage(infos[i], serviceClassMask)); tb.append("\n"); } @@ -1191,12 +1164,11 @@ public final class GsmMmiCode extends Handler implements MmiCode message = sb; phone.onMMIDone(this); - + } private void - onQueryComplete(AsyncResult ar) - { + onQueryComplete(AsyncResult ar) { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); @@ -1230,15 +1202,15 @@ public final class GsmMmiCode extends Handler implements MmiCode message = sb; phone.onMMIDone(this); } - + private CharSequence - createQueryCallWaitingResultMessage(int serviceClass) - { - StringBuilder sb = new StringBuilder(context.getText(com.android.internal.R.string.serviceEnabledFor)); + createQueryCallWaitingResultMessage(int serviceClass) { + StringBuilder sb = + new StringBuilder(context.getText(com.android.internal.R.string.serviceEnabledFor)); - for (int classMask = 1 + for (int classMask = 1 ; classMask <= SERVICE_CLASS_MAX - ; classMask <<= 1 + ; classMask <<= 1 ) { if ((classMask & serviceClass) != 0) { sb.append("\n"); @@ -1267,8 +1239,8 @@ public final class GsmMmiCode extends Handler implements MmiCode /*** * TODO: It would be nice to have a method here that can take in a dialstring and * figure out if there is an MMI code embedded within it. This code would replace - * some of the string parsing functionality in the Phone App's - * SpecialCharSequenceMgr class. + * some of the string parsing functionality in the Phone App's + * SpecialCharSequenceMgr class. */ } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java new file mode 100644 index 0000000..3e73caf --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -0,0 +1,379 @@ +/* + * 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.gsm; + +import android.app.Activity; +import android.app.PendingIntent; +import android.app.PendingIntent.CanceledException; +import android.content.Intent; +import android.os.AsyncResult; +import android.os.Message; +import android.telephony.ServiceState; +import android.util.Config; +import android.util.Log; + +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.gsm.SmsMessage; +import com.android.internal.telephony.SMSDispatcher; +import com.android.internal.telephony.SmsHeader; +import com.android.internal.telephony.SmsMessageBase; + +import java.util.ArrayList; +import java.util.HashMap; + + +final class GsmSMSDispatcher extends SMSDispatcher { + private static final String TAG = "GSM"; + + GsmSMSDispatcher(GSMPhone phone) { + super(phone); + } + + /** + * Called when a status report is received. This should correspond to + * a previously successful SEND. + * + * @param ar AsyncResult passed into the message handler. ar.result should + * be a String representing the status report PDU, as ASCII hex. + */ + protected void handleStatusReport(AsyncResult ar) { + String pduString = (String) ar.result; + SmsMessage sms = SmsMessage.newFromCDS(pduString); + + if (sms != null) { + int messageRef = sms.messageRef; + for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { + SmsTracker tracker = deliveryPendingList.get(i); + if (tracker.mMessageRef == messageRef) { + // Found it. Remove from list and broadcast. + deliveryPendingList.remove(i); + PendingIntent intent = tracker.mDeliveryIntent; + Intent fillIn = new Intent(); + fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString)); + try { + intent.send(mContext, Activity.RESULT_OK, fillIn); + } catch (CanceledException ex) {} + + // Only expect to see one tracker matching this messageref + break; + } + } + } + + if (mCm != null) { + mCm.acknowledgeLastIncomingSMS(true, null); + } + } + + + /** + * Dispatches an incoming SMS messages. + * + * @param sms the incoming message from the phone + */ + protected void dispatchMessage(SmsMessageBase smsb) { + + // If sms is null, means there was a parsing error. + // TODO: Should NAK this. + if (smsb == null) { + return; + } + SmsMessage sms = (SmsMessage) smsb; + boolean handled = false; + + // Special case the message waiting indicator messages + if (sms.isMWISetMessage()) { + ((GSMPhone) mPhone).updateMessageWaitingIndicator(true); + + if (sms.isMwiDontStore()) { + handled = true; + } + + if (Config.LOGD) { + Log.d(TAG, + "Received voice mail indicator set SMS shouldStore=" + + !handled); + } + } else if (sms.isMWIClearMessage()) { + ((GSMPhone) mPhone).updateMessageWaitingIndicator(false); + + if (sms.isMwiDontStore()) { + handled = true; + } + + if (Config.LOGD) { + Log.d(TAG, + "Received voice mail indicator clear SMS shouldStore=" + + !handled); + } + } + + if (handled) { + return; + } + + // Parse the headers to see if this is partial, or port addressed + int referenceNumber = -1; + int count = 0; + int sequence = 0; + int destPort = -1; + + SmsHeader header = sms.getUserDataHeader(); + if (header != null) { + for (SmsHeader.Element element : header.getElements()) { + try { + switch (element.getID()) { + case SmsHeader.CONCATENATED_8_BIT_REFERENCE: { + byte[] data = element.getData(); + + referenceNumber = data[0] & 0xff; + count = data[1] & 0xff; + sequence = data[2] & 0xff; + + // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence + // is zero, or sequence > count, ignore the entire element + if (count == 0 || sequence == 0 || sequence > count) { + referenceNumber = -1; + } + break; + } + + case SmsHeader.CONCATENATED_16_BIT_REFERENCE: { + byte[] data = element.getData(); + + referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff); + count = data[2] & 0xff; + sequence = data[3] & 0xff; + + // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence + // is zero, or sequence > count, ignore the entire element + if (count == 0 || sequence == 0 || sequence > count) { + referenceNumber = -1; + } + break; + } + + case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: { + byte[] data = element.getData(); + + destPort = (data[0] & 0xff) << 8; + destPort |= (data[1] & 0xff); + + break; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + Log.e(TAG, "Bad element in header", e); + return; // TODO: NACK the message or something, don't just discard. + } + } + } + + if (referenceNumber == -1) { + // notify everyone of the message if it isn't partial + byte[][] pdus = new byte[1][]; + pdus[0] = sms.getPdu(); + + if (destPort != -1) { + if (destPort == SmsHeader.PORT_WAP_PUSH) { + mWapPush.dispatchWapPdu(sms.getUserData()); + } + // The message was sent to a port, so concoct a URI for it + dispatchPortAddressedPdus(pdus, destPort); + } else { + // It's a normal message, dispatch it + dispatchPdus(pdus); + } + } else { + // Process the message part + processMessagePart(sms, referenceNumber, sequence, count, destPort); + } + } + + /** {@inheritDoc} */ + protected void sendMultipartText(String destinationAddress, String scAddress, + ArrayList parts, ArrayList sentIntents, + ArrayList deliveryIntents) { + int ref = ++sConcatenatedRef & 0xff; + + for (int i = 0, count = parts.size(); i < count; i++) { + // build SmsHeader + byte[] data = new byte[3]; + data[0] = (byte) ref; // reference #, unique per message + data[1] = (byte) count; // total part count + data[2] = (byte) (i + 1); // 1-based sequence + SmsHeader header = new SmsHeader(); + header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data)); + PendingIntent sentIntent = null; + PendingIntent deliveryIntent = null; + + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + if (deliveryIntents != null && deliveryIntents.size() > i) { + deliveryIntent = deliveryIntents.get(i); + } + + SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, + parts.get(i), deliveryIntent != null, header.toByteArray()); + + sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); + } + } + + /** + * Send a multi-part text based SMS which already passed SMS control check. + * + * It is the working function for sendMultipartText(). + * + * @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 ArrayList of strings that, in order, + * comprise the original message + * @param sentIntents if not null, an ArrayList of + * PendingIntents (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be Activity.RESULT_OK for success, + * or one of these errors: + * RESULT_ERROR_GENERIC_FAILURE + * RESULT_ERROR_RADIO_OFF + * RESULT_ERROR_NULL_PDU. + * @param deliveryIntents if not null, an ArrayList of + * PendingIntents (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"). + */ + private void sendMultipartTextWithPermit(String destinationAddress, + String scAddress, ArrayList parts, + ArrayList sentIntents, + ArrayList deliveryIntents) { + + PendingIntent sentIntent = null; + PendingIntent deliveryIntent = null; + + // check if in service + int ss = mPhone.getServiceState().getState(); + if (ss != ServiceState.STATE_IN_SERVICE) { + for (int i = 0, count = parts.size(); i < count; i++) { + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + SmsTracker tracker = SmsTrackerFactory(null, sentIntent, null); + handleNotInService(ss, tracker); + } + return; + } + + int ref = ++sConcatenatedRef & 0xff; + + for (int i = 0, count = parts.size(); i < count; i++) { + // build SmsHeader + byte[] data = new byte[3]; + data[0] = (byte) ref; // reference #, unique per message + data[1] = (byte) count; // total part count + data[2] = (byte) (i + 1); // 1-based sequence + SmsHeader header = new SmsHeader(); + header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data)); + + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + if (deliveryIntents != null && deliveryIntents.size() > i) { + deliveryIntent = deliveryIntents.get(i); + } + + SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, + parts.get(i), deliveryIntent != null, header.toByteArray()); + + HashMap map = new HashMap(); + map.put("smsc", pdus.encodedScAddress); + map.put("pdu", pdus.encodedMessage); + + SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); + sendSms(tracker); + } + } + + /** {@inheritDoc} */ + protected void sendSms(SmsTracker tracker) { + HashMap map = tracker.mData; + + byte smsc[] = (byte[]) map.get("smsc"); + byte pdu[] = (byte[]) map.get("pdu"); + + Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); + mCm.sendSMS(IccUtils.bytesToHexString(smsc), + IccUtils.bytesToHexString(pdu), reply); + } + + /** + * Send the multi-part SMS based on multipart Sms tracker + * + * @param tracker holds the multipart Sms tracker ready to be sent + */ + protected void sendMultipartSms (SmsTracker tracker) { + ArrayList parts; + ArrayList sentIntents; + ArrayList deliveryIntents; + + HashMap map = tracker.mData; + + String destinationAddress = (String) map.get("destination"); + String scAddress = (String) map.get("scaddress"); + + parts = (ArrayList) map.get("parts"); + sentIntents = (ArrayList) map.get("sentIntents"); + deliveryIntents = (ArrayList) map.get("deliveryIntents"); + + sendMultipartTextWithPermit(destinationAddress, + scAddress, parts, sentIntents, deliveryIntents); + + } + + /** {@inheritDoc} */ + protected void acknowledgeLastIncomingSms(boolean success, Message response){ + // FIXME unit test leaves cm == null. this should change + if (mCm != null) { + mCm.acknowledgeLastIncomingSMS(success, response); + } + } + + /** {@inheritDoc} */ + protected void activateCellBroadcastSms(int activate, Message response) { + // Unless CBS is implemented for GSM, this point should be unreachable. + Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); + response.recycle(); + } + + /** {@inheritDoc} */ + protected void getCellBroadcastSmsConfig(Message response){ + // Unless CBS is implemented for GSM, this point should be unreachable. + Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); + response.recycle(); + } + + /** {@inheritDoc} */ + protected void setCellBroadcastConfig(int[] configValuesArray, Message response) { + // Unless CBS is implemented for GSM, this point should be unreachable. + Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM."); + response.recycle(); + } + +} + diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java new file mode 100644 index 0000000..3c0b603 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java @@ -0,0 +1,1588 @@ +/* + * 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.gsm; + +import com.android.internal.telephony.Phone; +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.os.Registrant; +import android.os.RegistrantList; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.provider.Checkin; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.provider.Telephony.Intents; +import android.telephony.ServiceState; +import android.telephony.gsm.GsmCellLocation; +import android.text.TextUtils; +import android.util.Config; +import android.util.EventLog; +import android.util.Log; +import android.util.TimeUtils; + +import com.android.internal.telephony.CommandException; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.DataConnectionTracker; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.ServiceStateTracker; +import com.android.internal.telephony.TelephonyEventLog; +import com.android.internal.telephony.TelephonyIntents; + +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * {@hide} + */ +final class GsmServiceStateTracker extends ServiceStateTracker { + //***** Instance Variables + GSMPhone phone; + GsmCellLocation cellLoc; + GsmCellLocation newCellLoc; + int mPreferredNetworkType; + RestrictedState rs; + + int rssi = 99; // signal strength 0-31, 99=unknown + // That's "received signal strength indication" fyi + + private int gprsState = ServiceState.STATE_OUT_OF_SERVICE; + private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE; + + /** + * The access technology currently in use: DATA_ACCESS_ + */ + private int networkType = 0; + private int newNetworkType = 0; + /* gsm roaming status solely based on TS 27.007 7.2 CREG */ + private boolean mGsmRoaming = false; + + private RegistrantList gprsAttachedRegistrants = new RegistrantList(); + private RegistrantList gprsDetachedRegistrants = new RegistrantList(); + private RegistrantList psRestrictEnabledRegistrants = new RegistrantList(); + private RegistrantList psRestrictDisabledRegistrants = new RegistrantList(); + + // Sometimes we get the NITZ time before we know what country we are in. + // Keep the time zone information from the NITZ string so we can fix + // the time zone once know the country. + private boolean mNeedFixZone = false; + private int mZoneOffset; + private boolean mZoneDst; + private long mZoneTime; + private boolean mGotCountryCode = false; + private ContentResolver cr; + + String mSavedTimeZone; + long mSavedTime; + long mSavedAtTime; + + // We can't register for SIM_RECORDS_LOADED immediately because the + // SIMRecords object may not be instantiated yet. + private boolean mNeedToRegForSimLoaded; + + // Started the recheck process after finding gprs should registerd but not + private boolean mStartedGprsRegCheck = false; + // Already sent the event-log for no gprs register + private boolean mReportedGprsNoReg = false; + + /** + * The Notification object given to the NotificationManager. + */ + private Notification mNotification; + + // Wake lock used while setting time of day. + private PowerManager.WakeLock mWakeLock; + private static final String WAKELOCK_TAG = "ServiceStateTracker"; + + // Keep track of SPN display rules, so we only broadcast intent if something changes. + private String curSpn = null; + private String curPlmn = null; + private int curSpnRule = 0; + + //***** Constants + + static final boolean DBG = true; + static final String LOG_TAG = "GSM"; + + // waiting period before recheck gprs and voice registration + static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; + + // notification type + static final int PS_ENABLED = 1001; // Access Control blocks data service + static final int PS_DISABLED = 1002; // Access Control enables data service + static final int CS_ENABLED = 1003; // Access Control blocks all voice/sms service + static final int CS_DISABLED = 1004; // Access Control enables all voice/sms service + static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service + static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service + + // notification id + static final int PS_NOTIFICATION = 888; //id to update and cancel PS restricted + static final int CS_NOTIFICATION = 999; //id to update and cancel CS restricted + + private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + Log.i("GsmServiceStateTracker", "Auto time state changed"); + revertToNitz(); + } + }; + + + //***** Constructors + + public GsmServiceStateTracker(GSMPhone phone) { + super(); + + this.phone = phone; + cm = phone.mCM; + ss = new ServiceState(); + newSS = new ServiceState(); + cellLoc = new GsmCellLocation(); + newCellLoc = new GsmCellLocation(); + rs = new RestrictedState(); + + PowerManager powerManager = + (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); + + cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); + cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); + + cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null); + cm.setOnNITZTime(this, EVENT_NITZ_TIME, null); + cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); + cm.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null); + cm.registerForSIMReady(this, EVENT_SIM_READY, null); + + // system setting property AIRPLANE_MODE_ON is set in Settings. + int airplaneMode = Settings.System.getInt( + phone.getContext().getContentResolver(), + Settings.System.AIRPLANE_MODE_ON, 0); + mDesiredPowerState = ! (airplaneMode > 0); + + cr = phone.getContext().getContentResolver(); + cr.registerContentObserver( + Settings.System.getUriFor(Settings.System.AUTO_TIME), true, + mAutoTimeObserver); + setRssiDefaultValues(); + mNeedToRegForSimLoaded = true; + } + + public void dispose() { + //Unregister for all events + cm.unregisterForAvailable(this); + cm.unregisterForRadioStateChanged(this); + cm.unregisterForNetworkStateChanged(this); + cm.unregisterForSIMReady(this); + + phone.mSIMRecords.unregisterForRecordsLoaded(this); + cm.unSetOnSignalStrengthUpdate(this); + cm.unSetOnRestrictedStateChanged(this); + cm.unSetOnNITZTime(this); + cr.unregisterContentObserver(this.mAutoTimeObserver); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "GsmServiceStateTracker finalized"); + } + + /** + * Registration point for transition into GPRS attached. + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + /*protected*/ void registerForGprsAttached(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + gprsAttachedRegistrants.add(r); + + if (gprsState == ServiceState.STATE_IN_SERVICE) { + r.notifyRegistrant(); + } + } + + /*protected*/ void unregisterForGprsAttached(Handler h) { + gprsAttachedRegistrants.remove(h); + } + + /*protected*/ void registerForNetworkAttach(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + networkAttachedRegistrants.add(r); + + if (ss.getState() == ServiceState.STATE_IN_SERVICE) { + r.notifyRegistrant(); + } + } + + /*protected*/ void unregisterForNetworkAttach(Handler h) { + networkAttachedRegistrants.remove(h); + } + /** + * Registration point for transition into GPRS detached. + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + /*protected*/ void registerForGprsDetached(Handler h, int what, Object obj) { + Registrant r = new Registrant(h, what, obj); + gprsDetachedRegistrants.add(r); + + if (gprsState == ServiceState.STATE_OUT_OF_SERVICE) { + r.notifyRegistrant(); + } + } + + /*protected*/ void unregisterForGprsDetached(Handler h) { + gprsDetachedRegistrants.remove(h); + } + + /** + * Registration point for transition into packet service restricted zone. + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + /*protected*/ void registerForPsRestrictedEnabled(Handler h, int what, Object obj) { + Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedEnabled "); + Registrant r = new Registrant(h, what, obj); + psRestrictEnabledRegistrants.add(r); + + if (rs.isPsRestricted()) { + r.notifyRegistrant(); + } + } + + /*protected*/ void unregisterForPsRestrictedEnabled(Handler h) { + psRestrictEnabledRegistrants.remove(h); + } + + /** + * Registration point for transition out of packet service restricted zone. + * @param h handler to notify + * @param what what code of message when delivered + * @param obj placed in Message.obj + */ + /*protected*/ void registerForPsRestrictedDisabled(Handler h, int what, Object obj) { + Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedDisabled "); + Registrant r = new Registrant(h, what, obj); + psRestrictDisabledRegistrants.add(r); + + if (rs.isPsRestricted()) { + r.notifyRegistrant(); + } + } + + /*protected*/ void unregisterForPsRestrictedDisabled(Handler h) { + psRestrictDisabledRegistrants.remove(h); + } + + //***** Called from GSMPhone + public void + getLacAndCid(Message onComplete) { + cm.getRegistrationState(obtainMessage( + EVENT_GET_LOC_DONE, onComplete)); + } + + + //***** Overridden from ServiceStateTracker + public void + handleMessage (Message msg) { + AsyncResult ar; + int[] ints; + String[] strings; + Message message; + + switch (msg.what) { + case EVENT_RADIO_AVAILABLE: + //this is unnecessary + //setPowerStateToDesired(); + break; + + case EVENT_SIM_READY: + // The SIM is now ready i.e if it was locked + // it has been unlocked. At this stage, the radio is already + // powered on. + if (mNeedToRegForSimLoaded) { + phone.mSIMRecords.registerForRecordsLoaded(this, + EVENT_SIM_RECORDS_LOADED, null); + mNeedToRegForSimLoaded = false; + } + // restore the previous network selection. + phone.restoreSavedNetworkSelection(null); + pollState(); + // Signal strength polling stops when radio is off + queueNextSignalStrengthPoll(); + break; + + case EVENT_RADIO_STATE_CHANGED: + // This will do nothing in the radio not + // available case + setPowerStateToDesired(); + pollState(); + break; + + case EVENT_NETWORK_STATE_CHANGED: + pollState(); + break; + + case EVENT_GET_SIGNAL_STRENGTH: + // This callback is called when signal strength is polled + // all by itself + + if (!(cm.getRadioState().isOn()) || (cm.getRadioState().isCdma())) { + // Polling will continue when radio turns back on and not CDMA + return; + } + ar = (AsyncResult) msg.obj; + onSignalStrengthResult(ar); + queueNextSignalStrengthPoll(); + + break; + + case EVENT_GET_LOC_DONE: + ar = (AsyncResult) msg.obj; + + if (ar.exception == null) { + String states[] = (String[])ar.result; + int lac = -1; + int cid = -1; + if (states.length == 3) { + try { + if (states[1] != null && states[1].length() > 0) { + lac = Integer.parseInt(states[1], 16); + } + if (states[2] != null && states[2].length() > 0) { + cid = Integer.parseInt(states[2], 16); + } + } catch (NumberFormatException ex) { + Log.w(LOG_TAG, "error parsing location: " + ex); + } + } + + // only update if lac or cid changed + if (cellLoc.getCid() != cid || cellLoc.getLac() != lac) { + cellLoc.setLacAndCid(lac, cid); + phone.notifyLocationChanged(); + } + } + + if (ar.userObj != null) { + AsyncResult.forMessage(((Message) ar.userObj)).exception + = ar.exception; + ((Message) ar.userObj).sendToTarget(); + } + break; + + case EVENT_POLL_STATE_REGISTRATION: + case EVENT_POLL_STATE_GPRS: + case EVENT_POLL_STATE_OPERATOR: + case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: + ar = (AsyncResult) msg.obj; + + handlePollStateResult(msg.what, ar); + break; + + case EVENT_POLL_SIGNAL_STRENGTH: + // Just poll signal strength...not part of pollState() + + cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH)); + break; + + case EVENT_NITZ_TIME: + ar = (AsyncResult) msg.obj; + + String nitzString = (String)((Object[])ar.result)[0]; + long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); + + setTimeFromNITZString(nitzString, nitzReceiveTime); + break; + + case EVENT_SIGNAL_STRENGTH_UPDATE: + // This is a notification from + // CommandsInterface.setOnSignalStrengthUpdate + + ar = (AsyncResult) msg.obj; + + // The radio is telling us about signal strength changes + // we don't have to ask it + dontPollSignalStrength = true; + + onSignalStrengthResult(ar); + break; + + case EVENT_SIM_RECORDS_LOADED: + updateSpnDisplay(); + break; + + case EVENT_LOCATION_UPDATES_ENABLED: + ar = (AsyncResult) msg.obj; + + if (ar.exception == null) { + getLacAndCid(null); + } + break; + + case EVENT_SET_PREFERRED_NETWORK_TYPE: + ar = (AsyncResult) msg.obj; + // Don't care the result, only use for dereg network (COPS=2) + message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj); + cm.setPreferredNetworkType(mPreferredNetworkType, message); + break; + + case EVENT_RESET_PREFERRED_NETWORK_TYPE: + ar = (AsyncResult) msg.obj; + if (ar.userObj != null) { + AsyncResult.forMessage(((Message) ar.userObj)).exception + = ar.exception; + ((Message) ar.userObj).sendToTarget(); + } + break; + + case EVENT_GET_PREFERRED_NETWORK_TYPE: + ar = (AsyncResult) msg.obj; + + if (ar.exception == null) { + mPreferredNetworkType = ((int[])ar.result)[0]; + } else { + mPreferredNetworkType = RILConstants.NETWORK_MODE_GLOBAL; + } + + message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj); + int toggledNetworkType = RILConstants.NETWORK_MODE_GLOBAL; + + cm.setPreferredNetworkType(toggledNetworkType, message); + break; + + case EVENT_CHECK_REPORT_GPRS: + if (ss != null && !isGprsConsistant(gprsState, ss.getState())) { + + // Can't register data sevice while voice service is ok + // i.e. CREG is ok while CGREG is not + // possible a network or baseband side error + int cid = -1; + GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); + if (loc != null) cid = loc.getCid(); + + EventLog.List val = new EventLog.List(ss.getOperatorNumeric(), cid); + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CGREG_FAIL, val); + mReportedGprsNoReg = true; + } + mStartedGprsRegCheck = false; + break; + + case EVENT_RESTRICTED_STATE_CHANGED: + // This is a notification from + // CommandsInterface.setOnRestrictedStateChanged + + Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_RESTRICTED_STATE_CHANGED"); + + ar = (AsyncResult) msg.obj; + + onRestrictedStateChanged(ar); + break; + + default: + Log.e(LOG_TAG, "Unhandled message with number: " + msg.what); + break; + } + } + + //***** Private Instance Methods + + protected void setPowerStateToDesired() + { + // If we want it on and it's off, turn it on + if (mDesiredPowerState + && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) { + cm.setRadioPower(true, null); + } else if (!mDesiredPowerState && cm.getRadioState().isOn()) { + DataConnectionTracker dcTracker = phone.mDataConnection; + if (! dcTracker.isDataConnectionAsDesired()) { + + EventLog.List val = new EventLog.List( + dcTracker.getStateInString(), + (dcTracker.getAnyDataEnabled() ? 1 : 0) ); + EventLog.writeEvent(TelephonyEventLog.EVENT_DATA_STATE_RADIO_OFF, val); + } + dcTracker.cleanConnectionBeforeRadioOff(); + + // poll data state up to 15 times, with a 100ms delay + // totaling 1.5 sec. Normal data disable action will finish in 100ms. + for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) { + if (dcTracker.getState() != DataConnectionTracker.State.CONNECTED + && dcTracker.getState() != DataConnectionTracker.State.DISCONNECTING) { + Log.d(LOG_TAG, "Data shutdown complete."); + break; + } + SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS); + } + // If it's on and available and we want it off.. + cm.setRadioPower(false, null); + } // Otherwise, we're in the desired state + } + + protected void updateSpnDisplay() { + int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric()); + String spn = phone.mSIMRecords.getServiceProviderName(); + String plmn = ss.getOperatorAlphaLong(); + + if (rule != curSpnRule + || !TextUtils.equals(spn, curSpn) + || !TextUtils.equals(plmn, curPlmn)) { + boolean showSpn = + (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN; + boolean showPlmn = + (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN; + Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION); + intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn); + intent.putExtra(Intents.EXTRA_SPN, spn); + intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn); + intent.putExtra(Intents.EXTRA_PLMN, plmn); + phone.getContext().sendStickyBroadcast(intent); + } + curSpnRule = rule; + curSpn = spn; + curPlmn = plmn; + } + + /** + * Handle the result of one of the pollState()-related requests + */ + + protected void + handlePollStateResult (int what, AsyncResult ar) { + int ints[]; + String states[]; + + // Ignore stale requests from last poll + if (ar.userObj != pollingContext) return; + + if (ar.exception != null) { + CommandException.Error err=null; + + if (ar.exception instanceof CommandException) { + err = ((CommandException)(ar.exception)).getCommandError(); + } + + if (err == CommandException.Error.RADIO_NOT_AVAILABLE) { + // Radio has crashed or turned off + cancelPollState(); + return; + } + + if (!cm.getRadioState().isOn()) { + // Radio has crashed or turned off + cancelPollState(); + return; + } + + if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW && + err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) { + Log.e(LOG_TAG, + "RIL implementation has returned an error where it must succeed" + + ar.exception); + } + } else try { + switch (what) { + case EVENT_POLL_STATE_REGISTRATION: + states = (String[])ar.result; + int lac = -1; + int cid = -1; + int regState = -1; + if (states.length > 0) { + try { + regState = Integer.parseInt(states[0]); + if (states.length == 3) { + if (states[1] != null && states[1].length() > 0) { + lac = Integer.parseInt(states[1], 16); + } + if (states[2] != null && states[2].length() > 0) { + cid = Integer.parseInt(states[2], 16); + } + } + } catch (NumberFormatException ex) { + Log.w(LOG_TAG, "error parsing RegistrationState: " + ex); + } + } + + mGsmRoaming = regCodeIsRoaming(regState); + newSS.setState (regCodeToServiceState(regState)); + + // LAC and CID are -1 if not avail + newCellLoc.setLacAndCid(lac, cid); + break; + + case EVENT_POLL_STATE_GPRS: + states = (String[])ar.result; + + int type = 0; + regState = -1; + if (states.length > 0) { + try { + regState = Integer.parseInt(states[0]); + + // states[3] (if present) is the current radio technology + if (states.length >= 4 && states[3] != null) { + type = Integer.parseInt(states[3]); + } + } catch (NumberFormatException ex) { + Log.w(LOG_TAG, "error parsing GprsRegistrationState: " + ex); + } + } + newGPRSState = regCodeToServiceState(regState); + newNetworkType = type; + break; + + case EVENT_POLL_STATE_OPERATOR: + String opNames[] = (String[])ar.result; + + if (opNames != null && opNames.length >= 3) { + newSS.setOperatorName ( + opNames[0], opNames[1], opNames[2]); + } + break; + + case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: + ints = (int[])ar.result; + newSS.setIsManualSelection(ints[0] == 1); + break; + } + + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "Exception while polling service state. " + + "Probably malformed RIL response.", ex); + } + + pollingContext[0]--; + + if (pollingContext[0] == 0) { + newSS.setRoaming(isRoamingBetweenOperators(mGsmRoaming, newSS)); + pollStateDone(); + } + + } + + private void + setRssiDefaultValues() { + rssi = 99; + } + + /** + * A complete "service state" from our perspective is + * composed of a handful of separate requests to the radio. + * + * We make all of these requests at once, but then abandon them + * and start over again if the radio notifies us that some + * event has changed + */ + + private void + pollState() { + pollingContext = new int[1]; + pollingContext[0] = 0; + + switch (cm.getRadioState()) { + case RADIO_UNAVAILABLE: + newSS.setStateOutOfService(); + newCellLoc.setStateInvalid(); + setRssiDefaultValues(); + mGotCountryCode = false; + + pollStateDone(); + break; + + case RADIO_OFF: + newSS.setStateOff(); + newCellLoc.setStateInvalid(); + setRssiDefaultValues(); + mGotCountryCode = false; + + pollStateDone(); + break; + + case RUIM_NOT_READY: + case RUIM_READY: + case RUIM_LOCKED_OR_ABSENT: + case NV_NOT_READY: + case NV_READY: + Log.d(LOG_TAG, "Radio Technology Change ongoing, setting SS to off"); + newSS.setStateOff(); + newCellLoc.setStateInvalid(); + setRssiDefaultValues(); + mGotCountryCode = false; + + pollStateDone(); + break; + + default: + // Issue all poll-related commands at once + // then count down the responses, which + // are allowed to arrive out-of-order + + pollingContext[0]++; + cm.getOperator( + obtainMessage( + EVENT_POLL_STATE_OPERATOR, pollingContext)); + + pollingContext[0]++; + cm.getGPRSRegistrationState( + obtainMessage( + EVENT_POLL_STATE_GPRS, pollingContext)); + + pollingContext[0]++; + cm.getRegistrationState( + obtainMessage( + EVENT_POLL_STATE_REGISTRATION, pollingContext)); + + pollingContext[0]++; + cm.getNetworkSelectionMode( + obtainMessage( + EVENT_POLL_STATE_NETWORK_SELECTION_MODE, pollingContext)); + break; + } + } + + private static String networkTypeToString(int type) { + //Network Type from GPRS_REGISTRATION_STATE + String ret = "unknown"; + + switch (type) { + case DATA_ACCESS_GPRS: + ret = "GPRS"; + break; + case DATA_ACCESS_EDGE: + ret = "EDGE"; + break; + case DATA_ACCESS_UMTS: + ret = "UMTS"; + break; + default: + Log.e(LOG_TAG, "Wrong network type: " + Integer.toString(type)); + break; + } + + return ret; + } + + private void + pollStateDone() { + if (DBG) { + Log.d(LOG_TAG, "Poll ServiceState done: " + + " oldSS=[" + ss + "] newSS=[" + newSS + + "] oldGprs=" + gprsState + " newGprs=" + newGPRSState + + " oldType=" + networkTypeToString(networkType) + + " newType=" + networkTypeToString(newNetworkType)); + } + + boolean hasRegistered = + ss.getState() != ServiceState.STATE_IN_SERVICE + && newSS.getState() == ServiceState.STATE_IN_SERVICE; + + boolean hasDeregistered = + ss.getState() == ServiceState.STATE_IN_SERVICE + && newSS.getState() != ServiceState.STATE_IN_SERVICE; + + boolean hasGprsAttached = + gprsState != ServiceState.STATE_IN_SERVICE + && newGPRSState == ServiceState.STATE_IN_SERVICE; + + boolean hasGprsDetached = + gprsState == ServiceState.STATE_IN_SERVICE + && newGPRSState != ServiceState.STATE_IN_SERVICE; + + boolean hasNetworkTypeChanged = networkType != newNetworkType; + + boolean hasChanged = !newSS.equals(ss); + + boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); + + boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); + + boolean hasLocationChanged = !newCellLoc.equals(cellLoc); + + ServiceState tss; + tss = ss; + ss = newSS; + newSS = tss; + // clean slate for next time + newSS.setStateOutOfService(); + + GsmCellLocation tcl = cellLoc; + cellLoc = newCellLoc; + newCellLoc = tcl; + + gprsState = newGPRSState; + networkType = newNetworkType; + + newSS.setStateOutOfService(); // clean slate for next time + + if (hasNetworkTypeChanged) { + phone.setSystemProperty(PROPERTY_DATA_NETWORK_TYPE, + networkTypeToString(networkType)); + } + + if (hasRegistered) { + Checkin.updateStats(phone.getContext().getContentResolver(), + Checkin.Stats.Tag.PHONE_GSM_REGISTERED, 1, 0.0); + networkAttachedRegistrants.notifyRegistrants(); + } + + if (hasChanged) { + String operatorNumeric; + + phone.setSystemProperty(PROPERTY_OPERATOR_ALPHA, + ss.getOperatorAlphaLong()); + + operatorNumeric = ss.getOperatorNumeric(); + phone.setSystemProperty(PROPERTY_OPERATOR_NUMERIC, operatorNumeric); + + if (operatorNumeric == null) { + phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, ""); + } else { + String iso = ""; + try{ + iso = MccTable.countryCodeForMcc(Integer.parseInt( + operatorNumeric.substring(0,3))); + } catch ( NumberFormatException ex){ + Log.w(LOG_TAG, "countryCodeForMcc error" + ex); + } catch ( StringIndexOutOfBoundsException ex) { + Log.w(LOG_TAG, "countryCodeForMcc error" + ex); + } + + phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, iso); + mGotCountryCode = true; + + if (mNeedFixZone) { + TimeZone zone = null; + // If the offset is (0, false) and the timezone property + // is set, use the timezone property rather than + // GMT. + String zoneName = SystemProperties.get(TIMEZONE_PROPERTY); + if ((mZoneOffset == 0) && (mZoneDst == false) && + (zoneName != null) && (zoneName.length() > 0) && + (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) { + zone = TimeZone.getDefault(); + // For NITZ string without timezone, + // need adjust time to reflect default timezone setting + long tzOffset; + tzOffset = zone.getOffset(System.currentTimeMillis()); + if (getAutoTime()) { + setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); + } else { + // Adjust the saved NITZ time to account for tzOffset. + mSavedTime = mSavedTime - tzOffset; + } + } else if (iso.equals("")){ + // Country code not found. This is likely a test network. + // Get a TimeZone based only on the NITZ parameters (best guess). + zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime); + } else { + zone = TimeUtils.getTimeZone(mZoneOffset, + mZoneDst, mZoneTime, iso); + } + + mNeedFixZone = false; + + if (zone != null) { + if (getAutoTime()) { + setAndBroadcastNetworkSetTimeZone(zone.getID()); + } + saveNitzTimeZone(zone.getID()); + } + } + } + + phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING, + ss.getRoaming() ? "true" : "false"); + + updateSpnDisplay(); + phone.notifyServiceStateChanged(ss); + } + + if (hasGprsAttached) { + gprsAttachedRegistrants.notifyRegistrants(); + } + + if (hasGprsDetached) { + gprsDetachedRegistrants.notifyRegistrants(); + } + + if (hasNetworkTypeChanged) { + phone.notifyDataConnection(null); + } + + if (hasRoamingOn) { + roamingOnRegistrants.notifyRegistrants(); + } + + if (hasRoamingOff) { + roamingOffRegistrants.notifyRegistrants(); + } + + if (hasLocationChanged) { + phone.notifyLocationChanged(); + } + + if (! isGprsConsistant(gprsState, ss.getState())) { + if (!mStartedGprsRegCheck && !mReportedGprsNoReg) { + mStartedGprsRegCheck = true; + + int check_period = Settings.Gservices.getInt( + phone.getContext().getContentResolver(), + Settings.Gservices.GPRS_REGISTER_CHECK_PERIOD_MS, + DEFAULT_GPRS_CHECK_PERIOD_MILLIS); + sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS), + check_period); + } + } else { + mReportedGprsNoReg = false; + } + } + + /** + * Check if GPRS got registred while voice is registered + * + * @param gprsState for GPRS registration state, i.e. CGREG in GSM + * @param serviceState for voice registration state, i.e. CREG in GSM + * @return false if device only register to voice but not gprs + */ + private boolean isGprsConsistant (int gprsState, int serviceState) { + return !((serviceState == ServiceState.STATE_IN_SERVICE) && + (gprsState != ServiceState.STATE_IN_SERVICE)); + } + + /** + * Returns a TimeZone object based only on parameters from the NITZ string. + */ + private TimeZone getNitzTimeZone(int offset, boolean dst, long when) { + TimeZone guess = findTimeZone(offset, dst, when); + if (guess == null) { + // Couldn't find a proper timezone. Perhaps the DST data is wrong. + guess = findTimeZone(offset, !dst, when); + } + if (DBG) { + Log.d(LOG_TAG, "getNitzTimeZone returning " + + (guess == null ? guess : guess.getID())); + } + return guess; + } + + private TimeZone findTimeZone(int offset, boolean dst, long when) { + int rawOffset = offset; + if (dst) { + rawOffset -= 3600000; + } + String[] zones = TimeZone.getAvailableIDs(rawOffset); + TimeZone guess = null; + Date d = new Date(when); + for (String zone : zones) { + TimeZone tz = TimeZone.getTimeZone(zone); + if (tz.getOffset(when) == offset && + tz.inDaylightTime(d) == dst) { + guess = tz; + break; + } + } + + return guess; + } + + private void + queueNextSignalStrengthPoll() { + if (dontPollSignalStrength || (cm.getRadioState().isCdma())) { + // The radio is telling us about signal strength changes + // we don't have to ask it + return; + } + + Message msg; + + msg = obtainMessage(); + msg.what = EVENT_POLL_SIGNAL_STRENGTH; + + long nextTime; + + // TODO Done't poll signal strength if screen is off + sendMessageDelayed(msg, POLL_PERIOD_MILLIS); + } + + /** + * send signal-strength-changed notification if rssi changed + * Called both for solicited and unsolicited signal stength updates + */ + private void + onSignalStrengthResult(AsyncResult ar) { + int oldRSSI = rssi; + + if (ar.exception != null) { + // 99 = unknown + // most likely radio is resetting/disconnected + rssi = 99; + } else { + int[] ints = (int[])ar.result; + + // bug 658816 seems to be a case where the result is 0-length + if (ints.length != 0) { + rssi = ints[0]; + } else { + Log.e(LOG_TAG, "Bogus signal strength response"); + rssi = 99; + } + } + + if (rssi != oldRSSI) { + try { // This takes care of delayed EVENT_POLL_SIGNAL_STRENGTH (scheduled after + // POLL_PERIOD_MILLIS) during Radio Technology Change) + phone.notifySignalStrength(); + } catch (NullPointerException ex) { + Log.d(LOG_TAG, "onSignalStrengthResult() Phone already destroyed: " + ex + + "Signal Stranth not notified"); + } + } + } + + /** + * Set restricted state based on the OnRestrictedStateChanged notification + * If any voice or packet restricted state changes, trigger a UI + * notification and notify registrants when sim is ready. + * + * @param ar an int value of RIL_RESTRICTED_STATE_* + */ + private void onRestrictedStateChanged(AsyncResult ar) + { + Log.d(LOG_TAG, "[DSAC DEB] " + "onRestrictedStateChanged"); + RestrictedState newRs = new RestrictedState(); + + Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at enter "+ rs); + + if (ar.exception == null) { + int[] ints = (int[])ar.result; + int state = ints[0]; + + newRs.setCsEmergencyRestricted( + ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) || + ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); + //ignore the normal call and data restricted state before SIM READY + if (phone.getIccCard().getState() == IccCard.State.READY) { + newRs.setCsNormalRestricted( + ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) || + ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); + newRs.setPsRestricted( + (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0); + } + + Log.d(LOG_TAG, "[DSAC DEB] " + "new rs "+ newRs); + + if (!rs.isPsRestricted() && newRs.isPsRestricted()) { + psRestrictEnabledRegistrants.notifyRegistrants(); + setNotification(PS_ENABLED); + } else if (rs.isPsRestricted() && !newRs.isPsRestricted()) { + psRestrictDisabledRegistrants.notifyRegistrants(); + setNotification(PS_DISABLED); + } + + /** + * There are two kind of cs restriction, normal and emergency. So + * there are 4 x 4 combinations in current and new restricted states + * and we only need to notify when state is changed. + */ + if (rs.isCsRestricted()) { + if (!newRs.isCsRestricted()) { + // remove all restriction + setNotification(CS_DISABLED); + } else if (!newRs.isCsNormalRestricted()) { + // remove normal restriction + setNotification(CS_EMERGENCY_ENABLED); + } else if (!newRs.isCsEmergencyRestricted()) { + // remove emergency restriction + setNotification(CS_NORMAL_ENABLED); + } + } else if (rs.isCsEmergencyRestricted() && !rs.isCsNormalRestricted()) { + if (!newRs.isCsRestricted()) { + // remove all restriction + setNotification(CS_DISABLED); + } else if (newRs.isCsRestricted()) { + // enable all restriction + setNotification(CS_ENABLED); + } else if (newRs.isCsNormalRestricted()) { + // remove emergency restriction and enable normal restriction + setNotification(CS_NORMAL_ENABLED); + } + } else if (!rs.isCsEmergencyRestricted() && rs.isCsNormalRestricted()) { + if (!newRs.isCsRestricted()) { + // remove all restriction + setNotification(CS_DISABLED); + } else if (newRs.isCsRestricted()) { + // enable all restriction + setNotification(CS_ENABLED); + } else if (newRs.isCsEmergencyRestricted()) { + // remove normal restriction and enable emergency restriction + setNotification(CS_EMERGENCY_ENABLED); + } + } else { + if (newRs.isCsRestricted()) { + // enable all restriction + setNotification(CS_ENABLED); + } else if (newRs.isCsEmergencyRestricted()) { + // enable emergency restriction + setNotification(CS_EMERGENCY_ENABLED); + } else if (newRs.isCsNormalRestricted()) { + // enable normal restriction + setNotification(CS_NORMAL_ENABLED); + } + } + + rs = newRs; + } + Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at return "+ rs); + } + + /** code is registration state 0-5 from TS 27.007 7.2 */ + private int + regCodeToServiceState(int code) { + switch (code) { + case 0: + case 2: // 2 is "searching" + case 3: // 3 is "registration denied" + case 4: // 4 is "unknown" no vaild in current baseband + return ServiceState.STATE_OUT_OF_SERVICE; + + case 1: + return ServiceState.STATE_IN_SERVICE; + + case 5: + // in service, roam + return ServiceState.STATE_IN_SERVICE; + + default: + Log.w(LOG_TAG, "unexpected service state " + code); + return ServiceState.STATE_OUT_OF_SERVICE; + } + } + + + /** + * code is registration state 0-5 from TS 27.007 7.2 + * returns true if registered roam, false otherwise + */ + private boolean + regCodeIsRoaming (int code) { + // 5 is "in service -- roam" + return 5 == code; + } + + /** + * Set roaming state when gsmRoaming is true and, if operator mcc is the + * same as sim mcc, ons is different from spn + * @param gsmRoaming TS 27.007 7.2 CREG registered roaming + * @param s ServiceState hold current ons + * @return true for roaming state set + */ + private + boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) { + String spn = SystemProperties.get(PROPERTY_ICC_OPERATOR_ALPHA, "empty"); + + String onsl = s.getOperatorAlphaLong(); + String onss = s.getOperatorAlphaShort(); + + boolean equalsOnsl = onsl != null && spn.equals(onsl); + boolean equalsOnss = onss != null && spn.equals(onss); + + String simNumeric = SystemProperties.get(PROPERTY_ICC_OPERATOR_NUMERIC, ""); + String operatorNumeric = s.getOperatorNumeric(); + + boolean equalsMcc = true; + try { + equalsMcc = simNumeric.substring(0, 3). + equals(operatorNumeric.substring(0, 3)); + } catch (Exception e){ + } + + return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss)); + } + + private static + int twoDigitsAt(String s, int offset) { + int a, b; + + a = Character.digit(s.charAt(offset), 10); + b = Character.digit(s.charAt(offset+1), 10); + + if (a < 0 || b < 0) { + + throw new RuntimeException("invalid format"); + } + + return a*10 + b; + } + + /** + * @return The current GPRS state. IN_SERVICE is the same as "attached" + * and OUT_OF_SERVICE is the same as detached. + */ + /*package*/ int getCurrentGprsState() { + return gprsState; + } + + /** + * @return true if phone is camping on a technology (eg UMTS) + * that could support voice and data simultaniously. + */ + boolean isConcurrentVoiceAndData() { + return (networkType == DATA_ACCESS_UMTS); + } + + /** + * Provides the name of the algorithmic time zone for the specified + * offset. Taken from TimeZone.java. + */ + private static String displayNameFor(int off) { + off = off / 1000 / 60; + + char[] buf = new char[9]; + buf[0] = 'G'; + buf[1] = 'M'; + buf[2] = 'T'; + + if (off < 0) { + buf[3] = '-'; + off = -off; + } else { + buf[3] = '+'; + } + + int hours = off / 60; + int minutes = off % 60; + + buf[4] = (char) ('0' + hours / 10); + buf[5] = (char) ('0' + hours % 10); + + buf[6] = ':'; + + buf[7] = (char) ('0' + minutes / 10); + buf[8] = (char) ('0' + minutes % 10); + + return new String(buf); + } + + /** + * nitzReceiveTime is time_t that the NITZ time was posted + */ + + private + void setTimeFromNITZString (String nitz, long nitzReceiveTime) + { + // "yy/mm/dd,hh:mm:ss(+/-)tz" + // tz is in number of quarter-hours + + long start = SystemClock.elapsedRealtime(); + Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime + + " start=" + start + " delay=" + (start - nitzReceiveTime)); + + try { + /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone + * offset as well (which we won't worry about until later) */ + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + c.clear(); + c.set(Calendar.DST_OFFSET, 0); + + String[] nitzSubs = nitz.split("[/:,+-]"); + + int year = 2000 + Integer.parseInt(nitzSubs[0]); + c.set(Calendar.YEAR, year); + + // month is 0 based! + int month = Integer.parseInt(nitzSubs[1]) - 1; + c.set(Calendar.MONTH, month); + + int date = Integer.parseInt(nitzSubs[2]); + c.set(Calendar.DATE, date); + + int hour = Integer.parseInt(nitzSubs[3]); + c.set(Calendar.HOUR, hour); + + int minute = Integer.parseInt(nitzSubs[4]); + c.set(Calendar.MINUTE, minute); + + int second = Integer.parseInt(nitzSubs[5]); + c.set(Calendar.SECOND, second); + + boolean sign = (nitz.indexOf('-') == -1); + + int tzOffset = Integer.parseInt(nitzSubs[6]); + + int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) + : 0; + + // The zone offset received from NITZ is for current local time, + // so DST correction is already applied. Don't add it again. + // + // tzOffset += dst * 4; + // + // We could unapply it if we wanted the raw offset. + + tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000; + + TimeZone zone = null; + + // As a special extension, the Android emulator appends the name of + // the host computer's timezone to the nitz string. this is zoneinfo + // timezone name of the form Area!Location or Area!Location!SubLocation + // so we need to convert the ! into / + if (nitzSubs.length >= 9) { + String tzname = nitzSubs[8].replace('!','/'); + zone = TimeZone.getTimeZone( tzname ); + } + + String iso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY); + + if (zone == null) { + + if (mGotCountryCode) { + if (iso != null && iso.length() > 0) { + zone = TimeUtils.getTimeZone(tzOffset, dst != 0, + c.getTimeInMillis(), + iso); + } else { + // We don't have a valid iso country code. This is + // most likely because we're on a test network that's + // using a bogus MCC (eg, "001"), so get a TimeZone + // based only on the NITZ parameters. + zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis()); + } + } + } + + if (zone == null) { + // We got the time before the country, so we don't know + // how to identify the DST rules yet. Save the information + // and hope to fix it up later. + + mNeedFixZone = true; + mZoneOffset = tzOffset; + mZoneDst = dst != 0; + mZoneTime = c.getTimeInMillis(); + } + + if (zone != null) { + if (getAutoTime()) { + setAndBroadcastNetworkSetTimeZone(zone.getID()); + } + saveNitzTimeZone(zone.getID()); + } + + String ignore = SystemProperties.get("gsm.ignore-nitz"); + if (ignore != null && ignore.equals("yes")) { + Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set"); + return; + } + + try { + mWakeLock.acquire(); + + if (getAutoTime()) { + long millisSinceNitzReceived + = SystemClock.elapsedRealtime() - nitzReceiveTime; + + if (millisSinceNitzReceived < 0) { + // Sanity check: something is wrong + Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " + + "backwards since NITZ time was received, " + + nitz); + return; + } + + if (millisSinceNitzReceived > Integer.MAX_VALUE) { + // If the time is this far off, something is wrong > 24 days! + Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " + + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) + + " days"); + return; + } + + // Note: with range checks above, cast to int is safe + c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); + + Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() + + " NITZ receive delay(ms): " + millisSinceNitzReceived + + " gained(ms): " + + (c.getTimeInMillis() - System.currentTimeMillis()) + + " from " + nitz); + + setAndBroadcastNetworkSetTime(c.getTimeInMillis()); + Log.i(LOG_TAG, "NITZ: after Setting time of day"); + } + SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); + saveNitzTime(c.getTimeInMillis()); + if (Config.LOGV) { + long end = SystemClock.elapsedRealtime(); + Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); + } + } finally { + mWakeLock.release(); + } + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex); + } + } + + private boolean getAutoTime() { + try { + return Settings.System.getInt(phone.getContext().getContentResolver(), + Settings.System.AUTO_TIME) > 0; + } catch (SettingNotFoundException snfe) { + return true; + } + } + + private void saveNitzTimeZone(String zoneId) { + mSavedTimeZone = zoneId; + } + + private void saveNitzTime(long time) { + mSavedTime = time; + mSavedAtTime = SystemClock.elapsedRealtime(); + } + + /** + * Set the timezone and send out a sticky broadcast so the system can + * determine if the timezone was set by the carrier. + * + * @param zoneId timezone set by carrier + */ + private void setAndBroadcastNetworkSetTimeZone(String zoneId) { + AlarmManager alarm = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + alarm.setTimeZone(zoneId); + Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); + intent.putExtra("time-zone", zoneId); + phone.getContext().sendStickyBroadcast(intent); + } + + /** + * Set the time and Send out a sticky broadcast so the system can determine + * if the time was set by the carrier. + * + * @param time time set by network + */ + private void setAndBroadcastNetworkSetTime(long time) { + SystemClock.setCurrentTimeMillis(time); + Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); + intent.putExtra("time", time); + phone.getContext().sendStickyBroadcast(intent); + } + + private void revertToNitz() { + if (Settings.System.getInt(phone.getContext().getContentResolver(), + Settings.System.AUTO_TIME, 0) == 0) { + return; + } + Log.d(LOG_TAG, "Reverting to NITZ: tz='" + mSavedTimeZone + + "' mSavedTime=" + mSavedTime + + " mSavedAtTime=" + mSavedAtTime); + if (mSavedTimeZone != null && mSavedTime != 0 && mSavedAtTime != 0) { + setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); + setAndBroadcastNetworkSetTime(mSavedTime + + (SystemClock.elapsedRealtime() - mSavedAtTime)); + } + } + + /** + * Post a notification to NotificationManager for restricted state + * + * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE + */ + private void setNotification(int notifyType) { + + Log.d(LOG_TAG, "[DSAC DEB] " + "create notification " + notifyType); + Context context = phone.getContext(); + + mNotification = new Notification(); + mNotification.when = System.currentTimeMillis(); + mNotification.flags = Notification.FLAG_AUTO_CANCEL; + mNotification.icon = com.android.internal.R.drawable.stat_sys_warning; + Intent intent = new Intent(); + mNotification.contentIntent = PendingIntent + .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + + CharSequence details = ""; + CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle); + int notificationId = CS_NOTIFICATION; + + switch (notifyType) { + case PS_ENABLED: + notificationId = PS_NOTIFICATION; + details = context.getText(com.android.internal.R.string.RestrictedOnData);; + break; + case PS_DISABLED: + notificationId = PS_NOTIFICATION; + break; + case CS_ENABLED: + details = context.getText(com.android.internal.R.string.RestrictedOnAll);; + break; + case CS_NORMAL_ENABLED: + details = context.getText(com.android.internal.R.string.RestrictedOnNormal);; + break; + case CS_EMERGENCY_ENABLED: + details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);; + break; + case CS_DISABLED: + // do nothing and cancel the notification later + break; + } + + Log.d(LOG_TAG, "[DSAC DEB] " + "put notification " + title + " / " +details); + mNotification.tickerText = title; + mNotification.setLatestEventInfo(context, title, details, + mNotification.contentIntent); + + NotificationManager notificationManager = (NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + + if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) { + // cancel previous post notification + notificationManager.cancel(notificationId); + } else { + // update restricted state notification + notificationManager.notify(notificationId, mNotification); + } + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSimCard.java b/telephony/java/com/android/internal/telephony/gsm/GsmSimCard.java deleted file mode 100644 index a4cded9..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSimCard.java +++ /dev/null @@ -1,506 +0,0 @@ -/* - * 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.gsm; - -import android.os.AsyncResult; -import android.os.RemoteException; -import android.os.Handler; -import android.os.Message; -import android.os.Registrant; -import android.os.RegistrantList; -import android.util.Log; -import com.android.internal.telephony.SimCard; -import com.android.internal.telephony.TelephonyProperties; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.TelephonyIntents; -import android.content.Intent; -import android.content.res.Configuration; -import android.app.ActivityManagerNative; - -import static android.Manifest.permission.READ_PHONE_STATE; - -/** - * {@hide} - */ -public final class GsmSimCard extends Handler implements SimCard { - static final String LOG_TAG="GSM"; - - //***** Instance Variables - private static final boolean DBG = true; - - private GSMPhone phone; - private CommandsInterface.SimStatus status = null; - private boolean mSimPinLocked = true; // Default to locked - private boolean mSimFdnEnabled = false; // Default to disabled. - // Will be updated when SIM_READY. - private boolean mDesiredPinLocked; - private boolean mDesiredFdnEnabled; - - //***** Constants - - // FIXME I hope this doesn't conflict with the Dialer's notifications - static final int NOTIFICATION_ID_SIM_STATUS = 33456; - - //***** Event Constants - - static final int EVENT_SIM_LOCKED_OR_ABSENT = 1; - static final int EVENT_GET_SIM_STATUS_DONE = 2; - static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3; - static final int EVENT_PINPUK_DONE = 4; - static final int EVENT_REPOLL_STATUS_DONE = 5; - static final int EVENT_SIM_READY = 6; - static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7; - static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8; - static final int EVENT_CHANGE_SIM_PASSWORD_DONE = 9; - static final int EVENT_QUERY_FACILITY_FDN_DONE = 10; - static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11; - - - //***** Constructor - - GsmSimCard(GSMPhone phone) - { - this.phone = phone; - - phone.mCM.registerForSIMLockedOrAbsent( - this, EVENT_SIM_LOCKED_OR_ABSENT, null); - - phone.mCM.registerForOffOrNotAvailable( - this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); - - phone.mCM.registerForSIMReady( - this, EVENT_SIM_READY, null); - - updateStateProperty(); - } - - //***** SimCard implementation - - public State - getState() - { - if (status == null) { - switch(phone.mCM.getRadioState()) { - /* This switch block must not return anything in - * State.isLocked() or State.ABSENT. - * If it does, handleSimStatus() may break - */ - case RADIO_OFF: - case RADIO_UNAVAILABLE: - case SIM_NOT_READY: - return State.UNKNOWN; - case SIM_LOCKED_OR_ABSENT: - //this should be transient-only - return State.UNKNOWN; - case SIM_READY: - return State.READY; - } - } else { - switch (status) { - case SIM_ABSENT: return State.ABSENT; - case SIM_NOT_READY: return State.UNKNOWN; - case SIM_READY: return State.READY; - case SIM_PIN: return State.PIN_REQUIRED; - case SIM_PUK: return State.PUK_REQUIRED; - case SIM_NETWORK_PERSONALIZATION: return State.NETWORK_LOCKED; - } - } - - Log.e(LOG_TAG, "GsmSimCard.getState(): case should never be reached"); - return State.UNKNOWN; - } - - private RegistrantList absentRegistrants = new RegistrantList(); - private RegistrantList pinLockedRegistrants = new RegistrantList(); - private RegistrantList networkLockedRegistrants = new RegistrantList(); - - - public void registerForAbsent(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - absentRegistrants.add(r); - - if (getState() == State.ABSENT) { - r.notifyRegistrant(); - } - } - - public void unregisterForAbsent(Handler h) { - absentRegistrants.remove(h); - } - - public void registerForNetworkLocked(Handler h, int what, Object obj) { - Registrant r = new Registrant (h, what, obj); - - networkLockedRegistrants.add(r); - - if (getState() == State.NETWORK_LOCKED) { - r.notifyRegistrant(); - } - } - - public void unregisterForNetworkLocked(Handler h) { - networkLockedRegistrants.remove(h); - } - - public void registerForLocked(Handler h, int what, Object obj) - { - Registrant r = new Registrant (h, what, obj); - - pinLockedRegistrants.add(r); - - if (getState().isPinLocked()) { - r.notifyRegistrant(); - } - } - - public void unregisterForLocked(Handler h) - { - pinLockedRegistrants.remove(h); - } - - - public void supplyPin (String pin, Message onComplete) - { - phone.mCM.supplySimPin(pin, - obtainMessage(EVENT_PINPUK_DONE, onComplete)); - } - - public void supplyPuk (String puk, String newPin, Message onComplete) - { - phone.mCM.supplySimPuk(puk, newPin, - obtainMessage(EVENT_PINPUK_DONE, onComplete)); - } - public void supplyPin2 (String pin2, Message onComplete) - { - phone.mCM.supplySimPin2(pin2, - obtainMessage(EVENT_PINPUK_DONE, onComplete)); - } - public void supplyPuk2 (String puk2, String newPin2, Message onComplete) - { - phone.mCM.supplySimPuk2(puk2, newPin2, - obtainMessage(EVENT_PINPUK_DONE, onComplete)); - } - - public void supplyNetworkDepersonalization (String pin, Message onComplete) - { - if(DBG) log("Network Despersonalization: " + pin); - phone.mCM.supplyNetworkDepersonalization(pin, - obtainMessage(EVENT_PINPUK_DONE, onComplete)); - } - - public boolean getSimLockEnabled() { - return mSimPinLocked; - } - - public boolean getSimFdnEnabled() { - return mSimFdnEnabled; - } - - public void setSimLockEnabled (boolean enabled, - String password, Message onComplete) { - int serviceClassX; - serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + - CommandsInterface.SERVICE_CLASS_DATA + - CommandsInterface.SERVICE_CLASS_FAX; - - mDesiredPinLocked = enabled; - - phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM, - enabled, password, serviceClassX, - obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete)); - } - - public void setSimFdnEnabled (boolean enabled, - String password, Message onComplete) { - int serviceClassX; - serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + - CommandsInterface.SERVICE_CLASS_DATA + - CommandsInterface.SERVICE_CLASS_FAX + - CommandsInterface.SERVICE_CLASS_SMS; - - mDesiredFdnEnabled = enabled; - - phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD, - enabled, password, serviceClassX, - obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete)); - } - - public void changeSimLockPassword(String oldPassword, String newPassword, - Message onComplete) { - if(DBG) log("Change Pin1 old: " + oldPassword + " new: " + newPassword); - phone.mCM.changeSimPin(oldPassword, newPassword, - obtainMessage(EVENT_CHANGE_SIM_PASSWORD_DONE, onComplete)); - - } - - public void changeSimFdnPassword(String oldPassword, String newPassword, - Message onComplete) { - if(DBG) log("Change Pin2 old: " + oldPassword + " new: " + newPassword); - phone.mCM.changeSimPin2(oldPassword, newPassword, - obtainMessage(EVENT_CHANGE_SIM_PASSWORD_DONE, onComplete)); - - } - - public String getServiceProviderName () { - return phone.mSIMRecords.getServiceProviderName(); - } - - //***** Handler implementation - @Override - public void handleMessage(Message msg){ - AsyncResult ar; - int serviceClassX; - - serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + - CommandsInterface.SERVICE_CLASS_DATA + - CommandsInterface.SERVICE_CLASS_FAX; - - switch (msg.what) { - case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: - status = null; - updateStateProperty(); - broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_SIM_NOT_READY, null); - break; - case EVENT_SIM_READY: - //TODO: put facility read in SIM_READY now, maybe in REG_NW - phone.mCM.getSimStatus(obtainMessage(EVENT_GET_SIM_STATUS_DONE)); - phone.mCM.queryFacilityLock ( - CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, - obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); - phone.mCM.queryFacilityLock ( - CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX, - obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE)); - break; - case EVENT_SIM_LOCKED_OR_ABSENT: - phone.mCM.getSimStatus(obtainMessage(EVENT_GET_SIM_STATUS_DONE)); - phone.mCM.queryFacilityLock ( - CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, - obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); - break; - case EVENT_GET_SIM_STATUS_DONE: - ar = (AsyncResult)msg.obj; - - getSimStatusDone(ar); - break; - case EVENT_PINPUK_DONE: - // a PIN/PUK/PIN2/PUK2/Network Personalization - // request has completed. ar.userObj is the response Message - // Repoll before returning - ar = (AsyncResult)msg.obj; - // TODO should abstract these exceptions - AsyncResult.forMessage(((Message)ar.userObj)).exception - = ar.exception; - phone.mCM.getSimStatus( - obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj)); - break; - case EVENT_REPOLL_STATUS_DONE: - // Finished repolling status after PIN operation - // ar.userObj is the response messaeg - // ar.userObj.obj is already an AsyncResult with an - // appropriate exception filled in if applicable - - ar = (AsyncResult)msg.obj; - getSimStatusDone(ar); - ((Message)ar.userObj).sendToTarget(); - break; - case EVENT_QUERY_FACILITY_LOCK_DONE: - ar = (AsyncResult)msg.obj; - onQueryFacilityLock(ar); - break; - case EVENT_QUERY_FACILITY_FDN_DONE: - ar = (AsyncResult)msg.obj; - onQueryFdnEnabled(ar); - break; - case EVENT_CHANGE_FACILITY_LOCK_DONE: - ar = (AsyncResult)msg.obj; - if (ar.exception == null) { - mSimPinLocked = mDesiredPinLocked; - if (DBG) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " + - "mSimPinLocked= " + mSimPinLocked); - } else { - Log.e(LOG_TAG, "Error change facility lock with exception " - + ar.exception); - } - AsyncResult.forMessage(((Message)ar.userObj)).exception - = ar.exception; - ((Message)ar.userObj).sendToTarget(); - break; - case EVENT_CHANGE_FACILITY_FDN_DONE: - ar = (AsyncResult)msg.obj; - - if (ar.exception == null) { - mSimFdnEnabled = mDesiredFdnEnabled; - if (DBG) log("EVENT_CHANGE_FACILITY_FDN_DONE: " + - "mSimFdnEnabled=" + mSimFdnEnabled); - } else { - Log.e(LOG_TAG, "Error change facility fdn with exception " - + ar.exception); - } - AsyncResult.forMessage(((Message)ar.userObj)).exception - = ar.exception; - ((Message)ar.userObj).sendToTarget(); - break; - case EVENT_CHANGE_SIM_PASSWORD_DONE: - ar = (AsyncResult)msg.obj; - if(ar.exception != null) { - Log.e(LOG_TAG, "Error in change sim password with exception" - + ar.exception); - } - AsyncResult.forMessage(((Message)ar.userObj)).exception - = ar.exception; - ((Message)ar.userObj).sendToTarget(); - break; - default: - Log.e(LOG_TAG, "[GsmSimCard] Unknown Event " + msg.what); - } - } - - - //***** Private methods - - /** - * Interperate EVENT_QUERY_FACILITY_LOCK_DONE - * @param ar is asyncResult of Query_Facility_Locked - */ - private void onQueryFacilityLock(AsyncResult ar) { - if(ar.exception != null) { - if (DBG) log("Error in querying facility lock:" + ar.exception); - return; - } - - int[] ints = (int[])ar.result; - if(ints.length != 0) { - mSimPinLocked = (0!=ints[0]); - if(DBG) log("Query facility lock : " + mSimPinLocked); - } else { - Log.e(LOG_TAG, "[GsmSimCard] Bogus facility lock response"); - } - } - - /** - * Interperate EVENT_QUERY_FACILITY_LOCK_DONE - * @param ar is asyncResult of Query_Facility_Locked - */ - private void onQueryFdnEnabled(AsyncResult ar) { - if(ar.exception != null) { - if(DBG) log("Error in querying facility lock:" + ar.exception); - return; - } - - int[] ints = (int[])ar.result; - if(ints.length != 0) { - mSimFdnEnabled = (0!=ints[0]); - if(DBG) log("Query facility lock : " + mSimFdnEnabled); - } else { - Log.e(LOG_TAG, "[GsmSimCard] Bogus facility lock response"); - } - } - - private void - getSimStatusDone(AsyncResult ar) { - if (ar.exception != null) { - Log.e(LOG_TAG,"Error getting SIM status. " - + "RIL_REQUEST_GET_SIM_STATUS should " - + "never return an error", ar.exception); - return; - } - - CommandsInterface.SimStatus newStatus - = (CommandsInterface.SimStatus) ar.result; - - handleSimStatus(newStatus); - } - - private void - handleSimStatus(CommandsInterface.SimStatus newStatus) { - boolean transitionedIntoPinLocked; - boolean transitionedIntoAbsent; - boolean transitionedIntoNetworkLocked; - - SimCard.State oldState, newState; - - oldState = getState(); - status = newStatus; - newState = getState(); - - updateStateProperty(); - - transitionedIntoPinLocked = ( - (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED) - || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED)); - transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT); - transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED - && newState == State.NETWORK_LOCKED); - - if (transitionedIntoPinLocked) { - if(DBG) log("Notify SIM pin or puk locked."); - pinLockedRegistrants.notifyRegistrants(); - broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_SIM_LOCKED, - (newState == State.PIN_REQUIRED) ? - INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK); - } else if (transitionedIntoAbsent) { - if(DBG) log("Notify SIM missing."); - absentRegistrants.notifyRegistrants(); - broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_SIM_ABSENT, null); - } else if (transitionedIntoNetworkLocked) { - if(DBG) log("Notify SIM network locked."); - networkLockedRegistrants.notifyRegistrants(); - broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_SIM_LOCKED, - INTENT_VALUE_LOCKED_NETWORK); - } - } - - public void broadcastSimStateChangedIntent(String value, String reason) { - Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED); - intent.putExtra(Phone.PHONE_NAME_KEY, phone.getPhoneName()); - intent.putExtra(SimCard.INTENT_KEY_SIM_STATE, value); - intent.putExtra(SimCard.INTENT_KEY_LOCKED_REASON, reason); - if(DBG) log("Broadcasting intent SIM_STATE_CHANGED_ACTION " + value - + " reason " + reason); - ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE); - } - - public void updateImsiConfiguration(String imsi) { - if (imsi.length() >= 6) { - Configuration config = new Configuration(); - config.mcc = ((imsi.charAt(0)-'0')*100) - + ((imsi.charAt(1)-'0')*10) - + (imsi.charAt(2)-'0'); - config.mnc = ((imsi.charAt(3)-'0')*100) - + ((imsi.charAt(4)-'0')*10) - + (imsi.charAt(5)-'0'); - try { - ActivityManagerNative.getDefault().updateConfiguration(config); - } catch (RemoteException e) { - } - } - } - - private void - updateStateProperty() { - phone.setSystemProperty( - TelephonyProperties.PROPERTY_SIM_STATE, - getState().toString()); - } - - private void log(String msg) { - Log.d(LOG_TAG, "[GsmSimCard] " + msg); - } -} - diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java new file mode 100644 index 0000000..c163803 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java @@ -0,0 +1,148 @@ +/* + * 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.gsm; + +import android.telephony.PhoneNumberUtils; + +import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.SmsAddress; + +public class GsmSmsAddress extends SmsAddress { + + static final int OFFSET_ADDRESS_LENGTH = 0; + + static final int OFFSET_TOA = 1; + + static final int OFFSET_ADDRESS_VALUE = 2; + + /** + * New GsmSmsAddress from TS 23.040 9.1.2.5 Address Field + * + * @param offset the offset of the Address-Length byte + * @param length the length in bytes rounded up, e.g. "2 + + * (addressLength + 1) / 2" + */ + + public GsmSmsAddress(byte[] data, int offset, int length) { + origBytes = new byte[length]; + System.arraycopy(data, offset, origBytes, 0, length); + + // addressLength is the count of semi-octets, not bytes + int addressLength = origBytes[OFFSET_ADDRESS_LENGTH] & 0xff; + + int toa = origBytes[OFFSET_TOA] & 0xff; + ton = 0x7 & (toa >> 4); + + // TOA must have its high bit set + if ((toa & 0x80) != 0x80) { + throw new RuntimeException("Invalid TOA - high bit must be set"); + } + + if (isAlphanumeric()) { + // An alphanumeric address + int countSeptets = addressLength * 4 / 7; + + address = GsmAlphabet.gsm7BitPackedToString(origBytes, + OFFSET_ADDRESS_VALUE, countSeptets); + } else { + // TS 23.040 9.1.2.5 says + // that "the MS shall interpret reserved values as 'Unknown' + // but shall store them exactly as received" + + byte lastByte = origBytes[length - 1]; + + if ((addressLength & 1) == 1) { + // Make sure the final unused BCD digit is 0xf + origBytes[length - 1] |= 0xf0; + } + address = PhoneNumberUtils.calledPartyBCDToString(origBytes, + OFFSET_TOA, length - OFFSET_TOA); + + // And restore origBytes + origBytes[length - 1] = lastByte; + } + } + + public String getAddressString() { + return address; + } + + /** + * Returns true if this is an alphanumeric address + */ + public boolean isAlphanumeric() { + return ton == TON_ALPHANUMERIC; + } + + public boolean isNetworkSpecific() { + return ton == TON_NETWORK; + } + + /** + * Returns true of this is a valid CPHS voice message waiting indicator + * address + */ + public boolean isCphsVoiceMessageIndicatorAddress() { + // CPHS-style MWI message + // See CPHS 4.7 B.4.2.1 + // + // Basically: + // + // - Originating address should be 4 bytes long and alphanumeric + // - Decode will result with two chars: + // - Char 1 + // 76543210 + // ^ set/clear indicator (0 = clear) + // ^^^ type of indicator (000 = voice) + // ^^^^ must be equal to 0001 + // - Char 2: + // 76543210 + // ^ line number (0 = line 1) + // ^^^^^^^ set to 0 + // + // Remember, since the alpha address is stored in 7-bit compact form, + // the "line number" is really the top bit of the first address value + // byte + + return (origBytes[OFFSET_ADDRESS_LENGTH] & 0xff) == 4 + && isAlphanumeric() && (origBytes[OFFSET_TOA] & 0x0f) == 0; + } + + /** + * Returns true if this is a valid CPHS voice message waiting indicator + * address indicating a "set" of "indicator 1" of type "voice message + * waiting" + */ + public boolean isCphsVoiceMessageSet() { + // 0x11 means "set" "voice message waiting" "indicator 1" + return isCphsVoiceMessageIndicatorAddress() + && (origBytes[OFFSET_ADDRESS_VALUE] & 0xff) == 0x11; + + } + + /** + * Returns true if this is a valid CPHS voice message waiting indicator + * address indicating a "clear" of "indicator 1" of type "voice message + * waiting" + */ + public boolean isCphsVoiceMessageClear() { + // 0x10 means "clear" "voice message waiting" "indicator 1" + return isCphsVoiceMessageIndicatorAddress() + && (origBytes[OFFSET_ADDRESS_VALUE] & 0xff) == 0x10; + + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/ISimPhoneBook.aidl b/telephony/java/com/android/internal/telephony/gsm/ISimPhoneBook.aidl deleted file mode 100644 index 77033a7..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/ISimPhoneBook.aidl +++ /dev/null @@ -1,101 +0,0 @@ -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package com.android.internal.telephony.gsm; - -import com.android.internal.telephony.gsm.AdnRecord; - -import java.util.List; - -/** Interface for applications to access the SIM phone book. - * - *

    The following code snippet demonstrates a static method to - * retrieve the ISimPhoneBook interface from Android:

    - *
    private static ISimPhoneBook getSimPhoneBookInterface()
    -            throws DeadObjectException {
    -    IServiceManager sm = ServiceManagerNative.getDefault();
    -    ISimPhoneBook spb;
    -    spb = ISimPhoneBook.Stub.asInterface(sm.getService("simphonebook"));
    -    return spb;
    -}
    - * 
    - */ - -interface ISimPhoneBook { - - /** - * Loads the AdnRecords in efid and returns them as a - * List of AdnRecords - * - * @param efid the EF id of a ADN-like SIM - * @return List of AdnRecord - */ - List getAdnRecordsInEf(int efid); - - /** - * Replace oldAdn with newAdn in ADN-like record in EF - * - * getAdnRecordsInEf must be called at least once before this function, - * otherwise an error will be returned - * - * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN - * @param oldTag adn tag to be replaced - * @param oldPhoneNumber adn number to be replaced - * Set both oldTag and oldPhoneNubmer to "" means to replace an - * empty record, aka, insert new record - * @param newTag adn tag to be stored - * @param newPhoneNumber adn number ot be stored - * Set both newTag and newPhoneNubmer to "" means to replace the old - * record with empty one, aka, delete old record - * @param pin2 required to update EF_FDN, otherwise must be null - * @return true for success - */ - boolean updateAdnRecordsInEfBySearch(int efid, - String oldTag, String oldPhoneNumber, - String newTag, String newPhoneNumber, - String pin2); - - /** - * Update an ADN-like EF record by record index - * - * This is useful for iteration the whole ADN file, such as write the whole - * phone book or erase/format the whole phonebook - * - * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN - * @param newTag adn tag to be stored - * @param newPhoneNumber adn number to be stored - * Set both newTag and newPhoneNubmer to "" means to replace the old - * record with empty one, aka, delete old record - * @param index is 1-based adn record index to be updated - * @param pin2 required to update EF_FDN, otherwise must be null - * @return true for success - */ - boolean updateAdnRecordsInEfByIndex(int efid, String newTag, - String newPhoneNumber, int index, - String pin2); - - /** - * Get the max munber of records in efid - * - * @param efid the EF id of a ADN-like SIM - * @return int[3] array - * recordSizes[0] is the single record length - * recordSizes[1] is the total length of the EF file - * recordSizes[2] is the number of records in the EF file - */ - int[] getAdnRecordsSize(int efid); - -} diff --git a/telephony/java/com/android/internal/telephony/gsm/ISms.aidl b/telephony/java/com/android/internal/telephony/gsm/ISms.aidl deleted file mode 100644 index 904a54e..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/ISms.aidl +++ /dev/null @@ -1,115 +0,0 @@ -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package com.android.internal.telephony.gsm; - -import android.app.PendingIntent; -import com.android.internal.telephony.gsm.SmsRawData; - -/** Interface for applications to access the SIM phone book. - * - *

    The following code snippet demonstrates a static method to - * retrieve the ISimSms interface from Android:

    - *
    private static ISimSms getSimSmsInterface()
    -            throws DeadObjectException {
    -    IServiceManager sm = ServiceManagerNative.getDefault();
    -    ISimSms ss;
    -    ss = ISimSms.Stub.asInterface(sm.getService("isms"));
    -    return ss;
    -}
    - * 
    - */ - -interface ISms { - /** - * Retrieves all messages currently stored on SIM. - * - * @return list of SmsRawData of all sms on SIM - */ - List getAllMessagesFromSimEf(); - - /** - * 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 success or not - * - */ - boolean updateMessageOnSimEf(int messageIndex, int newStatus, - in byte[] pdu); - - /** - * Copy a raw SMS PDU to the SIM. - * - * @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 success or not - * - */ - boolean copyMessageToSimEf(int status, in byte[] pdu, in byte[] smsc); - - /** - * Send a SMS - * - * @param smsc the SMSC to send the message through, or NULL for the - * defatult SMSC - * @param pdu the raw PDU to send - * @param sentIntent if not NULL this Intent is - * broadcast when the message is sucessfully sent, or failed. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * @param deliveryIntent if not NULL this Intent is - * broadcast when the message is delivered to the recipient. The - * raw pdu of the status report is in the extended data ("pdu"). - */ - void sendRawPdu(in byte[] smsc, in byte[] pdu, in PendingIntent sentIntent, - in PendingIntent deliveryIntent); - - /** - * Send a multi-part 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 parts an ArrayList of strings that, in order, - * comprise the original message - * @param sentIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * @param deliveryIntents if not null, an ArrayList of - * PendingIntents (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"). - */ - void sendMultipartText(in String destinationAddress, in String scAddress, - in List parts, in List sentIntents, - in List deliveryIntents); - -} diff --git a/telephony/java/com/android/internal/telephony/gsm/MccTable.java b/telephony/java/com/android/internal/telephony/gsm/MccTable.java index bb17cc4..8473a21 100644 --- a/telephony/java/com/android/internal/telephony/gsm/MccTable.java +++ b/telephony/java/com/android/internal/telephony/gsm/MccTable.java @@ -62,11 +62,11 @@ public final class MccTable entryForMcc(int mcc) { int index; - + MccEntry m; m = new MccEntry(mcc, null, 0); - + index = Collections.binarySearch(table, m); if (index < 0) { @@ -154,7 +154,7 @@ public final class MccTable /* * The table below is built from two resources: - * + * * 1) ITU "Mobile Network Code (MNC) for the international * identification plan for mobile terminals and mobile users" * which is available as an annex to the ITU operational bulletin diff --git a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl b/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl index c600530..d88d0b7 100644 --- a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl +++ b/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.aidl @@ -16,11 +16,11 @@ package com.android.internal.telephony.gsm; -/** +/** * Used to indicate that the NetworkInfo object is parcelable to aidl. * This is a simple effort to make NetworkInfo parcelable rather than * trying to make the conventional containing object (AsyncResult), - * implement parcelable. This functionality is needed for the + * implement parcelable. This functionality is needed for the * NetworkQueryService to fix 1128695 */ parcelable NetworkInfo; diff --git a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java b/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java index bebf9ba..04fd13e 100644 --- a/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java +++ b/telephony/java/com/android/internal/telephony/gsm/NetworkInfo.java @@ -22,8 +22,7 @@ import android.os.Parcelable; /** * {@hide} */ -public class NetworkInfo implements Parcelable -{ +public class NetworkInfo implements Parcelable { public enum State { UNKNOWN, AVAILABLE, @@ -39,34 +38,29 @@ public class NetworkInfo implements Parcelable public String - getOperatorAlphaLong() - { + getOperatorAlphaLong() { return operatorAlphaLong; } public String - getOperatorAlphaShort() - { + getOperatorAlphaShort() { return operatorAlphaShort; } public String - getOperatorNumeric() - { + getOperatorNumeric() { return operatorNumeric; } public State - getState() - { + getState() { return state; } - NetworkInfo(String operatorAlphaLong, - String operatorAlphaShort, - String operatorNumeric, - State state) - { + NetworkInfo(String operatorAlphaLong, + String operatorAlphaShort, + String operatorNumeric, + State state) { this.operatorAlphaLong = operatorAlphaLong; this.operatorAlphaShort = operatorAlphaShort; @@ -76,20 +70,18 @@ public class NetworkInfo implements Parcelable } - NetworkInfo(String operatorAlphaLong, - String operatorAlphaShort, - String operatorNumeric, - String stateString) - { - this (operatorAlphaLong, operatorAlphaShort, + public NetworkInfo(String operatorAlphaLong, + String operatorAlphaShort, + String operatorNumeric, + String stateString) { + this (operatorAlphaLong, operatorAlphaShort, operatorNumeric, rilStateToState(stateString)); } /** * See state strings defined in ril.h RIL_REQUEST_QUERY_AVAILABLE_NETWORKS */ - private static State rilStateToState(String s) - { + private static State rilStateToState(String s) { if (s.equals("unknown")) { return State.UNKNOWN; } else if (s.equals("available")) { @@ -105,29 +97,28 @@ public class NetworkInfo implements Parcelable } - public String toString() - { - return "NetworkInfo " + operatorAlphaLong - + "/" + operatorAlphaShort - + "/" + operatorNumeric + public String toString() { + return "NetworkInfo " + operatorAlphaLong + + "/" + operatorAlphaShort + + "/" + operatorNumeric + "/" + state; } - - /** + + /** * Parcelable interface implemented below. * This is a simple effort to make NetworkInfo parcelable rather than * trying to make the conventional containing object (AsyncResult), - * implement parcelable. This functionality is needed for the + * implement parcelable. This functionality is needed for the * NetworkQueryService to fix 1128695. */ - + public int describeContents() { return 0; } - /** + /** * Implement the Parcelable interface. - * Method to serialize a NetworkInfo object. + * Method to serialize a NetworkInfo object. */ public void writeToParcel(Parcel dest, int flags) { dest.writeString(operatorAlphaLong); @@ -136,9 +127,9 @@ public class NetworkInfo implements Parcelable dest.writeSerializable(state); } - /** + /** * Implement the Parcelable interface - * Method to deserialize a NetworkInfo object, or an array thereof. + * Method to deserialize a NetworkInfo object, or an array thereof. */ public static final Creator CREATOR = new Creator() { diff --git a/telephony/java/com/android/internal/telephony/gsm/PDPContextState.java b/telephony/java/com/android/internal/telephony/gsm/PDPContextState.java index d5d481a..feb78f8 100644 --- a/telephony/java/com/android/internal/telephony/gsm/PDPContextState.java +++ b/telephony/java/com/android/internal/telephony/gsm/PDPContextState.java @@ -19,8 +19,7 @@ package com.android.internal.telephony.gsm; /** * {@hide} */ -public class PDPContextState -{ +public class PDPContextState { public int cid; public boolean active; public String type; diff --git a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java index 66f8b72..88acb1b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java +++ b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java @@ -17,88 +17,26 @@ package com.android.internal.telephony.gsm; import android.os.*; -import android.database.Cursor; -import android.provider.Telephony; import android.text.util.Regex; import android.util.EventLog; import android.util.Log; -import java.util.ArrayList; +import com.android.internal.telephony.CommandException; +import com.android.internal.telephony.DataConnection; +import com.android.internal.telephony.DataLink; import com.android.internal.telephony.Phone; +import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.TelephonyEventLog; /** * {@hide} */ -public class PdpConnection extends Handler { +public class PdpConnection extends DataConnection { private static final String LOG_TAG = "GSM"; private static final boolean DBG = true; private static final boolean FAKE_FAIL = false; - public enum PdpState { - ACTIVE, /* has active pdp context */ - ACTIVATING, /* during connecting process */ - INACTIVE; /* has empty pdp context */ - - public String toString() { - switch (this) { - case ACTIVE: return "active"; - case ACTIVATING: return "setting up"; - default: return "inactive"; - } - } - - public boolean isActive() { - return this == ACTIVE; - } - - public boolean isInactive() { - return this == INACTIVE; - } - } - - public enum PdpFailCause { - NONE, - BAD_APN, - BAD_PAP_SECRET, - BARRED, - USER_AUTHENTICATION, - SERVICE_OPTION_NOT_SUPPORTED, - SERVICE_OPTION_NOT_SUBSCRIBED, - SIM_LOCKED, - RADIO_OFF, - NO_SIGNAL, - NO_DATA_PLAN, - RADIO_NOT_AVIALABLE, - SUSPENED_TEMPORARY, - RADIO_ERROR_RETRY, - UNKNOWN; - - public boolean isPermanentFail() { - return (this == RADIO_OFF); - } - - public String toString() { - switch (this) { - case NONE: return "no error"; - case BAD_APN: return "bad apn"; - case BAD_PAP_SECRET:return "bad pap secret"; - case BARRED: return "barred"; - case USER_AUTHENTICATION: return "error user autentication"; - case SERVICE_OPTION_NOT_SUPPORTED: return "data not supported"; - case SERVICE_OPTION_NOT_SUBSCRIBED: return "datt not subcribed"; - case SIM_LOCKED: return "sim locked"; - case RADIO_OFF: return "radio is off"; - case NO_SIGNAL: return "no signal"; - case NO_DATA_PLAN: return "no data plan"; - case RADIO_NOT_AVIALABLE: return "radio not available"; - case SUSPENED_TEMPORARY: return "suspend temporary"; - case RADIO_ERROR_RETRY: return "transient radio error"; - default: return "unknown data error"; - } - } - } - /** Fail cause of last PDP activate, from RIL_LastPDPActivateFailCause */ private static final int PDP_FAIL_RIL_BARRED = 8; private static final int PDP_FAIL_RIL_BAD_APN = 27; @@ -107,54 +45,20 @@ public class PdpConnection extends Handler { private static final int PDP_FAIL_RIL_SERVICE_OPTION_NOT_SUBSCRIBED = 33; private static final int PDP_FAIL_RIL_ERROR_UNSPECIFIED = 0xffff; - //***** Event codes - private static final int EVENT_SETUP_PDP_DONE = 1; - private static final int EVENT_GET_LAST_FAIL_DONE = 2; - private static final int EVENT_LINK_STATE_CHANGED = 3; - private static final int EVENT_DEACTIVATE_DONE = 4; - private static final int EVENT_FORCE_RETRY = 5; - //***** Instance Variables - private GSMPhone phone; private String pdp_name; - private PdpState state; - private Message onConnectCompleted; - private Message onDisconnect; - private int cid; - private long createTime; - private long lastFailTime; - private PdpFailCause lastFailCause; private ApnSetting apn; - private String interfaceName; - private String ipAddress; - private String gatewayAddress; - private String[] dnsServers; - - private static final String NULL_IP = "0.0.0.0"; // dataLink is only used to support pppd link - DataLink dataLink; - // receivedDisconnectReq is set when disconnect pdp link during activating - private boolean receivedDisconnectReq; + private DataLink dataLink; //***** Constructor - PdpConnection(GSMPhone phone) - { - this.phone = phone; - this.state = PdpState.INACTIVE; - onConnectCompleted = null; - onDisconnect = null; - this.cid = -1; - this.createTime = -1; - this.lastFailTime = -1; - this.lastFailCause = PdpFailCause.NONE; - this.apn = null; + PdpConnection(GSMPhone phone) { + super(phone); this.dataLink = null; - receivedDisconnectReq = false; - this.dnsServers = new String[2]; if (SystemProperties.get("ro.radio.use-ppp","no").equals("yes")) { - dataLink = new PppLink(phone.mDataConnection); + dataLink = new PppLink((GsmDataConnectionTracker) phone.mDataConnection, phone); dataLink.setOnLinkChange(this, EVENT_LINK_STATE_CHANGED, null); } } @@ -171,37 +75,37 @@ public class PdpConnection extends Handler { setHttpProxy (apn.proxy, apn.port); - state = PdpState.ACTIVATING; + state = State.ACTIVATING; this.apn = apn; onConnectCompleted = onCompleted; createTime = -1; lastFailTime = -1; - lastFailCause = PdpFailCause.NONE; + lastFailCause = FailCause.NONE; receivedDisconnectReq = false; if (FAKE_FAIL) { // for debug before baseband implement error in setup PDP if (apn.apn.equalsIgnoreCase("badapn")){ - notifyFail(PdpFailCause.BAD_APN, onConnectCompleted); + notifyFail(FailCause.BAD_APN, onConnectCompleted); return; } } - phone.mCM.setupDefaultPDP(apn.apn, apn.user, apn.password, - obtainMessage(EVENT_SETUP_PDP_DONE)); + phone.mCM.setupDataCall(Integer.toString(RILConstants.GSM_PHONE), null, apn.apn, apn.user, + apn.password, obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE)); } - void disconnect(Message msg) { + protected void disconnect(Message msg) { onDisconnect = msg; - if (state == PdpState.ACTIVE) { + if (state == State.ACTIVE) { if (dataLink != null) { dataLink.disconnect(); } if (phone.mCM.getRadioState().isOn()) { - phone.mCM.deactivateDefaultPDP(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); + phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, msg)); } - } else if (state == PdpState.ACTIVATING) { + } else if (state == State.ACTIVATING) { receivedDisconnectReq = true; } else { // state == INACTIVE. Nothing to do, so notify immediately. @@ -209,20 +113,9 @@ public class PdpConnection extends Handler { } } - private void - setHttpProxy(String httpProxy, String httpPort) - { - if (httpProxy == null || httpProxy.length() == 0) { - phone.setSystemProperty("net.gprs.http-proxy", null); - return; - } - - if (httpPort == null || httpPort.length() == 0) { - httpPort = "8080"; // Default to port 8080 - } - - phone.setSystemProperty("net.gprs.http-proxy", - "http://" + httpProxy + ":" + httpPort + "/"); + public void clearSettings() { + super.clearSettings(); + apn = null; } public String toString() { @@ -231,61 +124,30 @@ public class PdpConnection extends Handler { " lastFailCause=" + lastFailCause; } - public long getConnectionTime() { - return createTime; - } - - public long getLastFailTime() { - return lastFailTime; - } - - public PdpFailCause getLastFailCause() { - return lastFailCause; - } - - public ApnSetting getApn() { - return apn; - } - - String getInterface() { - return interfaceName; - } - - String getIpAddress() { - return ipAddress; - } - - String getGatewayAddress() { - return gatewayAddress; - } - - String[] getDnsServers() { - return dnsServers; - } - - public PdpState getState() { - return state; - } - private void notifyFail(PdpFailCause cause, Message onCompleted) { + protected void notifyFail(FailCause cause, Message onCompleted) { if (onCompleted == null) return; - state = PdpState.INACTIVE; + state = State.INACTIVE; lastFailCause = cause; lastFailTime = System.currentTimeMillis(); onConnectCompleted = null; - if (DBG) log("Notify PDP fail at " + lastFailTime - + " due to " + lastFailCause); + if (DBG) { + log("Notify PDP fail at " + lastFailTime + + " due to " + lastFailCause); + } AsyncResult.forMessage(onCompleted, cause, new Exception()); onCompleted.sendToTarget(); } - private void notifySuccess(Message onCompleted) { - if (onCompleted == null) return; + protected void notifySuccess(Message onCompleted) { + if (onCompleted == null) { + return; + } - state = PdpState.ACTIVE; + state = State.ACTIVE; createTime = System.currentTimeMillis(); onConnectCompleted = null; onCompleted.arg1 = cid; @@ -296,7 +158,7 @@ public class PdpConnection extends Handler { onCompleted.sendToTarget(); } - private void notifyDisconnect(Message msg) { + protected void notifyDisconnect(Message msg) { if (DBG) log("Notify PDP disconnect"); if (msg != null) { @@ -306,22 +168,7 @@ public class PdpConnection extends Handler { clearSettings(); } - void clearSettings() { - state = PdpState.INACTIVE; - receivedDisconnectReq = false; - createTime = -1; - lastFailTime = -1; - lastFailCause = PdpFailCause.NONE; - apn = null; - onConnectCompleted = null; - interfaceName = null; - ipAddress = null; - gatewayAddress = null; - dnsServers[0] = null; - dnsServers[1] = null; - } - - private void onLinkStateChanged(DataLink.LinkState linkState) { + protected void onLinkStateChanged(DataLink.LinkState linkState) { switch (linkState) { case LINK_UP: notifySuccess(onConnectCompleted); @@ -335,151 +182,110 @@ public class PdpConnection extends Handler { } } - private PdpFailCause getFailCauseFromRequest(int rilCause) { - PdpFailCause cause; + protected FailCause getFailCauseFromRequest(int rilCause) { + FailCause cause; switch (rilCause) { case PDP_FAIL_RIL_BARRED: - cause = PdpFailCause.BARRED; + cause = FailCause.BARRED; break; case PDP_FAIL_RIL_BAD_APN: - cause = PdpFailCause.BAD_APN; + cause = FailCause.BAD_APN; break; case PDP_FAIL_RIL_USER_AUTHENTICATION: - cause = PdpFailCause.USER_AUTHENTICATION; + cause = FailCause.USER_AUTHENTICATION; break; case PDP_FAIL_RIL_SERVICE_OPTION_NOT_SUPPORTED: - cause = PdpFailCause.SERVICE_OPTION_NOT_SUPPORTED; + cause = FailCause.SERVICE_OPTION_NOT_SUPPORTED; break; case PDP_FAIL_RIL_SERVICE_OPTION_NOT_SUBSCRIBED: - cause = PdpFailCause.SERVICE_OPTION_NOT_SUBSCRIBED; + cause = FailCause.SERVICE_OPTION_NOT_SUBSCRIBED; break; default: - cause = PdpFailCause.UNKNOWN; + cause = FailCause.UNKNOWN; } return cause; } - - private void log(String s) { + protected void log(String s) { Log.d(LOG_TAG, "[PdpConnection] " + s); } @Override - public void handleMessage(Message msg) { - AsyncResult ar; - - switch (msg.what) { - case EVENT_SETUP_PDP_DONE: - ar = (AsyncResult) msg.obj; - - if (ar.exception != null) { - Log.e(LOG_TAG, "PDP Context Init failed " + ar.exception); - - if (receivedDisconnectReq) { - // Don't bother reporting the error if there's already a - // pending disconnect request, since DataConnectionTracker - // has already updated its state. - notifyDisconnect(onDisconnect); - } else { - if ( ar.exception instanceof CommandException && - ((CommandException) (ar.exception)).getCommandError() - == CommandException.Error.RADIO_NOT_AVAILABLE) { - notifyFail(PdpFailCause.RADIO_NOT_AVIALABLE, - onConnectCompleted); - } else { - phone.mCM.getLastPdpFailCause( - obtainMessage(EVENT_GET_LAST_FAIL_DONE)); - } - } + protected void onDeactivated(AsyncResult ar) { + notifyDisconnect((Message) ar.userObj); + if (DBG) log("PDP Connection Deactivated"); + } + + @Override + protected void onSetupConnectionCompleted(AsyncResult ar) { + if (ar.exception != null) { + Log.e(LOG_TAG, "PDP Context Init failed " + ar.exception); + + if (receivedDisconnectReq) { + // Don't bother reporting the error if there's already a + // pending disconnect request, since DataConnectionTracker + // has already updated its state. + notifyDisconnect(onDisconnect); + } else { + if ( ar.exception instanceof CommandException && + ((CommandException) (ar.exception)).getCommandError() + == CommandException.Error.RADIO_NOT_AVAILABLE) { + notifyFail(FailCause.RADIO_NOT_AVAILABLE, + onConnectCompleted); } else { - if (receivedDisconnectReq) { - // Don't bother reporting success if there's already a - // pending disconnect request, since DataConnectionTracker - // has already updated its state. - // Set ACTIVE so that disconnect does the right thing. - state = PdpState.ACTIVE; - disconnect(onDisconnect); - } else { - String[] response = ((String[]) ar.result); - cid = Integer.parseInt(response[0]); - - if (response.length > 2) { - interfaceName = response[1]; - ipAddress = response[2]; - String prefix = "net." + interfaceName + "."; - gatewayAddress = SystemProperties.get(prefix + "gw"); - dnsServers[0] = SystemProperties.get(prefix + "dns1"); - dnsServers[1] = SystemProperties.get(prefix + "dns2"); - if (DBG) { - log("interface=" + interfaceName + " ipAddress=" + ipAddress - + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0] - + " DNS2=" + dnsServers[1]); - } - - if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1]) - && !phone.isDnsCheckDisabled()) { - // Work around a race condition where QMI does not fill in DNS: - // Deactivate PDP and let DataConnectionTracker retry. - // Do not apply the race condition workaround for MMS APN - // if Proxy is an IP-address. - // Otherwise, the default APN will not be restored anymore. - if (!apn.types[0].equals(Phone.APN_TYPE_MMS) - || !isIpAddress(apn.mmsProxy)) { - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_BAD_DNS_ADDRESS, - dnsServers[0]); - phone.mCM.deactivateDefaultPDP(cid, - obtainMessage(EVENT_FORCE_RETRY)); - break; - } - } - } + phone.mCM.getLastPdpFailCause( + obtainMessage(EVENT_GET_LAST_FAIL_DONE)); + } + } + } else { + if (receivedDisconnectReq) { + // Don't bother reporting success if there's already a + // pending disconnect request, since DataConnectionTracker + // has already updated its state. + disconnect(onDisconnect); + } else { + String[] response = ((String[]) ar.result); + cid = Integer.parseInt(response[0]); + + if (response.length > 2) { + interfaceName = response[1]; + ipAddress = response[2]; + String prefix = "net." + interfaceName + "."; + gatewayAddress = SystemProperties.get(prefix + "gw"); + dnsServers[0] = SystemProperties.get(prefix + "dns1"); + dnsServers[1] = SystemProperties.get(prefix + "dns2"); + if (DBG) { + log("interface=" + interfaceName + " ipAddress=" + ipAddress + + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0] + + " DNS2=" + dnsServers[1]); + } - if (dataLink != null) { - dataLink.connect(); - } else { - onLinkStateChanged(DataLink.LinkState.LINK_UP); + if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1]) + && !((GSMPhone) phone).isDnsCheckDisabled()) { + // Work around a race condition where QMI does not fill in DNS: + // Deactivate PDP and let DataConnectionTracker retry. + // Do not apply the race condition workaround for MMS APN + // if Proxy is an IP-address. + // Otherwise, the default APN will not be restored anymore. + if (!apn.types[0].equals(Phone.APN_TYPE_MMS) + || !isIpAddress(apn.mmsProxy)) { + EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_BAD_DNS_ADDRESS, + dnsServers[0]); + phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_FORCE_RETRY)); + return; } - - if (DBG) log("PDP setup on cid = " + cid); } } - break; - case EVENT_FORCE_RETRY: - if (receivedDisconnectReq) { - notifyDisconnect(onDisconnect); - } else { - ar = (AsyncResult) msg.obj; - notifyFail(PdpFailCause.RADIO_ERROR_RETRY, onConnectCompleted); - } - break; - case EVENT_GET_LAST_FAIL_DONE: - if (receivedDisconnectReq) { - // Don't bother reporting the error if there's already a - // pending disconnect request, since DataConnectionTracker - // has already updated its state. - notifyDisconnect(onDisconnect); - } else { - ar = (AsyncResult) msg.obj; - PdpFailCause cause = PdpFailCause.UNKNOWN; - if (ar.exception == null) { - int rilFailCause = ((int[]) (ar.result))[0]; - cause = getFailCauseFromRequest(rilFailCause); - } - notifyFail(cause, onConnectCompleted); + if (dataLink != null) { + dataLink.connect(); + } else { + onLinkStateChanged(DataLink.LinkState.LINK_UP); } - break; - case EVENT_LINK_STATE_CHANGED: - ar = (AsyncResult) msg.obj; - DataLink.LinkState ls = (DataLink.LinkState) ar.result; - onLinkStateChanged(ls); - break; - case EVENT_DEACTIVATE_DONE: - ar = (AsyncResult) msg.obj; - notifyDisconnect((Message) ar.userObj); - break; + if (DBG) log("PDP setup on cid = " + cid); + } } } @@ -488,4 +294,8 @@ public class PdpConnection extends Handler { return Regex.IP_ADDRESS_PATTERN.matcher(apn.mmsProxy).matches(); } + + public ApnSetting getApn() { + return this.apn; + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/PppLink.java b/telephony/java/com/android/internal/telephony/gsm/PppLink.java index 43d4f1f..9627696 100644 --- a/telephony/java/com/android/internal/telephony/gsm/PppLink.java +++ b/telephony/java/com/android/internal/telephony/gsm/PppLink.java @@ -16,28 +16,31 @@ package com.android.internal.telephony.gsm; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.RandomAccessFile; - import android.database.Cursor; import android.os.Message; import android.os.SystemProperties; import android.os.SystemService; -import com.android.internal.telephony.gsm.DataConnectionTracker.State; -import com.android.internal.util.ArrayUtils; import android.util.Log; +import com.android.internal.telephony.DataLink; +import com.android.internal.telephony.DataConnectionTracker.State; +import com.android.internal.telephony.PhoneBase; +import com.android.internal.util.ArrayUtils; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + /** * Represents a PPP link. - * + * * Ideally this would be managed by the RIL implementation, but * we currently have implementations where this is not the case. * * {@hide} */ -final class PppLink extends DataLink implements DataLinkInterface { +final class PppLink extends DataLink { private static final String LOG_TAG = "GSM"; static final String PATH_PPP_OPERSTATE = "/sys/class/net/ppp0/operstate"; @@ -69,11 +72,14 @@ final class PppLink extends DataLink implements DataLinkInterface { }; private final byte[] mCheckPPPBuffer = new byte[32]; + private PhoneBase phone; + int lastPppdExitCode = EXIT_OK; - PppLink(DataConnectionTracker dc) { + PppLink(GsmDataConnectionTracker dc, GSMPhone p) { super(dc); + this.phone = p; } public void connect() { @@ -131,7 +137,7 @@ final class PppLink extends DataLink implements DataLinkInterface { checkPPP(); // keep polling in case interface goes down - if (dataConnection.state != State.IDLE) { + if (dataConnection.getState() != State.IDLE) { Message poll = obtainMessage(); poll.what = EVENT_POLL_DATA_CONNECTION; sendMessageDelayed(poll, POLL_SYSFS_MILLIS); @@ -141,7 +147,7 @@ final class PppLink extends DataLink implements DataLinkInterface { } private void checkPPP() { - boolean connecting = (dataConnection.state == State.CONNECTING); + boolean connecting = (dataConnection.getState() == State.CONNECTING); try { RandomAccessFile file = new RandomAccessFile(PATH_PPP_OPERSTATE, "r"); @@ -152,10 +158,10 @@ final class PppLink extends DataLink implements DataLinkInterface { // "unknown" where one might otherwise expect "up" if (ArrayUtils.equals(mCheckPPPBuffer, UP_ASCII_STRING, UP_ASCII_STRING.length) || ArrayUtils.equals(mCheckPPPBuffer, UNKNOWN_ASCII_STRING, - UNKNOWN_ASCII_STRING.length) - && dataConnection.state == State.CONNECTING) { + UNKNOWN_ASCII_STRING.length) + && dataConnection.getState() == State.CONNECTING) { - Log.i(LOG_TAG, + Log.i(LOG_TAG, "found ppp interface. Notifying GPRS connected"); if (mLinkChangeRegistrant != null) { @@ -163,23 +169,23 @@ final class PppLink extends DataLink implements DataLinkInterface { } connecting = false; - } else if (dataConnection.state == State.CONNECTED + } else if (dataConnection.getState() == State.CONNECTED && ArrayUtils.equals(mCheckPPPBuffer, DOWN_ASCII_STRING, - DOWN_ASCII_STRING.length)) { + DOWN_ASCII_STRING.length)) { - Log.i(LOG_TAG, + Log.i(LOG_TAG, "ppp interface went down. Reconnecting..."); if (mLinkChangeRegistrant != null) { mLinkChangeRegistrant.notifyResult(LinkState.LINK_DOWN); } - } + } } catch (IOException ex) { if (! (ex instanceof FileNotFoundException)) { Log.i(LOG_TAG, "Poll ppp0 ex " + ex.toString()); } - if (dataConnection.state == State.CONNECTED && + if (dataConnection.getState() == State.CONNECTED && mLinkChangeRegistrant != null) { mLinkChangeRegistrant.notifyResult(LinkState.LINK_DOWN); } @@ -206,4 +212,8 @@ final class PppLink extends DataLink implements DataLinkInterface { } } + + protected void log(String s) { + Log.d(LOG_TAG, "[PppLink] " + s); + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/RIL.java b/telephony/java/com/android/internal/telephony/gsm/RIL.java deleted file mode 100644 index e2d0f72..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/RIL.java +++ /dev/null @@ -1,2586 +0,0 @@ -/* - * 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.gsm; - -import static com.android.internal.telephony.gsm.RILConstants.*; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.LocalSocket; -import android.net.LocalSocketAddress; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.Parcel; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.telephony.PhoneNumberUtils; -import android.telephony.gsm.SmsManager; -import android.telephony.gsm.SmsMessage; -import android.telephony.NeighboringCellInfo; -import android.util.Config; -import android.util.Log; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; - -/** - * {@hide} - */ -class RILRequest -{ - static final String LOG_TAG = "RILJ"; - - //***** Class Variables - static int sNextSerial = 0; - static Object sSerialMonitor = new Object(); - private static Object sPoolSync = new Object(); - private static RILRequest sPool = null; - private static int sPoolSize = 0; - private static final int MAX_POOL_SIZE = 4; - - //***** Instance Variables - int mSerial; - int mRequest; - Message mResult; - Parcel mp; - RILRequest mNext; - - /** - * Retrieves a new RILRequest instance from the pool. - * - * @param request RIL_REQUEST_* - * @param result sent when operation completes - * @return a RILRequest instance from the pool. - */ - static RILRequest obtain(int request, Message result) { - RILRequest rr = null; - - synchronized(sPoolSync) { - if (sPool != null) { - rr = sPool; - sPool = rr.mNext; - rr.mNext = null; - sPoolSize--; - } - } - - if (rr == null) { - rr = new RILRequest(); - } - - synchronized(sSerialMonitor) { - rr.mSerial = sNextSerial++; - } - rr.mRequest = request; - rr.mResult = result; - rr.mp = Parcel.obtain(); - - if (result != null && result.getTarget() == null) { - throw new NullPointerException("Message target must not be null"); - } - - // first elements in any RIL Parcel - rr.mp.writeInt(request); - rr.mp.writeInt(rr.mSerial); - - return rr; - } - - /** - * Returns a RILRequest instance to the pool. - * - * Note: This should only be called once per use. - */ - void release() { - synchronized (sPoolSync) { - if (sPoolSize < MAX_POOL_SIZE) { - this.mNext = sPool; - sPool = this; - sPoolSize++; - } - } - } - - private RILRequest() - { - } - - static void - resetSerial() - { - synchronized(sSerialMonitor) { - sNextSerial = 0; - } - } - - String - serialString() - { - //Cheesy way to do %04d - StringBuilder sb = new StringBuilder(8); - String sn; - - sn = Integer.toString(mSerial); - - //sb.append("J["); - sb.append('['); - for (int i = 0, s = sn.length() ; i < 4 - s; i++) { - sb.append('0'); - } - - sb.append(sn); - sb.append(']'); - return sb.toString(); - } - - void - onError(int error) - { - CommandException ex; - - ex = CommandException.fromRilErrno(error); - - if (RIL.RILJ_LOGD) Log.d(LOG_TAG, serialString() + "< " - + RIL.requestToString(mRequest) - + " error: " + ex); - - if (mResult != null) { - AsyncResult.forMessage(mResult, null, ex); - mResult.sendToTarget(); - } - - if (mp != null) { - mp.recycle(); - mp = null; - } - } -} - - -/** - * RIL implementation of the CommandsInterface. - * FIXME public only for testing - * - * {@hide} - */ -public final class RIL extends BaseCommands implements CommandsInterface -{ - static final String LOG_TAG = "RILJ"; - private static final boolean DBG = false; - static final boolean RILJ_LOGD = Config.LOGD; - static final boolean RILJ_LOGV = DBG ? Config.LOGD : Config.LOGV; - static int WAKE_LOCK_TIMEOUT = 5000; - - //***** Instance Variables - - LocalSocket mSocket; - HandlerThread mSenderThread; - RILSender mSender; - Thread mReceiverThread; - RILReceiver mReceiver; - private Context mContext; - WakeLock mWakeLock; - int mRequestMessagesPending; - - // Is this the first radio state change? - private boolean mInitialRadioStateChange = true; - - //I'd rather this be LinkedList or something - ArrayList mRequestsList = new ArrayList(); - - Object mLastNITZTimeInfo; - - //***** Events - - static final int EVENT_SEND = 1; - static final int EVENT_WAKE_LOCK_TIMEOUT = 2; - - //***** Constants - - // match with constant in ril.cpp - static final int RIL_MAX_COMMAND_BYTES = (8 * 1024); - static final int RESPONSE_SOLICITED = 0; - static final int RESPONSE_UNSOLICITED = 1; - - static final String SOCKET_NAME_RIL = "rild"; - - static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000; - - - BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { - sendScreenState(true); - } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { - sendScreenState(false); - } else { - Log.w(LOG_TAG, "RIL received unexpected Intent: " + intent.getAction()); - } - } - }; - - class RILSender extends Handler implements Runnable - { - public RILSender(Looper looper) { - super(looper); - } - - // Only allocated once - byte[] dataLength = new byte[4]; - - //***** Runnable implementation - public void - run() - { - //setup if needed - } - - - //***** Handler implemementation - - public void - handleMessage(Message msg) - { - RILRequest rr = (RILRequest)(msg.obj); - RILRequest req = null; - - switch (msg.what) { - case EVENT_SEND: - /** - * mRequestMessagePending++ already happened for every - * EVENT_SEND, thus we must make sure - * mRequestMessagePending-- happens once and only once - */ - boolean alreadySubtracted = false; - try { - LocalSocket s; - - s = mSocket; - - if (s == null) { - rr.onError(RADIO_NOT_AVAILABLE); - rr.release(); - mRequestMessagesPending--; - alreadySubtracted = true; - return; - } - - synchronized (mRequestsList) { - mRequestsList.add(rr); - } - - mRequestMessagesPending--; - alreadySubtracted = true; - - byte[] data; - - data = rr.mp.marshall(); - rr.mp.recycle(); - rr.mp = null; - - if (data.length > RIL_MAX_COMMAND_BYTES) { - throw new RuntimeException( - "Parcel larger than max bytes allowed! " - + data.length); - } - - // parcel length in big endian - dataLength[0] = dataLength[1] = 0; - dataLength[2] = (byte)((data.length >> 8) & 0xff); - dataLength[3] = (byte)((data.length) & 0xff); - - //Log.v(LOG_TAG, "writing packet: " + data.length + " bytes"); - - s.getOutputStream().write(dataLength); - s.getOutputStream().write(data); - } catch (IOException ex) { - Log.e(LOG_TAG, "IOException", ex); - req = findAndRemoveRequestFromList(rr.mSerial); - // make sure this request has not already been handled, - // eg, if RILReceiver cleared the list. - if (req != null || !alreadySubtracted) { - rr.onError(RADIO_NOT_AVAILABLE); - rr.release(); - } - } catch (RuntimeException exc) { - Log.e(LOG_TAG, "Uncaught exception ", exc); - req = findAndRemoveRequestFromList(rr.mSerial); - // make sure this request has not already been handled, - // eg, if RILReceiver cleared the list. - if (req != null || !alreadySubtracted) { - rr.onError(GENERIC_FAILURE); - rr.release(); - } - } - - if (!alreadySubtracted) { - mRequestMessagesPending--; - } - - break; - - case EVENT_WAKE_LOCK_TIMEOUT: - // Haven't heard back from the last request. Assume we're - // not getting a response and release the wake lock. - // TODO should we clean up mRequestList and mRequestPending - synchronized (mWakeLock) { - if (mWakeLock.isHeld()) { - if (RILJ_LOGD) { - synchronized (mRequestsList) { - int count = mRequestsList.size(); - Log.d(LOG_TAG, "WAKE_LOCK_TIMEOUT " + - " mReqPending=" + mRequestMessagesPending + - " mRequestList=" + count); - - for (int i = 0; i < count; i++) { - rr = mRequestsList.get(i); - Log.d(LOG_TAG, i + ": [" + rr.mSerial + "] " + - requestToString(rr.mRequest)); - - } - } - } - mWakeLock.release(); - } - } - - break; - } - } - } - - /** - * Reads in a single RIL message off the wire. A RIL message consists - * of a 4-byte little-endian length and a subsequent series of bytes. - * The final message (length header omitted) is read into - * buffer and the length of the final message (less header) - * is returned. A return value of -1 indicates end-of-stream. - * - * @param is non-null; Stream to read from - * @param buffer Buffer to fill in. Must be as large as maximum - * message size, or an ArrayOutOfBounds exception will be thrown. - * @return Length of message less header, or -1 on end of stream. - * @throws IOException - */ - private static int readRilMessage(InputStream is, byte[] buffer) - throws IOException - { - int countRead; - int offset; - int remaining; - int messageLength; - - // First, read in the length of the message - offset = 0; - remaining = 4; - do { - countRead = is.read(buffer, offset, remaining); - - if (countRead < 0 ) { - Log.e(LOG_TAG, "Hit EOS reading message length"); - return -1; - } - - offset += countRead; - remaining -= countRead; - } while (remaining > 0); - - messageLength = ((buffer[0] & 0xff) << 24) - | ((buffer[1] & 0xff) << 16) - | ((buffer[2] & 0xff) << 8) - | (buffer[3] & 0xff); - - // Then, re-use the buffer and read in the message itself - offset = 0; - remaining = messageLength; - do { - countRead = is.read(buffer, offset, remaining); - - if (countRead < 0 ) { - Log.e(LOG_TAG, "Hit EOS reading message. messageLength=" + messageLength - + " remaining=" + remaining); - return -1; - } - - offset += countRead; - remaining -= countRead; - } while (remaining > 0); - - return messageLength; - } - - class RILReceiver implements Runnable - { - byte[] buffer; - - RILReceiver() - { - buffer = new byte[RIL_MAX_COMMAND_BYTES]; - } - - public void - run() - { - int retryCount = 0; - - try {for (;;) { - LocalSocket s = null; - LocalSocketAddress l; - - try { - s = new LocalSocket(); - l = new LocalSocketAddress(SOCKET_NAME_RIL, - LocalSocketAddress.Namespace.RESERVED); - s.connect(l); - } catch (IOException ex){ - try { - if (s != null) { - s.close(); - } - } catch (IOException ex2) { - //ignore failure to close after failure to connect - } - - // don't print an error message after the the first time - // or after the 8th time - - if (retryCount == 8) { - Log.e (LOG_TAG, - "Couldn't find '" + SOCKET_NAME_RIL - + "' socket after " + retryCount - + " times, continuing to retry silently"); - } else if (retryCount > 0 && retryCount < 8) { - Log.i (LOG_TAG, - "Couldn't find '" + SOCKET_NAME_RIL - + "' socket; retrying after timeout"); - } - - try { - Thread.sleep(SOCKET_OPEN_RETRY_MILLIS); - } catch (InterruptedException er) { - } - - retryCount++; - continue; - } - - retryCount = 0; - - mSocket = s; - Log.i(LOG_TAG, "Connected to '" + SOCKET_NAME_RIL + "' socket"); - - int length = 0; - try { - InputStream is = mSocket.getInputStream(); - - for (;;) { - Parcel p; - - length = readRilMessage(is, buffer); - - if (length < 0) { - // End-of-stream reached - break; - } - - p = Parcel.obtain(); - p.unmarshall(buffer, 0, length); - p.setDataPosition(0); - - //Log.v(LOG_TAG, "Read packet: " + length + " bytes"); - - processResponse(p); - p.recycle(); - } - } catch (java.io.IOException ex) { - Log.i(LOG_TAG, "'" + SOCKET_NAME_RIL + "' socket closed", - ex); - } catch (Throwable tr) { - Log.e(LOG_TAG, "Uncaught exception read length=" + length + - "Exception:" + tr.toString()); - } - - Log.i(LOG_TAG, "Disconnected from '" + SOCKET_NAME_RIL - + "' socket"); - - setRadioState (RadioState.RADIO_UNAVAILABLE); - - try { - mSocket.close(); - } catch (IOException ex) { - } - - mSocket = null; - RILRequest.resetSerial(); - - // Clear request list on close - synchronized (mRequestsList) { - for (int i = 0, sz = mRequestsList.size() ; i < sz ; i++) { - RILRequest rr = mRequestsList.get(i); - rr.onError(RADIO_NOT_AVAILABLE); - rr.release(); - } - - mRequestsList.clear(); - } - }} catch (Throwable tr) { - Log.e(LOG_TAG,"Uncaught exception", tr); - } - } - } - - - - //***** Constructor - - public - RIL(Context context) - { - super(context); - - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); - mWakeLock.setReferenceCounted(false); - mRequestMessagesPending = 0; - - mContext = context; - - mSenderThread = new HandlerThread("RILSender"); - mSenderThread.start(); - - Looper looper = mSenderThread.getLooper(); - mSender = new RILSender(looper); - - mReceiver = new RILReceiver(); - mReceiverThread = new Thread(mReceiver, "RILReceiver"); - mReceiverThread.start(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - context.registerReceiver(mIntentReceiver, filter); - } - - //***** CommandsInterface implementation - - @Override public void - setOnNITZTime(Handler h, int what, Object obj) - { - super.setOnNITZTime(h, what, obj); - - // Send the last NITZ time if we have it - if (mLastNITZTimeInfo != null) { - mNITZTimeRegistrant - .notifyRegistrant( - new AsyncResult (null, mLastNITZTimeInfo, null)); - mLastNITZTimeInfo = null; - } - } - - public void - getSimStatus(Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - supplySimPin(String pin, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(1); - rr.mp.writeString(pin); - - send(rr); - } - - public void - supplySimPuk(String puk, String newPin, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(2); - rr.mp.writeString(puk); - rr.mp.writeString(newPin); - - send(rr); - } - - public void - supplySimPin2(String pin, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN2, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(1); - rr.mp.writeString(pin); - - send(rr); - } - - public void - supplySimPuk2(String puk, String newPin2, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK2, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(2); - rr.mp.writeString(puk); - rr.mp.writeString(newPin2); - - send(rr); - } - - public void - changeSimPin(String oldPin, String newPin, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(2); - rr.mp.writeString(oldPin); - rr.mp.writeString(newPin); - - send(rr); - } - - public void - changeSimPin2(String oldPin2, String newPin2, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN2, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(2); - rr.mp.writeString(oldPin2); - rr.mp.writeString(newPin2); - - send(rr); - } - - public void - changeBarringPassword(String facility, String oldPwd, String newPwd, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(3); - rr.mp.writeString(facility); - rr.mp.writeString(oldPwd); - rr.mp.writeString(newPwd); - - send(rr); - } - - public void - supplyNetworkDepersonalization(String netpin, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeInt(1); - rr.mp.writeString(netpin); - - send(rr); - } - - public void - getCurrentCalls (Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getPDPContextList(Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_PDP_CONTEXT_LIST, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - dial (String address, int clirMode, Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result); - - rr.mp.writeString(address); - rr.mp.writeInt(clirMode); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getIMSI(Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> getIMSI:RIL_REQUEST_GET_IMSI " + RIL_REQUEST_GET_IMSI + " " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getIMEI(Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEI, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getIMEISV(Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEISV, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - - public void - hangupConnection (int gsmIndex, Message result) - { - if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex); - - RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + gsmIndex); - - rr.mp.writeInt(1); - rr.mp.writeInt(gsmIndex); - - send(rr); - } - - public void - hangupWaitingOrBackground (Message result) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, - result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - hangupForegroundResumeBackground (Message result) - { - RILRequest rr - = RILRequest.obtain( - RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, - result); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - switchWaitingOrHoldingAndActive (Message result) - { - RILRequest rr - = RILRequest.obtain( - RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, - result); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - conference (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_CONFERENCE, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - - public void - separateConnection (int gsmIndex, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SEPARATE_CONNECTION, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + gsmIndex); - - rr.mp.writeInt(1); - rr.mp.writeInt(gsmIndex); - - send(rr); - } - - public void - acceptCall (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_ANSWER, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - rejectCall (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_UDUB, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - explicitCallTransfer (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getLastCallFailCause (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getLastPdpFailCause (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_LAST_PDP_FAIL_CAUSE, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - setMute (boolean enableMute, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_MUTE, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + enableMute); - - rr.mp.writeInt(1); - rr.mp.writeInt(enableMute ? 1 : 0); - - send(rr); - } - - public void - getMute (Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_GET_MUTE, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getSignalStrength (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SIGNAL_STRENGTH, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getRegistrationState (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_REGISTRATION_STATE, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getGPRSRegistrationState (Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_GPRS_REGISTRATION_STATE, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getOperator(Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_OPERATOR, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - sendDtmf(char c, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_DTMF, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeString(Character.toString(c)); - - send(rr); - } - - public void - startDtmf(char c, Message result) { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_DTMF_START, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeString(Character.toString(c)); - - send(rr); - } - - public void - stopDtmf(Message result) { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_DTMF_STOP, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - - public void - sendSMS (String smscPDU, String pdu, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result); - - rr.mp.writeInt(2); - rr.mp.writeString(smscPDU); - rr.mp.writeString(pdu); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void deleteSmsOnSim(int index, Message response) { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_DELETE_SMS_ON_SIM, - response); - - rr.mp.writeInt(1); - rr.mp.writeInt(index); - - if (RILJ_LOGD) { - riljLog(rr.serialString() + "> " - + requestToString(rr.mRequest) - + " " + index); - } - - send(rr); - } - - public void writeSmsToSim(int status, String smsc, String pdu, Message response) { - status = translateStatus(status); - - RILRequest rr = RILRequest.obtain(RIL_REQUEST_WRITE_SMS_TO_SIM, - response); - - rr.mp.writeInt(status); - rr.mp.writeString(pdu); - rr.mp.writeString(smsc); - - if (RILJ_LOGD) { - riljLog(rr.serialString() + "> " - + requestToString(rr.mRequest) - + " " + status); - } - - send(rr); - } - - /** - * Translates EF_SMS status bits to a status value compatible with - * SMS AT commands. See TS 27.005 3.1. - */ - private int translateStatus(int status) { - switch(status & 0x7) { - case SmsManager.STATUS_ON_SIM_READ: - return 1; - case SmsManager.STATUS_ON_SIM_UNREAD: - return 0; - case SmsManager.STATUS_ON_SIM_SENT: - return 3; - case SmsManager.STATUS_ON_SIM_UNSENT: - return 2; - } - - // Default to READ. - return 1; - } - - public void - setupDefaultPDP(String apn, String user, String password, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SETUP_DEFAULT_PDP, result); - - rr.mp.writeInt(3); - rr.mp.writeString(apn); - rr.mp.writeString(user); - rr.mp.writeString(password); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " - + apn); - - send(rr); - } - - public void - deactivateDefaultPDP(int cid, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DEFAULT_PDP, result); - - rr.mp.writeInt(1); - rr.mp.writeString(Integer.toString(cid)); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + cid); - - send(rr); - } - - public void - setRadioPower(boolean on, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_RADIO_POWER, result); - - rr.mp.writeInt(1); - rr.mp.writeInt(on ? 1 : 0); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - setSuppServiceNotifications(boolean enable, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result); - - rr.mp.writeInt(1); - rr.mp.writeInt(enable ? 1 : 0); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " - + requestToString(rr.mRequest)); - - send(rr); - } - - public void - acknowledgeLastIncomingSMS(boolean success, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SMS_ACKNOWLEDGE, result); - - rr.mp.writeInt(1); - rr.mp.writeInt(success ? 1 : 0); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - simIO (int command, int fileid, String path, int p1, int p2, int p3, - String data, String pin2, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SIM_IO, result); - - rr.mp.writeInt(command); - rr.mp.writeInt(fileid); - rr.mp.writeString(path); - rr.mp.writeInt(p1); - rr.mp.writeInt(p2); - rr.mp.writeInt(p3); - rr.mp.writeString(data); - rr.mp.writeString(pin2); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> simIO: " + requestToString(rr.mRequest) - + " 0x" + Integer.toHexString(command) - + " 0x" + Integer.toHexString(fileid) + " " - + p1 + "," + p2 + "," + p3); - - send(rr); - } - - public void - getCLIR(Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_GET_CLIR, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - setCLIR(int clirMode, Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_CLIR, result); - - // count ints - rr.mp.writeInt(1); - - rr.mp.writeInt(clirMode); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + clirMode); - - send(rr); - } - - public void - queryCallWaiting(int serviceClass, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_WAITING, response); - - rr.mp.writeInt(1); - rr.mp.writeInt(serviceClass); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + serviceClass); - - send(rr); - } - - public void - setCallWaiting(boolean enable, int serviceClass, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_CALL_WAITING, response); - - rr.mp.writeInt(2); - rr.mp.writeInt(enable ? 1 : 0); - rr.mp.writeInt(serviceClass); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + enable + ", " + serviceClass); - - send(rr); - } - - public void - setNetworkSelectionModeAutomatic(Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, - response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - setNetworkSelectionModeManual(String operatorNumeric, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, - response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + operatorNumeric); - - rr.mp.writeString(operatorNumeric); - - send(rr); - } - - public void - getNetworkSelectionMode(Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, - response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - getAvailableNetworks(Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, - response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - setCallForward(int action, int cfReason, int serviceClass, - String number, int timeSeconds, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_CALL_FORWARD, response); - - rr.mp.writeInt(action); - rr.mp.writeInt(cfReason); - rr.mp.writeInt(serviceClass); - rr.mp.writeInt(PhoneNumberUtils.toaFromString(number)); - rr.mp.writeString(number); - rr.mp.writeInt (timeSeconds); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + action + " " + cfReason + " " + serviceClass - + timeSeconds); - - send(rr); - } - - public void - queryCallForwardStatus(int cfReason, int serviceClass, - String number, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, response); - - rr.mp.writeInt(2); // 2 is for query action, not in used anyway - rr.mp.writeInt(cfReason); - rr.mp.writeInt(serviceClass); - rr.mp.writeInt(PhoneNumberUtils.toaFromString(number)); - rr.mp.writeString(number); - rr.mp.writeInt (0); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + cfReason + " " + serviceClass); - - send(rr); - } - - public void - queryCLIP(Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_QUERY_CLIP, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - - public void - getBasebandVersion (Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_BASEBAND_VERSION, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void - queryFacilityLock (String facility, String password, int serviceClass, - Message response) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_QUERY_FACILITY_LOCK, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - // count strings - rr.mp.writeInt(3); - - rr.mp.writeString(facility); - rr.mp.writeString(password); - - rr.mp.writeString(Integer.toString(serviceClass)); - - send(rr); - } - - public void - setFacilityLock (String facility, boolean lockState, String password, - int serviceClass, Message response) - { - String lockString; - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_FACILITY_LOCK, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - // count strings - rr.mp.writeInt(4); - - rr.mp.writeString(facility); - lockString = (lockState)?"1":"0"; - rr.mp.writeString(lockString); - rr.mp.writeString(password); - rr.mp.writeString(Integer.toString(serviceClass)); - - send(rr); - - } - - public void - sendUSSD (String ussdString, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SEND_USSD, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + ussdString); - - rr.mp.writeString(ussdString); - - send(rr); - } - - // inherited javadoc suffices - public void cancelPendingUssd (Message response) { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_CANCEL_USSD, response); - - if (RILJ_LOGD) riljLog(rr.serialString() - + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - - public void resetRadio(Message result) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_RESET_RADIO, result); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - public void invokeOemRilRequestRaw(byte[] data, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_RAW, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + "[" + SimUtils.bytesToHexString(data) + "]"); - - rr.mp.writeByteArray(data); - - send(rr); - - } - - public void invokeOemRilRequestStrings(String[] strings, Message response) - { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_STRINGS, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeStringArray(strings); - - send(rr); - } - - /** - * Assign a specified band for RF configuration. - * - * @param bandMode one of BM_*_BAND - * @param response is callback message - */ - public void setBandMode (int bandMode, Message response) { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_SET_BAND_MODE, response); - - rr.mp.writeInt(1); - rr.mp.writeInt(bandMode); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " " + bandMode); - - send(rr); - } - - /** - * Query the list of band mode supported by RF. - * - * @param response is callback message - * ((AsyncResult)response.obj).result is an int[] with every - * element representing one avialable BM_*_BAND - */ - public void queryAvailableBandMode (Message response) { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, - response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - /** - * {@inheritDoc} - */ - public void sendTerminalResponse(String contents, Message response) { - RILRequest rr = RILRequest.obtain( - RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeString(contents); - send(rr); - } - - /** - * {@inheritDoc} - */ - public void sendEnvelope(String contents, Message response) { - RILRequest rr = RILRequest.obtain( - RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - rr.mp.writeString(contents); - send(rr); - } - - /** - * {@inheritDoc} - */ - public void handleCallSetupRequestFromSim( - boolean accept, Message response) { - - RILRequest rr = RILRequest.obtain( - RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM, - response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - int[] param = new int[1]; - param[0] = accept ? 1 : 0; - rr.mp.writeIntArray(param); - send(rr); - } - - /** - * {@inheritDoc} - */ - public void setPreferredNetworkType(int networkType , Message response) { - RILRequest rr = RILRequest.obtain( - RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, response); - - rr.mp.writeInt(1); - rr.mp.writeInt(networkType); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) - + " : " + networkType); - - send(rr); - } - - /** - * {@inheritDoc} - */ - public void getPreferredNetworkType(Message response) { - RILRequest rr = RILRequest.obtain( - RILConstants.RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - /** - * {@inheritDoc} - */ - public void getNeighboringCids(Message response) { - RILRequest rr = RILRequest.obtain( - RILConstants.RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, response); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - - send(rr); - } - - /** - * {@inheritDoc} - */ - public void setLocationUpdates(boolean enable, Message response) { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_LOCATION_UPDATES, response); - rr.mp.writeInt(1); - rr.mp.writeInt(enable ? 1 : 0); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " - + requestToString(rr.mRequest) + ": " + enable); - - send(rr); - } - - //***** Private Methods - - private void sendScreenState(boolean on) - { - RILRequest rr = RILRequest.obtain(RIL_REQUEST_SCREEN_STATE, null); - rr.mp.writeInt(1); - rr.mp.writeInt(on ? 1 : 0); - - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + ": " + on); - - send(rr); - } - - protected void - onRadioAvailable() - { - // In case screen state was lost (due to process crash), - // this ensures that the RIL knows the correct screen state. - - // TODO: Should query Power Manager and send the actual - // screen state. Just send true for now. - sendScreenState(true); - } - - private void setRadioStateFromRILInt(int state) { - RadioState newState; - - /* RIL_RadioState ril.h */ - switch(state) { - case 0: newState = RadioState.RADIO_OFF; break; - case 1: newState = RadioState.RADIO_UNAVAILABLE; break; - case 2: newState = RadioState.SIM_NOT_READY; break; - case 3: newState = RadioState.SIM_LOCKED_OR_ABSENT; break; - case 4: newState = RadioState.SIM_READY; break; - default: - throw new RuntimeException( - "Unrecognized RIL_RadioState: " +state); - } - - if (mInitialRadioStateChange) { - mInitialRadioStateChange = false; - if (newState.isOn()) { - /* If this is our first notification, make sure the radio - * is powered off. This gets the radio into a known state, - * since it's possible for the phone proc to have restarted - * (eg, if it or the runtime crashed) without the RIL - * and/or radio knowing. - */ - if (RILJ_LOGD) Log.d(LOG_TAG, "Radio ON @ init; reset to OFF"); - setRadioPower(false, null); - return; - } - } - - setRadioState(newState); - } - - /** - * Holds a PARTIAL_WAKE_LOCK whenever - * a) There is outstanding RIL request sent to RIL deamon and no replied - * b) There is a request waiting to be sent out. - * - * There is a WAKE_LOCK_TIMEOUT to release the lock, though it shouldn't - * happen often. - */ - - private void - acquireWakeLock() - { - synchronized (mWakeLock) { - mWakeLock.acquire(); - mRequestMessagesPending++; - - mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); - Message msg = mSender.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); - mSender.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT); - } - } - - private void - releaseWakeLockIfDone() - { - synchronized (mWakeLock) { - if (mWakeLock.isHeld() && - (mRequestMessagesPending == 0) && - (mRequestsList.size() == 0)) { - mSender.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); - mWakeLock.release(); - } - } - } - - private void - send(RILRequest rr) - { - Message msg; - - msg = mSender.obtainMessage(EVENT_SEND, rr); - - acquireWakeLock(); - - msg.sendToTarget(); - } - - private void - processResponse (Parcel p) - { - int type; - - type = p.readInt(); - - if (type == RESPONSE_UNSOLICITED) { - processUnsolicited (p); - } else if (type == RESPONSE_SOLICITED) { - processSolicited (p); - } - - releaseWakeLockIfDone(); - } - - - - private RILRequest findAndRemoveRequestFromList(int serial) - { - synchronized (mRequestsList) { - for (int i = 0, s = mRequestsList.size() ; i < s ; i++) { - RILRequest rr = mRequestsList.get(i); - - if (rr.mSerial == serial) { - mRequestsList.remove(i); - return rr; - } - } - } - - return null; - } - - private void - processSolicited (Parcel p) - { - int serial, error; - boolean found = false; - - serial = p.readInt(); - error = p.readInt(); - - RILRequest rr; - - rr = findAndRemoveRequestFromList(serial); - - if (rr == null) { - Log.w(LOG_TAG, "Unexpected solicited response! sn: " - + serial + " error: " + error); - return; - } - - if (error != 0) { - rr.onError(error); - rr.release(); - return; - } - - Object ret; - - try {switch (rr.mRequest) { -/* - cat libs/telephony/ril_commands.h \ - | egrep "^ *{RIL_" \ - | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/' -*/ - case RIL_REQUEST_GET_SIM_STATUS: ret = responseSimStatus(p); break; - case RIL_REQUEST_ENTER_SIM_PIN: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_SIM_PUK: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_SIM_PIN2: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_SIM_PUK2: ret = responseVoid(p); break; - case RIL_REQUEST_CHANGE_SIM_PIN: ret = responseVoid(p); break; - case RIL_REQUEST_CHANGE_SIM_PIN2: ret = responseVoid(p); break; - case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret = responseVoid(p); break; - case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break; - case RIL_REQUEST_DIAL: ret = responseVoid(p); break; - case RIL_REQUEST_GET_IMSI: ret = responseString(p); break; - case RIL_REQUEST_HANGUP: ret = responseVoid(p); break; - case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret = responseVoid(p); break; - case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: ret = responseVoid(p); break; - case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: ret = responseVoid(p); break; - case RIL_REQUEST_CONFERENCE: ret = responseVoid(p); break; - case RIL_REQUEST_UDUB: ret = responseVoid(p); break; - case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseInts(p); break; - case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseInts(p); break; - case RIL_REQUEST_REGISTRATION_STATE: ret = responseStrings(p); break; - case RIL_REQUEST_GPRS_REGISTRATION_STATE: ret = responseStrings(p); break; - case RIL_REQUEST_OPERATOR: ret = responseStrings(p); break; - case RIL_REQUEST_RADIO_POWER: ret = responseVoid(p); break; - case RIL_REQUEST_DTMF: ret = responseVoid(p); break; - case RIL_REQUEST_SEND_SMS: ret = responseSMS(p); break; - case RIL_REQUEST_SEND_SMS_EXPECT_MORE: ret = responseSMS(p); break; - case RIL_REQUEST_SETUP_DEFAULT_PDP: ret = responseStrings(p); break; - case RIL_REQUEST_SIM_IO: ret = responseSIM_IO(p); break; - case RIL_REQUEST_SEND_USSD: ret = responseVoid(p); break; - case RIL_REQUEST_CANCEL_USSD: ret = responseVoid(p); break; - case RIL_REQUEST_GET_CLIR: ret = responseInts(p); break; - case RIL_REQUEST_SET_CLIR: ret = responseVoid(p); break; - case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: ret = responseCallForward(p); break; - case RIL_REQUEST_SET_CALL_FORWARD: ret = responseVoid(p); break; - case RIL_REQUEST_QUERY_CALL_WAITING: ret = responseInts(p); break; - case RIL_REQUEST_SET_CALL_WAITING: ret = responseVoid(p); break; - case RIL_REQUEST_SMS_ACKNOWLEDGE: ret = responseVoid(p); break; - case RIL_REQUEST_GET_IMEI: ret = responseString(p); break; - case RIL_REQUEST_GET_IMEISV: ret = responseString(p); break; - case RIL_REQUEST_ANSWER: ret = responseVoid(p); break; - case RIL_REQUEST_DEACTIVATE_DEFAULT_PDP: ret = responseVoid(p); break; - case RIL_REQUEST_QUERY_FACILITY_LOCK: ret = responseInts(p); break; - case RIL_REQUEST_SET_FACILITY_LOCK: ret = responseVoid(p); break; - case RIL_REQUEST_CHANGE_BARRING_PASSWORD: ret = responseVoid(p); break; - case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret = responseInts(p); break; - case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret = responseVoid(p); break; - case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: ret = responseVoid(p); break; - case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret = responseNetworkInfos(p); break; - case RIL_REQUEST_DTMF_START: ret = responseVoid(p); break; - case RIL_REQUEST_DTMF_STOP: ret = responseVoid(p); break; - case RIL_REQUEST_BASEBAND_VERSION: ret = responseString(p); break; - case RIL_REQUEST_SEPARATE_CONNECTION: ret = responseVoid(p); break; - case RIL_REQUEST_SET_MUTE: ret =responseVoid(p); break; - case RIL_REQUEST_GET_MUTE: ret = responseInts(p); break; - case RIL_REQUEST_QUERY_CLIP: ret = responseInts(p); break; - case RIL_REQUEST_LAST_PDP_FAIL_CAUSE: ret = responseInts(p); break; - case RIL_REQUEST_PDP_CONTEXT_LIST: ret = responseContextList(p); break; - case RIL_REQUEST_RESET_RADIO: ret = responseVoid(p); break; - case RIL_REQUEST_OEM_HOOK_RAW: ret = responseRaw(p); break; - case RIL_REQUEST_OEM_HOOK_STRINGS: ret = responseStrings(p); break; - case RIL_REQUEST_SCREEN_STATE: ret = responseVoid(p); break; - case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: ret = responseVoid(p); break; - case RIL_REQUEST_WRITE_SMS_TO_SIM: ret = responseInts(p); break; - case RIL_REQUEST_DELETE_SMS_ON_SIM: ret = responseVoid(p); break; - case RIL_REQUEST_SET_BAND_MODE: ret = responseVoid(p); break; - case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: ret = responseInts(p); break; - case RIL_REQUEST_STK_GET_PROFILE: ret = responseString(p); break; - case RIL_REQUEST_STK_SET_PROFILE: ret = responseVoid(p); break; - case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: ret = responseString(p); break; - case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: ret = responseVoid(p); break; - case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret = responseInts(p); break; - case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break; - case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break; - case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseInts(p); break; - case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break; - case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break; - - default: - throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest); - //break; - }} catch (Throwable tr) { - // Exceptions here usually mean invalid RIL responses - - Log.w(LOG_TAG, rr.serialString() + "< " - + requestToString(rr.mRequest) + " exception, possible invalid RIL response", tr); - - if (rr.mResult != null) { - AsyncResult.forMessage(rr.mResult, null, tr); - rr.mResult.sendToTarget(); - } - rr.release(); - return; - } - - if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) - + " " + retToString(rr.mRequest, ret)); - - if (rr.mResult != null) { - AsyncResult.forMessage(rr.mResult, ret, null); - rr.mResult.sendToTarget(); - } - - rr.release(); - } - - private String - retToString(int req, Object ret) - { - if (ret == null) return ""; - switch (req) { - // Don't log these return values, for privacy's sake. - case RIL_REQUEST_GET_IMSI: - case RIL_REQUEST_GET_IMEI: - case RIL_REQUEST_GET_IMEISV: - return ""; - } - - StringBuilder sb; - String s; - int length; - if (ret instanceof int[]){ - int[] intArray = (int[]) ret; - length = intArray.length; - sb = new StringBuilder("{"); - if (length > 0) { - int i = 0; - sb.append(intArray[i++]); - while ( i < length) { - sb.append(", ").append(intArray[i++]); - } - } - sb.append("}"); - s = sb.toString(); - } else if (ret instanceof String[]) { - String[] strings = (String[]) ret; - length = strings.length; - sb = new StringBuilder("{"); - if (length > 0) { - int i = 0; - sb.append(strings[i++]); - while ( i < length) { - sb.append(", ").append(strings[i++]); - } - } - sb.append("}"); - s = sb.toString(); - }else if (req == RIL_REQUEST_GET_CURRENT_CALLS) { - ArrayList calls = (ArrayList) ret; - sb = new StringBuilder(" "); - for (DriverCall dc : calls) { - sb.append("[").append(dc).append("] "); - } - s = sb.toString(); - } else if (req == RIL_REQUEST_GET_NEIGHBORING_CELL_IDS) { - ArrayList cells; - cells = (ArrayList) ret; - sb = new StringBuilder(" "); - for (NeighboringCellInfo cell : cells) { - sb.append(cell).append(" "); - } - s = sb.toString(); - } else { - s = ret.toString(); - } - return s; - } - - private void - processUnsolicited (Parcel p) - { - int response; - Object ret; - - response = p.readInt(); - - try {switch(response) { -/* - cat libs/telephony/ril_unsol_commands.h \ - | egrep "^ *{RIL_" \ - | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/' -*/ - - case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret = responseVoid(p); break; - case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break; - case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: ret = responseVoid(p); break; - case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break; - case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret = responseString(p); break; - case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break; - case RIL_UNSOL_ON_USSD: ret = responseStrings(p); break; - case RIL_UNSOL_NITZ_TIME_RECEIVED: ret = responseString(p); break; - case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseInts(p); break; - case RIL_UNSOL_PDP_CONTEXT_LIST_CHANGED: ret = responseContextList(p);break; - case RIL_UNSOL_SUPP_SVC_NOTIFICATION: ret = responseSuppServiceNotification(p); break; - case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break; - case RIL_UNSOL_STK_PROACTIVE_COMMAND: ret = responseString(p); break; - case RIL_UNSOL_STK_EVENT_NOTIFY: ret = responseString(p); break; - case RIL_UNSOL_STK_CALL_SETUP: ret = responseInts(p); break; - case RIL_UNSOL_SIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; - case RIL_UNSOL_SIM_REFRESH: ret = responseInts(p); break; - case RIL_UNSOL_CALL_RING: ret = responseVoid(p); break; - case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break; - default: - throw new RuntimeException("Unrecognized unsol response: " + response); - //break; (implied) - }} catch (Throwable tr) { - Log.e(LOG_TAG, "Exception processing unsol response: " + response + - "Exception:" + tr.toString()); - return; - } - - switch(response) { - case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: - /* has bonus radio state int */ - setRadioStateFromRILInt(p.readInt()); - - if (RILJ_LOGD) unsljLogMore(response, mState.toString()); - break; - case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: - if (RILJ_LOGD) unsljLog(response); - - mCallStateRegistrants - .notifyRegistrants(new AsyncResult(null, null, null)); - break; - case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: - if (RILJ_LOGD) unsljLog(response); - - mNetworkStateRegistrants - .notifyRegistrants(new AsyncResult(null, null, null)); - break; - case RIL_UNSOL_RESPONSE_NEW_SMS: { - if (RILJ_LOGD) unsljLog(response); - - // FIXME this should move up a layer - String a[] = new String[2]; - - a[1] = (String)ret; - - SmsMessage sms; - - sms = SmsMessage.newFromCMT(a); - if (mSMSRegistrant != null) { - mSMSRegistrant - .notifyRegistrant(new AsyncResult(null, sms, null)); - } - break; - } - case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: - if (RILJ_LOGD) unsljLogRet(response, ret); - - if (mSmsStatusRegistrant != null) { - mSmsStatusRegistrant.notifyRegistrant( - new AsyncResult(null, ret, null)); - } - break; - case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: - if (RILJ_LOGD) unsljLogRet(response, ret); - - int[] smsIndex = (int[])ret; - - if(smsIndex.length == 1) { - if (mSmsOnSimRegistrant != null) { - mSmsOnSimRegistrant. - notifyRegistrant(new AsyncResult(null, smsIndex, null)); - } - } else { - if (RILJ_LOGD) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length " - + smsIndex.length); - } - break; - case RIL_UNSOL_ON_USSD: - String[] resp = (String[])ret; - - if (resp.length < 2) { - resp = new String[2]; - resp[0] = ((String[])ret)[0]; - resp[1] = null; - } - if (RILJ_LOGD) unsljLogMore(response, resp[0]); - if (mUSSDRegistrant != null) { - mUSSDRegistrant.notifyRegistrant( - new AsyncResult (null, resp, null)); - } - break; - case RIL_UNSOL_NITZ_TIME_RECEIVED: - if (RILJ_LOGD) unsljLogRet(response, ret); - - // has bonus long containing milliseconds since boot that the NITZ - // time was received - long nitzReceiveTime = p.readLong(); - - Object[] result = new Object[2]; - - result[0] = ret; - result[1] = Long.valueOf(nitzReceiveTime); - - if (mNITZTimeRegistrant != null) { - - mNITZTimeRegistrant - .notifyRegistrant(new AsyncResult (null, result, null)); - } else { - // in case NITZ time registrant isnt registered yet - mLastNITZTimeInfo = result; - } - break; - - case RIL_UNSOL_SIGNAL_STRENGTH: - // Note this is set to "verbose" because it happens - // frequently - if (RILJ_LOGV) unsljLogvRet(response, ret); - - if (mSignalStrengthRegistrant != null) { - mSignalStrengthRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - break; - case RIL_UNSOL_PDP_CONTEXT_LIST_CHANGED: - if (RILJ_LOGD) unsljLogRet(response, ret); - - mPDPRegistrants - .notifyRegistrants(new AsyncResult(null, ret, null)); - break; - - case RIL_UNSOL_SUPP_SVC_NOTIFICATION: - if (RILJ_LOGD) unsljLogRet(response, ret); - - if (mSsnRegistrant != null) { - mSsnRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - break; - - case RIL_UNSOL_STK_SESSION_END: - if (RILJ_LOGD) unsljLog(response); - - if (mStkSessionEndRegistrant != null) { - mStkSessionEndRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - break; - - case RIL_UNSOL_STK_PROACTIVE_COMMAND: - if (RILJ_LOGD) unsljLogRet(response, ret); - - if (mStkProCmdRegistrant != null) { - mStkProCmdRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - break; - - case RIL_UNSOL_STK_EVENT_NOTIFY: - if (RILJ_LOGD) unsljLogRet(response, ret); - - if (mStkEventRegistrant != null) { - mStkEventRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - break; - - case RIL_UNSOL_STK_CALL_SETUP: - if (RILJ_LOGD) unsljLogRet(response, ret); - - if (mStkCallSetUpRegistrant != null) { - mStkCallSetUpRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - break; - - case RIL_UNSOL_SIM_SMS_STORAGE_FULL: - if (RILJ_LOGD) unsljLog(response); - - if (mSimSmsFullRegistrant != null) { - mSimSmsFullRegistrant.notifyRegistrant(); - } - break; - - case RIL_UNSOL_SIM_REFRESH: - if (RILJ_LOGD) unsljLogRet(response, ret); - - if (mSimRefreshRegistrant != null) { - mSimRefreshRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - break; - - case RIL_UNSOL_CALL_RING: - if (RILJ_LOGD) unsljLog(response); - - if (mRingRegistrant != null) { - mRingRegistrant.notifyRegistrant(); - } - break; - - case RIL_UNSOL_RESTRICTED_STATE_CHANGED: - if (RILJ_LOGD) unsljLogvRet(response, ret); - if (mRestrictedStateRegistrant != null) { - mRestrictedStateRegistrant.notifyRegistrant( - new AsyncResult (null, ret, null)); - } - } - } - - private Object - responseInts(Parcel p) - { - int numInts; - int response[]; - - numInts = p.readInt(); - - response = new int[numInts]; - - for (int i = 0 ; i < numInts ; i++) { - response[i] = p.readInt(); - } - - return response; - } - - - private Object - responseVoid(Parcel p) - { - return null; - } - - private Object - responseCallForward(Parcel p) - { - int numInfos; - CallForwardInfo infos[]; - - numInfos = p.readInt(); - - infos = new CallForwardInfo[numInfos]; - - for (int i = 0 ; i < numInfos ; i++) { - infos[i] = new CallForwardInfo(); - - infos[i].status = p.readInt(); - infos[i].reason = p.readInt(); - infos[i].serviceClass = p.readInt(); - infos[i].toa = p.readInt(); - infos[i].number = p.readString(); - infos[i].timeSeconds = p.readInt(); - } - - return infos; - } - - private Object - responseSuppServiceNotification(Parcel p) - { - SuppServiceNotification notification = new SuppServiceNotification(); - - notification.notificationType = p.readInt(); - notification.code = p.readInt(); - notification.index = p.readInt(); - notification.type = p.readInt(); - notification.number = p.readString(); - - return notification; - } - - private Object - responseString(Parcel p) - { - String response; - - response = p.readString(); - - return response; - } - - private Object - responseStrings(Parcel p) - { - int num; - String response[]; - - response = p.readStringArray(); - - if (false) { - num = p.readInt(); - - response = new String[num]; - for (int i = 0; i < num; i++) { - response[i] = p.readString(); - } - } - - return response; - } - - private Object - responseRaw(Parcel p) - { - int num; - byte response[]; - - response = p.createByteArray(); - - return response; - } - - private Object - responseSMS(Parcel p) - { - int messageRef; - String ackPDU; - - messageRef = p.readInt(); - ackPDU = p.readString(); - - SmsResponse response = new SmsResponse(messageRef, ackPDU); - - return response; - } - - - private Object - responseSIM_IO(Parcel p) - { - int sw1, sw2; - byte data[] = null; - Message ret; - - sw1 = p.readInt(); - sw2 = p.readInt(); - - String s = p.readString(); - - return new SimIoResult(sw1, sw2, s); - } - - private Object - responseSimStatus(Parcel p) - { - int status; - - status = ((int[])responseInts(p))[0]; - switch (status){ - case RIL_SIM_ABSENT: return SimStatus.SIM_ABSENT; - case RIL_SIM_NOT_READY: return SimStatus.SIM_NOT_READY; - case RIL_SIM_READY: return SimStatus.SIM_READY; - case RIL_SIM_PIN: return SimStatus.SIM_PIN; - case RIL_SIM_PUK: return SimStatus.SIM_PUK; - case RIL_SIM_NETWORK_PERSONALIZATION: - return SimStatus.SIM_NETWORK_PERSONALIZATION; - default: - // Unrecognized SIM status. Treat it like a missing SIM. - Log.e(LOG_TAG, "Unrecognized RIL_REQUEST_GET_SIM_STATUS result: " + status); - return SimStatus.SIM_ABSENT; - } - } - - - private Object - responseCallList(Parcel p) - { - int num; - ArrayList response; - DriverCall dc; - - num = p.readInt(); - response = new ArrayList(num); - - for (int i = 0 ; i < num ; i++) { - dc = new DriverCall(); - - dc.state = DriverCall.stateFromCLCC(p.readInt()); - dc.index = p.readInt(); - dc.TOA = p.readInt(); - dc.isMpty = (0 != p.readInt()); - dc.isMT = (0 != p.readInt()); - dc.als = p.readInt(); - dc.isVoice = (0 == p.readInt()) ? false : true; - dc.number = p.readString(); - dc.numberPresentation = DriverCall.presentationFromCLIP(p.readInt()); - - // Make sure there's a leading + on addresses with a TOA - // of 145 - - dc.number = PhoneNumberUtils.stringFromStringAndTOA( - dc.number, dc.TOA); - - response.add(dc); - } - - Collections.sort(response); - - return response; - } - - private Object - responseContextList(Parcel p) - { - int num; - ArrayList response; - - num = p.readInt(); - response = new ArrayList(num); - - for (int i = 0; i < num; i++) { - PDPContextState pdp = new PDPContextState(); - - pdp.cid = p.readInt(); - pdp.active = p.readInt() == 0 ? false : true; - pdp.type = p.readString(); - pdp.apn = p.readString(); - pdp.address = p.readString(); - - response.add(pdp); - } - - return response; - } - - private Object - responseNetworkInfos(Parcel p) - { - String strings[] = (String [])responseStrings(p); - ArrayList ret; - - if (strings.length % 4 != 0) { - throw new RuntimeException( - "RIL_REQUEST_QUERY_AVAILABLE_NETWORKS: invalid response. Got " - + strings.length + " strings, expected multible of 4"); - } - - ret = new ArrayList(strings.length / 4); - - for (int i = 0 ; i < strings.length ; i += 4) { - ret.add ( - new NetworkInfo( - strings[i+0], - strings[i+1], - strings[i+2], - strings[i+3])); - } - - return ret; - } - - private Object - responseCellList(Parcel p) - { - int num; - ArrayList response; - NeighboringCellInfo cell; - - num = p.readInt(); - response = new ArrayList(num); - - for (int i = 0 ; i < num ; i++) { - try { - int rssi = p.readInt(); - int cid = Integer.valueOf(p.readString(), 16); - cell = new NeighboringCellInfo(rssi, cid); - response.add(cell); - } catch ( Exception e) { - } - } - - return response; - } - - - static String - requestToString(int request) - { -/* - cat libs/telephony/ril_commands.h \ - | egrep "^ *{RIL_" \ - | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/' -*/ - switch(request) { - case RIL_REQUEST_GET_SIM_STATUS: return "GET_SIM_STATUS"; - case RIL_REQUEST_ENTER_SIM_PIN: return "ENTER_SIM_PIN"; - case RIL_REQUEST_ENTER_SIM_PUK: return "ENTER_SIM_PUK"; - case RIL_REQUEST_ENTER_SIM_PIN2: return "ENTER_SIM_PIN2"; - case RIL_REQUEST_ENTER_SIM_PUK2: return "ENTER_SIM_PUK2"; - case RIL_REQUEST_CHANGE_SIM_PIN: return "CHANGE_SIM_PIN"; - case RIL_REQUEST_CHANGE_SIM_PIN2: return "CHANGE_SIM_PIN2"; - case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: return "ENTER_NETWORK_DEPERSONALIZATION"; - case RIL_REQUEST_GET_CURRENT_CALLS: return "GET_CURRENT_CALLS"; - case RIL_REQUEST_DIAL: return "DIAL"; - case RIL_REQUEST_GET_IMSI: return "GET_IMSI"; - case RIL_REQUEST_HANGUP: return "HANGUP"; - case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "HANGUP_WAITING_OR_BACKGROUND"; - case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "HANGUP_FOREGROUND_RESUME_BACKGROUND"; - case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: return "REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE"; - case RIL_REQUEST_CONFERENCE: return "CONFERENCE"; - case RIL_REQUEST_UDUB: return "UDUB"; - case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "LAST_CALL_FAIL_CAUSE"; - case RIL_REQUEST_SIGNAL_STRENGTH: return "SIGNAL_STRENGTH"; - case RIL_REQUEST_REGISTRATION_STATE: return "REGISTRATION_STATE"; - case RIL_REQUEST_GPRS_REGISTRATION_STATE: return "GPRS_REGISTRATION_STATE"; - case RIL_REQUEST_OPERATOR: return "OPERATOR"; - case RIL_REQUEST_RADIO_POWER: return "RADIO_POWER"; - case RIL_REQUEST_DTMF: return "DTMF"; - case RIL_REQUEST_SEND_SMS: return "SEND_SMS"; - case RIL_REQUEST_SEND_SMS_EXPECT_MORE: return "SEND_SMS_EXPECT_MORE"; - case RIL_REQUEST_SETUP_DEFAULT_PDP: return "SETUP_DEFAULT_PDP"; - case RIL_REQUEST_SIM_IO: return "SIM_IO"; - case RIL_REQUEST_SEND_USSD: return "SEND_USSD"; - case RIL_REQUEST_CANCEL_USSD: return "CANCEL_USSD"; - case RIL_REQUEST_GET_CLIR: return "GET_CLIR"; - case RIL_REQUEST_SET_CLIR: return "SET_CLIR"; - case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: return "QUERY_CALL_FORWARD_STATUS"; - case RIL_REQUEST_SET_CALL_FORWARD: return "SET_CALL_FORWARD"; - case RIL_REQUEST_QUERY_CALL_WAITING: return "QUERY_CALL_WAITING"; - case RIL_REQUEST_SET_CALL_WAITING: return "SET_CALL_WAITING"; - case RIL_REQUEST_SMS_ACKNOWLEDGE: return "SMS_ACKNOWLEDGE"; - case RIL_REQUEST_GET_IMEI: return "GET_IMEI"; - case RIL_REQUEST_GET_IMEISV: return "GET_IMEISV"; - case RIL_REQUEST_ANSWER: return "ANSWER"; - case RIL_REQUEST_DEACTIVATE_DEFAULT_PDP: return "DEACTIVATE_DEFAULT_PDP"; - case RIL_REQUEST_QUERY_FACILITY_LOCK: return "QUERY_FACILITY_LOCK"; - case RIL_REQUEST_SET_FACILITY_LOCK: return "SET_FACILITY_LOCK"; - case RIL_REQUEST_CHANGE_BARRING_PASSWORD: return "CHANGE_BARRING_PASSWORD"; - case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: return "QUERY_NETWORK_SELECTION_MODE"; - case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: return "SET_NETWORK_SELECTION_AUTOMATIC"; - case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: return "SET_NETWORK_SELECTION_MANUAL"; - case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : return "QUERY_AVAILABLE_NETWORKS "; - case RIL_REQUEST_DTMF_START: return "DTMF_START"; - case RIL_REQUEST_DTMF_STOP: return "DTMF_STOP"; - case RIL_REQUEST_BASEBAND_VERSION: return "BASEBAND_VERSION"; - case RIL_REQUEST_SEPARATE_CONNECTION: return "SEPARATE_CONNECTION"; - case RIL_REQUEST_SET_MUTE: return "SET_MUTE"; - case RIL_REQUEST_GET_MUTE: return "GET_MUTE"; - case RIL_REQUEST_QUERY_CLIP: return "QUERY_CLIP"; - case RIL_REQUEST_LAST_PDP_FAIL_CAUSE: return "LAST_PDP_FAIL_CAUSE"; - case RIL_REQUEST_PDP_CONTEXT_LIST: return "PDP_CONTEXT_LIST"; - case RIL_REQUEST_RESET_RADIO: return "RESET_RADIO"; - case RIL_REQUEST_OEM_HOOK_RAW: return "OEM_HOOK_RAW"; - case RIL_REQUEST_OEM_HOOK_STRINGS: return "OEM_HOOK_STRINGS"; - case RIL_REQUEST_SCREEN_STATE: return "SCREEN_STATE"; - case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: return "SET_SUPP_SVC_NOTIFICATION"; - case RIL_REQUEST_WRITE_SMS_TO_SIM: return "WRITE_SMS_TO_SIM"; - case RIL_REQUEST_DELETE_SMS_ON_SIM: return "DELETE_SMS_ON_SIM"; - case RIL_REQUEST_SET_BAND_MODE: return "SET_BAND_MODE"; - case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: return "QUERY_AVAILABLE_BAND_MODE"; - case RIL_REQUEST_STK_GET_PROFILE: return "REQUEST_STK_GET_PROFILE"; - case RIL_REQUEST_STK_SET_PROFILE: return "REQUEST_STK_SET_PROFILE"; - case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "REQUEST_STK_SEND_ENVELOPE_COMMAND"; - case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "REQUEST_STK_SEND_TERMINAL_RESPONSE"; - case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM"; - case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "REQUEST_EXPLICIT_CALL_TRANSFER"; - case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "REQUEST_SET_PREFERRED_NETWORK_TYPE"; - case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "REQUEST_GET_PREFERRED_NETWORK_TYPE"; - case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "REQUEST_GET_NEIGHBORING_CELL_IDS"; - case RIL_REQUEST_SET_LOCATION_UPDATES: return "REQUEST_SET_LOCATION_UPDATES"; - default: return ""; - } - } - - static String - responseToString(int request) - { -/* - cat libs/telephony/ril_unsol_commands.h \ - | egrep "^ *{RIL_" \ - | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/' -*/ - switch(request) { - case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED"; - case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED"; - case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_NETWORK_STATE_CHANGED"; - case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS"; - case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT"; - case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM"; - case RIL_UNSOL_ON_USSD: return "UNSOL_ON_USSD"; - case RIL_UNSOL_ON_USSD_REQUEST: return "UNSOL_ON_USSD_REQUEST"; - case RIL_UNSOL_NITZ_TIME_RECEIVED: return "UNSOL_NITZ_TIME_RECEIVED"; - case RIL_UNSOL_SIGNAL_STRENGTH: return "UNSOL_SIGNAL_STRENGTH"; - case RIL_UNSOL_PDP_CONTEXT_LIST_CHANGED: return "UNSOL_PDP_CONTEXT_LIST_CHANGED"; - case RIL_UNSOL_SUPP_SVC_NOTIFICATION: return "UNSOL_SUPP_SVC_NOTIFICATION"; - case RIL_UNSOL_STK_SESSION_END: return "UNSOL_STK_SESSION_END"; - case RIL_UNSOL_STK_PROACTIVE_COMMAND: return "UNSOL_STK_PROACTIVE_COMMAND"; - case RIL_UNSOL_STK_EVENT_NOTIFY: return "UNSOL_STK_EVENT_NOTIFY"; - case RIL_UNSOL_STK_CALL_SETUP: return "UNSOL_STK_CALL_SETUP"; - case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FULL"; - case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH"; - case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING"; - case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "RIL_UNSOL_RESTRICTED_STATE_CHANGED"; - default: return ""; - } - } - - private void riljLog(String msg) { - Log.d(LOG_TAG, msg); - } - - private void riljLogv(String msg) { - Log.v(LOG_TAG, msg); - } - - private void unsljLog(int response) { - riljLog("[UNSL]< " + responseToString(response)); - } - - private void unsljLogMore(int response, String more) { - riljLog("[UNSL]< " + responseToString(response) + " " + more); - } - - private void unsljLogRet(int response, Object ret) { - riljLog("[UNSL]< " + responseToString(response) + " " + retToString(response, ret)); - } - - private void unsljLogvRet(int response, Object ret) { - riljLogv("[UNSL]< " + responseToString(response) + " " + retToString(response, ret)); - } - -} diff --git a/telephony/java/com/android/internal/telephony/gsm/RILConstants.java b/telephony/java/com/android/internal/telephony/gsm/RILConstants.java deleted file mode 100644 index 4463b20..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/RILConstants.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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.gsm; - - -/** - * {@hide} - */ -interface RILConstants -{ - // From the top of ril.cpp - int RIL_ERRNO_INVALID_RESPONSE = -1; - - // from RIL_Errno - int SUCCESS = 0; - int RADIO_NOT_AVAILABLE = 1; /* If radio did not start or is resetting */ - int GENERIC_FAILURE = 2; - int PASSWORD_INCORRECT = 3; /* for PIN/PIN2 methods only! */ - int SIM_PIN2 = 4; /* Operation requires SIM PIN2 to be entered */ - int SIM_PUK2 = 5; /* Operation requires SIM PIN2 to be entered */ - int REQUEST_NOT_SUPPORTED = 6; - int REQUEST_CANCELLED = 7; - int OP_NOT_ALLOWED_DURING_VOICE_CALL = 8; /* data operation is not allowed during voice call in class C */ - int OP_NOT_ALLOWED_BEFORE_REG_NW = 9; /* request is not allowed before device registers to network */ - int SMS_SEND_FAIL_RETRY = 10; /* send sms fail and need retry */ -/* -cat include/telephony/ril.h | \ - egrep '^#define' | \ - sed -re 's/^#define +([^ ]+)* +([^ ]+)/ int \1 = \2;/' \ - >>java/android/com.android.internal.telephony/gsm/RILConstants.java -*/ - - - int RIL_SIM_ABSENT = 0; - int RIL_SIM_NOT_READY = 1; - int RIL_SIM_READY = 2; - int RIL_SIM_PIN = 3; - int RIL_SIM_PUK = 4; - int RIL_SIM_NETWORK_PERSONALIZATION = 5; - - /** - * No restriction at all including voice/SMS/USSD/SS/AV64 - * and packet data. - */ - int RIL_RESTRICTED_STATE_NONE = 0x00; - /** - * Block emergency call due to restriction. - * But allow all normal voice/SMS/USSD/SS/AV64. - */ - int RIL_RESTRICTED_STATE_CS_EMERGENCY = 0x01; - /** - * Block all normal voice/SMS/USSD/SS/AV64 due to restriction. - * Only Emergency call allowed. - */ - int RIL_RESTRICTED_STATE_CS_NORMAL = 0x02; - /** - * Block all voice/SMS/USSD/SS/AV64 - * including emergency call due to restriction. - */ - int RIL_RESTRICTED_STATE_CS_ALL = 0x04; - /** - * Block packet data access due to restriction. - */ - int RIL_RESTRICTED_STATE_PS_ALL = 0x10; - - int RIL_REQUEST_GET_SIM_STATUS = 1; - int RIL_REQUEST_ENTER_SIM_PIN = 2; - int RIL_REQUEST_ENTER_SIM_PUK = 3; - int RIL_REQUEST_ENTER_SIM_PIN2 = 4; - int RIL_REQUEST_ENTER_SIM_PUK2 = 5; - int RIL_REQUEST_CHANGE_SIM_PIN = 6; - int RIL_REQUEST_CHANGE_SIM_PIN2 = 7; - int RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION = 8; - int RIL_REQUEST_GET_CURRENT_CALLS = 9; - int RIL_REQUEST_DIAL = 10; - int RIL_REQUEST_GET_IMSI = 11; - int RIL_REQUEST_HANGUP = 12; - int RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND = 13; - int RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND = 14; - int RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE = 15; - int RIL_REQUEST_CONFERENCE = 16; - int RIL_REQUEST_UDUB = 17; - int RIL_REQUEST_LAST_CALL_FAIL_CAUSE = 18; - int RIL_REQUEST_SIGNAL_STRENGTH = 19; - int RIL_REQUEST_REGISTRATION_STATE = 20; - int RIL_REQUEST_GPRS_REGISTRATION_STATE = 21; - int RIL_REQUEST_OPERATOR = 22; - int RIL_REQUEST_RADIO_POWER = 23; - int RIL_REQUEST_DTMF = 24; - int RIL_REQUEST_SEND_SMS = 25; - int RIL_REQUEST_SEND_SMS_EXPECT_MORE = 26; - int RIL_REQUEST_SETUP_DEFAULT_PDP = 27; - int RIL_REQUEST_SIM_IO = 28; - int RIL_REQUEST_SEND_USSD = 29; - int RIL_REQUEST_CANCEL_USSD = 30; - int RIL_REQUEST_GET_CLIR = 31; - int RIL_REQUEST_SET_CLIR = 32; - int RIL_REQUEST_QUERY_CALL_FORWARD_STATUS = 33; - int RIL_REQUEST_SET_CALL_FORWARD = 34; - int RIL_REQUEST_QUERY_CALL_WAITING = 35; - int RIL_REQUEST_SET_CALL_WAITING = 36; - int RIL_REQUEST_SMS_ACKNOWLEDGE = 37; - int RIL_REQUEST_GET_IMEI = 38; - int RIL_REQUEST_GET_IMEISV = 39; - int RIL_REQUEST_ANSWER = 40; - int RIL_REQUEST_DEACTIVATE_DEFAULT_PDP = 41; - int RIL_REQUEST_QUERY_FACILITY_LOCK = 42; - int RIL_REQUEST_SET_FACILITY_LOCK = 43; - int RIL_REQUEST_CHANGE_BARRING_PASSWORD = 44; - int RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE = 45; - int RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC = 46; - int RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL = 47; - int RIL_REQUEST_QUERY_AVAILABLE_NETWORKS = 48; - int RIL_REQUEST_DTMF_START = 49; - int RIL_REQUEST_DTMF_STOP = 50; - int RIL_REQUEST_BASEBAND_VERSION = 51; - int RIL_REQUEST_SEPARATE_CONNECTION = 52; - int RIL_REQUEST_SET_MUTE = 53; - int RIL_REQUEST_GET_MUTE = 54; - int RIL_REQUEST_QUERY_CLIP = 55; - int RIL_REQUEST_LAST_PDP_FAIL_CAUSE = 56; - int RIL_REQUEST_PDP_CONTEXT_LIST = 57; - int RIL_REQUEST_RESET_RADIO = 58; - int RIL_REQUEST_OEM_HOOK_RAW = 59; - int RIL_REQUEST_OEM_HOOK_STRINGS = 60; - int RIL_REQUEST_SCREEN_STATE = 61; - int RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION = 62; - int RIL_REQUEST_WRITE_SMS_TO_SIM = 63; - int RIL_REQUEST_DELETE_SMS_ON_SIM = 64; - int RIL_REQUEST_SET_BAND_MODE = 65; - int RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE = 66; - int RIL_REQUEST_STK_GET_PROFILE = 67; - int RIL_REQUEST_STK_SET_PROFILE = 68; - int RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND = 69; - int RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE = 70; - int RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM = 71; - int RIL_REQUEST_EXPLICIT_CALL_TRANSFER = 72; - int RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE = 73; - int RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE = 74; - int RIL_REQUEST_GET_NEIGHBORING_CELL_IDS = 75; - int RIL_REQUEST_SET_LOCATION_UPDATES = 76; - int RIL_UNSOL_RESPONSE_BASE = 1000; - int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000; - int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001; - int RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED = 1002; - int RIL_UNSOL_RESPONSE_NEW_SMS = 1003; - int RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT = 1004; - int RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM = 1005; - int RIL_UNSOL_ON_USSD = 1006; - int RIL_UNSOL_ON_USSD_REQUEST = 1007; - int RIL_UNSOL_NITZ_TIME_RECEIVED = 1008; - int RIL_UNSOL_SIGNAL_STRENGTH = 1009; - int RIL_UNSOL_PDP_CONTEXT_LIST_CHANGED = 1010; - int RIL_UNSOL_SUPP_SVC_NOTIFICATION = 1011; - int RIL_UNSOL_STK_SESSION_END = 1012; - int RIL_UNSOL_STK_PROACTIVE_COMMAND = 1013; - int RIL_UNSOL_STK_EVENT_NOTIFY = 1014; - int RIL_UNSOL_STK_CALL_SETUP = 1015; - int RIL_UNSOL_SIM_SMS_STORAGE_FULL = 1016; - int RIL_UNSOL_SIM_REFRESH = 1017; - int RIL_UNSOL_CALL_RING = 1018; - int RIL_UNSOL_RESTRICTED_STATE_CHANGED = 1023; -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java index 81fc657..ead1327 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java @@ -16,506 +16,54 @@ package com.android.internal.telephony.gsm; -import com.android.internal.telephony.*; -import com.android.internal.telephony.gsm.stk.ImageDescriptor; import android.os.*; import android.os.AsyncResult; -import android.os.RegistrantList; -import android.os.Registrant; import android.util.Log; + +import com.android.internal.telephony.IccConstants; +import com.android.internal.telephony.IccException; +import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.IccFileTypeMismatch; +import com.android.internal.telephony.IccIoResult; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.PhoneProxy; + import java.util.ArrayList; /** * {@hide} */ -public final class SIMFileHandler extends Handler -{ +public final class SIMFileHandler extends IccFileHandler { static final String LOG_TAG = "GSM"; - //from TS 11.11 9.1 or elsewhere - static private final int COMMAND_READ_BINARY = 0xb0; - static private final int COMMAND_UPDATE_BINARY = 0xd6; - static private final int COMMAND_READ_RECORD = 0xb2; - static private final int COMMAND_UPDATE_RECORD = 0xdc; - static private final int COMMAND_SEEK = 0xa2; - static private final int COMMAND_GET_RESPONSE = 0xc0; - - // from TS 11.11 9.2.5 - static private final int READ_RECORD_MODE_ABSOLUTE = 4; - - //***** types of files TS 11.11 9.3 - static private final int EF_TYPE_TRANSPARENT = 0; - static private final int EF_TYPE_LINEAR_FIXED = 1; - static private final int EF_TYPE_CYCLIC = 3; - - //***** types of files TS 11.11 9.3 - static private final int TYPE_RFU = 0; - static private final int TYPE_MF = 1; - static private final int TYPE_DF = 2; - static private final int TYPE_EF = 4; - - // size of GET_RESPONSE for EF - static private final int GET_RESPONSE_EF_SIZE_BYTES = 15; - - // Byte order received in response to COMMAND_GET_RESPONSE - // Refer TS 51.011 Section 9.2.1 - static private final int RESPONSE_DATA_RFU_1 = 0; - static private final int RESPONSE_DATA_RFU_2 = 1; - - static private final int RESPONSE_DATA_FILE_SIZE_1 = 2; - static private final int RESPONSE_DATA_FILE_SIZE_2 = 3; - - static private final int RESPONSE_DATA_FILE_ID_1 = 4; - static private final int RESPONSE_DATA_FILE_ID_2 = 5; - static private final int RESPONSE_DATA_FILE_TYPE = 6; - static private final int RESPONSE_DATA_RFU_3 = 7; - static private final int RESPONSE_DATA_ACCESS_CONDITION_1 = 8; - static private final int RESPONSE_DATA_ACCESS_CONDITION_2 = 9; - static private final int RESPONSE_DATA_ACCESS_CONDITION_3 = 10; - static private final int RESPONSE_DATA_FILE_STATUS = 11; - static private final int RESPONSE_DATA_LENGTH = 12; - static private final int RESPONSE_DATA_STRUCTURE = 13; - static private final int RESPONSE_DATA_RECORD_LENGTH = 14; - - //***** Instance Variables - GSMPhone phone; - - //***** Events - - /** Finished retrieving size of transparent EF; start loading. */ - static private final int EVENT_GET_BINARY_SIZE_DONE = 4; - /** Finished loading contents of transparent EF; post result. */ - static private final int EVENT_READ_BINARY_DONE = 5; - /** Finished retrieving size of records for linear-fixed EF; now load. */ - static private final int EVENT_GET_RECORD_SIZE_DONE = 6; - /** Finished loading single record from a linear-fixed EF; post result. */ - static private final int EVENT_READ_RECORD_DONE = 7; - /** Finished retrieving record size; post result. */ - static private final int EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE = 8; - /** Finished retrieving image instance record; post result. */ - static private final int EVENT_READ_IMG_DONE = 9; - /** Finished retrieving icon data; post result. */ - static private final int EVENT_READ_ICON_DONE = 10; - - //***** Inner Classes - - static class LoadLinearFixedContext - { - - int efid; - int recordNum, recordSize, countRecords; - boolean loadAll; - - Message onLoaded; - - ArrayList results; - - LoadLinearFixedContext(int efid, int recordNum, Message onLoaded) - { - this.efid = efid; - this.recordNum = recordNum; - this.onLoaded = onLoaded; - this.loadAll = false; - } - - LoadLinearFixedContext(int efid, Message onLoaded) - { - this.efid = efid; - this.recordNum = 1; - this.loadAll = true; - this.onLoaded = onLoaded; - } - - } - //***** Constructor - SIMFileHandler(GSMPhone phone) - { - this.phone = phone; + SIMFileHandler(GSMPhone phone) { + super(phone); } - //***** Public Methods - - /** - * Load a record from a SIM Linear Fixed EF - * - * @param fileid EF id - * @param recordNum 1-based (not 0-based) record number - * @param onLoaded - * - * ((AsyncResult)(onLoaded.obj)).result is the byte[] - * - */ - void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) - { - Message response - = obtainMessage(EVENT_GET_RECORD_SIZE_DONE, - new LoadLinearFixedContext(fileid, recordNum, onLoaded)); - - phone.mCM.simIO(COMMAND_GET_RESPONSE, fileid, null, - 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); - } - - /** - * Load a image instance record from a SIM Linear Fixed EF-IMG - * - * @param recordNum 1-based (not 0-based) record number - * @param onLoaded - * - * ((AsyncResult)(onLoaded.obj)).result is the byte[] - * - */ - public void loadEFImgLinearFixed(int recordNum, Message onLoaded) { - Message response = obtainMessage(EVENT_READ_IMG_DONE, - new LoadLinearFixedContext(SimConstants.EF_IMG, recordNum, - onLoaded)); - - phone.mCM.simIO(COMMAND_GET_RESPONSE, SimConstants.EF_IMG, "img", - recordNum, READ_RECORD_MODE_ABSOLUTE, - ImageDescriptor.ID_LENGTH, null, null, response); - } - - /** - * get record size for a linear fixed EF - * - * @param fileid EF id - * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[] - * int[0] is the record length int[1] is the total length of the EF - * file int[3] is the number of records in the EF file So int[0] * - * int[3] = int[1] - */ - void getEFLinearRecordSize(int fileid, Message onLoaded) - { - Message response - = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE, - new LoadLinearFixedContext(fileid, onLoaded)); - phone.mCM.simIO(COMMAND_GET_RESPONSE, fileid, null, - 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); - } - - /** - * Load all records from a SIM Linear Fixed EF - * - * @param fileid EF id - * @param onLoaded - * - * ((AsyncResult)(onLoaded.obj)).result is an ArrayList - * - */ - void loadEFLinearFixedAll(int fileid, Message onLoaded) - { - Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE, - new LoadLinearFixedContext(fileid,onLoaded)); - - phone.mCM.simIO(COMMAND_GET_RESPONSE, fileid, null, - 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); + public void dispose() { + super.dispose(); } - /** - * Load a SIM Transparent EF - * - * @param fileid EF id - * @param onLoaded - * - * ((AsyncResult)(onLoaded.obj)).result is the byte[] - * - */ - - void loadEFTransparent(int fileid, Message onLoaded) - { - Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE, - fileid, 0, onLoaded); - - phone.mCM.simIO(COMMAND_GET_RESPONSE, fileid, null, - 0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, response); - } - - /** - * Load a SIM Transparent EF-IMG. Used right after loadEFImgLinearFixed to - * retrive STK's icon data. - * - * @param fileid EF id - * @param onLoaded - * - * ((AsyncResult)(onLoaded.obj)).result is the byte[] - * - */ - public void loadEFImgTransparent(int fileid, int highOffset, int lowOffset, - int length, Message onLoaded) { - Message response = obtainMessage(EVENT_READ_ICON_DONE, fileid, 0, - onLoaded); - - phone.mCM.simIO(COMMAND_READ_BINARY, fileid, "img", highOffset, lowOffset, - length, null, null, response); + protected void finalize() { + Log.d(LOG_TAG, "SIMFileHandler finalized"); } - /** - * Update a record in a linear fixed EF - * @param fileid EF id - * @param recordNum 1-based (not 0-based) record number - * @param data must be exactly as long as the record in the EF - * @param pin2 for CHV2 operations, otherwist must be null - * @param onComplete onComplete.obj will be an AsyncResult - * onComplete.obj.userObj will be a SimIoResult on success - */ - void updateEFLinearFixed(int fileid, int recordNum, byte[] data, - String pin2, Message onComplete) - { - phone.mCM.simIO(COMMAND_UPDATE_RECORD, fileid, null, - recordNum, READ_RECORD_MODE_ABSOLUTE, data.length, - SimUtils.bytesToHexString(data), pin2, onComplete); - } + //***** Overridden from IccFileHandler - /** - * Update a transparent EF - * @param fileid EF id - * @param data must be exactly as long as the EF - */ - void updateEFTransparent(int fileid, byte[] data, Message onComplete) - { - phone.mCM.simIO(COMMAND_UPDATE_BINARY, fileid, null, - 0, 0, data.length, - SimUtils.bytesToHexString(data), null, onComplete); + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); } - //***** Overridden from Handler - - public void handleMessage(Message msg) - { - AsyncResult ar; - SimIoResult result; - Message response = null; - String str; - LoadLinearFixedContext lc; - - SimException simException; - byte data[]; - int size; - int fileid; - int recordNum; - int recordSize[]; - - try { - switch (msg.what) { - case EVENT_READ_IMG_DONE: - ar = (AsyncResult) msg.obj; - lc = (LoadLinearFixedContext) ar.userObj; - result = (SimIoResult) ar.result; - response = lc.onLoaded; - - simException = result.getException(); - if (simException != null) { - sendResult(response, result.payload, ar.exception); - } - break; - case EVENT_READ_ICON_DONE: - ar = (AsyncResult) msg.obj; - response = (Message) ar.userObj; - result = (SimIoResult) ar.result; - - simException = result.getException(); - if (simException != null) { - sendResult(response, result.payload, ar.exception); - } - break; - case EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE: - ar = (AsyncResult)msg.obj; - lc = (LoadLinearFixedContext) ar.userObj; - result = (SimIoResult) ar.result; - response = lc.onLoaded; - - if (ar.exception != null) { - sendResult(response, null, ar.exception); - break; - } - - simException = result.getException(); - if (simException != null) { - sendResult(response, null, simException); - break; - } - - data = result.payload; - - if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE] || - EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) { - throw new SimFileTypeMismatch(); - } - - recordSize = new int[3]; - recordSize[0] = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF; - recordSize[1] = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) - + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); - recordSize[2] = recordSize[1] / recordSize[0]; - - sendResult(response, recordSize, null); - break; - case EVENT_GET_RECORD_SIZE_DONE: - ar = (AsyncResult)msg.obj; - lc = (LoadLinearFixedContext) ar.userObj; - result = (SimIoResult) ar.result; - response = lc.onLoaded; - - if (ar.exception != null) { - sendResult(response, null, ar.exception); - break; - } - - simException = result.getException(); - - if (simException != null) { - sendResult(response, null, simException); - break; - } - - data = result.payload; - fileid = lc.efid; - recordNum = lc.recordNum; - - if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) { - throw new SimFileTypeMismatch(); - } - - if (EF_TYPE_LINEAR_FIXED != data[RESPONSE_DATA_STRUCTURE]) { - throw new SimFileTypeMismatch(); - } - - lc.recordSize = data[RESPONSE_DATA_RECORD_LENGTH] & 0xFF; - - size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) - + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); - - lc.countRecords = size / lc.recordSize; - - if (lc.loadAll) { - lc.results = new ArrayList(lc.countRecords); - } - - phone.mCM.simIO(COMMAND_READ_RECORD, lc.efid, null, - lc.recordNum, - READ_RECORD_MODE_ABSOLUTE, - lc.recordSize, null, null, - obtainMessage(EVENT_READ_RECORD_DONE, lc)); - break; - case EVENT_GET_BINARY_SIZE_DONE: - ar = (AsyncResult)msg.obj; - response = (Message) ar.userObj; - result = (SimIoResult) ar.result; - - if (ar.exception != null) { - sendResult(response, null, ar.exception); - break; - } - - simException = result.getException(); - - if (simException != null) { - sendResult(response, null, simException); - break; - } - - data = result.payload; - - fileid = msg.arg1; - - if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) { - throw new SimFileTypeMismatch(); - } - - if (EF_TYPE_TRANSPARENT != data[RESPONSE_DATA_STRUCTURE]) { - throw new SimFileTypeMismatch(); - } - - size = ((data[RESPONSE_DATA_FILE_SIZE_1] & 0xff) << 8) - + (data[RESPONSE_DATA_FILE_SIZE_2] & 0xff); - - phone.mCM.simIO(COMMAND_READ_BINARY, fileid, null, - 0, 0, size, null, null, - obtainMessage(EVENT_READ_BINARY_DONE, - fileid, 0, response)); - break; - - case EVENT_READ_RECORD_DONE: - - ar = (AsyncResult)msg.obj; - lc = (LoadLinearFixedContext) ar.userObj; - result = (SimIoResult) ar.result; - response = lc.onLoaded; - - if (ar.exception != null) { - sendResult(response, null, ar.exception); - break; - } - - simException = result.getException(); - - if (simException != null) { - sendResult(response, null, simException); - break; - } - - if (!lc.loadAll) { - sendResult(response, result.payload, null); - } else { - lc.results.add(result.payload); - - lc.recordNum++; - - if (lc.recordNum > lc.countRecords) { - sendResult(response, lc.results, null); - } else { - phone.mCM.simIO(COMMAND_READ_RECORD, lc.efid, null, - lc.recordNum, - READ_RECORD_MODE_ABSOLUTE, - lc.recordSize, null, null, - obtainMessage(EVENT_READ_RECORD_DONE, lc)); - } - } - - break; - - case EVENT_READ_BINARY_DONE: - ar = (AsyncResult)msg.obj; - response = (Message) ar.userObj; - result = (SimIoResult) ar.result; - - if (ar.exception != null) { - sendResult(response, null, ar.exception); - break; - } - - simException = result.getException(); - - if (simException != null) { - sendResult(response, null, simException); - break; - } - - sendResult(response, result.payload, null); - break; - - }} catch (Exception exc) { - if (response != null) { - sendResult(response, null, exc); - } else { - Log.e(LOG_TAG, "uncaught exception", exc); - } - } + protected void logd(String msg) { + Log.d(LOG_TAG, "[SIMFileHandler] " + msg); } - //***** Private Methods - - private void sendResult(Message response, Object result, Throwable ex) - { - if (response == null) { - return; - } - - AsyncResult.forMessage(response, result, ex); - - response.sendToTarget(); + protected void loge(String msg) { + Log.e(LOG_TAG, "[SIMFileHandler] " + msg); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index 4467536..09a17f5 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -16,26 +16,45 @@ package com.android.internal.telephony.gsm; +import android.app.ActivityManagerNative; import android.app.AlarmManager; +import android.app.IActivityManager; import android.content.Context; +import android.content.res.Configuration; import android.os.AsyncResult; -import android.os.RegistrantList; -import android.os.Registrant; import android.os.Handler; import android.os.Message; import android.os.SystemProperties; -import android.telephony.gsm.SmsMessage; +import android.os.Registrant; import android.util.Log; import java.util.ArrayList; + import static com.android.internal.telephony.TelephonyProperties.*; -import com.android.internal.telephony.SimCard; + +import com.android.internal.telephony.AdnRecord; +import com.android.internal.telephony.AdnRecordCache; +import com.android.internal.telephony.AdnRecordLoader; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.gsm.SimCard; +import com.android.internal.telephony.gsm.SmsMessage; +import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.IccRecords; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.IccVmFixedException; +import com.android.internal.telephony.IccVmNotSupportedException; +import com.android.internal.telephony.PhoneProxy; + + + + + + /** * {@hide} */ -public final class SIMRecords extends Handler implements SimConstants -{ +public final class SIMRecords extends IccRecords { static final String LOG_TAG = "GSM"; private static final boolean CRASH_RIL = false; @@ -44,37 +63,19 @@ public final class SIMRecords extends Handler implements SimConstants //***** Instance Variables - GSMPhone phone; - RegistrantList recordsLoadedRegistrants = new RegistrantList(); - - int recordsToLoad; // number of pending load requests + VoiceMailConstants mVmConfig; - AdnRecordCache adnCache; - VoiceMailConstants mVmConfig; SpnOverride mSpnOverride; //***** Cached SIM State; cleared on channel close - boolean recordsRequested = false; // true if we've made requests for the sim records - String imsi; - String iccid; - String msisdn = null; // My mobile number - String msisdnTag = null; - String voiceMailNum = null; - String voiceMailTag = null; - String newVoiceMailNum = null; - String newVoiceMailTag = null; - boolean isVoiceMailFixed = false; - int countVoiceMessages = 0; boolean callForwardingEnabled; - int mncLength = 0; // 0 is used to indicate that the value - // is not initialized - int mailboxIndex = 0; // 0 is no mailbox dailing number associated + /** - * Sates only used by getSpnFsm FSM + * States only used by getSpnFsm FSM */ private Get_Spn_Fsm_State spnState; @@ -157,9 +158,8 @@ public final class SIMRecords extends Handler implements SimConstants //***** Constructor - SIMRecords(GSMPhone phone) - { - this.phone = phone; + SIMRecords(GSMPhone p) { + super(p); adnCache = new AdnRecordCache(phone); @@ -172,30 +172,35 @@ public final class SIMRecords extends Handler implements SimConstants recordsToLoad = 0; - phone.mCM.registerForSIMReady(this, EVENT_SIM_READY, null); - phone.mCM.registerForOffOrNotAvailable( + p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null); + p.mCM.registerForOffOrNotAvailable( this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); - phone.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null); - phone.mCM.setOnSimRefresh(this, EVENT_SIM_REFRESH, null); + p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null); + p.mCM.setOnIccRefresh(this, EVENT_SIM_REFRESH, null); // Start off by setting empty state - onRadioOffOrNotAvailable(); + onRadioOffOrNotAvailable(); } - AdnRecordCache getAdnCache() { - return adnCache; + public void dispose() { + //Unregister for all events + phone.mCM.unregisterForSIMReady(this); + phone.mCM.unregisterForOffOrNotAvailable( this); + phone.mCM.unSetOnIccRefresh(this); } - private void onRadioOffOrNotAvailable() - { + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "SIMRecords finalized"); + } + + protected void onRadioOffOrNotAvailable() { imsi = null; msisdn = null; voiceMailNum = null; countVoiceMessages = 0; mncLength = 0; iccid = null; - spn = null; // -1 means no EF_SPN found; treat accordingly. spnDisplayCondition = -1; efMWIS = null; @@ -205,9 +210,9 @@ public final class SIMRecords extends Handler implements SimConstants adnCache.reset(); - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_NUMERIC, null); - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ALPHA, null); - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ISO_COUNTRY, null); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, null); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null); // recordsRequested is set to false indicating that the SIM // read requests made so far are not valid. This is set to @@ -217,24 +222,13 @@ public final class SIMRecords extends Handler implements SimConstants //***** Public Methods - public void registerForRecordsLoaded(Handler h, int what, Object obj) - { - Registrant r = new Registrant(h, what, obj); - recordsLoadedRegistrants.add(r); - - if (recordsToLoad == 0 && recordsRequested == true) { - r.notifyRegistrant(new AsyncResult(null, null, null)); - } - } /** Returns null if SIM is not yet ready */ - public String getIMSI() - { + public String getIMSI() { return imsi; } - public String getMsisdnNumber() - { + public String getMsisdnNumber() { return msisdn; } @@ -272,28 +266,18 @@ public final class SIMRecords extends Handler implements SimConstants return msisdnTag; } - public String getVoiceMailNumber() - { + public String getVoiceMailNumber() { return voiceMailNum; } /** - * Return Service Provider Name stored in SIM - * @return null if SIM is not yet ready - */ - String getServiceProviderName() - { - return spn; - } - - /** * Set voice mail number to SIM record * * The voice mail number can be stored either in EF_MBDN (TS 51.011) or * EF_MAILBOX_CPHS (CPHS 4.2) * * If EF_MBDN is available, store the voice mail number to EF_MBDN - * + * * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS * * So the voice mail number will be stored in both EFs if both are available @@ -314,7 +298,7 @@ public final class SIMRecords extends Handler implements SimConstants Message onComplete) { if (isVoiceMailFixed) { AsyncResult.forMessage((onComplete)).exception = - new SimVmFixedException("Voicemail number is fixed by operator"); + new IccVmFixedException("Voicemail number is fixed by operator"); onComplete.sendToTarget(); return; } @@ -336,9 +320,9 @@ public final class SIMRecords extends Handler implements SimConstants EF_EXT1, 1, null, obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete)); - }else { + } else { AsyncResult.forMessage((onComplete)).exception = - new SimVmNotSupportedException("Update SIM voice mailbox error"); + new IccVmNotSupportedException("Update SIM voice mailbox error"); onComplete.sendToTarget(); } } @@ -352,12 +336,11 @@ public final class SIMRecords extends Handler implements SimConstants * Sets the SIM voice message waiting indicator records * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported * @param countWaiting The number of messages waiting, if known. Use - * -1 to indicate that an unknown number of + * -1 to indicate that an unknown number of * messages are waiting */ public void - setVoiceMessageWaiting(int line, int countWaiting) - { + setVoiceMessageWaiting(int line, int countWaiting) { if (line != 1) { // only profile 1 is supported return; @@ -374,14 +357,14 @@ public final class SIMRecords extends Handler implements SimConstants countVoiceMessages = countWaiting; - phone.notifyMessageWaitingIndicator(); + ((GSMPhone) phone).notifyMessageWaitingIndicator(); try { if (efMWIS != null) { // TS 51.011 10.3.45 // lsb of byte 0 is 'voicemail' status - efMWIS[0] = (byte)((efMWIS[0] & 0xfe) + efMWIS[0] = (byte)((efMWIS[0] & 0xfe) | (countVoiceMessages == 0 ? 0 : 1)); // byte 1 is the number of voice messages waiting @@ -393,17 +376,17 @@ public final class SIMRecords extends Handler implements SimConstants efMWIS[1] = (byte) countWaiting; } - phone.mSIMFileHandler.updateEFLinearFixed( + phone.getIccFileHandler().updateEFLinearFixed( EF_MWIS, 1, efMWIS, null, obtainMessage (EVENT_UPDATE_DONE, EF_MWIS)); - } + } if (efCPHS_MWI != null) { // Refer CPHS4_2.WW6 B4.2.3 - efCPHS_MWI[0] = (byte)((efCPHS_MWI[0] & 0xf0) + efCPHS_MWI[0] = (byte)((efCPHS_MWI[0] & 0xf0) | (countVoiceMessages == 0 ? 0x5 : 0xa)); - phone.mSIMFileHandler.updateEFTransparent( + phone.getIccFileHandler().updateEFTransparent( EF_VOICE_MAIL_INDICATOR_CPHS, efCPHS_MWI, obtainMessage (EVENT_UPDATE_DONE, EF_VOICE_MAIL_INDICATOR_CPHS)); } @@ -413,22 +396,6 @@ public final class SIMRecords extends Handler implements SimConstants } } - /** @return true if there are messages waiting, false otherwise. */ - public boolean getVoiceMessageWaiting() - { - return countVoiceMessages != 0; - } - - /** - * Returns number of voice messages waiting, if available - * If not available (eg, on an older CPHS SIM) -1 is returned if - * getVoiceMessageWaiting() is true - */ - public int getCountVoiceMessages() - { - return countVoiceMessages; - } - public boolean getVoiceCallForwardingFlag() { return callForwardingEnabled; } @@ -439,7 +406,7 @@ public final class SIMRecords extends Handler implements SimConstants callForwardingEnabled = enable; - phone.notifyCallForwardingIndicator(); + ((GSMPhone) phone).notifyCallForwardingIndicator(); try { if (mEfCfis != null) { @@ -453,7 +420,7 @@ public final class SIMRecords extends Handler implements SimConstants // TODO: Should really update other fields in EF_CFIS, eg, // dialing number. We don't read or use it right now. - phone.mSIMFileHandler.updateEFLinearFixed( + phone.getIccFileHandler().updateEFLinearFixed( EF_CFIS, 1, mEfCfis, null, obtainMessage (EVENT_UPDATE_DONE, EF_CFIS)); } @@ -467,7 +434,7 @@ public final class SIMRecords extends Handler implements SimConstants | CFF_UNCONDITIONAL_DEACTIVE); } - phone.mSIMFileHandler.updateEFTransparent( + phone.getIccFileHandler().updateEFTransparent( EF_CFF_CPHS, mEfCff, obtainMessage (EVENT_UPDATE_DONE, EF_CFF_CPHS)); } @@ -496,8 +463,7 @@ public final class SIMRecords extends Handler implements SimConstants /** Returns the 5 or 6 digit MCC/MNC of the operator that * provided the SIM card. Returns null of SIM is not yet ready */ - String getSIMOperatorNumeric() - { + String getSIMOperatorNumeric() { if (imsi == null) { return null; } @@ -517,17 +483,8 @@ public final class SIMRecords extends Handler implements SimConstants return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); } - - boolean getRecordsLoaded() - { - if (recordsToLoad == 0 && recordsRequested == true) { - return true; - } else { - return false; - } - } - - /** + + /** * If the timezone is not already set, set it based on the MCC of the SIM. * @param mcc Mobile Country Code of the SIM */ @@ -557,8 +514,7 @@ public final class SIMRecords extends Handler implements SimConstants } //***** Overridden from Handler - public void handleMessage(Message msg) - { + public void handleMessage(Message msg) { AsyncResult ar; AdnRecord adn; @@ -573,19 +529,19 @@ public final class SIMRecords extends Handler implements SimConstants case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: onRadioOffOrNotAvailable(); - break; + break; /* IO events */ case EVENT_GET_IMSI_DONE: isRecordLoadResponse = true; - + ar = (AsyncResult)msg.obj; if (ar.exception != null) { Log.e(LOG_TAG, "Exception querying IMSI, Exception:" + ar.exception); break; - } - + } + imsi = (String) ar.result; // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more @@ -594,11 +550,11 @@ public final class SIMRecords extends Handler implements SimConstants Log.e(LOG_TAG, "invalid IMSI " + imsi); imsi = null; } - + Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxxxx"); - phone.mSimCard.updateImsiConfiguration(imsi); - phone.mSimCard.broadcastSimStateChangedIntent( - SimCard.INTENT_VALUE_SIM_IMSI, null); + ((GSMPhone) phone).mSimCard.updateImsiConfiguration(imsi); + ((GSMPhone) phone).mSimCard.broadcastSimStateChangedIntent( + SimCard.INTENT_VALUE_ICC_IMSI, null); int mcc = Integer.parseInt(imsi.substring(0, 3)); setTimezoneFromMccIfNeeded(mcc); @@ -616,7 +572,7 @@ public final class SIMRecords extends Handler implements SimConstants if (ar.exception == null) { // Refer TS 51.011 Section 10.3.44 for content details Log.d(LOG_TAG, "EF_MBI: " + - SimUtils.bytesToHexString(data)); + IccUtils.bytesToHexString(data)); // Voice mail record number stored first mailboxIndex = (int)data[0] & 0xff; @@ -651,20 +607,20 @@ public final class SIMRecords extends Handler implements SimConstants ar = (AsyncResult)msg.obj; if (ar.exception != null) { - - Log.d(LOG_TAG, "Invalid or missing EF" + + Log.d(LOG_TAG, "Invalid or missing EF" + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]")); // Bug #645770 fall back to CPHS // FIXME should use SST to decide if (msg.what == EVENT_GET_MBDN_DONE) { - //load CPHS on fail... + //load CPHS on fail... // FIXME right now, only load line1's CPHS voice mail entry recordsToLoad += 1; new AdnRecordLoader(phone).loadFromEF( - EF_MAILBOX_CPHS, EF_EXT1, 1, + EF_MAILBOX_CPHS, EF_EXT1, 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); } break; @@ -672,7 +628,8 @@ public final class SIMRecords extends Handler implements SimConstants adn = (AdnRecord)ar.result; - Log.d(LOG_TAG, "VM: " + adn + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]")); + Log.d(LOG_TAG, "VM: " + adn + + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]")); if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) { // Bug #645770 fall back to CPHS @@ -680,7 +637,7 @@ public final class SIMRecords extends Handler implements SimConstants // FIXME right now, only load line1's CPHS voice mail entry recordsToLoad += 1; new AdnRecordLoader(phone).loadFromEF( - EF_MAILBOX_CPHS, EF_EXT1, 1, + EF_MAILBOX_CPHS, EF_EXT1, 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); break; @@ -730,7 +687,7 @@ public final class SIMRecords extends Handler implements SimConstants } Log.d(LOG_TAG, "EF_MWIS: " + - SimUtils.bytesToHexString(data)); + IccUtils.bytesToHexString(data)); efMWIS = data; @@ -744,11 +701,11 @@ public final class SIMRecords extends Handler implements SimConstants countVoiceMessages = data[1] & 0xff; if (voiceMailWaiting && countVoiceMessages == 0) { - // Unknown count = -1 + // Unknown count = -1 countVoiceMessages = -1; } - phone.notifyMessageWaitingIndicator(); + ((GSMPhone) phone).notifyMessageWaitingIndicator(); break; case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE: @@ -777,7 +734,7 @@ public final class SIMRecords extends Handler implements SimConstants countVoiceMessages = 0; } - phone.notifyMessageWaitingIndicator(); + ((GSMPhone) phone).notifyMessageWaitingIndicator(); } break; @@ -786,13 +743,13 @@ public final class SIMRecords extends Handler implements SimConstants ar = (AsyncResult)msg.obj; data = (byte[])ar.result; - + if (ar.exception != null) { break; - } + } + + iccid = IccUtils.bcdToString(data, 0, data.length); - iccid = SimUtils.bcdToString(data, 0, data.length); - Log.d(LOG_TAG, "iccid: " + iccid); break; @@ -809,7 +766,7 @@ public final class SIMRecords extends Handler implements SimConstants } Log.d(LOG_TAG, "EF_AD: " + - SimUtils.bytesToHexString(data)); + IccUtils.bytesToHexString(data)); if (data.length < 3) { Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM"); @@ -851,14 +808,14 @@ public final class SIMRecords extends Handler implements SimConstants } Log.d(LOG_TAG, "EF_CFF_CPHS: " + - SimUtils.bytesToHexString(data)); + IccUtils.bytesToHexString(data)); mEfCff = data; if (mEfCfis == null) { callForwardingEnabled = ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE); - phone.notifyCallForwardingIndicator(); + ((GSMPhone) phone).notifyCallForwardingIndicator(); } break; @@ -872,7 +829,7 @@ public final class SIMRecords extends Handler implements SimConstants break; } - parseEfSpdi(data); + parseEfSpdi(data); break; case EVENT_UPDATE_DONE: @@ -896,8 +853,8 @@ public final class SIMRecords extends Handler implements SimConstants for ( ; tlv.isValidObject() ; tlv.nextObject()) { if (tlv.getTag() == TAG_FULL_NETWORK_NAME) { - pnnHomeName - = SimUtils.networkNameToString( + pnnHomeName + = IccUtils.networkNameToString( tlv.getData(), 0, tlv.getData().length); break; } @@ -931,7 +888,8 @@ public final class SIMRecords extends Handler implements SimConstants + ar.exception + " length " + index.length); } else { Log.d(LOG_TAG, "READ EF_SMS RECORD index=" + index[0]); - phone.mSIMFileHandler.loadEFLinearFixed(EF_SMS,index[0],obtainMessage(EVENT_GET_SMS_DONE)); + phone.getIccFileHandler().loadEFLinearFixed(EF_SMS,index[0], + obtainMessage(EVENT_GET_SMS_DONE)); } break; @@ -955,7 +913,7 @@ public final class SIMRecords extends Handler implements SimConstants break; } - //Log.d(LOG_TAG, "SST: " + SimUtils.bytesToHexString(data)); + //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data)); break; case EVENT_GET_INFO_CPHS_DONE: @@ -969,7 +927,7 @@ public final class SIMRecords extends Handler implements SimConstants mCphsInfo = (byte[])ar.result; - if (DBG) log("iCPHS: " + SimUtils.bytesToHexString(mCphsInfo)); + if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo)); break; case EVENT_SET_MBDN_DONE: @@ -1050,20 +1008,20 @@ public final class SIMRecords extends Handler implements SimConstants } Log.d(LOG_TAG, "EF_CFIS: " + - SimUtils.bytesToHexString(data)); + IccUtils.bytesToHexString(data)); mEfCfis = data; // Refer TS 51.011 Section 10.3.46 for the content description callForwardingEnabled = ((data[1] & 0x01) != 0); - phone.notifyCallForwardingIndicator(); + ((GSMPhone) phone).notifyCallForwardingIndicator(); break; }}catch (RuntimeException exc) { // I don't want these exceptions to be fatal Log.w(LOG_TAG, "Exception parsing SIM record", exc); - } finally { + } finally { // Count up record load responses even if they are fails if (isRecordLoadResponse) { onRecordLoaded(); @@ -1076,12 +1034,12 @@ public final class SIMRecords extends Handler implements SimConstants case EF_MBDN: recordsToLoad++; new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6, - mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); + mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); break; case EF_MAILBOX_CPHS: recordsToLoad++; new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1, - 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); + 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); break; default: // For now, fetch all records if this is not a @@ -1093,7 +1051,7 @@ public final class SIMRecords extends Handler implements SimConstants } } - private void handleSimRefresh(int[] result) { + private void handleSimRefresh(int[] result) { if (result == null || result.length == 0) { if (DBG) log("handleSimRefresh without input"); return; @@ -1102,7 +1060,7 @@ public final class SIMRecords extends Handler implements SimConstants switch ((result[0])) { case CommandsInterface.SIM_REFRESH_FILE_UPDATED: if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED"); - // result[1] contains the EFID of the updated file. + // result[1] contains the EFID of the updated file. int efid = result[1]; handleFileUpdate(efid); break; @@ -1130,8 +1088,7 @@ public final class SIMRecords extends Handler implements SimConstants } } - private void handleSms(byte[] ba) - { + private void handleSms(byte[] ba) { if (ba[0] != 0) Log.d("ENF", "status : " + ba[0]); @@ -1145,12 +1102,13 @@ public final class SIMRecords extends Handler implements SimConstants byte[] nba = new byte[n - 1]; System.arraycopy(ba, 1, nba, 0, n - 1); - String pdu = SimUtils.bytesToHexString(nba); + String pdu = IccUtils.bytesToHexString(nba); // XXX first line is bogus SmsMessage message = SmsMessage.newFromCMT( new String[] { "", pdu }); - phone.mSMS.dispatchMessage(message); + + ((GSMPhone) phone).mSMS.dispatchMessage(message); } } @@ -1175,12 +1133,13 @@ public final class SIMRecords extends Handler implements SimConstants byte[] nba = new byte[n - 1]; System.arraycopy(ba, 1, nba, 0, n - 1); - String pdu = SimUtils.bytesToHexString(nba); + String pdu = IccUtils.bytesToHexString(nba); // XXX first line is bogus SmsMessage message = SmsMessage.newFromCMT( new String[] { "", pdu }); - phone.mSMS.dispatchMessage(message); + + ((GSMPhone) phone).mSMS.dispatchMessage(message); // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 // 1 == "received by MS from network; message read" @@ -1188,18 +1147,14 @@ public final class SIMRecords extends Handler implements SimConstants ba[0] = 1; if (false) { // XXX writing seems to crash RdoServD - phone.mSIMFileHandler.updateEFLinearFixed(EF_SMS, i, ba, null, - obtainMessage(EVENT_MARK_SMS_READ_DONE, i)); + phone.getIccFileHandler().updateEFLinearFixed(EF_SMS, + i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i)); } } } } - - //***** Private Methods - - private void onRecordLoaded() - { + protected void onRecordLoaded() { // One record loaded successfully or failed, In either case // we need to update the recordsToLoad count recordsToLoad -= 1; @@ -1211,21 +1166,19 @@ public final class SIMRecords extends Handler implements SimConstants recordsToLoad = 0; } } - - private void onAllRecordsLoaded() - { + + protected void onAllRecordsLoaded() { Log.d(LOG_TAG, "SIMRecords: record load complete"); String operator = getSIMOperatorNumeric(); // Some fields require more than one SIM record to set - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_NUMERIC, operator); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator); if (imsi != null) { - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ISO_COUNTRY, - MccTable.countryCodeForMcc( - Integer.parseInt(imsi.substring(0,3)))); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, + MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3)))); } else { Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!"); @@ -1236,16 +1189,19 @@ public final class SIMRecords extends Handler implements SimConstants recordsLoadedRegistrants.notifyRegistrants( new AsyncResult(null, null, null)); - phone.mSimCard.broadcastSimStateChangedIntent( - SimCard.INTENT_VALUE_SIM_LOADED, null); + ((GSMPhone) phone).mSimCard.broadcastSimStateChangedIntent( + SimCard.INTENT_VALUE_ICC_LOADED, null); } + //***** Private methods + private void setSpnFromConfig(String carrier) { if (mSpnOverride.containsCarrier(carrier)) { spn = mSpnOverride.getSpn(carrier); } } + private void setVoiceMailByCountry (String spn) { if (mVmConfig.containsCarrier(spn)) { isVoiceMailFixed = true; @@ -1258,22 +1214,22 @@ public final class SIMRecords extends Handler implements SimConstants /* broadcast intent SIM_READY here so that we can make sure READY is sent before IMSI ready */ - phone.mSimCard.broadcastSimStateChangedIntent( - SimCard.INTENT_VALUE_SIM_READY, null); + ((GSMPhone) phone).mSimCard.broadcastSimStateChangedIntent( + SimCard.INTENT_VALUE_ICC_READY, null); fetchSimRecords(); } private void fetchSimRecords() { recordsRequested = true; + IccFileHandler iccFh = phone.getIccFileHandler(); - Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad); + Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad); phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE)); recordsToLoad++; - phone.mSIMFileHandler.loadEFTransparent(EF_ICCID, - obtainMessage(EVENT_GET_ICCID_DONE)); + iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); recordsToLoad++; // FIXME should examine EF[MSISDN]'s capability configuration @@ -1283,17 +1239,14 @@ public final class SIMRecords extends Handler implements SimConstants recordsToLoad++; // Record number is subscriber profile - phone.mSIMFileHandler.loadEFLinearFixed(EF_MBI, 1, - obtainMessage(EVENT_GET_MBI_DONE)); + iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); recordsToLoad++; - phone.mSIMFileHandler.loadEFTransparent(EF_AD, - obtainMessage(EVENT_GET_AD_DONE)); + iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE)); recordsToLoad++; // Record number is subscriber profile - phone.mSIMFileHandler.loadEFLinearFixed(EF_MWIS, 1, - obtainMessage(EVENT_GET_MWIS_DONE)); + iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE)); recordsToLoad++; @@ -1301,50 +1254,49 @@ public final class SIMRecords extends Handler implements SimConstants // the same info as EF[MWIS]. If both exist, both are updated // but the EF[MWIS] data is preferred // Please note this must be loaded after EF[MWIS] - phone.mSIMFileHandler.loadEFTransparent( - EF_VOICE_MAIL_INDICATOR_CPHS, + iccFh.loadEFTransparent( + EF_VOICE_MAIL_INDICATOR_CPHS, obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE)); recordsToLoad++; // Same goes for Call Forward Status indicator: fetch both // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred. - phone.mSIMFileHandler.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE)); + iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE)); recordsToLoad++; - phone.mSIMFileHandler.loadEFTransparent(EF_CFF_CPHS, - obtainMessage(EVENT_GET_CFF_DONE)); + iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE)); recordsToLoad++; getSpnFsm(true, null); - phone.mSIMFileHandler.loadEFTransparent(EF_SPDI, - obtainMessage(EVENT_GET_SPDI_DONE)); + iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); recordsToLoad++; - phone.mSIMFileHandler.loadEFLinearFixed(EF_PNN, 1, - obtainMessage(EVENT_GET_PNN_DONE)); + iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); recordsToLoad++; - phone.mSIMFileHandler.loadEFTransparent(EF_SST, - obtainMessage(EVENT_GET_SST_DONE)); + iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); recordsToLoad++; - phone.mSIMFileHandler.loadEFTransparent(EF_INFO_CPHS, - obtainMessage(EVENT_GET_INFO_CPHS_DONE)); + iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE)); recordsToLoad++; // XXX should seek instead of examining them all if (false) { // XXX - phone.mSIMFileHandler.loadEFLinearFixedAll(EF_SMS, - obtainMessage(EVENT_GET_ALL_SMS_DONE)); + iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE)); recordsToLoad++; } if (CRASH_RIL) { - String sms = "0107912160130310f20404d0110041007030208054832b0120ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; - byte[] ba = SimUtils.hexStringToBytes(sms); - - phone.mSIMFileHandler.updateEFLinearFixed(EF_SMS, 1, ba, null, + String sms = "0107912160130310f20404d0110041007030208054832b0120" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + "ffffffffffffffffffffffffffffff"; + byte[] ba = IccUtils.hexStringToBytes(sms); + + iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, 1)); } } @@ -1356,7 +1308,7 @@ public final class SIMRecords extends Handler implements SimConstants * * If the SPN is not found on the SIM, the rule is always PLMN_ONLY. */ - int getDisplayRule(String plmn) { + protected int getDisplayRule(String plmn) { int rule; if (spn == null || spnDisplayCondition == -1) { // EF_SPN was not found on the SIM, or not yet loaded. Just show ONS. @@ -1431,7 +1383,7 @@ public final class SIMRecords extends Handler implements SimConstants case INIT: spn = null; - phone.mSIMFileHandler.loadEFTransparent( EF_SPN, + phone.getIccFileHandler().loadEFTransparent( EF_SPN, obtainMessage(EVENT_GET_SPN_DONE)); recordsToLoad++; @@ -1441,20 +1393,20 @@ public final class SIMRecords extends Handler implements SimConstants if (ar != null && ar.exception == null) { data = (byte[]) ar.result; spnDisplayCondition = 0xff & data[0]; - spn = SimUtils.adnStringFieldToString(data, 1, data.length - 1); + spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1); if (DBG) log("Load EF_SPN: " + spn + " spnDisplayCondition: " + spnDisplayCondition); - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ALPHA, spn); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); spnState = Get_Spn_Fsm_State.IDLE; } else { - phone.mSIMFileHandler.loadEFTransparent( EF_SPN_CPHS, + phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS, obtainMessage(EVENT_GET_SPN_DONE)); recordsToLoad++; spnState = Get_Spn_Fsm_State.READ_SPN_CPHS; - + // See TS 51.011 10.3.11. Basically, default to // show PLMN always, and SPN also if roaming. spnDisplayCondition = -1; @@ -1463,16 +1415,16 @@ public final class SIMRecords extends Handler implements SimConstants case READ_SPN_CPHS: if (ar != null && ar.exception == null) { data = (byte[]) ar.result; - spn = SimUtils.adnStringFieldToString( + spn = IccUtils.adnStringFieldToString( data, 0, data.length - 1 ); if (DBG) log("Load EF_SPN_CPHS: " + spn); - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ALPHA, spn); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); spnState = Get_Spn_Fsm_State.IDLE; } else { - phone.mSIMFileHandler.loadEFTransparent( EF_SPN_SHORT_CPHS, - obtainMessage(EVENT_GET_SPN_DONE)); + phone.getIccFileHandler().loadEFTransparent( + EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE)); recordsToLoad++; spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS; @@ -1481,11 +1433,11 @@ public final class SIMRecords extends Handler implements SimConstants case READ_SPN_SHORT_CPHS: if (ar != null && ar.exception == null) { data = (byte[]) ar.result; - spn = SimUtils.adnStringFieldToString( + spn = IccUtils.adnStringFieldToString( data, 0, data.length - 1); if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn); - phone.setSystemProperty(PROPERTY_SIM_OPERATOR_ALPHA, spn); + phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); }else { if (DBG) log("No SPN loaded in either CHPS or 3GPP"); } @@ -1503,8 +1455,7 @@ public final class SIMRecords extends Handler implements SimConstants * are treated specially when determining SPN display */ private void - parseEfSpdi(byte[] data) - { + parseEfSpdi(byte[] data) { SimTlv tlv = new SimTlv(data, 0, data.length); byte[] plmnEntries = null; @@ -1524,8 +1475,8 @@ public final class SIMRecords extends Handler implements SimConstants spdiNetworks = new ArrayList(plmnEntries.length / 3); for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) { - String plmnCode; - plmnCode = SimUtils.bcdToString(plmnEntries, i, 3); + String plmnCode; + plmnCode = IccUtils.bcdToString(plmnEntries, i, 3); // Valid operator codes are 5 or 6 digits if (plmnCode.length() >= 5) { @@ -1543,7 +1494,11 @@ public final class SIMRecords extends Handler implements SimConstants return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED ); } - private void log(String s) { + protected void log(String s) { Log.d(LOG_TAG, "[SIMRecords] " + s); } + } + + + diff --git a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java deleted file mode 100644 index 5585524..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SMSDispatcher.java +++ /dev/null @@ -1,979 +0,0 @@ -/* - * 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.gsm; - -import android.app.Activity; -import android.app.PendingIntent; -import android.app.AlertDialog; -import android.app.PendingIntent.CanceledException; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.DialogInterface; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.SQLException; -import android.net.Uri; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.Message; -import android.os.PowerManager; -import android.provider.Telephony; -import android.provider.Settings; -import android.provider.Telephony.Sms.Intents; -import android.telephony.gsm.SmsMessage; -import android.telephony.gsm.SmsManager; -import com.android.internal.telephony.WapPushOverSms; -import android.telephony.ServiceState; -import android.util.Config; -import com.android.internal.util.HexDump; -import android.util.Log; -import android.view.WindowManager; - -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Random; - -import com.android.internal.R; - -final class SMSDispatcher extends Handler { - private static final String TAG = "GSM"; - - /** Default checking period for SMS sent without uesr permit */ - private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; - - /** Default number of SMS sent in checking period without uesr permit */ - private static final int DEFAULT_SMS_MAX_COUNT = 100; - - /** Default timeout for SMS sent query */ - private static final int DEFAULT_SMS_TIMOUEOUT = 6000; - - private static final String[] RAW_PROJECTION = new String[] { - "pdu", - "sequence", - }; - - static final int MAIL_SEND_SMS = 1; - - static final int EVENT_NEW_SMS = 1; - - static final int EVENT_SEND_SMS_COMPLETE = 2; - - /** Retry sending a previously failed SMS message */ - static final int EVENT_SEND_RETRY = 3; - - /** Status report received */ - static final int EVENT_NEW_SMS_STATUS_REPORT = 5; - - /** SIM storage is full */ - static final int EVENT_SIM_FULL = 6; - - /** SMS confirm required */ - static final int EVENT_POST_ALERT = 7; - - /** Send the user confirmed SMS */ - static final int EVENT_SEND_CONFIRMED_SMS = 8; - - /** Alert is timeout */ - static final int EVENT_ALERT_TIMEOUT = 9; - - private final GSMPhone mPhone; - - private final WapPushOverSms mWapPush; - - private final Context mContext; - - private final ContentResolver mResolver; - - private final CommandsInterface mCm; - - private final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); - - /** Maximum number of times to retry sending a failed SMS. */ - private static final int MAX_SEND_RETRIES = 3; - /** Delay before next send attempt on a failed SMS, in milliseconds. */ - private static final int SEND_RETRY_DELAY = 2000; - /** single part SMS */ - private static final int SINGLE_PART_SMS = 1; - - /** - * Message reference for a CONCATENATED_8_BIT_REFERENCE or - * CONCATENATED_16_BIT_REFERENCE message set. Should be - * incremented for each set of concatenated messages. - */ - private static int sConcatenatedRef; - - private SmsCounter mCounter; - - private SmsTracker mSTracker; - - /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ - private PowerManager.WakeLock mWakeLock; - - /** - * Hold the wake lock for 5 seconds, which should be enough time for - * any receiver(s) to grab its own wake lock. - */ - private final int WAKE_LOCK_TIMEOUT = 5000; - - /** - * Implement the per-application based SMS control, which only allows - * a limit on the number of SMS/MMS messages an app can send in checking - * period. - */ - private class SmsCounter { - private int mCheckPeriod; - private int mMaxAllowed; - private HashMap> mSmsStamp; - - /** - * Create SmsCounter - * @param mMax is the number of SMS allowed without user permit - * @param mPeriod is the checking period - */ - SmsCounter(int mMax, int mPeriod) { - mMaxAllowed = mMax; - mCheckPeriod = mPeriod; - mSmsStamp = new HashMap> (); - } - - /** - * Check to see if an application allow to send new SMS messages - * - * @param appName is the application sending sms - * @param smsWaiting is the number of new sms wants to be sent - * @return true if application is allowed to send the requested number - * of new sms messages - */ - boolean check(String appName, int smsWaiting) { - if (!mSmsStamp.containsKey(appName)) { - mSmsStamp.put(appName, new ArrayList()); - } - - return isUnderLimit(mSmsStamp.get(appName), smsWaiting); - } - - private boolean isUnderLimit(ArrayList sent, int smsWaiting) { - Long ct = System.currentTimeMillis(); - - Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct); - - while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) { - sent.remove(0); - } - - if ( (sent.size() + smsWaiting) <= mMaxAllowed) { - for (int i = 0; i < smsWaiting; i++ ) { - sent.add(ct); - } - return true; - } - return false; - } - } - - SMSDispatcher(GSMPhone phone) { - mPhone = phone; - mWapPush = new WapPushOverSms(phone); - mContext = phone.getContext(); - mResolver = mContext.getContentResolver(); - mCm = phone.mCM; - mSTracker = null; - - createWakelock(); - - int check_period = Settings.Gservices.getInt(mResolver, - Settings.Gservices.SMS_OUTGOING_CEHCK_INTERVAL_MS, - DEFAULT_SMS_CHECK_PERIOD); - int max_count = Settings.Gservices.getInt(mResolver, - Settings.Gservices.SMS_OUTGOING_CEHCK_MAX_COUNT, - DEFAULT_SMS_MAX_COUNT); - mCounter = new SmsCounter(max_count, check_period); - - mCm.setOnNewSMS(this, EVENT_NEW_SMS, null); - mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); - mCm.setOnSimSmsFull(this, EVENT_SIM_FULL, null); - - // Don't always start message ref at 0. - sConcatenatedRef = new Random().nextInt(256); - } - - /* TODO: Need to figure out how to keep track of status report routing in a - * persistent manner. If the phone process restarts (reboot or crash), - * we will lose this list and any status reports that come in after - * will be dropped. - */ - /** Sent messages awaiting a delivery status report. */ - private final ArrayList deliveryPendingList = new ArrayList(); - - /** - * Handles events coming from the phone stack. Overridden from handler. - * - * @param msg the message to handle - */ - @Override - public void handleMessage(Message msg) { - AsyncResult ar; - - switch (msg.what) { - case EVENT_NEW_SMS: - // A new SMS has been received by the device - if (Config.LOGD) { - Log.d(TAG, "New SMS Message Received"); - } - - SmsMessage sms; - - ar = (AsyncResult) msg.obj; - - // FIXME unit test leaves cm == null. this should change - if (mCm != null) { - // FIXME only acknowledge on store - mCm.acknowledgeLastIncomingSMS(true, null); - } - - if (ar.exception != null) { - Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); - return; - } - - sms = (SmsMessage) ar.result; - dispatchMessage(sms); - - break; - - case EVENT_SEND_SMS_COMPLETE: - // An outbound SMS has been sucessfully transferred, or failed. - handleSendComplete((AsyncResult) msg.obj); - break; - - case EVENT_SEND_RETRY: - sendSms((SmsTracker) msg.obj); - break; - - case EVENT_NEW_SMS_STATUS_REPORT: - handleStatusReport((AsyncResult)msg.obj); - break; - - case EVENT_SIM_FULL: - handleSimFull(); - break; - - case EVENT_POST_ALERT: - handleReachSentLimit((SmsTracker)(msg.obj)); - break; - - case EVENT_ALERT_TIMEOUT: - ((AlertDialog)(msg.obj)).dismiss(); - msg.obj = null; - mSTracker = null; - break; - - case EVENT_SEND_CONFIRMED_SMS: - if (mSTracker!=null) { - if (isMultipartTracker(mSTracker)) { - sendMultipartSms(mSTracker); - } else { - sendSms(mSTracker); - } - mSTracker = null; - } - break; - } - } - - private void createWakelock() { - PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher"); - mWakeLock.setReferenceCounted(true); - } - - private void sendBroadcast(Intent intent, String permission) { - // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any - // receivers time to take their own wake locks. - mWakeLock.acquire(WAKE_LOCK_TIMEOUT); - mContext.sendBroadcast(intent, permission); - } - - /** - * Called when SIM_FULL message is received from the RIL. Notifies interested - * parties that SIM storage for SMS messages is full. - */ - private void handleSimFull() { - // broadcast SIM_FULL intent - Intent intent = new Intent(Intents.SIM_FULL_ACTION); - sendBroadcast(intent, "android.permission.RECEIVE_SMS"); - } - - /** - * Called when a status report is received. This should correspond to - * a previously successful SEND. - * - * @param ar AsyncResult passed into the message handler. ar.result should - * be a String representing the status report PDU, as ASCII hex. - */ - private void handleStatusReport(AsyncResult ar) { - String pduString = (String) ar.result; - SmsMessage sms = SmsMessage.newFromCDS(pduString); - - if (sms != null) { - int messageRef = sms.messageRef; - for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { - SmsTracker tracker = deliveryPendingList.get(i); - if (tracker.mMessageRef == messageRef) { - // Found it. Remove from list and broadcast. - deliveryPendingList.remove(i); - PendingIntent intent = tracker.mDeliveryIntent; - Intent fillIn = new Intent(); - fillIn.putExtra("pdu", SimUtils.hexStringToBytes(pduString)); - try { - intent.send(mContext, Activity.RESULT_OK, fillIn); - } catch (CanceledException ex) {} - - // Only expect to see one tracker matching this messageref - break; - } - } - } - - if (mCm != null) { - mCm.acknowledgeLastIncomingSMS(true, null); - } - } - - /** - * Called when SMS send completes. Broadcasts a sentIntent on success. - * On failure, either sets up retries or broadcasts a sentIntent with - * the failure in the result code. - * - * @param ar AsyncResult passed into the message handler. ar.result should - * an SmsResponse instance if send was successful. ar.userObj - * should be an SmsTracker instance. - */ - private void handleSendComplete(AsyncResult ar) { - SmsTracker tracker = (SmsTracker) ar.userObj; - PendingIntent sentIntent = tracker.mSentIntent; - - if (ar.exception == null) { - if (Config.LOGD) { - Log.d(TAG, "SMS send complete. Broadcasting " - + "intent: " + sentIntent); - } - - if (tracker.mDeliveryIntent != null) { - // Expecting a status report. Add it to the list. - int messageRef = ((SmsResponse)ar.result).messageRef; - tracker.mMessageRef = messageRef; - deliveryPendingList.add(tracker); - } - - if (sentIntent != null) { - try { - sentIntent.send(Activity.RESULT_OK); - } catch (CanceledException ex) {} - } - } else { - if (Config.LOGD) { - Log.d(TAG, "SMS send failed"); - } - - int ss = mPhone.getServiceState().getState(); - - if (ss != ServiceState.STATE_IN_SERVICE) { - handleNotInService(ss, tracker); - } else if ((((CommandException)(ar.exception)).getCommandError() - == CommandException.Error.SMS_FAIL_RETRY) && - tracker.mRetryCount < MAX_SEND_RETRIES) { - // Retry after a delay if needed. - // TODO: According to TS 23.040, 9.2.3.6, we should resend - // with the same TP-MR as the failed message, and - // TP-RD set to 1. However, we don't have a means of - // knowing the MR for the failed message (EF_SMSstatus - // may or may not have the MR corresponding to this - // message, depending on the failure). Also, in some - // implementations this retry is handled by the baseband. - tracker.mRetryCount++; - Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); - sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); - } else if (tracker.mSentIntent != null) { - // Done retrying; return an error to the app. - try { - tracker.mSentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE); - } catch (CanceledException ex) {} - } - } - } - - /** - * Handles outbound message when the phone is not in service. - * - * @param ss Current service state. Valid values are: - * OUT_OF_SERVICE - * EMERGENCY_ONLY - * POWER_OFF - * @param tracker An SmsTracker for the current message. - */ - private void handleNotInService(int ss, SmsTracker tracker) { - if (tracker.mSentIntent != null) { - try { - if (ss == ServiceState.STATE_POWER_OFF) { - tracker.mSentIntent.send(SmsManager.RESULT_ERROR_RADIO_OFF); - } else { - tracker.mSentIntent.send(SmsManager.RESULT_ERROR_NO_SERVICE); - } - } catch (CanceledException ex) {} - } - } - - /** - * Dispatches an incoming SMS messages. - * - * @param sms the incoming message from the phone - */ - /* package */ void dispatchMessage(SmsMessage sms) { - - // If sms is null, means there was a parsing error. - // TODO: Should NAK this. - if (sms == null) { - return; - } - - boolean handled = false; - - // Special case the message waiting indicator messages - if (sms.isMWISetMessage()) { - mPhone.updateMessageWaitingIndicator(true); - - if (sms.isMwiDontStore()) { - handled = true; - } - - if (Config.LOGD) { - Log.d(TAG, - "Received voice mail indicator set SMS shouldStore=" - + !handled); - } - } else if (sms.isMWIClearMessage()) { - mPhone.updateMessageWaitingIndicator(false); - - if (sms.isMwiDontStore()) { - handled = true; - } - - if (Config.LOGD) { - Log.d(TAG, - "Received voice mail indicator clear SMS shouldStore=" - + !handled); - } - } - - if (handled) { - return; - } - - // Parse the headers to see if this is partial, or port addressed - int referenceNumber = -1; - int count = 0; - int sequence = 0; - int destPort = -1; - - SmsHeader header = sms.getUserDataHeader(); - if (header != null) { - for (SmsHeader.Element element : header.getElements()) { - try { - switch (element.getID()) { - case SmsHeader.CONCATENATED_8_BIT_REFERENCE: { - byte[] data = element.getData(); - - referenceNumber = data[0] & 0xff; - count = data[1] & 0xff; - sequence = data[2] & 0xff; - - // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence - // is zero, or sequence > count, ignore the entire element - if (count == 0 || sequence == 0 || sequence > count) { - referenceNumber = -1; - } - break; - } - - case SmsHeader.CONCATENATED_16_BIT_REFERENCE: { - byte[] data = element.getData(); - - referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff); - count = data[2] & 0xff; - sequence = data[3] & 0xff; - - // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence - // is zero, or sequence > count, ignore the entire element - if (count == 0 || sequence == 0 || sequence > count) { - referenceNumber = -1; - } - break; - } - - case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: { - byte[] data = element.getData(); - - destPort = (data[0] & 0xff) << 8; - destPort |= (data[1] & 0xff); - - break; - } - } - } catch (ArrayIndexOutOfBoundsException e) { - Log.e(TAG, "Bad element in header", e); - return; // TODO: NACK the message or something, don't just discard. - } - } - } - - if (referenceNumber == -1) { - // notify everyone of the message if it isn't partial - byte[][] pdus = new byte[1][]; - pdus[0] = sms.getPdu(); - - if (destPort != -1) { - if (destPort == SmsHeader.PORT_WAP_PUSH) { - mWapPush.dispatchWapPdu(sms.getUserData()); - } - // The message was sent to a port, so concoct a URI for it - dispatchPortAddressedPdus(pdus, destPort); - } else { - // It's a normal message, dispatch it - dispatchPdus(pdus); - } - } else { - // Process the message part - processMessagePart(sms, referenceNumber, sequence, count, destPort); - } - } - - /** - * If this is the last part send the parts out to the application, otherwise - * the part is stored for later processing. - */ - private void processMessagePart(SmsMessage sms, int referenceNumber, - int sequence, int count, int destinationPort) { - // Lookup all other related parts - StringBuilder where = new StringBuilder("reference_number ="); - where.append(referenceNumber); - where.append(" AND address = ?"); - String[] whereArgs = new String[] {sms.getOriginatingAddress()}; - - byte[][] pdus = null; - Cursor cursor = null; - try { - cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); - int cursorCount = cursor.getCount(); - if (cursorCount != count - 1) { - // We don't have all the parts yet, store this one away - ContentValues values = new ContentValues(); - values.put("date", new Long(sms.getTimestampMillis())); - values.put("pdu", HexDump.toHexString(sms.getPdu())); - values.put("address", sms.getOriginatingAddress()); - values.put("reference_number", referenceNumber); - values.put("count", count); - values.put("sequence", sequence); - if (destinationPort != -1) { - values.put("destination_port", destinationPort); - } - mResolver.insert(mRawUri, values); - - return; - } - - // All the parts are in place, deal with them - int pduColumn = cursor.getColumnIndex("pdu"); - int sequenceColumn = cursor.getColumnIndex("sequence"); - - pdus = new byte[count][]; - for (int i = 0; i < cursorCount; i++) { - cursor.moveToNext(); - int cursorSequence = (int)cursor.getLong(sequenceColumn); - pdus[cursorSequence - 1] = HexDump.hexStringToByteArray( - cursor.getString(pduColumn)); - } - // This one isn't in the DB, so add it - pdus[sequence - 1] = sms.getPdu(); - - // Remove the parts from the database - mResolver.delete(mRawUri, where.toString(), whereArgs); - } catch (SQLException e) { - Log.e(TAG, "Can't access multipart SMS database", e); - return; // TODO: NACK the message or something, don't just discard. - } finally { - if (cursor != null) cursor.close(); - } - - // Dispatch the PDUs to applications - switch (destinationPort) { - case SmsHeader.PORT_WAP_PUSH: { - // Build up the data stream - ByteArrayOutputStream output = new ByteArrayOutputStream(); - for (int i = 0; i < count; i++) { - SmsMessage msg = SmsMessage.createFromPdu(pdus[i]); - byte[] data = msg.getUserData(); - output.write(data, 0, data.length); - } - - // Handle the PUSH - mWapPush.dispatchWapPdu(output.toByteArray()); - break; - } - - case -1: - // The messages were not sent to a port - dispatchPdus(pdus); - break; - - default: - // The messages were sent to a port, so concoct a URI for it - dispatchPortAddressedPdus(pdus, destinationPort); - break; - } - } - - /** - * Dispatches standard PDUs to interested applications - * - * @param pdus The raw PDUs making up the message - */ - private void dispatchPdus(byte[][] pdus) { - Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); - intent.putExtra("pdus", pdus); - sendBroadcast(intent, "android.permission.RECEIVE_SMS"); - } - - /** - * Dispatches port addressed PDUs to interested applications - * - * @param pdus The raw PDUs making up the message - * @param port The destination port of the messages - */ - private void dispatchPortAddressedPdus(byte[][] pdus, int port) { - Uri uri = Uri.parse("sms://localhost:" + port); - Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); - intent.putExtra("pdus", pdus); - sendBroadcast(intent, "android.permission.RECEIVE_SMS"); - } - - - /** - * Send a multi-part 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 parts an ArrayList of strings that, in order, - * comprise the original message - * @param sentIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * 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 ArrayList of - * PendingIntents (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"). - */ - void sendMultipartText(String destinationAddress, String scAddress, ArrayList parts, - ArrayList sentIntents, ArrayList deliveryIntents) { - - PendingIntent sentIntent = null; - - - int ss = mPhone.getServiceState().getState(); - - if (ss == ServiceState.STATE_IN_SERVICE) { - // Only check SMS sending limit while in service - if (sentIntents != null && sentIntents.size() > 0) { - sentIntent = sentIntents.get(0); - } - String appName = getAppNameByIntent(sentIntent); - if ( !mCounter.check(appName, parts.size())) { - HashMap map = new HashMap(); - map.put("destination", destinationAddress); - map.put("scaddress", scAddress); - map.put("parts", parts); - map.put("sentIntents", sentIntents); - map.put("deliveryIntents", deliveryIntents); - - SmsTracker multipartParameter = new SmsTracker(map, null, null); - - sendMessage(obtainMessage(EVENT_POST_ALERT, multipartParameter)); - return; - } - } - - sendMultipartTextWithPermit(destinationAddress, - scAddress, parts, sentIntents, deliveryIntents); - } - - /** - * Send a multi-part text based SMS which already passed SMS control check. - * - * It is the working function for sendMultipartText(). - * - * @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 ArrayList of strings that, in order, - * comprise the original message - * @param sentIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * @param deliveryIntents if not null, an ArrayList of - * PendingIntents (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"). - */ - private void sendMultipartTextWithPermit(String destinationAddress, - String scAddress, ArrayList parts, - ArrayList sentIntents, - ArrayList deliveryIntents) { - - PendingIntent sentIntent = null; - PendingIntent deliveryIntent = null; - - // check if in service - int ss = mPhone.getServiceState().getState(); - if (ss != ServiceState.STATE_IN_SERVICE) { - for (int i = 0, count = parts.size(); i < count; i++) { - if (sentIntents != null && sentIntents.size() > i) { - sentIntent = sentIntents.get(i); - } - SmsTracker tracker = new SmsTracker(null, sentIntent, null); - handleNotInService(ss, tracker); - } - return; - } - - int ref = ++sConcatenatedRef & 0xff; - - for (int i = 0, count = parts.size(); i < count; i++) { - // build SmsHeader - byte[] data = new byte[3]; - data[0] = (byte) ref; // reference #, unique per message - data[1] = (byte) count; // total part count - data[2] = (byte) (i + 1); // 1-based sequence - SmsHeader header = new SmsHeader(); - header.add(new SmsHeader.Element(SmsHeader.CONCATENATED_8_BIT_REFERENCE, data)); - - if (sentIntents != null && sentIntents.size() > i) { - sentIntent = sentIntents.get(i); - } - if (deliveryIntents != null && deliveryIntents.size() > i) { - deliveryIntent = deliveryIntents.get(i); - } - - SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, - parts.get(i), deliveryIntent != null, header.toByteArray()); - - HashMap map = new HashMap(); - map.put("smsc", pdus.encodedScAddress); - map.put("pdu", pdus.encodedMessage); - - SmsTracker tracker = new SmsTracker(map, sentIntent, - deliveryIntent); - sendSms(tracker); - } - } - - /** - * Send a SMS - * - * @param smsc the SMSC to send the message through, or NULL for the - * defatult SMSC - * @param pdu the raw PDU to send - * @param sentIntent if not NULL this Intent is - * broadcast when the message is sucessfully sent, or failed. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * 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 Intent is - * broadcast when the message is delivered to the recipient. The - * raw pdu of the status report is in the extended data ("pdu"). - */ - void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, - PendingIntent deliveryIntent) { - if (pdu == null) { - if (sentIntent != null) { - try { - sentIntent.send(SmsManager.RESULT_ERROR_NULL_PDU); - } catch (CanceledException ex) {} - } - return; - } - - HashMap map = new HashMap(); - map.put("smsc", smsc); - map.put("pdu", pdu); - - SmsTracker tracker = new SmsTracker(map, sentIntent, - deliveryIntent); - int ss = mPhone.getServiceState().getState(); - - if (ss != ServiceState.STATE_IN_SERVICE) { - handleNotInService(ss, tracker); - } else { - String appName = getAppNameByIntent(sentIntent); - if (mCounter.check(appName, SINGLE_PART_SMS)) { - sendSms(tracker); - } else { - sendMessage(obtainMessage(EVENT_POST_ALERT, tracker)); - } - } - } - - /** - * Post an alert while SMS needs user confirm. - * - * An SmsTracker for the current message. - */ - private void handleReachSentLimit(SmsTracker tracker) { - - Resources r = Resources.getSystem(); - - String appName = getAppNameByIntent(tracker.mSentIntent); - - AlertDialog d = new AlertDialog.Builder(mContext) - .setTitle(r.getString(R.string.sms_control_title)) - .setMessage(appName + " " + r.getString(R.string.sms_control_message)) - .setPositiveButton(r.getString(R.string.sms_control_yes), mListener) - .setNegativeButton(r.getString(R.string.sms_control_no), null) - .create(); - - d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); - d.show(); - - mSTracker = tracker; - sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d), - DEFAULT_SMS_TIMOUEOUT); - } - - private String getAppNameByIntent(PendingIntent intent) { - Resources r = Resources.getSystem(); - return (intent != null) ? intent.getTargetPackage() - : r.getString(R.string.sms_control_default_app_name); - } - - /** - * Send the message along to the radio. - * - * @param tracker holds the SMS message to send - */ - private void sendSms(SmsTracker tracker) { - HashMap map = tracker.mData; - - byte smsc[] = (byte[]) map.get("smsc"); - byte pdu[] = (byte[]) map.get("pdu"); - - Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); - mCm.sendSMS(SimUtils.bytesToHexString(smsc), - SimUtils.bytesToHexString(pdu), reply); - } - - /** - * Send the multi-part SMS based on multipart Sms tracker - * - * @param tracker holds the multipart Sms tracker ready to be sent - */ - private void sendMultipartSms (SmsTracker tracker) { - ArrayList parts; - ArrayList sentIntents; - ArrayList deliveryIntents; - - HashMap map = tracker.mData; - - String destinationAddress = (String) map.get("destination"); - String scAddress = (String) map.get("scaddress"); - - parts = (ArrayList) map.get("parts"); - sentIntents = (ArrayList) map.get("sentIntents"); - deliveryIntents = (ArrayList) map.get("deliveryIntents"); - - sendMultipartTextWithPermit(destinationAddress, - scAddress, parts, sentIntents, deliveryIntents); - - } - - /** - * Check if a SmsTracker holds multi-part Sms - * - * @param tracker a SmsTracker could hold a multi-part Sms - * @return true for tracker holds Multi-parts Sms - */ - private boolean isMultipartTracker (SmsTracker tracker) { - HashMap map = tracker.mData; - return ( map.get("parts") != null); - } - - /** - * Keeps track of an SMS that has been sent to the RIL, until it it has - * successfully been sent, or we're done trying. - * - */ - static class SmsTracker { - HashMap mData; - int mRetryCount; - int mMessageRef; - - PendingIntent mSentIntent; - PendingIntent mDeliveryIntent; - - SmsTracker(HashMap data, PendingIntent sentIntent, - PendingIntent deliveryIntent) { - mData = data; - mSentIntent = sentIntent; - mDeliveryIntent = deliveryIntent; - mRetryCount = 0; - } - - } - - private DialogInterface.OnClickListener mListener = - new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON_POSITIVE) { - Log.d(TAG, "click YES to send out sms"); - sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS)); - } - } - }; -} diff --git a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java deleted file mode 100644 index e336d7d..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/ServiceStateTracker.java +++ /dev/null @@ -1,1696 +0,0 @@ -/* - * 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.gsm; - -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_SIM_OPERATOR_ALPHA; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_SIM_OPERATOR_NUMERIC; - -import com.android.internal.telephony.SimCard; -import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.gsm.DataConnectionTracker.State; - -import android.app.AlarmManager; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.database.ContentObserver; -import android.os.AsyncResult; -import android.os.Handler; -import android.os.Message; -import android.os.PowerManager; -import android.os.Registrant; -import android.os.RegistrantList; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.provider.Checkin; -import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; -import android.provider.Telephony.Intents; -import android.telephony.gsm.GsmCellLocation; -import android.telephony.ServiceState; -import android.text.TextUtils; -import android.util.Config; -import android.util.Log; -import android.util.TimeUtils; -import android.util.EventLog; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - -/** - * {@hide} - */ -final class ServiceStateTracker extends Handler -{ - /** - * The access technology currently in use: - * 0 = unknown - * 1 = GPRS only - * 2 = EDGE - * 3 = UMTS - */ - static final int DATA_ACCESS_UNKNOWN = 0; - static final int DATA_ACCESS_GPRS = 1; - static final int DATA_ACCESS_EDGE = 2; - static final int DATA_ACCESS_UMTS = 3; - - static final int MAX_NUM_DATA_STATE_READS = 15; - static final int DATA_STATE_POLL_SLEEP_MS = 100; - - //***** Instance Variables - - GSMPhone phone; - CommandsInterface cm; - - ServiceState ss; - ServiceState newSS; - GsmCellLocation cellLoc; - GsmCellLocation newCellLoc; - int mPreferredNetworkType; - RestrictedState rs; - - int rssi = 99; // signal strength 0-31, 99=unknown - // That's "received signal strength indication" fyi - - int[] pollingContext; // Used as a unique identifier to - // track requests associated with a poll - // and ignore stale responses. - // The value is a count-down of expected responses - // in this pollingContext - - boolean mDesiredPowerState; - - boolean dontPollSignalStrength = false; // Default is to poll strength - // If we're getting unsolicited signal strength updates from the radio, - // set value to true and don't bother polling any more - - private int gprsState = ServiceState.STATE_OUT_OF_SERVICE; - private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE; - - /** - * The access technology currently in use: DATA_ACCESS_ - */ - private int networkType = 0; - private int newNetworkType = 0; - /* gsm roaming status solely based on TS 27.007 7.2 CREG */ - private boolean mGsmRoaming = false; - - private RegistrantList networkAttachedRegistrants = new RegistrantList(); - private RegistrantList gprsAttachedRegistrants = new RegistrantList(); - private RegistrantList gprsDetachedRegistrants = new RegistrantList(); - private RegistrantList roamingOnRegistrants = new RegistrantList(); - private RegistrantList roamingOffRegistrants = new RegistrantList(); - private RegistrantList psRestrictEnabledRegistrants = new RegistrantList(); - private RegistrantList psRestrictDisabledRegistrants = new RegistrantList(); - - - // Sometimes we get the NITZ time before we know what country we are in. - // Keep the time zone information from the NITZ string so we can fix - // the time zone once know the country. - private boolean mNeedFixZone = false; - private int mZoneOffset; - private boolean mZoneDst; - private long mZoneTime; - private boolean mGotCountryCode = false; - - String mSavedTimeZone; - long mSavedTime; - long mSavedAtTime; - - // We can't register for SIM_RECORDS_LOADED immediately because the - // SIMRecords object may not be instantiated yet. - private boolean mNeedToRegForSimLoaded; - - // Started the recheck process after finding gprs should registerd but not - private boolean mStartedGprsRegCheck = false; - // Already sent the event-log for no gprs register - private boolean mReportedGprsNoReg = false; - - /** - * The Notification object given to the NotificationManager. - */ - private Notification mNotification; - - // Wake lock used while setting time of day. - private PowerManager.WakeLock mWakeLock; - private static final String WAKELOCK_TAG = "ServiceStateTracker"; - - // Keep track of SPN display rules, so we only broadcast intent if something changes. - private String curSpn = null; - private String curPlmn = null; - private int curSpnRule = 0; - - //***** Constants - - static final boolean DBG = true; - static final String LOG_TAG = "GSM"; - - // signal strength poll rate - static final int POLL_PERIOD_MILLIS = 20 * 1000; - - // waiting period before recheck gprs and voice registration - static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000; - - // restricted state type - static final int PS_ENABLED = 1001; // Access Control blocks data service - static final int PS_DISABLED = 1002; // Access Control enables data service - static final int CS_ENABLED = 1003; // Access Control blocks all voice/sms service - static final int CS_DISABLED = 1004; // Access Control enables all voice/sms service - static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service - static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service - - // notification id - static final int PS_NOTIFICATION = 888; //id to update and cancel PS restricted - static final int CS_NOTIFICATION = 999; //id to update and cancel CS restricted - - //***** Events - static final int EVENT_RADIO_STATE_CHANGED = 1; - static final int EVENT_NETWORK_STATE_CHANGED = 2; - static final int EVENT_GET_SIGNAL_STRENGTH = 3; - static final int EVENT_POLL_STATE_REGISTRATION = 4; - static final int EVENT_POLL_STATE_GPRS = 5; - static final int EVENT_POLL_STATE_OPERATOR = 6; - static final int EVENT_POLL_SIGNAL_STRENGTH = 10; - static final int EVENT_NITZ_TIME = 11; - static final int EVENT_SIGNAL_STRENGTH_UPDATE = 12; - static final int EVENT_RADIO_AVAILABLE = 13; - static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14; - static final int EVENT_GET_LOC_DONE = 15; - static final int EVENT_SIM_RECORDS_LOADED = 16; - static final int EVENT_SIM_READY = 17; - static final int EVENT_LOCATION_UPDATES_ENABLED = 18; - static final int EVENT_GET_PREFERRED_NETWORK_TYPE = 19; - static final int EVENT_SET_PREFERRED_NETWORK_TYPE = 20; - static final int EVENT_RESET_PREFERRED_NETWORK_TYPE = 21; - static final int EVENT_CHECK_REPORT_GPRS = 22; - static final int EVENT_RESTRICTED_STATE_CHANGED = 23; - - //***** Time Zones - - private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; - - // List of ISO codes for countries that can have an offset of GMT+0 - // when not in daylight savings time. This ignores some small places - // such as the Canary Islands (Spain) and Danmarkshavn (Denmark). - // The list must be sorted by code. - private static final String[] GMT_COUNTRY_CODES = { - "bf", // Burkina Faso - "ci", // Cote d'Ivoire - "eh", // Western Sahara - "fo", // Faroe Islands, Denmark - "gh", // Ghana - "gm", // Gambia - "gn", // Guinea - "gw", // Guinea Bissau - "ie", // Ireland - "lr", // Liberia - "is", // Iceland - "ma", // Morocco - "ml", // Mali - "mr", // Mauritania - "pt", // Portugal - "sl", // Sierra Leone - "sn", // Senegal - "st", // Sao Tome and Principe - "tg", // Togo - "uk", // U.K - }; - - private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - Log.i("ServiceStateTracker", "Auto time state changed"); - revertToNitz(); - } - }; - - //***** Constructors - - ServiceStateTracker(GSMPhone phone) - { - this.phone = phone; - cm = phone.mCM; - ss = new ServiceState(); - newSS = new ServiceState(); - cellLoc = new GsmCellLocation(); - newCellLoc = new GsmCellLocation(); - rs = new RestrictedState(); - - PowerManager powerManager = - (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); - - cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); - cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); - - cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null); - cm.setOnNITZTime(this, EVENT_NITZ_TIME, null); - cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); - cm.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null); - cm.registerForSIMReady(this, EVENT_SIM_READY, null); - - // system setting property AIRPLANE_MODE_ON is set in Settings. - int airplaneMode = Settings.System.getInt( - phone.getContext().getContentResolver(), - Settings.System.AIRPLANE_MODE_ON, 0); - mDesiredPowerState = ! (airplaneMode > 0); - - ContentResolver cr = phone.getContext().getContentResolver(); - cr.registerContentObserver( - Settings.System.getUriFor(Settings.System.AUTO_TIME), true, - mAutoTimeObserver); - setRssiDefaultValues(); - mNeedToRegForSimLoaded = true; - } - - /** - * Registration point for transition into GPRS attached. - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - */ - /*protected*/ void registerForGprsAttached(Handler h, int what, Object obj) { - Registrant r = new Registrant(h, what, obj); - gprsAttachedRegistrants.add(r); - - if (gprsState == ServiceState.STATE_IN_SERVICE) { - r.notifyRegistrant(); - } - } - - void registerForNetworkAttach(Handler h, int what, Object obj) { - Registrant r = new Registrant(h, what, obj); - networkAttachedRegistrants.add(r); - - if (ss.getState() == ServiceState.STATE_IN_SERVICE) { - r.notifyRegistrant(); - } - } - /** - * Registration point for transition into GPRS detached. - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - */ - /*protected*/ void registerForGprsDetached(Handler h, int what, Object obj) { - Registrant r = new Registrant(h, what, obj); - gprsDetachedRegistrants.add(r); - - if (gprsState == ServiceState.STATE_OUT_OF_SERVICE) { - r.notifyRegistrant(); - } - } - - /** - * Registration point for combined roaming on - * combined roaming is true when roaming is true and ONS differs SPN - * - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - */ - void registerForRoamingOn(Handler h, int what, Object obj) { - Registrant r = new Registrant(h, what, obj); - roamingOnRegistrants.add(r); - - if (ss.getRoaming()) { - r.notifyRegistrant(); - } - } - - /** - * Registration point for combined roaming off - * combined roaming is true when roaming is true and ONS differs SPN - * - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - */ - void registerForRoamingOff(Handler h, int what, Object obj) { - Registrant r = new Registrant(h, what, obj); - roamingOffRegistrants.add(r); - - if (!ss.getRoaming()) { - r.notifyRegistrant(); - } - } - - /** - * Reregister network through toggle perferred network type - * This is a work aorund to deregister and register network since there is - * no ril api to set COPS=2 (deregister) only. - * - * @param onComplete is dispatched when this is complete. it will be - * an AsyncResult, and onComplete.obj.exception will be non-null - * on failure. - */ - void reRegisterNetwork(Message onComplete) { - cm.getPreferredNetworkType( - obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete)); - } - - /** - * Registration point for transition into packet service restricted zone. - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - */ - void registerForPsRestrictedEnabled(Handler h, int what, Object obj) { - Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedEnabled "); - Registrant r = new Registrant(h, what, obj); - psRestrictEnabledRegistrants.add(r); - - if (rs.isPsRestricted()) { - r.notifyRegistrant(); - } - } - - /** - * Registration point for transition out of packet service restricted zone. - * @param h handler to notify - * @param what what code of message when delivered - * @param obj placed in Message.obj - */ - void registerForPsRestrictedDisabled(Handler h, int what, Object obj) { - Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedDisabled "); - Registrant r = new Registrant(h, what, obj); - psRestrictDisabledRegistrants.add(r); - - if (rs.isPsRestricted()) { - r.notifyRegistrant(); - } - } - - //***** Called from GSMPhone - - public void - setRadioPower(boolean power) - { - mDesiredPowerState = power; - - setPowerStateToDesired(); - } - - public void - getLacAndCid(Message onComplete) { - cm.getRegistrationState(obtainMessage( - EVENT_GET_LOC_DONE, onComplete)); - } - - /*package*/ void enableLocationUpdates() { - cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED)); - } - - /*package*/ void disableLocationUpdates() { - cm.setLocationUpdates(false, null); - } - //***** Overridden from Handler - - public void - handleMessage (Message msg) - { - AsyncResult ar; - int[] ints; - String[] strings; - Message message; - - switch (msg.what) { - case EVENT_RADIO_AVAILABLE: - //this is unnecessary - //setPowerStateToDesired(); - break; - - case EVENT_SIM_READY: - // The SIM is now ready i.e if it was locked - // it has been unlocked. At this stage, the radio is already - // powered on. - if (mNeedToRegForSimLoaded) { - phone.mSIMRecords.registerForRecordsLoaded(this, - EVENT_SIM_RECORDS_LOADED, null); - mNeedToRegForSimLoaded = false; - } - // restore the previous network selection. - phone.restoreSavedNetworkSelection(null); - pollState(); - // Signal strength polling stops when radio is off - queueNextSignalStrengthPoll(); - break; - - case EVENT_RADIO_STATE_CHANGED: - // This will do nothing in the radio not - // available case - setPowerStateToDesired(); - pollState(); - break; - - case EVENT_NETWORK_STATE_CHANGED: - pollState(); - break; - - case EVENT_GET_SIGNAL_STRENGTH: - // This callback is called when signal strength is polled - // all by itself - - if (!(cm.getRadioState().isOn())) { - // Polling will continue when radio turns back on - return; - } - ar = (AsyncResult) msg.obj; - onSignalStrengthResult(ar); - queueNextSignalStrengthPoll(); - - break; - - case EVENT_GET_LOC_DONE: - ar = (AsyncResult) msg.obj; - - if (ar.exception == null) { - String states[] = (String[])ar.result; - int lac = -1; - int cid = -1; - if (states.length == 3) { - try { - if (states[1] != null && states[1].length() > 0) { - lac = Integer.parseInt(states[1], 16); - } - if (states[2] != null && states[2].length() > 0) { - cid = Integer.parseInt(states[2], 16); - } - } catch (NumberFormatException ex) { - Log.w(LOG_TAG, "error parsing location: " + ex); - } - } - - // only update if lac or cid changed - if (cellLoc.getCid() != cid || cellLoc.getLac() != lac) { - cellLoc.setLacAndCid(lac, cid); - phone.notifyLocationChanged(); - } - } - - if (ar.userObj != null) { - AsyncResult.forMessage(((Message) ar.userObj)).exception - = ar.exception; - ((Message) ar.userObj).sendToTarget(); - } - break; - - case EVENT_POLL_STATE_REGISTRATION: - case EVENT_POLL_STATE_GPRS: - case EVENT_POLL_STATE_OPERATOR: - case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: - ar = (AsyncResult) msg.obj; - - handlePollStateResult(msg.what, ar); - break; - - case EVENT_POLL_SIGNAL_STRENGTH: - // Just poll signal strength...not part of pollState() - - cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH)); - break; - - case EVENT_NITZ_TIME: - ar = (AsyncResult) msg.obj; - - String nitzString = (String)((Object[])ar.result)[0]; - long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); - - setTimeFromNITZString(nitzString, nitzReceiveTime); - break; - - case EVENT_SIGNAL_STRENGTH_UPDATE: - // This is a notification from - // CommandsInterface.setOnSignalStrengthUpdate - - ar = (AsyncResult) msg.obj; - - // The radio is telling us about signal strength changes - // we don't have to ask it - dontPollSignalStrength = true; - - onSignalStrengthResult(ar); - break; - - case EVENT_SIM_RECORDS_LOADED: - updateSpnDisplay(); - break; - - case EVENT_LOCATION_UPDATES_ENABLED: - ar = (AsyncResult) msg.obj; - - if (ar.exception == null) { - getLacAndCid(null); - } - break; - - case EVENT_SET_PREFERRED_NETWORK_TYPE: - ar = (AsyncResult) msg.obj; - // Don't care the result, only use for dereg network (COPS=2) - message = obtainMessage(EVENT_RESET_PREFERRED_NETWORK_TYPE, ar.userObj); - cm.setPreferredNetworkType(mPreferredNetworkType, message); - break; - - case EVENT_RESET_PREFERRED_NETWORK_TYPE: - ar = (AsyncResult) msg.obj; - if (ar.userObj != null) { - AsyncResult.forMessage(((Message) ar.userObj)).exception - = ar.exception; - ((Message) ar.userObj).sendToTarget(); - } - break; - - case EVENT_GET_PREFERRED_NETWORK_TYPE: - ar = (AsyncResult) msg.obj; - - if (ar.exception == null) { - mPreferredNetworkType = ((int[])ar.result)[0]; - } else { - mPreferredNetworkType = Phone.NT_AUTO_TYPE; - } - - message = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE, ar.userObj); - int toggledNetworkType = - (mPreferredNetworkType == Phone.NT_AUTO_TYPE) ? - Phone.NT_GSM_TYPE : Phone.NT_AUTO_TYPE; - - cm.setPreferredNetworkType(toggledNetworkType, message); - break; - - case EVENT_CHECK_REPORT_GPRS: - if (ss != null && !isGprsConsistant(gprsState, ss.getState())) { - - // Can't register data sevice while voice service is ok - // i.e. CREG is ok while CGREG is not - // possible a network or baseband side error - int cid = -1; - GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); - if (loc != null) cid = loc.getCid(); - - EventLog.List val = new EventLog.List(ss.getOperatorNumeric(), cid); - EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CGREG_FAIL, val); - mReportedGprsNoReg = true; - } - mStartedGprsRegCheck = false; - break; - - case EVENT_RESTRICTED_STATE_CHANGED: - // This is a notification from - // CommandsInterface.setOnRestrictedStateChanged - - Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_RESTRICTED_STATE_CHANGED"); - - ar = (AsyncResult) msg.obj; - - onRestrictedStateChanged(ar); - break; - - } - } - - //***** Private Instance Methods - - private void updateSpnDisplay() { - int rule = phone.mSIMRecords.getDisplayRule(ss.getOperatorNumeric()); - String spn = phone.mSIMRecords.getServiceProviderName(); - String plmn = ss.getOperatorAlphaLong(); - - if (rule != curSpnRule - || !TextUtils.equals(spn, curSpn) - || !TextUtils.equals(plmn, curPlmn)) { - boolean showSpn = - (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN; - boolean showPlmn = - (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN; - Intent intent = new Intent(Intents.SPN_STRINGS_UPDATED_ACTION); - intent.putExtra(Intents.EXTRA_SHOW_SPN, showSpn); - intent.putExtra(Intents.EXTRA_SPN, spn); - intent.putExtra(Intents.EXTRA_SHOW_PLMN, showPlmn); - intent.putExtra(Intents.EXTRA_PLMN, plmn); - phone.getContext().sendStickyBroadcast(intent); - } - curSpnRule = rule; - curSpn = spn; - curPlmn = plmn; - } - - private void - setPowerStateToDesired() - { - // If we want it on and it's off, turn it on - if (mDesiredPowerState - && cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF - ) { - cm.setRadioPower(true, null); - } else if (!mDesiredPowerState && cm.getRadioState().isOn()) { - DataConnectionTracker dcTracker = phone.mDataConnection; - if (! dcTracker.isDataConnectionAsDesired()) { - - EventLog.List val = new EventLog.List( - dcTracker.getStateInString(), - (dcTracker.getAnyDataEnabled() ? 1 : 0) ); - EventLog.writeEvent(TelephonyEventLog.EVENT_DATA_STATE_RADIO_OFF, val); - } - dcTracker.cleanConnectionBeforeRadioOff(); - - // poll data state up to 15 times, with a 100ms delay - // totaling 1.5 sec. Normal data disable action will finish in 100ms. - for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) { - if (dcTracker.state != State.CONNECTED - && dcTracker.state != State.DISCONNECTING) { - Log.d(LOG_TAG, "Data shutdown complete."); - break; - } - SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS); - } - // If it's on and available and we want it off.. - cm.setRadioPower(false, null); - } // Otherwise, we're in the desired state - } - - /** Cancel a pending (if any) pollState() operation */ - private void - cancelPollState() - { - // This will effectively cancel the rest of the poll requests - pollingContext = new int[1]; - } - - /** - * Handle the result of one of the pollState()-related requests - */ - - private void - handlePollStateResult (int what, AsyncResult ar) - { - int ints[]; - String states[]; - - // Ignore stale requests from last poll - if (ar.userObj != pollingContext) return; - - if (ar.exception != null) { - CommandException.Error err=null; - - if (ar.exception instanceof CommandException) { - err = ((CommandException)(ar.exception)).getCommandError(); - } - - if (err == CommandException.Error.RADIO_NOT_AVAILABLE) { - // Radio has crashed or turned off - cancelPollState(); - return; - } - - if (!cm.getRadioState().isOn()) { - // Radio has crashed or turned off - cancelPollState(); - return; - } - - if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW && - err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) { - Log.e(LOG_TAG, - "RIL implementation has returned an error where it must succeed" + - ar.exception); - } - } else try { - switch (what) { - case EVENT_POLL_STATE_REGISTRATION: - states = (String[])ar.result; - int lac = -1; - int cid = -1; - int regState = -1; - if (states.length > 0) { - try { - regState = Integer.parseInt(states[0]); - if (states.length == 3) { - if (states[1] != null && states[1].length() > 0) { - lac = Integer.parseInt(states[1], 16); - } - if (states[2] != null && states[2].length() > 0) { - cid = Integer.parseInt(states[2], 16); - } - } - } catch (NumberFormatException ex) { - Log.w(LOG_TAG, "error parsing RegistrationState: " + ex); - } - } - - mGsmRoaming = regCodeIsRoaming(regState); - newSS.setState (regCodeToServiceState(regState)); - - // LAC and CID are -1 if not avail - newCellLoc.setLacAndCid(lac, cid); - break; - - case EVENT_POLL_STATE_GPRS: - states = (String[])ar.result; - - int type = 0; - regState = -1; - if (states.length > 0) { - try { - regState = Integer.parseInt(states[0]); - - // states[3] (if present) is the current radio technology - if (states.length >= 4 && states[3] != null) { - type = Integer.parseInt(states[3]); - } - } catch (NumberFormatException ex) { - Log.w(LOG_TAG, "error parsing GprsRegistrationState: " + ex); - } - } - newGPRSState = regCodeToServiceState(regState); - newNetworkType = type; - break; - - case EVENT_POLL_STATE_OPERATOR: - String opNames[] = (String[])ar.result; - - if (opNames != null && opNames.length >= 3) { - newSS.setOperatorName ( - opNames[0], opNames[1], opNames[2]); - } - break; - - case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: - ints = (int[])ar.result; - newSS.setIsManualSelection(ints[0] == 1); - break; - } - - } catch (RuntimeException ex) { - Log.e(LOG_TAG, "Exception while polling service state. " - + "Probably malformed RIL response.", ex); - } - - pollingContext[0]--; - - if (pollingContext[0] == 0) { - newSS.setRoaming(isRoamingBetweenOperators(mGsmRoaming, newSS)); - pollStateDone(); - } - - } - - private void - setRssiDefaultValues() - { - rssi = 99; - } - - /** - * A complete "service state" from our perspective is - * composed of a handful of separate requests to the radio. - * - * We make all of these requests at once, but then abandon them - * and start over again if the radio notifies us that some - * event has changed - */ - - private void - pollState() - { - pollingContext = new int[1]; - pollingContext[0] = 0; - - switch (cm.getRadioState()) { - case RADIO_UNAVAILABLE: - newSS.setStateOutOfService(); - newCellLoc.setStateInvalid(); - setRssiDefaultValues(); - mGotCountryCode = false; - - pollStateDone(); - break; - - - case RADIO_OFF: - newSS.setStateOff(); - newCellLoc.setStateInvalid(); - setRssiDefaultValues(); - mGotCountryCode = false; - - pollStateDone(); - break; - - default: - // Issue all poll-related commands at once - // then count down the responses, which - // are allowed to arrive out-of-order - - pollingContext[0]++; - cm.getOperator( - obtainMessage( - EVENT_POLL_STATE_OPERATOR, pollingContext)); - - pollingContext[0]++; - cm.getGPRSRegistrationState( - obtainMessage( - EVENT_POLL_STATE_GPRS, pollingContext)); - - pollingContext[0]++; - cm.getRegistrationState( - obtainMessage( - EVENT_POLL_STATE_REGISTRATION, pollingContext)); - - pollingContext[0]++; - cm.getNetworkSelectionMode( - obtainMessage( - EVENT_POLL_STATE_NETWORK_SELECTION_MODE, pollingContext)); - break; - } - } - - private static String networkTypeToString(int type) { - String ret = "unknown"; - - switch (type) { - case DATA_ACCESS_GPRS: - ret = "GPRS"; - break; - case DATA_ACCESS_EDGE: - ret = "EDGE"; - break; - case DATA_ACCESS_UMTS: - ret = "UMTS"; - break; - } - - return ret; - } - - private void - pollStateDone() - { - if (DBG) { - Log.d(LOG_TAG, "Poll ServiceState done: " + - " oldSS=[" + ss + "] newSS=[" + newSS + - "] oldGprs=" + gprsState + " newGprs=" + newGPRSState + - " oldType=" + networkTypeToString(networkType) + - " newType=" + networkTypeToString(newNetworkType)); - } - - boolean hasRegistered = - ss.getState() != ServiceState.STATE_IN_SERVICE - && newSS.getState() == ServiceState.STATE_IN_SERVICE; - - boolean hasDeregistered = - ss.getState() == ServiceState.STATE_IN_SERVICE - && newSS.getState() != ServiceState.STATE_IN_SERVICE; - - boolean hasGprsAttached = - gprsState != ServiceState.STATE_IN_SERVICE - && newGPRSState == ServiceState.STATE_IN_SERVICE; - - boolean hasGprsDetached = - gprsState == ServiceState.STATE_IN_SERVICE - && newGPRSState != ServiceState.STATE_IN_SERVICE; - - boolean hasNetworkTypeChanged = networkType != newNetworkType; - - boolean hasChanged = !newSS.equals(ss); - - boolean hasRoamingOn = !ss.getRoaming() && newSS.getRoaming(); - - boolean hasRoamingOff = ss.getRoaming() && !newSS.getRoaming(); - - boolean hasLocationChanged = !newCellLoc.equals(cellLoc); - - ServiceState tss; - tss = ss; - ss = newSS; - newSS = tss; - // clean slate for next time - newSS.setStateOutOfService(); - - GsmCellLocation tcl = cellLoc; - cellLoc = newCellLoc; - newCellLoc = tcl; - - gprsState = newGPRSState; - networkType = newNetworkType; - - newSS.setStateOutOfService(); // clean slate for next time - - if (hasNetworkTypeChanged) { - phone.setSystemProperty(PROPERTY_DATA_NETWORK_TYPE, - networkTypeToString(networkType)); - } - - if (hasRegistered) { - Checkin.updateStats(phone.getContext().getContentResolver(), - Checkin.Stats.Tag.PHONE_GSM_REGISTERED, 1, 0.0); - networkAttachedRegistrants.notifyRegistrants(); - } - - if (hasChanged) { - String operatorNumeric; - - phone.setSystemProperty(PROPERTY_OPERATOR_ALPHA, - ss.getOperatorAlphaLong()); - - operatorNumeric = ss.getOperatorNumeric(); - phone.setSystemProperty(PROPERTY_OPERATOR_NUMERIC, operatorNumeric); - - if (operatorNumeric == null) { - phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, ""); - } else { - String iso = ""; - try{ - iso = MccTable.countryCodeForMcc(Integer.parseInt( - operatorNumeric.substring(0,3))); - } catch ( NumberFormatException ex){ - Log.w(LOG_TAG, "countryCodeForMcc error" + ex); - } catch ( StringIndexOutOfBoundsException ex) { - Log.w(LOG_TAG, "countryCodeForMcc error" + ex); - } - - phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, iso); - mGotCountryCode = true; - - if (mNeedFixZone) { - TimeZone zone = null; - // If the offset is (0, false) and the timezone property - // is set, use the timezone property rather than - // GMT. - String zoneName = SystemProperties.get(TIMEZONE_PROPERTY); - if ((mZoneOffset == 0) && (mZoneDst == false) && - (zoneName != null) && (zoneName.length() > 0) && - (Arrays.binarySearch(GMT_COUNTRY_CODES, iso) < 0)) { - zone = TimeZone.getDefault(); - // For NITZ string without timezone, - // need adjust time to reflect default timezone setting - long tzOffset; - tzOffset = zone.getOffset(System.currentTimeMillis()); - if (getAutoTime()) { - setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); - } else { - // Adjust the saved NITZ time to account for tzOffset. - mSavedTime = mSavedTime - tzOffset; - } - } else if (iso.equals("")){ - // Country code not found. This is likely a test network. - // Get a TimeZone based only on the NITZ parameters (best guess). - zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime); - } else { - zone = TimeUtils.getTimeZone(mZoneOffset, - mZoneDst, mZoneTime, iso); - } - - mNeedFixZone = false; - - if (zone != null) { - if (getAutoTime()) { - setAndBroadcastNetworkSetTimeZone(zone.getID()); - } - saveNitzTimeZone(zone.getID()); - } - } - } - - phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING, - ss.getRoaming() ? "true" : "false"); - - updateSpnDisplay(); - phone.notifyServiceStateChanged(ss); - } - - if (hasGprsAttached) { - gprsAttachedRegistrants.notifyRegistrants(); - } - - if (hasGprsDetached) { - gprsDetachedRegistrants.notifyRegistrants(); - } - - if (hasNetworkTypeChanged) { - phone.notifyDataConnection(null); - } - - if (hasRoamingOn) { - roamingOnRegistrants.notifyRegistrants(); - } - - if (hasRoamingOff) { - roamingOffRegistrants.notifyRegistrants(); - } - - if (hasLocationChanged) { - phone.notifyLocationChanged(); - } - - if (! isGprsConsistant(gprsState, ss.getState())) { - if (!mStartedGprsRegCheck && !mReportedGprsNoReg) { - mStartedGprsRegCheck = true; - - int check_period = Settings.Gservices.getInt( - phone.getContext().getContentResolver(), - Settings.Gservices.GPRS_REGISTER_CHECK_PERIOD_MS, - DEFAULT_GPRS_CHECK_PERIOD_MILLIS); - sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS), - check_period); - } - } else { - mReportedGprsNoReg = false; - } - } - - /** - * Check if GPRS got registred while voice is registered - * - * @param gprsState for GPRS registration state, i.e. CGREG in GSM - * @param serviceState for voice registration state, i.e. CREG in GSM - * @return false if device only register to voice but not gprs - */ - private boolean isGprsConsistant (int gprsState, int serviceState) { - return !((serviceState == ServiceState.STATE_IN_SERVICE) && - (gprsState != ServiceState.STATE_IN_SERVICE)); - } - - /** - * Returns a TimeZone object based only on parameters from the NITZ string. - */ - private TimeZone getNitzTimeZone(int offset, boolean dst, long when) { - TimeZone guess = findTimeZone(offset, dst, when); - if (guess == null) { - // Couldn't find a proper timezone. Perhaps the DST data is wrong. - guess = findTimeZone(offset, !dst, when); - } - if (DBG) { - Log.d(LOG_TAG, "getNitzTimeZone returning " - + (guess == null ? guess : guess.getID())); - } - return guess; - } - - private TimeZone findTimeZone(int offset, boolean dst, long when) { - int rawOffset = offset; - if (dst) { - rawOffset -= 3600000; - } - String[] zones = TimeZone.getAvailableIDs(rawOffset); - TimeZone guess = null; - Date d = new Date(when); - for (String zone : zones) { - TimeZone tz = TimeZone.getTimeZone(zone); - if (tz.getOffset(when) == offset && - tz.inDaylightTime(d) == dst) { - guess = tz; - break; - } - } - - return guess; - } - - private void - queueNextSignalStrengthPoll() - { - if (dontPollSignalStrength) { - // The radio is telling us about signal strength changes - // we don't have to ask it - return; - } - - Message msg; - - msg = obtainMessage(); - msg.what = EVENT_POLL_SIGNAL_STRENGTH; - - long nextTime; - - // TODO Done't poll signal strength if screen is off - sendMessageDelayed(msg, POLL_PERIOD_MILLIS); - } - - /** - * send signal-strength-changed notification if rssi changed - * Called both for solicited and unsolicited signal stength updates - */ - private void - onSignalStrengthResult(AsyncResult ar) - { - int oldRSSI = rssi; - - if (ar.exception != null) { - // 99 = unknown - // most likely radio is resetting/disconnected - rssi = 99; - } else { - int[] ints = (int[])ar.result; - - // bug 658816 seems to be a case where the result is 0-length - if (ints.length != 0) { - rssi = ints[0]; - } else { - Log.e(LOG_TAG, "Bogus signal strength response"); - rssi = 99; - } - } - - if (rssi != oldRSSI) { - phone.notifySignalStrength(); - } - } - - /** - * Set restricted state based on the OnRestrictedStateChanged notification - * If any voice or packet restricted state changes, trigger a UI - * notification and notify registrants when sim is ready. - * - * @param ar an int value of RIL_RESTRICTED_STATE_* - */ - private void onRestrictedStateChanged(AsyncResult ar) - { - Log.d(LOG_TAG, "[DSAC DEB] " + "onRestrictedStateChanged"); - RestrictedState newRs = new RestrictedState(); - - Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at enter "+ rs); - - if (ar.exception == null) { - int[] ints = (int[])ar.result; - int state = ints[0]; - - newRs.setCsEmergencyRestricted( - ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) || - ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); - - - //ignore the normal call and data restricted state before SIM READY - if (phone.getSimCard().getState() == SimCard.State.READY){ - newRs.setCsNormalRestricted( - ((state & RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL) != 0) || - ((state & RILConstants.RIL_RESTRICTED_STATE_CS_ALL) != 0) ); - newRs.setPsRestricted( - (state & RILConstants.RIL_RESTRICTED_STATE_PS_ALL)!= 0); - } - - Log.d(LOG_TAG, "[DSAC DEB] " + "new rs "+ newRs); - - if (!rs.isPsRestricted() && newRs.isPsRestricted()) { - psRestrictEnabledRegistrants.notifyRegistrants(); - setNotification(PS_ENABLED); - } else if (rs.isPsRestricted() && !newRs.isPsRestricted()) { - psRestrictDisabledRegistrants.notifyRegistrants(); - setNotification(PS_DISABLED); - } - - /** - * There are two kind of cs restriction, normal and emergency. So - * there are 4 x 4 combinations in current and new restricted states - * and we only need to notify when state is changed. - */ - if (rs.isCsRestricted()) { - if (!newRs.isCsRestricted()) { - // remove all restriction - setNotification(CS_DISABLED); - } else if (!newRs.isCsNormalRestricted()) { - // remove normal restriction - setNotification(CS_EMERGENCY_ENABLED); - } else if (!newRs.isCsEmergencyRestricted()) { - // remove emergency restriction - setNotification(CS_NORMAL_ENABLED); - } - } else if (rs.isCsEmergencyRestricted() && !rs.isCsNormalRestricted()) { - if (!newRs.isCsRestricted()) { - // remove all restriction - setNotification(CS_DISABLED); - } else if (newRs.isCsRestricted()) { - // enable all restriction - setNotification(CS_ENABLED); - } else if (newRs.isCsNormalRestricted()) { - // remove emergency restriction and enable normal restriction - setNotification(CS_NORMAL_ENABLED); - } - } else if (!rs.isCsEmergencyRestricted() && rs.isCsNormalRestricted()) { - if (!newRs.isCsRestricted()) { - // remove all restriction - setNotification(CS_DISABLED); - } else if (newRs.isCsRestricted()) { - // enable all restriction - setNotification(CS_ENABLED); - } else if (newRs.isCsEmergencyRestricted()) { - // remove normal restriction and enable emergency restriction - setNotification(CS_EMERGENCY_ENABLED); - } - } else { - if (newRs.isCsRestricted()) { - // enable all restriction - setNotification(CS_ENABLED); - } else if (newRs.isCsEmergencyRestricted()) { - // enable emergency restriction - setNotification(CS_EMERGENCY_ENABLED); - } else if (newRs.isCsNormalRestricted()) { - // enable normal restriction - setNotification(CS_NORMAL_ENABLED); - } - } - - rs = newRs; - } - Log.d(LOG_TAG, "[DSAC DEB] " + "current rs at return "+ rs); - } - - /** code is registration state 0-5 from TS 27.007 7.2 */ - private int - regCodeToServiceState(int code) - { - switch (code) { - case 0: - case 2: // 2 is "searching" - case 3: // 3 is "registration denied" - case 4: // 4 is "unknown" no vaild in current baseband - return ServiceState.STATE_OUT_OF_SERVICE; - - case 1: - return ServiceState.STATE_IN_SERVICE; - - case 5: - // in service, roam - return ServiceState.STATE_IN_SERVICE; - - default: - Log.w(LOG_TAG, "unexpected service state " + code); - return ServiceState.STATE_OUT_OF_SERVICE; - } - } - - - /** - * code is registration state 0-5 from TS 27.007 7.2 - * returns true if registered roam, false otherwise - */ - private boolean - regCodeIsRoaming (int code) - { - // 5 is "in service -- roam" - return 5 == code; - } - - /** - * Set roaming state when gsmRoaming is true and, if operator mcc is the - * same as sim mcc, ons is different from spn - * @param gsmRoaming TS 27.007 7.2 CREG registered roaming - * @param s ServiceState hold current ons - * @return true for roaming state set - */ - private - boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) { - String spn = SystemProperties.get(PROPERTY_SIM_OPERATOR_ALPHA, "empty"); - - String onsl = s.getOperatorAlphaLong(); - String onss = s.getOperatorAlphaShort(); - - boolean equalsOnsl = onsl != null && spn.equals(onsl); - boolean equalsOnss = onss != null && spn.equals(onss); - - String simNumeric = SystemProperties.get(PROPERTY_SIM_OPERATOR_NUMERIC, ""); - String operatorNumeric = s.getOperatorNumeric(); - - boolean equalsMcc = true; - try { - equalsMcc = simNumeric.substring(0, 3). - equals(operatorNumeric.substring(0, 3)); - } catch (Exception e){ - } - - return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss)); - } - - private static - int twoDigitsAt(String s, int offset) - { - int a, b; - - a = Character.digit(s.charAt(offset), 10); - b = Character.digit(s.charAt(offset+1), 10); - - if (a < 0 || b < 0) { - - throw new RuntimeException("invalid format"); - } - - return a*10 + b; - } - - /** - * @return The current GPRS state. IN_SERVICE is the same as "attached" - * and OUT_OF_SERVICE is the same as detached. - */ - /*package*/ int getCurrentGprsState() { - return gprsState; - } - - /** - * @return true if phone is camping on a technology (eg UMTS) - * that could support voice and data simultaniously. - */ - boolean isConcurrentVoiceAndData() { - return (networkType == DATA_ACCESS_UMTS); - } - - /** - * Provides the name of the algorithmic time zone for the specified - * offset. Taken from TimeZone.java. - */ - private static String displayNameFor(int off) { - off = off / 1000 / 60; - - char[] buf = new char[9]; - buf[0] = 'G'; - buf[1] = 'M'; - buf[2] = 'T'; - - if (off < 0) { - buf[3] = '-'; - off = -off; - } else { - buf[3] = '+'; - } - - int hours = off / 60; - int minutes = off % 60; - - buf[4] = (char) ('0' + hours / 10); - buf[5] = (char) ('0' + hours % 10); - - buf[6] = ':'; - - buf[7] = (char) ('0' + minutes / 10); - buf[8] = (char) ('0' + minutes % 10); - - return new String(buf); - } - - /** - * nitzReceiveTime is time_t that the NITZ time was posted - */ - - private - void setTimeFromNITZString (String nitz, long nitzReceiveTime) - { - // "yy/mm/dd,hh:mm:ss(+/-)tz" - // tz is in number of quarter-hours - - long start = SystemClock.elapsedRealtime(); - Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime + - " start=" + start + " delay=" + (start - nitzReceiveTime)); - - try { - /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone - * offset as well (which we won't worry about until later) */ - Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - - c.clear(); - c.set(Calendar.DST_OFFSET, 0); - - String[] nitzSubs = nitz.split("[/:,+-]"); - - int year = 2000 + Integer.parseInt(nitzSubs[0]); - c.set(Calendar.YEAR, year); - - // month is 0 based! - int month = Integer.parseInt(nitzSubs[1]) - 1; - c.set(Calendar.MONTH, month); - - int date = Integer.parseInt(nitzSubs[2]); - c.set(Calendar.DATE, date); - - int hour = Integer.parseInt(nitzSubs[3]); - c.set(Calendar.HOUR, hour); - - int minute = Integer.parseInt(nitzSubs[4]); - c.set(Calendar.MINUTE, minute); - - int second = Integer.parseInt(nitzSubs[5]); - c.set(Calendar.SECOND, second); - - boolean sign = (nitz.indexOf('-') == -1); - - int tzOffset = Integer.parseInt(nitzSubs[6]); - - int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) - : 0; - - // The zone offset received from NITZ is for current local time, - // so DST correction is already applied. Don't add it again. - // - // tzOffset += dst * 4; - // - // We could unapply it if we wanted the raw offset. - - tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000; - - TimeZone zone = null; - - // As a special extension, the Android emulator appends the name of - // the host computer's timezone to the nitz string. this is zoneinfo - // timezone name of the form Area!Location or Area!Location!SubLocation - // so we need to convert the ! into / - if (nitzSubs.length >= 9) { - String tzname = nitzSubs[8].replace('!','/'); - zone = TimeZone.getTimeZone( tzname ); - } - - String iso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY); - - if (zone == null) { - - if (mGotCountryCode) { - if (iso != null && iso.length() > 0) { - zone = TimeUtils.getTimeZone(tzOffset, dst != 0, - c.getTimeInMillis(), - iso); - } else { - // We don't have a valid iso country code. This is - // most likely because we're on a test network that's - // using a bogus MCC (eg, "001"), so get a TimeZone - // based only on the NITZ parameters. - zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis()); - } - } - } - - if (zone == null) { - // We got the time before the country, so we don't know - // how to identify the DST rules yet. Save the information - // and hope to fix it up later. - - mNeedFixZone = true; - mZoneOffset = tzOffset; - mZoneDst = dst != 0; - mZoneTime = c.getTimeInMillis(); - } - - if (zone != null) { - if (getAutoTime()) { - setAndBroadcastNetworkSetTimeZone(zone.getID()); - } - saveNitzTimeZone(zone.getID()); - } - - String ignore = SystemProperties.get("gsm.ignore-nitz"); - if (ignore != null && ignore.equals("yes")) { - Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set"); - return; - } - - try { - mWakeLock.acquire(); - - if (getAutoTime()) { - long millisSinceNitzReceived - = SystemClock.elapsedRealtime() - nitzReceiveTime; - - if (millisSinceNitzReceived < 0) { - // Sanity check: something is wrong - Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " - + "backwards since NITZ time was received, " - + nitz); - return; - } - - if (millisSinceNitzReceived > Integer.MAX_VALUE) { - // If the time is this far off, something is wrong > 24 days! - Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " - + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) - + " days"); - return; - } - - // Note: with range checks above, cast to int is safe - c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); - - Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() - + " NITZ receive delay(ms): " + millisSinceNitzReceived - + " gained(ms): " - + (c.getTimeInMillis() - System.currentTimeMillis()) - + " from " + nitz); - - setAndBroadcastNetworkSetTime(c.getTimeInMillis()); - Log.i(LOG_TAG, "NITZ: after Setting time of day"); - } - SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); - saveNitzTime(c.getTimeInMillis()); - if (Config.LOGV) { - long end = SystemClock.elapsedRealtime(); - Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); - } - } finally { - mWakeLock.release(); - } - } catch (RuntimeException ex) { - Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex); - } - } - - private boolean getAutoTime() { - try { - return Settings.System.getInt(phone.getContext().getContentResolver(), - Settings.System.AUTO_TIME) > 0; - } catch (SettingNotFoundException snfe) { - return true; - } - } - - private void saveNitzTimeZone(String zoneId) { - mSavedTimeZone = zoneId; - } - - private void saveNitzTime(long time) { - mSavedTime = time; - mSavedAtTime = SystemClock.elapsedRealtime(); - } - - /** - * Set the timezone and send out a sticky broadcast so the system can - * determine if the timezone was set by the carrier. - * - * @param zoneId timezone set by carrier - */ - private void setAndBroadcastNetworkSetTimeZone(String zoneId) { - AlarmManager alarm = - (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); - alarm.setTimeZone(zoneId); - Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); - intent.putExtra("time-zone", zoneId); - phone.getContext().sendStickyBroadcast(intent); - } - - /** - * Set the time and Send out a sticky broadcast so the system can determine - * if the time was set by the carrier. - * - * @param time time set by network - */ - private void setAndBroadcastNetworkSetTime(long time) { - SystemClock.setCurrentTimeMillis(time); - Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); - intent.putExtra("time", time); - phone.getContext().sendStickyBroadcast(intent); - } - - private void revertToNitz() { - if (Settings.System.getInt(phone.getContext().getContentResolver(), - Settings.System.AUTO_TIME, 0) == 0) { - return; - } - Log.d(LOG_TAG, "Reverting to NITZ: tz='" + mSavedTimeZone - + "' mSavedTime=" + mSavedTime - + " mSavedAtTime=" + mSavedAtTime); - if (mSavedTimeZone != null && mSavedTime != 0 && mSavedAtTime != 0) { - setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); - setAndBroadcastNetworkSetTime(mSavedTime - + (SystemClock.elapsedRealtime() - mSavedAtTime)); - } - } - - /** - * Post a notification to NotificationManager for restricted state - * - * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE - */ - private void setNotification(int notifyType) { - - Log.d(LOG_TAG, "[DSAC DEB] " + "create notification " + notifyType); - - Context context = phone.getContext(); - - mNotification = new Notification(); - mNotification.when = System.currentTimeMillis(); - mNotification.flags = Notification.FLAG_AUTO_CANCEL; - mNotification.icon = com.android.internal.R.drawable.stat_sys_warning; - Intent intent = new Intent(); - mNotification.contentIntent = PendingIntent - .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); - - CharSequence details = ""; - CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle); - int notificationId = CS_NOTIFICATION; - - switch (notifyType) { - case PS_ENABLED: - notificationId = PS_NOTIFICATION; - details = context.getText(com.android.internal.R.string.RestrictedOnData);; - break; - case PS_DISABLED: - notificationId = PS_NOTIFICATION; - break; - case CS_ENABLED: - details = context.getText(com.android.internal.R.string.RestrictedOnAll);; - break; - case CS_NORMAL_ENABLED: - details = context.getText(com.android.internal.R.string.RestrictedOnNormal);; - break; - case CS_EMERGENCY_ENABLED: - details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);; - break; - case CS_DISABLED: - // do nothing and cancel the notification later - break; - } - - Log.d(LOG_TAG, "[DSAC DEB] " + "put notification " + title + " / " +details); - mNotification.tickerText = title; - mNotification.setLatestEventInfo(context, title, details, - mNotification.contentIntent); - - NotificationManager notificationManager = (NotificationManager) - context.getSystemService(Context.NOTIFICATION_SERVICE); - - if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) { - // cancel previous post notification - notificationManager.cancel(notificationId); - } else { - // update restricted state notification - notificationManager.notify(notificationId, mNotification); - } - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java new file mode 100644 index 0000000..9af3aa6 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java @@ -0,0 +1,512 @@ +/* + * 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.gsm; + +import android.app.ActivityManagerNative; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.AsyncResult; +import android.os.RemoteException; +import android.os.Handler; +import android.os.Message; +import android.os.Registrant; +import android.os.RegistrantList; +import android.util.Log; + +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.TelephonyProperties; + +import static android.Manifest.permission.READ_PHONE_STATE; + +/** + * Note: this class shares common code with RuimCard, consider a base class to minimize code + * duplication. + * {@hide} + */ +public final class SimCard extends Handler implements IccCard { + static final String LOG_TAG="GSM"; + + //***** Instance Variables + private static final boolean DBG = true; + + private GSMPhone phone; + private CommandsInterface.IccStatus status = null; + private boolean mDesiredPinLocked; + private boolean mDesiredFdnEnabled; + private boolean mSimPinLocked = true; // Default to locked + private boolean mSimFdnEnabled = false; // Default to disabled. + // Will be updated when SIM_READY. + + //***** Constants + + // FIXME I hope this doesn't conflict with the Dialer's notifications + static final int NOTIFICATION_ID_SIM_STATUS = 33456; + + //***** Event Constants + + static final int EVENT_SIM_LOCKED_OR_ABSENT = 1; + static final int EVENT_GET_SIM_STATUS_DONE = 2; + static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3; + static final int EVENT_PINPUK_DONE = 4; + static final int EVENT_REPOLL_STATUS_DONE = 5; + static final int EVENT_SIM_READY = 6; + static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7; + static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8; + static final int EVENT_CHANGE_SIM_PASSWORD_DONE = 9; + static final int EVENT_QUERY_FACILITY_FDN_DONE = 10; + static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11; + + + //***** Constructor + + SimCard(GSMPhone phone) { + this.phone = phone; + + phone.mCM.registerForSIMLockedOrAbsent( + this, EVENT_SIM_LOCKED_OR_ABSENT, null); + + phone.mCM.registerForOffOrNotAvailable( + this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); + + phone.mCM.registerForSIMReady( + this, EVENT_SIM_READY, null); + + updateStateProperty(); + } + + public void dispose() { + //Unregister for all events + phone.mCM.unregisterForSIMLockedOrAbsent(this); + phone.mCM.unregisterForOffOrNotAvailable(this); + phone.mCM.unregisterForSIMReady(this); + } + + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "SimCard finalized"); + } + + //***** SimCard implementation + + public State + getState() { + if (status == null) { + switch(phone.mCM.getRadioState()) { + /* This switch block must not return anything in + * State.isLocked() or State.ABSENT. + * If it does, handleSimStatus() may break + */ + case RADIO_OFF: + case RADIO_UNAVAILABLE: + case SIM_NOT_READY: + return State.UNKNOWN; + case SIM_LOCKED_OR_ABSENT: + //this should be transient-only + return State.UNKNOWN; + case SIM_READY: + return State.READY; + } + } else { + switch (status) { + case ICC_ABSENT: return State.ABSENT; + case ICC_NOT_READY: return State.UNKNOWN; + case ICC_READY: return State.READY; + case ICC_PIN: return State.PIN_REQUIRED; + case ICC_PUK: return State.PUK_REQUIRED; + case ICC_NETWORK_PERSONALIZATION: return State.NETWORK_LOCKED; + } + } + + Log.e(LOG_TAG, "GsmSimCard.getState(): case should never be reached"); + return State.UNKNOWN; + } + + private RegistrantList absentRegistrants = new RegistrantList(); + private RegistrantList pinLockedRegistrants = new RegistrantList(); + private RegistrantList networkLockedRegistrants = new RegistrantList(); + + + public void registerForAbsent(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + absentRegistrants.add(r); + + if (getState() == State.ABSENT) { + r.notifyRegistrant(); + } + } + + public void unregisterForAbsent(Handler h) { + absentRegistrants.remove(h); + } + + public void registerForNetworkLocked(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + networkLockedRegistrants.add(r); + + if (getState() == State.NETWORK_LOCKED) { + r.notifyRegistrant(); + } + } + + public void unregisterForNetworkLocked(Handler h) { + networkLockedRegistrants.remove(h); + } + + public void registerForLocked(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + + pinLockedRegistrants.add(r); + + if (getState().isPinLocked()) { + r.notifyRegistrant(); + } + } + + public void unregisterForLocked(Handler h) { + pinLockedRegistrants.remove(h); + } + + + public void supplyPin (String pin, Message onComplete) { + phone.mCM.supplyIccPin(pin, + obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public void supplyPuk (String puk, String newPin, Message onComplete) { + phone.mCM.supplyIccPuk(puk, newPin, + obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + public void supplyPin2 (String pin2, Message onComplete) { + phone.mCM.supplyIccPin2(pin2, + obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + public void supplyPuk2 (String puk2, String newPin2, Message onComplete) { + phone.mCM.supplyIccPuk2(puk2, newPin2, + obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public void supplyNetworkDepersonalization (String pin, Message onComplete) { + if(DBG) log("Network Despersonalization: " + pin); + phone.mCM.supplyNetworkDepersonalization(pin, + obtainMessage(EVENT_PINPUK_DONE, onComplete)); + } + + public boolean getIccLockEnabled() { + return mSimPinLocked; + } + + public boolean getIccFdnEnabled() { + return mSimFdnEnabled; + } + + public void setIccLockEnabled (boolean enabled, + String password, Message onComplete) { + int serviceClassX; + serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + + CommandsInterface.SERVICE_CLASS_DATA + + CommandsInterface.SERVICE_CLASS_FAX; + + mDesiredPinLocked = enabled; + + phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM, + enabled, password, serviceClassX, + obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete)); + } + + public void setIccFdnEnabled (boolean enabled, + String password, Message onComplete) { + int serviceClassX; + serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + + CommandsInterface.SERVICE_CLASS_DATA + + CommandsInterface.SERVICE_CLASS_FAX + + CommandsInterface.SERVICE_CLASS_SMS; + + mDesiredFdnEnabled = enabled; + + phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD, + enabled, password, serviceClassX, + obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete)); + } + + public void changeIccLockPassword(String oldPassword, String newPassword, + Message onComplete) { + if(DBG) log("Change Pin1 old: " + oldPassword + " new: " + newPassword); + phone.mCM.changeIccPin(oldPassword, newPassword, + obtainMessage(EVENT_CHANGE_SIM_PASSWORD_DONE, onComplete)); + + } + + public void changeIccFdnPassword(String oldPassword, String newPassword, + Message onComplete) { + if(DBG) log("Change Pin2 old: " + oldPassword + " new: " + newPassword); + phone.mCM.changeIccPin2(oldPassword, newPassword, + obtainMessage(EVENT_CHANGE_SIM_PASSWORD_DONE, onComplete)); + + } + + public String getServiceProviderName () { + return phone.mSIMRecords.getServiceProviderName(); + } + + //***** Handler implementation + @Override + public void handleMessage(Message msg){ + AsyncResult ar; + int serviceClassX; + + serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE + + CommandsInterface.SERVICE_CLASS_DATA + + CommandsInterface.SERVICE_CLASS_FAX; + + switch (msg.what) { + case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: + status = null; + updateStateProperty(); + broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_NOT_READY, null); + break; + case EVENT_SIM_READY: + //TODO: put facility read in SIM_READY now, maybe in REG_NW + phone.mCM.getIccStatus(obtainMessage(EVENT_GET_SIM_STATUS_DONE)); + phone.mCM.queryFacilityLock ( + CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, + obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); + phone.mCM.queryFacilityLock ( + CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX, + obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE)); + break; + case EVENT_SIM_LOCKED_OR_ABSENT: + phone.mCM.getIccStatus(obtainMessage(EVENT_GET_SIM_STATUS_DONE)); + phone.mCM.queryFacilityLock ( + CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX, + obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE)); + break; + case EVENT_GET_SIM_STATUS_DONE: + ar = (AsyncResult)msg.obj; + + getSimStatusDone(ar); + break; + case EVENT_PINPUK_DONE: + // a PIN/PUK/PIN2/PUK2/Network Personalization + // request has completed. ar.userObj is the response Message + // Repoll before returning + ar = (AsyncResult)msg.obj; + // TODO should abstract these exceptions + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + phone.mCM.getIccStatus( + obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj)); + break; + case EVENT_REPOLL_STATUS_DONE: + // Finished repolling status after PIN operation + // ar.userObj is the response messaeg + // ar.userObj.obj is already an AsyncResult with an + // appropriate exception filled in if applicable + + ar = (AsyncResult)msg.obj; + getSimStatusDone(ar); + ((Message)ar.userObj).sendToTarget(); + break; + case EVENT_QUERY_FACILITY_LOCK_DONE: + ar = (AsyncResult)msg.obj; + onQueryFacilityLock(ar); + break; + case EVENT_QUERY_FACILITY_FDN_DONE: + ar = (AsyncResult)msg.obj; + onQueryFdnEnabled(ar); + break; + case EVENT_CHANGE_FACILITY_LOCK_DONE: + ar = (AsyncResult)msg.obj; + if (ar.exception == null) { + mSimPinLocked = mDesiredPinLocked; + if (DBG) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " + + "mSimPinLocked= " + mSimPinLocked); + } else { + Log.e(LOG_TAG, "Error change facility lock with exception " + + ar.exception); + } + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + ((Message)ar.userObj).sendToTarget(); + break; + case EVENT_CHANGE_FACILITY_FDN_DONE: + ar = (AsyncResult)msg.obj; + + if (ar.exception == null) { + mSimFdnEnabled = mDesiredFdnEnabled; + if (DBG) log("EVENT_CHANGE_FACILITY_FDN_DONE: " + + "mSimFdnEnabled=" + mSimFdnEnabled); + } else { + Log.e(LOG_TAG, "Error change facility fdn with exception " + + ar.exception); + } + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + ((Message)ar.userObj).sendToTarget(); + break; + case EVENT_CHANGE_SIM_PASSWORD_DONE: + ar = (AsyncResult)msg.obj; + if(ar.exception != null) { + Log.e(LOG_TAG, "Error in change sim password with exception" + + ar.exception); + } + AsyncResult.forMessage(((Message)ar.userObj)).exception + = ar.exception; + ((Message)ar.userObj).sendToTarget(); + break; + default: + Log.e(LOG_TAG, "[GsmSimCard] Unknown Event " + msg.what); + } + } + + + //***** Private methods + + /** + * Interperate EVENT_QUERY_FACILITY_LOCK_DONE + * @param ar is asyncResult of Query_Facility_Locked + */ + private void onQueryFacilityLock(AsyncResult ar) { + if(ar.exception != null) { + if (DBG) log("Error in querying facility lock:" + ar.exception); + return; + } + + int[] ints = (int[])ar.result; + if(ints.length != 0) { + mSimPinLocked = (0!=ints[0]); + if(DBG) log("Query facility lock : " + mSimPinLocked); + } else { + Log.e(LOG_TAG, "[GsmSimCard] Bogus facility lock response"); + } + } + + /** + * Interperate EVENT_QUERY_FACILITY_LOCK_DONE + * @param ar is asyncResult of Query_Facility_Locked + */ + private void onQueryFdnEnabled(AsyncResult ar) { + if(ar.exception != null) { + if(DBG) log("Error in querying facility lock:" + ar.exception); + return; + } + + int[] ints = (int[])ar.result; + if(ints.length != 0) { + mSimFdnEnabled = (0!=ints[0]); + if(DBG) log("Query facility lock : " + mSimFdnEnabled); + } else { + Log.e(LOG_TAG, "[GsmSimCard] Bogus facility lock response"); + } + } + + private void + getSimStatusDone(AsyncResult ar) { + if (ar.exception != null) { + Log.e(LOG_TAG,"Error getting ICC status. " + + "RIL_REQUEST_GET_ICC_STATUS should " + + "never return an error", ar.exception); + return; + } + + CommandsInterface.IccStatus newStatus + = (CommandsInterface.IccStatus) ar.result; + + handleSimStatus(newStatus); + } + + private void + handleSimStatus(CommandsInterface.IccStatus newStatus) { + boolean transitionedIntoPinLocked; + boolean transitionedIntoAbsent; + boolean transitionedIntoNetworkLocked; + + SimCard.State oldState, newState; + + oldState = getState(); + status = newStatus; + newState = getState(); + + updateStateProperty(); + + transitionedIntoPinLocked = ( + (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED) + || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED)); + transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT); + transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED + && newState == State.NETWORK_LOCKED); + + if (transitionedIntoPinLocked) { + if(DBG) log("Notify SIM pin or puk locked."); + pinLockedRegistrants.notifyRegistrants(); + broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_LOCKED, + (newState == State.PIN_REQUIRED) ? + INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK); + } else if (transitionedIntoAbsent) { + if(DBG) log("Notify SIM missing."); + absentRegistrants.notifyRegistrants(); + broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_ABSENT, null); + } else if (transitionedIntoNetworkLocked) { + if(DBG) log("Notify SIM network locked."); + networkLockedRegistrants.notifyRegistrants(); + broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_LOCKED, + INTENT_VALUE_LOCKED_NETWORK); + } + } + + public void broadcastSimStateChangedIntent(String value, String reason) { + Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + intent.putExtra(Phone.PHONE_NAME_KEY, phone.getPhoneName()); + intent.putExtra(SimCard.INTENT_KEY_ICC_STATE, value); + intent.putExtra(SimCard.INTENT_KEY_LOCKED_REASON, reason); + if(DBG) log("Broadcasting intent SIM_STATE_CHANGED_ACTION " + value + + " reason " + reason); + ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE); + } + + public void updateImsiConfiguration(String imsi) { + if (imsi.length() >= 6) { + Configuration config = new Configuration(); + config.mcc = ((imsi.charAt(0)-'0')*100) + + ((imsi.charAt(1)-'0')*10) + + (imsi.charAt(2)-'0'); + config.mnc = ((imsi.charAt(3)-'0')*100) + + ((imsi.charAt(4)-'0')*10) + + (imsi.charAt(5)-'0'); + try { + ActivityManagerNative.getDefault().updateConfiguration(config); + } catch (RemoteException e) { + } + } + } + + private void + updateStateProperty() { + phone.setSystemProperty( + TelephonyProperties.PROPERTY_SIM_STATE, + getState().toString()); + } + + private void log(String msg) { + Log.d(LOG_TAG, "[GsmSimCard] " + msg); + } +} + diff --git a/telephony/java/com/android/internal/telephony/gsm/SimConstants.java b/telephony/java/com/android/internal/telephony/gsm/SimConstants.java deleted file mode 100644 index a7e3bbc..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SimConstants.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.gsm; - -/** - * {@hide} - */ -public interface SimConstants { - // SIM file ids from TS 51.011 - public static final int EF_ADN = 0x6F3A; - public static final int EF_FDN = 0x6F3B; - public static final int EF_SDN = 0x6F49; - public static final int EF_EXT1 = 0x6F4A; - public static final int EF_EXT2 = 0x6F4B; - public static final int EF_EXT3 = 0x6F4C; - public static final int EF_EXT6 = 0x6fc8; // Ext record for EF[MBDN] - public static final int EF_MWIS = 0x6FCA; - public static final int EF_MBDN = 0x6fc7; - public static final int EF_PNN = 0x6fc5; - public static final int EF_SPN = 0x6F46; - public static final int EF_SMS = 0x6F3C; - public static final int EF_ICCID = 0x2fe2; - public static final int EF_AD = 0x6FAD; - public static final int EF_MBI = 0x6fc9; - public static final int EF_MSISDN = 0x6f40; - public static final int EF_SPDI = 0x6fcd; - public static final int EF_SST = 0x6f38; - public static final int EF_CFIS = 0x6FCB; - public static final int EF_IMG = 0x4f20; - - // SIM file ids from CPHS (phase 2, version 4.2) CPHS4_2.WW6 - public static final int EF_MAILBOX_CPHS = 0x6F17; - public static final int EF_VOICE_MAIL_INDICATOR_CPHS = 0x6F11; - public static final int EF_CFF_CPHS = 0x6F13; - public static final int EF_SPN_CPHS = 0x6f14; - public static final int EF_SPN_SHORT_CPHS = 0x6f18; - public static final int EF_INFO_CPHS = 0x6f16; - - // SMS record length from TS 51.011 10.5.3 - static public final int SMS_RECORD_LENGTH = 176; -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SimException.java b/telephony/java/com/android/internal/telephony/gsm/SimException.java deleted file mode 100644 index 1c0daba..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SimException.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.gsm; - -/** - * {@hide} - */ -public class SimException extends Exception -{ - SimException() - { - - } - - SimException(String s) - { - super(s); - } -} - -final class SimVmFixedException extends SimException { - SimVmFixedException() - { - - } - - SimVmFixedException(String s) - { - super(s); - } -} - -final class SimVmNotSupportedException extends SimException { - SimVmNotSupportedException() - { - - } - - SimVmNotSupportedException(String s) - { - super(s); - } -} - diff --git a/telephony/java/com/android/internal/telephony/gsm/SimFileNotFound.java b/telephony/java/com/android/internal/telephony/gsm/SimFileNotFound.java deleted file mode 100644 index 982e2fd..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SimFileNotFound.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.gsm; - -/** - * {@hide} - */ -public class SimFileNotFound extends SimException -{ - SimFileNotFound() - { - - } - - SimFileNotFound(String s) - { - super(s); - } - - SimFileNotFound(int ef) - { - super("SIM EF Not Found 0x" + Integer.toHexString(ef)); - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SimFileTypeMismatch.java b/telephony/java/com/android/internal/telephony/gsm/SimFileTypeMismatch.java deleted file mode 100644 index 72790d0..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SimFileTypeMismatch.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.gsm; - -/** - * {@hide} - */ -public class SimFileTypeMismatch extends SimException -{ - SimFileTypeMismatch() - { - - } - - SimFileTypeMismatch(String s) - { - super(s); - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SimIoResult.java b/telephony/java/com/android/internal/telephony/gsm/SimIoResult.java deleted file mode 100644 index 2c4da83..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SimIoResult.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.gsm; - -/** - * {@hide} - */ -public class -SimIoResult -{ - int sw1; - int sw2; - byte[] payload; - - public SimIoResult(int sw1, int sw2, byte[] payload) - { - this.sw1 = sw1; - this.sw2 = sw2; - this.payload = payload; - } - - public SimIoResult(int sw1, int sw2, String hexString) - { - this(sw1, sw2, SimUtils.hexStringToBytes(hexString)); - } - - public String toString() - { - return "SimIoResponse sw1:0x" + Integer.toHexString(sw1) + " sw2:0x" - + Integer.toHexString(sw2); - } - - /** - * true if this operation was successful - * See GSM 11.11 Section 9.4 - * (the fun stuff is absent in 51.011) - */ - public boolean success() - { - return sw1 == 0x90 || sw1 == 0x91 || sw1 == 0x9e || sw1 == 0x9f; - } - - /** - * Returns exception on error or null if success - */ - public SimException getException() - { - if (success()) return null; - - switch (sw1) { - case 0x94: - if (sw2 == 0x08) { - return new SimFileTypeMismatch(); - } else { - return new SimFileNotFound(); - } - default: - return new SimException("sw1:" + sw1 + " sw2:" + sw2); - } - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java index 7cc9a80..076da6b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java @@ -25,6 +25,11 @@ import android.os.ServiceManager; import android.telephony.PhoneNumberUtils; import android.util.Log; +import com.android.internal.telephony.AdnRecord; +import com.android.internal.telephony.AdnRecordCache; +import com.android.internal.telephony.IccPhoneBookInterfaceManager; +import com.android.internal.telephony.PhoneProxy; + import java.util.ArrayList; import java.util.List; @@ -32,247 +37,65 @@ import java.util.List; * SimPhoneBookInterfaceManager to provide an inter-process communication to * access ADN-like SIM records. */ -public class SimPhoneBookInterfaceManager extends ISimPhoneBook.Stub { - static final String LOG_TAG = "GSM"; - static final boolean DBG = false; - private GSMPhone phone; - private AdnRecordCache adnCache; - private final Object mLock = new Object(); - private int recordSize[]; - private boolean success; - private List records; - private static final boolean ALLOW_SIM_OP_IN_UI_THREAD = false; +public class SimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager { + static final String LOG_TAG = "GSM"; - private static final int EVENT_GET_SIZE_DONE = 1; - private static final int EVENT_LOAD_DONE = 2; - private static final int EVENT_UPDATE_DONE = 3; Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { AsyncResult ar; - switch (msg.what) { - case EVENT_GET_SIZE_DONE: - ar = (AsyncResult) msg.obj; - synchronized (mLock) { - if (ar.exception == null) { - recordSize = (int[])ar.result; - // recordSize[0] is the record length - // recordSize[1] is the total length of the EF file - // recordSize[2] is the number of records in the EF file - log("GET_RECORD_SIZE Size " + recordSize[0] + - " total " + recordSize[1] + - " #record " + recordSize[2]); - mLock.notifyAll(); - } - } - break; - case EVENT_UPDATE_DONE: - ar = (AsyncResult) msg.obj; - synchronized (mLock) { - success = (ar.exception == null); - mLock.notifyAll(); - } - break; - case EVENT_LOAD_DONE: - ar = (AsyncResult)msg.obj; - synchronized (mLock) { - if (ar.exception == null) { - records = (List) - ((ArrayList) ar.result); - } else { - if(DBG) log("Cannot load ADN records"); - if (records != null) { - records.clear(); - } - } - mLock.notifyAll(); - } + switch(msg.what) { + default: + mBaseHandler.handleMessage(msg); break; } } }; public SimPhoneBookInterfaceManager(GSMPhone phone) { - this.phone = phone; + super(phone); adnCache = phone.mSIMRecords.getAdnCache(); - publish(); + //NOTE service "simphonebook" added by IccSmsInterfaceManagerProxy } - private void publish() { - ServiceManager.addService("simphonebook", this); + public void dispose() { + super.dispose(); } - /** - * Replace oldAdn with newAdn in ADN-like record in EF - * - * getAdnRecordsInEf must be called at least once before this function, - * otherwise an error will be returned - * throws SecurityException if no WRITE_CONTACTS permission - * - * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN - * @param oldTag adn tag to be replaced - * @param oldPhoneNumber adn number to be replaced - * Set both oldTag and oldPhoneNubmer to "" means to replace an - * empty record, aka, insert new record - * @param newTag adn tag to be stored - * @param newPhoneNumber adn number ot be stored - * Set both newTag and newPhoneNubmer to "" means to replace the old - * record with empty one, aka, delete old record - * @param pin2 required to update EF_FDN, otherwise must be null - * @return true for success - */ - public boolean - updateAdnRecordsInEfBySearch (int efid, - String oldTag, String oldPhoneNumber, - String newTag, String newPhoneNumber, String pin2) { - - - if (phone.getContext().checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_CONTACTS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires android.permission.WRITE_CONTACTS permission"); - } - - - if (DBG) log("updateAdnRecordsInEfBySearch: efid=" + efid + - " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" + - " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); - synchronized(mLock) { - checkThread(); - success = false; - Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); - AdnRecord oldAdn = new AdnRecord(oldTag, oldPhoneNumber); - AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); - adnCache.updateAdnBySearch(efid, oldAdn, newAdn, pin2, response); - try { - mLock.wait(); - } catch (InterruptedException e) { - log("interrupted while trying to update by search"); - } - } - return success; - } - - /** - * Update an ADN-like EF record by record index - * - * This is useful for iteration the whole ADN file, such as write the whole - * phone book or erase/format the whole phonebook - * throws SecurityException if no WRITE_CONTACTS permission - * - * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN - * @param newTag adn tag to be stored - * @param newPhoneNumber adn number to be stored - * Set both newTag and newPhoneNubmer to "" means to replace the old - * record with empty one, aka, delete old record - * @param index is 1-based adn record index to be updated - * @param pin2 required to update EF_FDN, otherwise must be null - * @return true for success - */ - public boolean - updateAdnRecordsInEfByIndex(int efid, String newTag, - String newPhoneNumber, int index, String pin2) { - - if (phone.getContext().checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_CONTACTS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires android.permission.WRITE_CONTACTS permission"); - } - - if (DBG) log("updateAdnRecordsInEfByIndex: efid=" + efid + - " Index=" + index + " ==> " + - "("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); - synchronized(mLock) { - checkThread(); - success = false; - Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); - AdnRecord newAdn = new AdnRecord(newTag, newPhoneNumber); - adnCache.updateAdnByIndex(efid, newAdn, index, pin2, response); - try { - mLock.wait(); - } catch (InterruptedException e) { - log("interrupted while trying to update by index"); - } - } - return success; + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "SimPhoneBookInterfaceManager finalized"); } - /** - * Get the capacity of records in efid - * - * @param efid the EF id of a ADN-like SIM - * @return int[3] array - * recordSizes[0] is the single record length - * recordSizes[1] is the total length of the EF file - * recordSizes[2] is the number of records in the EF file - */ public int[] getAdnRecordsSize(int efid) { - if (DBG) log("getAdnRecordsSize: efid=" + efid); + if (DBG) logd("getAdnRecordsSize: efid=" + efid); synchronized(mLock) { checkThread(); recordSize = new int[3]; - Message response = mHandler.obtainMessage(EVENT_GET_SIZE_DONE); - phone.mSIMFileHandler.getEFLinearRecordSize(efid, response); - try { - mLock.wait(); - } catch (InterruptedException e) { - log("interrupted while trying to load from the SIM"); - } - } - return recordSize; - } + //Using mBaseHandler, no difference in EVENT_GET_SIZE_DONE handling + Message response = mBaseHandler.obtainMessage(EVENT_GET_SIZE_DONE); - /** - * Loads the AdnRecords in efid and returns them as a - * List of AdnRecords - * - * throws SecurityException if no READ_CONTACTS permission - * - * @param efid the EF id of a ADN-like SIM - * @return List of AdnRecord - */ - public List getAdnRecordsInEf(int efid) { - - if (phone.getContext().checkCallingOrSelfPermission( - android.Manifest.permission.READ_CONTACTS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires android.permission.READ_CONTACTS permission"); - } - - if (DBG) log("getAdnRecordsInEF: efid=" + efid); - - synchronized(mLock) { - checkThread(); - Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); - adnCache.requestLoadAllAdnLike(efid, response); + phone.getIccFileHandler().getEFLinearRecordSize(efid, response); try { mLock.wait(); } catch (InterruptedException e) { - log("interrupted while trying to load from the SIM"); + logd("interrupted while trying to load from the SIM"); } } - return records; + + return recordSize; } - private void checkThread() { - if (!ALLOW_SIM_OP_IN_UI_THREAD) { - // Make sure this isn't the UI thread, since it will block - if (mHandler.getLooper().equals(Looper.myLooper())) { - Log.e(LOG_TAG, "query() called on the main UI thread!"); - throw new IllegalStateException("You cannot call query on this provder from the main UI thread."); - } - } + protected void logd(String msg) { + Log.d(LOG_TAG, "[SimPbInterfaceManager] " + msg); } - private void log(String msg) { - Log.d(LOG_TAG, "[SpbInterfaceManager] " + msg); + protected void loge(String msg) { + Log.e(LOG_TAG, "[SimPbInterfaceManager] " + msg); } } + diff --git a/telephony/java/com/android/internal/telephony/gsm/SimProvider.java b/telephony/java/com/android/internal/telephony/gsm/SimProvider.java deleted file mode 100644 index cece4ba..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SimProvider.java +++ /dev/null @@ -1,455 +0,0 @@ -/* - * 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.gsm; - -import android.content.ContentProvider; -import android.content.UriMatcher; -import android.content.ContentValues; -import com.android.internal.database.ArrayListCursor; -import android.database.Cursor; -import android.net.Uri; -import android.os.SystemProperties; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.text.TextUtils; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@hide} - */ -public class SimProvider extends ContentProvider { - private static final String TAG = "SimProvider"; - private static final boolean DBG = false; - - - private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { - "name", - "number" - }; - - private static final int ADN = 1; - private static final int FDN = 2; - private static final int SDN = 3; - - private static final String STR_TAG = "tag"; - private static final String STR_NUMBER = "number"; - private static final String STR_PIN2 = "pin2"; - - private static final UriMatcher URL_MATCHER = - new UriMatcher(UriMatcher.NO_MATCH); - - static { - URL_MATCHER.addURI("sim", "adn", ADN); - URL_MATCHER.addURI("sim", "fdn", FDN); - URL_MATCHER.addURI("sim", "sdn", SDN); - } - - - private boolean mSimulator; - - @Override - public boolean onCreate() { - String device = SystemProperties.get("ro.product.device"); - if (!TextUtils.isEmpty(device)) { - mSimulator = false; - } else { - // simulator - mSimulator = true; - } - - return true; - } - - @Override - public Cursor query(Uri url, String[] projection, String selection, - String[] selectionArgs, String sort) { - ArrayList results; - - if (!mSimulator) { - switch (URL_MATCHER.match(url)) { - case ADN: - results = loadFromEf(SimConstants.EF_ADN); - break; - - case FDN: - results = loadFromEf(SimConstants.EF_FDN); - break; - - case SDN: - results = loadFromEf(SimConstants.EF_SDN); - break; - - default: - throw new IllegalArgumentException("Unknown URL " + url); - } - } else { - // Fake up some data for the simulator - results = new ArrayList(4); - ArrayList contact; - - contact = new ArrayList(); - contact.add("Ron Stevens/H"); - contact.add("512-555-5038"); - results.add(contact); - - contact = new ArrayList(); - contact.add("Ron Stevens/M"); - contact.add("512-555-8305"); - results.add(contact); - - contact = new ArrayList(); - contact.add("Melissa Owens"); - contact.add("512-555-8305"); - results.add(contact); - - contact = new ArrayList(); - contact.add("Directory Assistence"); - contact.add("411"); - results.add(contact); - } - - return new ArrayListCursor(ADDRESS_BOOK_COLUMN_NAMES, results); - } - - @Override - public String getType(Uri url) { - switch (URL_MATCHER.match(url)) { - case ADN: - case FDN: - case SDN: - return "vnd.android.cursor.dir/sim-contact"; - - default: - throw new IllegalArgumentException("Unknown URL " + url); - } - } - - @Override - public Uri insert(Uri url, ContentValues initialValues) { - Uri resultUri; - int efType; - String pin2 = null; - - if (DBG) log("insert"); - - int match = URL_MATCHER.match(url); - switch (match) { - case ADN: - efType = SimConstants.EF_ADN; - break; - - case FDN: - efType = SimConstants.EF_FDN; - pin2 = initialValues.getAsString("pin2"); - break; - - default: - throw new UnsupportedOperationException( - "Cannot insert into URL: " + url); - } - - String tag = initialValues.getAsString("tag"); - String number = initialValues.getAsString("number"); - boolean success = addSimRecordToEf(efType, tag, number, pin2); - - if (!success) { - return null; - } - - StringBuilder buf = new StringBuilder("content://im/"); - switch (match) { - case ADN: - buf.append("adn/"); - break; - - case FDN: - buf.append("fdn/"); - break; - } - - // TODO: we need to find out the rowId for the newly added record - buf.append(0); - - resultUri = Uri.parse(buf.toString()); - - /* - // notify interested parties that an insertion happened - getContext().getContentResolver().notifyInsert( - resultUri, rowID, null); - */ - - return resultUri; - } - - private String normalizeValue(String inVal) { - int len = inVal.length(); - String retVal = inVal; - - if (inVal.charAt(0) == '\'' && inVal.charAt(len-1) == '\'') { - retVal = inVal.substring(1, len-1); - } - - return retVal; - } - - @Override - public int delete(Uri url, String where, String[] whereArgs) { - int efType; - - if (DBG) log("delete"); - - int match = URL_MATCHER.match(url); - switch (match) { - case ADN: - efType = SimConstants.EF_ADN; - break; - - case FDN: - efType = SimConstants.EF_FDN; - break; - - default: - throw new UnsupportedOperationException( - "Cannot insert into URL: " + url); - } - - // parse where clause - String tag = null; - String number = null; - String pin2 = null; - - String[] tokens = where.split("AND"); - int n = tokens.length; - - while (--n >= 0) { - String param = tokens[n]; - if (DBG) log("parsing '" + param + "'"); - - String[] pair = param.split("="); - - if (pair.length != 2) { - Log.e(TAG, "resolve: bad whereClause parameter: " + param); - continue; - } - - String key = pair[0].trim(); - String val = pair[1].trim(); - - if (STR_TAG.equals(key)) { - tag = normalizeValue(val); - } else if (STR_NUMBER.equals(key)) { - number = normalizeValue(val); - } else if (STR_PIN2.equals(key)) { - pin2 = normalizeValue(val); - } - } - - if (TextUtils.isEmpty(tag)) { - return 0; - } - - if (efType == FDN && TextUtils.isEmpty(pin2)) { - return 0; - } - - boolean success = deleteSimRecordFromEf(efType, tag, number, pin2); - if (!success) { - return 0; - } - - return 1; - } - - @Override - public int update(Uri url, ContentValues values, String where, String[] whereArgs) { - int efType; - String pin2 = null; - - if (DBG) log("update"); - - int match = URL_MATCHER.match(url); - switch (match) { - case ADN: - efType = SimConstants.EF_ADN; - break; - - case FDN: - efType = SimConstants.EF_FDN; - pin2 = values.getAsString("pin2"); - break; - - default: - throw new UnsupportedOperationException( - "Cannot insert into URL: " + url); - } - - String tag = values.getAsString("tag"); - String number = values.getAsString("number"); - String newTag = values.getAsString("newTag"); - String newNumber = values.getAsString("newNumber"); - - boolean success = updateSimRecordInEf(efType, tag, number, - newTag, newNumber, pin2); - - if (!success) { - return 0; - } - - return 1; - } - - private ArrayList loadFromEf(int efType) { - ArrayList results = new ArrayList(); - List adnRecords = null; - - if (DBG) log("loadFromEf: efType=" + efType); - - try { - ISimPhoneBook simIpb = ISimPhoneBook.Stub.asInterface( - ServiceManager.getService("simphonebook")); - if (simIpb != null) { - adnRecords = simIpb.getAdnRecordsInEf(efType); - } - } catch (RemoteException ex) { - // ignore it - } catch (SecurityException ex) { - if (DBG) log(ex.toString()); - } - - if (adnRecords != null) { - // Load the results - - int N = adnRecords.size(); - if (DBG) log("adnRecords.size=" + N); - for (int i = 0; i < N ; i++) { - loadRecord(adnRecords.get(i), results); - } - } else { - // No results to load - Log.w(TAG, "Cannot load ADN records"); - results.clear(); - } - if (DBG) log("loadFromEf: return results"); - return results; - } - - private boolean - addSimRecordToEf(int efType, String name, String number, String pin2) { - if (DBG) log("addSimRecordToEf: efType=" + efType + ", name=" + name + - ", number=" + number); - - boolean success = false; - - // TODO: do we need to call getAdnRecordsInEf() before calling - // updateAdnRecordsInEfBySearch()? In any case, we will leave - // the UI level logic to fill that prereq if necessary. But - // hopefully, we can remove this requirement. - try { - ISimPhoneBook simIpb = ISimPhoneBook.Stub.asInterface( - ServiceManager.getService("simphonebook")); - if (simIpb != null) { - success = simIpb.updateAdnRecordsInEfBySearch(efType, "", "", - name, number, pin2); - } - } catch (RemoteException ex) { - // ignore it - } catch (SecurityException ex) { - if (DBG) log(ex.toString()); - } - if (DBG) log("addSimRecordToEf: " + success); - return success; - } - - private boolean - updateSimRecordInEf(int efType, String oldName, String oldNumber, - String newName, String newNumber,String pin2) { - if (DBG) log("updateSimRecordInEf: efType=" + efType + - ", oldname=" + oldName + ", oldnumber=" + oldNumber + - ", newname=" + newName + ", newnumber=" + newNumber); - boolean success = false; - - try { - ISimPhoneBook simIpb = ISimPhoneBook.Stub.asInterface( - ServiceManager.getService("simphonebook")); - if (simIpb != null) { - success = simIpb.updateAdnRecordsInEfBySearch(efType, - oldName, oldNumber, newName, newNumber, pin2); - } - } catch (RemoteException ex) { - // ignore it - } catch (SecurityException ex) { - if (DBG) log(ex.toString()); - } - - if (DBG) log("updateSimRecordInEf: " + success); - return success; - } - - - private boolean deleteSimRecordFromEf(int efType, - String name, String number, - String pin2) { - if (DBG) log("deleteSimRecordFromEf: efType=" + efType + - ", name=" + name + ", number=" + number + ", pin2=" + pin2); - - boolean success = false; - - try { - ISimPhoneBook simIpb = ISimPhoneBook.Stub.asInterface( - ServiceManager.getService("simphonebook")); - if (simIpb != null) { - success = simIpb.updateAdnRecordsInEfBySearch(efType, - name, number, "", "", pin2); - } - } catch (RemoteException ex) { - // ignore it - } catch (SecurityException ex) { - if (DBG) log(ex.toString()); - } - - if (DBG) log("deleteSimRecordFromEf: " + success); - return success; - } - - /** - * Loads an AdnRecord into an ArrayList. Must be called with mLock held. - * - * @param record the ADN record to load from - * @param results the array list to put the results in - */ - private void loadRecord(AdnRecord record, - ArrayList results) { - if (!record.isEmpty()) { - ArrayList contact = new ArrayList(2); - String alphaTag = record.getAlphaTag(); - String number = record.getNumber(); - - if (DBG) log("loadRecord: " + alphaTag + ", " + number); - contact.add(alphaTag); - contact.add(number); - results.add(contact); - } - } - - private void log(String msg) { - Log.d(TAG, "[SimProvider] " + msg); - } - -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java index c3df0d0..875d8d0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java @@ -16,27 +16,31 @@ package com.android.internal.telephony.gsm; -import android.app.PendingIntent; import android.content.Context; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; -import android.os.ServiceManager; -import android.telephony.gsm.SmsManager; import android.util.Log; +import com.android.internal.telephony.IccConstants; +import com.android.internal.telephony.IccSmsInterfaceManager; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.SmsRawData; + import java.util.ArrayList; import java.util.List; +import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; + /** * SimSmsInterfaceManager to provide an inter-process communication to * access Sms in Sim. */ -public class SimSmsInterfaceManager extends ISms.Stub { +public class SimSmsInterfaceManager extends IccSmsInterfaceManager { static final String LOG_TAG = "GSM"; - static final boolean DBG = false; + static final boolean DBG = true; - private GSMPhone mPhone; private final Object mLock = new Object(); private boolean mSuccess; private List mSms; @@ -76,33 +80,31 @@ public class SimSmsInterfaceManager extends ISms.Stub { }; public SimSmsInterfaceManager(GSMPhone phone) { - this.mPhone = phone; - ServiceManager.addService("isms", this); + super(phone); + mDispatcher = new GsmSMSDispatcher(phone); } - private void enforceReceiveAndSend(String message) { - Context context = mPhone.getContext(); + public void dispose() { + } - context.enforceCallingPermission( - "android.permission.RECEIVE_SMS", message); - context.enforceCallingPermission( - "android.permission.SEND_SMS", message); + protected void finalize() { + if(DBG) Log.d(LOG_TAG, "SimSmsInterfaceManager finalized"); } /** * Update the specified message on the SIM. * * @param index record index of message to update - * @param status new message status (STATUS_ON_SIM_READ, - * STATUS_ON_SIM_UNREAD, STATUS_ON_SIM_SENT, - * STATUS_ON_SIM_UNSENT, STATUS_ON_SIM_FREE) + * @param status 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 success or not * */ public boolean - updateMessageOnSimEf(int index, int status, byte[] pdu) { - if (DBG) log("updateMessageOnSimEf: index=" + index + + updateMessageOnIccEf(int index, int status, byte[] pdu) { + if (DBG) log("updateMessageOnIccEf: index=" + index + " status=" + status + " ==> " + "("+ pdu + ")"); enforceReceiveAndSend("Updating message on SIM"); @@ -110,13 +112,14 @@ public class SimSmsInterfaceManager extends ISms.Stub { mSuccess = false; Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); - if (status == SmsManager.STATUS_ON_SIM_FREE) { + if (status == STATUS_ON_ICC_FREE) { // Special case FREE: call deleteSmsOnSim instead of // manipulating the SIM record mPhone.mCM.deleteSmsOnSim(index, response); } else { byte[] record = makeSmsRecordData(status, pdu); - mPhone.mSIMFileHandler.updateEFLinearFixed( SimConstants.EF_SMS, + ((SIMFileHandler)mPhone.getIccFileHandler()).updateEFLinearFixed( + IccConstants.EF_SMS, index, record, null, response); } try { @@ -132,21 +135,21 @@ public class SimSmsInterfaceManager extends ISms.Stub { * Copy a raw SMS PDU to the SIM. * * @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) + * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, + * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) * @return success or not * */ - public boolean copyMessageToSimEf(int status, byte[] pdu, byte[] smsc) { - if (DBG) log("copyMessageToSimEf: status=" + status + " ==> " + + public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) { + if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " + "pdu=("+ pdu + "), smsm=(" + smsc +")"); enforceReceiveAndSend("Copying message to SIM"); synchronized(mLock) { mSuccess = false; Message response = mHandler.obtainMessage(EVENT_UPDATE_DONE); - mPhone.mCM.writeSmsToSim(status, SimUtils.bytesToHexString(smsc), - SimUtils.bytesToHexString(pdu), response); + mPhone.mCM.writeSmsToSim(status, IccUtils.bytesToHexString(smsc), + IccUtils.bytesToHexString(pdu), response); try { mLock.wait(); @@ -158,11 +161,11 @@ public class SimSmsInterfaceManager extends ISms.Stub { } /** - * Retrieves all messages currently stored on SIM. + * Retrieves all messages currently stored on ICC. * - * @return list of SmsRawData of all sms on SIM + * @return list of SmsRawData of all sms on ICC */ - public List getAllMessagesFromSimEf() { + public List getAllMessagesFromIccEf() { if (DBG) log("getAllMessagesFromEF"); Context context = mPhone.getContext(); @@ -172,7 +175,7 @@ public class SimSmsInterfaceManager extends ISms.Stub { "Reading messages from SIM"); synchronized(mLock) { Message response = mHandler.obtainMessage(EVENT_LOAD_DONE); - mPhone.mSIMFileHandler.loadEFLinearFixedAll(SimConstants.EF_SMS, + ((SIMFileHandler)mPhone.getIccFileHandler()).loadEFLinearFixedAll(IccConstants.EF_SMS, response); try { @@ -184,119 +187,7 @@ public class SimSmsInterfaceManager extends ISms.Stub { return mSms; } - /** - * Send a Raw PDU SMS - * - * @param smsc the SMSC to send the message through, or NULL for the - * defatult SMSC - * @param pdu the raw PDU to send - * @param sentIntent if not NULL this Intent is - * broadcast when the message is sucessfully sent, or failed. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * @param deliveryIntent if not NULL this Intent is - * broadcast when the message is delivered to the recipient. The - * raw pdu of the status report is in the extended data ("pdu"). - */ - public void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, - PendingIntent deliveryIntent) { - Context context = mPhone.getContext(); - - context.enforceCallingPermission( - "android.permission.SEND_SMS", - "Sending SMS message"); - if (DBG) log("sendRawPdu: smsc=" + smsc + - " pdu="+ pdu + " sentIntent" + sentIntent + - " deliveryIntent" + deliveryIntent); - mPhone.mSMS.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent); - } - - /** - * Send a multi-part 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 parts an ArrayList of strings that, in order, - * comprise the original message - * @param sentIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * @param deliveryIntents if not null, an ArrayList of - * PendingIntents (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"). - */ - public void sendMultipartText(String destinationAddress, String scAddress, List parts, - List sentIntents, List deliveryIntents) { - Context context = mPhone.getContext(); - - context.enforceCallingPermission( - "android.permission.SEND_SMS", - "Sending SMS message"); - if (DBG) log("sendMultipartText"); - mPhone.mSMS.sendMultipartText(destinationAddress, scAddress, (ArrayList) parts, - (ArrayList) sentIntents, (ArrayList) deliveryIntents); - } - - /** - * Generates an EF_SMS record from status and raw PDU. - * - * @param status Message status. See TS 51.011 10.5.3. - * @param pdu Raw message PDU. - * @return byte array for the record. - */ - private byte[] makeSmsRecordData(int status, byte[] pdu) { - byte[] data = new byte[SimConstants.SMS_RECORD_LENGTH]; - - // Status bits for this record. See TS 51.011 10.5.3 - data[0] = (byte)(status & 7); - - System.arraycopy(pdu, 0, data, 1, pdu.length); - - // Pad out with 0xFF's. - for (int j = pdu.length+1; j < SimConstants.SMS_RECORD_LENGTH; j++) { - data[j] = -1; - } - - return data; - } - - /** - * create SmsRawData lists from all sms record byte[] - * Use null to indicate "free" record - * - * @param messages List of message records from EF_SMS. - * @return SmsRawData list of all in-used records - */ - private ArrayList buildValidRawData(ArrayList messages) { - int count = messages.size(); - ArrayList ret; - - ret = new ArrayList(count); - - for (int i = 0; i < count; i++) { - byte[] ba = messages.get(i); - if (ba[0] == 0) { - ret.add(null); - } else { - ret.add(new SmsRawData(messages.get(i))); - } - } - - return ret; - } - - private void log(String msg) { - Log.d(LOG_TAG, "[SmsInterfaceManager] " + msg); + protected void log(String msg) { + Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/SimTlv.java b/telephony/java/com/android/internal/telephony/gsm/SimTlv.java index 00879ce..30543c7 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimTlv.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimTlv.java @@ -34,9 +34,8 @@ public class SimTlv int curDataOffset; int curDataLength; boolean hasValidTlvObject; - - public SimTlv(byte[] record, int offset, int length) - { + + public SimTlv(byte[] record, int offset, int length) { this.record = record; this.tlvOffset = offset; @@ -46,19 +45,15 @@ public class SimTlv hasValidTlvObject = parseCurrentTlvObject(); } - public boolean - nextObject() - { + public boolean nextObject() { if (!hasValidTlvObject) return false; curOffset = curDataOffset + curDataLength; - hasValidTlvObject = parseCurrentTlvObject(); + hasValidTlvObject = parseCurrentTlvObject(); return hasValidTlvObject; } - public boolean - isValidObject() - { + public boolean isValidObject() { return hasValidTlvObject; } @@ -68,9 +63,7 @@ public class SimTlv * 0 and 0xff are invalid tag values * valid tags range from 1 - 0xfe */ - public int - getTag() - { + public int getTag() { if (!hasValidTlvObject) return 0; return record[curOffset] & 0xff; } @@ -79,10 +72,8 @@ public class SimTlv * Returns data associated with current TLV object * returns null if !isValidObject() */ - - public byte[] - getData() - { + + public byte[] getData() { if (!hasValidTlvObject) return null; byte[] ret = new byte[curDataLength]; @@ -95,14 +86,12 @@ public class SimTlv * @return false on invalid record, true on valid record */ - private boolean - parseCurrentTlvObject() - { + private boolean parseCurrentTlvObject() { // 0x00 and 0xff are invalid tag values if (record[curOffset] == 0 || (record[curOffset] & 0xff) == 0xff) { return false; } - + try { if ((record[curOffset + 1] & 0xff) < 0x80) { // one byte length 0 - 0x7f diff --git a/telephony/java/com/android/internal/telephony/gsm/SimUtils.java b/telephony/java/com/android/internal/telephony/gsm/SimUtils.java deleted file mode 100644 index 2eecdba..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SimUtils.java +++ /dev/null @@ -1,476 +0,0 @@ -/* - * 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.gsm; - -import java.io.UnsupportedEncodingException; - -import android.graphics.Bitmap; -import android.graphics.Color; -import android.util.Log; - -/** - * Various methods, useful for dealing with SIM data. - */ -public class SimUtils -{ - static final String LOG_TAG="GSM"; - - /** - * Many fields in GSM SIM's are stored as nibble-swizzled BCD - * - * Assumes left-justified field that may be padded right with 0xf - * values. - * - * Stops on invalid BCD value, returning string so far - */ - public static String - bcdToString(byte[] data, int offset, int length) - { - StringBuilder ret = new StringBuilder(length*2); - - for (int i = offset ; i < offset + length ; i++) { - byte b; - int v; - - v = data[i] & 0xf; - if (v > 9) break; - ret.append((char)('0' + v)); - - v = (data[i] >> 4) & 0xf; - if (v > 9) break; - ret.append((char)('0' + v)); - } - - return ret.toString(); - } - - - /** - * Decodes a GSM-style BCD byte, returning an int ranging from 0-99. - * - * In GSM land, the least significant BCD digit is stored in the most - * significant nibble. - * - * Out-of-range digits are treated as 0 for the sake of the time stamp, - * because of this: - * - * TS 23.040 section 9.2.3.11 - * "if the MS receives a non-integer value in the SCTS, it shall - * assume the digit is set to 0 but shall store the entire field - * exactly as received" - */ - public static int - bcdByteToInt(byte b) - { - int ret = 0; - - // treat out-of-range BCD values as 0 - if ((b & 0xf0) <= 0x90) { - ret = (b >> 4) & 0xf; - } - - if ((b & 0x0f) <= 0x09) { - ret += (b & 0xf) * 10; - } - - return ret; - } - - - /** - * Decodes a string field that's formatted like the EF[ADN] alpha - * identifier - * - * From TS 51.011 10.5.1: - * Coding: - * this alpha tagging shall use either - * - the SMS default 7 bit coded alphabet as defined in - * TS 23.038 [12] with bit 8 set to 0. The alpha identifier - * shall be left justified. Unused bytes shall be set to 'FF'; or - * - one of the UCS2 coded options as defined in annex B. - * - * Annex B from TS 11.11 V8.13.0: - * 1) If the first octet in the alpha string is '80', then the - * remaining octets are 16 bit UCS2 characters ... - * 2) if the first octet in the alpha string is '81', then the - * second octet contains a value indicating the number of - * characters in the string, and the third octet contains an - * 8 bit number which defines bits 15 to 8 of a 16 bit - * base pointer, where bit 16 is set to zero and bits 7 to 1 - * are also set to zero. These sixteen bits constitute a - * base pointer to a "half page" in the UCS2 code space, to be - * used with some or all of the remaining octets in the string. - * The fourth and subsequent octets contain codings as follows: - * If bit 8 of the octet is set to zero, the remaining 7 bits - * of the octet contain a GSM Default Alphabet character, - * whereas if bit 8 of the octet is set to one, then the - * remaining seven bits are an offset value added to the - * 16 bit base pointer defined earlier... - * 3) If the first octet of the alpha string is set to '82', then - * the second octet contains a value indicating the number of - * characters in the string, and the third and fourth octets - * contain a 16 bit number which defines the complete 16 bit - * base pointer to a "half page" in the UCS2 code space... - */ - public static String - adnStringFieldToString(byte[] data, int offset, int length) - { - if (length >= 1) { - if (data[offset] == (byte) 0x80) { - int ucslen = (length - 1) / 2; - String ret = null; - - try { - ret = new String(data, offset + 1, ucslen * 2, "utf-16be"); - } catch (UnsupportedEncodingException ex) { - Log.e(LOG_TAG, "implausible UnsupportedEncodingException", - ex); - } - - if (ret != null) { - // trim off trailing FFFF characters - - ucslen = ret.length(); - while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF') - ucslen--; - - return ret.substring(0, ucslen); - } - } - } - - boolean isucs2 = false; - char base = '\0'; - int len = 0; - - if (length >= 3 && data[offset] == (byte) 0x81) { - len = data[offset + 1] & 0xFF; - if (len > length - 3) - len = length - 3; - - base = (char) ((data[offset + 2] & 0xFF) << 7); - offset += 3; - isucs2 = true; - } else if (length >= 4 && data[offset] == (byte) 0x82) { - len = data[offset + 1] & 0xFF; - if (len > length - 4) - len = length - 4; - - base = (char) (((data[offset + 2] & 0xFF) << 8) | - (data[offset + 3] & 0xFF)); - offset += 4; - isucs2 = true; - } - - if (isucs2) { - StringBuilder ret = new StringBuilder(); - - while (len > 0) { - // UCS2 subset case - - if (data[offset] < 0) { - ret.append((char) (base + (data[offset] & 0x7F))); - offset++; - len--; - } - - // GSM character set case - - int count = 0; - while (count < len && data[offset + count] >= 0) - count++; - - ret.append(GsmAlphabet.gsm8BitUnpackedToString(data, - offset, count)); - - offset += count; - len -= count; - } - - return ret.toString(); - } - - return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length); - } - - static int - hexCharToInt(char c) - { - if (c >= '0' && c <= '9') return (c - '0'); - if (c >= 'A' && c <= 'F') return (c - 'A' + 10); - if (c >= 'a' && c <= 'f') return (c - 'a' + 10); - - throw new RuntimeException ("invalid hex char '" + c + "'"); - } - - /** - * Converts a hex String to a byte array. - * - * @param s A string of hexadecimal characters, must be an even number of - * chars long - * - * @return byte array representation - * - * @throws RuntimeException on invalid format - */ - public static byte[] - hexStringToBytes(String s) - { - byte[] ret; - - if (s == null) return null; - - int sz = s.length(); - - ret = new byte[sz/2]; - - for (int i=0 ; i > 4); - - ret.append("0123456789abcdef".charAt(b)); - - b = 0x0f & bytes[i]; - - ret.append("0123456789abcdef".charAt(b)); - } - - return ret.toString(); - } - - - /** - * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string - * "offset" points to "octet 3", the coding scheme byte - * empty string returned on decode error - */ - public static String - networkNameToString(byte[] data, int offset, int length) - { - String ret; - - if ((data[offset] & 0x80) != 0x80 || length < 1) { - return ""; - } - - switch ((data[offset] >>> 4) & 0x7) { - case 0: - // SMS character set - int countSeptets; - int unusedBits = data[offset] & 7; - countSeptets = (((length - 1) * 8) - unusedBits) / 7 ; - ret = GsmAlphabet.gsm7BitPackedToString( - data, offset + 1, countSeptets); - break; - case 1: - // UCS2 - try { - ret = new String(data, - offset + 1, length - 1, "utf-16"); - } catch (UnsupportedEncodingException ex) { - ret = ""; - Log.e(LOG_TAG,"implausible UnsupportedEncodingException", ex); - } - break; - - // unsupported encoding - default: - ret = ""; - break; - } - - // "Add CI" - // "The MS should add the letters for the Country's Initials and - // a separator (e.g. a space) to the text string" - - if ((data[offset] & 0x40) != 0) { - // FIXME(mkf) add country initials here - - } - - return ret; - } - - /** - * Convert a TS 131.102 image instance of code scheme '11' into Bitmap - * @param data The raw data - * @param length The length of image body - * @return The bitmap - */ - public static Bitmap parseToBnW(byte[] data, int length){ - int valueIndex = 0; - int width = data[valueIndex++] & 0xFF; - int height = data[valueIndex++] & 0xFF; - int numOfPixels = width*height; - - int[] pixels = new int[numOfPixels]; - - int pixelIndex = 0; - int bitIndex = 7; - byte currentByte = 0x00; - while (pixelIndex < numOfPixels) { - // reassign data and index for every byte (8 bits). - if (pixelIndex % 8 == 0) { - currentByte = data[valueIndex++]; - bitIndex = 7; - } - pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01); - }; - - if (pixelIndex != numOfPixels) { - Log.e(LOG_TAG, "parse end and size error"); - } - return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); - } - - private static int bitToRGB(int bit){ - if(bit == 1){ - return Color.WHITE; - } else { - return Color.BLACK; - } - } - - /** - * a TS 131.102 image instance of code scheme '11' into color Bitmap - * - * @param data The raw data - * @param length the length of image body - * @param transparency with or without transparency - * @return The color bitmap - */ - public static Bitmap parseToRGB(byte[] data, int length, - boolean transparency) { - int valueIndex = 0; - int width = data[valueIndex++] & 0xFF; - int height = data[valueIndex++] & 0xFF; - int bits = data[valueIndex++] & 0xFF; - int colorNumber = data[valueIndex++] & 0xFF; - int clutOffset = ((data[valueIndex++] & 0xFF) << 8) - | data[valueIndex++]; - length = length - 6; - - int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber); - if (true == transparency) { - colorIndexArray[colorNumber - 1] = Color.TRANSPARENT; - } - - int[] resultArray = null; - if (0 == (8 % bits)) { - resultArray = mapTo2OrderBitColor(data, valueIndex, - (width * height), colorIndexArray, bits); - } else { - resultArray = mapToNon2OrderBitColor(data, valueIndex, - (width * height), colorIndexArray, bits); - } - - return Bitmap.createBitmap(resultArray, width, height, - Bitmap.Config.RGB_565); - } - - private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex, - int length, int[] colorArray, int bits) { - if (0 != (8 % bits)) { - Log.e(LOG_TAG, "not event number of color"); - return mapToNon2OrderBitColor(data, valueIndex, length, colorArray, - bits); - } - - int mask = 0x01; - switch (bits) { - case 1: - mask = 0x01; - break; - case 2: - mask = 0x03; - break; - case 4: - mask = 0x0F; - break; - case 8: - mask = 0xFF; - break; - } - - int[] resultArray = new int[length]; - int resultIndex = 0; - int run = 8 / bits; - while (resultIndex < length) { - byte tempByte = data[valueIndex++]; - for (int runIndex = 0; runIndex < run; ++runIndex) { - int offset = run - runIndex - 1; - resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits)) - & mask]; - } - } - return resultArray; - } - - private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex, - int length, int[] colorArray, int bits) { - if (0 == (8 % bits)) { - Log.e(LOG_TAG, "not odd number of color"); - return mapTo2OrderBitColor(data, valueIndex, length, colorArray, - bits); - } - - int[] resultArray = new int[length]; - // TODO fix me: - return resultArray; - } - - private static int[] getCLUT(byte[] rawData, int offset, int number) { - if (null == rawData) { - return null; - } - - int[] result = new int[number]; - int endIndex = offset + (number * 3); // 1 color use 3 bytes - int valueIndex = offset; - int colorIndex = 0; - int alpha = 0xff << 24; - do { - result[colorIndex++] = alpha - | ((rawData[valueIndex++] & 0xFF) << 16) - | ((rawData[valueIndex++] & 0xFF) << 8) - | ((rawData[valueIndex++] & 0xFF)); - } while (valueIndex < endIndex); - return result; - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsHeader.java deleted file mode 100644 index 22366ec..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SmsHeader.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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.gsm; - -import com.android.internal.util.HexDump; - -import java.util.ArrayList; - -/** - * This class represents a SMS user data header. - * - */ -public class SmsHeader -{ - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int CONCATENATED_8_BIT_REFERENCE = 0x00; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int SPECIAL_SMS_MESSAGE_INDICATION = 0x01; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int APPLICATION_PORT_ADDRESSING_8_BIT = 0x04; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int APPLICATION_PORT_ADDRESSING_16_BIT= 0x05; - /** See TS 23.040 9.2.3.24 for description of this element ID. */ - public static final int CONCATENATED_16_BIT_REFERENCE = 0x08; - - public static final int PORT_WAP_PUSH = 2948; - public static final int PORT_WAP_WSP = 9200; - - private byte[] m_data; - private ArrayList m_elements = new ArrayList(); - - /** - * Creates an SmsHeader object from raw user data header bytes. - * - * @param data is user data header bytes - * @return an SmsHeader object - */ - public static SmsHeader parse(byte[] data) - { - SmsHeader header = new SmsHeader(); - header.m_data = data; - - int index = 0; - while (index < data.length) - { - int id = data[index++] & 0xff; - int length = data[index++] & 0xff; - byte[] elementData = new byte[length]; - System.arraycopy(data, index, elementData, 0, length); - header.add(new Element(id, elementData)); - index += length; - } - - return header; - } - - public SmsHeader() - { - } - - /** - * Returns the list of SmsHeader Elements that make up the header. - * - * @return the list of SmsHeader Elements. - */ - public ArrayList getElements() - { - return m_elements; - } - - /** - * Add an element to the SmsHeader. - * - * @param element to add. - */ - public void add(Element element) - { - m_elements.add(element); - } - - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - - builder.append("UDH LENGTH: " + m_data.length + " octets"); - builder.append("UDH: "); - builder.append(HexDump.toHexString(m_data)); - builder.append("\n"); - - for (Element e : getElements()) { - builder.append(" 0x" + HexDump.toHexString((byte)e.getID()) + " - "); - switch (e.getID()) - { - case CONCATENATED_8_BIT_REFERENCE: - { - builder.append("Concatenated Short Message 8bit ref\n"); - byte[] data = e.getData(); - builder.append(" " + data.length + " (0x"); - builder.append(HexDump.toHexString((byte)data.length)+") Bytes - Information Element\n"); - builder.append(" " + data[0] + " : SM reference number\n"); - builder.append(" " + data[1] + " : number of messages\n"); - builder.append(" " + data[2] + " : this SM sequence number\n"); - break; - } - - case CONCATENATED_16_BIT_REFERENCE: - { - builder.append("Concatenated Short Message 16bit ref\n"); - byte[] data = e.getData(); - builder.append(" " + data.length + " (0x"); - builder.append(HexDump.toHexString((byte)data.length)+") Bytes - Information Element\n"); - builder.append(" " + (data[0] & 0xff) * 256 + (data[1] & 0xff) + - " : SM reference number\n"); - builder.append(" " + data[2] + " : number of messages\n"); - builder.append(" " + data[3] + " : this SM sequence number\n"); - break; - } - - case APPLICATION_PORT_ADDRESSING_16_BIT: - { - builder.append("Application port addressing 16bit\n"); - byte[] data = e.getData(); - - builder.append(" " + data.length + " (0x"); - builder.append(HexDump.toHexString((byte)data.length)+") Bytes - Information Element\n"); - - int source = (data[0] & 0xff) << 8; - source |= (data[1] & 0xff); - builder.append(" " + source + " : DESTINATION port\n"); - - int dest = (data[2] & 0xff) << 8; - dest |= (data[3] & 0xff); - builder.append(" " + dest + " : SOURCE port\n"); - break; - } - - default: - { - builder.append("Unknown element\n"); - break; - } - } - } - - return builder.toString(); - } - - private int calcSize() { - int size = 1; // +1 for the UDHL field - for (Element e : m_elements) { - size += e.getData().length; - size += 2; // 1 byte ID, 1 byte length - } - - return size; - } - - /** - * Converts SmsHeader object to a byte array as specified in TS 23.040 9.2.3.24. - * @return Byte array representing the SmsHeader - */ - public byte[] toByteArray() { - if (m_elements.size() == 0) return null; - - if (m_data == null) { - int size = calcSize(); - int cur = 1; - m_data = new byte[size]; - - m_data[0] = (byte) (size-1); // UDHL does not include itself - - for (Element e : m_elements) { - int length = e.getData().length; - m_data[cur++] = (byte) e.getID(); - m_data[cur++] = (byte) length; - System.arraycopy(e.getData(), 0, m_data, cur, length); - cur += length; - } - } - - return m_data; - } - - /** - * A single Element in the SMS User Data Header. - * - * See TS 23.040 9.2.3.24. - * - */ - public static class Element - { - private byte[] m_data; - private int m_id; - - public Element(int id, byte[] data) - { - m_id = id; - m_data = data; - } - - /** - * Returns the Information Element Identifier for this element. - * - * @return the IE identifier. - */ - public int getID() - { - return m_id; - } - - /** - * Returns the data portion of this element. - * - * @return element data. - */ - public byte[] getData() - { - return m_data; - } - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java new file mode 100644 index 0000000..867b719 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -0,0 +1,1058 @@ +/* + * 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.gsm; + +import android.os.Parcel; +import android.telephony.PhoneNumberUtils; +import android.text.format.Time; +import android.util.Config; +import android.util.Log; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.EncodeException; +import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.SmsHeader; +import com.android.internal.telephony.SmsMessageBase; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; + +import static android.telephony.SmsMessage.ENCODING_7BIT; +import static android.telephony.SmsMessage.ENCODING_8BIT; +import static android.telephony.SmsMessage.ENCODING_16BIT; +import static android.telephony.SmsMessage.ENCODING_UNKNOWN; +import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES; +import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; +import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS; +import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER; +import static android.telephony.SmsMessage.MessageClass; + +/** + * A Short Message Service message. + * + */ +public class SmsMessage extends SmsMessageBase{ + static final String LOG_TAG = "GSM"; + + private MessageClass messageClass; + + /** + * TP-Message-Type-Indicator + * 9.2.3 + */ + private int mti; + + /** TP-Protocol-Identifier (TP-PID) */ + private int protocolIdentifier; + + // TP-Data-Coding-Scheme + // see TS 23.038 + private int dataCodingScheme; + + // TP-Reply-Path + // e.g. 23.040 9.2.2.1 + private boolean replyPathPresent = false; + + // "Message Marked for Automatic Deletion Group" + // 23.038 Section 4 + private boolean automaticDeletion; + + /** True if Status Report is for SMS-SUBMIT; false for SMS-COMMAND. */ + private boolean forSubmit; + + /** The address of the receiver. */ + private GsmSmsAddress recipientAddress; + + /** Time when SMS-SUBMIT was delivered from SC to MSE. */ + private long dischargeTimeMillis; + + /** + * TP-Status - status of a previously submitted SMS. + * This field applies to SMS-STATUS-REPORT messages. 0 indicates success; + * see TS 23.040, 9.2.3.15 for description of other possible values. + */ + private int status; + + /** + * TP-Status - status of a previously submitted SMS. + * This field is true iff the message is a SMS-STATUS-REPORT message. + */ + private boolean isStatusReportMessage = false; + + public static class SubmitPdu extends SubmitPduBase { + } + + /** + * Create an SmsMessage from a raw PDU. + */ + public static SmsMessage createFromPdu(byte[] pdu) { + try { + SmsMessage msg = new SmsMessage(); + msg.parsePdu(pdu); + return msg; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex); + return null; + } + } + + /** + * 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: [<alpha>], + * + * Only public for debugging + * + * {@hide} + */ + public static SmsMessage newFromCMT(String[] lines) { + try { + SmsMessage msg = new SmsMessage(); + msg.parsePdu(IccUtils.hexStringToBytes(lines[1])); + return msg; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex); + return null; + } + } + + /** @hide */ + public static SmsMessage newFromCMTI(String line) { + // the thinking here is not to read the message immediately + // FTA test case + Log.e(LOG_TAG, "newFromCMTI: not yet supported"); + return null; + } + + /** @hide */ + public static SmsMessage newFromCDS(String line) { + try { + SmsMessage msg = new SmsMessage(); + msg.parsePdu(IccUtils.hexStringToBytes(line)); + return msg; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex); + return null; + } + } + + /** + * Note: This functionality is currently not supported in GSM mode. + * @hide + */ + public static SmsMessageBase newFromParcel(Parcel p){ + Log.w(LOG_TAG, "newFromParcel: is not supported in GSM mode."); + return null; + } + + /** + * 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) { + try { + SmsMessage msg = new SmsMessage(); + + msg.indexOnIcc = index; + + // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT, + // or STORED_UNSENT + // See TS 51.011 10.5.3 + if ((data[0] & 1) == 0) { + Log.w(LOG_TAG, + "SMS parsing failed: Trying to parse a free record"); + return null; + } else { + msg.statusOnIcc = data[0] & 0x07; + } + + int size = data.length - 1; + + // Note: Data may include trailing FF's. That's OK; message + // should still parse correctly. + byte[] pdu = new byte[size]; + System.arraycopy(data, 1, pdu, 0, size); + msg.parsePdu(pdu); + return msg; + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex); + return null; + } + } + + /** + * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the + * length in bytes (not hex chars) less the SMSC header + */ + public static int getTPLayerLengthForPDU(String pdu) { + int len = pdu.length() / 2; + int smscLen = 0; + + smscLen = Integer.parseInt(pdu.substring(0, 2), 16); + + return len - smscLen - 1; + } + + /** + * Get an SMS-SUBMIT PDU for a destination address and a message + * + * @param scAddress Service Centre address. Null means use default. + * @return a SubmitPdu containing the encoded SC + * address, if applicable, and the encoded message. + * Returns null on encode error. + * @hide + */ + public static SubmitPdu getSubmitPdu(String scAddress, + String destinationAddress, String message, + boolean statusReportRequested, byte[] header) { + + // Perform null parameter checks. + if (message == null || destinationAddress == null) { + return null; + } + + SubmitPdu ret = new SubmitPdu(); + // MTI = SMS-SUBMIT, UDHI = header != null + byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00)); + ByteArrayOutputStream bo = getSubmitPduHead( + scAddress, destinationAddress, mtiByte, + statusReportRequested, ret); + + try { + // First, try encoding it with the GSM alphabet + + // User Data (and length) + byte[] userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header); + + if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) { + // Message too long + return null; + } + + // TP-Data-Coding-Scheme + // Default encoding, uncompressed + bo.write(0x00); + + // (no TP-Validity-Period) + + bo.write(userData, 0, userData.length); + } catch (EncodeException ex) { + byte[] userData, textPart; + // Encoding to the 7-bit alphabet failed. Let's see if we can + // send it as a UCS-2 encoded message + + try { + textPart = message.getBytes("utf-16be"); + } catch (UnsupportedEncodingException uex) { + Log.e(LOG_TAG, + "Implausible UnsupportedEncodingException ", + uex); + return null; + } + + if (header != null) { + userData = new byte[header.length + textPart.length]; + + System.arraycopy(header, 0, userData, 0, header.length); + System.arraycopy(textPart, 0, userData, header.length, textPart.length); + } + else { + userData = textPart; + } + + if (userData.length > MAX_USER_DATA_BYTES) { + // Message too long + return null; + } + + // TP-Data-Coding-Scheme + // Class 3, UCS-2 encoding, uncompressed + bo.write(0x0b); + + // (no TP-Validity-Period) + + // TP-UDL + bo.write(userData.length); + + bo.write(userData, 0, userData.length); + } + + ret.encodedMessage = bo.toByteArray(); + return ret; + } + + /** + * Get an SMS-SUBMIT PDU for a destination address and a message + * + * @param scAddress Service Centre address. Null means use default. + * @return a SubmitPdu 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) { + + return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null); + } + + /** + * Get an SMS-SUBMIT PDU for a data message to a destination address & 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 SubmitPdu 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) { + if (data.length > (MAX_USER_DATA_BYTES - 7 /* UDH size */)) { + Log.e(LOG_TAG, "SMS data message may only contain " + + (MAX_USER_DATA_BYTES - 7) + " bytes"); + return null; + } + + SubmitPdu ret = new SubmitPdu(); + ByteArrayOutputStream bo = getSubmitPduHead( + scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT, + // TP-UDHI = true + statusReportRequested, ret); + + // TP-Data-Coding-Scheme + // No class, 8 bit data + bo.write(0x04); + + // (no TP-Validity-Period) + + // User data size + bo.write(data.length + 7); + + // User data header size + bo.write(0x06); // header is 6 octets + + // User data header, indicating the destination port + bo.write(SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT); // port + // addressing + // header + bo.write(0x04); // each port is 2 octets + bo.write((destinationPort >> 8) & 0xFF); // MSB of destination port + bo.write(destinationPort & 0xFF); // LSB of destination port + bo.write(0x00); // MSB of originating port + bo.write(0x00); // LSB of originating port + + // User data + bo.write(data, 0, data.length); + + ret.encodedMessage = bo.toByteArray(); + return ret; + } + + /** + * Create the beginning of a SUBMIT PDU. This is the part of the + * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu}, + * one of which takes a byte array and the other of which takes a + * String. + * + * @param scAddress Service Centre address. null == use default + * @param destinationAddress the address of the destination for the message + * @param mtiByte + * @param ret SubmitPdu containing the encoded SC + * address, if applicable, and the encoded message + */ + private static ByteArrayOutputStream getSubmitPduHead( + String scAddress, String destinationAddress, byte mtiByte, + boolean statusReportRequested, SubmitPdu ret) { + ByteArrayOutputStream bo = new ByteArrayOutputStream( + MAX_USER_DATA_BYTES + 40); + + // SMSC address with length octet, or 0 + if (scAddress == null) { + ret.encodedScAddress = null; + } else { + ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength( + scAddress); + } + + // TP-Message-Type-Indicator (and friends) + if (statusReportRequested) { + // Set TP-Status-Report-Request bit. + mtiByte |= 0x20; + if (Config.LOGD) Log.d(LOG_TAG, "SMS status report requested"); + } + bo.write(mtiByte); + + // space for TP-Message-Reference + bo.write(0); + + byte[] daBytes; + + daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress); + + // destination address length in BCD digits, ignoring TON byte and pad + // TODO Should be better. + bo.write((daBytes.length - 1) * 2 + - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0)); + + // destination address + bo.write(daBytes, 0, daBytes.length); + + // TP-Protocol-Identifier + bo.write(0); + return bo; + } + + static class PduParser { + byte pdu[]; + int cur; + SmsHeader userDataHeader; + byte[] userData; + int mUserDataSeptetPadding; + int mUserDataSize; + + PduParser(String s) { + this(IccUtils.hexStringToBytes(s)); + } + + PduParser(byte[] pdu) { + this.pdu = pdu; + cur = 0; + mUserDataSeptetPadding = 0; + } + + /** + * Parse and return the SC address prepended to SMS messages coming via + * the TS 27.005 / AT interface. Returns null on invalid address + */ + String getSCAddress() { + int len; + String ret; + + // length of SC Address + len = getByte(); + + if (len == 0) { + // no SC address + ret = null; + } else { + // SC address + try { + ret = PhoneNumberUtils + .calledPartyBCDToString(pdu, cur, len); + } catch (RuntimeException tr) { + Log.d(LOG_TAG, "invalid SC address: ", tr); + ret = null; + } + } + + cur += len; + + return ret; + } + + /** + * returns non-sign-extended byte value + */ + int getByte() { + return pdu[cur++] & 0xff; + } + + /** + * Any address except the SC address (eg, originating address) See TS + * 23.040 9.1.2.5 + */ + GsmSmsAddress getAddress() { + GsmSmsAddress ret; + + // "The Address-Length field is an integer representation of + // the number field, i.e. excludes any semi octet containing only + // fill bits." + // The TOA field is not included as part of this + int addressLength = pdu[cur] & 0xff; + int lengthBytes = 2 + (addressLength + 1) / 2; + + ret = new GsmSmsAddress(pdu, cur, lengthBytes); + + cur += lengthBytes; + + return ret; + } + + /** + * Parses an SC timestamp and returns a currentTimeMillis()-style + * timestamp + */ + + long getSCTimestampMillis() { + // TP-Service-Centre-Time-Stamp + int year = IccUtils.bcdByteToInt(pdu[cur++]); + int month = IccUtils.bcdByteToInt(pdu[cur++]); + int day = IccUtils.bcdByteToInt(pdu[cur++]); + int hour = IccUtils.bcdByteToInt(pdu[cur++]); + int minute = IccUtils.bcdByteToInt(pdu[cur++]); + int second = IccUtils.bcdByteToInt(pdu[cur++]); + + // For the timezone, the most significant bit of the + // least signficant nibble is the sign byte + // (meaning the max range of this field is 79 quarter-hours, + // which is more than enough) + + byte tzByte = pdu[cur++]; + + // Mask out sign bit. + int timezoneOffset = IccUtils + .bcdByteToInt((byte) (tzByte & (~0x08))); + + timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset + : -timezoneOffset; + + Time time = new Time(Time.TIMEZONE_UTC); + + // It's 2006. Should I really support years < 2000? + time.year = year >= 90 ? year + 1900 : 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) - (timezoneOffset * 15 * 60 * 1000); + } + + /** + * Pulls the user data out of the PDU, and separates the payload from + * the header if there is one. + * + * @param hasUserDataHeader true if there is a user data header + * @param dataInSeptets true if the data payload is in septets instead + * of octets + * @return the number of septets or octets in the user data payload + */ + int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) { + int offset = cur; + int userDataLength = pdu[offset++] & 0xff; + int headerSeptets = 0; + + if (hasUserDataHeader) { + int userDataHeaderLength = pdu[offset++] & 0xff; + + byte[] udh = new byte[userDataHeaderLength]; + System.arraycopy(pdu, offset, udh, 0, userDataHeaderLength); + userDataHeader = SmsHeader.parse(udh); + offset += userDataHeaderLength; + + int headerBits = (userDataHeaderLength + 1) * 8; + headerSeptets = headerBits / 7; + headerSeptets += (headerBits % 7) > 0 ? 1 : 0; + mUserDataSeptetPadding = (headerSeptets * 7) - headerBits; + } + + /* + * Here we just create the user data length to be the remainder of + * the pdu minus the user data hearder. This is because the count + * could mean the number of uncompressed sepets if the userdata is + * encoded in 7-bit. + */ + userData = new byte[pdu.length - offset]; + System.arraycopy(pdu, offset, userData, 0, userData.length); + cur = offset; + + if (dataInSeptets) { + // Return the number of septets + return userDataLength - headerSeptets; + } else { + // Return the number of octets + return userData.length; + } + } + + /** + * Returns the user data payload, not including the headers + * + * @return the user data payload, not including the headers + */ + byte[] getUserData() { + return userData; + } + + /** + * Returns the number of padding bits at the begining of the user data + * array before the start of the septets. + * + * @return the number of padding bits at the begining of the user data + * array before the start of the septets + */ + int getUserDataSeptetPadding() { + return mUserDataSeptetPadding; + } + + /** + * Returns an object representing the user data headers + * + * @return an object representing the user data headers + * + * {@hide} + */ + SmsHeader getUserDataHeader() { + return userDataHeader; + } + +/* + XXX Not sure what this one is supposed to be doing, and no one is using + it. + String getUserDataGSM8bit() { + // System.out.println("remainder of pud:" + + // HexDump.dumpHexString(pdu, cur, pdu.length - cur)); + int count = pdu[cur++] & 0xff; + int size = pdu[cur++]; + + // skip over header for now + cur += size; + + if (pdu[cur - 1] == 0x01) { + int tid = pdu[cur++] & 0xff; + int type = pdu[cur++] & 0xff; + + size = pdu[cur++] & 0xff; + + int i = cur; + + while (pdu[i++] != '\0') { + } + + int length = i - cur; + String mimeType = new String(pdu, cur, length); + + cur += length; + + if (false) { + System.out.println("tid = 0x" + HexDump.toHexString(tid)); + System.out.println("type = 0x" + HexDump.toHexString(type)); + System.out.println("header size = " + size); + System.out.println("mimeType = " + mimeType); + System.out.println("remainder of header:" + + HexDump.dumpHexString(pdu, cur, (size - mimeType.length()))); + } + + cur += size - mimeType.length(); + + // System.out.println("data count = " + count + " cur = " + cur + // + " :" + HexDump.dumpHexString(pdu, cur, pdu.length - cur)); + + MMSMessage msg = MMSMessage.parseEncoding(mContext, pdu, cur, + pdu.length - cur); + } else { + System.out.println(new String(pdu, cur, pdu.length - cur - 1)); + } + + return IccUtils.bytesToHexString(pdu); + } +*/ + + /** + * Interprets the user data payload as pack GSM 7bit characters, and + * decodes them into a String. + * + * @param septetCount the number of septets in the user data payload + * @return a String with the decoded characters + */ + String getUserDataGSM7Bit(int septetCount) { + String ret; + + ret = GsmAlphabet.gsm7BitPackedToString(pdu, cur, septetCount, + mUserDataSeptetPadding); + + cur += (septetCount * 7) / 8; + + return ret; + } + + /** + * Interprets the user data payload as UCS2 characters, and + * decodes them into a String. + * + * @param byteCount the number of bytes in the user data payload + * @return a String with the decoded characters + */ + String getUserDataUCS2(int byteCount) { + String ret; + + try { + ret = new String(pdu, cur, byteCount, "utf-16"); + } catch (UnsupportedEncodingException ex) { + ret = ""; + Log.e(LOG_TAG, "implausible UnsupportedEncodingException", ex); + } + + cur += byteCount; + return ret; + } + + boolean moreDataPresent() { + return (pdu.length > cur); + } + } + + /** {@inheritDoc} */ + public int getProtocolIdentifier() { + return protocolIdentifier; + } + + /** {@inheritDoc} */ + public boolean isReplace() { + return (protocolIdentifier & 0xc0) == 0x40 + && (protocolIdentifier & 0x3f) > 0 + && (protocolIdentifier & 0x3f) < 8; + } + + /** {@inheritDoc} */ + public boolean isCphsMwiMessage() { + return ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear() + || ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet(); + } + + /** {@inheritDoc} */ + public boolean isMWIClearMessage() { + if (isMwi && (mwiSense == false)) { + return true; + } + + return originatingAddress != null + && ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear(); + } + + /** {@inheritDoc} */ + public boolean isMWISetMessage() { + if (isMwi && (mwiSense == true)) { + return true; + } + + return originatingAddress != null + && ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet(); + } + + /** {@inheritDoc} */ + public boolean isMwiDontStore() { + if (isMwi && mwiDontStore) { + return true; + } + + if (isCphsMwiMessage()) { + // See CPHS 4.2 Section B.4.2.1 + // If the user data is a single space char, do not store + // the message. Otherwise, store and display as usual + if (" ".equals(getMessageBody())) { + ; + } + return true; + } + + return false; + } + + /** {@inheritDoc} */ + public int getStatus() { + return status; + } + + /** {@inheritDoc} */ + public boolean isStatusReportMessage() { + return isStatusReportMessage; + } + + /** {@inheritDoc} */ + public boolean isReplyPathPresent() { + return replyPathPresent; + } + + /** + * TS 27.005 3.1, definition "In the case of SMS: 3GPP TS 24.011 [6] + * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format: + * ME/TA converts each octet of TP data unit into two IRA character long + * hexad number (e.g. octet with integer value 42 is presented to TE as two + * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast, + * something else... + */ + private void parsePdu(byte[] pdu) { + mPdu = pdu; + // Log.d(LOG_TAG, "raw sms mesage:"); + // Log.d(LOG_TAG, s); + + PduParser p = new PduParser(pdu); + + scAddress = p.getSCAddress(); + + if (scAddress != null) { + if (Config.LOGD) Log.d(LOG_TAG, "SMS SC address: " + scAddress); + } + + // TODO(mkf) support reply path, user data header indicator + + // TP-Message-Type-Indicator + // 9.2.3 + int firstByte = p.getByte(); + + mti = firstByte & 0x3; + switch (mti) { + // TP-Message-Type-Indicator + // 9.2.3 + case 0: + parseSmsDeliver(p, firstByte); + break; + case 2: + parseSmsStatusReport(p, firstByte); + break; + default: + // TODO(mkf) the rest of these + throw new RuntimeException("Unsupported message type"); + } + } + + /** + * Parses a SMS-STATUS-REPORT message. + * + * @param p A PduParser, cued past the first byte. + * @param firstByte The first byte of the PDU, which contains MTI, etc. + */ + private void parseSmsStatusReport(PduParser p, int firstByte) { + isStatusReportMessage = true; + + // TP-Status-Report-Qualifier bit == 0 for SUBMIT + forSubmit = (firstByte & 0x20) == 0x00; + // TP-Message-Reference + messageRef = p.getByte(); + // TP-Recipient-Address + recipientAddress = p.getAddress(); + // TP-Service-Centre-Time-Stamp + scTimeMillis = p.getSCTimestampMillis(); + // TP-Discharge-Time + dischargeTimeMillis = p.getSCTimestampMillis(); + // TP-Status + status = p.getByte(); + + // The following are optional fields that may or may not be present. + if (p.moreDataPresent()) { + // TP-Parameter-Indicator + int extraParams = p.getByte(); + int moreExtraParams = extraParams; + while ((moreExtraParams & 0x80) != 0) { + // We only know how to parse a few extra parameters, all + // indicated in the first TP-PI octet, so skip over any + // additional TP-PI octets. + moreExtraParams = p.getByte(); + } + // TP-Protocol-Identifier + if ((extraParams & 0x01) != 0) { + protocolIdentifier = p.getByte(); + } + // TP-Data-Coding-Scheme + if ((extraParams & 0x02) != 0) { + dataCodingScheme = p.getByte(); + } + // TP-User-Data-Length (implies existence of TP-User-Data) + if ((extraParams & 0x04) != 0) { + boolean hasUserDataHeader = (firstByte & 0x40) == 0x40; + parseUserData(p, hasUserDataHeader); + } + } + } + + private void parseSmsDeliver(PduParser p, int firstByte) { + replyPathPresent = (firstByte & 0x80) == 0x80; + + originatingAddress = p.getAddress(); + + if (originatingAddress != null) { + if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: " + + originatingAddress.address); + } + + // TP-Protocol-Identifier (TP-PID) + // TS 23.040 9.2.3.9 + protocolIdentifier = p.getByte(); + + // TP-Data-Coding-Scheme + // see TS 23.038 + dataCodingScheme = p.getByte(); + + if (Config.LOGV) { + Log.v(LOG_TAG, "SMS TP-PID:" + protocolIdentifier + + " data coding scheme: " + dataCodingScheme); + } + + scTimeMillis = p.getSCTimestampMillis(); + + if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis); + + boolean hasUserDataHeader = (firstByte & 0x40) == 0x40; + + parseUserData(p, hasUserDataHeader); + } + + /** + * Parses the User Data of an SMS. + * + * @param p The current PduParser. + * @param hasUserDataHeader Indicates whether a header is present in the + * User Data. + */ + private void parseUserData(PduParser p, boolean hasUserDataHeader) { + boolean hasMessageClass = false; + boolean userDataCompressed = false; + + int encodingType = ENCODING_UNKNOWN; + + // Look up the data encoding scheme + if ((dataCodingScheme & 0x80) == 0) { + // Bits 7..4 == 0xxx + automaticDeletion = (0 != (dataCodingScheme & 0x40)); + userDataCompressed = (0 != (dataCodingScheme & 0x20)); + hasMessageClass = (0 != (dataCodingScheme & 0x10)); + + if (userDataCompressed) { + Log.w(LOG_TAG, "4 - Unsupported SMS data coding scheme " + + "(compression) " + (dataCodingScheme & 0xff)); + } else { + switch ((dataCodingScheme >> 2) & 0x3) { + case 0: // GSM 7 bit default alphabet + encodingType = ENCODING_7BIT; + break; + + case 2: // UCS 2 (16bit) + encodingType = ENCODING_16BIT; + break; + + case 1: // 8 bit data + case 3: // reserved + Log.w(LOG_TAG, "1 - Unsupported SMS data coding scheme " + + (dataCodingScheme & 0xff)); + encodingType = ENCODING_8BIT; + break; + } + } + } else if ((dataCodingScheme & 0xf0) == 0xf0) { + automaticDeletion = false; + hasMessageClass = true; + userDataCompressed = false; + + if (0 == (dataCodingScheme & 0x04)) { + // GSM 7 bit default alphabet + encodingType = ENCODING_7BIT; + } else { + // 8 bit data + encodingType = ENCODING_8BIT; + } + } else if ((dataCodingScheme & 0xF0) == 0xC0 + || (dataCodingScheme & 0xF0) == 0xD0 + || (dataCodingScheme & 0xF0) == 0xE0) { + // 3GPP TS 23.038 V7.0.0 (2006-03) section 4 + + // 0xC0 == 7 bit, don't store + // 0xD0 == 7 bit, store + // 0xE0 == UCS-2, store + + if ((dataCodingScheme & 0xF0) == 0xE0) { + encodingType = ENCODING_16BIT; + } else { + encodingType = ENCODING_7BIT; + } + + userDataCompressed = false; + boolean active = ((dataCodingScheme & 0x08) == 0x08); + + // bit 0x04 reserved + + if ((dataCodingScheme & 0x03) == 0x00) { + isMwi = true; + mwiSense = active; + mwiDontStore = ((dataCodingScheme & 0xF0) == 0xC0); + } else { + isMwi = false; + + Log.w(LOG_TAG, "MWI for fax, email, or other " + + (dataCodingScheme & 0xff)); + } + } else { + Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme " + + (dataCodingScheme & 0xff)); + } + + // set both the user data and the user data header. + int count = p.constructUserData(hasUserDataHeader, + encodingType == ENCODING_7BIT); + this.userData = p.getUserData(); + this.userDataHeader = p.getUserDataHeader(); + + switch (encodingType) { + case ENCODING_UNKNOWN: + case ENCODING_8BIT: + messageBody = null; + break; + + case ENCODING_7BIT: + messageBody = p.getUserDataGSM7Bit(count); + break; + + case ENCODING_16BIT: + messageBody = p.getUserDataUCS2(count); + break; + } + + if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'"); + + if (messageBody != null) { + parseMessageBody(); + } + + if (!hasMessageClass) { + messageClass = MessageClass.UNKNOWN; + } else { + switch (dataCodingScheme & 0x3) { + case 0: + messageClass = MessageClass.CLASS_0; + break; + case 1: + messageClass = MessageClass.CLASS_1; + break; + case 2: + messageClass = MessageClass.CLASS_2; + break; + case 3: + messageClass = MessageClass.CLASS_3; + break; + } + } + } + + /** + * {@inheritDoc} + */ + public MessageClass getMessageClass() { + return messageClass; + } + +} diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsRawData.aidl b/telephony/java/com/android/internal/telephony/gsm/SmsRawData.aidl deleted file mode 100644 index 6f1a46d..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SmsRawData.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package com.android.internal.telephony.gsm; - -parcelable SmsRawData; diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsRawData.java b/telephony/java/com/android/internal/telephony/gsm/SmsRawData.java deleted file mode 100644 index a029d5c..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SmsRawData.java +++ /dev/null @@ -1,62 +0,0 @@ -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - - -package com.android.internal.telephony.gsm; - -import android.os.Parcelable; -import android.os.Parcel; - -/** - * A parcelable holder class of byte[] for ISms aidl implementation - */ -public class SmsRawData implements Parcelable { - byte[] data; - - //Static Methods - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator (){ - public SmsRawData createFromParcel(Parcel source) { - int size; - size = source.readInt(); - byte[] data = new byte[size]; - source.readByteArray(data); - return new SmsRawData(data); - } - - public SmsRawData[] newArray(int size) { - return new SmsRawData[size]; - } - }; - - // Constructor - public SmsRawData(byte[] data) { - this.data = data; - } - - public byte[] getBytes() { - return data; - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(data.length); - dest.writeByteArray(data); - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsResponse.java b/telephony/java/com/android/internal/telephony/gsm/SmsResponse.java deleted file mode 100644 index c005b5f..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/SmsResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.gsm; - -/** - * Object returned by the RIL upon successful completion of sendSMS. - * Contains message reference and ackPdu. - * - */ -class SmsResponse { - /** Message reference of the just-sent SMS. */ - int messageRef; - /** ackPdu for the just-sent SMS. */ - String ackPdu; - - SmsResponse(int messageRef, String ackPdu) { - this.messageRef = messageRef; - this.ackPdu = ackPdu; - } -} diff --git a/telephony/java/com/android/internal/telephony/gsm/TelephonyEventLog.java b/telephony/java/com/android/internal/telephony/gsm/TelephonyEventLog.java deleted file mode 100644 index 1e583f0..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/TelephonyEventLog.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.gsm; - -/* This class contains the details related to Telephony Event Logging */ -public final class TelephonyEventLog { - - /* Event log tags */ - public static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; - public static final int EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED = 50101; - public static final int EVENT_LOG_RADIO_RESET = 50102; - public static final int EVENT_LOG_PDP_RESET = 50103; - public static final int EVENT_LOG_REREGISTER_NETWORK = 50104; - public static final int EVENT_LOG_RADIO_PDP_SETUP_FAIL = 50105; - public static final int EVENT_LOG_CALL_DROP = 50106; - public static final int EVENT_LOG_CGREG_FAIL = 50107; - public static final int EVENT_DATA_STATE_RADIO_OFF = 50108; - public static final int EVENT_LOG_PDP_NETWORK_DROP = 50109; -} diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java b/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java index 26c2175..19d3279 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java @@ -20,9 +20,9 @@ import java.util.List; /** * Class for representing BER-TLV objects. - * + * * @see "ETSI TS 102 223 Annex C" for more information. - * + * * {@hide} */ class BerTlv { @@ -41,16 +41,16 @@ class BerTlv { /** * Gets a list of ComprehensionTlv objects contained in this BER-TLV object. - * + * * @return A list of COMPREHENSION-TLV object */ public List getComprehensionTlvs() { return mCompTlvs; } - + /** * Gets a tag id of the BER-TLV object. - * + * * @return A tag integer. */ public int getTag() { @@ -59,7 +59,7 @@ class BerTlv { /** * Decodes a BER-TLV object from a byte array. - * + * * @param data A byte array to decode from * @return A BER-TLV object decoded * @throws ResultException diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java index 60e8148..a27c582 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java @@ -19,7 +19,7 @@ package com.android.internal.telephony.gsm.stk; import android.graphics.Bitmap; /** - * Container class for proactive command parameters. + * Container class for proactive command parameters. * */ class CommandParams { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java index eb354e9..06b36a4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java @@ -20,7 +20,7 @@ import android.graphics.Bitmap; import android.os.Handler; import android.os.Message; -import com.android.internal.telephony.gsm.GsmAlphabet; +import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.gsm.SIMFileHandler; import java.util.Iterator; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java b/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java index 833ff3c..4f746ac 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java @@ -22,9 +22,9 @@ import java.util.List; /** * Class for representing COMPREHENSION-TLV objects. - * + * * @see "ETSI TS 101 220 subsection 7.1.1" - * + * * {@hide} */ class ComprehensionTlv { @@ -38,7 +38,7 @@ class ComprehensionTlv { * Constructor. Private on purpose. Use * {@link #decodeMany(byte[], int) decodeMany} or * {@link #decode(byte[], int) decode} method. - * + * * @param tag The tag for this object * @param cr Comprehension Required flag * @param length Length of the value @@ -76,7 +76,7 @@ class ComprehensionTlv { /** * Parses a list of COMPREHENSION-TLV objects from a byte array. - * + * * @param data A byte array containing data to be parsed * @param startIndex Index in data at which to start parsing * @return A list of COMPREHENSION-TLV objects parsed @@ -97,7 +97,7 @@ class ComprehensionTlv { /** * Parses an COMPREHENSION-TLV object from a byte array. - * + * * @param data A byte array containing data to be parsed * @param startIndex Index in data at which to start parsing * @return A COMPREHENSION-TLV object parsed diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java b/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java index a63d1ca..fc02d2a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java @@ -30,9 +30,9 @@ import android.util.Log; import java.util.HashMap; /** - * Class for loading icons from the SIM card. Has two states: single, for loading + * Class for loading icons from the SIM card. Has two states: single, for loading * one icon. Multi, for loading icons list. - * + * */ class IconLoader extends Handler { // members @@ -51,13 +51,13 @@ class IconLoader extends Handler { private static IconLoader sLoader = null; - // Loader state values. + // Loader state values. private static final int STATE_SINGLE_ICON = 1; private static final int STATE_MULTI_ICONS = 2; - // Finished loading single record from a linear-fixed EF-IMG. + // Finished loading single record from a linear-fixed EF-IMG. private static final int EVENT_READ_EF_IMG_RECOED_DONE = 1; - // Finished loading single icon from a Transparent DF-Graphics. + // Finished loading single icon from a Transparent DF-Graphics. private static final int EVENT_READ_ICON_DONE = 2; // Finished loading single colour icon lookup table. private static final int EVENT_READ_CLUT_DONE = 3; @@ -170,10 +170,10 @@ class IconLoader extends Handler { } /** - * Handles Image descriptor parsing and required processing. This is the + * Handles Image descriptor parsing and required processing. This is the * first step required to handle retrieving icons from the SIM. - * - * @param data byte [] containing Image Instance descriptor as defined in + * + * @param data byte [] containing Image Instance descriptor as defined in * TS 51.011. */ private boolean handleImageDescriptor(byte[] rawData) { @@ -232,7 +232,7 @@ class IconLoader extends Handler { * @param data The raw data * @param length The length of image body * @return The bitmap - */ + */ public static Bitmap parseToBnW(byte[] data, int length){ int valueIndex = 0; int width = data[valueIndex++] & 0xFF; @@ -264,7 +264,7 @@ class IconLoader extends Handler { * 0 is black * 1 is white * @param bit to decode - * @return RGB color + * @return RGB color */ private static int bitToBnW(int bit){ if(bit == 1){ @@ -276,11 +276,11 @@ class IconLoader extends Handler { /** * a TS 131.102 image instance of code scheme '11' into color Bitmap - * + * * @param data The raw data * @param length the length of image body * @param transparency with or without transparency - * @param clut coulor lookup table + * @param clut coulor lookup table * @return The color bitmap */ public static Bitmap parseToRGB(byte[] data, int length, @@ -321,9 +321,9 @@ class IconLoader extends Handler { return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); } - + /** - * Calculate bit mask for a given number of bits. The mask should enable to + * Calculate bit mask for a given number of bits. The mask should enable to * make a bitwise and to the given number of bits. * @param numOfBits number of bits to calculate mask for. * @return bit mask diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java b/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java index 7120a37..880b9e5 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java @@ -33,7 +33,8 @@ public class ImageDescriptor { static final int CODING_SCHEME_BASIC = 0x11; static final int CODING_SCHEME_COLOUR = 0x21; - public static final int ID_LENGTH = 9; + // public static final int ID_LENGTH = 9; + // ID_LENGTH substituted by IccFileHandlerBase.GET_RESPONSE_EF_IMG_SIZE_BYTES ImageDescriptor() { width = 0; @@ -47,7 +48,7 @@ public class ImageDescriptor { /** * Extract descriptor information about image instance. - * + * * @param rawData * @param valueIndex * @return ImageDescriptor diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java b/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java index 9afa063..810afd2 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java @@ -16,8 +16,8 @@ package com.android.internal.telephony.gsm.stk; -import com.android.internal.telephony.gsm.EncodeException; -import com.android.internal.telephony.gsm.GsmAlphabet; +import com.android.internal.telephony.EncodeException; +import com.android.internal.telephony.GsmAlphabet; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java b/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java index 5d82473..1cf38ed 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java @@ -17,7 +17,7 @@ package com.android.internal.telephony.gsm.stk; import com.android.internal.telephony.gsm.SIMFileHandler; -import com.android.internal.telephony.gsm.SimUtils; +import com.android.internal.telephony.IccUtils; import android.os.Handler; import android.os.HandlerState; @@ -142,7 +142,7 @@ class RilMessageDecoder extends HandlerStateMachine { case StkService.MSG_ID_REFRESH: byte[] rawData = null; try { - rawData = SimUtils.hexStringToBytes((String) rilMsg.mData); + rawData = IccUtils.hexStringToBytes((String) rilMsg.mData); } catch (Exception e) { // zombie messages are dropped StkLog.d(this, "decodeMessageParams dropping zombie messages"); diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java b/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java index c0c4ceb..3de14f0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java @@ -23,11 +23,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; -import com.android.internal.telephony.gsm.CommandsInterface; -import com.android.internal.telephony.gsm.GsmSimCard; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.CommandsInterface; +import com.android.internal.telephony.EncodeException; +import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.gsm.SimCard; import com.android.internal.telephony.gsm.SIMFileHandler; import com.android.internal.telephony.gsm.SIMRecords; -import com.android.internal.telephony.gsm.SimUtils; import android.util.Config; @@ -115,10 +117,12 @@ class RilMessage { */ public class StkService extends Handler implements AppInterface { + // Class members + private static SIMRecords mSimRecords; + // Service members. private static StkService sInstance; private CommandsInterface mCmdIf; - private SIMRecords mSimRecords; private Context mContext; private StkCmdMessage mCurrntCmd = null; private StkCmdMessage mMenuCmd = null; @@ -147,7 +151,7 @@ public class StkService extends Handler implements AppInterface { /* Intentionally private for singleton */ private StkService(CommandsInterface ci, SIMRecords sr, Context context, - SIMFileHandler fh, GsmSimCard sc) { + SIMFileHandler fh, SimCard sc) { if (ci == null || sr == null || context == null || fh == null || sc == null) { throw new NullPointerException( @@ -172,6 +176,23 @@ public class StkService extends Handler implements AppInterface { mSimRecords.registerForRecordsLoaded(this, MSG_ID_SIM_LOADED, null); } + public void dispose() { + mSimRecords.unregisterForRecordsLoaded(this); + mCmdIf.unSetOnStkSessionEnd(this); + mCmdIf.unSetOnStkProactiveCmd(this); + mCmdIf.unSetOnStkEvent(this); + mCmdIf.unSetOnStkCallSetUp(this); + + this.removeCallbacksAndMessages(null); + + //removing instance + sInstance = null; + } + + protected void finalize() { + StkLog.d(this, "Service finalized"); + } + private void handleRilMsg(RilMessage rilMsg) { if (rilMsg == null) { return; @@ -334,7 +355,7 @@ public class StkService extends Handler implements AppInterface { } byte[] rawData = buf.toByteArray(); - String hexString = SimUtils.bytesToHexString(rawData); + String hexString = IccUtils.bytesToHexString(rawData); if (Config.LOGD) { StkLog.d(this, "TERMINAL RESPONSE: " + hexString); } @@ -380,7 +401,7 @@ public class StkService extends Handler implements AppInterface { int len = rawData.length - 2; // minus (tag + length) rawData[1] = (byte) len; - String hexString = SimUtils.bytesToHexString(rawData); + String hexString = IccUtils.bytesToHexString(rawData); mCmdIf.sendEnvelope(hexString, null); } @@ -423,7 +444,7 @@ public class StkService extends Handler implements AppInterface { int len = rawData.length - 2; // minus (tag + length) rawData[1] = (byte) len; - String hexString = SimUtils.bytesToHexString(rawData); + String hexString = IccUtils.bytesToHexString(rawData); mCmdIf.sendEnvelope(hexString, null); } @@ -439,7 +460,7 @@ public class StkService extends Handler implements AppInterface { * @return The only Service object in the system */ public static StkService getInstance(CommandsInterface ci, SIMRecords sr, - Context context, SIMFileHandler fh, GsmSimCard sc) { + Context context, SIMFileHandler fh, SimCard sc) { if (sInstance == null) { if (ci == null || sr == null || context == null || fh == null || sc == null) { @@ -448,6 +469,17 @@ public class StkService extends Handler implements AppInterface { HandlerThread thread = new HandlerThread("Stk Telephony service"); thread.start(); sInstance = new StkService(ci, sr, context, fh, sc); + StkLog.d(sInstance, "NEW sInstance"); + } else if ((sr != null) && (mSimRecords != sr)) { + StkLog.d(sInstance, String.format( + "Reinitialize the Service with SIMRecords sr=0x%x.", sr)); + mSimRecords = sr; + + // re-Register for SIM ready event. + mSimRecords.registerForRecordsLoaded(sInstance, MSG_ID_SIM_LOADED, null); + StkLog.d(sInstance, "sr changed reinitialize and return current sInstance"); + } else { + StkLog.d(sInstance, "Return current sInstance"); } return sInstance; } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java b/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java index 2cf87ba..8c8f977 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java +++ b/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java @@ -16,8 +16,8 @@ package com.android.internal.telephony.gsm.stk; -import com.android.internal.telephony.gsm.GsmAlphabet; -import com.android.internal.telephony.gsm.SimUtils; +import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.gsm.stk.Duration.TimeUnit; import java.io.UnsupportedEncodingException; @@ -117,7 +117,7 @@ abstract class ValueParser { try { int id = rawValue[valueIndex] & 0xff; - String text = SimUtils.adnStringFieldToString(rawValue, + String text = IccUtils.adnStringFieldToString(rawValue, valueIndex + 1, textLen); item = new Item(id, text); } catch (IndexOutOfBoundsException e) { @@ -278,7 +278,7 @@ abstract class ValueParser { int length = ctlv.getLength(); if (length != 0) { try { - return SimUtils.adnStringFieldToString(rawValue, valueIndex, + return IccUtils.adnStringFieldToString(rawValue, valueIndex, length); } catch (IndexOutOfBoundsException e) { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java index 33c1679..3e53654 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -23,19 +23,18 @@ import android.os.Looper; import android.os.Message; import android.util.Log; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.gsm.BaseCommands; +import com.android.internal.telephony.BaseCommands; +import com.android.internal.telephony.CommandException; +import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.gsm.CallFailCause; -import com.android.internal.telephony.gsm.CommandException; -import com.android.internal.telephony.gsm.CommandsInterface; import com.android.internal.telephony.gsm.PDPContextState; import com.android.internal.telephony.gsm.SuppServiceNotification; +import com.android.internal.telephony.Phone; import java.util.ArrayList; public final class SimulatedCommands extends BaseCommands - implements CommandsInterface, SimulatedRadioControl -{ + implements CommandsInterface, SimulatedRadioControl { private final static String LOG_TAG = "SIM"; private enum SimLockState { @@ -43,14 +42,14 @@ public final class SimulatedCommands extends BaseCommands REQUIRE_PIN, REQUIRE_PUK, SIM_PERM_LOCKED - }; + } private enum SimFdnState { NONE, REQUIRE_PIN2, REQUIRE_PUK2, SIM_PERM_LOCKED - }; + } private final static SimLockState INITIAL_LOCK_STATE = SimLockState.NONE; private final static String DEFAULT_SIM_PIN_CODE = "1234"; @@ -103,11 +102,10 @@ public final class SimulatedCommands extends BaseCommands //***** CommandsInterface implementation - public void getSimStatus(Message result) - { + public void getIccStatus(Message result) { switch (mState) { case SIM_READY: - resultSuccess(result, SimStatus.SIM_READY); + resultSuccess(result, IccStatus.ICC_READY); break; case SIM_LOCKED_OR_ABSENT: @@ -115,7 +113,7 @@ public final class SimulatedCommands extends BaseCommands break; default: - resultSuccess(result, SimStatus.SIM_NOT_READY); + resultSuccess(result, IccStatus.ICC_NOT_READY); break; } } @@ -123,13 +121,13 @@ public final class SimulatedCommands extends BaseCommands private void returnSimLockedStatus(Message result) { switch (mSimLockedState) { case REQUIRE_PIN: - Log.i(LOG_TAG, "[SimCmd] returnSimLockedStatus: SIM_PIN"); - resultSuccess(result, SimStatus.SIM_PIN); + Log.i(LOG_TAG, "[SimCmd] returnSimLockedStatus: ICC_PIN"); + resultSuccess(result, IccStatus.ICC_PIN); break; case REQUIRE_PUK: - Log.i(LOG_TAG, "[SimCmd] returnSimLockedStatus: SIM_PUK"); - resultSuccess(result, SimStatus.SIM_PUK); + Log.i(LOG_TAG, "[SimCmd] returnSimLockedStatus: ICC_PUK"); + resultSuccess(result, IccStatus.ICC_PUK); break; default: @@ -139,9 +137,9 @@ public final class SimulatedCommands extends BaseCommands } } - public void supplySimPin(String pin, Message result) { + public void supplyIccPin(String pin, Message result) { if (mSimLockedState != SimLockState.REQUIRE_PIN) { - Log.i(LOG_TAG, "[SimCmd] supplySimPin: wrong state, state=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPin: wrong state, state=" + mSimLockedState); CommandException ex = new CommandException( CommandException.Error.PASSWORD_INCORRECT); @@ -151,7 +149,7 @@ public final class SimulatedCommands extends BaseCommands } if (pin != null && pin.equals(mPinCode)) { - Log.i(LOG_TAG, "[SimCmd] supplySimPin: success!"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPin: success!"); setRadioState(RadioState.SIM_READY); mPinUnlockAttempts = 0; mSimLockedState = SimLockState.NONE; @@ -167,10 +165,10 @@ public final class SimulatedCommands extends BaseCommands if (result != null) { mPinUnlockAttempts ++; - Log.i(LOG_TAG, "[SimCmd] supplySimPin: failed! attempt=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPin: failed! attempt=" + mPinUnlockAttempts); if (mPinUnlockAttempts >= 3) { - Log.i(LOG_TAG, "[SimCmd] supplySimPin: set state to REQUIRE_PUK"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPin: set state to REQUIRE_PUK"); mSimLockedState = SimLockState.REQUIRE_PUK; } @@ -181,9 +179,9 @@ public final class SimulatedCommands extends BaseCommands } } - public void supplySimPuk(String puk, String newPin, Message result) { + public void supplyIccPuk(String puk, String newPin, Message result) { if (mSimLockedState != SimLockState.REQUIRE_PUK) { - Log.i(LOG_TAG, "[SimCmd] supplySimPuk: wrong state, state=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: wrong state, state=" + mSimLockedState); CommandException ex = new CommandException( CommandException.Error.PASSWORD_INCORRECT); @@ -193,7 +191,7 @@ public final class SimulatedCommands extends BaseCommands } if (puk != null && puk.equals(SIM_PUK_CODE)) { - Log.i(LOG_TAG, "[SimCmd] supplySimPuk: success!"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: success!"); setRadioState(RadioState.SIM_READY); mSimLockedState = SimLockState.NONE; mPukUnlockAttempts = 0; @@ -209,10 +207,10 @@ public final class SimulatedCommands extends BaseCommands if (result != null) { mPukUnlockAttempts ++; - Log.i(LOG_TAG, "[SimCmd] supplySimPuk: failed! attempt=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: failed! attempt=" + mPukUnlockAttempts); if (mPukUnlockAttempts >= 10) { - Log.i(LOG_TAG, "[SimCmd] supplySimPuk: set state to SIM_PERM_LOCKED"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk: set state to SIM_PERM_LOCKED"); mSimLockedState = SimLockState.SIM_PERM_LOCKED; } @@ -223,9 +221,9 @@ public final class SimulatedCommands extends BaseCommands } } - public void supplySimPin2(String pin2, Message result) { + public void supplyIccPin2(String pin2, Message result) { if (mSimFdnEnabledState != SimFdnState.REQUIRE_PIN2) { - Log.i(LOG_TAG, "[SimCmd] supplySimPin2: wrong state, state=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: wrong state, state=" + mSimFdnEnabledState); CommandException ex = new CommandException( CommandException.Error.PASSWORD_INCORRECT); @@ -235,7 +233,7 @@ public final class SimulatedCommands extends BaseCommands } if (pin2 != null && pin2.equals(mPin2Code)) { - Log.i(LOG_TAG, "[SimCmd] supplySimPin2: success!"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: success!"); mPin2UnlockAttempts = 0; mSimFdnEnabledState = SimFdnState.NONE; @@ -250,10 +248,10 @@ public final class SimulatedCommands extends BaseCommands if (result != null) { mPin2UnlockAttempts ++; - Log.i(LOG_TAG, "[SimCmd] supplySimPin2: failed! attempt=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: failed! attempt=" + mPin2UnlockAttempts); if (mPin2UnlockAttempts >= 3) { - Log.i(LOG_TAG, "[SimCmd] supplySimPin2: set state to REQUIRE_PUK2"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPin2: set state to REQUIRE_PUK2"); mSimFdnEnabledState = SimFdnState.REQUIRE_PUK2; } @@ -264,9 +262,9 @@ public final class SimulatedCommands extends BaseCommands } } - public void supplySimPuk2(String puk2, String newPin2, Message result) { + public void supplyIccPuk2(String puk2, String newPin2, Message result) { if (mSimFdnEnabledState != SimFdnState.REQUIRE_PUK2) { - Log.i(LOG_TAG, "[SimCmd] supplySimPuk2: wrong state, state=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: wrong state, state=" + mSimLockedState); CommandException ex = new CommandException( CommandException.Error.PASSWORD_INCORRECT); @@ -276,7 +274,7 @@ public final class SimulatedCommands extends BaseCommands } if (puk2 != null && puk2.equals(SIM_PUK2_CODE)) { - Log.i(LOG_TAG, "[SimCmd] supplySimPuk2: success!"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: success!"); mSimFdnEnabledState = SimFdnState.NONE; mPuk2UnlockAttempts = 0; @@ -291,10 +289,10 @@ public final class SimulatedCommands extends BaseCommands if (result != null) { mPuk2UnlockAttempts ++; - Log.i(LOG_TAG, "[SimCmd] supplySimPuk2: failed! attempt=" + + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: failed! attempt=" + mPuk2UnlockAttempts); if (mPuk2UnlockAttempts >= 10) { - Log.i(LOG_TAG, "[SimCmd] supplySimPuk2: set state to SIM_PERM_LOCKED"); + Log.i(LOG_TAG, "[SimCmd] supplyIccPuk2: set state to SIM_PERM_LOCKED"); mSimFdnEnabledState = SimFdnState.SIM_PERM_LOCKED; } @@ -305,7 +303,7 @@ public final class SimulatedCommands extends BaseCommands } } - public void changeSimPin(String oldPin, String newPin, Message result) { + public void changeIccPin(String oldPin, String newPin, Message result) { if (oldPin != null && oldPin.equals(mPinCode)) { mPinCode = newPin; if (result != null) { @@ -317,7 +315,7 @@ public final class SimulatedCommands extends BaseCommands } if (result != null) { - Log.i(LOG_TAG, "[SimCmd] changeSimPin: pin failed!"); + Log.i(LOG_TAG, "[SimCmd] changeIccPin: pin failed!"); CommandException ex = new CommandException( CommandException.Error.PASSWORD_INCORRECT); @@ -326,7 +324,7 @@ public final class SimulatedCommands extends BaseCommands } } - public void changeSimPin2(String oldPin2, String newPin2, Message result) { + public void changeIccPin2(String oldPin2, String newPin2, Message result) { if (oldPin2 != null && oldPin2.equals(mPin2Code)) { mPin2Code = newPin2; if (result != null) { @@ -338,7 +336,7 @@ public final class SimulatedCommands extends BaseCommands } if (result != null) { - Log.i(LOG_TAG, "[SimCmd] changeSimPin: pin2 failed!"); + Log.i(LOG_TAG, "[SimCmd] changeIccPin2: pin2 failed!"); CommandException ex = new CommandException( CommandException.Error.PASSWORD_INCORRECT); @@ -348,14 +346,12 @@ public final class SimulatedCommands extends BaseCommands } public void - changeBarringPassword(String facility, String oldPwd, String newPwd, Message result) - { + changeBarringPassword(String facility, String oldPwd, String newPwd, Message result) { unimplemented(result); } public void - setSuppServiceNotifications(boolean enable, Message result) - { + setSuppServiceNotifications(boolean enable, Message result) { resultSuccess(result, null); if (enable && mSsnNotifyOn) { @@ -477,8 +473,7 @@ public final class SimulatedCommands extends BaseCommands * ar.result contains a List of DriverCall * The ar.result List is sorted by DriverCall.index */ - public void getCurrentCalls (Message result) - { + public void getCurrentCalls (Message result) { if (mState == RadioState.SIM_READY) { //Log.i("GSM", "[SimCmds] getCurrentCalls"); resultSuccess(result, simulatedCallState.getDriverCalls()); @@ -491,14 +486,20 @@ public final class SimulatedCommands extends BaseCommands } /** + * @deprecated + */ + public void getPDPContextList(Message result) { + getDataCallList(result); + } + + /** * returned message * retMsg.obj = AsyncResult ar * ar.exception carries exception on failure * ar.userObject contains the orignal value of result.obj * ar.result contains a List of PDPContextState */ - public void getPDPContextList(Message result) - { + public void getDataCallList(Message result) { resultSuccess(result, new ArrayList(0)); } @@ -513,8 +514,7 @@ public final class SimulatedCommands extends BaseCommands * CLIR_SUPPRESSION == on "CLIR suppression" (allow CLI presentation) * CLIR_INVOCATION == on "CLIR invocation" (restrict CLI presentation) */ - public void dial (String address, int clirMode, Message result) - { + public void dial (String address, int clirMode, Message result) { simulatedCallState.onDial(address); resultSuccess(result, null); @@ -527,8 +527,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is String containing IMSI on success */ - public void getIMSI(Message result) - { + public void getIMSI(Message result) { resultSuccess(result, "012345678901234"); } @@ -539,8 +538,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is String containing IMEI on success */ - public void getIMEI(Message result) - { + public void getIMEI(Message result) { resultSuccess(result, "012345678901234"); } @@ -551,8 +549,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is String containing IMEISV on success */ - public void getIMEISV(Message result) - { + public void getIMEISV(Message result) { resultSuccess(result, "99"); } @@ -567,8 +564,7 @@ public final class SimulatedCommands extends BaseCommands * 3GPP 22.030 6.5.5 * "Releases a specific active call X" */ - public void hangupConnection (int gsmIndex, Message result) - { + public void hangupConnection (int gsmIndex, Message result) { boolean success; success = simulatedCallState.onChld('1', (char)('0'+gsmIndex)); @@ -590,8 +586,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void hangupWaitingOrBackground (Message result) - { + public void hangupWaitingOrBackground (Message result) { boolean success; success = simulatedCallState.onChld('0', '\0'); @@ -612,8 +607,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void hangupForegroundResumeBackground (Message result) - { + public void hangupForegroundResumeBackground (Message result) { boolean success; success = simulatedCallState.onChld('1', '\0'); @@ -634,8 +628,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void switchWaitingOrHoldingAndActive (Message result) - { + public void switchWaitingOrHoldingAndActive (Message result) { boolean success; success = simulatedCallState.onChld('2', '\0'); @@ -655,8 +648,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void conference (Message result) - { + public void conference (Message result) { boolean success; success = simulatedCallState.onChld('3', '\0'); @@ -676,8 +668,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void explicitCallTransfer (Message result) - { + public void explicitCallTransfer (Message result) { boolean success; success = simulatedCallState.onChld('4', '\0'); @@ -694,8 +685,7 @@ public final class SimulatedCommands extends BaseCommands * "Places all active calls on hold except call X with which * communication shall be supported." */ - public void separateConnection (int gsmIndex, Message result) - { + public void separateConnection (int gsmIndex, Message result) { boolean success; char ch = (char)(gsmIndex + '0'); @@ -714,8 +704,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void acceptCall (Message result) - { + public void acceptCall (Message result) { boolean success; success = simulatedCallState.onAnswer(); @@ -733,8 +722,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void rejectCall (Message result) - { + public void rejectCall (Message result) { boolean success; success = simulatedCallState.onChld('0', '\0'); @@ -754,17 +742,22 @@ public final class SimulatedCommands extends BaseCommands * - Any defined in 22.001 F.4 (for generating busy/congestion) * - Cause 68: ACM >= ACMMax */ - public void getLastCallFailCause (Message result) - { + public void getLastCallFailCause (Message result) { int[] ret = new int[1]; ret[0] = nextCallFailCause; resultSuccess(result, ret); } - public void - getLastPdpFailCause (Message result) - { + /** + * @deprecated + */ + public void getLastPdpFailCause (Message result) { + unimplemented(result); + } + + public void getLastDataCallFailCause(Message result) { + // unimplemented(result); } @@ -779,8 +772,7 @@ public final class SimulatedCommands extends BaseCommands * response.obj.result[1] is bit error rate (0-7, 99) * as defined in TS 27.007 8.5 */ - public void getSignalStrength (Message result) - { + public void getSignalStrength (Message result) { int ret[] = new int[2]; ret[0] = 23; @@ -850,8 +842,7 @@ public final class SimulatedCommands extends BaseCommands * Please note that registration state 4 ("unknown") is treated * as "out of service" above */ - public void getRegistrationState (Message result) - { + public void getRegistrationState (Message result) { String ret[] = new String[3]; ret[0] = "5"; // registered roam @@ -878,8 +869,7 @@ public final class SimulatedCommands extends BaseCommands * Please note that registration state 4 ("unknown") is treated * as "out of service" in the Android telephony system */ - public void getGPRSRegistrationState (Message result) - { + public void getGPRSRegistrationState (Message result) { String ret[] = new String[4]; ret[0] = "5"; // registered roam @@ -896,8 +886,7 @@ public final class SimulatedCommands extends BaseCommands * response.obj.result[1] is short alpha or null if unregistered * response.obj.result[2] is numeric or null if unregistered */ - public void getOperator(Message result) - { + public void getOperator(Message result) { String[] ret = new String[3]; ret[0] = "El Telco Loco"; @@ -912,8 +901,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void sendDtmf(char c, Message result) - { + public void sendDtmf(char c, Message result) { resultSuccess(result, null); } @@ -922,8 +910,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void startDtmf(char c, Message result) - { + public void startDtmf(char c, Message result) { resultSuccess(result, null); } @@ -932,8 +919,7 @@ public final class SimulatedCommands extends BaseCommands * ar.userObject contains the orignal value of result.obj * ar.result is null on success and failure */ - public void stopDtmf(Message result) - { + public void stopDtmf(Message result) { resultSuccess(result, null); } @@ -950,16 +936,35 @@ public final class SimulatedCommands extends BaseCommands unimplemented(response); } + public void deleteSmsOnRuim(int index, Message response) { + Log.d(LOG_TAG, "Delete RUIM message at index " + index); + unimplemented(response); + } + public void writeSmsToSim(int status, String smsc, String pdu, Message response) { Log.d(LOG_TAG, "Write SMS to SIM with status " + status); unimplemented(response); } + public void writeSmsToRuim(int status, String pdu, Message response) { + Log.d(LOG_TAG, "Write SMS to RUIM with status " + status); + unimplemented(response); + } public void setupDefaultPDP(String apn, String user, String password, Message result) { unimplemented(result); } + public void setupDataCall(String radioTechnology, String profile, String apn, String user, + String password, Message result) { + unimplemented(result); + } + + public void deactivateDataCall(int cid, Message result) {unimplemented(result);} + + /** + * @deprecated + */ public void deactivateDefaultPDP(int cid, Message result) {unimplemented(result);} public void setPreferredNetworkType(int networkType , Message result) { @@ -994,9 +999,8 @@ public final class SimulatedCommands extends BaseCommands } return false; } - - public void setRadioPower(boolean on, Message result) - { + + public void setRadioPower(boolean on, Message result) { if(on) { if (isSimLocked()) { Log.i("SIM", "[SimCmd] setRadioPower: SIM locked! state=" + @@ -1016,12 +1020,16 @@ public final class SimulatedCommands extends BaseCommands unimplemented(result); } + public void acknowledgeLastIncomingCdmaSms(boolean success, Message result) { + unimplemented(result); + } + /** * parameters equivilient to 27.007 AT+CRSM command * response.obj will be an AsyncResult * response.obj.userObj will be a SimIoResult on success */ - public void simIO (int command, int fileid, String path, int p1, int p2, + public void iccIO (int command, int fileid, String path, int p1, int p2, int p3, String data, String pin2, Message result) { unimplemented(result); } @@ -1069,8 +1077,7 @@ public final class SimulatedCommands extends BaseCommands * @param response is callback message */ - public void queryCallWaiting(int serviceClass, Message response) - { + public void queryCallWaiting(int serviceClass, Message response) { unimplemented(response); } @@ -1081,8 +1088,7 @@ public final class SimulatedCommands extends BaseCommands */ public void setCallWaiting(boolean enable, int serviceClass, - Message response) - { + Message response) { unimplemented(response); } @@ -1092,7 +1098,7 @@ public final class SimulatedCommands extends BaseCommands * @param serviceClass is a sum of SERVICE_CLASSS_* */ public void setCallForward(int action, int cfReason, int serviceClass, - String number, int timeSeconds, Message result) {unimplemented(result);} + String number, int timeSeconds, Message result) {unimplemented(result);} /** * cfReason is one of CF_REASON_* @@ -1103,11 +1109,12 @@ public final class SimulatedCommands extends BaseCommands * An array of length 0 means "disabled for all codes" */ public void queryCallForwardStatus(int cfReason, int serviceClass, - String number, Message result) {unimplemented(result);} + String number, Message result) {unimplemented(result);} public void setNetworkSelectionModeAutomatic(Message result) {unimplemented(result);} - public void setNetworkSelectionModeManual(String operatorNumeric, Message result) {unimplemented(result);} + public void setNetworkSelectionModeManual( + String operatorNumeric, Message result) {unimplemented(result);} /** * Queries whether the current network selection mode is automatic @@ -1117,8 +1124,7 @@ public final class SimulatedCommands extends BaseCommands * a 0 for automatic selection and a 1 for manual selection */ - public void getNetworkSelectionMode(Message result) - { + public void getNetworkSelectionMode(Message result) { int ret[] = new int[1]; ret[0] = 0; @@ -1132,8 +1138,7 @@ public final class SimulatedCommands extends BaseCommands */ public void getAvailableNetworks(Message result) {unimplemented(result);} - public void getBasebandVersion (Message result) - { + public void getBasebandVersion (Message result) { resultSuccess(result, "SimulatedCommands"); } @@ -1172,13 +1177,11 @@ public final class SimulatedCommands extends BaseCommands } - public void resetRadio(Message result) - { + public void resetRadio(Message result) { unimplemented(result); } - public void invokeOemRilRequestRaw(byte[] data, Message response) - { + public void invokeOemRilRequestRaw(byte[] data, Message response) { // Just echo back data if (response != null) { AsyncResult.forMessage(response).result = data; @@ -1186,8 +1189,7 @@ public final class SimulatedCommands extends BaseCommands } } - public void invokeOemRilRequestStrings(String[] strings, Message response) - { + public void invokeOemRilRequestStrings(String[] strings, Message response) { // Just echo back data if (response != null) { AsyncResult.forMessage(response).result = strings; @@ -1200,23 +1202,20 @@ public final class SimulatedCommands extends BaseCommands /** Start the simulated phone ringing */ public void - triggerRing(String number) - { + triggerRing(String number) { simulatedCallState.triggerRing(number); mCallStateRegistrants.notifyRegistrants(); } public void - progressConnectingCallState() - { + progressConnectingCallState() { simulatedCallState.progressConnectingCallState(); mCallStateRegistrants.notifyRegistrants(); } /** If a call is DIALING or ALERTING, progress it all the way to ACTIVE */ public void - progressConnectingToActive() - { + progressConnectingToActive() { simulatedCallState.progressConnectingToActive(); mCallStateRegistrants.notifyRegistrants(); } @@ -1225,40 +1224,34 @@ public final class SimulatedCommands extends BaseCommands * default to true */ public void - setAutoProgressConnectingCall(boolean b) - { + setAutoProgressConnectingCall(boolean b) { simulatedCallState.setAutoProgressConnectingCall(b); } public void - setNextDialFailImmediately(boolean b) - { + setNextDialFailImmediately(boolean b) { simulatedCallState.setNextDialFailImmediately(b); } public void - setNextCallFailCause(int gsmCause) - { + setNextCallFailCause(int gsmCause) { nextCallFailCause = gsmCause; } public void - triggerHangupForeground() - { + triggerHangupForeground() { simulatedCallState.triggerHangupForeground(); mCallStateRegistrants.notifyRegistrants(); } /** hangup holding calls */ public void - triggerHangupBackground() - { + triggerHangupBackground() { simulatedCallState.triggerHangupBackground(); mCallStateRegistrants.notifyRegistrants(); } - public void triggerSsn(int type, int code) - { + public void triggerSsn(int type, int code) { SuppServiceNotification not = new SuppServiceNotification(); not.notificationType = type; not.code = code; @@ -1266,8 +1259,7 @@ public final class SimulatedCommands extends BaseCommands } public void - shutdown() - { + shutdown() { setRadioState(RadioState.RADIO_UNAVAILABLE); Looper looper = mHandlerThread.getLooper(); if (looper != null) { @@ -1278,27 +1270,23 @@ public final class SimulatedCommands extends BaseCommands /** hangup all */ public void - triggerHangupAll() - { + triggerHangupAll() { simulatedCallState.triggerHangupAll(); mCallStateRegistrants.notifyRegistrants(); } public void - triggerIncomingSMS(String message) - { + triggerIncomingSMS(String message) { //TODO } public void - pauseResponses() - { + pauseResponses() { pausedResponseCount++; } public void - resumeResponses() - { + resumeResponses() { pausedResponseCount--; if (pausedResponseCount == 0) { @@ -1313,8 +1301,7 @@ public final class SimulatedCommands extends BaseCommands //***** Private Methods - private void unimplemented(Message result) - { + private void unimplemented(Message result) { if (result != null) { AsyncResult.forMessage(result).exception = new RuntimeException("Unimplemented"); @@ -1327,8 +1314,7 @@ public final class SimulatedCommands extends BaseCommands } } - private void resultSuccess(Message result, Object ret) - { + private void resultSuccess(Message result, Object ret) { if (result != null) { AsyncResult.forMessage(result).result = ret; if (pausedResponseCount > 0) { @@ -1339,8 +1325,7 @@ public final class SimulatedCommands extends BaseCommands } } - private void resultFail(Message result, Throwable tr) - { + private void resultFail(Message result, Throwable tr) { if (result != null) { AsyncResult.forMessage(result).exception = tr; if (pausedResponseCount > 0) { @@ -1351,4 +1336,103 @@ public final class SimulatedCommands extends BaseCommands } } + // ***** Methods for CDMA support + public void + getDeviceIdentity(Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + public void + getCDMASubscription(Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + public void + setCdmaSubscription(int cdmaSubscriptionType, Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + public void queryCdmaRoamingPreference(Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + public void + setPhoneType(int phoneType) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + } + + public void getPreferredVoicePrivacy(Message result) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(result); + } + + public void setPreferredVoicePrivacy(boolean enable, Message result) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(result); + } + + /** + * Set the TTY mode for the CDMA phone + * + * @param enable is true to enable, false to disable + * @param serviceClass is a sum of SERVICE_CLASS_* + * @param response is callback message + */ + public void setTTYModeEnabled(boolean enable, Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + /** + * Query the TTY mode for the CDMA phone + * (AsyncResult)response.obj).result is an int[] with element [0] set to + * 0 for disabled, 1 for enabled. + * + * @param serviceClass is a sum of SERVICE_CLASS_* + * @param response is callback message + */ + public void queryTTYModeEnabled(Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + /** + * {@inheritDoc} + */ + public void sendCDMAFeatureCode(String FeatureCode, Message response) { + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + unimplemented(response); + } + + /** + * {@inheritDoc} + */ + public void sendCdmaSms(byte[] pdu, Message response){ + Log.w(LOG_TAG, "CDMA not implemented in SimulatedCommands"); + } + + public void activateCdmaBroadcastSms(int activate, Message result) { + // TODO Auto-generated method stub + + } + + public void getCdmaBroadcastConfig(Message result) { + // TODO Auto-generated method stub + + } + + public void setCdmaBroadcastConfig(int[] configValuesArray, Message result) { + // TODO Auto-generated method stub + + } + } diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java b/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java index 340d788..803735c 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedGsmCallState.java @@ -21,14 +21,13 @@ import android.os.Message; import android.os.Handler; import android.telephony.PhoneNumberUtils; import com.android.internal.telephony.ATParseEx; -import com.android.internal.telephony.gsm.DriverCall; +import com.android.internal.telephony.DriverCall; import java.util.List; import java.util.ArrayList; import android.util.Log; -class CallInfo -{ +class CallInfo { enum State { ACTIVE(0), HOLDING(1), @@ -49,8 +48,7 @@ class CallInfo String number; int TOA; - CallInfo (boolean isMT, State state, boolean isMpty, String number) - { + CallInfo (boolean isMT, State state, boolean isMpty, String number) { this.isMT = isMT; this.state = state; this.isMpty = isMpty; @@ -64,20 +62,17 @@ class CallInfo } static CallInfo - createOutgoingCall(String number) - { + createOutgoingCall(String number) { return new CallInfo (false, State.DIALING, false, number); } static CallInfo - createIncomingCall(String number) - { + createIncomingCall(String number) { return new CallInfo (true, State.INCOMING, false, number); } String - toCLCCLine(int index) - { + toCLCCLine(int index) { return "+CLCC: " + index + "," + (isMT ? "1" : "0") +"," @@ -86,8 +81,7 @@ class CallInfo } DriverCall - toDriverCall(int index) - { + toDriverCall(int index) { DriverCall ret; ret = new DriverCall(); @@ -112,36 +106,30 @@ class CallInfo boolean - isActiveOrHeld() - { + isActiveOrHeld() { return state == State.ACTIVE || state == State.HOLDING; } boolean - isConnecting() - { + isConnecting() { return state == State.DIALING || state == State.ALERTING; } boolean - isRinging() - { + isRinging() { return state == State.INCOMING || state == State.WAITING; } } -class InvalidStateEx extends Exception -{ - InvalidStateEx() - { +class InvalidStateEx extends Exception { + InvalidStateEx() { } } -class SimulatedGsmCallState extends Handler -{ +class SimulatedGsmCallState extends Handler { //***** Instance Variables CallInfo calls[] = new CallInfo[MAX_CALLS]; @@ -168,8 +156,7 @@ class SimulatedGsmCallState extends Handler } public void - handleMessage(Message msg) - { + handleMessage(Message msg) { synchronized(this) { switch (msg.what) { // PLEASE REMEMBER // calls may have hung up by the time delayed events happen @@ -181,15 +168,13 @@ class SimulatedGsmCallState extends Handler } //***** Public Methods - - + /** * Start the simulated phone ringing * true if succeeded, false if failed */ public boolean - triggerRing(String number) - { + triggerRing(String number) { synchronized (this) { int empty = -1; boolean isCallWaiting = false; @@ -230,8 +215,7 @@ class SimulatedGsmCallState extends Handler /** If a call is DIALING or ALERTING, progress it to the next state */ public void - progressConnectingCallState() - { + progressConnectingCallState() { synchronized (this) { for (int i = 0 ; i < calls.length ; i++) { CallInfo call = calls[i]; @@ -257,8 +241,7 @@ class SimulatedGsmCallState extends Handler /** If a call is DIALING or ALERTING, progress it all the way to ACTIVE */ public void - progressConnectingToActive() - { + progressConnectingToActive() { synchronized (this) { for (int i = 0 ; i < calls.length ; i++) { CallInfo call = calls[i]; @@ -277,14 +260,12 @@ class SimulatedGsmCallState extends Handler * default to true */ public void - setAutoProgressConnectingCall(boolean b) - { + setAutoProgressConnectingCall(boolean b) { autoProgressConnecting = b; } public void - setNextDialFailImmediately(boolean b) - { + setNextDialFailImmediately(boolean b) { nextDialFailImmediately = b; } @@ -293,8 +274,7 @@ class SimulatedGsmCallState extends Handler * returns true if call was hung up, false if not */ public boolean - triggerHangupForeground() - { + triggerHangupForeground() { synchronized (this) { boolean found; @@ -333,8 +313,7 @@ class SimulatedGsmCallState extends Handler * returns true if call was hung up, false if not */ public boolean - triggerHangupBackground() - { + triggerHangupBackground() { synchronized (this) { boolean found = false; @@ -356,8 +335,7 @@ class SimulatedGsmCallState extends Handler * returns true if call was hung up, false if not */ public boolean - triggerHangupAll() - { + triggerHangupAll() { synchronized(this) { boolean found = false; @@ -376,8 +354,7 @@ class SimulatedGsmCallState extends Handler } public boolean - onAnswer() - { + onAnswer() { synchronized (this) { for (int i = 0 ; i < calls.length ; i++) { CallInfo call = calls[i]; @@ -395,8 +372,7 @@ class SimulatedGsmCallState extends Handler } public boolean - onHangup() - { + onHangup() { boolean found = false; for (int i = 0 ; i < calls.length ; i++) { @@ -412,8 +388,7 @@ class SimulatedGsmCallState extends Handler } public boolean - onChld(char c0, char c1) - { + onChld(char c0, char c1) { boolean ret; int callIndex = 0; @@ -499,8 +474,7 @@ class SimulatedGsmCallState extends Handler public boolean - releaseActiveAcceptHeldOrWaiting() - { + releaseActiveAcceptHeldOrWaiting() { boolean foundHeld = false; boolean foundActive = false; @@ -555,8 +529,7 @@ class SimulatedGsmCallState extends Handler } public boolean - switchActiveAndHeldOrWaiting() - { + switchActiveAndHeldOrWaiting() { boolean hasHeld = false; // first, are there held calls? @@ -589,8 +562,7 @@ class SimulatedGsmCallState extends Handler public boolean - separateCall(int index) - { + separateCall(int index) { try { CallInfo c; @@ -631,8 +603,7 @@ class SimulatedGsmCallState extends Handler public boolean - conference() - { + conference() { int countCalls = 0; // if there's connecting calls, we can't do this yet @@ -662,8 +633,7 @@ class SimulatedGsmCallState extends Handler } public boolean - explicitCallTransfer() - { + explicitCallTransfer() { int countCalls = 0; // if there's connecting calls, we can't do this yet @@ -684,8 +654,7 @@ class SimulatedGsmCallState extends Handler } public boolean - onDial(String address) - { + onDial(String address) { CallInfo call; int freeSlot = -1; @@ -758,8 +727,7 @@ class SimulatedGsmCallState extends Handler } public List - getDriverCalls() - { + getDriverCalls() { ArrayList ret = new ArrayList(calls.length); for (int i = 0 ; i < calls.length ; i++) { @@ -779,8 +747,7 @@ class SimulatedGsmCallState extends Handler } public List - getClccLines() - { + getClccLines() { ArrayList ret = new ArrayList(calls.length); for (int i = 0 ; i < calls.length ; i++) { @@ -795,8 +762,7 @@ class SimulatedGsmCallState extends Handler } private int - countActiveLines() throws InvalidStateEx - { + countActiveLines() throws InvalidStateEx { boolean hasMpty = false; boolean hasHeld = false; boolean hasActive = false; -- cgit v1.1