From 7edec4880a42c5a3fd7af847d4eda501533cd00a Mon Sep 17 00:00:00 2001 From: roytam1 Date: Tue, 24 Jan 2023 11:52:32 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 859764 - Part 5: Changes in Test Cases. r=echen (f1a5cd4fd3) - Bug 859764 - Part 6: Changes in Payment. r=ferjm, r=smaug (2b47641375) - Bug 1010756 - Helpful errors for using nsCOMPtr on non-XPCOM types; r=froydnj (6b1521c482) - leftover (e1a24351e9) - Bug 1192102 - Remove unused file embedded/android/GeckoSmsManager.java. r=blassey (ac05ae649d) - var-let (8ddb529f96) - Bug 1181466 - Fix observe function in SmsService/MmsService. r=btseng (ca93122404) - Bug 733331 - Part 2: Update enabledGsmTableTuples when MCC changes in SmsService.js and fix segmentChars in SmsSegmentHelper.jsm. r=btseng (24fa23e4be) - Bug 1173156 - Fix typo and add a Marionette test case. r=btseng (e7199eb55a) - Bug 1132774 - [B2G][SMS] Enable DEBUG Flag in SmsService if the default value of "ril.debugging.enabled" is true. r=btseng (7300d24fb7) - Bug 1169160 - [MobileConnection] Support more call barring program: all service, outgoing service and incoming service. r=hsinyi,aknow (872c2cc056) - Bug 1169225 - [MobileConnectionService] Support setting/getting call waiting on all serviceClass. r=aknow (14c546a9ca) - Bug 1110619 - Part 1: IDL Interface Changes. r=echen (beae2b4b77) - Bug 1168064 - B2G RIL: filter out cell info with unknown values. r=echen (11dfc5a7e8) - Bug 1159591 - Part 2: Move MMI logic from ril_worker to telephonyService (Call Forwarding). r=aknow (b4edb76863) - Bug 1159591 - Part 3: Move MMI logic from ril_worker to telephonyService (Icc Lock). r=aknow (3faba76808) - Bug 1138263 - Make TelephonyService.js JSHint friendly. r=hsinyi (454bd4c765) - Bug 1159591 - Part 4: Move MMI logic from ril_worker to telephonyService (IMEI). r=aknow (57f423ecdd) - Bug 1159591 - Part 5: Move MMI logic from ril_worker to telephonyService (CLIP). r=aknow (c6611dbcb8) - Bug 1159591 - Part 6: Move MMI logic from ril_worker to telephonyService (CLIR). r=aknow (8c1fc03edb) - Bug 1159591 - Part 7: Move MMI logic from ril_worker to telephonyService (Call Barring Password). r=aknow (5d8bcbf177) - Bug 1159591 - Part 8: Move MMI logic from ril_worker to telephonyService (Call Barring). r=aknow (aa0b89aea1) - Bug 1159591 - Part 9: Move MMI logic from ril_worker to telephonyService (Call Waiting). r=aknow (1d14d2b864) - Bug 1159591 - Part 10: Move MMI logic from ril_worker to telephonyService (USSD). r=aknow (c92130b5b2) - Bug 1159591 - Part 11: Move MMI consts from ril_consts to telephonyService. r=aknow (0e7b3fea9a) - Bug 1159591 - Part 12: Move radio check for MMI to a common place. r=aknow (dcdc3178ad) - Bug 1110619 - Part 2: Implementation Changes. r=echen (6d8e78d684) - Bug 1110619 - Part 3: Bluetooth Changes. r=btian (9787727be0) - Bug 1147736 - Part 1: Extend TelephonyCallInfo. r=aknow (2a426cc99f) - Bug 1147736 - Part 2: Bypass NotifyError. r=aknow (6cd6fd6867) - Bug 1147736 - Part 3: Deprecate NotifyError. r=aknow (e41c719442) - Bug 1147736 - Part 4: Deprecate NotifyError(Bluetooth). r=btian (9c8f97bb22) - Bug 1204817 - Delete the child property of a parent call only when the parent call exists. r=btseng (d35dc6b08f) - Bug 1191205 - Cancel USSD sessions only when needed. r=edgar (01a72dbacb) - Bug 1200134 - Control USSD Sessions with State-Transitions instead of Boolean. r=echen (a4e55b3d9d) - Bug 1163511 - Use defineLazyModuleGetter. r=hsinyi (8a97a4912c) - Bug 991582 - Part 2: Handle the result of RIL request in a consistent way. r=aknow (914ecc2bbb) - Bug 1164248 - Handling of session/sessionEnded for notifyUssdReceived. r=edgar (a79df75d38) - Bug 1223662 - Part 1: Check mmiServiceCode with correct constant. r=echen (abeb286050) - Bug 1174673 - Part 1: Automatically resume the held call. r=hsinyi (280543af7d) - Bug 1174673 - Part 2: Update test case. r=hsinyi (cfe19f1a52) - Bug 1185156 - Fix bug in resuming held call. r=hsinyi (b96346d319) - Bug 1162426 - Part 1: Provide TelephonyUtils. r=hsinyi (62b71e6e83) - Bug 1162426 - Part 2: Test case. r=hsniyi (13a0b3c6f5) - Bug 1171807 - Part 1: Add enums for TelephonyCall::State and TelephonyCallGroup::State (WebIDL). r=hsinyi (0b698eecc4) - Bug 1145551 - DTMF should be sent using the active SIM, the given or the default one (in that order). r=aknow (6157636493) - Bug 1171807 - Part 2: Move to enums and deprecate TelephonyCall.mCallState and TelephonyCallGroup.mCallState (DOM). r=btseng (5faef22d91) - Bug 1168515 - do not block incall MMI requests on alerting state. r=aknow (b1f85c5789) - Bug 1155072 - Part 1: Deprecate nsITelephonyListener.conferenceCallStateChanged (Telephony). r=btseng (e41d1a4bc9) - Bug 1155072 - Part 2: Deprecate nsITelephonyListener.conferenceCallStateChanged (Bluetooth). r=btian (27e69fa89b) - Bug 1166936 - JS Warning in TelephonyService.js r=aknow (3559d3ad3d) - Bug 1191237 - Part 1: Enhance |TelephonyService.js|. r=aknow (86576a6d32) - Bug 1202902 - Fix the world. (0dc256d67d) - Bug 1161438 - Part 1 - Exporting contact to SIM should also return updated contact. r=echen (505d7d7f83) - Bug 1159622 - Split test_icc_contact.js into read contact and add contact. r=echen (fec0c428df) - Bug 1122376 - Support read SIM contact dialling number exceed 20 digits. r=echen (5d0599e93c) - Bug 1161438 - Part 2 - marionette testcase. r=echen (1f0d18a479) - Bug 1114937 - Part 5: Fix Test Case to Remove Contact with Correct Contact Id. r=echen (8d746fdbd2) - Bug 1194149 - Continue importing contacts when there is no sufficient Type 2 USIM contact fields record. r=echen (e9be40dbf2) - Bug 962995 - xpcshell tests for write ICC UCS2 characters for 0x81 and 0x82 encoding. r=echen (9500afaa4d) - Bug 1161438 - Part 3 - xpcshell testcase. r=echen (01f7fb4514) - Bug 1122376 - Support write SIM contact dialling number exceed 20 digits. r=echen (91133e286d) - Bug 999300 - Part 1: Removed the Ril v5 legacy support. r=edgar (ded77fcb6f) - Bug 999300 - Part 2: Update the related testcases. r=edgar (f77a8b96cc) - Bug 1177146 - [Aries][RIL] Reply from QUERY_AVAILABLE_NETWORKS has extra strings. r=hsinyi (a6816cbbab) - Bug 1043250 - Part 1: Update ril_worker and xpcshell test. r=btseng (8b9b25b5cf) - Bug 1185406 - B2G RIL: Read 'pcscf' and expose it in nsIRilNetworkInfo. r=hsinyi (ce707ecb83) - Bug 1174998 - Part 3: Read data call's MTU from network/apn settings. r=echen (bfa08d8380) - Bug 1166320 - Make volume service safer to use off main thread. r=dhylands (b3976622ad) - Bug 1177374 - Call realpath on volume mount points so thatVolumeService::GetVolumeByPath works properly. r=achen (aea97080eb) - Bug 1195166 - AutoMounter: add ignore command to allow volumes to be ignored. r=alchen (f265d832c8) - Bug 1196724 - Refactoring of AudioManager r=alwu (e5b896b7ce) - Bug 1222564 - Save audio volume to database r=alwu (1303d01ae7) - Bug 1164049 - Fix some mode lines in embedding/. r=smaug (79ddce4871) --- b2g/installer/package-manifest.in | 2 + dom/bluetooth/common/BluetoothRilListener.cpp | 45 +- .../gonk/CellBroadcastService.js | 6 +- dom/icc/tests/marionette/manifest.ini | 4 +- .../tests/marionette/test_icc_contact_add.js | 97 + ...cc_contact.js => test_icc_contact_read.js} | 56 +- .../marionette/test_icc_contact_update.js | 122 ++ dom/mobileconnection/MobileConnection.cpp | 13 +- .../MobileConnectionCallback.cpp | 8 + .../gonk/MobileConnectionService.js | 335 +++- .../gonk/nsIGonkMobileConnectionService.idl | 25 +- .../interfaces/nsICellInfo.idl | 107 +- .../interfaces/nsIMobileConnectionService.idl | 33 +- .../interfaces/nsIMobileDeviceIdentities.idl | 46 + .../interfaces/nsINeighboringCellInfo.idl | 10 +- .../ipc/MobileConnectionChild.cpp | 17 +- .../ipc/MobileConnectionChild.h | 3 + .../ipc/MobileConnectionParent.cpp | 17 +- .../ipc/PMobileConnection.ipdl | 1 + .../ipc/PMobileConnectionRequest.ipdl | 6 + dom/mobileconnection/moz.build | 1 + dom/mobileconnection/tests/marionette/head.js | 31 + .../tests/marionette/manifest.ini | 3 +- ...tion.js => test_call_barring_get_error.js} | 2 +- .../marionette/test_call_barring_set_error.js | 2 +- .../tests/marionette/test_call_waiting.js | 29 + dom/mobilemessage/gonk/MmsPduHelper.jsm | 6 +- dom/mobilemessage/gonk/MmsService.js | 7 +- .../gonk/MobileMessageDatabaseService.js | 2 +- dom/mobilemessage/gonk/SmsSegmentHelper.jsm | 9 +- dom/mobilemessage/gonk/SmsService.js | 88 +- dom/mobilemessage/gonk/WspPduHelper.jsm | 4 +- .../interfaces/nsIMobileMessageCallback.idl | 2 +- dom/mobilemessage/ipc/SmsChild.cpp | 1 - dom/mobilemessage/tests/marionette/head.js | 4 +- .../test_error_of_mms_manual_retrieval.js | 2 +- .../tests/marionette/test_incoming.js | 2 +- .../tests/marionette/test_message_classes.js | 2 +- .../tests/marionette/test_outgoing.js | 2 +- .../tests/mochitest/test_sms_basics.html | 20 +- .../test_smsservice_createsmsmessage.js | 4 +- dom/payment/PaymentProvider.js | 21 +- dom/system/gonk/AudioManager.cpp | 1254 +++++++----- dom/system/gonk/AudioManager.h | 146 +- dom/system/gonk/DataCallInterfaceService.js | 4 +- dom/system/gonk/DataCallManager.js | 41 +- dom/system/gonk/RadioInterfaceLayer.js | 45 +- dom/system/gonk/Volume.cpp | 27 +- dom/system/gonk/Volume.h | 2 + dom/system/gonk/VolumeManager.cpp | 32 + dom/system/gonk/VolumeManager.h | 1 + dom/system/gonk/android_audio/AudioSystem.h | 20 + .../gonk/nsIDataCallInterfaceService.idl | 13 +- dom/system/gonk/nsIDataCallManager.idl | 13 +- dom/system/gonk/nsVolume.cpp | 46 +- dom/system/gonk/nsVolume.h | 22 +- dom/system/gonk/nsVolumeService.cpp | 76 +- dom/system/gonk/nsVolumeService.h | 7 +- dom/system/gonk/ril_consts.js | 182 +- dom/system/gonk/ril_worker.js | 1706 +++++++---------- .../test_ril_worker_cellbroadcast_config.js | 2 +- dom/system/gonk/tests/test_ril_worker_cf.js | 6 +- dom/system/gonk/tests/test_ril_worker_clip.js | 4 +- dom/system/gonk/tests/test_ril_worker_clir.js | 8 +- dom/system/gonk/tests/test_ril_worker_cw.js | 16 +- .../tests/test_ril_worker_icc_CardLock.js | 34 +- .../test_ril_worker_icc_ICCContactHelper.js | 290 ++- .../tests/test_ril_worker_icc_ICCPDUHelper.js | 176 +- .../test_ril_worker_icc_ICCRecordHelper.js | 385 +++- .../test_ril_worker_icc_SimRecordHelper.js | 6 +- dom/system/gonk/tests/test_ril_worker_mmi.js | 525 ----- .../gonk/tests/test_ril_worker_mmi_cf.js | 3 - .../tests/test_ril_worker_smsc_address.js | 43 +- dom/system/gonk/tests/xpcshell.ini | 2 - dom/telephony/Telephony.cpp | 217 +-- dom/telephony/Telephony.h | 19 +- dom/telephony/TelephonyCall.cpp | 205 +- dom/telephony/TelephonyCall.h | 37 +- dom/telephony/TelephonyCallGroup.cpp | 189 +- dom/telephony/TelephonyCallGroup.h | 31 +- dom/telephony/TelephonyCallInfo.cpp | 13 + dom/telephony/TelephonyCallInfo.h | 24 +- dom/telephony/TelephonyDialCallback.cpp | 2 +- dom/telephony/gonk/TelephonyService.js | 1237 +++++++++--- dom/telephony/gonk/TelephonyService.manifest | 2 - dom/telephony/gonk/TelephonyUtils.jsm | 109 ++ dom/telephony/gonk/USSDReceivedWrapper.js | 82 + .../gonk/USSDReceivedWrapper.manifest | 2 + dom/telephony/ipc/PTelephony.ipdl | 4 - dom/telephony/ipc/TelephonyChild.cpp | 20 - dom/telephony/ipc/TelephonyChild.h | 7 - dom/telephony/ipc/TelephonyIPCSerializer.h | 34 +- dom/telephony/ipc/TelephonyIPCService.cpp | 19 - dom/telephony/ipc/TelephonyParent.cpp | 34 - dom/telephony/moz.build | 8 +- dom/telephony/nsITelephonyCallInfo.idl | 10 +- dom/telephony/nsITelephonyService.idl | 27 +- dom/telephony/test/marionette/head.js | 6 + dom/telephony/test/marionette/manifest.ini | 4 +- .../test/marionette/test_TelephonyUtils.js | 91 + dom/telephony/test/marionette/test_dtmf.js | 69 + .../{test_mmi.js => test_mmi_imei.js} | 0 .../test/marionette/test_multiple_hold.js | 10 +- dom/telephony/test/xpcshell/test_parseMMI.js | 33 +- dom/webidl/MozMobileConnection.webidl | 3 + dom/webidl/MozPaymentProvider.webidl | 12 +- dom/webidl/TelephonyCall.webidl | 11 +- dom/webidl/TelephonyCallGroup.webidl | 8 +- embedding/browser/nsCTooltipTextProvider.h | 3 +- embedding/browser/nsCommandHandler.cpp | 6 +- embedding/browser/nsCommandHandler.h | 6 +- embedding/browser/nsContextMenuInfo.h | 6 +- embedding/browser/nsDocShellTreeOwner.cpp | 3 +- embedding/browser/nsDocShellTreeOwner.h | 6 +- embedding/browser/nsEmbedStream.cpp | 3 +- embedding/browser/nsEmbedStream.h | 3 +- .../browser/nsWebBrowserContentPolicy.cpp | 7 +- embedding/browser/nsWebBrowserContentPolicy.h | 6 +- .../nsBaseCommandController.cpp | 3 +- .../commandhandler/nsBaseCommandController.h | 3 +- .../commandhandler/nsCommandGroup.cpp | 3 +- .../commandhandler/nsCommandGroup.h | 3 +- .../nsControllerCommandTable.cpp | 3 +- .../commandhandler/nsControllerCommandTable.h | 3 +- embedding/components/find/nsFind.cpp | 3 +- embedding/components/find/nsFind.h | 3 +- .../windowwatcher/nsAutoWindowStateHelper.cpp | 3 +- .../windowwatcher/nsAutoWindowStateHelper.h | 3 +- .../windowwatcher/nsDialogParamBlock.cpp | 3 +- .../windowwatcher/nsDialogParamBlock.h | 3 +- .../components/windowwatcher/nsPromptUtils.h | 2 + xpcom/glue/nsCOMPtr.h | 29 + 132 files changed, 5482 insertions(+), 3547 deletions(-) create mode 100644 dom/icc/tests/marionette/test_icc_contact_add.js rename dom/icc/tests/marionette/{test_icc_contact.js => test_icc_contact_read.js} (51%) create mode 100644 dom/icc/tests/marionette/test_icc_contact_update.js create mode 100644 dom/mobileconnection/interfaces/nsIMobileDeviceIdentities.idl rename dom/mobileconnection/tests/marionette/{test_call_barring_get_option.js => test_call_barring_get_error.js} (97%) create mode 100644 dom/mobileconnection/tests/marionette/test_call_waiting.js delete mode 100644 dom/system/gonk/tests/test_ril_worker_mmi.js create mode 100644 dom/telephony/gonk/TelephonyUtils.jsm create mode 100644 dom/telephony/gonk/USSDReceivedWrapper.js create mode 100644 dom/telephony/gonk/USSDReceivedWrapper.manifest create mode 100644 dom/telephony/test/marionette/test_TelephonyUtils.js create mode 100644 dom/telephony/test/marionette/test_dtmf.js rename dom/telephony/test/marionette/{test_mmi.js => test_mmi_imei.js} (100%) diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 23c8a1887e..6685ea175e 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -538,6 +538,8 @@ @RESPATH@/components/RILSystemMessengerHelper.manifest @RESPATH@/components/TelephonyAudioService.js @RESPATH@/components/TelephonyAudioService.manifest +@RESPATH@/components/USSDReceivedWrapper.js +@RESPATH@/components/USSDReceivedWrapper.manifest #ifndef DISABLE_MOZ_RIL_GEOLOC @RESPATH@/components/TelephonyService.js @RESPATH@/components/TelephonyService.manifest diff --git a/dom/bluetooth/common/BluetoothRilListener.cpp b/dom/bluetooth/common/BluetoothRilListener.cpp index e2c09134a9..d7fcae68c6 100644 --- a/dom/bluetooth/common/BluetoothRilListener.cpp +++ b/dom/bluetooth/common/BluetoothRilListener.cpp @@ -165,6 +165,12 @@ MobileConnectionListener::NotifyNetworkSelectionModeChanged() return NS_OK; } +NS_IMETHODIMP +MobileConnectionListener::NotifyDeviceIdentitiesChanged() +{ + return NS_OK; +} + bool MobileConnectionListener::Listen(bool aStart) { @@ -203,16 +209,23 @@ TelephonyListener::HandleCallInfo(nsITelephonyCallInfo* aInfo, bool aSend) uint32_t callIndex; uint16_t callState; nsAutoString number; + nsAutoString disconnectedReason; bool isOutgoing; bool isConference; aInfo->GetCallIndex(&callIndex); aInfo->GetCallState(&callState); aInfo->GetNumber(number); + aInfo->GetDisconnectedReason(disconnectedReason); aInfo->GetIsOutgoing(&isOutgoing); aInfo->GetIsConference(&isConference); - hfp->HandleCallStateChanged(callIndex, callState, EmptyString(), number, + // The disconnectedReason of a disconnected call must be nonempty no matter + // the call is disconnected for a normal reason or an error. + MOZ_ASSERT((callState != nsITelephonyService::CALL_STATE_DISCONNECTED || + !disconnectedReason.IsEmpty()), + "disconnectedReason of an disconnected call must be nonempty."); + hfp->HandleCallStateChanged(callIndex, callState, disconnectedReason, number, isOutgoing, isConference, aSend); return NS_OK; } @@ -233,36 +246,6 @@ TelephonyListener::EnumerateCallState(nsITelephonyCallInfo* aInfo) return HandleCallInfo(aInfo, false); } -NS_IMETHODIMP -TelephonyListener::NotifyError(uint32_t aServiceId, - int32_t aCallIndex, - const nsAString& aError) -{ - BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); - NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE); - - if (aCallIndex > 0) { - // In order to not miss any related call state transition. - // It's possible that 3G network signal lost for unknown reason. - // If a call is released abnormally, NotifyError() will be called, - // instead of CallStateChanged(). We need to reset the call array state - // via setting CALL_STATE_DISCONNECTED - hfp->HandleCallStateChanged(aCallIndex, - nsITelephonyService::CALL_STATE_DISCONNECTED, - aError, EmptyString(), false, false, true); - BT_WARNING("Reset the call state due to call transition ends abnormally"); - } - - BT_WARNING(NS_ConvertUTF16toUTF8(aError).get()); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyListener::ConferenceCallStateChanged(uint16_t aCallState) -{ - return NS_OK; -} - NS_IMETHODIMP TelephonyListener::EnumerateCallStateComplete() { diff --git a/dom/cellbroadcast/gonk/CellBroadcastService.js b/dom/cellbroadcast/gonk/CellBroadcastService.js index 5b11d5f5cf..7555ad301c 100644 --- a/dom/cellbroadcast/gonk/CellBroadcastService.js +++ b/dom/cellbroadcast/gonk/CellBroadcastService.js @@ -47,7 +47,7 @@ const CELLBROADCASTETWSINFO_CID = const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown"; -let DEBUG; +var DEBUG; function debug(s) { dump("CellBroadcastService: " + s); } @@ -156,7 +156,7 @@ CellBroadcastService.prototype = { .getRadioInterface(clientId).sendWorkerMessage("setCellBroadcastSearchList", { searchList: newSearchList }, (function callback(aResponse) { - if (DEBUG && !aResponse.success) { + if (DEBUG && aResponse.errorMsg) { debug("Failed to set new search list: " + newSearchList + " to client id: " + clientId); } @@ -165,7 +165,7 @@ CellBroadcastService.prototype = { if (responses.length == numOfRilClients) { let successCount = 0; for (let i = 0; i < responses.length; i++) { - if (responses[i].success) { + if (!responses[i].errorMsg) { successCount++; } } diff --git a/dom/icc/tests/marionette/manifest.ini b/dom/icc/tests/marionette/manifest.ini index b500a10c4b..3dcfa740fb 100644 --- a/dom/icc/tests/marionette/manifest.ini +++ b/dom/icc/tests/marionette/manifest.ini @@ -3,7 +3,9 @@ b2g = true browser = false qemu = true -[test_icc_contact.js] +[test_icc_contact_read.js] +[test_icc_contact_add.js] +[test_icc_contact_update.js] [test_icc_card_lock_get_retry_count.js] [test_icc_card_lock_change_pin.js] [test_icc_card_lock_enable_pin.js] diff --git a/dom/icc/tests/marionette/test_icc_contact_add.js b/dom/icc/tests/marionette/test_icc_contact_add.js new file mode 100644 index 0000000000..2e39580198 --- /dev/null +++ b/dom/icc/tests/marionette/test_icc_contact_add.js @@ -0,0 +1,97 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 120000; +MARIONETTE_HEAD_JS = "head.js"; + +var TEST_ADD_DATA = [{ + // a contact without email and anr. + name: ["add1"], + tel: [{value: "0912345678"}], + }, { + // a contact over 20 digits. + name: ["add2"], + tel: [{value: "012345678901234567890123456789"}], + }, { + // a contact over 40 digits. + name: ["add3"], + tel: [{value: "01234567890123456789012345678901234567890123456789"}], + }, { + // a contact with email but without anr. + name: ["add4"], + tel: [{value: "01234567890123456789"}], + email:[{value: "test@mozilla.com"}], + }, { + // a contact with anr but without email. + name: ["add5"], + tel: [{value: "01234567890123456789"}, {value: "123456"}, {value: "123"}], + }, { + // a contact with email and anr. + name: ["add6"], + tel: [{value: "01234567890123456789"}, {value: "123456"}, {value: "123"}], + email:[{value: "test@mozilla.com"}], + }]; + +function testAddContact(aIcc, aType, aMozContact, aPin2) { + log("testAddContact: type=" + aType + ", pin2=" + aPin2); + let contact = new mozContact(aMozContact); + + return aIcc.updateContact(aType, contact, aPin2) + .then((aResult) => { + is(aResult.name[0], aMozContact.name[0]); + // Maximum digits of the Dialling Number is 20, and maximum digits of Extension is 20. + is(aResult.tel[0].value, aMozContact.tel[0].value.substring(0, 40)); + // We only support SIM in emulator, so we don't have anr and email field. + ok(aResult.tel.length == 1); + ok(!aResult.email); + + // Get ICC contact for checking new contact + return aIcc.readContacts(aType) + .then((aResult) => { + let contact = aResult[aResult.length - 1]; + is(contact.name[0], aMozContact.name[0]); + // Maximum digits of the Dialling Number is 20, and maximum digits of Extension is 20. + is(contact.tel[0].value, aMozContact.tel[0].value.substring(0, 40)); + is(contact.id.substring(0, aIcc.iccInfo.iccid.length), aIcc.iccInfo.iccid); + + return contact.id; + }) + .then((aContactId) => { + // Clean up contact + return removeContact(aIcc, aContactId, aType, aPin2); + }); + }, (aError) => { + if (aType === "fdn" && aPin2 === undefined) { + ok(aError.name === "SimPin2", + "expected error when pin2 is not provided"); + } else { + ok(false, "Cannot add " + aType + " contact: " + aError.name); + } + }) +} + +function removeContact(aIcc, aContactId, aType, aPin2) { + log("removeContact: contactId=" + aContactId + + ", type=" + aType + ", pin2=" + aPin2); + + let contact = new mozContact({}); + contact.id = aContactId; + + return aIcc.updateContact(aType, contact, aPin2); +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let promise = Promise.resolve(); + for (let i = 0; i < TEST_ADD_DATA.length; i++) { + let test_data = TEST_ADD_DATA[i]; + // Test add adn contacts + promise = promise.then(() => testAddContact(icc, "adn", test_data)) + // Test add fdn contacts + .then(() => testAddContact(icc, "fdn", test_data, "0000")) + // Test add fdn contacts without passing pin2 + .then(() => testAddContact(icc, "fdn", test_data)); + } + return promise; +}); diff --git a/dom/icc/tests/marionette/test_icc_contact.js b/dom/icc/tests/marionette/test_icc_contact_read.js similarity index 51% rename from dom/icc/tests/marionette/test_icc_contact.js rename to dom/icc/tests/marionette/test_icc_contact_read.js index 7f2f2fdc26..93f3380804 100644 --- a/dom/icc/tests/marionette/test_icc_contact.js +++ b/dom/icc/tests/marionette/test_icc_contact_read.js @@ -9,58 +9,46 @@ function testReadContacts(aIcc, aType) { let iccId = aIcc.iccInfo.iccid; return aIcc.readContacts(aType) .then((aResult) => { - is(Array.isArray(aResult), true); + is(Array.isArray(aResult), true); + is(aResult.length, 6, "Check contact number."); + + // Alpha Id(Encoded with GSM 8 bit): "Mozilla", Dialling Number: 15555218201 is(aResult[0].name[0], "Mozilla"); is(aResult[0].tel[0].value, "15555218201"); is(aResult[0].id, iccId + "1"); + // Alpha Id(Encoded with UCS2 0x80: "Saßê\u9ec3", Dialling Number: 15555218202 is(aResult[1].name[0], "Saßê黃"); is(aResult[1].tel[0].value, "15555218202"); is(aResult[1].id, iccId + "2"); + // Alpha Id(Encoded with UCS2 0x81): "Fire \u706b", Dialling Number: 15555218203 is(aResult[2].name[0], "Fire 火"); is(aResult[2].tel[0].value, "15555218203"); is(aResult[2].id, iccId + "3"); + // Alpha Id(Encoded with UCS2 0x82): "Huang \u9ec3", Dialling Number: 15555218204 is(aResult[3].name[0], "Huang 黃"); is(aResult[3].tel[0].value, "15555218204"); is(aResult[3].id, iccId + "4"); + + // Alpha Id(Encoded with GSM 8 bit): "Contact001", + // Dialling Number: 9988776655443322110001234567890123456789 + is(aResult[4].name[0], "Contact001"); + is(aResult[4].tel[0].value, "9988776655443322110001234567890123456789"); + is(aResult[4].id, iccId + "5"); + + // Alpha Id(Encoded with GSM 8 bit): "Contact002", + // Dialling Number: 0123456789012345678999887766554433221100 + is(aResult[5].name[0], "Contact002"); + is(aResult[5].tel[0].value, "0123456789012345678999887766554433221100"); + is(aResult[5].id, iccId + "6"); }, (aError) => { ok(false, "Cannot get " + aType + " contacts"); }); } -function testAddContact(aIcc, aType, aPin2) { - log("testAddContact: type=" + aType + ", pin2=" + aPin2); - let contact = new mozContact({ - name: ["add"], - tel: [{value: "0912345678"}], - email:[] - }); - - return aIcc.updateContact(aType, contact, aPin2) - .then((aResult) => { - // Get ICC contact for checking new contact - return aIcc.readContacts(aType) - .then((aResult) => { - // There are 4 SIM contacts which are harded in emulator - is(aResult.length, 5); - - is(aResult[4].name[0], "add"); - is(aResult[4].tel[0].value, "0912345678"); - }, (aError) => { - ok(false, "Cannot get " + aType + " contacts: " + aError.name); - }) - }, (aError) => { - if (aType === "fdn" && aPin2 === undefined) { - ok(aError.name === "SimPin2", - "expected error when pin2 is not provided"); - } else { - ok(false, "Cannot add " + aType + " contact: " + aError.name); - } - }); -} // Start tests startTestCommon(function() { @@ -68,14 +56,8 @@ startTestCommon(function() { // Test read adn contacts return testReadContacts(icc, "adn") - // Test add adn contacts - .then(() => testAddContact(icc, "adn")) // Test read fdn contact .then(() => testReadContacts(icc, "fdn")) - // Test add fdn contacts - .then(() => testAddContact(icc, "fdn", "0000")) - // Test add fdn contacts without passing pin2 - .then(() => testAddContact(icc, "fdn")) // Test read sdn contacts .then(() => testReadContacts(icc, "sdn")); }); diff --git a/dom/icc/tests/marionette/test_icc_contact_update.js b/dom/icc/tests/marionette/test_icc_contact_update.js new file mode 100644 index 0000000000..12e304bb7b --- /dev/null +++ b/dom/icc/tests/marionette/test_icc_contact_update.js @@ -0,0 +1,122 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 120000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_UPDATE_DATA = [{ + id: 1, + data: { + name: ["Mozilla"], + tel: [{value: "9876543210987654321001234"}]}, + expect: { + number: "9876543210987654321001234"} + }, { + id:2, + data: { + name: ["Saßê黃"], + tel: [{value: "98765432109876543210998877665544332211001234"}]}, + expect: { + // We don't support extension chain now. + number: "9876543210987654321099887766554433221100"} + }, { + id: 3, + data: { + name: ["Fire 火"], + tel: [{value: ""}]}, + expect: { + number: null} + }, { + id: 5, + data: { + name: ["Contact001"], + tel: [{value: "9988776655443322110098765432109876543210"}]}, + expect: { + number: "9988776655443322110098765432109876543210"} + }, { + id: 6, + data: { + name: ["Contact002"], + tel: [{value: "+99887766554433221100"}]}, + expect: { + number: "+99887766554433221100"} + }]; + +function testUpdateContact(aIcc, aType, aContactId, aMozContact, aExpect, aPin2) { + log("testUpdateContact: type=" + aType + + ", mozContact=" + JSON.stringify(aMozContact) + + ", expect=" + aExpect.number + ", pin2=" + aPin2); + + let contact = new mozContact(aMozContact); + contact.id = aIcc.iccInfo.iccid + aContactId; + + return aIcc.updateContact(aType, contact, aPin2) + .then((aResult) => { + // Get ICC contact for checking expect contact + return aIcc.readContacts(aType) + .then((aResult) => { + let contact = aResult[aContactId - 1]; + + is(contact.name[0], aMozContact.name[0]); + + if (aExpect.number == null) { + is(contact.tel, null); + } else { + is(contact.tel[0].value, aExpect.number); + } + + is(contact.id, aIcc.iccInfo.iccid + aContactId); + }); + }, (aError) => { + if (aType === "fdn" && aPin2 === undefined) { + ok(aError.name === "SimPin2", + "expected error when pin2 is not provided"); + } else { + ok(false, "Cannot update " + aType + " contact: " + aError.name); + } + }); +} + +function revertContact(aIcc, aContact, aType, aPin2) { + log("revertContact: contact:" + JSON.stringify(aContact) + + ", type=" + aType + ", pin2=" + aPin2); + + return aIcc.updateContact(aType, aContact, aPin2); +} + +// Start tests +startTestCommon(function() { + let icc = getMozIcc(); + let adnContacts; + let fdnContacts; + + return icc.readContacts("adn") + .then((aResult) => { + adnContacts = aResult; + }) + .then(() => icc.readContacts("fdn")) + .then((aResult) => { + fdnContacts = aResult; + }) + .then(() => { + let promise = Promise.resolve(); + for (let i = 0; i < TEST_UPDATE_DATA.length; i++) { + let test_data = TEST_UPDATE_DATA[i]; + let adnContact = adnContacts[test_data.id - 1]; + let fdnContact = fdnContacts[test_data.id - 1]; + + // Test update adn contacts + promise = promise.then(() => testUpdateContact(icc, "adn", test_data.id, + test_data.data, test_data.expect)) + // Test update fdn contacts + .then(() => testUpdateContact(icc, "fdn", test_data.id, test_data.data, + test_data.expect)) + // Test update fdn contacts without passing pin2 + .then(() => testUpdateContact(icc, "fdn", test_data.id, test_data.data, + test_data.expect, "0000")) + .then(() => revertContact(icc, adnContact, "adn")) + .then(() => revertContact(icc, fdnContact, "fdn", "0000")); + } + return promise; + }); +}); diff --git a/dom/mobileconnection/MobileConnection.cpp b/dom/mobileconnection/MobileConnection.cpp index aba8052d83..21eff515bb 100644 --- a/dom/mobileconnection/MobileConnection.cpp +++ b/dom/mobileconnection/MobileConnection.cpp @@ -299,7 +299,7 @@ bool MobileConnection::IsValidCallBarringProgram(int32_t aProgram) { return aProgram >= nsIMobileConnection::CALL_BARRING_PROGRAM_ALL_OUTGOING && - aProgram <= nsIMobileConnection::CALL_BARRING_PROGRAM_INCOMING_ROAMING; + aProgram <= nsIMobileConnection::CALL_BARRING_PROGRAM_INCOMING_SERVICE; } bool @@ -875,7 +875,9 @@ MobileConnection::SetCallWaitingOption(bool aEnabled, ErrorResult& aRv) RefPtr requestCallback = new MobileConnectionCallback(GetOwner(), request); - nsresult rv = mMobileConnection->SetCallWaiting(aEnabled, requestCallback); + nsresult rv = mMobileConnection->SetCallWaiting(aEnabled, + nsIMobileConnection::ICC_SERVICE_CLASS_VOICE, + requestCallback); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; @@ -1125,6 +1127,13 @@ MobileConnection::NotifyNetworkSelectionModeChanged() return NS_OK; } +NS_IMETHODIMP +MobileConnection::NotifyDeviceIdentitiesChanged() +{ + // To be supported when bug 1222870 is required in m-c. + return NS_OK; +} + // nsIIccListener NS_IMETHODIMP diff --git a/dom/mobileconnection/MobileConnectionCallback.cpp b/dom/mobileconnection/MobileConnectionCallback.cpp index 7b1c812cb4..6090fba437 100644 --- a/dom/mobileconnection/MobileConnectionCallback.cpp +++ b/dom/mobileconnection/MobileConnectionCallback.cpp @@ -191,6 +191,14 @@ MobileConnectionCallback::NotifyGetCallBarringSuccess(uint16_t aProgram, return NotifySuccess(jsResult); } +NS_IMETHODIMP +MobileConnectionCallback::NotifyGetCallWaitingSuccess(uint16_t aServiceClass) +{ + return (aServiceClass & nsIMobileConnection::ICC_SERVICE_CLASS_VOICE) + ? NotifySuccess(JS::TrueHandleValue) + : NotifySuccess(JS::FalseHandleValue); +} + NS_IMETHODIMP MobileConnectionCallback::NotifyGetClirStatusSuccess(uint16_t aN, uint16_t aM) { diff --git a/dom/mobileconnection/gonk/MobileConnectionService.js b/dom/mobileconnection/gonk/MobileConnectionService.js index 7524e858de..c0114ed5df 100644 --- a/dom/mobileconnection/gonk/MobileConnectionService.js +++ b/dom/mobileconnection/gonk/MobileConnectionService.js @@ -46,8 +46,7 @@ const NS_DATA_CALL_ERROR_TOPIC_ID = "data-call-error"; const kPrefRilDebuggingEnabled = "ril.debugging.enabled"; -const INT32_MAX = 2147483647; -const UNKNOWN_RSSI = 99; +const UNKNOWN_VALUE = Ci.nsICellInfo.UNKNOWN_VALUE; XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionMessenger", "@mozilla.org/ril/system-messenger-helper;1", @@ -77,7 +76,7 @@ XPCOMUtils.defineLazyGetter(this, "gRadioInterfaceLayer", function() { return ril; }); -let DEBUG = RIL.DEBUG_RIL; +var DEBUG = RIL.DEBUG_RIL; function debug(s) { dump("MobileConnectionService: " + s + "\n"); } @@ -163,7 +162,26 @@ MobileCallForwardingOptions.prototype = { serviceClass: Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE } -function NeighboringCellInfo() {} +function NeighboringCellInfo(aOptions) { + this.networkType = aOptions.networkType; + this.gsmLocationAreaCode = (aOptions.gsmLocationAreaCode !== undefined && + aOptions.gsmLocationAreaCode >= 0 && + aOptions.gsmLocationAreaCode <= 65535) ? + aOptions.gsmLocationAreaCode : UNKNOWN_VALUE; + this.gsmCellId = (aOptions.gsmCellId !== undefined && + aOptions.gsmCellId >= 0 && + aOptions.gsmCellId <= 65535) ? + aOptions.gsmCellId : UNKNOWN_VALUE; + this.wcdmaPsc = (aOptions.wcdmaPsc !== undefined && aOptions.wcdmaPsc >= 0 && + aOptions.wcdmaPsc <= 511) ? + aOptions.wcdmaPsc : UNKNOWN_VALUE; + this.signalStrength = (aOptions.signalStrength !== undefined && + ((aOptions.signalStrength >= 0 && + aOptions.signalStrength <= 31) || + (aOptions.signalStrength >= -120 && + aOptions.signalStrength <= -25))) ? + aOptions.signalStrength : UNKNOWN_VALUE; +} NeighboringCellInfo.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsINeighboringCellInfo]), classID: NEIGHBORINGCELLINFO_CID, @@ -173,16 +191,28 @@ NeighboringCellInfo.prototype = { interfaces: [Ci.nsINeighboringCellInfo] }), + isValid: function() { + return !(this.gsmLocationAreaCode == UNKNOWN_VALUE && + this.gsmCellId == UNKNOWN_VALUE && + this.wcdmaPsc == UNKNOWN_VALUE && + this.signalStrength == UNKNOWN_VALUE); + }, + // nsINeighboringCellInfo networkType: null, - gsmLocationAreaCode: -1, - gsmCellId: -1, - wcdmaPsc: -1, - signalStrength: UNKNOWN_RSSI + gsmLocationAreaCode: UNKNOWN_VALUE, + gsmCellId: UNKNOWN_VALUE, + wcdmaPsc: UNKNOWN_VALUE, + signalStrength: UNKNOWN_VALUE }; -function CellInfo() {} +function CellInfo(aOptions) { + this.type = aOptions.type; + this.registered = aOptions.registered; + this.timestampType = aOptions.timestampType; + this.timestamp = aOptions.timestamp; +} CellInfo.prototype = { // nsICellInfo @@ -193,7 +223,28 @@ CellInfo.prototype = { timestamp: 0 }; -function GsmCellInfo() {} +function GsmCellInfo(aOptions) { + CellInfo.call(this, aOptions); + + // Cell Identity + this.mcc = (aOptions.mcc !== undefined && aOptions.mcc >= 0 && + aOptions.mcc <= 999) ? aOptions.mcc : UNKNOWN_VALUE; + this.mnc = (aOptions.mnc !== undefined && aOptions.mnc >= 0 && + aOptions.mnc <= 999) ? aOptions.mnc : UNKNOWN_VALUE; + this.lac = (aOptions.lac !== undefined && aOptions.lac >= 0 && + aOptions.lac <= 65535) ? aOptions.lac : UNKNOWN_VALUE; + this.cid = (aOptions.cid !== undefined && aOptions.cid >= 0 && + aOptions.cid <= 65535) ? aOptions.cid : UNKNOWN_VALUE; + + // Signal Strength + this.signalStrength = (aOptions.signalStrength !== undefined && + aOptions.signalStrength >= 0 && + aOptions.signalStrength <= 31) ? + aOptions.signalStrength : UNKNOWN_VALUE; + this.bitErrorRate = (aOptions.bitErrorRate !== undefined && + aOptions.bitErrorRate >= 0 && aOptions.bitErrorRate <= 7) + ? aOptions.bitErrorRate : UNKNOWN_VALUE; +} GsmCellInfo.prototype = { __proto__: CellInfo.prototype, QueryInterface: XPCOMUtils.generateQI([Ci.nsICellInfo, @@ -205,17 +256,47 @@ GsmCellInfo.prototype = { interfaces: [Ci.nsIGsmCellInfo] }), + isValid: function() { + return !(this.mcc == UNKNOWN_VALUE && this.mnc == UNKNOWN_VALUE && + this.lac == UNKNOWN_VALUE && this.cid == UNKNOWN_VALUE && + this.signalStrength == UNKNOWN_VALUE && + this.bitErrorRate == UNKNOWN_VALUE); + }, + // nsIGsmCellInfo - mcc: INT32_MAX, - mnc: INT32_MAX, - lac: INT32_MAX, - cid: INT32_MAX, - signalStrength: UNKNOWN_RSSI, - bitErrorRate: UNKNOWN_RSSI + mcc: UNKNOWN_VALUE, + mnc: UNKNOWN_VALUE, + lac: UNKNOWN_VALUE, + cid: UNKNOWN_VALUE, + signalStrength: UNKNOWN_VALUE, + bitErrorRate: UNKNOWN_VALUE }; -function WcdmaCellInfo() {} +function WcdmaCellInfo(aOptions) { + CellInfo.call(this, aOptions); + + // Cell Identity + this.mcc = (aOptions.mcc !== undefined && aOptions.mcc >= 0 && + aOptions.mcc <= 999) ? aOptions.mcc : UNKNOWN_VALUE; + this.mnc = (aOptions.mnc !== undefined && aOptions.mnc >= 0 && + aOptions.mnc <= 999) ? aOptions.mnc : UNKNOWN_VALUE; + this.lac = (aOptions.lac !== undefined && aOptions.lac >= 0 && + aOptions.lac <= 65535) ? aOptions.lac : UNKNOWN_VALUE; + this.cid = (aOptions.cid !== undefined && aOptions.cid >= 0 && + aOptions.cid <= 268435455) ? aOptions.cid : UNKNOWN_VALUE; + this.psc = (aOptions.psc !== undefined && aOptions.psc >= 0 && + aOptions.psc <= 511) ? aOptions.psc : UNKNOWN_VALUE; + + // Signal Strength + this.signalStrength = (aOptions.signalStrength !== undefined && + aOptions.signalStrength >= 0 && + aOptions.signalStrength <= 31) ? + aOptions.signalStrength : UNKNOWN_VALUE; + this.bitErrorRate = (aOptions.bitErrorRate !== undefined && + aOptions.bitErrorRate >= 0 && aOptions.bitErrorRate <= 7) + ? aOptions.bitErrorRate : UNKNOWN_VALUE; +} WcdmaCellInfo.prototype = { __proto__: CellInfo.prototype, QueryInterface: XPCOMUtils.generateQI([Ci.nsICellInfo, @@ -227,18 +308,57 @@ WcdmaCellInfo.prototype = { interfaces: [Ci.nsIWcdmaCellInfo] }), + isValid: function() { + return !(this.mcc == UNKNOWN_VALUE && this.mnc == UNKNOWN_VALUE && + this.lac == UNKNOWN_VALUE && this.cid == UNKNOWN_VALUE && + this.psc == UNKNOWN_VALUE && this.signalStrength == UNKNOWN_VALUE && + this.bitErrorRate == UNKNOWN_VALUE); + }, + // nsIWcdmaCellInfo - mcc: INT32_MAX, - mnc: INT32_MAX, - lac: INT32_MAX, - cid: INT32_MAX, - psc: INT32_MAX, - signalStrength: UNKNOWN_RSSI, - bitErrorRate: UNKNOWN_RSSI + mcc: UNKNOWN_VALUE, + mnc: UNKNOWN_VALUE, + lac: UNKNOWN_VALUE, + cid: UNKNOWN_VALUE, + psc: UNKNOWN_VALUE, + signalStrength: UNKNOWN_VALUE, + bitErrorRate: UNKNOWN_VALUE }; -function LteCellInfo() {} +function LteCellInfo(aOptions) { + CellInfo.call(this, aOptions); + + // Cell Identity + this.mcc = (aOptions.mcc !== undefined && aOptions.mcc >= 0 && + aOptions.mcc <= 999) ? aOptions.mcc : UNKNOWN_VALUE; + this.mnc = (aOptions.mnc !== undefined && aOptions.mnc >= 0 && + aOptions.mnc <= 999) ? aOptions.mnc : UNKNOWN_VALUE; + this.cid = (aOptions.cid !== undefined && aOptions.cid >= 0 && + aOptions.cid <= 268435455) ? aOptions.cid : UNKNOWN_VALUE; + this.pcid = (aOptions.pcid !== undefined && aOptions.pcid >= 0 && + aOptions.pcid <= 503) ? aOptions.pcid : UNKNOWN_VALUE; + this.tac = (aOptions.tac !== undefined && aOptions.tac >= 0 && + aOptions.tac <= 65535) ? aOptions.tac : UNKNOWN_VALUE; + + // Signal Strength + this.signalStrength = (aOptions.signalStrength !== undefined && + aOptions.signalStrength >= 0 && + aOptions.signalStrength <= 31) ? + aOptions.signalStrength : UNKNOWN_VALUE; + this.rsrp = (aOptions.rsrp !== undefined && aOptions.rsrp >= 44 && + aOptions.rsrp <= 140) ? aOptions.rsrp : UNKNOWN_VALUE; + this.rsrq = (aOptions.rsrq !== undefined && aOptions.rsrq >= 3 && + aOptions.rsrq <= 20) ? aOptions.rsrq : UNKNOWN_VALUE; + this.rssnr = (aOptions.rssnr !== undefined && aOptions.rssnr >= -200 && + aOptions.rssnr <= 300) ? aOptions.rssnr : UNKNOWN_VALUE; + this.cqi = (aOptions.cqi !== undefined && aOptions.cqi >= 0 && + aOptions.cqi <= 15) ? aOptions.cqi : UNKNOWN_VALUE; + this.timingAdvance = (aOptions.timingAdvance !== undefined && + aOptions.timingAdvance >= 0 && + aOptions.timingAdvance <= 2147483646) ? + aOptions.timingAdvance : UNKNOWN_VALUE; +} LteCellInfo.prototype = { __proto__: CellInfo.prototype, QueryInterface: XPCOMUtils.generateQI([Ci.nsICellInfo, @@ -250,22 +370,64 @@ LteCellInfo.prototype = { interfaces: [Ci.nsILteCellInfo] }), + isValid: function() { + return !(this.mcc == UNKNOWN_VALUE && this.mnc == UNKNOWN_VALUE && + this.cid == UNKNOWN_VALUE && this.pcid == UNKNOWN_VALUE && + this.tac == UNKNOWN_VALUE && this.signalStrength == UNKNOWN_VALUE && + this.rsrp == UNKNOWN_VALUE && this.rsrq == UNKNOWN_VALUE && + this.rssnr == UNKNOWN_VALUE && this.cqi == UNKNOWN_VALUE && + this.timingAdvance == UNKNOWN_VALUE); + }, + // nsILteCellInfo - mcc: INT32_MAX, - mnc: INT32_MAX, - cid: INT32_MAX, - pcid: INT32_MAX, - tac: INT32_MAX, - signalStrength: UNKNOWN_RSSI, - rsrp: INT32_MAX, - rsrq: INT32_MAX, - rssnr: INT32_MAX, - cqi: INT32_MAX, - timingAdvance: INT32_MAX + mcc: UNKNOWN_VALUE, + mnc: UNKNOWN_VALUE, + cid: UNKNOWN_VALUE, + pcid: UNKNOWN_VALUE, + tac: UNKNOWN_VALUE, + signalStrength: UNKNOWN_VALUE, + rsrp: UNKNOWN_VALUE, + rsrq: UNKNOWN_VALUE, + rssnr: UNKNOWN_VALUE, + cqi: UNKNOWN_VALUE, + timingAdvance: UNKNOWN_VALUE }; -function CdmaCellInfo() {} +function CdmaCellInfo(aOptions) { + CellInfo.call(this, aOptions); + + // Cell Identity + this.networkId = (aOptions.networkId !== undefined && + aOptions.networkId >= 0 && aOptions.networkId <= 65535) ? + aOptions.networkId : UNKNOWN_VALUE; + this.systemId = (aOptions.systemId !== undefined && aOptions.systemId >= 0 && + aOptions.systemId <= 32767) ? + aOptions.systemId : UNKNOWN_VALUE; + this.baseStationId = (aOptions.baseStationId !== undefined && + aOptions.baseStationId >= 0 && + aOptions.baseStationId <= 65535) ? + aOptions.baseStationId : UNKNOWN_VALUE; + this.longitude = (aOptions.longitude !== undefined && + aOptions.longitude >= -2592000 && + aOptions.longitude <= 2592000) ? + aOptions.longitude : UNKNOWN_VALUE; + this.latitude = (aOptions.latitude !== undefined && + aOptions.latitude >= -1296000 && + aOptions.latitude <= 1296000) ? + aOptions.latitude : UNKNOWN_VALUE; + + // Signal Strength + this.cdmaEcio = (aOptions.cdmaEcio !== undefined && + aOptions.cdmaEcio >= 0) ? aOptions.cdmaEcio : UNKNOWN_VALUE; + this.evdoDbm = (aOptions.evdoDbm !== undefined && + aOptions.evdoDbm >= 0) ? aOptions.evdoDbm : UNKNOWN_VALUE; + this.evdoEcio = (aOptions.evdoEcio !== undefined && + aOptions.evdoEcio >= 0) ? aOptions.evdoEcio : UNKNOWN_VALUE; + this.evdoSnr = (aOptions.evdoSnr !== undefined && + aOptions.evdoSnr >= 0 && aOptions.evdoSnr <= 8) ? + aOptions.evdoSnr : UNKNOWN_VALUE; +} CdmaCellInfo.prototype = { __proto__: CellInfo.prototype, QueryInterface: XPCOMUtils.generateQI([Ci.nsICellInfo, @@ -277,18 +439,43 @@ CdmaCellInfo.prototype = { interfaces: [Ci.nsICdmaCellInfo] }), + isValid: function() { + return !(this.networkId == UNKNOWN_VALUE && this.systemId == UNKNOWN_VALUE && + this.baseStationId == UNKNOWN_VALUE && + this.longitude == UNKNOWN_VALUE && + this.latitude == UNKNOWN_VALUE && this.cdmaDbm == UNKNOWN_VALUE && + this.cdmaEcio == UNKNOWN_VALUE && this.evdoDbm == UNKNOWN_VALUE && + this.evdoEcio == UNKNOWN_VALUE && this.evdoSnr == UNKNOWN_VALUE); + }, + // nsICdmaCellInfo - networkId: INT32_MAX, - systemId: INT32_MAX, - baseStationId: INT32_MAX, - longitude: INT32_MAX, - latitude: INT32_MAX, - cdmaDbm: INT32_MAX, - cdmaEcio: INT32_MAX, - evdoDbm: INT32_MAX, - evdoEcio: INT32_MAX, - evdoSnr: INT32_MAX + networkId: UNKNOWN_VALUE, + systemId: UNKNOWN_VALUE, + baseStationId: UNKNOWN_VALUE, + longitude: UNKNOWN_VALUE, + latitude: UNKNOWN_VALUE, + cdmaDbm: UNKNOWN_VALUE, + cdmaEcio: UNKNOWN_VALUE, + evdoDbm: UNKNOWN_VALUE, + evdoEcio: UNKNOWN_VALUE, + evdoSnr: UNKNOWN_VALUE +}; + +function MobileDeviceIdentities(aImei, aImeisv, aEsn, aMeid) { + this.imei = aImei; + this.imeisv = aImeisv; + this.esn = aEsn; + this.meid = aMeid; +} +MobileDeviceIdentities.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileDeviceIdentities]), + + // nsIMobileDeviceIdentities + imei: null, + imeisv: null, + esn: null, + meid: null }; function MobileConnectionProvider(aClientId, aRadioInterface) { @@ -330,6 +517,7 @@ MobileConnectionProvider.prototype = { lastKnownNetwork: null, lastKnownHomeNetwork: null, supportedNetworkTypes: null, + deviceIdentities: null, /** * A utility function to dump debug message. @@ -790,6 +978,17 @@ MobileConnectionProvider.prototype = { aServiceClass]); }, + notifyDeviceIdentitiesChanged: function(aImei, aImeisv, aEsn, aMeid) { + if (this.deviceIdentities) { + if (DEBUG) this._debug("deviceIdentities shall not be changed once being updated."); + return; + } + + this.deviceIdentities = + new MobileDeviceIdentities(aImei, aImeisv, aEsn, aMeid); + this.deliverListenerEvent("notifyDeviceIdentitiesChanged"); + }, + getSupportedNetworkTypes: function(aTypes) { aTypes.value = this.supportedNetworkTypes.slice(); return aTypes.value.length; @@ -1054,9 +1253,13 @@ MobileConnectionProvider.prototype = { }).bind(this)); }, - setCallWaiting: function(aEnabled, aCallback) { - this._radioInterface.sendWorkerMessage("setCallWaiting", - {enabled: aEnabled}, + setCallWaiting: function(aEnabled, aServiceClass, aCallback) { + let options = { + enabled: aEnabled, + serviceClass: aServiceClass + }; + + this._radioInterface.sendWorkerMessage("setCallWaiting", options, (function(aResponse) { if (aResponse.errorMsg) { aCallback.notifyError(aResponse.errorMsg); @@ -1076,7 +1279,7 @@ MobileConnectionProvider.prototype = { return false; } - aCallback.notifySuccessWithBoolean(aResponse.enabled); + aCallback.notifyGetCallWaitingSuccess(aResponse.serviceClass); return false; }).bind(this)); }, @@ -1176,26 +1379,26 @@ MobileConnectionProvider.prototype = { let cellInfo; switch (srcCellInfo.type) { case RIL.CELL_INFO_TYPE_GSM: - cellInfo = new GsmCellInfo(); + cellInfo = new GsmCellInfo(srcCellInfo); break; case RIL.CELL_INFO_TYPE_WCDMA: - cellInfo = new WcdmaCellInfo(); + cellInfo = new WcdmaCellInfo(srcCellInfo); break; case RIL.CELL_INFO_TYPE_LTE: - cellInfo = new LteCellInfo(); + cellInfo = new LteCellInfo(srcCellInfo); break; case RIL.CELL_INFO_TYPE_CDMA: - cellInfo = new CdmaCellInfo(); + cellInfo = new CdmaCellInfo(srcCellInfo); break; } - if (!cellInfo) { + if (!cellInfo || !cellInfo.isValid()) { continue; } - this._updateInfo(cellInfo, srcCellInfo); + cellInfoList.push(cellInfo); } - aCallback.notifyGetCellInfoList(count, cellInfoList); + aCallback.notifyGetCellInfoList(cellInfoList.length, cellInfoList); }.bind(this)); }, @@ -1212,11 +1415,13 @@ MobileConnectionProvider.prototype = { let count = aResponse.result.length; for (let i = 0; i < count; i++) { let srcCellInfo = aResponse.result[i]; - let cellInfo = new NeighboringCellInfo(); - this._updateInfo(cellInfo, srcCellInfo); - neighboringCellIds.push(cellInfo); + let cellInfo = new NeighboringCellInfo(srcCellInfo); + if (cellInfo && cellInfo.isValid()) { + neighboringCellIds.push(cellInfo); + } } - aCallback.notifyGetNeighboringCellIds(count, neighboringCellIds); + aCallback.notifyGetNeighboringCellIds(neighboringCellIds.length, + neighboringCellIds); }.bind(this)); }, @@ -1516,6 +1721,12 @@ MobileConnectionService.prototype = { .notifyCdmaInfoRecAudioControl(aClientId, aUpLink, aDownLink); }, + notifyDeviceIdentitiesChanged: function(aClientId, aImei, aImeisv, + aEsn, aMeid) { + this.getItemByServiceId(aClientId) + .notifyDeviceIdentitiesChanged(aImei, aImeisv, aEsn, aMeid); + }, + /** * nsIObserver interface. */ diff --git a/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl b/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl index 3fc864a520..9fe8344d93 100644 --- a/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl +++ b/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl @@ -9,7 +9,7 @@ "@mozilla.org/mobileconnection/gonkmobileconnectionservice;1" %} -[scriptable, uuid(3a7b8d47-d1c6-44c3-a312-df73fda1161e)] +[scriptable, uuid(3c306f88-86bf-11e5-91af-3b2233acec65)] interface nsIGonkMobileConnectionService : nsIMobileConnectionService { void notifyNetworkInfoChanged(in unsigned long clientId, in jsval networkInfo); @@ -248,4 +248,27 @@ interface nsIGonkMobileConnectionService : nsIMobileConnectionService void notifyCdmaInfoRecAudioControl(in unsigned long clientId, in short upLink, in short downLink); + + /** + * Notify Device Identities. + * + * @param aClientId + * The ID of radioInterface where this info is notified from. + * @param aImei + * Device IMEI, valid if GSM subscription is available. + * @param aImeisv + * Device IMEISV, valid if GSM subscription is available. + * @param aEsn + * Device ESN, valid if CDMA subscription is available. + * @param aMeid + * Device MEID, valid if CDMA subscription is available. + * + * Note: The value might be dummy like "000..." from modem + * if the corresponding subscription is not available. + */ + void notifyDeviceIdentitiesChanged(in unsigned long aClientId, + in DOMString aImei, + in DOMString aImeisv, + in DOMString aEsn, + in DOMString aMeid); }; diff --git a/dom/mobileconnection/interfaces/nsICellInfo.idl b/dom/mobileconnection/interfaces/nsICellInfo.idl index f7d4a7401a..b560202753 100644 --- a/dom/mobileconnection/interfaces/nsICellInfo.idl +++ b/dom/mobileconnection/interfaces/nsICellInfo.idl @@ -22,20 +22,22 @@ interface nsICellInfoListCallback : nsISupports void notifyGetCellInfoListFailed(in DOMString error); }; -[scriptable, uuid(86667898-c9ab-44ee-8a9a-026916b3183e)] +[scriptable, uuid(a9a34341-5a33-4e0a-98e1-13e7ea4228be)] interface nsICellInfo : nsISupports { - const long CELL_INFO_TYPE_GSM = 1; - const long CELL_INFO_TYPE_CDMA = 2; - const long CELL_INFO_TYPE_LTE = 3; + const long CELL_INFO_TYPE_GSM = 1; + const long CELL_INFO_TYPE_CDMA = 2; + const long CELL_INFO_TYPE_LTE = 3; const long CELL_INFO_TYPE_WCDMA = 4; - const long TIMESTAMP_TYPE_UNKNOWN = 0; - const long TIMESTAMP_TYPE_ANTENNA = 1; - const long TIMESTAMP_TYPE_MODEM = 2; - const long TIMESTAMP_TYPE_OEM_RIL = 3; + const long TIMESTAMP_TYPE_UNKNOWN = 0; + const long TIMESTAMP_TYPE_ANTENNA = 1; + const long TIMESTAMP_TYPE_MODEM = 2; + const long TIMESTAMP_TYPE_OEM_RIL = 3; const long TIMESTAMP_TYPE_JAVA_RIL = 4; + const long UNKNOWN_VALUE = 0x7FFFFFFF; + /** * Network type. One of the CELL_INFO_TYPE_* constants. */ @@ -57,204 +59,207 @@ interface nsICellInfo : nsISupports readonly attribute long long timestamp; }; -[scriptable, uuid(6345967c-61fc-45a1-8362-39e9261df052)] +[scriptable, uuid(cc476ded-350f-4c25-9a57-6a876e32f092)] interface nsIGsmCellInfo : nsICellInfo { /** - * 3-digit Mobile Country Code, 0..999, INT_MAX if unknown. + * 3-digit Mobile Country Code, 0..999, UNKNOWN_VALUE if unknown. */ readonly attribute long mcc; /** - * 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown. + * 2 or 3-digit Mobile Network Code, 0..999, UNKNOWN_VALUE if unknown. */ readonly attribute long mnc; /** - * 16-bit Location Area Code, 0..65535, INT_MAX if unknown. + * 16-bit Location Area Code, 0..65535, UNKNOWN_VALUE if unknown. */ readonly attribute long lac; /** - * 16-bit GSM Cell Identity described in TS 27.007, 0..65535, INT_MAX if unknown. + * 16-bit GSM Cell Identity described in TS 27.007, 0..65535, + * UNKNOWN_VALUE if unknown. */ readonly attribute long cid; /** - * Valid values are 0-31 as defined in TS 27.007 8.5, 99 if unknown. + * Valid values are 0-31 as defined in TS 27.007 8.5, UNKNOWN_VALUE if unknown. */ readonly attribute long signalStrength; /** - * Bit error rate 0-7 as defined in TS 27.007 8.5, 99 if unknown. + * Bit error rate 0-7 as defined in TS 27.007 8.5, UNKNOWN_VALUE if unknown. */ readonly attribute long bitErrorRate; }; -[scriptable, uuid(19693f98-943d-45e7-a3e8-25373228ce6b)] +[scriptable, uuid(aa52647b-38dd-487c-be36-b46ed2e99554)] interface nsIWcdmaCellInfo : nsICellInfo { /** - * 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown. + * 2 or 3-digit Mobile Network Code, 0..999, UNKNOWN_VALUE if unknown. */ readonly attribute long mcc; /** - * 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown. + * 2 or 3-digit Mobile Network Code, 0..999, UNKNOWN_VALUE if unknown. */ readonly attribute long mnc; /** - * 16-bit Location Area Code, 0..65535, INT_MAX if unknown. + * 16-bit Location Area Code, 0..65535, UNKNOWN_VALUE if unknown. */ readonly attribute long lac; /** * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, - * INT_MAX if unknown. + * UNKNOWN_VALUE if unknown. */ readonly attribute long cid; /** * 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511, - * INT_MAX if unknown. + * UNKNOWN_VALUE if unknown. */ readonly attribute long psc; /** - * Valid values are 0-31 as defined in TS 27.007 8.5, 99 if unknown. + * Valid values are 0-31 as defined in TS 27.007 8.5, UNKNOWN_VALUE if unknown. */ readonly attribute long signalStrength; /** - * Bit error rate 0-7 as defined in TS 27.007 8.5, 99 if unknown. + * Bit error rate 0-7 as defined in TS 27.007 8.5, UNKNOWN_VALUE if unknown. */ readonly attribute long bitErrorRate; }; -[scriptable, uuid(76b4a35d-7e45-42bc-a2e0-bc07a6434db3)] +[scriptable, uuid(60a38ca7-ca62-4384-aa07-eac7d4893786)] interface nsICdmaCellInfo : nsICellInfo { /** - * Network Id, 0..65535, INT_MAX if unknown. + * Network Id, 0..65535, UNKNOWN_VALUE if unknown. */ readonly attribute long networkId; /** - * CDMA System Id, 0..32767, INT_MAX if unknown. + * CDMA System Id, 0..32767, UNKNOWN_VALUE if unknown. */ readonly attribute long systemId; /** - * Base Station Id, 0..65535, INT_MAX if unknown. + * Base Station Id, 0..65535, UNKNOWN_VALUE if unknown. */ readonly attribute long baseStationId; /** * Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. * It is represented in units of 0.25 seconds and ranges from -2592000 to - * 2592000, INT_MAX if unknown. + * 2592000, UNKNOWN_VALUE if unknown. */ readonly attribute long longitude; /** * Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0. * It is represented in units of 0.25 seconds and ranges from -1296000 to - * 1296000, INT_MAX if unknown. + * 1296000, UNKNOWN_VALUE if unknown. */ readonly attribute long latitude; /** - * Valid values are positive integers, INT_MAX if unknown. This value is the - * actual RSSI value multiplied by -1. + * Valid values are positive integers, UNKNOWN_VALUE if unknown. This value is + * the actual RSSI value multiplied by -1. */ readonly attribute long cdmaDbm; /** - * Valid values are positive integers, INT_MAX if unknown. This value is the - * actual Ec/Io multiplied by -10. -1 if unknown. + * Valid values are positive integers, UNKNOWN_VALUE if unknown. This value is + * the actual Ec/Io multiplied by -10. */ readonly attribute long cdmaEcio; /** - * Valid values are positive integers, INT_MAX if unknown. This value is the - * actual Evdo RSSI value multiplied by -1. + * Valid values are positive integers, UNKNOWN_VALUE if unknown. This value is + * the actual Evdo RSSI value multiplied by -1. */ readonly attribute long evdoDbm; /** - * Valid values are positive integers, INT_MAX if unknown. This value is the - * actual Evdo Ec/Io multiplied by -10. + * Valid values are positive integers, UNKNOWN_VALUE if unknown. This value is + * the actual Evdo Ec/Io multiplied by -10. */ readonly attribute long evdoEcio; /** - * Valid values are 0-8, INT_MAX if unknown. 8 is the highest signal to noise - * ratio. + * Valid values are 0-8, UNKNOWN_VALUE if unknown. 8 is the highest signal to + * noise ratio. */ readonly attribute long evdoSnr; }; -[scriptable, uuid(122937d9-1ee5-45e0-a360-5959d578bc31)] +[scriptable, uuid(e7b3e826-bb85-49b2-a2f1-70af46c47733)] interface nsILteCellInfo : nsICellInfo { /** - * 3-digit Mobile Country Code, 0..999, INT_MAX if unknown. + * 3-digit Mobile Country Code, 0..999, UNKNOWN_VALUE if unknown. */ readonly attribute long mcc; /** - * 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown. + * 2 or 3-digit Mobile Network Code, 0..999, UNKNOWN_VALUE if unknown. */ readonly attribute long mnc; /** - * 28-bit Cell Identity, 0..268435455, INT_MAX if unknown. + * 28-bit Cell Identity, 0..268435455, UNKNOWN_VALUE if unknown. */ readonly attribute long cid; /** - * Physical cell id, 0..503, INT_MAX if unknown. + * Physical cell id, 0..503, UNKNOWN_VALUE if unknown. */ readonly attribute long pcid; /** - * 16-bit tracking area code, 0..65535, INT_MAX if unknown. + * 16-bit tracking area code, 0..65535, UNKNOWN_VALUE if unknown. */ readonly attribute long tac; /** - * Valid values are 0-31 as defined in TS 27.007 8.5, 99 if unknown. + * Valid values are 0-31 as defined in TS 27.007 8.5, UNKNOWN_VALUE if unknown. */ readonly attribute long signalStrength; /** * The current Reference Signal Receive Power in dBm multipled by -1. - * Range: 44 to 140 dBm, INT_MAX if unknown. + * Range: 44 to 140 dBm, UNKNOWN_VALUE if unknown. */ readonly attribute long rsrp; /** * The current Reference Signal Receive Quality in dB multiplied by -1. - * Range: 3 to 20 dB, INT_MAX if unknown. + * Range: 3 to 20 dB, UNKNOWN_VALUE if unknown. */ readonly attribute long rsrq; /** * The current reference signal signal-to-noise ratio in 0.1 dB units. - * Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB), INT_MAX if unknown. + * Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB), UNKNOWN_VALUE if + * unknown. */ readonly attribute long rssnr; /** - * The current Channel Quality Indicator. Range: 0 to 15, INT_MAX if unknown. + * The current Channel Quality Indicator. Range: 0 to 15, UNKNOWN_VALUE if + * unknown. */ readonly attribute long cqi; /** * Timing advance in micro seconds for a one way trip from cell to device. * Approximate distance can be calculated using 300m/us * timingAdvance. - * Range: 0 to 0x7FFFFFFE, INT_MAX if unknown. + * Range: 0 to 0x7FFFFFFE, UNKNOWN_VALUE if unknown. */ readonly attribute long timingAdvance; }; \ No newline at end of file diff --git a/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl b/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl index c92e9ec057..c4bccbc87e 100644 --- a/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl +++ b/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl @@ -8,11 +8,12 @@ interface nsICellInfoListCallback; interface nsIMobileCallForwardingOptions; interface nsIMobileConnection; interface nsIMobileConnectionInfo; +interface nsIMobileDeviceIdentities; interface nsIMobileNetworkInfo; interface nsINeighboringCellIdsCallback; interface nsIVariant; -[scriptable, uuid(d6827b51-61a7-4b7c-8454-42d0cffc1829)] +[scriptable, uuid(8884b326-891c-11e5-a434-67def07c4a41)] interface nsIMobileConnectionListener : nsISupports { /** @@ -102,13 +103,18 @@ interface nsIMobileConnectionListener : nsISupports * Notify when network selection mode is changed. */ void notifyNetworkSelectionModeChanged(); + + /** + * Notify when device identities are changed. + */ + void notifyDeviceIdentitiesChanged(); }; %{C++ #define NO_ADDITIONAL_INFORMATION 0 %} -[scriptable, uuid(14d66926-8434-11e4-8c3f-f724194bb5f1)] +[scriptable, uuid(ef5e02a6-adff-4425-8634-ec49ced1f14f)] interface nsIMobileConnectionCallback : nsISupports { /** @@ -128,6 +134,8 @@ interface nsIMobileConnectionCallback : nsISupports in boolean enabled, in unsigned short serviceClass); + void notifyGetCallWaitingSuccess(in unsigned short serviceClass); + void notifyGetClirStatusSuccess(in unsigned short n, in unsigned short m); void notifyGetPreferredNetworkTypeSuccess(in long type); @@ -163,7 +171,7 @@ already_AddRefed NS_CreateMobileConnectionService(); %} -[scriptable, uuid(b9845f09-7cbb-46d0-b713-773d80844e0d)] +[scriptable, uuid(7a557116-8753-11e5-9f9b-6794b577c0a1)] interface nsIMobileConnection : nsISupports { /* @@ -214,6 +222,9 @@ interface nsIMobileConnection : nsISupports const long CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME = 2; const long CALL_BARRING_PROGRAM_ALL_INCOMING = 3; const long CALL_BARRING_PROGRAM_INCOMING_ROAMING = 4; + const long CALL_BARRING_PROGRAM_ALL_SERVICE = 5; + const long CALL_BARRING_PROGRAM_OUTGOING_SERVICE = 6; + const long CALL_BARRING_PROGRAM_INCOMING_SERVICE = 7; /** * Calling line identification restriction constants. @@ -315,6 +326,11 @@ interface nsIMobileConnection : nsISupports */ readonly attribute long radioState; + /** + * Device Identities, including IMEI, IMEISV, ESN and MEID. + */ + readonly attribute nsIMobileDeviceIdentities deviceIdentities; + /** * The network types supported by this radio. * @@ -598,6 +614,8 @@ interface nsIMobileConnection : nsISupports * * @param enabled * Boolean indicates the desired call waiting status. + * @param serviceClass + * One of the nsIMobileConnection.ICC_SERVICE_CLASS_* values. * @param requestCallback * Called when request is finished. * @@ -608,6 +626,7 @@ interface nsIMobileConnection : nsISupports * 'GenericFailure'. */ void setCallWaiting(in bool enabled, + in unsigned short serviceClass, in nsIMobileConnectionCallback requestCallback); /** @@ -616,8 +635,12 @@ interface nsIMobileConnection : nsISupports * @param requestCallback * Called when request is finished. * - * If successful, the notifySuccessWithBoolean() will be called. And the result - * will be a boolean indicating the call waiting status. + * If successful, the notifyGetCallWaitingSuccess() will be called. And the + * result will be a service class bit vector of services for which call + * waiting is enabled. e.g. 3 means call waiting is enabled for data + * and voice and disabled for everything else. 0 means call waiting is + * disabled for all service. + * @see nsIMobileConnection.ICC_SERVICE_CLASS_*. * * Otherwise, the notifyError() will be called, and the error will be either * 'RadioNotAvailable', 'RequestNotSupported', 'IllegalSIMorME', or diff --git a/dom/mobileconnection/interfaces/nsIMobileDeviceIdentities.idl b/dom/mobileconnection/interfaces/nsIMobileDeviceIdentities.idl new file mode 100644 index 0000000000..d9b40812b6 --- /dev/null +++ b/dom/mobileconnection/interfaces/nsIMobileDeviceIdentities.idl @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(3fc79ece-8399-11e5-beff-6b8209cb93f6)] +interface nsIMobileDeviceIdentities : nsISupports +{ + /** + * Device IMEI, including check digit. + * + * Valid if GSM subscription is available. + * + * Note: The value might be dummy like "000..." from modem if invalid. + */ + readonly attribute DOMString imei; + + /** + * Device IMEISV. + * + * Valid if GSM subscription is available. + * + * Note: IMEISV is presented in 2-decimal digits. + * The value might be dummy like "00" from modem if invalid. + */ + readonly attribute DOMString imeisv; + + /** + * Device ESN. + * + * Valid if CDMA subscription is available. + * + * Note: The value might be dummy like "000..." from modem if invalid. + */ + readonly attribute DOMString esn; + + /** + * Device MEID. + * + * Valid if CDMA subscription is available. + * + * Note: The value might be dummy like "000..." from modem if invalid. + */ + readonly attribute DOMString meid; +}; diff --git a/dom/mobileconnection/interfaces/nsINeighboringCellInfo.idl b/dom/mobileconnection/interfaces/nsINeighboringCellInfo.idl index 3d42d4b968..4c1d492d71 100644 --- a/dom/mobileconnection/interfaces/nsINeighboringCellInfo.idl +++ b/dom/mobileconnection/interfaces/nsINeighboringCellInfo.idl @@ -36,15 +36,15 @@ interface nsINeighboringCellInfo: nsISupports * Mobile Location Area Code (LAC) for GSM networks. * * Possible ranges from 0x0000 to 0xffff. - * -1 if the LAC is unknown. + * nsICellInfo.UNKNOWN_VALUE if the LAC is unknown. */ readonly attribute long gsmLocationAreaCode; /** * Mobile Cell ID for GSM networks. * - * Possible ranges from 0x00000000 to 0xffffffff. - * -1 if the cell id is unknown. + * Possible ranges from 0x0000 to 0xffff. + * nsICellInfo.UNKNOWN_VALUE if the cell id is unknown. */ readonly attribute long long gsmCellId; @@ -52,7 +52,7 @@ interface nsINeighboringCellInfo: nsISupports * Primary Scrambling Code (PSC) for WCDMA networks. * * Possible ranges from 0x0000 to 0x01ff. - * -1 if the psc is unknown. + * nsICellInfo.UNKNOWN_VALUE if the psc is unknown. */ readonly attribute long wcdmaPsc; @@ -61,7 +61,7 @@ interface nsINeighboringCellInfo: nsISupports * For WCDMA networks, signalStrength is the CPICH Received Signal Code Power, * ranging from -120 to -25. * - * 99 if signalStrength is unknown. + * nsICellInfo.UNKNOWN_VALUE if signalStrength is unknown. */ readonly attribute long signalStrength; }; \ No newline at end of file diff --git a/dom/mobileconnection/ipc/MobileConnectionChild.cpp b/dom/mobileconnection/ipc/MobileConnectionChild.cpp index 2a3a994677..a41ccdbdd8 100644 --- a/dom/mobileconnection/ipc/MobileConnectionChild.cpp +++ b/dom/mobileconnection/ipc/MobileConnectionChild.cpp @@ -107,6 +107,12 @@ MobileConnectionChild::GetRadioState(int32_t* aRadioState) return NS_OK; } +NS_IMETHODIMP +MobileConnectionChild::GetDeviceIdentities(nsIMobileDeviceIdentities** aIdentities) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP MobileConnectionChild::GetSupportedNetworkTypes(int32_t** aTypes, uint32_t* aLength) @@ -276,9 +282,10 @@ MobileConnectionChild::ChangeCallBarringPassword(const nsAString& aPin, NS_IMETHODIMP MobileConnectionChild::SetCallWaiting(bool aEnabled, + uint16_t aServiceClass, nsIMobileConnectionCallback* aCallback) { - return SendRequest(SetCallWaitingRequest(aEnabled), aCallback) + return SendRequest(SetCallWaitingRequest(aEnabled, aServiceClass), aCallback) ? NS_OK : NS_ERROR_FAILURE; } @@ -550,6 +557,12 @@ MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessCallBarr aReply.serviceClass())); } +bool +MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessCallWaiting& aReply) +{ + return NS_SUCCEEDED(mRequestCallback->NotifyGetCallWaitingSuccess(aReply.serviceClass())); +} + bool MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessClirStatus& aReply) { @@ -591,6 +604,8 @@ MobileConnectionRequestChild::Recv__delete__(const MobileConnectionReply& aReply return DoReply(aReply.get_MobileConnectionReplySuccessCallForwarding()); case MobileConnectionReply::TMobileConnectionReplySuccessCallBarring: return DoReply(aReply.get_MobileConnectionReplySuccessCallBarring()); + case MobileConnectionReply::TMobileConnectionReplySuccessCallWaiting: + return DoReply(aReply.get_MobileConnectionReplySuccessCallWaiting()); case MobileConnectionReply::TMobileConnectionReplySuccessClirStatus: return DoReply(aReply.get_MobileConnectionReplySuccessClirStatus()); case MobileConnectionReply::TMobileConnectionReplySuccessPreferredNetworkType: diff --git a/dom/mobileconnection/ipc/MobileConnectionChild.h b/dom/mobileconnection/ipc/MobileConnectionChild.h index 218c462abd..31f7a5aa1c 100644 --- a/dom/mobileconnection/ipc/MobileConnectionChild.h +++ b/dom/mobileconnection/ipc/MobileConnectionChild.h @@ -148,6 +148,9 @@ public: bool DoReply(const MobileConnectionReplySuccessCallBarring& aReply); + bool + DoReply(const MobileConnectionReplySuccessCallWaiting& aReply); + bool DoReply(const MobileConnectionReplySuccessClirStatus& aReply); diff --git a/dom/mobileconnection/ipc/MobileConnectionParent.cpp b/dom/mobileconnection/ipc/MobileConnectionParent.cpp index b31a9f2462..d5824182db 100644 --- a/dom/mobileconnection/ipc/MobileConnectionParent.cpp +++ b/dom/mobileconnection/ipc/MobileConnectionParent.cpp @@ -288,6 +288,13 @@ MobileConnectionParent::NotifyNetworkSelectionModeChanged() return SendNotifyNetworkSelectionModeChanged(mode) ? NS_OK : NS_ERROR_FAILURE; } +NS_IMETHODIMP +MobileConnectionParent::NotifyDeviceIdentitiesChanged() +{ + // To be supported when bug 1222870 is required in m-c. + return NS_OK; +} + /****************************************************************************** * PMobileConnectionRequestParent ******************************************************************************/ @@ -434,7 +441,9 @@ MobileConnectionRequestParent::DoRequest(const SetCallWaitingRequest& aRequest) { NS_ENSURE_TRUE(mMobileConnection, false); - return NS_SUCCEEDED(mMobileConnection->SetCallWaiting(aRequest.enabled(), this)); + return NS_SUCCEEDED(mMobileConnection->SetCallWaiting(aRequest.enabled(), + aRequest.serviceClass(), + this)); } bool @@ -536,6 +545,12 @@ MobileConnectionRequestParent::NotifyGetCallBarringSuccess(uint16_t aProgram, aServiceClass)); } +NS_IMETHODIMP +MobileConnectionRequestParent::NotifyGetCallWaitingSuccess(uint16_t aServiceClass) +{ + return SendReply(MobileConnectionReplySuccessCallWaiting(aServiceClass)); +} + NS_IMETHODIMP MobileConnectionRequestParent::NotifyGetClirStatusSuccess(uint16_t aN, uint16_t aM) diff --git a/dom/mobileconnection/ipc/PMobileConnection.ipdl b/dom/mobileconnection/ipc/PMobileConnection.ipdl index 81dcdf4296..ceaa9ad371 100644 --- a/dom/mobileconnection/ipc/PMobileConnection.ipdl +++ b/dom/mobileconnection/ipc/PMobileConnection.ipdl @@ -133,6 +133,7 @@ struct ChangeCallBarringPasswordRequest struct SetCallWaitingRequest { bool enabled; + uint16_t serviceClass; }; struct GetCallWaitingRequest diff --git a/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl b/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl index ce7ed401bf..73c0be9c99 100644 --- a/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl +++ b/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl @@ -52,6 +52,11 @@ struct MobileConnectionReplySuccessCallBarring uint16_t serviceClass; }; +struct MobileConnectionReplySuccessCallWaiting +{ + uint16_t serviceClass; +}; + struct MobileConnectionReplySuccessClirStatus { uint16_t n; @@ -82,6 +87,7 @@ union MobileConnectionReply MobileConnectionReplySuccessNetworks; MobileConnectionReplySuccessCallForwarding; MobileConnectionReplySuccessCallBarring; + MobileConnectionReplySuccessCallWaiting; MobileConnectionReplySuccessClirStatus; MobileConnectionReplySuccessPreferredNetworkType; MobileConnectionReplySuccessRoamingPreference; diff --git a/dom/mobileconnection/moz.build b/dom/mobileconnection/moz.build index 7ce76871f8..e541fb5286 100644 --- a/dom/mobileconnection/moz.build +++ b/dom/mobileconnection/moz.build @@ -27,6 +27,7 @@ XPIDL_SOURCES += [ 'interfaces/nsIMobileCellInfo.idl', 'interfaces/nsIMobileConnectionInfo.idl', 'interfaces/nsIMobileConnectionService.idl', + 'interfaces/nsIMobileDeviceIdentities.idl', 'interfaces/nsIMobileNetworkInfo.idl', 'interfaces/nsINeighboringCellInfo.idl', ] diff --git a/dom/mobileconnection/tests/marionette/head.js b/dom/mobileconnection/tests/marionette/head.js index 1952b8ed1f..afc92bae1b 100644 --- a/dom/mobileconnection/tests/marionette/head.js +++ b/dom/mobileconnection/tests/marionette/head.js @@ -640,6 +640,37 @@ function selectNetworkAutomaticallyAndWait() { return request.then(null, () => { throw request.error }); } +/** + * Configures call waiting options. + * + * Fulfill params: (none) + * Reject params: + * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter' or + * 'GenericFailure'. + * + * @return A deferred promise. + */ + function setCallWaitingOption(aEnabled) { + let request = mobileConnection.setCallWaitingOption(aEnabled); + return request.then(null, () => { throw request.error }); +} + +/** + * Queries current call waiting status. + * + * Fulfill params: + * A boolean indicating the call waiting status. + * Reject params: + * 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter' or + * 'GenericFailure'. + * + * @return A deferred promise. + */ + function getCallWaitingOption() { + let request = mobileConnection.getCallWaitingOption(); + return request.then(() => request.result, () => { throw request.error }); +} + /** * Set data connection enabling state and wait for "datachange" event. * diff --git a/dom/mobileconnection/tests/marionette/manifest.ini b/dom/mobileconnection/tests/marionette/manifest.ini index ba36f1acf2..5879c3ee05 100644 --- a/dom/mobileconnection/tests/marionette/manifest.ini +++ b/dom/mobileconnection/tests/marionette/manifest.ini @@ -15,9 +15,10 @@ qemu = true [test_mobile_data_location.js] [test_mobile_data_state.js] [test_mobile_roaming_preference.js] -[test_call_barring_get_option.js] +[test_call_barring_get_error.js] [test_call_barring_set_error.js] [test_call_barring_change_password.js] +[test_call_waiting.js] [test_mobile_set_radio.js] [test_mobile_last_known_network.js] [test_mobile_icc_change.js] diff --git a/dom/mobileconnection/tests/marionette/test_call_barring_get_option.js b/dom/mobileconnection/tests/marionette/test_call_barring_get_error.js similarity index 97% rename from dom/mobileconnection/tests/marionette/test_call_barring_get_option.js rename to dom/mobileconnection/tests/marionette/test_call_barring_get_error.js index 7ce5afc7bb..5b6eb2c46b 100644 --- a/dom/mobileconnection/tests/marionette/test_call_barring_get_option.js +++ b/dom/mobileconnection/tests/marionette/test_call_barring_get_error.js @@ -1,5 +1,5 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 60000; MARIONETTE_HEAD_JS = "head.js"; diff --git a/dom/mobileconnection/tests/marionette/test_call_barring_set_error.js b/dom/mobileconnection/tests/marionette/test_call_barring_set_error.js index d62574461d..33d7cd22c7 100644 --- a/dom/mobileconnection/tests/marionette/test_call_barring_set_error.js +++ b/dom/mobileconnection/tests/marionette/test_call_barring_set_error.js @@ -1,5 +1,5 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 60000; MARIONETTE_HEAD_JS = "head.js"; diff --git a/dom/mobileconnection/tests/marionette/test_call_waiting.js b/dom/mobileconnection/tests/marionette/test_call_waiting.js new file mode 100644 index 0000000000..062fe189b6 --- /dev/null +++ b/dom/mobileconnection/tests/marionette/test_call_waiting.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +// Start tests +startTestCommon(function() { + return Promise.resolve() + // TODO: Bug 1090811 - [B2G] support SET_CALL_WAITING and QUERY_CALL_WAITING + // Currently emulator doesn't support RIL_REQUEST_QUERY_CALL_WAITING and + // RIL_REQUEST_SET_CALL_WAITING, so we expect to get a 'RequestNotSupported' + // error here. + .then(() => setCallWaitingOption(true)) + .then(() => { + ok(false, "setCallWaitingOption should not success"); + }, aError => { + is(aError.name, "RequestNotSupported", + "failed to setCallWaitingOption"); + }) + + .then(() => getCallWaitingOption()) + .then(() => { + ok(false, "getCallWaitingOption should not success"); + }, aError => { + is(aError.name, "RequestNotSupported", + "failed to getCallWaitingOption"); + }); +}); diff --git a/dom/mobilemessage/gonk/MmsPduHelper.jsm b/dom/mobilemessage/gonk/MmsPduHelper.jsm index ba27c9c827..420144380f 100644 --- a/dom/mobilemessage/gonk/MmsPduHelper.jsm +++ b/dom/mobilemessage/gonk/MmsPduHelper.jsm @@ -6,14 +6,14 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -let WSP = {}; +var WSP = {}; Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP); Cu.import("resource://gre/modules/mms_consts.js"); Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); -let DEBUG; // set to true to see debug messages +var DEBUG; // set to true to see debug messages this.MMS_VERSION = (function() { Cu.import("resource://gre/modules/Services.jsm"); @@ -1660,7 +1660,7 @@ const MMS_WELL_KNOWN_PARAMS = (function() { return params; })(); -let debug; +var debug; if (DEBUG) { debug = function(s) { dump("-$- MmsPduHelper: " + s + "\n"); diff --git a/dom/mobilemessage/gonk/MmsService.js b/dom/mobilemessage/gonk/MmsService.js index 6183103fa5..b9386c7de4 100644 --- a/dom/mobilemessage/gonk/MmsService.js +++ b/dom/mobilemessage/gonk/MmsService.js @@ -17,7 +17,7 @@ Cu.import("resource://gre/modules/Promise.jsm"); const GONK_MMSSERVICE_CONTRACTID = "@mozilla.org/mms/gonkmmsservice;1"; const GONK_MMSSERVICE_CID = Components.ID("{9b069b8c-8697-11e4-a406-474f5190272b}"); -let DEBUG = false; +var DEBUG = false; function debug(s) { dump("-@- MmsService: " + s + "\n"); }; @@ -1072,8 +1072,9 @@ CancellableTransaction.prototype = { break; } case kSmsDeletedObserverTopic: { - if (subject && subject.deletedMessageIds && - subject.deletedMessageIds.indexOf(this.cancellableId) >= 0) { + let deletedInfo = subject.QueryInterface(Ci.nsIDeletedMessageInfo); + if (deletedInfo && deletedInfo.deletedMessageIds && + deletedInfo.deletedMessageIds.indexOf(this.cancellableId) >= 0) { this.cancelRunning(_MMS_ERROR_MESSAGE_DELETED); } break; diff --git a/dom/mobilemessage/gonk/MobileMessageDatabaseService.js b/dom/mobilemessage/gonk/MobileMessageDatabaseService.js index a88cdb747e..16288aed75 100644 --- a/dom/mobilemessage/gonk/MobileMessageDatabaseService.js +++ b/dom/mobilemessage/gonk/MobileMessageDatabaseService.js @@ -9,7 +9,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -let MMDB = {}; +var MMDB = {}; Cu.import("resource://gre/modules/MobileMessageDB.jsm", MMDB); const GONK_MOBILEMESSAGEDATABASESERVICE_CONTRACTID = diff --git a/dom/mobilemessage/gonk/SmsSegmentHelper.jsm b/dom/mobilemessage/gonk/SmsSegmentHelper.jsm index ba5e0a4fd1..757b106e7d 100644 --- a/dom/mobilemessage/gonk/SmsSegmentHelper.jsm +++ b/dom/mobilemessage/gonk/SmsSegmentHelper.jsm @@ -6,7 +6,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -let RIL = {}; +var RIL = {}; Cu.import("resource://gre/modules/ril_consts.js", RIL); /** @@ -122,10 +122,9 @@ this.SmsSegmentHelper = { * * |enabledGsmTableTuples|: * List of tuples of national language identifier pairs. - * TODO: Support static/runtime settings, see bug 733331. * |segmentRef16Bit|: * Use 16-bit reference number for concatenated outgoint messages. - * TODO: Support static/runtime settings, see bug 733331. + * TODO: Support static/runtime settings, see bug 1019443. */ enabledGsmTableTuples: [ [RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT], @@ -162,8 +161,8 @@ this.SmsSegmentHelper = { if ((bodySeptets + headerSeptets) > segmentSeptets) { headerLen += this.segmentRef16Bit ? 6 : 5; headerSeptets = Math.ceil((headerLen + 1) * 8 / 7); - segmentSeptets -= headerSeptets; } + segmentSeptets -= headerSeptets; let segments = Math.ceil(bodySeptets / segmentSeptets); let userDataSeptets = bodySeptets + headerSeptets * segments; @@ -423,4 +422,4 @@ this.SmsSegmentHelper = { } }; -this.EXPORTED_SYMBOLS = [ 'SmsSegmentHelper' ]; \ No newline at end of file +this.EXPORTED_SYMBOLS = [ 'SmsSegmentHelper' ]; diff --git a/dom/mobilemessage/gonk/SmsService.js b/dom/mobilemessage/gonk/SmsService.js index 4005bc2a8e..be39f4e680 100644 --- a/dom/mobilemessage/gonk/SmsService.js +++ b/dom/mobilemessage/gonk/SmsService.js @@ -21,6 +21,7 @@ const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; const kPrefDefaultServiceId = "dom.sms.defaultServiceId"; const kPrefRilDebuggingEnabled = "ril.debugging.enabled"; const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces"; +const kPrefLastKnownSimMcc = "ril.lastKnownSimMcc"; const kDiskSpaceWatcherObserverTopic = "disk-space-watcher"; @@ -56,6 +57,10 @@ XPCOMUtils.defineLazyGetter(this, "gRadioInterfaces", function() { XPCOMUtils.defineLazyGetter(this, "gSmsSegmentHelper", function() { let ns = {}; Cu.import("resource://gre/modules/SmsSegmentHelper.jsm", ns); + + // Initialize enabledGsmTableTuples from current MCC. + ns.SmsSegmentHelper.enabledGsmTableTuples = getEnabledGsmTableTuplesFromMcc(); + return ns.SmsSegmentHelper; }); @@ -71,17 +76,17 @@ XPCOMUtils.defineLazyGetter(this, "gWAP", function() { return ns; }); -XPCOMUtils.defineLazyGetter(this, "gSmsSendingSchedulars", function() { +XPCOMUtils.defineLazyGetter(this, "gSmsSendingSchedulers", function() { return { - _schedulars: [], - getSchedularByServiceId: function(aServiceId) { - let schedular = this._schedulars[aServiceId]; - if (!schedular) { - schedular = this._schedulars[aServiceId] = - new SmsSendingSchedular(aServiceId); + _schedulers: [], + getSchedulerByServiceId: function(aServiceId) { + let scheduler = this._schedulers[aServiceId]; + if (!scheduler) { + scheduler = this._schedulers[aServiceId] = + new SmsSendingScheduler(aServiceId); } - return schedular; + return scheduler; } }; }); @@ -120,6 +125,7 @@ function debug(s) { } function SmsService() { + this._updateDebugFlag(); this._silentNumbers = []; this.smsDefaultServiceId = this._getDefaultServiceId(); @@ -131,6 +137,7 @@ function SmsService() { Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false); Services.prefs.addObserver(kPrefDefaultServiceId, this, false); + Services.prefs.addObserver(kPrefLastKnownSimMcc, this, false); Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); Services.obs.addObserver(this, kDiskSpaceWatcherObserverTopic, false); } @@ -331,11 +338,14 @@ SmsService.prototype = { * Schedule the sending request. */ _scheduleSending: function(aServiceId, aDomMessage, aSilent, aOptions, aRequest) { - gSmsSendingSchedulars.getSchedularByServiceId(aServiceId) + gSmsSendingSchedulers.getSchedulerByServiceId(aServiceId) .schedule({ messageId: aDomMessage.id, onSend: () => { - if (DEBUG) debug("onSend: " + aDomMessage.id); + if (DEBUG) { + debug("onSend: messageId=" + aDomMessage.id + + ", serviceId=" + aServiceId); + } this._sendToTheAir(aServiceId, aDomMessage, aSilent, @@ -1187,6 +1197,10 @@ SmsService.prototype = { else if (aData === kPrefDefaultServiceId) { this.smsDefaultServiceId = this._getDefaultServiceId(); } + else if ( aData === kPrefLastKnownSimMcc) { + gSmsSegmentHelper.enabledGsmTableTuples = + getEnabledGsmTableTuplesFromMcc(); + } break; case kDiskSpaceWatcherObserverTopic: if (DEBUG) { @@ -1206,14 +1220,36 @@ SmsService.prototype = { } }; -function SmsSendingSchedular(aServiceId) { +/** + * Get enabled GSM national language locking shift / single shift table pairs + * for current SIM MCC. + * + * @return a list of pairs of national language identifiers for locking shift + * table and single shfit table, respectively. + */ +function getEnabledGsmTableTuplesFromMcc() { + let mcc; + try { + mcc = Services.prefs.getCharPref(kPrefLastKnownSimMcc); + } catch (e) {} + let tuples = [[RIL.PDU_NL_IDENTIFIER_DEFAULT, + RIL.PDU_NL_IDENTIFIER_DEFAULT]]; + let extraTuples = RIL.PDU_MCC_NL_TABLE_TUPLES_MAPPING[mcc]; + if (extraTuples) { + tuples = tuples.concat(extraTuples); + } + + return tuples; +}; + +function SmsSendingScheduler(aServiceId) { this._serviceId = aServiceId; this._queue = []; Services.obs.addObserver(this, kSmsDeletedObserverTopic, false); Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); } -SmsSendingSchedular.prototype = { +SmsSendingScheduler.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionListener]), _serviceId: 0, @@ -1263,6 +1299,10 @@ SmsSendingSchedular.prototype = { */ schedule: function(aSendingRequest) { if (aSendingRequest) { + if (DEBUG) { + debug("scheduling message: messageId=" + aSendingRequest.messageId + + ", serviceId=" + this._serviceId); + } this._ensureMoboConnObserverRegistration(); this._queue.push(aSendingRequest) @@ -1281,7 +1321,7 @@ SmsSendingSchedular.prototype = { */ send: function() { let connection = - gMobileConnectionService.getItemByServiceId(this._servicdeId); + gMobileConnectionService.getItemByServiceId(this._serviceId); // If the voice connection is temporarily unavailable, pend the request. let voiceInfo = connection && connection.voice; @@ -1313,19 +1353,19 @@ SmsSendingSchedular.prototype = { observe: function(aSubject, aTopic, aData) { switch (aTopic) { case kSmsDeletedObserverTopic: + let deletedInfo = aSubject.QueryInterface(Ci.nsIDeletedMessageInfo); if (DEBUG) { debug("Observe " + kSmsDeletedObserverTopic + ": " + - JSON.stringify(aSubject)); + JSON.stringify(deletedInfo)); } - if (aSubject && aSubject.deletedMessageIds) { - for (let id of aSubject.deletedMessageIds) { - for (let i = 0; i < this._queue.length; i++) { - if (this._queue[i].messageId === id) { - if (DEBUG) debug("Deleting message with id=" + id); - this._queue.splice(i, 1)[0].onCancel( - Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR); - } + if (deletedInfo && deletedInfo.deletedMessageIds) { + for (let i = 0; i < this._queue.length; i++) { + let id = this._queue[i].messageId; + if (deletedInfo.deletedMessageIds.includes(id)) { + if (DEBUG) debug("Deleting message with id=" + id); + this._queue.splice(i, 1)[0].onCancel( + Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR); } } } @@ -1370,7 +1410,9 @@ SmsSendingSchedular.prototype = { notifyClirModeChanged: function(mode) {}, notifyLastKnownNetworkChanged: function() {}, notifyLastKnownHomeNetworkChanged: function() {}, - notifyNetworkSelectionModeChanged: function() {} + notifyNetworkSelectionModeChanged: function() {}, + notifyDeviceIdentitiesChanged: function() {} + }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsService]); diff --git a/dom/mobilemessage/gonk/WspPduHelper.jsm b/dom/mobilemessage/gonk/WspPduHelper.jsm index e1a4f68040..8b3ca49d41 100644 --- a/dom/mobilemessage/gonk/WspPduHelper.jsm +++ b/dom/mobilemessage/gonk/WspPduHelper.jsm @@ -9,7 +9,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.importGlobalProperties(['Blob']); Cu.import("resource://gre/modules/wap_consts.js", this); -let DEBUG; // set to true to see debug messages +var DEBUG; // set to true to see debug messages // Special ASCII characters const NUL = 0; @@ -2834,7 +2834,7 @@ this.OMNA_PUSH_APPLICATION_IDS = (function() { return ids; })(); -let debug; +var debug; if (DEBUG) { debug = function(s) { dump("-@- WspPduHelper: " + s + "\n"); diff --git a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl index 3d91c5c680..2964b3590a 100644 --- a/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl +++ b/dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl @@ -10,7 +10,7 @@ interface nsIMobileMessageCallback : nsISupports /** * All SMS related errors. * Make sure to keep this list in sync with the list in: - * embedding/android/GeckoSmsManager.java + * mobile/android/base/GeckoSmsManager.java */ const unsigned short SUCCESS_NO_ERROR = 0; const unsigned short NO_SIGNAL_ERROR = 1; diff --git a/dom/mobilemessage/ipc/SmsChild.cpp b/dom/mobilemessage/ipc/SmsChild.cpp index b42fe08742..d0594450c9 100644 --- a/dom/mobilemessage/ipc/SmsChild.cpp +++ b/dom/mobilemessage/ipc/SmsChild.cpp @@ -200,7 +200,6 @@ SmsRequestChild::Recv__delete__(const MessageReply& aReply) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mReplyRequest); - nsCOMPtr message; switch(aReply.type()) { case MessageReply::TReplyMessageSend: { const MobileMessageData& data = diff --git a/dom/mobilemessage/tests/marionette/head.js b/dom/mobilemessage/tests/marionette/head.js index f4eb83882d..eee60f1346 100644 --- a/dom/mobilemessage/tests/marionette/head.js +++ b/dom/mobilemessage/tests/marionette/head.js @@ -227,7 +227,7 @@ function sendMmsWithFailure(aMmsParameters, aSendParameters) { /** * Retrieve message by message id. * - * Fulfill params: MozSmsMessage + * Fulfill params: SmsMessage * Reject params: * event -- a DOMEvent * @@ -476,7 +476,7 @@ function sendTextSmsToEmulator(aFrom, aText) { /** * Send simple text SMS to emulator and wait for a received event. * - * Fulfill params: MozSmsMessage + * Fulfill params: SmsMessage * Reject params: (none) * * @param aFrom diff --git a/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js b/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js index 14684c7ebb..3ba2f00876 100644 --- a/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js +++ b/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js @@ -53,7 +53,7 @@ function saveMmsNotification() { .saveReceivedMessage(notification, function(aRv, aDomMessage) { log("saveReceivedMessage(): " + aRv); if (Components.isSuccessCode(aRv)) { - deferred.resolve(aDomMessage); + deferred.resolve(aDomMessage.QueryInterface(Ci.nsIMmsMessage)); } else { deferred.reject(); } diff --git a/dom/mobilemessage/tests/marionette/test_incoming.js b/dom/mobilemessage/tests/marionette/test_incoming.js index edfa10baa2..03871f65ed 100644 --- a/dom/mobilemessage/tests/marionette/test_incoming.js +++ b/dom/mobilemessage/tests/marionette/test_incoming.js @@ -12,7 +12,7 @@ const LONG_BODY = new Array(17).join(SHORT_BODY); ok(LONG_BODY.length > 160, "LONG_BODY.length"); function checkMessage(aMessage, aBody) { - ok(aMessage instanceof MozSmsMessage, "Message is instanceof MozSmsMessage"); + ok(aMessage instanceof SmsMessage, "Message is instanceof SmsMessage"); is(aMessage.type, "sms", "message.type"); ok(aMessage.id, "message.id"); diff --git a/dom/mobilemessage/tests/marionette/test_message_classes.js b/dom/mobilemessage/tests/marionette/test_message_classes.js index 6c067ba82a..65724ae892 100644 --- a/dom/mobilemessage/tests/marionette/test_message_classes.js +++ b/dom/mobilemessage/tests/marionette/test_message_classes.js @@ -34,7 +34,7 @@ function sendSmsPduToEmulator(pdu) { } function checkMessage(message, id, threadId, messageClass) { - ok(message instanceof MozSmsMessage, + ok(message instanceof SmsMessage, "message is instanceof " + message.constructor); if (id == null) { ok(message.id > 0, "message.id"); diff --git a/dom/mobilemessage/tests/marionette/test_outgoing.js b/dom/mobilemessage/tests/marionette/test_outgoing.js index fe08212493..eb772fe3fe 100644 --- a/dom/mobilemessage/tests/marionette/test_outgoing.js +++ b/dom/mobilemessage/tests/marionette/test_outgoing.js @@ -24,7 +24,7 @@ const LONG_BODY = "Let me not to the marriage of true minds\n" function checkMessage(message, delivery, body) { ok(message, "message is valid"); - ok(message instanceof MozSmsMessage, + ok(message instanceof SmsMessage, "message is instanceof " + message.constructor); is(message.type, "sms", "message.type"); diff --git a/dom/mobilemessage/tests/mochitest/test_sms_basics.html b/dom/mobilemessage/tests/mochitest/test_sms_basics.html index 91adcb8fd2..bebeeff897 100644 --- a/dom/mobilemessage/tests/mochitest/test_sms_basics.html +++ b/dom/mobilemessage/tests/mochitest/test_sms_basics.html @@ -15,13 +15,14 @@ /** Test for WebSMS **/ -// idl interfaces exposed to every page. -// TODO Bug 859764 -const IDL_IFACE_WITH_PREFIX = ["SmsMessage", "MmsMessage", "MobileMessageThread"]; - // webidl interfaces guarded by [AvailableIn=CertifiedApps]. const WEBIDL_IFACE_WITH_PREFIX = ["SmsEvent", "MmsEvent", "MessageDeletedEvent"]; -const WEBIDL_IFACE = ["DOMMobileMessageError"]; +const WEBIDL_IFACE = [ + "DOMMobileMessageError", + "MmsMessage", + "MobileMessageThread", + "SmsMessage", +]; function checkSmsDisabled() { ok(!('mozMobileMessage' in frames[0].navigator), "navigator.mozMobileMessage should not exist"); @@ -42,14 +43,6 @@ function checkSmsEnabled() { "navigator.mozMobileMessage is an MobileMessageManager object"); } -function checkIdlInterfaceInWindow() { - for (let i = 0; i < IDL_IFACE_WITH_PREFIX.length; i++) { - let iface = IDL_IFACE_WITH_PREFIX[i]; - ok(!(iface in window), iface + " should be prefixed"); - ok(("Moz" + iface) in window, iface + " should be prefixed"); - } -} - function checkWebidlInterfaceInWindow() { for (let i = 0; i < WEBIDL_IFACE_WITH_PREFIX.length; i++) { let iface = WEBIDL_IFACE_WITH_PREFIX[i]; @@ -77,7 +70,6 @@ function checkWebidlInterfaceNotInWindow() { } function test() { - checkIdlInterfaceInWindow(); checkWebidlInterfaceNotInWindow(); // If sms is disabled and permission is removed, sms is disabled. diff --git a/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js b/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js index f4130d4014..5b2d7ddbbb 100644 --- a/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js +++ b/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js @@ -34,7 +34,7 @@ function run_test() { add_test(function test_interface() { let sms = newMessage(null, null, ICC_ID, "sent", "success", null, null, null, "normal", Date.now(), Date.now(), Date.now(), true); - do_check_true(sms instanceof Ci.nsIDOMMozSmsMessage); + do_check_true(sms instanceof Ci.nsISmsMessage); do_check_eq(sms.id, 0); do_check_eq(sms.threadId, 0); do_check_eq(sms.iccId, ICC_ID); @@ -54,7 +54,7 @@ add_test(function test_interface() { add_test(function test_icc_id_not_available() { let sms = newMessage(null, null, null, "sent", "success", null, null, null, "normal", Date.now(), Date.now(), Date.now(), true); - do_check_true(sms instanceof Ci.nsIDOMMozSmsMessage); + do_check_true(sms instanceof Ci.nsISmsMessage); do_check_eq(sms.id, 0); do_check_eq(sms.threadId, 0); do_check_eq(sms.iccId, null); diff --git a/dom/payment/PaymentProvider.js b/dom/payment/PaymentProvider.js index 4fe187b3c1..b3d757675f 100644 --- a/dom/payment/PaymentProvider.js +++ b/dom/payment/PaymentProvider.js @@ -231,9 +231,16 @@ PaymentProvider.prototype.removeSilentSmsObserver = function(aNumber, aCallback) }; PaymentProvider.prototype._onSilentSms = function(aSubject, aTopic, aData) { - _debug && DEBUG("Got silent message! " + aSubject.sender + " - " + aSubject.body); + if (!aSubject || !(aSubject instanceof Ci.nsISmsMessage)) { + _debug && DEBUG("Invalid subject when receiving silent message!"); + return; + } - let number = aSubject.sender; + let message = aSubject.QueryInterface(Ci.nsISmsMessage); + + _debug && DEBUG("Got silent message! " + message.sender + " - " + message.body); + + let number = message.sender; if (!number || this._silentNumbers.indexOf(number) == -1) { _debug && DEBUG("No observers for " + number); return; @@ -246,7 +253,7 @@ PaymentProvider.prototype._onSilentSms = function(aSubject, aTopic, aData) { if (this._strategy.paymentServiceId === null) { let i = 0; while(i < gRil.numRadioInterfaces) { - if (this.iccInfo[i].iccId === aSubject.iccId) { + if (this.iccInfo[i].iccId === message.iccId) { this._strategy.paymentServiceId = i; break; } @@ -255,7 +262,13 @@ PaymentProvider.prototype._onSilentSms = function(aSubject, aTopic, aData) { } this._silentSmsObservers[number].forEach(function(callback) { - callback(aSubject); + callback({ + iccId: message.iccId, + sender: message.sender, + body: message.body, + timestamp: message.timestamp, + sentTimestamp: message.sentTimestamp + }); }); }; diff --git a/dom/system/gonk/AudioManager.cpp b/dom/system/gonk/AudioManager.cpp index ae6757d370..94bc4240c6 100644 --- a/dom/system/gonk/AudioManager.cpp +++ b/dom/system/gonk/AudioManager.cpp @@ -46,6 +46,7 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/SettingChangeNotificationBinding.h" +using namespace mozilla::dom; using namespace mozilla::dom::gonk; using namespace android; using namespace mozilla; @@ -81,13 +82,47 @@ static const uint32_t sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = { #endif }; -// Use a half value of each volume category as the default volume. -static const uint32_t sDefaultVolumeCategoriesTbl[VOLUME_TOTAL_NUMBER] = { - 8, // VOLUME_MEDIA - 8, // VOLUME_NOTIFICATION - 8, // VOLUME_ALARM - 3, // VOLUME_TELEPHONY - 8, // VOLUME_BLUETOOTH_SCO +static const uint32_t sDefaultStreamVolumeTbl[AUDIO_STREAM_CNT] = { + 3, // voice call + 8, // system + 8, // ring + 8, // music + 8, // alarm + 8, // notification + 8, // BT SCO + 15, // enforced audible // XXX Handle as fixed maximum audio setting + 8, // DTMF + 8, // TTS +#if ANDROID_VERSION < 19 + 8, // FM +#endif +}; + +static const int32_t sStreamVolumeAliasTbl[AUDIO_STREAM_CNT] = { + AUDIO_STREAM_VOICE_CALL, // voice call + AUDIO_STREAM_NOTIFICATION, // system + AUDIO_STREAM_NOTIFICATION, // ring + AUDIO_STREAM_MUSIC, // music + AUDIO_STREAM_ALARM, // alarm + AUDIO_STREAM_NOTIFICATION, // notification + AUDIO_STREAM_BLUETOOTH_SCO, // BT SCO + AUDIO_STREAM_ENFORCED_AUDIBLE,// enforced audible + AUDIO_STREAM_DTMF, // DTMF + AUDIO_STREAM_TTS, // TTS +#if ANDROID_VERSION < 19 + AUDIO_STREAM_MUSIC, // FM +#endif +}; + +static const uint32_t sChannelStreamTbl[NUMBER_OF_AUDIO_CHANNELS] = { + AUDIO_STREAM_MUSIC, // AudioChannel::Normal + AUDIO_STREAM_MUSIC, // AudioChannel::Content + AUDIO_STREAM_NOTIFICATION, // AudioChannel::Notification + AUDIO_STREAM_ALARM, // AudioChannel::Alarm + AUDIO_STREAM_VOICE_CALL, // AudioChannel::Telephony + AUDIO_STREAM_RING, // AudioChannel::Ringer + AUDIO_STREAM_ENFORCED_AUDIBLE,// AudioChannel::Publicnotification + AUDIO_STREAM_SYSTEM, // AudioChannel::System }; // Mappings AudioOutputProfiles to strings. @@ -106,12 +141,26 @@ namespace mozilla { namespace dom { namespace gonk { -static const VolumeData gVolumeData[VOLUME_TOTAL_NUMBER] = { - {"audio.volume.content", VOLUME_MEDIA}, - {"audio.volume.notification", VOLUME_NOTIFICATION}, - {"audio.volume.alarm", VOLUME_ALARM}, - {"audio.volume.telephony", VOLUME_TELEPHONY}, - {"audio.volume.bt_sco", VOLUME_BLUETOOTH_SCO} +/** + * We have five sound volume settings from UX spec, + * You can see more informations in Bug1068219. + * (1) Media : music, video, FM ... + * (2) Notification : ringer, notification ... + * (3) Alarm : alarm + * (4) Telephony : GSM call, WebRTC call + * (5) Bluetooth SCO : SCO call + **/ +struct VolumeData { + const char* mChannelName; + int32_t mStreamType; +}; + +static const VolumeData gVolumeData[] = { + {"audio.volume.content", AUDIO_STREAM_MUSIC}, + {"audio.volume.notification", AUDIO_STREAM_NOTIFICATION}, + {"audio.volume.alarm", AUDIO_STREAM_ALARM}, + {"audio.volume.telephony", AUDIO_STREAM_VOICE_CALL}, + {"audio.volume.bt_sco", AUDIO_STREAM_BLUETOOTH_SCO} }; class RunnableCallTask : public Task @@ -145,80 +194,74 @@ GetSettingServiceLock() return lock.forget(); } -class AudioProfileData final +#if ANDROID_VERSION >= 21 +class GonkAudioPortCallback : public AudioSystem::AudioPortCallback { public: - explicit AudioProfileData(AudioOutputProfiles aProfile) - : mProfile(aProfile) - , mActive(false) + virtual void onAudioPortListUpdate() { - for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) { - mVolumeTable.AppendElement(0); - } - }; - - AudioOutputProfiles GetProfile() const - { - return mProfile; + nsCOMPtr runnable = + NS_NewRunnableFunction([]() { + MOZ_ASSERT(NS_IsMainThread()); + RefPtr audioManager = AudioManager::GetInstance(); + NS_ENSURE_TRUE(audioManager.get(), ); + audioManager->UpdateCachedActiveDevicesForStreams(); + audioManager->MaybeUpdateVolumeSettingToDatabase(); + }); + NS_DispatchToMainThread(runnable); } - - void SetActive(bool aActive) - { - mActive = aActive; - } - - bool GetActive() const - { - return mActive; - } - - nsTArray mVolumeTable; -private: - const AudioOutputProfiles mProfile; - bool mActive; + virtual void onAudioPatchListUpdate() { } + virtual void onServiceDied() { } }; +#endif void AudioManager::HandleAudioFlingerDied() { + //Disable volume change notification + mIsVolumeInited = false; + uint32_t attempt; for (attempt = 0; attempt < 50; attempt++) { if (defaultServiceManager()->checkService(String16(AUDIO_POLICY_SERVICE_NAME)) != 0) { break; } - LOG("AudioPolicyService is dead! attempt=%d", attempt); usleep(1000 * 200); } MOZ_RELEASE_ASSERT(attempt < 50); - for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) { - AudioSystem::initStreamVolume(static_cast(loop), - 0, - sMaxStreamVolumeTbl[loop]); - uint32_t index; - GetStreamVolumeIndex(loop, &index); - SetStreamVolumeIndex(loop, index); - } + // Indicate to audio HAL that we start the reconfiguration phase after a media + // server crash + AudioSystem::setParameters(0, String8("restarting=true")); - if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) { - UpdateHeadsetConnectionState(hal::SWITCH_STATE_HEADSET); - } else if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { - UpdateHeadsetConnectionState(hal::SWITCH_STATE_HEADPHONE); - } else { - UpdateHeadsetConnectionState(hal::SWITCH_STATE_OFF); - } + // Restore device connection states + SetAllDeviceConnectionStates(); - int32_t phoneState = nsIAudioManager::PHONE_STATE_INVALID; - GetPhoneState(&phoneState); + // Restore call state #if ANDROID_VERSION < 17 - AudioSystem::setPhoneState(phoneState); + AudioSystem::setPhoneState(mPhoneState); #else - AudioSystem::setPhoneState(static_cast(phoneState)); + AudioSystem::setPhoneState(static_cast(mPhoneState)); #endif - AudioSystem::get_audio_flinger(); + // Restore master volume + AudioSystem::setMasterVolume(1.0); + + // Restore stream volumes + for (uint32_t streamType = 0; streamType < AUDIO_STREAM_CNT; ++streamType) { + mStreamStates[streamType]->InitStreamVolume(); + mStreamStates[streamType]->RestoreVolumeIndexToAllDevices(); + } + + // Indicate the end of reconfiguration phase to audio HAL + AudioSystem::setParameters(0, String8("restarting=true")); + + // Enable volume change notification + mIsVolumeInited = true; + mAudioOutProfileUpdated = 0; + MaybeUpdateVolumeSettingToDatabase(true); } class VolumeInitCallback final : public nsISettingsServiceCallback @@ -241,23 +284,22 @@ public: { RefPtr audioManager = AudioManager::GetInstance(); MOZ_ASSERT(audioManager); - for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) { + for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { NS_ConvertASCIItoUTF16 volumeType(gVolumeData[idx].mChannelName); if (StringBeginsWith(aName, volumeType)) { AudioOutputProfiles profile = GetProfileFromSettingName(aName); MOZ_ASSERT(profile != DEVICE_ERROR); - - uint32_t category = gVolumeData[idx].mCategory; + int32_t stream = gVolumeData[idx].mStreamType; uint32_t volIndex = aResult.isInt32() ? - aResult.toInt32() : sDefaultVolumeCategoriesTbl[category]; - nsresult rv = audioManager->ValidateVolumeIndex(category, volIndex); + aResult.toInt32() : sDefaultStreamVolumeTbl[stream]; + nsresult rv = audioManager->ValidateVolumeIndex(stream, volIndex); if (NS_WARN_IF(NS_FAILED(rv))) { mPromiseHolder.Reject("Error : invalid volume index.", __func__); return rv; } - audioManager->InitProfileVolume(profile, category, volIndex); - if (++mInitCounter == DEVICE_TOTAL_NUMBER * VOLUME_TOTAL_NUMBER) { + audioManager->InitVolumeForProfile(profile, stream, volIndex); + if (++mInitCounter == DEVICE_TOTAL_NUMBER * MOZ_ARRAY_LENGTH(gVolumeData)) { mPromiseHolder.Resolve(true, __func__); } return NS_OK; @@ -312,95 +354,136 @@ BinderDeadCallback(status_t aErr) NS_DispatchToMainThread(runnable); } -static bool -IsDeviceOn(audio_devices_t device) +bool +AudioManager::IsFmOutConnected() { -#if ANDROID_VERSION >= 15 - return AudioSystem::getDeviceConnectionState(device, "") == - AUDIO_POLICY_DEVICE_STATE_AVAILABLE; -#else - return false; -#endif + return mConnectedDevices.Get(AUDIO_DEVICE_OUT_FM, nullptr); } NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver) +void +AudioManager::AudioOutProfileUpdated(AudioOutputProfiles aProfile) +{ + mAudioOutProfileUpdated |= (1 << aProfile); +} + void AudioManager::UpdateHeadsetConnectionState(hal::SwitchState aState) { -#if ANDROID_VERSION >= 15 + bool headphoneConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADPHONE, + nullptr); + bool headsetConnected = mConnectedDevices.Get(AUDIO_DEVICE_OUT_WIRED_HEADSET, + nullptr); if (aState == hal::SWITCH_STATE_HEADSET) { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, ""); - mHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADSET; + UpdateDeviceConnectionState(true, + AUDIO_DEVICE_OUT_WIRED_HEADSET, + NS_LITERAL_CSTRING("")); } else if (aState == hal::SWITCH_STATE_HEADPHONE) { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE, - AUDIO_POLICY_DEVICE_STATE_AVAILABLE, ""); - mHeadsetState |= AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + UpdateDeviceConnectionState(true, + AUDIO_DEVICE_OUT_WIRED_HEADPHONE, + NS_LITERAL_CSTRING("")); } else if (aState == hal::SWITCH_STATE_OFF) { - if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADSET) { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADSET, - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, ""); + if (headsetConnected) { + UpdateDeviceConnectionState(false, + AUDIO_DEVICE_OUT_WIRED_HEADSET, + NS_LITERAL_CSTRING("")); } - if (mHeadsetState & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_WIRED_HEADPHONE, - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, ""); + if (headphoneConnected) { + UpdateDeviceConnectionState(false, + AUDIO_DEVICE_OUT_WIRED_HEADPHONE, + NS_LITERAL_CSTRING("")); } - mHeadsetState = 0; } +} +void +AudioManager::UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName) +{ +#if ANDROID_VERSION >= 15 + bool isConnected = mConnectedDevices.Get(aDevice, nullptr); + if (isConnected && !aIsConnected) { + AudioSystem::setDeviceConnectionState(static_cast(aDevice), + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, + aDeviceName.get()); + mConnectedDevices.Remove(aDevice); + } else if(!isConnected && aIsConnected) { + AudioSystem::setDeviceConnectionState(static_cast(aDevice), + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + aDeviceName.get()); + mConnectedDevices.Put(aDevice, aDeviceName); + } +#if ANDROID_VERSION < 21 + // Manually call it, since AudioPortCallback is not supported. + // Current volumes might be changed by updating active devices in android + // AudioPolicyManager. + MaybeUpdateVolumeSettingToDatabase(); +#endif #else NS_NOTREACHED("Doesn't support audio routing on GB version"); #endif } +void +AudioManager::SetAllDeviceConnectionStates() +{ + for (auto iter = mConnectedDevices.Iter(); !iter.Done(); iter.Next()) { + const uint32_t& device = iter.Key(); + nsCString& deviceAddress = iter.Data(); + AudioSystem::setDeviceConnectionState(static_cast(device), + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, + deviceAddress.get()); + } +#if ANDROID_VERSION < 21 + // Manually call it, since AudioPortCallback is not supported. + // Current volumes might be changed by updating active devices in android + // AudioPolicyManager. + MaybeUpdateVolumeSettingToDatabase(true); +#endif +} + void AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject, const char* aTopic, const nsCString aAddress) { #ifdef MOZ_B2G_BT - bool status; + bool isConnected = false; if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) { BluetoothHfpManagerBase* hfp = static_cast(aSubject); - status = hfp->IsScoConnected(); + isConnected = hfp->IsScoConnected(); } else { BluetoothProfileManagerBase* profile = static_cast(aSubject); - status = profile->IsConnected(); + isConnected = profile->IsConnected(); } - audio_policy_dev_state_t audioState = status ? - AUDIO_POLICY_DEVICE_STATE_AVAILABLE : - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; - if (!strcmp(aTopic, BLUETOOTH_SCO_STATUS_CHANGED_ID)) { - if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + if (isConnected) { String8 cmd; cmd.appendFormat("bt_samplerate=%d", kBtSampleRate); AudioSystem::setParameters(0, cmd); SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_BT_SCO); - SwitchProfileData(DEVICE_BLUETOOTH, true); } else { int32_t force; GetForceForUse(nsIAudioManager::USE_COMMUNICATION, &force); if (force == nsIAudioManager::FORCE_BT_SCO) { SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE); } - SwitchProfileData(DEVICE_BLUETOOTH, false); } } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) { - if (audioState == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE && mA2dpSwitchDone) { + if (!isConnected && mA2dpSwitchDone) { RefPtr self = this; nsCOMPtr runnable = - NS_NewRunnableFunction([self, audioState, aAddress]() { + NS_NewRunnableFunction([self, isConnected, aAddress]() { if (self->mA2dpSwitchDone) { return; } - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, - audioState, - aAddress.get()); + self->UpdateDeviceConnectionState(isConnected, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, + aAddress); + String8 cmd("bluetooth_enabled=false"); AudioSystem::setParameters(0, cmd); cmd.setTo("A2dpSuspended=true"); @@ -411,28 +494,29 @@ AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject, FROM_HERE, new RunnableCallTask(runnable), 1000); mA2dpSwitchDone = false; - SwitchProfileData(DEVICE_BLUETOOTH, false); } else { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, - audioState, aAddress.get()); + UpdateDeviceConnectionState(isConnected, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, + aAddress); String8 cmd("bluetooth_enabled=true"); AudioSystem::setParameters(0, cmd); cmd.setTo("A2dpSuspended=false"); AudioSystem::setParameters(0, cmd); mA2dpSwitchDone = true; - SwitchProfileData(DEVICE_BLUETOOTH, true); #if ANDROID_VERSION >= 17 if (AudioSystem::getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_NO_BT_A2DP) { SetForceForUse(AUDIO_POLICY_FORCE_FOR_MEDIA, AUDIO_POLICY_FORCE_NONE); } #endif } - mBluetoothA2dpEnabled = audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE; + mBluetoothA2dpEnabled = isConnected; } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, - audioState, aAddress.get()); - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, - audioState, aAddress.get()); + UpdateDeviceConnectionState(isConnected, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, + aAddress); + UpdateDeviceConnectionState(isConnected, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, + aAddress); } else if (!strcmp(aTopic, BLUETOOTH_HFP_NREC_STATUS_CHANGED_ID)) { String8 cmd; BluetoothHfpManagerBase* hfp = @@ -511,15 +595,9 @@ AudioManager::Observe(nsISupports* aSubject, } uint32_t volIndex = setting.mValue.toNumber(); - for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) { + for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { if (setting.mKey.EqualsASCII(gVolumeData[idx].mChannelName)) { - SetVolumeByCategory(gVolumeData[idx].mCategory, volIndex); - nsCOMPtr lock = GetSettingServiceLock(); - UpdateVolumeSettingToDatabase(lock.get(), - AppendProfileToVolumeSetting( - gVolumeData[idx].mChannelName, - mPresentProfile).get(), - volIndex); + SetStreamVolumeIndex(gVolumeData[idx].mStreamType, volIndex); return NS_OK; } } @@ -573,12 +651,9 @@ AudioManager::HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent) self->mSwitchDone = true; }); MessageLoop::current()->PostDelayedTask(FROM_HERE, new RunnableCallTask(runnable), 1000); - - SwitchProfileData(DEVICE_HEADSET, false); mSwitchDone = false; } else if (aEvent.status() != hal::SWITCH_STATE_OFF) { UpdateHeadsetConnectionState(aEvent.status()); - SwitchProfileData(DEVICE_HEADSET, true); mSwitchDone = true; } // Handle the coexistence of a2dp / headset device, latest one wins. @@ -595,7 +670,8 @@ AudioManager::HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent) AudioManager::AudioManager() : mPhoneState(PHONE_STATE_CURRENT) - , mHeadsetState(0) + , mIsVolumeInited(false) + , mAudioOutProfileUpdated(0) , mSwitchDone(true) #if defined(MOZ_B2G_BT) || ANDROID_VERSION >= 17 , mBluetoothA2dpEnabled(false) @@ -608,28 +684,31 @@ AudioManager::AudioManager() , mMuteCallToRIL(false) #endif { - hal::RegisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver); + AudioSystem::setErrorCallback(BinderDeadCallback); +#if ANDROID_VERSION >= 21 + android::sp callback = new GonkAudioPortCallback(); + AudioSystem::setAudioPortCallback(callback); +#endif + // Create VolumeStreamStates + for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) { + VolumeStreamState* streamState = + new VolumeStreamState(*this, static_cast(loop)); + mStreamStates.AppendElement(streamState); + } + UpdateCachedActiveDevicesForStreams(); + + RegisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver); + // Initialize headhone/heaset status UpdateHeadsetConnectionState(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES)); NotifyHeadphonesStatus(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES)); - for (uint32_t loop = 0; loop < AUDIO_STREAM_CNT; ++loop) { - AudioSystem::initStreamVolume(static_cast(loop), 0, - sMaxStreamVolumeTbl[loop]); - mCurrentStreamVolumeTbl[loop] = sMaxStreamVolumeTbl[loop]; - } - // Force publicnotification to output at maximal volume - SetStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, - sMaxStreamVolumeTbl[AUDIO_STREAM_ENFORCED_AUDIBLE]); - CreateAudioProfilesData(); - // Get the initial volume index from settings DB during boot up. InitVolumeFromDatabase(); // Gecko only control stream volume not master so set to default value // directly. AudioSystem::setMasterVolume(1.0); - AudioSystem::setErrorCallback(BinderDeadCallback); nsCOMPtr obs = services::GetObserverService(); NS_ENSURE_TRUE_VOID(obs); @@ -662,6 +741,10 @@ AudioManager::AudioManager() } AudioManager::~AudioManager() { + AudioSystem::setErrorCallback(nullptr); +#if ANDROID_VERSION >= 21 + AudioSystem::setAudioPortCallback(nullptr); +#endif hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, mObserver); nsCOMPtr obs = services::GetObserverService(); @@ -770,6 +853,12 @@ AudioManager::SetPhoneState(int32_t aState) return NS_ERROR_FAILURE; } +#if ANDROID_VERSION < 21 + // Manually call it, since AudioPortCallback is not supported. + // Current volumes might be changed by updating active devices in android + // AudioPolicyManager. + MaybeUpdateVolumeSettingToDatabase(); +#endif mPhoneState = aState; return NS_OK; } @@ -781,6 +870,12 @@ AudioManager::SetForceForUse(int32_t aUsage, int32_t aForce) status_t status = AudioSystem::setForceUse( (audio_policy_force_use_t)aUsage, (audio_policy_forced_cfg_t)aForce); +#if ANDROID_VERSION < 21 + // Manually call it, since AudioPortCallback is not supported. + // Current volumes might be changed by updating active devices in android + // AudioPolicyManager. + MaybeUpdateVolumeSettingToDatabase(); +#endif return status ? NS_ERROR_FAILURE : NS_OK; #else NS_NOTREACHED("Doesn't support force routing on GB version"); @@ -802,34 +897,79 @@ AudioManager::GetForceForUse(int32_t aUsage, int32_t* aForce) { NS_IMETHODIMP AudioManager::GetFmRadioAudioEnabled(bool *aFmRadioAudioEnabled) { - *aFmRadioAudioEnabled = IsDeviceOn(AUDIO_DEVICE_OUT_FM); + *aFmRadioAudioEnabled = IsFmOutConnected(); return NS_OK; } NS_IMETHODIMP AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled) { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_FM, - aFmRadioAudioEnabled ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : - AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, ""); - UpdateHeadsetConnectionState(hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES)); + UpdateDeviceConnectionState(aFmRadioAudioEnabled, + AUDIO_DEVICE_OUT_FM, + NS_LITERAL_CSTRING("")); // AUDIO_STREAM_FM is not used on recent gonk. // AUDIO_STREAM_MUSIC is used for FM radio volume control. #if ANDROID_VERSION < 19 // sync volume with music after powering on fm radio if (aFmRadioAudioEnabled) { - uint32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC]; - SetStreamVolumeIndex(AUDIO_STREAM_FM, volIndex); - mCurrentStreamVolumeTbl[AUDIO_STREAM_FM] = volIndex; + uint32_t volIndex = mStreamStates[AUDIO_STREAM_MUSIC]->GetVolumeIndex(); + nsresult rv = mStreamStates[AUDIO_STREAM_FM]-> + SetVolumeIndex(volIndex, AUDIO_DEVICE_OUT_FM); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } #endif return NS_OK; } -nsresult -AudioManager::ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const +NS_IMETHODIMP +AudioManager::SetAudioChannelVolume(uint32_t aChannel, uint32_t aIndex) { - uint32_t maxIndex = GetMaxVolumeByCategory(aCategory); + if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) { + return NS_ERROR_INVALID_ARG; + } + + return SetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex); +} + +NS_IMETHODIMP +AudioManager::GetAudioChannelVolume(uint32_t aChannel, uint32_t* aIndex) +{ + if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) { + return NS_ERROR_INVALID_ARG; + } + + if (!aIndex) { + return NS_ERROR_NULL_POINTER; + } + + return GetStreamVolumeIndex(sChannelStreamTbl[aChannel], aIndex); +} + +NS_IMETHODIMP +AudioManager::GetMaxAudioChannelVolume(uint32_t aChannel, uint32_t* aMaxIndex) +{ + if (aChannel >= NUMBER_OF_AUDIO_CHANNELS) { + return NS_ERROR_INVALID_ARG; + } + + if (!aMaxIndex) { + return NS_ERROR_NULL_POINTER; + } + + *aMaxIndex = mStreamStates[sChannelStreamTbl[aChannel]]->GetMaxIndex(); + return NS_OK; +} + +nsresult +AudioManager::ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const +{ + if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { + return NS_ERROR_INVALID_ARG; + } + + uint32_t maxIndex = mStreamStates[aStream]->GetMaxIndex(); if (aIndex > maxIndex) { return NS_ERROR_FAILURE; } @@ -837,246 +977,71 @@ AudioManager::ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const } nsresult -AudioManager::SetVolumeByCategory(uint32_t aCategory, uint32_t aIndex) +AudioManager::SetStreamVolumeForProfile(AudioOutputProfiles aProfile, + int32_t aStream, + uint32_t aIndex) { - nsresult status; - switch (static_cast(aCategory)) { - case VOLUME_MEDIA: - // AUDIO_STREAM_FM is not used on recent gonk. - // AUDIO_STREAM_MUSIC is used for FM radio volume control. -#if ANDROID_VERSION < 19 - // sync FMRadio's volume with content channel. - if (IsDeviceOn(AUDIO_DEVICE_OUT_FM)) { - status = SetStreamVolumeIndex(AUDIO_STREAM_FM, aIndex); - if (NS_WARN_IF(NS_FAILED(status))) { - return status; - } - } -#endif - status = SetStreamVolumeIndex(AUDIO_STREAM_MUSIC, aIndex); - break; - case VOLUME_NOTIFICATION: - status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex); - if (NS_WARN_IF(NS_FAILED(status))) { - return status; - } - status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex); - if (NS_WARN_IF(NS_FAILED(status))) { - return status; - } - status = SetStreamVolumeIndex(AUDIO_STREAM_SYSTEM, aIndex); - break; - case VOLUME_ALARM: - status = SetStreamVolumeIndex(AUDIO_STREAM_ALARM, aIndex); - break; - case VOLUME_TELEPHONY: - status = SetStreamVolumeIndex(AUDIO_STREAM_VOICE_CALL, aIndex); - case VOLUME_BLUETOOTH_SCO: - status = SetStreamVolumeIndex(AUDIO_STREAM_BLUETOOTH_SCO, aIndex); - break; - default: - return NS_ERROR_INVALID_ARG; - } - return status; -} - -uint32_t -AudioManager::GetVolumeByCategory(uint32_t aCategory) const -{ - switch (static_cast(aCategory)) { - case VOLUME_MEDIA: - return mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC]; - case VOLUME_NOTIFICATION: - MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] == - mCurrentStreamVolumeTbl[AUDIO_STREAM_RING]); - MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] == - mCurrentStreamVolumeTbl[AUDIO_STREAM_SYSTEM]); - return mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION]; - case VOLUME_ALARM: - return mCurrentStreamVolumeTbl[AUDIO_STREAM_ALARM]; - case VOLUME_TELEPHONY: - return mCurrentStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL]; - case VOLUME_BLUETOOTH_SCO: - return mCurrentStreamVolumeTbl[AUDIO_STREAM_BLUETOOTH_SCO]; - default: - NS_WARNING("Can't get volume from error volume category."); - return 0; - } -} - -uint32_t -AudioManager::GetMaxVolumeByCategory(uint32_t aCategory) const -{ - switch (static_cast(aCategory)) { - case VOLUME_MEDIA: - return sMaxStreamVolumeTbl[AUDIO_STREAM_MUSIC]; - case VOLUME_NOTIFICATION: - MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] == - sMaxStreamVolumeTbl[AUDIO_STREAM_RING]); - MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] == - sMaxStreamVolumeTbl[AUDIO_STREAM_SYSTEM]); - return sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION]; - case VOLUME_ALARM: - return sMaxStreamVolumeTbl[AUDIO_STREAM_ALARM]; - case VOLUME_TELEPHONY: - return sMaxStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL]; - case VOLUME_BLUETOOTH_SCO: - return sMaxStreamVolumeTbl[AUDIO_STREAM_BLUETOOTH_SCO]; - default: - NS_WARNING("Can't get max volume from error volume category."); - return 0; - } -} - -NS_IMETHODIMP -AudioManager::SetAudioChannelVolume(uint32_t aChannel, uint32_t aIndex) -{ - nsresult status; - AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ? - VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY; - switch (static_cast(aChannel)) { - case AudioChannel::Normal: - case AudioChannel::Content: - status = SetVolumeByCategory(VOLUME_MEDIA, aIndex); - break; - case AudioChannel::Notification: - case AudioChannel::Ringer: - case AudioChannel::Publicnotification: - case AudioChannel::System: - status = SetVolumeByCategory(VOLUME_NOTIFICATION, aIndex); - break; - case AudioChannel::Alarm: - status = SetVolumeByCategory(VOLUME_ALARM, aIndex); - break; - case AudioChannel::Telephony: - status = SetVolumeByCategory(category, aIndex); - break; - default: - return NS_ERROR_INVALID_ARG; - } - return status; -} - -NS_IMETHODIMP -AudioManager::GetAudioChannelVolume(uint32_t aChannel, uint32_t* aIndex) -{ - if (!aIndex) { - return NS_ERROR_NULL_POINTER; - } - AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ? - VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY; - switch (static_cast(aChannel)) { - case AudioChannel::Normal: - case AudioChannel::Content: - *aIndex = GetVolumeByCategory(VOLUME_MEDIA); - break; - case AudioChannel::Notification: - case AudioChannel::Ringer: - case AudioChannel::Publicnotification: - case AudioChannel::System: - *aIndex = GetVolumeByCategory(VOLUME_NOTIFICATION); - break; - case AudioChannel::Alarm: - *aIndex = GetVolumeByCategory(VOLUME_ALARM); - break; - case AudioChannel::Telephony: - *aIndex = GetVolumeByCategory(category); - break; - default: - return NS_ERROR_INVALID_ARG; - } - return NS_OK; -} - -NS_IMETHODIMP -AudioManager::GetMaxAudioChannelVolume(uint32_t aChannel, uint32_t* aMaxIndex) -{ - if (!aMaxIndex) { - return NS_ERROR_NULL_POINTER; - } - AudioVolumeCategories category = (mPresentProfile == DEVICE_BLUETOOTH) ? - VOLUME_BLUETOOTH_SCO : VOLUME_TELEPHONY; - switch (static_cast(aChannel)) { - case AudioChannel::Normal: - case AudioChannel::Content: - *aMaxIndex = GetMaxVolumeByCategory(VOLUME_MEDIA); - break; - case AudioChannel::Notification: - case AudioChannel::Ringer: - case AudioChannel::Publicnotification: - case AudioChannel::System: - *aMaxIndex = GetMaxVolumeByCategory(VOLUME_NOTIFICATION); - break; - case AudioChannel::Alarm: - *aMaxIndex = GetMaxVolumeByCategory(VOLUME_ALARM); - break; - case AudioChannel::Telephony: - *aMaxIndex = GetMaxVolumeByCategory(category); - break; - default: - return NS_ERROR_INVALID_ARG; - } - return NS_OK; -} - -nsresult -AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex) { - if (aIndex > sMaxStreamVolumeTbl[aStream]) { + if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { return NS_ERROR_INVALID_ARG; } - mCurrentStreamVolumeTbl[aStream] = aIndex; - status_t status; -#if ANDROID_VERSION < 17 - status = AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex); - return status ? NS_ERROR_FAILURE : NS_OK; -#else -#if ANDROID_VERSION < 19 - if (aStream == AUDIO_STREAM_FM) { - status = AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex, - AUDIO_DEVICE_OUT_FM); - return status ? NS_ERROR_FAILURE : NS_OK; + + int32_t streamAlias = sStreamVolumeAliasTbl[aStream]; + VolumeStreamState* state = mStreamStates[streamAlias].get(); + // Rescaling of index is not necessary. + switch (aProfile) { + case DEVICE_PRIMARY: + state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_SPEAKER); + break; + case DEVICE_HEADSET: + state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADSET); + break; + case DEVICE_BLUETOOTH: + state->SetVolumeIndexToAliasStreams(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP); + break; + default: + break; } -#endif - if (mPresentProfile == DEVICE_PRIMARY) { - status = AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex, - AUDIO_DEVICE_OUT_SPEAKER); - status += AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex, - AUDIO_DEVICE_OUT_EARPIECE); - } else if (mPresentProfile == DEVICE_HEADSET) { - status = AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex, - AUDIO_DEVICE_OUT_WIRED_HEADSET); - status += AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex, - AUDIO_DEVICE_OUT_WIRED_HEADPHONE); - } else if (mPresentProfile == DEVICE_BLUETOOTH) { - status = AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex, - AUDIO_DEVICE_OUT_BLUETOOTH_A2DP); - status += AudioSystem::setStreamVolumeIndex( - static_cast(aStream), - aIndex, - AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET); - } else { - NS_WARNING("Can't set stream volume on error profile!"); - } - return status ? NS_ERROR_FAILURE : NS_OK; -#endif + return NS_OK; } nsresult -AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex) { +AudioManager::SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex) +{ + if (aStream <= AUDIO_STREAM_DEFAULT || aStream >= AUDIO_STREAM_MAX) { + return NS_ERROR_INVALID_ARG; + } + + int32_t streamAlias = sStreamVolumeAliasTbl[aStream]; + + nsresult rv; + for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { + if (streamAlias == sStreamVolumeAliasTbl[streamType]) { + rv = mStreamStates[streamType]->SetVolumeIndexToActiveDevices(aIndex); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + } + + // AUDIO_STREAM_FM is not used on recent gonk. + // AUDIO_STREAM_MUSIC is used for FM radio volume control. +#if ANDROID_VERSION < 19 + if (streamAlias == AUDIO_STREAM_MUSIC && IsFmOutConnected()) { + rv = mStreamStates[AUDIO_STREAM_FM]-> + SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_FM); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } +#endif + + MaybeUpdateVolumeSettingToDatabase(); + return NS_OK; +} + +nsresult +AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex) +{ if (!aIndex) { return NS_ERROR_INVALID_ARG; } @@ -1085,25 +1050,10 @@ AudioManager::GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex) { return NS_ERROR_INVALID_ARG; } - *aIndex = mCurrentStreamVolumeTbl[aStream]; - + *aIndex = mStreamStates[aStream]->GetVolumeIndex(); return NS_OK; } -AudioProfileData* -AudioManager::FindAudioProfileData(AudioOutputProfiles aProfile) -{ - uint32_t profilesNum = mAudioProfiles.Length(); - MOZ_ASSERT(profilesNum == DEVICE_TOTAL_NUMBER, "Error profile numbers!"); - for (uint32_t idx = 0; idx < profilesNum; ++idx) { - if (mAudioProfiles[idx]->GetProfile() == aProfile) { - return mAudioProfiles[idx]; - } - } - NS_WARNING("Can't find audio profile data"); - return nullptr; -} - nsAutoCString AudioManager::AppendProfileToVolumeSetting(const char* aName, AudioOutputProfiles aProfile) { @@ -1140,10 +1090,10 @@ AudioManager::InitVolumeFromDatabase() &AudioManager::InitProfileVolumeSucceeded, &AudioManager::InitProfileVolumeFailed); - for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) { - for (uint32_t pIdx = 0; pIdx < DEVICE_TOTAL_NUMBER; ++pIdx) { + for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { + for (uint32_t profile = 0; profile < DEVICE_TOTAL_NUMBER; ++profile) { lock->Get(AppendProfileToVolumeSetting(gVolumeData[idx].mChannelName, - static_cast(pIdx)).get(), callback); + static_cast(profile)).get(), callback); } } } @@ -1151,139 +1101,431 @@ AudioManager::InitVolumeFromDatabase() void AudioManager::InitProfileVolumeSucceeded() { - SendVolumeChangeNotification(FindAudioProfileData(mPresentProfile)); + mIsVolumeInited = true; + MaybeUpdateVolumeSettingToDatabase(true); } void AudioManager::InitProfileVolumeFailed(const char* aError) { + // Initialize stream volumes with default values + for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { + for (int32_t profile = 0; profile < DEVICE_TOTAL_NUMBER; ++profile) { + int32_t stream = gVolumeData[idx].mStreamType; + uint32_t volIndex = sDefaultStreamVolumeTbl[stream]; + InitVolumeForProfile(static_cast(profile), + stream, + volIndex); + } + } + mIsVolumeInited = true; + MaybeUpdateVolumeSettingToDatabase(true); NS_WARNING(aError); } void -AudioManager::UpdateVolumeSettingToDatabase(nsISettingsServiceLock* aLock, - const char* aTopic, - uint32_t aVolIndex) +AudioManager::MaybeUpdateVolumeSettingToDatabase(bool aForce) { - MOZ_ASSERT(aLock); + if (!mIsVolumeInited) { + return; + } + nsCOMPtr lock = GetSettingServiceLock(); + if (NS_WARN_IF(!lock)) { + return; + } + + // Send events to update the Gaia volumes mozilla::AutoSafeJSContext cx; JS::Rooted value(cx); - value.setInt32(aVolIndex); - aLock->Set(aTopic, value, nullptr, nullptr); + uint32_t volume = 0; + for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { + int32_t streamType = gVolumeData[idx].mStreamType; + VolumeStreamState* streamState = mStreamStates[streamType].get(); + if(!aForce && !streamState->IsDevicesChanged()) { + continue; + } + // Get volume index of active device. + volume = streamState->GetVolumeIndex(); + value.setInt32(volume); + lock->Set(gVolumeData[idx].mChannelName, value, nullptr, nullptr); + } + + // For reducing the code dependency, Gaia doesn't need to know the + // profile volume, it only need to care about different volume categories. + // However, we need to send the setting volume to the permanent database, + // so that we can store the volume setting even if the phone reboots. + + for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { + int32_t streamType = gVolumeData[idx].mStreamType; + VolumeStreamState* streamState = mStreamStates[streamType].get(); + + if(!streamState->IsVolumeIndexesChanged()) { + continue; + } + + if (mAudioOutProfileUpdated & (1 << DEVICE_PRIMARY)) { + volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_SPEAKER); + value.setInt32(volume); + lock->Set(AppendProfileToVolumeSetting( + gVolumeData[idx].mChannelName, + DEVICE_PRIMARY).get(), + value, nullptr, nullptr); + } + if (mAudioOutProfileUpdated & (1 << DEVICE_HEADSET)) { + volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_WIRED_HEADSET); + value.setInt32(volume); + lock->Set(AppendProfileToVolumeSetting( + gVolumeData[idx].mChannelName, + DEVICE_HEADSET).get(), + value, nullptr, nullptr); + } + if (mAudioOutProfileUpdated & (1 << DEVICE_BLUETOOTH)) { + volume = streamState->GetVolumeIndex(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP); + value.setInt32(volume); + lock->Set(AppendProfileToVolumeSetting( + gVolumeData[idx].mChannelName, + DEVICE_BLUETOOTH).get(), + value, nullptr, nullptr); + } + } + + // Clear changed flags + for (uint32_t idx = 0; idx < MOZ_ARRAY_LENGTH(gVolumeData); ++idx) { + int32_t streamType = gVolumeData[idx].mStreamType; + mStreamStates[streamType]->ClearDevicesChanged(); + mStreamStates[streamType]->ClearVolumeIndexesChanged(); + } + // Clear mAudioOutProfileUpdated + mAudioOutProfileUpdated = 0; } void -AudioManager::SendVolumeChangeNotification(AudioProfileData* aProfileData) +AudioManager::InitVolumeForProfile(AudioOutputProfiles aProfile, + int32_t aStreamType, + uint32_t aIndex) { - MOZ_ASSERT(aProfileData); - - // Change the value of the current volume setting, so that the Gaia can get - // correct volume values and update the volume UI. In addition, for reducing - // the code dependency, Gaia doesn't need to know the current profile, it - // only need to care about different volume categories. - nsCOMPtr lock = GetSettingServiceLock(); - for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) { - uint32_t volSetting = gVolumeData[idx].mCategory; - UpdateVolumeSettingToDatabase(lock.get(), - gVolumeData[idx].mChannelName, - aProfileData->mVolumeTable[volSetting]); - } -} - -void -AudioManager::CreateAudioProfilesData() -{ - MOZ_ASSERT(mAudioProfiles.IsEmpty(), "mAudioProfiles should be empty!"); - for (uint32_t idx = 0; idx < DEVICE_TOTAL_NUMBER; ++idx) { - AudioProfileData* profile = new AudioProfileData(static_cast(idx)); - mAudioProfiles.AppendElement(profile); - } - UpdateProfileState(DEVICE_PRIMARY, true); -} - -void -AudioManager::InitProfileVolume(AudioOutputProfiles aProfile, - uint32_t aCategory, - uint32_t aIndex) -{ - AudioProfileData* profileData = FindAudioProfileData(aProfile); - MOZ_ASSERT(profileData); - profileData->mVolumeTable[aCategory] = aIndex; - SetVolumeByCategory(aCategory, aIndex); -} - -void -AudioManager::SwitchProfileData(AudioOutputProfiles aProfile, - bool aActive) -{ - MOZ_ASSERT(DEVICE_PRIMARY <= aProfile && - aProfile < DEVICE_TOTAL_NUMBER, "Error profile type!"); - - // Save the present profile volume data. - AudioOutputProfiles oldProfile = mPresentProfile; - AudioProfileData* profileData = FindAudioProfileData(oldProfile); - MOZ_ASSERT(profileData); - UpdateVolumeToProfile(profileData); - UpdateProfileState(aProfile, aActive); - - AudioOutputProfiles newProfile = mPresentProfile; - if (oldProfile == newProfile) { - return; - } - - // Update new profile volume data and send the changing event. - profileData = FindAudioProfileData(newProfile); - MOZ_ASSERT(profileData); - UpdateVolumeFromProfile(profileData); - SendVolumeChangeNotification(profileData); -} - -void -AudioManager::UpdateProfileState(AudioOutputProfiles aProfile, bool aActive) -{ - MOZ_ASSERT(DEVICE_PRIMARY <= aProfile && aProfile < DEVICE_TOTAL_NUMBER, - "Error profile type!"); - if (aProfile == DEVICE_PRIMARY && !aActive) { - NS_WARNING("Can't turn off the primary profile!"); - return; - } - - mAudioProfiles[aProfile]->SetActive(aActive); - if (aActive) { - mPresentProfile = aProfile; - return; - } - - // The primary profile has the lowest priority. We will check whether there - // are other profiles. The bluetooth and headset have the same priotity. - uint32_t profilesNum = mAudioProfiles.Length(); - MOZ_ASSERT(profilesNum == DEVICE_TOTAL_NUMBER, "Error profile numbers!"); - for (int32_t idx = profilesNum - 1; idx >= 0; --idx) { - if (mAudioProfiles[idx]->GetActive()) { - mPresentProfile = static_cast(idx); - break; + // Set volume to streams of aStreamType and devices of Profile. + for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { + if (aStreamType == sStreamVolumeAliasTbl[streamType]) { + SetStreamVolumeForProfile(aProfile, streamType, aIndex); } } } void -AudioManager::UpdateVolumeToProfile(AudioProfileData* aProfileData) +AudioManager::UpdateCachedActiveDevicesForStreams() { - MOZ_ASSERT(aProfileData); - for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) { - uint32_t volume = GetVolumeByCategory(gVolumeData[idx].mCategory); - aProfileData->mVolumeTable[gVolumeData[idx].mCategory] = volume; + // This function updates cached active devices for streams. + // It is used for optimization of GetDevicesForStream() since L. + // AudioManager could know when active devices + // are changed in AudioPolicyManager by onAudioPortListUpdate(). + // Except it, AudioManager normally do not need to ask AuidoPolicyManager + // about current active devices of streams and could use cached values. + // Before L, onAudioPortListUpdate() does not exist and GetDevicesForStream() + // does not use the cache. Therefore this function do nothing. +#if ANDROID_VERSION >= 21 + for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { + // Update cached active devices of stream + mStreamStates[streamType]->IsDevicesChanged(false /* aFromCache */); } +#endif +} + +uint32_t +AudioManager::GetDevicesForStream(int32_t aStream, bool aFromCache) +{ +#if ANDROID_VERSION >= 21 + // Since Lollipop, devices update could be notified by AudioPortCallback. + // Cached values can be used if there is no update. + if (aFromCache) { + return mStreamStates[aStream]->GetLastDevices(); + } +#endif + +#if ANDROID_VERSION >= 17 + audio_devices_t devices = + AudioSystem::getDevicesForStream(static_cast(aStream)); + + return static_cast(devices); +#else + return AUDIO_DEVICE_OUT_DEFAULT; +#endif +} + +uint32_t +AudioManager::GetDeviceForStream(int32_t aStream) +{ + uint32_t devices = + GetDevicesForStream(static_cast(aStream)); + uint32_t device = SelectDeviceFromDevices(devices); + return device; +} + +/* static */ uint32_t +AudioManager::SelectDeviceFromDevices(uint32_t aOutDevices) +{ + uint32_t device = aOutDevices; + + // See android AudioService.getDeviceForStream(). + // AudioPolicyManager expects it. + // See also android AudioPolicyManager::getDeviceForVolume(). + if ((device & (device - 1)) != 0) { + // Multiple device selection. + if ((device & AUDIO_DEVICE_OUT_SPEAKER) != 0) { + device = AUDIO_DEVICE_OUT_SPEAKER; +#if ANDROID_VERSION >= 21 + } else if ((device & AUDIO_DEVICE_OUT_HDMI_ARC) != 0) { + device = AUDIO_DEVICE_OUT_HDMI_ARC; + } else if ((device & AUDIO_DEVICE_OUT_SPDIF) != 0) { + device = AUDIO_DEVICE_OUT_SPDIF; + } else if ((device & AUDIO_DEVICE_OUT_AUX_LINE) != 0) { + device = AUDIO_DEVICE_OUT_AUX_LINE; +#endif + } else { + device &= AUDIO_DEVICE_OUT_ALL_A2DP; + } + } + return device; +} +AudioManager::VolumeStreamState::VolumeStreamState(AudioManager& aManager, + int32_t aStreamType) + : mManager(aManager) + , mStreamType(aStreamType) + , mLastDevices(0) + , mIsDevicesChanged(true) + , mIsVolumeIndexesChanged(true) +{ + InitStreamVolume(); +} + +bool +AudioManager::VolumeStreamState::IsDevicesChanged(bool aFromCache) +{ + uint32_t devices = mManager.GetDevicesForStream(mStreamType, aFromCache); + if (devices != mLastDevices) { + mLastDevices = devices; + mIsDevicesChanged = true; + } + return mIsDevicesChanged; } void -AudioManager::UpdateVolumeFromProfile(AudioProfileData* aProfileData) +AudioManager::VolumeStreamState::ClearDevicesChanged() { - MOZ_ASSERT(aProfileData); - for (uint32_t idx = 0; idx < VOLUME_TOTAL_NUMBER; ++idx) { - SetVolumeByCategory(gVolumeData[idx].mCategory, - aProfileData->mVolumeTable[gVolumeData[idx].mCategory]); + mIsDevicesChanged = false; +} + +bool +AudioManager::VolumeStreamState::IsVolumeIndexesChanged() +{ + return mIsVolumeIndexesChanged; +} + +void +AudioManager::VolumeStreamState::ClearVolumeIndexesChanged() +{ + mIsVolumeIndexesChanged = false; +} + +void +AudioManager::VolumeStreamState::InitStreamVolume() +{ + AudioSystem::initStreamVolume(static_cast(mStreamType), + 0, + GetMaxIndex()); +} + +uint32_t +AudioManager::VolumeStreamState::GetMaxIndex() +{ + return sMaxStreamVolumeTbl[mStreamType]; +} + +uint32_t +AudioManager::VolumeStreamState::GetDefaultIndex() +{ + return sDefaultStreamVolumeTbl[mStreamType]; +} + +uint32_t +AudioManager::VolumeStreamState::GetVolumeIndex() +{ + uint32_t device = mManager.GetDeviceForStream(mStreamType); + return GetVolumeIndex(device); +} + +uint32_t +AudioManager::VolumeStreamState::GetVolumeIndex(uint32_t aDevice) +{ + uint32_t index = 0; + bool ret = mVolumeIndexes.Get(aDevice, &index); + if (!ret) { + index = mVolumeIndexes.Get(AUDIO_DEVICE_OUT_DEFAULT); + } + return index; +} + +nsresult +AudioManager::VolumeStreamState::SetVolumeIndexToActiveDevices(uint32_t aIndex) +{ + uint32_t device = mManager.GetDeviceForStream(mStreamType); + + // Update volume index for device + uint32_t oldVolumeIndex = 0; + bool exist = mVolumeIndexes.Get(device, &oldVolumeIndex); + if (exist && aIndex == oldVolumeIndex) { + // No update + return NS_OK; + } + + // AudioPolicyManager::setStreamVolumeIndex() set volumes of all active + // devices for stream. + nsresult rv = SetVolumeIndexToAliasDevices(aIndex, device); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Workaround to make audio volume control consisitent. + // Active devices of AUDIO_STREAM_NOTIFICATION are affected by + // AUDIO_STREAM_MUSIC's activity. It makes audio volume control inconsistent. + // See Bug 1196724 + if (device != AUDIO_DEVICE_OUT_SPEAKER && + mStreamType == AUDIO_STREAM_NOTIFICATION) { + // Rescaling of index is not necessary. + rv = SetVolumeIndexToAliasDevices(aIndex, AUDIO_DEVICE_OUT_SPEAKER); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + +nsresult +AudioManager::VolumeStreamState::SetVolumeIndexToAliasStreams(uint32_t aIndex, + uint32_t aDevice) +{ + uint32_t oldVolumeIndex = 0; + bool exist = mVolumeIndexes.Get(aDevice, &oldVolumeIndex); + if (exist && aIndex == oldVolumeIndex) { + // No update + return NS_OK; + } + nsresult rv = SetVolumeIndexToAliasDevices(aIndex, aDevice); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) { + if ((streamType != mStreamType) && + sStreamVolumeAliasTbl[streamType] == mStreamType) { + // Rescaling of index is not necessary. + rv = mManager.mStreamStates[streamType]-> + SetVolumeIndexToAliasStreams(aIndex, aDevice); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + } + return NS_OK; +} + +nsresult +AudioManager::VolumeStreamState::SetVolumeIndexToAliasDevices(uint32_t aIndex, + uint32_t aDevice) +{ +#if ANDROID_VERSION >= 17 + nsresult rv = NS_ERROR_FAILURE; + switch (aDevice) { + case AUDIO_DEVICE_OUT_EARPIECE: + case AUDIO_DEVICE_OUT_SPEAKER: + // Apply volume index of DEVICE_PRIMARY devices + rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_EARPIECE); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + mManager.AudioOutProfileUpdated(DEVICE_PRIMARY); + break; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + // Apply volume index of DEVICE_HEADSET devices + rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADSET); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_WIRED_HEADPHONE); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + mManager.AudioOutProfileUpdated(DEVICE_HEADSET); + break; + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: + // Apply volume index of DEVICE_BLUETOOTH devices + rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + mManager.AudioOutProfileUpdated(DEVICE_BLUETOOTH); + break; + default: + rv = SetVolumeIndex(aIndex, aDevice); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + break; + } +#else + SetVolumeIndex(aIndex, aDevice); +#endif + return NS_OK; +} + +nsresult +AudioManager::VolumeStreamState::SetVolumeIndex(uint32_t aIndex, + uint32_t aDevice, + bool aUpdateCache) +{ + status_t rv; +#if ANDROID_VERSION >= 17 + if (aUpdateCache) { + mVolumeIndexes.Put(aDevice, aIndex); + mIsVolumeIndexesChanged = true; + } + + rv = AudioSystem::setStreamVolumeIndex( + static_cast(mStreamType), + aIndex, + aDevice); + return rv ? NS_ERROR_FAILURE : NS_OK; +#else + if (aUpdateCache) { + mVolumeIndexes.Put(AUDIO_DEVICE_OUT_DEFAULT, aIndex); + mIsVolumeIndexesChanged = true; + } + rv = AudioSystem::setStreamVolumeIndex( + static_cast(mStreamType), + aIndex); + return rv ? NS_ERROR_FAILURE : NS_OK; +#endif +} + +void +AudioManager::VolumeStreamState::RestoreVolumeIndexToAllDevices() +{ + for (auto iter = mVolumeIndexes.Iter(); !iter.Done(); iter.Next()) { + const uint32_t& key = iter.Key(); + uint32_t& index = iter.Data(); + SetVolumeIndex(key, index, /* aUpdateCache */ false); } } diff --git a/dom/system/gonk/AudioManager.h b/dom/system/gonk/AudioManager.h index 54d6586f4e..ce0a8e3e3b 100644 --- a/dom/system/gonk/AudioManager.h +++ b/dom/system/gonk/AudioManager.h @@ -18,7 +18,9 @@ #include "mozilla/HalTypes.h" #include "mozilla/Observer.h" +#include "mozilla/UniquePtr.h" #include "nsAutoPtr.h" +#include "nsDataHashtable.h" #include "nsIAudioManager.h" #include "nsIObserver.h" #include "android_audio/AudioSystem.h" @@ -53,32 +55,7 @@ enum AudioOutputProfiles { DEVICE_TOTAL_NUMBER = 3, }; -/** - * We have five sound volume settings from UX spec, - * You can see more informations in Bug1068219. - * (1) Media : music, video, FM ... - * (2) Notification : ringer, notification ... - * (3) Alarm : alarm - * (4) Telephony : GSM call, WebRTC call - * (5) Bluetooth SCO : SCO call - **/ -enum AudioVolumeCategories { - VOLUME_MEDIA = 0, - VOLUME_NOTIFICATION = 1, - VOLUME_ALARM = 2, - VOLUME_TELEPHONY = 3, - VOLUME_BLUETOOTH_SCO = 4, - VOLUME_TOTAL_NUMBER = 5, -}; - -struct VolumeData { - const char* mChannelName; - uint32_t mCategory; -}; - -class RecoverTask; class VolumeInitCallback; -class AudioProfileData; class AudioManager final : public nsIAudioManager , public nsIObserver @@ -90,27 +67,66 @@ public: NS_DECL_NSIAUDIOMANAGER NS_DECL_NSIOBSERVER - // When audio backend is dead, recovery task needs to read all volume - // settings then set back into audio backend. - friend class RecoverTask; - friend class VolumeInitCallback; - - // Open or close the specific profile - void SwitchProfileData(AudioOutputProfiles aProfile, bool aActive); - // Validate whether the volume index is within the range - nsresult ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const; + nsresult ValidateVolumeIndex(int32_t aStream, uint32_t aIndex) const; // Called when android AudioFlinger in mediaserver is died void HandleAudioFlingerDied(); void HandleHeadphoneSwitchEvent(const hal::SwitchEvent& aEvent); + class VolumeStreamState { + public: + explicit VolumeStreamState(AudioManager& aManager, int32_t aStreamType); + int32_t GetStreamType() + { + return mStreamType; + } + bool IsDevicesChanged(bool aFromCache = true); + void ClearDevicesChanged(); + uint32_t GetLastDevices() + { + return mLastDevices; + } + bool IsVolumeIndexesChanged(); + void ClearVolumeIndexesChanged(); + void InitStreamVolume(); + uint32_t GetMaxIndex(); + uint32_t GetDefaultIndex(); + uint32_t GetVolumeIndex(); + uint32_t GetVolumeIndex(uint32_t aDevice); + void ClearCurrentVolumeUpdated(); + // Set volume index to all active devices. + // Active devices are chosen by android AudioPolicyManager. + nsresult SetVolumeIndexToActiveDevices(uint32_t aIndex); + // Set volume index to all alias streams. Alias streams have same volume. + // It is used to update volume based on audio output profile data. + nsresult SetVolumeIndexToAliasStreams(uint32_t aIndex, uint32_t aDevice); + // Set volume index to all alias devices in audio output profile. + // Alias devices have same volume. + nsresult SetVolumeIndexToAliasDevices(uint32_t aIndex, uint32_t aDevice); + nsresult SetVolumeIndex(uint32_t aIndex, uint32_t aDevice, bool aUpdateCache = true); + // Restore volume index to all devices. Called when AudioFlinger is restarted. + void RestoreVolumeIndexToAllDevices(); + private: + AudioManager& mManager; + const int32_t mStreamType; + uint32_t mLastDevices; + bool mIsDevicesChanged; + bool mIsVolumeIndexesChanged; + nsDataHashtable mVolumeIndexes; + }; + protected: int32_t mPhoneState; - // A bitwise variable for recording what kind of headset/headphone is attached. - int32_t mHeadsetState; + bool mIsVolumeInited; + + // A bitwise variable for volume update of audio output profiles + uint32_t mAudioOutProfileUpdated; + + // Connected devices that are controlled by setDeviceConnectionState() + nsDataHashtable mConnectedDevices; bool mSwitchDone; @@ -120,11 +136,23 @@ protected: #ifdef MOZ_B2G_BT bool mA2dpSwitchDone; #endif - uint32_t mCurrentStreamVolumeTbl[AUDIO_STREAM_CNT]; + nsTArray > mStreamStates; + uint32_t mLastChannelVolume[AUDIO_STREAM_CNT]; + bool IsFmOutConnected(); + + nsresult SetStreamVolumeForProfile(AudioOutputProfiles aProfile, + int32_t aStream, + uint32_t aIndex); nsresult SetStreamVolumeIndex(int32_t aStream, uint32_t aIndex); nsresult GetStreamVolumeIndex(int32_t aStream, uint32_t *aIndex); + void UpdateCachedActiveDevicesForStreams(); + uint32_t GetDevicesForStream(int32_t aStream, bool aFromCache = true); + uint32_t GetDeviceForStream(int32_t aStream); + // Choose one device as representative of active devices. + static uint32_t SelectDeviceFromDevices(uint32_t aOutDevices); + private: nsAutoPtr mObserver; #ifdef MOZ_B2G_RIL @@ -132,57 +160,41 @@ private: // mIsMicMuted is only used for toggling mute call to RIL. bool mIsMicMuted; #endif - nsTArray> mAudioProfiles; - AudioOutputProfiles mPresentProfile; void HandleBluetoothStatusChanged(nsISupports* aSubject, const char* aTopic, const nsCString aAddress); void HandleAudioChannelProcessChanged(); - void CreateAudioProfilesData(); + // Initialize volume index for audio output profile + void InitVolumeForProfile(AudioOutputProfiles aProfile, + int32_t aStreamType, + uint32_t aIndex); - // Init the volume setting from the init setting callback - void InitProfileVolume(AudioOutputProfiles aProfile, - uint32_t aCatogory, uint32_t aIndex); - - // Update volume data of profiles - void UpdateVolumeToProfile(AudioProfileData* aProfileData); - - // Apply the volume data to device - void UpdateVolumeFromProfile(AudioProfileData* aProfileData); - - // Send the volume changing event to Gaia - void SendVolumeChangeNotification(AudioProfileData* aProfileData); - - // Update the mPresentProfile and profiles active status - void UpdateProfileState(AudioOutputProfiles aProfile, bool aActive); - - // Volume control functions - nsresult SetVolumeByCategory(uint32_t aCategory, uint32_t aIndex); - uint32_t GetVolumeByCategory(uint32_t aCategory) const; - uint32_t GetMaxVolumeByCategory(uint32_t aCategory) const; - - AudioProfileData* FindAudioProfileData(AudioOutputProfiles aProfile); - - // Append the profile to the volume setting string. + // Append the audio output profile to the volume setting string. nsAutoCString AppendProfileToVolumeSetting(const char* aName, AudioOutputProfiles aProfile); // We store the volume setting in the database, these are related functions. void InitVolumeFromDatabase(); - void UpdateVolumeSettingToDatabase(nsISettingsServiceLock* aLock, - const char* aTopic, - uint32_t aVolIndex); + void MaybeUpdateVolumeSettingToDatabase(bool aForce = false); // Promise functions. void InitProfileVolumeSucceeded(); void InitProfileVolumeFailed(const char* aError); + void AudioOutProfileUpdated(AudioOutputProfiles aProfile); + void UpdateHeadsetConnectionState(hal::SwitchState aState); + void UpdateDeviceConnectionState(bool aIsConnected, uint32_t aDevice, const nsCString& aDeviceName); + void SetAllDeviceConnectionStates(); AudioManager(); ~AudioManager(); + + friend class VolumeInitCallback; + friend class VolumeStreamState; + friend class GonkAudioPortCallback; }; } /* namespace gonk */ diff --git a/dom/system/gonk/DataCallInterfaceService.js b/dom/system/gonk/DataCallInterfaceService.js index abb98fe674..0f0e7101ce 100644 --- a/dom/system/gonk/DataCallInterfaceService.js +++ b/dom/system/gonk/DataCallInterfaceService.js @@ -71,7 +71,9 @@ DataCall.prototype = { ifname: null, addreses: null, dnses: null, - gateways: null + gateways: null, + pcscf: null, + mtu: -1 }; function DataCallInterfaceService() { diff --git a/dom/system/gonk/DataCallManager.js b/dom/system/gonk/DataCallManager.js index 921e749b94..6327da1013 100644 --- a/dom/system/gonk/DataCallManager.js +++ b/dom/system/gonk/DataCallManager.js @@ -941,7 +941,9 @@ DataCallHandler.prototype = { notifyLastKnownHomeNetworkChanged: function() {}, - notifyNetworkSelectionModeChanged: function() {} + notifyNetworkSelectionModeChanged: function() {}, + + notifyDeviceIdentitiesChanged: function() {} }; function DataCall(aClientId, aApnSetting, aDataCallHandler) { @@ -960,7 +962,9 @@ function DataCall(aClientId, aApnSetting, aDataCallHandler) { ifname: null, addresses: [], dnses: [], - gateways: [] + gateways: [], + pcscf: [], + mtu: null }; this.state = NETWORK_STATE_UNKNOWN; this.requestedNetworkIfaces = []; @@ -1026,6 +1030,10 @@ DataCall.prototype = { } } + if (aCurrentDataCall.mtu != aUpdatedDataCall.mtu) { + return "changed"; + } + return "identical"; }, @@ -1091,6 +1099,8 @@ DataCall.prototype = { this.linkInfo.addresses = aDataCall.addresses ? aDataCall.addresses.split(" ") : []; this.linkInfo.gateways = aDataCall.gateways ? aDataCall.gateways.split(" ") : []; this.linkInfo.dnses = aDataCall.dnses ? aDataCall.dnses.split(" ") : []; + this.linkInfo.pcscf = aDataCall.pcscf ? aDataCall.pcscf.split(" ") : []; + this.linkInfo.mtu = aDataCall.mtu > 0 ? aDataCall.mtu : 0; this.state = this._getGeckoDataCallState(aDataCall); // Notify DataCallHandler about data call connected. @@ -1143,7 +1153,9 @@ DataCall.prototype = { ifname: aUpdatedDataCall.ifname, addresses: aUpdatedDataCall.addresses ? aUpdatedDataCall.addresses.split(" ") : [], dnses: aUpdatedDataCall.dnses ? aUpdatedDataCall.dnses.split(" ") : [], - gateways: aUpdatedDataCall.gateways ? aUpdatedDataCall.gateways.split(" ") : [] + gateways: aUpdatedDataCall.gateways ? aUpdatedDataCall.gateways.split(" ") : [], + pcscf: aUpdatedDataCall.pcscf ? aUpdatedDataCall.pcscf.split(" ") : [], + mtu: aUpdatedDataCall.mtu > 0 ? aUpdatedDataCall.mtu : 0 }; switch (dataCallState) { @@ -1169,6 +1181,8 @@ DataCall.prototype = { this.linkInfo.addresses = newLinkInfo.addresses.slice(); this.linkInfo.gateways = newLinkInfo.gateways.slice(); this.linkInfo.dnses = newLinkInfo.dnses.slice(); + this.linkInfo.pcscf = newLinkInfo.pcscf.slice(); + this.linkInfo.mtu = newLinkInfo.mtu; } break; case NETWORK_STATE_DISCONNECTED: @@ -1267,6 +1281,8 @@ DataCall.prototype = { this.linkInfo.addresses = []; this.linkInfo.dnses = []; this.linkInfo.gateways = []; + this.linkInfo.pcscf = []; + this.linkInfo.mtu = null; }, reset: function() { @@ -1604,6 +1620,20 @@ RILNetworkInfo.prototype = { // See http://www.iana.org/assignments/port-numbers return this.getApnSetting().mmsport || -1; }, + + getPcscf: function(aCount) { + if (this.type != NETWORK_TYPE_MOBILE_IMS) { + if (DEBUG) this.debug("Error! Only IMS network can get pcscf."); + throw Cr.NS_ERROR_UNEXPECTED; + } + + let linkInfo = this.getDataCall().linkInfo; + + if (aCount) { + aCount.value = linkInfo.pcscf.length; + } + return linkInfo.pcscf.slice(); + }, }; function RILNetworkInterface(aDataCallHandler, aType, aApnSetting, aDataCall) { @@ -1647,6 +1677,11 @@ RILNetworkInterface.prototype = { return this.apnSetting.port || ""; }, + get mtu() { + // Value provided by network has higher priority than apn settings. + return this.dataCall.linkInfo.mtu || this.apnSetting.mtu || -1; + }, + // Helpers debug: function(aMsg) { diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 5581b7d626..58292b5010 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -30,26 +30,11 @@ XPCOMUtils.defineLazyGetter(this, "RIL", function () { return obj; }); -// Ril quirk to attach data registration on demand. -let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND = - libcutils.property_get("ro.moz.ril.data_reg_on_demand", "false") == "true"; - -// Ril quirk to control the uicc/data subscription. -let RILQUIRKS_SUBSCRIPTION_CONTROL = - libcutils.property_get("ro.moz.ril.subscription_control", "false") == "true"; - // Ril quirk to always turn the radio off for the client without SIM card // except hw default client. var RILQUIRKS_RADIO_OFF_WO_CARD = libcutils.property_get("ro.moz.ril.radio_off_wo_card", "false") == "true"; -// Ril quirk to enable IPv6 protocol/roaming protocol in APN settings. -let RILQUIRKS_HAVE_IPV6 = - libcutils.property_get("ro.moz.ril.ipv6", "false") == "true"; - -let RILQUIRKS_SIGNAL_EXTRA_INT32 = - libcutils.property_get("ro.moz.ril.signal_extra_int", "false") == "true"; - const RADIOINTERFACELAYER_CID = Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}"); const RADIOINTERFACE_CID = @@ -407,7 +392,9 @@ DataCall.prototype = { ifname: null, addreses: null, dnses: null, - gateways: null + gateways: null, + pcscf: null, + mtu: -1 }; function RadioInterfaceLayer() { @@ -522,23 +509,26 @@ WorkerMessenger.prototype = { quirks: { callstateExtraUint32: libcutils.property_get("ro.moz.ril.callstate_extra_int", "false") === "true", - v5Legacy: - libcutils.property_get("ro.moz.ril.v5_legacy", "true") === "true", requestUseDialEmergencyCall: libcutils.property_get("ro.moz.ril.dial_emergency_call", "false") === "true", simAppStateExtraFields: libcutils.property_get("ro.moz.ril.simstate_extra_field", "false") === "true", extraUint2ndCall: - libcutils.property_get("ro.moz.ril.extra_int_2nd_call", "false") == "true", + libcutils.property_get("ro.moz.ril.extra_int_2nd_call", "false") === "true", haveQueryIccLockRetryCount: - libcutils.property_get("ro.moz.ril.query_icc_count", "false") == "true", + libcutils.property_get("ro.moz.ril.query_icc_count", "false") === "true", sendStkProfileDownload: - libcutils.property_get("ro.moz.ril.send_stk_profile_dl", "false") == "true", + libcutils.property_get("ro.moz.ril.send_stk_profile_dl", "false") === "true", smscAddressFormat: libcutils.property_get("ro.moz.ril.smsc_address_format", "text"), - dataRegistrationOnDemand: RILQUIRKS_DATA_REGISTRATION_ON_DEMAND, - subscriptionControl: RILQUIRKS_SUBSCRIPTION_CONTROL, - signalExtraInt: RILQUIRKS_SIGNAL_EXTRA_INT32 + dataRegistrationOnDemand: + libcutils.property_get("ro.moz.ril.data_reg_on_demand", "false") === "true", + subscriptionControl: + libcutils.property_get("ro.moz.ril.subscription_control", "false") === "true", + signalExtraInt: + libcutils.property_get("ro.moz.ril.signal_extra_int", "false") === "true", + availableNetworkExtraStr: + libcutils.property_get("ro.moz.ril.avlbl_nw_extra_str", "false") === "true", } }; @@ -778,6 +768,13 @@ RadioInterface.prototype = { case "otastatuschange": gMobileConnectionService.notifyOtaStatusChanged(this.clientId, message.status); break; + case "deviceidentitieschange": + gMobileConnectionService.notifyDeviceIdentitiesChanged(this.clientId, + message.deviceIdentities.imei, + message.deviceIdentities.imeisv, + message.deviceIdentities.esn, + message.deviceIdentities.meid); + break; case "radiostatechange": // gRadioEnabledController should know the radio state for each client, // so notify gRadioEnabledController here. diff --git a/dom/system/gonk/Volume.cpp b/dom/system/gonk/Volume.cpp index 772c16b099..1fd276eee8 100644 --- a/dom/system/gonk/Volume.cpp +++ b/dom/system/gonk/Volume.cpp @@ -112,11 +112,33 @@ Volume::Dump(const char* aLabel) const : (IsUnmounting() ? "y" : "n")); } +void +Volume::ResolveAndSetMountPoint(const nsCSubstring& aMountPoint) +{ + nsCString mountPoint(aMountPoint); + char realPathBuf[PATH_MAX]; + + // Call realpath so that we wind up with a path which is compatible with + // functions like nsVolumeService::GetVolumeByPath. + + if (realpath(mountPoint.get(), realPathBuf) < 0) { + // The path we were handed doesn't exist. Warn about it, but use it + // anyways assuming that the user knows what they're doing. + + ERR("ResolveAndSetMountPoint: realpath on '%s' failed: %d", + mountPoint.get(), errno); + mMountPoint = mountPoint; + } else { + mMountPoint = realPathBuf; + } + DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get()); +} + void Volume::SetFakeVolume(const nsACString& aMountPoint) { this->mMountLocked = false; this->mCanBeShared = false; - this->mMountPoint = aMountPoint; + ResolveAndSetMountPoint(aMountPoint); SetState(nsIVolume::STATE_MOUNTED); } @@ -386,8 +408,7 @@ Volume::SetMountPoint(const nsCSubstring& aMountPoint) if (mMountPoint.Equals(aMountPoint)) { return; } - mMountPoint = aMountPoint; - DBG("Volume %s: Setting mountpoint to '%s'", NameStr(), mMountPoint.get()); + ResolveAndSetMountPoint(aMountPoint); } void diff --git a/dom/system/gonk/Volume.h b/dom/system/gonk/Volume.h index 7ba303c781..821292a9a4 100644 --- a/dom/system/gonk/Volume.h +++ b/dom/system/gonk/Volume.h @@ -119,6 +119,8 @@ private: void SetMountPoint(const nsCSubstring& aMountPoint); void StartCommand(VolumeCommand* aCommand); + void ResolveAndSetMountPoint(const nsCSubstring& aMountPoint); + bool BoolConfigValue(const nsCString& aConfigValue, bool& aBoolValue); void SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue); diff --git a/dom/system/gonk/VolumeManager.cpp b/dom/system/gonk/VolumeManager.cpp index 4b99c3acd1..0a73531ff7 100644 --- a/dom/system/gonk/VolumeManager.cpp +++ b/dom/system/gonk/VolumeManager.cpp @@ -156,6 +156,27 @@ VolumeManager::FindAddVolumeByName(const nsCSubstring& aName) return vol.forget(); } +//static +bool +VolumeManager::RemoveVolumeByName(const nsCSubstring& aName) +{ + if (!sVolumeManager) { + return false; + } + VolumeArray::size_type numVolumes = NumVolumes(); + VolumeArray::index_type volIndex; + for (volIndex = 0; volIndex < numVolumes; volIndex++) { + RefPtr vol = GetVolume(volIndex); + if (vol->Name().Equals(aName)) { + sVolumeManager->mVolumeArray.RemoveElementAt(volIndex); + return true; + } + } + // No volume found. Return false to indicate this. + return false; +} + + //static void VolumeManager::InitConfig() { @@ -236,6 +257,17 @@ void VolumeManager::InitConfig() } continue; } + if (command.EqualsLiteral("ignore")) { + // This command is useful to remove volumes which are being tracked by + // vold, but for which we have no interest. + if (!tokenizer.hasMoreTokens()) { + ERR("No vol_name in %s line %d", filename, n); + continue; + } + nsCString volName(tokenizer.nextToken()); + RemoveVolumeByName(volName); + continue; + } ERR("Unrecognized command: '%s'", command.get()); } } diff --git a/dom/system/gonk/VolumeManager.h b/dom/system/gonk/VolumeManager.h index 882ae77c4f..7c0503389f 100644 --- a/dom/system/gonk/VolumeManager.h +++ b/dom/system/gonk/VolumeManager.h @@ -128,6 +128,7 @@ public: static already_AddRefed GetVolume(VolumeArray::index_type aIndex); static already_AddRefed FindVolumeByName(const nsCSubstring& aName); static already_AddRefed FindAddVolumeByName(const nsCSubstring& aName); + static bool RemoveVolumeByName(const nsCSubstring& aName); static void InitConfig(); static void PostCommand(VolumeCommand* aCommand); diff --git a/dom/system/gonk/android_audio/AudioSystem.h b/dom/system/gonk/android_audio/AudioSystem.h index 86cbb16da0..10402e9cf4 100644 --- a/dom/system/gonk/android_audio/AudioSystem.h +++ b/dom/system/gonk/android_audio/AudioSystem.h @@ -969,6 +969,9 @@ public: static status_t getStreamVolumeIndex(audio_stream_type_t stream, int *index); static uint32_t getStrategyForStream(stream_type stream); +#if ANDROID_VERSION >= 17 + static audio_devices_t getDevicesForStream(audio_stream_type_t stream); +#endif static audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc); static status_t registerEffect(effect_descriptor_t *desc, @@ -995,6 +998,23 @@ public: static bool isLinearPCM(uint32_t format); static bool isModeInCall(); +#if ANDROID_VERSION >= 21 + class AudioPortCallback : public RefBase + { + public: + + AudioPortCallback() {} + virtual ~AudioPortCallback() {} + + virtual void onAudioPortListUpdate() = 0; + virtual void onAudioPatchListUpdate() = 0; + virtual void onServiceDied() = 0; + + }; + + static void setAudioPortCallback(sp callBack); +#endif + private: class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient diff --git a/dom/system/gonk/nsIDataCallInterfaceService.idl b/dom/system/gonk/nsIDataCallInterfaceService.idl index fedf55c049..c387879fa0 100644 --- a/dom/system/gonk/nsIDataCallInterfaceService.idl +++ b/dom/system/gonk/nsIDataCallInterfaceService.idl @@ -4,7 +4,7 @@ #include "nsISupports.idl" -[scriptable, uuid(d27ce247-9c7c-4582-826b-125e8275e9c2)] +[scriptable, uuid(6b66446a-7000-438f-8e1b-b56b4cbf4fa9)] interface nsIDataCall : nsISupports { /** @@ -55,6 +55,17 @@ interface nsIDataCall : nsISupports * A space-delimited list of default gateway addresses. */ readonly attribute DOMString gateways; + + /** + * A space-delimited list of Proxy Call State Control Function addresses for + * IMS client. + */ + readonly attribute DOMString pcscf; + + /** + * MTU received from network, -1 if not set or invalid. + */ + readonly attribute long mtu; }; [scriptable, uuid(e119c54b-9354-4ad6-a1ee-18608bde9320)] diff --git a/dom/system/gonk/nsIDataCallManager.idl b/dom/system/gonk/nsIDataCallManager.idl index 780887600d..de8477801b 100644 --- a/dom/system/gonk/nsIDataCallManager.idl +++ b/dom/system/gonk/nsIDataCallManager.idl @@ -5,7 +5,7 @@ #include "nsISupports.idl" #include "nsINetworkInterface.idl" -[scriptable, uuid(501b7041-0754-4ddb-9174-946e2c2ebd83)] +[scriptable, uuid(b8bcd6aa-5b06-4362-a68c-317878429e51)] interface nsIRilNetworkInfo : nsINetworkInfo { readonly attribute unsigned long serviceId; @@ -15,6 +15,17 @@ interface nsIRilNetworkInfo : nsINetworkInfo readonly attribute DOMString mmsc; // Empty string if not set. readonly attribute DOMString mmsProxy; // Empty string if not set. readonly attribute long mmsPort; // -1 if not set. + + /** + * Get the list of pcscf addresses, could be IPv4 or IPv6. + * + * @param count + * The length of the list of pcscf addresses. + * + * @returns the list of pcscf addresses. + */ + void getPcscf([optional] out unsigned long count, + [array, size_is(count), retval] out wstring pcscf); }; [scriptable, function, uuid(cb2f0f5b-67f4-4c14-93e8-01e66b630464)] diff --git a/dom/system/gonk/nsVolume.cpp b/dom/system/gonk/nsVolume.cpp index 138e2236c8..efb28facd6 100644 --- a/dom/system/gonk/nsVolume.cpp +++ b/dom/system/gonk/nsVolume.cpp @@ -66,6 +66,22 @@ nsVolume::nsVolume(const Volume* aVolume) { } +nsVolume::nsVolume(const nsVolume* aVolume) + : mName(aVolume->mName), + mMountPoint(aVolume->mMountPoint), + mState(aVolume->mState), + mMountGeneration(aVolume->mMountGeneration), + mMountLocked(aVolume->mMountLocked), + mIsFake(aVolume->mIsFake), + mIsMediaPresent(aVolume->mIsMediaPresent), + mIsSharing(aVolume->mIsSharing), + mIsFormatting(aVolume->mIsFormatting), + mIsUnmounting(aVolume->mIsUnmounting), + mIsRemovable(aVolume->mIsRemovable), + mIsHotSwappable(aVolume->mIsHotSwappable) +{ +} + void nsVolume::Dump(const char* aLabel) const { LOG("%s: Volume: %s is %s and %s @ %s gen %d locked %d", @@ -333,40 +349,28 @@ nsVolume::LogState() const LOG("nsVolume: %s state %s", NameStr().get(), StateStr()); } -void nsVolume::Set(nsIVolume* aVolume) +void nsVolume::UpdateMountLock(nsVolume* aOldVolume) { MOZ_ASSERT(NS_IsMainThread()); - aVolume->GetName(mName); - aVolume->GetMountPoint(mMountPoint); - aVolume->GetState(&mState); - aVolume->GetIsFake(&mIsFake); - aVolume->GetIsMediaPresent(&mIsMediaPresent); - aVolume->GetIsSharing(&mIsSharing); - aVolume->GetIsFormatting(&mIsFormatting); - aVolume->GetIsUnmounting(&mIsUnmounting); - aVolume->GetIsRemovable(&mIsRemovable); - aVolume->GetIsHotSwappable(&mIsHotSwappable); - - int32_t volMountGeneration; - aVolume->GetMountGeneration(&volMountGeneration); - + bool oldMountLocked = aOldVolume ? aOldVolume->mMountLocked : false; if (mState != nsIVolume::STATE_MOUNTED) { // Since we're not in the mounted state, we need to // forgot whatever mount generation we may have had. mMountGeneration = -1; - return; - } - if (mMountGeneration == volMountGeneration) { - // No change in mount generation, nothing else to do + mMountLocked = oldMountLocked; return; } - mMountGeneration = volMountGeneration; + int32_t oldMountGeneration = aOldVolume ? aOldVolume->mMountGeneration : -1; + if (mMountGeneration == oldMountGeneration) { + // No change in mount generation, nothing else to do + mMountLocked = oldMountLocked; + return; + } if (!XRE_IsParentProcess()) { // Child processes just track the state, not maintain it. - aVolume->GetIsMountLocked(&mMountLocked); return; } diff --git a/dom/system/gonk/nsVolume.h b/dom/system/gonk/nsVolume.h index 3a07d1b003..88be425f6c 100644 --- a/dom/system/gonk/nsVolume.h +++ b/dom/system/gonk/nsVolume.h @@ -24,6 +24,9 @@ public: // This constructor is used by the UpdateVolumeRunnable constructor nsVolume(const Volume* aVolume); + // This constructor is used by nsVolumeService::SetFakeVolumeState + nsVolume(const nsVolume* aVolume); + // This constructor is used by ContentChild::RecvFileSystemUpdate which is // used to update the volume cache maintained in the child process. nsVolume(const nsAString& aName, const nsAString& aMountPoint, @@ -47,25 +50,8 @@ public: { } - // This constructor is used by nsVolumeService::FindAddVolumeByName, and - // will be followed shortly by a Set call. - nsVolume(const nsAString& aName) - : mName(aName), - mState(STATE_INIT), - mMountGeneration(-1), - mMountLocked(true), // Needs to agree with Volume::Volume - mIsFake(false), - mIsMediaPresent(false), - mIsSharing(false), - mIsFormatting(false), - mIsUnmounting(false), - mIsRemovable(false), - mIsHotSwappable(false) - { - } - bool Equals(nsIVolume* aVolume); - void Set(nsIVolume* aVolume); + void UpdateMountLock(nsVolume* aOldVolume); void LogState() const; diff --git a/dom/system/gonk/nsVolumeService.cpp b/dom/system/gonk/nsVolumeService.cpp index a0a3e412e3..6fa93057c1 100644 --- a/dom/system/gonk/nsVolumeService.cpp +++ b/dom/system/gonk/nsVolumeService.cpp @@ -368,7 +368,7 @@ nsVolumeService::FindVolumeByMountLockName(const nsAString& aMountLockName) } already_AddRefed -nsVolumeService::FindVolumeByName(const nsAString& aName) +nsVolumeService::FindVolumeByName(const nsAString& aName, nsVolume::Array::index_type* aIndex) { mArrayMonitor.AssertCurrentThreadOwns(); @@ -377,52 +377,35 @@ nsVolumeService::FindVolumeByName(const nsAString& aName) for (volIndex = 0; volIndex < numVolumes; volIndex++) { RefPtr vol = mVolumeArray[volIndex]; if (vol->Name().Equals(aName)) { + if (aIndex) { + *aIndex = volIndex; + } return vol.forget(); } } return nullptr; } -//static -already_AddRefed -nsVolumeService::CreateOrFindVolumeByName(const nsAString& aName, bool aIsFake /*= false*/) -{ - MonitorAutoLock autoLock(mArrayMonitor); - - RefPtr vol; - vol = FindVolumeByName(aName); - if (vol) { - return vol.forget(); - } - // Volume not found - add a new one - vol = new nsVolume(aName); - vol->SetIsFake(aIsFake); - mVolumeArray.AppendElement(vol); - return vol.forget(); -} - void -nsVolumeService::UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers) +nsVolumeService::UpdateVolume(nsVolume* aVolume, bool aNotifyObservers) { MOZ_ASSERT(NS_IsMainThread()); - nsString volName; - aVolume->GetName(volName); - bool aIsFake; - aVolume->GetIsFake(&aIsFake); - RefPtr vol = CreateOrFindVolumeByName(volName, aIsFake); - if (vol->Equals(aVolume)) { - // Nothing has really changed. Don't bother telling anybody. - return; + { + MonitorAutoLock autoLock(mArrayMonitor); + nsVolume::Array::index_type volIndex; + RefPtr vol = FindVolumeByName(aVolume->Name(), &volIndex); + if (!vol) { + mVolumeArray.AppendElement(aVolume); + } else if (vol->Equals(aVolume) || (!vol->IsFake() && aVolume->IsFake())) { + // Ignore if nothing changed or if a fake tries to override a real volume. + return; + } else { + mVolumeArray.ReplaceElementAt(volIndex, aVolume); + } + aVolume->UpdateMountLock(vol); } - if (!vol->IsFake() && aIsFake) { - // Prevent an incoming fake volume from overriding an existing real volume. - return; - } - - vol->Set(aVolume); - if (!aNotifyObservers) { return; } @@ -431,8 +414,8 @@ nsVolumeService::UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers) if (!obs) { return; } - NS_ConvertUTF8toUTF16 stateStr(vol->StateStr()); - obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get()); + NS_ConvertUTF8toUTF16 stateStr(aVolume->StateStr()); + obs->NotifyObservers(aVolume, NS_VOLUME_STATE_CHANGED, stateStr.get()); } NS_IMETHODIMP @@ -471,11 +454,8 @@ nsVolumeService::SetFakeVolumeState(const nsAString& name, int32_t state) return NS_ERROR_NOT_AVAILABLE; } - // UpdateVolume expects the volume passed in to NOT be the - // same pointer as what CreateOrFindVolumeByName would return, - // which is why we allocate a temporary volume here. - RefPtr volume = new nsVolume(name); - volume->Set(vol); + // Clone the existing volume so we can replace it + RefPtr volume = new nsVolume(vol); volume->SetState(state); volume->LogState(); UpdateVolume(volume.get()); @@ -502,15 +482,15 @@ nsVolumeService::RemoveFakeVolume(const nsAString& name) void nsVolumeService::RemoveVolumeByName(const nsAString& aName) { - RefPtr vol; { MonitorAutoLock autoLock(mArrayMonitor); - vol = FindVolumeByName(aName); + nsVolume::Array::index_type volIndex; + RefPtr vol = FindVolumeByName(aName, &volIndex); + if (!vol) { + return; + } + mVolumeArray.RemoveElementAt(volIndex); } - if (!vol) { - return; - } - mVolumeArray.RemoveElement(vol); if (XRE_IsParentProcess()) { nsCOMPtr obs = GetObserverService(); diff --git a/dom/system/gonk/nsVolumeService.h b/dom/system/gonk/nsVolumeService.h index 9c1f11cc48..9bddc0b8fe 100644 --- a/dom/system/gonk/nsVolumeService.h +++ b/dom/system/gonk/nsVolumeService.h @@ -47,7 +47,7 @@ public: void DumpNoLock(const char* aLabel); // To use this function, you have to create a new volume and pass it in. - void UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers = true); + void UpdateVolume(nsVolume* aVolume, bool aNotifyObservers = true); void UpdateVolumeIOThread(const Volume* aVolume); void RecvVolumesFromParent(const nsTArray& aVolumes); @@ -61,8 +61,9 @@ private: void CheckMountLock(const nsAString& aMountLockName, const nsAString& aMountLockState); already_AddRefed FindVolumeByMountLockName(const nsAString& aMountLockName); - already_AddRefed FindVolumeByName(const nsAString& aName); - already_AddRefed CreateOrFindVolumeByName(const nsAString& aName, bool aIsFake = false); + + already_AddRefed FindVolumeByName(const nsAString& aName, + nsVolume::Array::index_type* aIndex = nullptr); Monitor mArrayMonitor; nsVolume::Array mVolumeArray; diff --git a/dom/system/gonk/ril_consts.js b/dom/system/gonk/ril_consts.js index 85d2e70d49..e3d46b6f65 100644 --- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -278,17 +278,7 @@ this.SMS_RETRY_MAX = 3; this.RADIO_STATE_OFF = 0; this.RADIO_STATE_UNAVAILABLE = 1; -this.RADIO_STATE_ON = 10; // RIL v7 - -// RIL v5 legacy constants: -this.RADIO_STATE_SIM_NOT_READY = 2; -this.RADIO_STATE_SIM_LOCKED_OR_ABSENT = 3; -this.RADIO_STATE_SIM_READY = 4; -this.RADIO_STATE_RUIM_NOT_READY = 5; -this.RADIO_STATE_RUIM_READY = 6; -this.RADIO_STATE_RUIM_LOCKED_OR_ABSENT = 7; -this.RADIO_STATE_NV_NOT_READY = 8; -this.RADIO_STATE_NV_READY = 9; +this.RADIO_STATE_ON = 10; // since RIL v7 this.CARD_STATE_ABSENT = 0; this.CARD_STATE_PRESENT = 1; @@ -582,6 +572,12 @@ this.ADN_MAX_BCD_NUMBER_BYTES = 11; // Maximum digits of the Dialling Number in ADN. // See TS 151.011 clause 10.5.1 EF_ADN, 'Dialling Number'. this.ADN_MAX_NUMBER_DIGITS = 20; +// Maximum size of BCD numbers in EXT. +// See TS 151.011 clause 10.5.10 EF_EXT1, 'Extension data'. +this.EXT_MAX_BCD_NUMBER_BYTES = 10; +// Maximum digits of the Dialling Number in EXT. +// See TS 151.011 clause 10.5.10 EF_EXT1, 'Extension data'. +this.EXT_MAX_NUMBER_DIGITS = 20; // READ_RECORD mode, TS 102.221 this.READ_RECORD_ABSOLUTE_MODE = 4; @@ -1270,10 +1266,13 @@ this.GECKO_ICC_SERVICES = { FDN: 3, PLMNSEL: 7, MSISDN: 9, + EXT1: 10, + EXT2: 11, CBMI: 14, GID1: 15, SPN: 17, SDN: 18, + EXT3: 19, DATA_DOWNLOAD_SMS_CB: 25, DATA_DOWNLOAD_SMS_PP: 26, CBMIR: 30, @@ -1288,7 +1287,9 @@ this.GECKO_ICC_SERVICES = { // @see 3GPP TS 31.102 4.2.8 (USIM). usim: { FDN: 2, + EXT2: 3, SDN: 4, + EXT3: 5, BDN: 6, CBMI: 15, CBMIR: 16, @@ -1308,8 +1309,11 @@ this.GECKO_ICC_SERVICES = { ruim: { FDN: 3, ENHANCED_PHONEBOOK: 6, + EXT1: 10, + EXT2: 11, SPN: 17, - SDN: 18 + SDN: 18, + EXT3: 19, }, // @see B.3.1.1 CPHS Information in CPHS Phase 2: // Indicates which of the CPHS 'optional' data-fields are present in the SIM card: @@ -1419,7 +1423,8 @@ this.CALLED_PARTY_BCD_NPI_PRIVATE = 9; /** * Array of number plan identification values which can be used to map an - * enumeration to the corresponding value. + * enumeration to the corresponding value. The indices should be consistent + * with nsISmsService::NUMBER_PLAN_IDENTIFICATION_* constants. */ this.CALLED_PARTY_BCD_NPI = [ CALLED_PARTY_BCD_NPI_UNKNOWN, @@ -2970,6 +2975,9 @@ this.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL = 1; this.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME = 2; this.CALL_BARRING_PROGRAM_ALL_INCOMING = 3; this.CALL_BARRING_PROGRAM_INCOMING_ROAMING = 4; +this.CALL_BARRING_PROGRAM_ALL_SERVICE = 5; +this.CALL_BARRING_PROGRAM_OUTGOING_SERVICE = 6; +this.CALL_BARRING_PROGRAM_INCOMING_SERVICE = 7; this.CALL_BARRING_PROGRAM_TO_FACILITY = {}; CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_ALL_OUTGOING] = ICC_CB_FACILITY_BAOC; @@ -2977,152 +2985,10 @@ CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL] = CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME] = ICC_CB_FACILITY_BAOICxH; CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_ALL_INCOMING] = ICC_CB_FACILITY_BAIC; CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_INCOMING_ROAMING] = ICC_CB_FACILITY_BAICr; +CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_ALL_SERVICE] = ICC_CB_FACILITY_BA_ALL; +CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_OUTGOING_SERVICE] = ICC_CB_FACILITY_BA_MO; +CALL_BARRING_PROGRAM_TO_FACILITY[CALL_BARRING_PROGRAM_INCOMING_SERVICE] = ICC_CB_FACILITY_BA_MT; -// CLIR constants. Must be in sync with nsIMobileConnectionService interface -this.CLIR_DEFAULT = 0; -this.CLIR_INVOCATION = 1; -this.CLIR_SUPPRESSION = 2; - -// MMI procedure as defined in TS.22.030 6.5.2 -this.MMI_PROCEDURE_ACTIVATION = "*"; -this.MMI_PROCEDURE_DEACTIVATION = "#"; -this.MMI_PROCEDURE_INTERROGATION = "*#"; -this.MMI_PROCEDURE_REGISTRATION = "**"; -this.MMI_PROCEDURE_ERASURE = "##"; - -this.MMI_PROC_TO_CF_ACTION = {}; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ACTIVATION] = CALL_FORWARD_ACTION_ENABLE; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_DEACTIVATION] = CALL_FORWARD_ACTION_DISABLE; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_INTERROGATION] = CALL_FORWARD_ACTION_QUERY_STATUS; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_REGISTRATION] = CALL_FORWARD_ACTION_REGISTRATION; -MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ERASURE] = CALL_FORWARD_ACTION_ERASURE; - -// MMI call forwarding service codes as defined in TS.22.030 Annex B -this.MMI_SC_CFU = "21"; -this.MMI_SC_CF_BUSY = "67"; -this.MMI_SC_CF_NO_REPLY = "61"; -this.MMI_SC_CF_NOT_REACHABLE = "62"; -this.MMI_SC_CF_ALL = "002"; -this.MMI_SC_CF_ALL_CONDITIONAL = "004"; - -this.MMI_SC_TO_CF_REASON = {}; -MMI_SC_TO_CF_REASON[MMI_SC_CFU] = CALL_FORWARD_REASON_UNCONDITIONAL; -MMI_SC_TO_CF_REASON[MMI_SC_CF_BUSY] = CALL_FORWARD_REASON_MOBILE_BUSY; -MMI_SC_TO_CF_REASON[MMI_SC_CF_NO_REPLY] = CALL_FORWARD_REASON_NO_REPLY; -MMI_SC_TO_CF_REASON[MMI_SC_CF_NOT_REACHABLE] = CALL_FORWARD_REASON_NOT_REACHABLE; -MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL] = CALL_FORWARD_REASON_ALL_CALL_FORWARDING; -MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL_CONDITIONAL] = CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING; - -// MMI service codes for PIN/PIN2/PUK/PUK2 management as defined in TS.22.030 -// sec 6.6 -this.MMI_SC_PIN = "04"; -this.MMI_SC_PIN2 = "042"; -this.MMI_SC_PUK = "05"; -this.MMI_SC_PUK2 = "052"; - -// MMI service code for IMEI presentation as defined in TS.22.030 sec 6.7 -this.MMI_SC_IMEI = "06"; - -// MMI called line presentation service codes -this.MMI_SC_CLIP = "30"; -this.MMI_SC_CLIR = "31"; - -// MMI call waiting service code -this.MMI_SC_CALL_WAITING = "43"; - -// MMI service code for registration new password as defined in TS 22.030 6.5.4 -this.MMI_SC_CHANGE_PASSWORD = "03"; -this.MMI_ZZ_BARRING_SERVICE = "330"; - -// MMI call barring service codes -this.MMI_SC_BAOC = "33"; -this.MMI_SC_BAOIC = "331"; -this.MMI_SC_BAOICxH = "332"; -this.MMI_SC_BAIC = "35"; -this.MMI_SC_BAICr = "351"; -this.MMI_SC_BA_ALL = "330"; -this.MMI_SC_BA_MO = "333"; -this.MMI_SC_BA_MT = "353"; - -this.MMI_SC_TO_CB_FACILITY = {}; - -MMI_SC_TO_CB_FACILITY[MMI_SC_BAOC] = ICC_CB_FACILITY_BAOC; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAOIC] = ICC_CB_FACILITY_BAOIC; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAOICxH] = ICC_CB_FACILITY_BAOICxH; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAIC] = ICC_CB_FACILITY_BAIC; -MMI_SC_TO_CB_FACILITY[MMI_SC_BAICr] = ICC_CB_FACILITY_BAICr; -MMI_SC_TO_CB_FACILITY[MMI_SC_BA_ALL] = ICC_CB_FACILITY_BA_ALL; -MMI_SC_TO_CB_FACILITY[MMI_SC_BA_MO] = ICC_CB_FACILITY_BA_MO; -MMI_SC_TO_CB_FACILITY[MMI_SC_BA_MT] = ICC_CB_FACILITY_BA_MT; - -// MMI service code key strings. -this.MMI_KS_SC_CALL_BARRING = "scCallBarring"; -this.MMI_KS_SC_CALL_FORWARDING = "scCallForwarding"; -this.MMI_KS_SC_CLIP = "scClip"; -this.MMI_KS_SC_CLIR = "scClir"; -this.MMI_KS_SC_PWD = "scPwd"; -this.MMI_KS_SC_CALL_WAITING = "scCallWaiting"; -this.MMI_KS_SC_PIN = "scPin"; -this.MMI_KS_SC_PIN2 = "scPin2"; -this.MMI_KS_SC_PUK = "scPuk"; -this.MMI_KS_SC_PUK2 = "scPuk2"; -this.MMI_KS_SC_CHANGE_PASSWORD = "scChangePassword"; -this.MMI_KS_SC_IMEI = "scImei"; -this.MMI_KS_SC_USSD = "scUssd"; -this.MMI_KS_SC_CALL = "scCall"; - -// MMI error messages key strings. -this.MMI_ERROR_KS_ERROR = "emMmiError"; -this.MMI_ERROR_KS_NOT_SUPPORTED = "emMmiErrorNotSupported"; -this.MMI_ERROR_KS_INVALID_ACTION = "emMmiErrorInvalidAction"; -this.MMI_ERROR_KS_MISMATCH_PIN = "emMmiErrorMismatchPin"; -this.MMI_ERROR_KS_MISMATCH_PASSWORD = "emMmiErrorMismatchPassword"; -this.MMI_ERROR_KS_BAD_PIN = "emMmiErrorBadPin"; -this.MMI_ERROR_KS_BAD_PUK = "emMmiErrorBadPuk"; -this.MMI_ERROR_KS_INVALID_PIN = "emMmiErrorInvalidPin"; -this.MMI_ERROR_KS_INVALID_PASSWORD = "emMmiErrorInvalidPassword"; -this.MMI_ERROR_KS_NEEDS_PUK = "emMmiErrorNeedsPuk"; -this.MMI_ERROR_KS_SIM_BLOCKED = "emMmiErrorSimBlocked"; - -// MMI status message. -this.MMI_SM_KS_PASSWORD_CHANGED = "smPasswordChanged"; -this.MMI_SM_KS_PIN_CHANGED = "smPinChanged"; -this.MMI_SM_KS_PIN2_CHANGED = "smPin2Changed"; -this.MMI_SM_KS_PIN_UNBLOCKED = "smPinUnblocked"; -this.MMI_SM_KS_PIN2_UNBLOCKED = "smPin2Unblocked"; -this.MMI_SM_KS_SERVICE_ENABLED = "smServiceEnabled"; -this.MMI_SM_KS_SERVICE_ENABLED_FOR = "smServiceEnabledFor"; -this.MMI_SM_KS_SERVICE_DISABLED = "smServiceDisabled"; -this.MMI_SM_KS_SERVICE_REGISTERED = "smServiceRegistered"; -this.MMI_SM_KS_SERVICE_ERASED = "smServiceErased"; -this.MMI_SM_KS_SERVICE_INTERROGATED = "smServiceInterrogated"; -this.MMI_SM_KS_SERVICE_NOT_PROVISIONED = "smServiceNotProvisioned"; -this.MMI_SM_KS_CLIR_PERMANENT = "smClirPermanent"; -this.MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON = "smClirDefaultOnNextCallOn"; -this.MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF = "smClirDefaultOnNextCallOff"; -this.MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON = "smClirDefaultOffNextCallOn"; -this.MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF = "smClirDefaultOffNextCallOff"; -this.MMI_SM_KS_CALL_CONTROL = "smCallControl"; - -// MMI Service class -this.MMI_KS_SERVICE_CLASS_VOICE = "serviceClassVoice"; -this.MMI_KS_SERVICE_CLASS_DATA = "serviceClassData"; -this.MMI_KS_SERVICE_CLASS_FAX = "serviceClassFax"; -this.MMI_KS_SERVICE_CLASS_SMS = "serviceClassSms"; -this.MMI_KS_SERVICE_CLASS_DATA_SYNC = "serviceClassDataSync"; -this.MMI_KS_SERVICE_CLASS_DATA_ASYNC = "serviceClassDataAsync"; -this.MMI_KS_SERVICE_CLASS_PACKET = "serviceClassPacket"; -this.MMI_KS_SERVICE_CLASS_PAD = "serviceClassPad"; - -this.MMI_KS_SERVICE_CLASS_MAPPING = {}; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_VOICE] = MMI_KS_SERVICE_CLASS_VOICE; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_DATA] = MMI_KS_SERVICE_CLASS_DATA; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_FAX] = MMI_KS_SERVICE_CLASS_FAX; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_SMS] = MMI_KS_SERVICE_CLASS_SMS; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_DATA_SYNC] = MMI_KS_SERVICE_CLASS_DATA_SYNC; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_DATA_ASYNC] = MMI_KS_SERVICE_CLASS_DATA_ASYNC; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_PACKET] = MMI_KS_SERVICE_CLASS_PACKET; -MMI_KS_SERVICE_CLASS_MAPPING[ICC_SERVICE_CLASS_PAD] = MMI_KS_SERVICE_CLASS_PAD; /** * CDMA PDU constants */ diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index afc9fda45a..aa66b50560 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -64,30 +64,23 @@ const ICC_MAX_LINEAR_FIXED_RECORDS = 0xfe; const GET_CURRENT_CALLS_RETRY_MAX = 3; -let RILQUIRKS_CALLSTATE_EXTRA_UINT32; -// This may change at runtime since in RIL v6 and later, we get the version -// number via the UNSOLICITED_RIL_CONNECTED parcel. -let RILQUIRKS_V5_LEGACY; -let RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL; -let RILQUIRKS_SIM_APP_STATE_EXTRA_FIELDS; +var RILQUIRKS_CALLSTATE_EXTRA_UINT32; +var RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL; +var RILQUIRKS_SIM_APP_STATE_EXTRA_FIELDS; +var RILQUIRKS_SIGNAL_EXTRA_INT32; +var RILQUIRKS_AVAILABLE_NETWORKS_EXTRA_STRING; // Needed for call-waiting on Peak device -let RILQUIRKS_EXTRA_UINT32_2ND_CALL; +var RILQUIRKS_EXTRA_UINT32_2ND_CALL; // On the emulator we support querying the number of lock retries -let RILQUIRKS_HAVE_QUERY_ICC_LOCK_RETRY_COUNT; - +var RILQUIRKS_HAVE_QUERY_ICC_LOCK_RETRY_COUNT; // Ril quirk to Send STK Profile Download -let RILQUIRKS_SEND_STK_PROFILE_DOWNLOAD; - +var RILQUIRKS_SEND_STK_PROFILE_DOWNLOAD; // Ril quirk to attach data registration on demand. -let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND; - +var RILQUIRKS_DATA_REGISTRATION_ON_DEMAND; // Ril quirk to control the uicc/data subscription. -let RILQUIRKS_SUBSCRIPTION_CONTROL; - -let RILQUIRKS_SIGNAL_EXTRA_INT32; - +var RILQUIRKS_SUBSCRIPTION_CONTROL; // Ril quirk to describe the SMSC address format. -let RILQUIRKS_SMSC_ADDRESS_FORMAT; +var RILQUIRKS_SMSC_ADDRESS_FORMAT; /** * The RIL state machine. @@ -105,9 +98,6 @@ function RilObject(aContext) { this.pendingNetworkType = {}; this._receivedSmsCbPagesMap = {}; this._getCurrentCallsRetryCount = 0; - - // Init properties that are only initialized once. - this.v5Legacy = RILQUIRKS_V5_LEGACY; } RilObject.prototype = { context: null, @@ -116,7 +106,6 @@ RilObject.prototype = { * RIL version. */ version: null, - v5Legacy: null, /** * Call state of current conference group. @@ -175,13 +164,9 @@ RilObject.prototype = { this.cardState = GECKO_CARDSTATE_UNINITIALIZED; /** - * Strings + * Device Identities including IMEI, IMEISV, ESN and MEID. */ - this.IMEI = null; - this.IMEISV = null; - this.ESN = null; - this.MEID = null; - this.SMSC = null; + this.deviceIdentities = null; /** * ICC information that is not exposed to Gaia. @@ -251,16 +236,6 @@ RilObject.prototype = { */ this._pendingNetworkInfo = {rilMessageType: "networkinfochanged"}; - /** - * USSD session flag. - * Only one USSD session may exist at a time, and the session is assumed - * to exist until: - * a) There's a call to cancelUSSD() - * b) The implementation sends a UNSOLICITED_ON_USSD with a type code - * of "0" (USSD-Notify/no further action) or "2" (session terminated) - */ - this._ussdSession = null; - /** * Cell Broadcast Search Lists. */ @@ -377,11 +352,9 @@ RilObject.prototype = { enterICCPIN: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_ENTER_SIM_PIN, options); - Buf.writeInt32(this.v5Legacy ? 1 : 2); + Buf.writeInt32(2); Buf.writeString(options.password); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } + Buf.writeString(options.aid || this.aid); Buf.sendParcel(); }, @@ -396,11 +369,9 @@ RilObject.prototype = { enterICCPIN2: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_ENTER_SIM_PIN2, options); - Buf.writeInt32(this.v5Legacy ? 1 : 2); + Buf.writeInt32(2); Buf.writeString(options.password); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } + Buf.writeString(options.aid || this.aid); Buf.sendParcel(); }, @@ -433,12 +404,10 @@ RilObject.prototype = { changeICCPIN: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_CHANGE_SIM_PIN, options); - Buf.writeInt32(this.v5Legacy ? 2 : 3); + Buf.writeInt32(3); Buf.writeString(options.password); Buf.writeString(options.newPassword); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } + Buf.writeString(options.aid || this.aid); Buf.sendParcel(); }, @@ -455,12 +424,10 @@ RilObject.prototype = { changeICCPIN2: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_CHANGE_SIM_PIN2, options); - Buf.writeInt32(this.v5Legacy ? 2 : 3); + Buf.writeInt32(3); Buf.writeString(options.password); Buf.writeString(options.newPassword); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } + Buf.writeString(options.aid || this.aid); Buf.sendParcel(); }, @@ -474,17 +441,15 @@ RilObject.prototype = { * @param [optional] aid * AID value. */ - enterICCPUK: function(options) { - let Buf = this.context.Buf; - Buf.newParcel(REQUEST_ENTER_SIM_PUK, options); - Buf.writeInt32(this.v5Legacy ? 2 : 3); - Buf.writeString(options.password); - Buf.writeString(options.newPin); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } - Buf.sendParcel(); - }, + enterICCPUK: function(options) { + let Buf = this.context.Buf; + Buf.newParcel(REQUEST_ENTER_SIM_PUK, options); + Buf.writeInt32(3); + Buf.writeString(options.password); + Buf.writeString(options.newPin); + Buf.writeString(options.aid || this.aid); + Buf.sendParcel(); + }, /** * Supplies ICC PUK2 and a new PIN2 to unlock the ICC. @@ -496,17 +461,15 @@ RilObject.prototype = { * @param [optional] aid * AID value. */ - enterICCPUK2: function(options) { - let Buf = this.context.Buf; - Buf.newParcel(REQUEST_ENTER_SIM_PUK2, options); - Buf.writeInt32(this.v5Legacy ? 2 : 3); - Buf.writeString(options.password); - Buf.writeString(options.newPin); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } - Buf.sendParcel(); - }, + enterICCPUK2: function(options) { + let Buf = this.context.Buf; + Buf.newParcel(REQUEST_ENTER_SIM_PUK2, options); + Buf.writeInt32(3); + Buf.writeString(options.password); + Buf.writeString(options.newPin); + Buf.writeString(options.aid || this.aid); + Buf.sendParcel(); + }, /** * Helper function for changing ICC locks. @@ -635,13 +598,11 @@ RilObject.prototype = { queryICCFacilityLock: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_QUERY_FACILITY_LOCK, options); - Buf.writeInt32(this.v5Legacy ? 3 : 4); + Buf.writeInt32(4); Buf.writeString(options.facility); Buf.writeString(options.password); Buf.writeString(options.serviceClass.toString()); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } + Buf.writeString(options.aid || this.aid); Buf.sendParcel(); }, @@ -662,14 +623,12 @@ RilObject.prototype = { setICCFacilityLock: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_SET_FACILITY_LOCK, options); - Buf.writeInt32(this.v5Legacy ? 4 : 5); + Buf.writeInt32(5); Buf.writeString(options.facility); Buf.writeString(options.enabled ? "1" : "0"); Buf.writeString(options.password); Buf.writeString(options.serviceClass.toString()); - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } + Buf.writeString(options.aid || this.aid); Buf.sendParcel(); }, @@ -721,9 +680,7 @@ RilObject.prototype = { Buf.writeString(null); } - if (!this.v5Legacy) { - Buf.writeString(options.aid || this.aid); - } + Buf.writeString(options.aid || this.aid); Buf.sendParcel(); }, @@ -735,10 +692,6 @@ RilObject.prototype = { */ getIMSI: function(aid) { let Buf = this.context.Buf; - if (this.v5Legacy) { - Buf.simpleRequest(REQUEST_GET_IMSI); - return; - } Buf.newParcel(REQUEST_GET_IMSI); Buf.writeInt32(1); Buf.writeString(aid || this.aid); @@ -797,10 +750,11 @@ RilObject.prototype = { * @param requestId Request id from RadioInterfaceLayer. */ updateICCContact: function(options) { - let onsuccess = function onsuccess() { + let onsuccess = function onsuccess(updatedContact) { let recordIndex = - contact.pbrIndex * ICC_MAX_LINEAR_FIXED_RECORDS + contact.recordId; - contact.contactId = this.iccInfo.iccid + recordIndex; + updatedContact.pbrIndex * ICC_MAX_LINEAR_FIXED_RECORDS + updatedContact.recordId; + updatedContact.contactId = this.iccInfo.iccid + recordIndex; + options.contact = updatedContact; // Reuse 'options' to get 'requestId' and 'contactType'. this.sendChromeMessage(options); }.bind(this); @@ -898,60 +852,6 @@ RilObject.prototype = { Buf.sendParcel(); }, - /** - * Query call waiting status via MMI. - */ - _handleQueryMMICallWaiting: function(options) { - let Buf = this.context.Buf; - - function callback(options) { - options.length = Buf.readInt32(); - options.enabled = (Buf.readInt32() === 1); - let services = Buf.readInt32(); - if (options.enabled) { - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED_FOR; - let serviceClass = []; - for (let serviceClassMask = 1; - serviceClassMask <= ICC_SERVICE_CLASS_MAX; - serviceClassMask <<= 1) { - if ((serviceClassMask & services) !== 0) { - serviceClass.push(MMI_KS_SERVICE_CLASS_MAPPING[serviceClassMask]); - } - } - options.additionalInformation = serviceClass; - } else { - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - } - - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - this.sendChromeMessage(options); - } - - options.callback = callback; - this.queryCallWaiting(options); - }, - - /** - * Set call waiting status via MMI. - */ - _handleSetMMICallWaiting: function(options) { - function callback(options) { - if (options.enabled) { - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - } else { - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - } - - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - this.sendChromeMessage(options); - } - - options.callback = callback; - this.setCallWaiting(options); - }, - /** * Query call waiting status. * @@ -961,7 +861,7 @@ RilObject.prototype = { Buf.newParcel(REQUEST_QUERY_CALL_WAITING, options); Buf.writeInt32(1); // As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service - // class parameter in call waiting interrogation to network + // class parameter in call waiting interrogation to network. Buf.writeInt32(ICC_SERVICE_CLASS_NONE); Buf.sendParcel(); }, @@ -969,24 +869,22 @@ RilObject.prototype = { /** * Set call waiting status. * - * @param on + * @param enabled * Boolean indicating the desired waiting status. + * @param serviceClass + * One of ICC_SERVICE_CLASS_* constants. */ setCallWaiting: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_SET_CALL_WAITING, options); Buf.writeInt32(2); Buf.writeInt32(options.enabled ? 1 : 0); - Buf.writeInt32(options.serviceClass !== undefined ? - options.serviceClass : ICC_SERVICE_CLASS_VOICE); + Buf.writeInt32(options.serviceClass); Buf.sendParcel(); }, /** * Queries current CLIP status. - * - * (MMI request for code "*#30#") - * */ queryCLIP: function(options) { this.context.Buf.simpleRequest(REQUEST_QUERY_CLIP, options); @@ -1265,16 +1163,8 @@ RilObject.prototype = { this.context.Buf.simpleRequest(REQUEST_SIGNAL_STRENGTH); }, - getIMEI: function(options) { - this.context.Buf.simpleRequest(REQUEST_GET_IMEI, options); - }, - - getIMEISV: function() { - this.context.Buf.simpleRequest(REQUEST_GET_IMEISV); - }, - getDeviceIdentity: function() { - this.context.Buf.simpleRequest(REQUEST_DEVICE_IDENTITY); + this.deviceIdentities || this.context.Buf.simpleRequest(REQUEST_DEVICE_IDENTITY); }, getBasebandVersion: function() { @@ -1581,16 +1471,15 @@ RilObject.prototype = { try { let str = getSearchListStr(options.searchList); this.cellBroadcastConfigs.MMI = this._convertCellBroadcastSearchList(str); - options.success = true; } catch (e) { if (DEBUG) { this.context.debug("Invalid Cell Broadcast search list: " + e); } - options.success = false; + options.errorMsg = GECKO_ERROR_UNSPECIFIED_ERROR; } this.sendChromeMessage(options); - if (!options.success) { + if (options.errorMsg) { return; } @@ -1711,17 +1600,7 @@ RilObject.prototype = { * Get the Short Message Service Center address. */ getSmscAddress: function(options) { - if (!this.SMSC) { - this.context.Buf.simpleRequest(REQUEST_GET_SMSC_ADDRESS, options); - return; - } - - if (!options || options.rilMessageType !== "getSmscAddress") { - return; - } - - options.smscAddress = this.SMSC; - this.sendChromeMessage(options); + this.context.Buf.simpleRequest(REQUEST_GET_SMSC_ADDRESS, options); }, /** @@ -1733,8 +1612,8 @@ RilObject.prototype = { * Type of number in integer, as defined in * |Table 10.5.118: Called party BCD number| of 3GPP TS 24.008. * @param numberPlanIdentification - * Number plan identification in integer, as defined in - * |Table 10.5.118: Called party BCD number| of 3GPP TS 24.008. + * The index of number plan identification value in + * CALLED_PARTY_BCD_NPI array. */ setSmscAddress: function(options) { let ton = options.typeOfNumber; @@ -1763,7 +1642,6 @@ RilObject.prototype = { } // Init parcel. - this.SMSC = null; let Buf = this.context.Buf; Buf.newParcel(REQUEST_SET_SMSC_ADDRESS, options); @@ -1848,13 +1726,7 @@ RilObject.prototype = { // Otherwise, it must be + 2 // // See also bug 901232 and 867873 - let radioTech; - if (this.v5Legacy) { - radioTech = this._isCdma ? DATACALL_RADIOTECHNOLOGY_CDMA - : DATACALL_RADIOTECHNOLOGY_GSM; - } else { - radioTech = options.radioTech + 2; - } + let radioTech = options.radioTech + 2; let Buf = this.context.Buf; let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL, options); Buf.writeInt32(7); @@ -1927,328 +1799,13 @@ RilObject.prototype = { this.context.Buf.simpleRequest(REQUEST_LAST_CALL_FAIL_CAUSE, options); }, - sendMMI: function(options) { - if (DEBUG) { - this.context.debug("SendMMI " + JSON.stringify(options)); - } - - let _sendMMIError = (function(errorMsg) { - options.success = false; - options.errorMsg = errorMsg; - this.sendChromeMessage(options); - }).bind(this); - - // It's neither a valid mmi code nor an ongoing ussd. - let mmi = options.mmi; - if (!mmi && !this._ussdSession) { - _sendMMIError(MMI_ERROR_KS_ERROR); - return; - } - - function _isValidPINPUKRequest() { - // The only allowed MMI procedure for ICC PIN, PIN2, PUK and PUK2 handling - // is "Registration" (**). - if (mmi.procedure != MMI_PROCEDURE_REGISTRATION ) { - _sendMMIError(MMI_ERROR_KS_INVALID_ACTION); - return false; - } - - if (!mmi.sia || !mmi.sib || !mmi.sic) { - _sendMMIError(MMI_ERROR_KS_ERROR); - return false; - } - - if (mmi.sia.length < 4 || mmi.sia.length > 8 || - mmi.sib.length < 4 || mmi.sib.length > 8 || - mmi.sic.length < 4 || mmi.sic.length > 8) { - _sendMMIError(MMI_ERROR_KS_INVALID_PIN); - return false; - } - - if (mmi.sib != mmi.sic) { - _sendMMIError(MMI_ERROR_KS_MISMATCH_PIN); - return false; - } - - return true; - } - - function _isValidChangePasswordRequest() { - if (mmi.procedure !== MMI_PROCEDURE_REGISTRATION && - mmi.procedure !== MMI_PROCEDURE_ACTIVATION) { - _sendMMIError(MMI_ERROR_KS_INVALID_ACTION); - return false; - } - - if (mmi.sia !== "" && mmi.sia !== MMI_ZZ_BARRING_SERVICE) { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return false; - } - - let validPassword = si => /^[0-9]{4}$/.test(si); - if (!validPassword(mmi.sib) || !validPassword(mmi.sic) || - !validPassword(mmi.pwd)) { - _sendMMIError(MMI_ERROR_KS_INVALID_PASSWORD); - return false; - } - - if (mmi.sic != mmi.pwd) { - _sendMMIError(MMI_ERROR_KS_MISMATCH_PASSWORD); - return false; - } - - return true; - } - - let _isRadioAvailable = (function() { - if (this.radioState !== GECKO_RADIOSTATE_ENABLED) { - _sendMMIError(GECKO_ERROR_RADIO_NOT_AVAILABLE); - return false; - } - return true; - }).bind(this); - - // We check if the MMI service code is supported and in that case we - // trigger the appropriate RIL request if possible. - let sc = mmi.serviceCode; - switch (sc) { - // Call forwarding - case MMI_SC_CFU: - case MMI_SC_CF_BUSY: - case MMI_SC_CF_NO_REPLY: - case MMI_SC_CF_NOT_REACHABLE: - case MMI_SC_CF_ALL: - case MMI_SC_CF_ALL_CONDITIONAL: - if (!_isRadioAvailable()) { - return; - } - // Call forwarding requires at least an action, given by the MMI - // procedure, and a reason, given by the MMI service code, but there - // is no way that we get this far without a valid procedure or service - // code. - options.action = MMI_PROC_TO_CF_ACTION[mmi.procedure]; - options.reason = MMI_SC_TO_CF_REASON[sc]; - options.number = mmi.sia; - options.serviceClass = this._siToServiceClass(mmi.sib); - if (options.action == CALL_FORWARD_ACTION_QUERY_STATUS) { - this.queryCallForwardStatus(options); - return; - } - - options.isSetCallForward = true; - options.timeSeconds = mmi.sic; - this.setCallForward(options); - return; - - // Change the current ICC PIN number. - case MMI_SC_PIN: - // As defined in TS.122.030 6.6.2 to change the ICC PIN we should expect - // an MMI code of the form **04*OLD_PIN*NEW_PIN*NEW_PIN#, where old PIN - // should be entered as the SIA parameter and the new PIN as SIB and - // SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPassword = mmi.sib; - this.changeICCPIN(options); - return; - - // Change the current ICC PIN2 number. - case MMI_SC_PIN2: - // As defined in TS.122.030 6.6.2 to change the ICC PIN2 we should - // enter and MMI code of the form **042*OLD_PIN2*NEW_PIN2*NEW_PIN2#, - // where the old PIN2 should be entered as the SIA parameter and the - // new PIN2 as SIB and SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPassword = mmi.sib; - this.changeICCPIN2(options); - return; - - // Unblock ICC PIN. - case MMI_SC_PUK: - // As defined in TS.122.030 6.6.3 to unblock the ICC PIN we should - // enter an MMI code of the form **05*PUK*NEW_PIN*NEW_PIN#, where PUK - // should be entered as the SIA parameter and the new PIN as SIB and - // SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPin = mmi.sib; - this.enterICCPUK(options); - return; - - // Unblock ICC PIN2. - case MMI_SC_PUK2: - // As defined in TS.122.030 6.6.3 to unblock the ICC PIN2 we should - // enter an MMI code of the form **052*PUK2*NEW_PIN2*NEW_PIN2#, where - // PUK2 should be entered as the SIA parameter and the new PIN2 as SIB - // and SIC. - if (!_isRadioAvailable() || !_isValidPINPUKRequest()) { - return; - } - - options.password = mmi.sia; - options.newPin = mmi.sib; - this.enterICCPUK2(options); - return; - - // IMEI - case MMI_SC_IMEI: - // A device's IMEI can't change, so we only need to request it once. - if (this.IMEI == null) { - this.getIMEI(options); - return; - } - // If we already had the device's IMEI, we just send it to chrome. - options.success = true; - options.statusMessage = this.IMEI; - this.sendChromeMessage(options); - return; - - // CLIP - case MMI_SC_CLIP: - options.procedure = mmi.procedure; - if (options.procedure === MMI_PROCEDURE_INTERROGATION) { - this.queryCLIP(options); - } else { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - } - return; - - // CLIR (non-temporary ones) - // TODO: Both dial() and sendMMI() functions should be unified at some - // point in the future. In the mean time we handle temporary CLIR MMI - // commands through the dial() function. Please see bug 889737. - case MMI_SC_CLIR: - options.procedure = mmi.procedure; - switch (options.procedure) { - case MMI_PROCEDURE_INTERROGATION: - this.getCLIR(options); - return; - case MMI_PROCEDURE_ACTIVATION: - options.clirMode = CLIR_INVOCATION; - break; - case MMI_PROCEDURE_DEACTIVATION: - options.clirMode = CLIR_SUPPRESSION; - break; - default: - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return; - } - options.isSetCLIR = true; - this.setCLIR(options); - return; - - // Change call barring password - case MMI_SC_CHANGE_PASSWORD: - if (!_isRadioAvailable() || !_isValidChangePasswordRequest()) { - return; - } - - options.pin = mmi.sib; - options.newPin = mmi.sic; - this.changeCallBarringPassword(options); - return; - - // Call barring - case MMI_SC_BAOC: - case MMI_SC_BAOIC: - case MMI_SC_BAOICxH: - case MMI_SC_BAIC: - case MMI_SC_BAICr: - case MMI_SC_BA_ALL: - case MMI_SC_BA_MO: - case MMI_SC_BA_MT: - options.password = mmi.sia || ""; - options.serviceClass = this._siToServiceClass(mmi.sib); - options.facility = MMI_SC_TO_CB_FACILITY[sc]; - options.procedure = mmi.procedure; - if (mmi.procedure === MMI_PROCEDURE_INTERROGATION) { - this.queryICCFacilityLock(options); - return; - } - if (mmi.procedure === MMI_PROCEDURE_ACTIVATION) { - options.enabled = 1; - } else if (mmi.procedure === MMI_PROCEDURE_DEACTIVATION) { - options.enabled = 0; - } else { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return; - } - this.setICCFacilityLock(options); - return; - - // Call waiting - case MMI_SC_CALL_WAITING: - if (!_isRadioAvailable()) { - return; - } - - - if (mmi.procedure === MMI_PROCEDURE_INTERROGATION) { - this._handleQueryMMICallWaiting(options); - return; - } - - if (mmi.procedure === MMI_PROCEDURE_ACTIVATION) { - options.enabled = true; - } else if (mmi.procedure === MMI_PROCEDURE_DEACTIVATION) { - options.enabled = false; - } else { - _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); - return; - } - - options.serviceClass = this._siToServiceClass(mmi.sia); - this._handleSetMMICallWaiting(options); - return; - } - - // If the MMI code is not a known code, it is treated as an ussd. - if (!_isRadioAvailable()) { - return; - } - - options.ussd = mmi.fullMMI; - - if (this._ussdSession) { - if (DEBUG) this.context.debug("Cancel existing ussd session."); - this.cachedUSSDRequest = options; - this.cancelUSSD({}); - return; - } - - this.sendUSSD(options, false); - }, - - /** - * Cache the request for send out a new ussd when there is an existing - * session. We should do cancelUSSD first. - */ - cachedUSSDRequest : null, - /** * Send USSD. * * @param ussd * String containing the USSD code. */ - sendUSSD: function(options, checkSession = true) { - if (checkSession && !this._ussdSession) { - options.success = false; - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - this.sendChromeMessage(options); - return; - } - + sendUSSD: function(options) { let Buf = this.context.Buf; Buf.newParcel(REQUEST_SEND_USSD, options); Buf.writeString(options.ussd); @@ -2969,49 +2526,7 @@ RilObject.prototype = { * Helper for processing responses of functions such as enterICC* and changeICC*. */ _processEnterAndChangeICCResponses: function(length, options) { - options.success = !options.errorMsg; options.retryCount = length ? this.context.Buf.readInt32List()[0] : -1; - if (options.rilMessageType != "sendMMI") { - this.sendChromeMessage(options); - return; - } - - let serviceCode = options.mmi.serviceCode; - - if (options.success) { - switch (serviceCode) { - case MMI_SC_PIN: - options.statusMessage = MMI_SM_KS_PIN_CHANGED; - break; - case MMI_SC_PIN2: - options.statusMessage = MMI_SM_KS_PIN2_CHANGED; - break; - case MMI_SC_PUK: - options.statusMessage = MMI_SM_KS_PIN_UNBLOCKED; - break; - case MMI_SC_PUK2: - options.statusMessage = MMI_SM_KS_PIN2_UNBLOCKED; - break; - } - } else { - if (options.retryCount <= 0) { - if (serviceCode === MMI_SC_PUK) { - options.errorMsg = MMI_ERROR_KS_SIM_BLOCKED; - } else if (serviceCode === MMI_SC_PIN) { - options.errorMsg = MMI_ERROR_KS_NEEDS_PUK; - } - } else { - if (serviceCode === MMI_SC_PIN || serviceCode === MMI_SC_PIN2) { - options.errorMsg = MMI_ERROR_KS_BAD_PIN; - } else if (serviceCode === MMI_SC_PUK || serviceCode === MMI_SC_PUK2) { - options.errorMsg = MMI_ERROR_KS_BAD_PUK; - } - if (options.retryCount !== undefined) { - options.additionalInformation = options.retryCount; - } - } - } - this.sendChromeMessage(options); }, @@ -3511,7 +3026,8 @@ RilObject.prototype = { let strings = this.context.Buf.readStringList(); let networks = []; - for (let i = 0; i < strings.length; i += 4) { + for (let i = 0; i < strings.length; + i += RILQUIRKS_AVAILABLE_NETWORKS_EXTRA_STRING ? 5 : 4) { let network = { longName: strings[i], shortName: strings[i + 1], @@ -3599,12 +3115,6 @@ RilObject.prototype = { if (this._waitingRadioTech || isCdma != this._isCdma) { this._isCdma = isCdma; this._waitingRadioTech = false; - if (this._isCdma) { - this.getDeviceIdentity(); - } else { - this.getIMEI(); - this.getIMEISV(); - } this.getICCStatus(); } }, @@ -3620,44 +3130,6 @@ RilObject.prototype = { return toa; }, - /** - * Helper for translating basic service group to call forwarding service class - * parameter. - */ - _siToServiceClass: function(si) { - if (!si) { - return ICC_SERVICE_CLASS_NONE; - } - - let serviceCode = parseInt(si, 10); - switch (serviceCode) { - case 10: - return ICC_SERVICE_CLASS_SMS + ICC_SERVICE_CLASS_FAX + ICC_SERVICE_CLASS_VOICE; - case 11: - return ICC_SERVICE_CLASS_VOICE; - case 12: - return ICC_SERVICE_CLASS_SMS + ICC_SERVICE_CLASS_FAX; - case 13: - return ICC_SERVICE_CLASS_FAX; - case 16: - return ICC_SERVICE_CLASS_SMS; - case 19: - return ICC_SERVICE_CLASS_FAX + ICC_SERVICE_CLASS_VOICE; - case 21: - return ICC_SERVICE_CLASS_PAD + ICC_SERVICE_CLASS_DATA_ASYNC; - case 22: - return ICC_SERVICE_CLASS_PACKET + ICC_SERVICE_CLASS_DATA_SYNC; - case 25: - return ICC_SERVICE_CLASS_DATA_ASYNC; - case 26: - return ICC_SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE; - case 99: - return ICC_SERVICE_CLASS_PACKET; - default: - return ICC_SERVICE_CLASS_NONE; - } - }, - /** * @param message A decoded SMS-DELIVER message. * @@ -4483,9 +3955,7 @@ RilObject.prototype[REQUEST_GET_SIM_STATUS] = function REQUEST_GET_SIM_STATUS(le iccStatus.universalPINState = Buf.readInt32(); // CARD_PINSTATE_* iccStatus.gsmUmtsSubscriptionAppIndex = Buf.readInt32(); iccStatus.cdmaSubscriptionAppIndex = Buf.readInt32(); - if (!this.v5Legacy) { - iccStatus.imsSubscriptionAppIndex = Buf.readInt32(); - } + iccStatus.imsSubscriptionAppIndex = Buf.readInt32(); let apps_length = Buf.readInt32(); if (apps_length > CARD_MAX_APPS) { @@ -4681,13 +4151,11 @@ RilObject.prototype[REQUEST_SIGNAL_STRENGTH] = function REQUEST_SIGNAL_STRENGTH( signal.evdoECIO = Buf.readInt32(); signal.evdoSNR = Buf.readInt32(); - if (!this.v5Legacy) { - signal.lteSignalStrength = Buf.readInt32(); - signal.lteRSRP = Buf.readInt32(); - signal.lteRSRQ = Buf.readInt32(); - signal.lteRSSNR = Buf.readInt32(); - signal.lteCQI = Buf.readInt32(); - } + signal.lteSignalStrength = Buf.readInt32(); + signal.lteRSRP = Buf.readInt32(); + signal.lteRSRQ = Buf.readInt32(); + signal.lteRSSNR = Buf.readInt32(); + signal.lteCQI = Buf.readInt32(); if (DEBUG) this.context.debug("signal strength: " + JSON.stringify(signal)); @@ -4735,45 +4203,18 @@ RilObject.prototype[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, option }; RilObject.prototype[REQUEST_SEND_SMS_EXPECT_MORE] = null; -RilObject.prototype.readSetupDataCall_v5 = function readSetupDataCall_v5(options) { - if (!options) { - options = {}; - } - let [cid, ifname, addresses, dnses, gateways] = this.context.Buf.readStringList(); - options.cid = cid; - options.ifname = ifname; - options.addresses = addresses ? [addresses] : []; - options.dnses = dnses ? [dnses] : []; - options.gateways = gateways ? [gateways] : []; - options.active = DATACALL_ACTIVE_UNKNOWN; - options.state = GECKO_NETWORK_STATE_CONNECTING; - return options; -}; - RilObject.prototype[REQUEST_SETUP_DATA_CALL] = function REQUEST_SETUP_DATA_CALL(length, options) { if (options.errorMsg) { this.sendChromeMessage(options); return; } - if (this.v5Legacy) { - // Populate the `options` object with the data call information. That way - // we retain the APN and other info about how the data call was set up. - this.readSetupDataCall_v5(options); - this.sendChromeMessage(options); - // Let's get the list of data calls to ensure we know whether it's active - // or not. - this.getDataCallList(); - return; - } - let Buf = this.context.Buf; - // Skip version of data call. - Buf.readInt32(); + let version = Buf.readInt32(); // Skip number of data calls. Buf.readInt32(); - this.readDataCall_v6(options); + this.readDataCall(options, version); this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SIM_IO] = function REQUEST_SIM_IO(length, options) { @@ -4813,30 +4254,16 @@ RilObject.prototype[REQUEST_SEND_USSD] = function REQUEST_SEND_USSD(length, opti if (DEBUG) { this.context.debug("REQUEST_SEND_USSD " + JSON.stringify(options)); } - options.success = (this._ussdSession = !options.errorMsg); this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_CANCEL_USSD] = function REQUEST_CANCEL_USSD(length, options) { if (DEBUG) { this.context.debug("REQUEST_CANCEL_USSD" + JSON.stringify(options)); } - - options.success = !options.errorMsg; - this._ussdSession = !options.success; - - // The cancelUSSD is triggered by ril_worker itself. - if (this.cachedUSSDRequest) { - if (DEBUG) this.context.debug("Send out the cached ussd request"); - this.sendUSSD(this.cachedUSSDRequest); - this.cachedUSSDRequest = null; - return; - } - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_GET_CLIR] = function REQUEST_GET_CLIR(length, options) { - options.success = !options.errorMsg; - if (!options.success) { + if (options.errorMsg) { this.sendChromeMessage(options); return; } @@ -4844,7 +4271,6 @@ RilObject.prototype[REQUEST_GET_CLIR] = function REQUEST_GET_CLIR(length, option let Buf = this.context.Buf; let bufLength = Buf.readInt32(); if (!bufLength || bufLength < 2) { - options.success = false; options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; this.sendChromeMessage(options); return; @@ -4852,70 +4278,6 @@ RilObject.prototype[REQUEST_GET_CLIR] = function REQUEST_GET_CLIR(length, option options.n = Buf.readInt32(); // Will be TS 27.007 +CLIR parameter 'n'. options.m = Buf.readInt32(); // Will be TS 27.007 +CLIR parameter 'm'. - - if (options.rilMessageType === "sendMMI") { - // TS 27.007 +CLIR parameter 'm'. - switch (options.m) { - // CLIR not provisioned. - case 0: - options.statusMessage = MMI_SM_KS_SERVICE_NOT_PROVISIONED; - break; - // CLIR provisioned in permanent mode. - case 1: - options.statusMessage = MMI_SM_KS_CLIR_PERMANENT; - break; - // Unknown (e.g. no network, etc.). - case 2: - options.success = false; - options.errorMsg = MMI_ERROR_KS_ERROR; - break; - // CLIR temporary mode presentation restricted. - case 3: - // TS 27.007 +CLIR parameter 'n'. - switch (options.n) { - // Default. - case 0: - // CLIR invocation. - case 1: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON; - break; - // CLIR suppression. - case 2: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF; - break; - default: - options.success = false; - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - break; - } - break; - // CLIR temporary mode presentation allowed. - case 4: - // TS 27.007 +CLIR parameter 'n'. - switch (options.n) { - // Default. - case 0: - // CLIR suppression. - case 2: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF; - break; - // CLIR invocation. - case 1: - options.statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON; - break; - default: - options.success = false; - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - break; - } - break; - default: - options.success = false; - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - break; - } - } - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_CLIR] = function REQUEST_SET_CLIR(length, options) { @@ -4923,24 +4285,13 @@ RilObject.prototype[REQUEST_SET_CLIR] = function REQUEST_SET_CLIR(length, option // The request was made by ril_worker itself automatically. Don't report. return; } - options.success = !options.errorMsg; - if (options.success && options.rilMessageType === "sendMMI") { - switch (options.procedure) { - case MMI_PROCEDURE_ACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - case MMI_PROCEDURE_DEACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - } - } + this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_QUERY_CALL_FORWARD_STATUS] = function REQUEST_QUERY_CALL_FORWARD_STATUS(length, options) { - options.success = !options.errorMsg; - if (!options.success) { + if (options.errorMsg) { this.sendChromeMessage(options); return; } @@ -4951,7 +4302,6 @@ RilObject.prototype[REQUEST_QUERY_CALL_FORWARD_STATUS] = rulesLength = Buf.readInt32(); } if (!rulesLength) { - options.success = false; options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; this.sendChromeMessage(options); return; @@ -4968,103 +4318,32 @@ RilObject.prototype[REQUEST_QUERY_CALL_FORWARD_STATUS] = rules[i] = rule; } options.rules = rules; - if (options.rilMessageType === "sendMMI") { - options.statusMessage = MMI_SM_KS_SERVICE_INTERROGATED; - // MMI query call forwarding options request returns a set of rules that - // will be exposed in the form of an array of MozCallForwardingOptions - // instances. - options.additionalInformation = rules; - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_CALL_FORWARD] = function REQUEST_SET_CALL_FORWARD(length, options) { - options.success = !options.errorMsg; - if (options.success && options.rilMessageType === "sendMMI") { - switch (options.action) { - case CALL_FORWARD_ACTION_ENABLE: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - case CALL_FORWARD_ACTION_DISABLE: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - case CALL_FORWARD_ACTION_REGISTRATION: - options.statusMessage = MMI_SM_KS_SERVICE_REGISTERED; - break; - case CALL_FORWARD_ACTION_ERASURE: - options.statusMessage = MMI_SM_KS_SERVICE_ERASED; - break; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_QUERY_CALL_WAITING] = function REQUEST_QUERY_CALL_WAITING(length, options) { - options.success = !options.errorMsg; - if (!options.success) { - if (options.callback) { - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - } - + if (options.errorMsg) { this.sendChromeMessage(options); return; } - if (options.callback) { - options.callback.call(this, options); - return; - } - let Buf = this.context.Buf; - options.length = Buf.readInt32(); - options.enabled = ((Buf.readInt32() == 1) && - ((Buf.readInt32() & ICC_SERVICE_CLASS_VOICE) == 0x01)); + let results = Buf.readInt32List(); + let enabled = (results[0] === 1); + options.serviceClass = enabled ? results[1] : ICC_SERVICE_CLASS_NONE; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_CALL_WAITING] = function REQUEST_SET_CALL_WAITING(length, options) { - options.success = !options.errorMsg; - if (!options.success) { - if (options.callback) { - // Prevent DataCloneError when sending chrome messages. - delete options.callback; - } - - this.sendChromeMessage(options); - return; - } - - if (options.callback) { - options.callback.call(this, options); - return; - } - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SMS_ACKNOWLEDGE] = null; -RilObject.prototype[REQUEST_GET_IMEI] = function REQUEST_GET_IMEI(length, options) { - this.IMEI = this.context.Buf.readString(); - let rilMessageType = options.rilMessageType; - // So far we only send the IMEI back to chrome if it was requested via MMI. - if (rilMessageType !== "sendMMI") { - return; - } - - options.success = !options.errorMsg; - if (options.success && this.IMEI == null) { - options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; - } - options.statusMessage = this.IMEI; - this.sendChromeMessage(options); -}; -RilObject.prototype[REQUEST_GET_IMEISV] = function REQUEST_GET_IMEISV(length, options) { - if (options.errorMsg) { - return; - } - - this.IMEISV = this.context.Buf.readString(); -}; +RilObject.prototype[REQUEST_GET_IMEI] = null; +RilObject.prototype[REQUEST_GET_IMEISV] = null; RilObject.prototype[REQUEST_ANSWER] = function REQUEST_ANSWER(length, options) { this.sendDefaultResponse(options); }; @@ -5072,14 +4351,12 @@ RilObject.prototype[REQUEST_DEACTIVATE_DATA_CALL] = function REQUEST_DEACTIVATE_ this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_QUERY_FACILITY_LOCK] = function REQUEST_QUERY_FACILITY_LOCK(length, options) { - options.success = !options.errorMsg; - if (!options.success) { + if (options.errorMsg) { this.sendChromeMessage(options); return; } if (!length) { - options.success = false; options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; this.sendChromeMessage(options); return; @@ -5095,49 +4372,14 @@ RilObject.prototype[REQUEST_QUERY_FACILITY_LOCK] = function REQUEST_QUERY_FACILI options.enabled = services ? true : false; } - if (options.rilMessageType === "sendMMI") { - if (!options.enabled) { - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - } else { - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED_FOR; - let serviceClass = []; - for (let serviceClassMask = 1; - serviceClassMask <= ICC_SERVICE_CLASS_MAX; - serviceClassMask <<= 1) { - if ((serviceClassMask & services) !== 0) { - serviceClass.push(MMI_KS_SERVICE_CLASS_MAPPING[serviceClassMask]); - } - } - - options.additionalInformation = serviceClass; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_FACILITY_LOCK] = function REQUEST_SET_FACILITY_LOCK(length, options) { - options.success = !options.errorMsg; options.retryCount = length ? this.context.Buf.readInt32List()[0] : -1; - - if (options.success && (options.rilMessageType === "sendMMI")) { - switch (options.procedure) { - case MMI_PROCEDURE_ACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - case MMI_PROCEDURE_DEACTIVATION: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_CHANGE_BARRING_PASSWORD] = function REQUEST_CHANGE_BARRING_PASSWORD(length, options) { - if (options.rilMessageType != "sendMMI") { - this.sendChromeMessage(options); - return; - } - - options.statusMessage = MMI_SM_KS_PASSWORD_CHANGED; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_QUERY_NETWORK_SELECTION_MODE] = function REQUEST_QUERY_NETWORK_SELECTION_MODE(length, options) { @@ -5183,7 +4425,6 @@ RilObject.prototype[REQUEST_QUERY_AVAILABLE_NETWORKS] = function REQUEST_QUERY_A this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_DTMF_START] = function REQUEST_DTMF_START(length, options) { - options.success = !options.errorMsg; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_DTMF_STOP] = null; @@ -5201,8 +4442,7 @@ RilObject.prototype[REQUEST_SEPARATE_CONNECTION] = function REQUEST_SEPARATE_CON RilObject.prototype[REQUEST_SET_MUTE] = null; RilObject.prototype[REQUEST_GET_MUTE] = null; RilObject.prototype[REQUEST_QUERY_CLIP] = function REQUEST_QUERY_CLIP(length, options) { - options.success = !options.errorMsg; - if (!options.success) { + if (options.errorMsg) { this.sendChromeMessage(options); return; } @@ -5210,70 +4450,29 @@ RilObject.prototype[REQUEST_QUERY_CLIP] = function REQUEST_QUERY_CLIP(length, op let Buf = this.context.Buf; let bufLength = Buf.readInt32(); if (!bufLength) { - options.success = false; options.errorMsg = GECKO_ERROR_GENERIC_FAILURE; this.sendChromeMessage(options); return; } - // options.provisioned informs about the called party receives the calling - // party's address information: - // 0 for CLIP not provisioned - // 1 for CLIP provisioned - // 2 for unknown options.provisioned = Buf.readInt32(); - if (options.rilMessageType === "sendMMI") { - switch (options.provisioned) { - case 0: - options.statusMessage = MMI_SM_KS_SERVICE_DISABLED; - break; - case 1: - options.statusMessage = MMI_SM_KS_SERVICE_ENABLED; - break; - default: - options.success = false; - options.errorMsg = MMI_ERROR_KS_ERROR; - break; - } - } this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null; /** - * V3: - * # address - A space-delimited list of addresses. - * - * V4: - * # address - An address. - * - * V5: - * # addresses - A space-delimited list of addresses. - * # dnses - A space-delimited list of DNS server addresses. - * * V6: * # addresses - A space-delimited list of addresses with optional "/" prefix * length. * # dnses - A space-delimited list of DNS server addresses. * # gateways - A space-delimited list of default gateway addresses. + * + * V10: + * # pcscf - A space-delimited list of Proxy Call State Control Function + * addresses. */ -RilObject.prototype.readDataCall_v5 = function(options) { - if (!options) { - options = {}; - } - let Buf = this.context.Buf; - options.cid = Buf.readInt32().toString(); - options.active = Buf.readInt32(); // DATACALL_ACTIVE_* - options.type = Buf.readString(); - options.apn = Buf.readString(); - options.addresses = Buf.readString(); - options.dnses = Buf.readString(); - options.gateways = []; - return options; -}; - -RilObject.prototype.readDataCall_v6 = function(options) { +RilObject.prototype.readDataCall = function(options, version) { if (!options) { options = {}; } @@ -5288,6 +4487,15 @@ RilObject.prototype.readDataCall_v6 = function(options) { options.dnses = Buf.readString(); options.gateways = Buf.readString(); + if (version >= 10) { + options.pcscf = Buf.readString(); + } + + if (version >= 11) { + let mtu = Buf.readInt32(); + options.mtu = (mtu > 0) ? mtu : -1 ; + } + return options; }; @@ -5311,19 +4519,12 @@ RilObject.prototype[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(le } let Buf = this.context.Buf; - let version = 0; - if (!this.v5Legacy) { - version = Buf.readInt32(); - } + let version = Buf.readInt32(); let num = Buf.readInt32(); let datacalls = []; for (let i = 0; i < num; i++) { let datacall; - if (version < 6) { - datacall = this.readDataCall_v5(); - } else { - datacall = this.readDataCall_v6(); - } + datacall = this.readDataCall({}, version); datacalls.push(datacall); } @@ -5559,33 +4760,116 @@ RilObject.prototype[REQUEST_CDMA_WRITE_SMS_TO_RUIM] = null; RilObject.prototype[REQUEST_CDMA_DELETE_SMS_ON_RUIM] = null; RilObject.prototype[REQUEST_DEVICE_IDENTITY] = function REQUEST_DEVICE_IDENTITY(length, options) { if (options.errorMsg) { + this.context.debug("Failed to get device identities:" + options.errorMsg); return; } let result = this.context.Buf.readStringList(); + this.deviceIdentities = { + imei: result[0] || null, + imeisv: result[1] || null, + esn: result[2] || null, + meid: result[3] || null, + }; - // The result[0] is for IMEI. (Already be handled in REQUEST_GET_IMEI) - // The result[1] is for IMEISV. (Already be handled in REQUEST_GET_IMEISV) - // They are both ignored. - this.ESN = result[2]; - this.MEID = result[3]; + this.sendChromeMessage({ + rilMessageType: "deviceidentitieschange", + deviceIdentities: this.deviceIdentities + }); }; RilObject.prototype[REQUEST_EXIT_EMERGENCY_CALLBACK_MODE] = function REQUEST_EXIT_EMERGENCY_CALLBACK_MODE(length, options) { if (options.internal) { return; } - options.success = !options.errorMsg; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_GET_SMSC_ADDRESS] = function REQUEST_GET_SMSC_ADDRESS(length, options) { - this.SMSC = options.errorMsg ? null : this.context.Buf.readString(); - if (!options.rilMessageType || options.rilMessageType !== "getSmscAddress") { return; } - options.smscAddress = this.SMSC; + if (options.errorMsg) { + this.sendChromeMessage(options); + return; + } + + let tosca = TOA_UNKNOWN; + let smsc = ""; + let Buf = this.context.Buf; + if (RILQUIRKS_SMSC_ADDRESS_FORMAT === "pdu") { + let pduHelper = this.context.GsmPDUHelper; + let strlen = Buf.readInt32(); + let length = pduHelper.readHexOctet(); + + // As defined in |8.2.5.2 Destination address element| of 3GPP TS 24.011, + // the value of length field can not exceed 11. Since the content might be + // filled with 12 'F' when SMSC is cleared, we don't parse the TOA and + // address fields if reported length exceeds 11 here. Instead, keep the + // default value (TOA_UNKNOWN with an empty address) in this case. + const MAX_LENGTH = 11 + if (length <= MAX_LENGTH) { + tosca = pduHelper.readHexOctet(); + + // Read and covert the decimal values back to special BCD digits defined in + // |Called party BCD number| of 3GPP TS 24.008 (refer the following table). + // + // +=========+=======+=====+ + // | value | digit | hex | + // +======================== + // | 1 0 1 0 | * | 0xA | + // | 1 0 1 1 | # | 0xB | + // | 1 1 0 0 | a | 0xC | + // | 1 1 0 1 | b | 0xD | + // | 1 1 1 0 | c | 0xE | + // +=========+=======+=====+ + smsc = pduHelper.readSwappedNibbleBcdString(length - 1, true) + .replace(/a/ig, "*") + .replace(/b/ig, "#") + .replace(/c/ig, "a") + .replace(/d/ig, "b") + .replace(/e/ig, "c"); + + Buf.readStringDelimiter(strlen); + } + } else /* RILQUIRKS_SMSC_ADDRESS_FORMAT === "text" */ { + let text = Buf.readString(); + let segments = text.split(",", 2); + // Parse TOA only if it presents since some devices might omit the TOA + // segment in the reported SMSC address. If TOA does not present, keep the + // default value TOA_UNKNOWN. + if (segments.length === 2) { + tosca = this.parseInt(segments[1], TOA_UNKNOWN, 10); + } + + smsc = segments[0].replace(/\"/g, ""); + } + + // Convert the NPI value to the corresponding index of CALLED_PARTY_BCD_NPI + // array. If the value does not present in the array, use + // CALLED_PARTY_BCD_NPI_ISDN. + let npi = CALLED_PARTY_BCD_NPI.indexOf(tosca & 0xf); + if (npi === -1) { + npi = CALLED_PARTY_BCD_NPI.indexOf(CALLED_PARTY_BCD_NPI_ISDN); + } + + // Extract TON. + let ton = (tosca & 0x70) >> 4; + + // Ensure + sign if TON is international, and vice versa. + const TON_INTERNATIONAL = (TOA_INTERNATIONAL & 0x70) >> 4; + if (ton === TON_INTERNATIONAL && smsc.charAt(0) !== "+") { + smsc = "+" + smsc; + } else if (smsc.charAt(0) === "+" && ton !== TON_INTERNATIONAL) { + if (DEBUG) { + this.context.debug("SMSC address number begins with '+' while the TON is not international. Change TON to international."); + } + ton = TON_INTERNATIONAL; + } + + options.smscAddress = smsc; + options.typeOfNumber = ton; + options.numberPlanIdentification = npi; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SET_SMSC_ADDRESS] = function REQUEST_SET_SMSC_ADDRESS(length, options) { @@ -5593,10 +4877,6 @@ RilObject.prototype[REQUEST_SET_SMSC_ADDRESS] = function REQUEST_SET_SMSC_ADDRES return; } - if (options.rilRequestError) { - optioins.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError]; - } - this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_REPORT_SMS_MEMORY_STATUS] = function REQUEST_REPORT_SMS_MEMORY_STATUS(length, options) { @@ -5704,11 +4984,9 @@ RilObject.prototype[REQUEST_SET_DATA_SUBSCRIPTION] = function REQUEST_SET_DATA_S // The request was made by ril_worker itself. Don't report. return; } - options.success = !options.errorMsg; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_GET_UNLOCK_RETRY_COUNT] = function REQUEST_GET_UNLOCK_RETRY_COUNT(length, options) { - options.success = !options.errorMsg; options.retryCount = length ? this.context.Buf.readInt32List()[0] : -1; this.sendChromeMessage(options); }; @@ -5717,11 +4995,9 @@ RilObject.prototype[RIL_REQUEST_GPRS_ATTACH] = function RIL_REQUEST_GPRS_ATTACH( // The request was made by ril_worker itself. Don't report. return; } - options.success = !options.errorMsg; this.sendChromeMessage(options); }; RilObject.prototype[RIL_REQUEST_GPRS_DETACH] = function RIL_REQUEST_GPRS_DETACH(length, options) { - options.success = !options.errorMsg; this.sendChromeMessage(options); }; RilObject.prototype[UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED] = function UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED() { @@ -5746,42 +5022,23 @@ RilObject.prototype[UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED] = function UNSOLIC return; } - switch (radioState) { - case RADIO_STATE_SIM_READY: - case RADIO_STATE_SIM_NOT_READY: - case RADIO_STATE_SIM_LOCKED_OR_ABSENT: - this._isCdma = false; - this._waitingRadioTech = false; - break; - case RADIO_STATE_RUIM_READY: - case RADIO_STATE_RUIM_NOT_READY: - case RADIO_STATE_RUIM_LOCKED_OR_ABSENT: - case RADIO_STATE_NV_READY: - case RADIO_STATE_NV_NOT_READY: - this._isCdma = true; - this._waitingRadioTech = false; - break; - case RADIO_STATE_ON: // RIL v7 - // This value is defined in RIL v7, we will retrieve radio tech by another - // request. We leave _isCdma untouched, and it will be set once we get the - // radio technology. - this._waitingRadioTech = true; - this.getVoiceRadioTechnology(); - break; + if (radioState !== RADIO_STATE_UNAVAILABLE) { + // Retrieve device identities once radio is available. + this.getDeviceIdentity(); + } + + if (radioState == RADIO_STATE_ON) { + // This value is defined in RIL v7, we will retrieve radio tech by another + // request. We leave _isCdma untouched, and it will be set once we get the + // radio technology. + this._waitingRadioTech = true; + this.getVoiceRadioTechnology(); } if ((this.radioState == GECKO_RADIOSTATE_UNKNOWN || this.radioState == GECKO_RADIOSTATE_DISABLED) && newState == GECKO_RADIOSTATE_ENABLED) { // The radio became available, let's get its info. - if (!this._waitingRadioTech) { - if (this._isCdma) { - this.getDeviceIdentity(); - } else { - this.getIMEI(); - this.getIMEISV(); - } - } this.getBasebandVersion(); this.updateCellBroadcastConfig(); if ((RILQUIRKS_DATA_REGISTRATION_ON_DEMAND || @@ -5863,18 +5120,12 @@ RilObject.prototype[UNSOLICITED_ON_USSD] = function UNSOLICITED_ON_USSD() { this.context.debug("On USSD. Type Code: " + typeCode + " Message: " + message); } - let oldSession = this._ussdSession; - - // Per ril.h the USSD session is assumed to persist if the type code is "1". - this._ussdSession = typeCode == "1"; - - if (!oldSession && !this._ussdSession && !message) { - return; - } - this.sendChromeMessage({rilMessageType: "ussdreceived", message: message, - sessionEnded: !this._ussdSession}); + // Per ril.h the USSD session is assumed to persist if + // the type code is "1", otherwise the current session + // (if any) is assumed to have terminated. + sessionEnded: typeCode !== "1"}); }; RilObject.prototype[UNSOLICITED_ON_USSD_REQUEST] = null; RilObject.prototype[UNSOLICITED_NITZ_TIME_RECEIVED] = function UNSOLICITED_NITZ_TIME_RECEIVED() { @@ -5919,10 +5170,6 @@ RilObject.prototype[UNSOLICITED_SIGNAL_STRENGTH] = function UNSOLICITED_SIGNAL_S this[REQUEST_SIGNAL_STRENGTH](length, {}); }; RilObject.prototype[UNSOLICITED_DATA_CALL_LIST_CHANGED] = function UNSOLICITED_DATA_CALL_LIST_CHANGED(length) { - if (this.v5Legacy) { - this.getDataCallList(); - return; - } this[REQUEST_DATA_CALL_LIST](length, {}); }; RilObject.prototype[UNSOLICITED_SUPP_SVC_NOTIFICATION] = function UNSOLICITED_SUPP_SVC_NOTIFICATION(length) { @@ -6064,10 +5311,8 @@ RilObject.prototype[UNSOLICITED_RIL_CONNECTED] = function UNSOLICITED_RIL_CONNEC } this.version = this.context.Buf.readInt32List()[0]; - this.v5Legacy = (this.version < 5); if (DEBUG) { this.context.debug("Detected RIL version " + this.version); - this.context.debug("this.v5Legacy is " + this.v5Legacy); } this.initRILState(); @@ -6283,6 +5528,16 @@ GsmPDUHelperObject.prototype = { return this.extendedBcdChars.charAt(semiOctet); }, + /** + * Convert string to a GSM extended BCD string + */ + stringToExtendedBcd: function(string) { + return string.replace(/[^0-9*#,]/g, "") + .replace(/\*/g, "a") + .replace(/\#/g, "b") + .replace(/\,/g, "c"); + }, + /** * Read a *swapped nibble* binary coded decimal (BCD) * @@ -9391,6 +8646,8 @@ ICCPDUHelperObject.prototype = { * @param numOctets Number of total octets to be writen, including trailing * 0xff. * @param str String to be written. Could be null. + * + * @return The string has been written into Buf. */ writeStringTo8BitUnpacked: function(numOctets, str) { const langTable = PDU_NL_LOCKING_SHIFT_TABLES[PDU_NL_IDENTIFIER_DEFAULT]; @@ -9429,6 +8686,8 @@ ICCPDUHelperObject.prototype = { while (j++ < numOctets) { GsmPDUHelper.writeHexOctet(0xff); } + + return (str) ? str.substring(0, i) : null; }, /** @@ -9442,6 +8701,7 @@ ICCPDUHelperObject.prototype = { * @param str * String to be written. * + * @return The string has been written into Buf. */ writeICCUCS2String: function(numOctets, str) { let GsmPDUHelper = this.context.GsmPDUHelper; @@ -9501,7 +8761,7 @@ ICCPDUHelperObject.prototype = { for (let i = str.length * 2; i < numOctets; i++) { GsmPDUHelper.writeHexOctet(0xff); } - return; + return str; } /** * +------+-----+--------------+-----+-----+-----+--------+------+ @@ -9574,6 +8834,7 @@ ICCPDUHelperObject.prototype = { GsmPDUHelper.writeHexOctet(0xff); } } + return str; }, /** @@ -9690,26 +8951,34 @@ ICCPDUHelperObject.prototype = { let number = this.readNumberWithLength(); - // Skip 2 unused octets, CCP and EXT1. - Buf.seekIncoming(2 * Buf.PDU_HEX_OCTET_SIZE); + // Skip unused octet, CCP + Buf.seekIncoming(Buf.PDU_HEX_OCTET_SIZE); + + let extRecordNumber = this.context.GsmPDUHelper.readHexOctet(); Buf.readStringDelimiter(length); let contact = null; if (alphaId || number) { contact = {alphaId: alphaId, - number: number}; + number: number, + extRecordNumber: extRecordNumber}; } + return contact; }, /** * Write Alpha Identifier and Dialling number from TS 151.011 clause 10.5.1 * - * @param recordSize The size of linear fixed record. - * @param alphaId Alpha Identifier to be written. - * @param number Dialling Number to be written. + * @param recordSize The size of linear fixed record. + * @param alphaId Alpha Identifier to be written. + * @param number Dialling Number to be written. + * @param extRecordNumber The record identifier of the EXT. + * + * @return An object contains the alphaId and number + * that have been written into Buf. */ - writeAlphaIdDiallingNumber: function(recordSize, alphaId, number) { + writeAlphaIdDiallingNumber: function(recordSize, alphaId, number, extRecordNumber) { let Buf = this.context.Buf; let GsmPDUHelper = this.context.GsmPDUHelper; @@ -9718,13 +8987,17 @@ ICCPDUHelperObject.prototype = { Buf.writeInt32(strLen); let alphaLen = recordSize - ADN_FOOTER_SIZE_BYTES; - this.writeAlphaIdentifier(alphaLen, alphaId); - this.writeNumberWithLength(number); + let writtenAlphaId = this.writeAlphaIdentifier(alphaLen, alphaId); + let writtenNumber = this.writeNumberWithLength(number); - // Write unused octets 0xff, CCP and EXT1. - GsmPDUHelper.writeHexOctet(0xff); + // Write unused CCP octet 0xff. GsmPDUHelper.writeHexOctet(0xff); + GsmPDUHelper.writeHexOctet((extRecordNumber != null) ? extRecordNumber : 0xff); + Buf.writeStringDelimiter(strLen); + + return {alphaId: writtenAlphaId, + number: writtenNumber}; }, /** @@ -9769,18 +9042,20 @@ ICCPDUHelperObject.prototype = { * @param alphaId * Alpha Identifier to be written. * + * @return The Alpha Identifier has been written into Buf. + * * Unused octets will be written as 0xff. */ writeAlphaIdentifier: function(numOctets, alphaId) { if (numOctets === 0) { - return; + return null; } // If alphaId is empty or it's of GSM 8 bit. if (!alphaId || this.context.ICCUtilsHelper.isGsm8BitAlphabet(alphaId)) { - this.writeStringTo8BitUnpacked(numOctets, alphaId); + return this.writeStringTo8BitUnpacked(numOctets, alphaId); } else { - this.writeICCUCS2String(numOctets, alphaId); + return this.writeICCUCS2String(numOctets, alphaId); } }, @@ -9867,37 +9142,45 @@ ICCPDUHelperObject.prototype = { return number; }, + /** + * Write Number with Length + * + * @param number The value to be written. + * + * @return The number has been written into Buf. + */ writeNumberWithLength: function(number) { let GsmPDUHelper = this.context.GsmPDUHelper; if (number) { let numStart = number[0] == "+" ? 1 : 0; - number = number.substring(0, numStart) + - number.substring(numStart) - .replace(/[^0-9*#,]/g, "") - .replace(/\*/g, "a") - .replace(/\#/g, "b") - .replace(/\,/g, "c"); + let writtenNumber = number.substring(0, numStart) + + number.substring(numStart) + .replace(/[^0-9*#,]/g, ""); + let numDigits = writtenNumber.length - numStart; - let numDigits = number.length - numStart; if (numDigits > ADN_MAX_NUMBER_DIGITS) { - number = number.substring(0, ADN_MAX_NUMBER_DIGITS + numStart); - numDigits = number.length - numStart; + writtenNumber = writtenNumber.substring(0, ADN_MAX_NUMBER_DIGITS + numStart); + numDigits = writtenNumber.length - numStart; } // +1 for TON/NPI let numLen = Math.ceil(numDigits / 2) + 1; GsmPDUHelper.writeHexOctet(numLen); - this.writeDiallingNumber(number); + this.writeDiallingNumber(writtenNumber.replace(/\*/g, "a") + .replace(/\#/g, "b") + .replace(/\,/g, "c")); // Write trailing 0xff of Dialling Number. for (let i = 0; i < ADN_MAX_BCD_NUMBER_BYTES - numLen; i++) { GsmPDUHelper.writeHexOctet(0xff); } + return writtenNumber; } else { // +1 for numLen for (let i = 0; i < ADN_MAX_BCD_NUMBER_BYTES + 1; i++) { GsmPDUHelper.writeHexOctet(0xff); } + return null; } } }; @@ -11781,6 +11064,9 @@ ICCFileHelperObject.prototype = { case ICC_EF_FDN: case ICC_EF_MSISDN: case ICC_EF_SMS: + case ICC_EF_EXT1: + case ICC_EF_EXT2: + case ICC_EF_EXT3: return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM; case ICC_EF_AD: case ICC_EF_MBDN: @@ -11850,6 +11136,9 @@ ICCFileHelperObject.prototype = { case ICC_EF_CSIM_SPN: return EF_PATH_MF_SIM + EF_PATH_DF_CDMA; case ICC_EF_FDN: + case ICC_EF_EXT1: + case ICC_EF_EXT2: + case ICC_EF_EXT3: return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM; default: return null; @@ -12255,24 +11544,20 @@ ICCRecordHelperObject.prototype = { /** * Read ICC ADN like EF, i.e. EF_ADN, EF_FDN. * - * @param fileId EF id of the ADN or FDN. + * @param fileId EF id of the ADN, FDN or SDN. + * @param extFileId EF id of the EXT. * @param onsuccess Callback to be called when success. * @param onerror Callback to be called when error. */ - readADNLike: function(fileId, onsuccess, onerror) { + readADNLike: function(fileId, extFileId, onsuccess, onerror) { let ICCIOHelper = this.context.ICCIOHelper; function callback(options) { - let contact = - this.context.ICCPDUHelper.readAlphaIdDiallingNumber(options.recordSize); - if (contact) { - contact.recordId = options.p1; - contacts.push(contact); - } - - if (options.p1 < options.totalRecords) { - ICCIOHelper.loadNextRecord(options); - } else { + let loadNextContactRecord = () => { + if (options.p1 < options.totalRecords) { + ICCIOHelper.loadNextRecord(options); + return; + } if (DEBUG) { for (let i = 0; i < contacts.length; i++) { this.context.debug("contact [" + i + "] " + @@ -12282,7 +11567,29 @@ ICCRecordHelperObject.prototype = { if (onsuccess) { onsuccess(contacts); } + }; + + let contact = + this.context.ICCPDUHelper.readAlphaIdDiallingNumber(options.recordSize); + if (contact) { + let record = { + recordId: options.p1, + alphaId: contact.alphaId, + number: contact.number + }; + contacts.push(record); + + if (extFileId && contact.extRecordNumber != 0xff) { + this.readExtension(extFileId, contact.extRecordNumber, (number) => { + if (number) { + record.number += number; + } + loadNextContactRecord(); + }, () => loadNextContactRecord()); + return; + } } + loadNextContactRecord(); } let contacts = []; @@ -12294,22 +11601,25 @@ ICCRecordHelperObject.prototype = { /** * Update ICC ADN like EFs, like EF_ADN, EF_FDN. * - * @param fileId EF id of the ADN or FDN. - * @param contact The contact will be updated. (Shall have recordId property) - * @param pin2 PIN2 is required when updating ICC_EF_FDN. - * @param onsuccess Callback to be called when success. - * @param onerror Callback to be called when error. + * @param fileId EF id of the ADN or FDN. + * @param extRecordNumber The record identifier of the EXT. + * @param contact The contact will be updated. (Shall have recordId property) + * @param pin2 PIN2 is required when updating ICC_EF_FDN. + * @param onsuccess Callback to be called when success. + * @param onerror Callback to be called when error. */ - updateADNLike: function(fileId, contact, pin2, onsuccess, onerror) { + updateADNLike: function(fileId, extRecordNumber, contact, pin2, onsuccess, onerror) { + let updatedContact; function dataWriter(recordSize) { - this.context.ICCPDUHelper.writeAlphaIdDiallingNumber(recordSize, - contact.alphaId, - contact.number); + updatedContact = this.context.ICCPDUHelper.writeAlphaIdDiallingNumber(recordSize, + contact.alphaId, + contact.number, + extRecordNumber); } function callback(options) { if (onsuccess) { - onsuccess(); + onsuccess(updatedContact); } } @@ -12544,6 +11854,7 @@ ICCRecordHelperObject.prototype = { updateEmail: function(pbr, recordNumber, email, adnRecordId, onsuccess, onerror) { let fileId = pbr[USIM_PBR_EMAIL].fileId; let fileType = pbr[USIM_PBR_EMAIL].fileType; + let writtenEmail; let dataWriter = function dataWriter(recordSize) { let Buf = this.context.Buf; let GsmPDUHelper = this.context.GsmPDUHelper; @@ -12554,9 +11865,9 @@ ICCRecordHelperObject.prototype = { Buf.writeInt32(strLen); if (fileType == ICC_USIM_TYPE1_TAG) { - ICCPDUHelper.writeStringTo8BitUnpacked(recordSize, email); + writtenEmail = ICCPDUHelper.writeStringTo8BitUnpacked(recordSize, email); } else { - ICCPDUHelper.writeStringTo8BitUnpacked(recordSize - 2, email); + writtenEmail = ICCPDUHelper.writeStringTo8BitUnpacked(recordSize - 2, email); GsmPDUHelper.writeHexOctet(pbr.adn.sfi || 0xff); GsmPDUHelper.writeHexOctet(adnRecordId); } @@ -12564,11 +11875,17 @@ ICCRecordHelperObject.prototype = { Buf.writeStringDelimiter(strLen); }.bind(this); + let callback = (options) => { + if (onsuccess) { + onsuccess(writtenNumber); + } + } + this.context.ICCIOHelper.updateLinearFixedEF({ fileId: fileId, recordNumber: recordNumber, dataWriter: dataWriter, - callback: onsuccess, + callback: callback, onerror: onerror }); }, @@ -12641,6 +11958,7 @@ ICCRecordHelperObject.prototype = { updateANR: function(pbr, recordNumber, number, adnRecordId, onsuccess, onerror) { let fileId = pbr[USIM_PBR_ANR0].fileId; let fileType = pbr[USIM_PBR_ANR0].fileType; + let writtenNumber; let dataWriter = function dataWriter(recordSize) { let Buf = this.context.Buf; let GsmPDUHelper = this.context.GsmPDUHelper; @@ -12652,7 +11970,7 @@ ICCRecordHelperObject.prototype = { // EF_AAS record Id. Unused for now. GsmPDUHelper.writeHexOctet(0xff); - this.context.ICCPDUHelper.writeNumberWithLength(number); + writtenNumber = this.context.ICCPDUHelper.writeNumberWithLength(number); // Write unused octets 0xff, CCP and EXT1. GsmPDUHelper.writeHexOctet(0xff); @@ -12667,11 +11985,17 @@ ICCRecordHelperObject.prototype = { Buf.writeStringDelimiter(strLen); }.bind(this); + let callback = (options) => { + if (onsuccess) { + onsuccess(writtenEmail); + } + } + this.context.ICCIOHelper.updateLinearFixedEF({ fileId: fileId, recordNumber: recordNumber, dataWriter: dataWriter, - callback: onsuccess, + callback: callback, onerror: onerror }); }, @@ -12742,6 +12066,171 @@ ICCRecordHelperObject.prototype = { callback: callback.bind(this), onerror: onerror}); }, + + /** + * Read Extension Number from TS 151.011 clause 10.5.10, TS 31.102, clause 4.4.2.4 + * + * @param fileId EF Extension id + * @param recordNumber The number of the record shall be loaded. + * @param onsuccess Callback to be called when success. + * @param onerror Callback to be called when error. + */ + readExtension: function(fileId, recordNumber, onsuccess, onerror) { + let callback = (options) => { + let Buf = this.context.Buf; + let length = Buf.readInt32(); + let recordType = this.context.GsmPDUHelper.readHexOctet(); + let number = ""; + + // TS 31.102, clause 4.4.2.4 EFEXT1 + // Case 1, Extension1 record is additional data + if (recordType & 0x02) { + let numLen = this.context.GsmPDUHelper.readHexOctet(); + if (numLen != 0xff) { + if (numLen > EXT_MAX_BCD_NUMBER_BYTES) { + if (DEBUG) { + this.context.debug( + "Error: invalid length of BCD number/SSC contents - " + numLen); + } + // +1 to skip Identifier + Buf.seekIncoming((EXT_MAX_BCD_NUMBER_BYTES + 1) * Buf.PDU_HEX_OCTET_SIZE); + Buf.readStringDelimiter(length); + onerror(); + return; + } + + number = this.context.GsmPDUHelper.readSwappedNibbleExtendedBcdString(numLen); + if (DEBUG) this.context.debug("Contact Extension Number: "+ number); + Buf.seekIncoming((EXT_MAX_BCD_NUMBER_BYTES - numLen) * Buf.PDU_HEX_OCTET_SIZE); + } else { + Buf.seekIncoming(EXT_MAX_BCD_NUMBER_BYTES * Buf.PDU_HEX_OCTET_SIZE); + } + } else { + // Don't support Case 2, Extension1 record is Called Party Subaddress. + // +1 skip numLen + Buf.seekIncoming((EXT_MAX_BCD_NUMBER_BYTES + 1) * Buf.PDU_HEX_OCTET_SIZE); + } + + // Skip Identifier + Buf.seekIncoming(Buf.PDU_HEX_OCTET_SIZE); + Buf.readStringDelimiter(length); + onsuccess(number); + } + + this.context.ICCIOHelper.loadLinearFixedEF({ + fileId: fileId, + recordNumber: recordNumber, + callback: callback, + onerror: onerror + }); + }, + + /** + * Update Extension. + * + * @param fileId EF id of the EXT. + * @param recordNumber The number of the record shall be updated. + * @param number Dialling Number to be written. + * @param onsuccess Callback to be called when success. + * @param onerror Callback to be called when error. + */ + updateExtension: function(fileId, recordNumber, number, onsuccess, onerror) { + let dataWriter = (recordSize) => { + let GsmPDUHelper = this.context.GsmPDUHelper; + // Write String length + let strLen = recordSize * 2; + let Buf = this.context.Buf; + Buf.writeInt32(strLen); + + // We don't support extension chain. + if (number.length > EXT_MAX_NUMBER_DIGITS) { + number = number.substring(0, EXT_MAX_NUMBER_DIGITS); + } + + let numLen = Math.ceil(number.length / 2); + // Write Extension record + GsmPDUHelper.writeHexOctet(0x02); + GsmPDUHelper.writeHexOctet(numLen); + GsmPDUHelper.writeSwappedNibbleBCD(number); + // Write trailing 0xff of Extension data. + for (let i = 0; i < EXT_MAX_BCD_NUMBER_BYTES - numLen; i++) { + GsmPDUHelper.writeHexOctet(0xff); + } + // Write trailing 0xff for Identifier. + GsmPDUHelper.writeHexOctet(0xff); + Buf.writeStringDelimiter(strLen); + }; + + this.context.ICCIOHelper.updateLinearFixedEF({ + fileId: fileId, + recordNumber: recordNumber, + dataWriter: dataWriter, + callback: onsuccess, + onerror: onerror + }); + }, + + /** + * Clean an EF record. + * + * @param fileId EF id. + * @param recordNumber The number of the record shall be updated. + * @param onsuccess Callback to be called when success. + * @param onerror Callback to be called when error. + */ + cleanEFRecord: function(fileId, recordNumber, onsuccess, onerror) { + let dataWriter = (recordSize) => { + let GsmPDUHelper = this.context.GsmPDUHelper; + let Buf = this.context.Buf; + // Write String length + let strLen = recordSize * 2; + + Buf.writeInt32(strLen); + // Write record to 0xff + for (let i = 0; i < recordSize; i++) { + GsmPDUHelper.writeHexOctet(0xff); + } + Buf.writeStringDelimiter(strLen); + } + + this.context.ICCIOHelper.updateLinearFixedEF({ + fileId: fileId, + recordNumber: recordNumber, + dataWriter: dataWriter, + callback: onsuccess, + onerror: onerror + }); + }, + + /** + * Get ADNLike extension record number. + * + * @param fileId EF id of the ADN or FDN. + * @param recordNumber EF record id of the ADN or FDN. + * @param onsuccess Callback to be called when success. + * @param onerror Callback to be called when error. + */ + getADNLikeExtensionRecordNumber: function(fileId, recordNumber, onsuccess, onerror) { + let callback = (options) => { + let Buf = this.context.Buf; + let length = Buf.readInt32(); + + // Skip alphaLen, numLen, BCD Number, CCP octets. + Buf.seekIncoming((options.recordSize -1) * Buf.PDU_HEX_OCTET_SIZE); + + let extRecordNumber = this.context.GsmPDUHelper.readHexOctet(); + Buf.readStringDelimiter(length); + + onsuccess(extRecordNumber); + } + + this.context.ICCIOHelper.loadLinearFixedEF({ + fileId: fileId, + recordNumber: recordNumber, + callback: callback, + onerror: onerror + }); + }, }; /** @@ -14652,7 +14141,9 @@ ICCContactHelperObject.prototype = { switch (contactType) { case GECKO_CARDCONTACT_TYPE_ADN: if (!this.hasDfPhoneBook(appType)) { - ICCRecordHelper.readADNLike(ICC_EF_ADN, onsuccess, onerror); + ICCRecordHelper.readADNLike(ICC_EF_ADN, + (ICCUtilsHelper.isICCServiceAvailable("EXT1")) ? ICC_EF_EXT1 : null, + onsuccess, onerror); } else { this.readUSimContacts(onsuccess, onerror); } @@ -14662,7 +14153,9 @@ ICCContactHelperObject.prototype = { onerror(CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED); break; } - ICCRecordHelper.readADNLike(ICC_EF_FDN, onsuccess, onerror); + ICCRecordHelper.readADNLike(ICC_EF_FDN, + (ICCUtilsHelper.isICCServiceAvailable("EXT2")) ? ICC_EF_EXT2 : null, + onsuccess, onerror); break; case GECKO_CARDCONTACT_TYPE_SDN: if (!ICCUtilsHelper.isICCServiceAvailable("SDN")) { @@ -14670,7 +14163,9 @@ ICCContactHelperObject.prototype = { break; } - ICCRecordHelper.readADNLike(ICC_EF_SDN, onsuccess, onerror); + ICCRecordHelper.readADNLike(ICC_EF_SDN, + (ICCUtilsHelper.isICCServiceAvailable("EXT3")) ? ICC_EF_EXT3 : null, + onsuccess, onerror); break; default: if (DEBUG) { @@ -14793,12 +14288,26 @@ ICCContactHelperObject.prototype = { let ICCRecordHelper = this.context.ICCRecordHelper; let ICCUtilsHelper = this.context.ICCUtilsHelper; + let updateContactCb = (updatedContact) => { + updatedContact.pbrIndex = contact.pbrIndex; + updatedContact.recordId = contact.recordId; + onsuccess(updatedContact); + } + switch (contactType) { case GECKO_CARDCONTACT_TYPE_ADN: if (!this.hasDfPhoneBook(appType)) { - ICCRecordHelper.updateADNLike(ICC_EF_ADN, contact, null, onsuccess, onerror); + if (ICCUtilsHelper.isICCServiceAvailable("EXT1")) { + this.updateADNLikeWithExtension(ICC_EF_ADN, ICC_EF_EXT1, + contact, null, + updateContactCb, onerror); + } else { + ICCRecordHelper.updateADNLike(ICC_EF_ADN, 0xff, + contact, null, + updateContactCb, onerror); + } } else { - this.updateUSimContact(contact, onsuccess, onerror); + this.updateUSimContact(contact, updateContactCb, onerror); } break; case GECKO_CARDCONTACT_TYPE_FDN: @@ -14810,7 +14319,16 @@ ICCContactHelperObject.prototype = { onerror(CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED); break; } - ICCRecordHelper.updateADNLike(ICC_EF_FDN, contact, pin2, onsuccess, onerror); + if (ICCUtilsHelper.isICCServiceAvailable("EXT2")) { + this.updateADNLikeWithExtension(ICC_EF_FDN, ICC_EF_EXT2, + contact, pin2, + updateContactCb, onerror); + } else { + ICCRecordHelper.updateADNLike(ICC_EF_FDN, + 0xff, + contact, pin2, + updateContactCb, onerror); + } break; default: if (DEBUG) { @@ -14876,11 +14394,13 @@ ICCContactHelperObject.prototype = { * @param onerror Callback to be called when error. */ readPhonebookSet: function(pbr, onsuccess, onerror) { + let ICCRecordHelper = this.context.ICCRecordHelper; let gotAdnCb = function gotAdnCb(contacts) { this.readSupportedPBRFields(pbr, contacts, onsuccess, onerror); }.bind(this); - this.context.ICCRecordHelper.readADNLike(pbr.adn.fileId, gotAdnCb, onerror); + ICCRecordHelper.readADNLike(pbr.adn.fileId, + (pbr.ext1) ? pbr.ext1.fileId : null, gotAdnCb, onerror); }, /** @@ -15070,12 +14590,19 @@ ICCContactHelperObject.prototype = { * @param onerror Callback to be called when error. */ updatePhonebookSet: function(pbr, contact, onsuccess, onerror) { - let updateAdnCb = function() { - this.updateSupportedPBRFields(pbr, contact, onsuccess, onerror); + let updateAdnCb = function(updatedContact) { + this.updateSupportedPBRFields(pbr, contact, (updatedContactField) => { + onsuccess(Object.assign(updatedContact, updatedContactField)); + }, onerror); }.bind(this); - this.context.ICCRecordHelper.updateADNLike(pbr.adn.fileId, contact, null, - updateAdnCb, onerror); + if (pbr.ext1) { + this.updateADNLikeWithExtension(pbr.adn.fileId, pbr.ext1.fileId, + contact, null, updateAdnCb, onerror); + } else { + this.context.ICCRecordHelper.updateADNLike(pbr.adn.fileId, 0xff, contact, + null, updateAdnCb, onerror); + } }, /** @@ -15088,12 +14615,15 @@ ICCContactHelperObject.prototype = { */ updateSupportedPBRFields: function(pbr, contact, onsuccess, onerror) { let fieldIndex = 0; + let contactField = {}; + (function updateField() { let field = USIM_PBR_FIELDS[fieldIndex]; fieldIndex += 1; + if (!field) { if (onsuccess) { - onsuccess(); + onsuccess(contactField); } return; } @@ -15104,7 +14634,19 @@ ICCContactHelperObject.prototype = { return; } - this.updateContactField(pbr, contact, field, updateField.bind(this), onerror); + this.updateContactField(pbr, contact, field, (fieldEntry) => { + contactField = Object.assign(contactField, fieldEntry); + updateField.call(this); + }, (errorMsg) => { + // Bug 1194149, there are some sim cards without sufficient + // Type 2 USIM contact fields record. We allow user continue + // importing contacts. + if (errorMsg === CONTACT_ERR_NO_FREE_RECORD_FOUND) { + updateField.call(this); + return; + } + onerror(errorMsg); + }); }).call(this); }, @@ -15143,10 +14685,18 @@ ICCContactHelperObject.prototype = { let ICCRecordHelper = this.context.ICCRecordHelper; if (field === USIM_PBR_EMAIL) { - ICCRecordHelper.updateEmail(pbr, contact.recordId, contact.email, null, onsuccess, onerror); + ICCRecordHelper.updateEmail(pbr, contact.recordId, contact.email, null, + (updatedEmail) => { + onsuccess({email: updatedEmail}); + }, onerror); } else if (field === USIM_PBR_ANR0) { let anr = Array.isArray(contact.anr) ? contact.anr[0] : null; - ICCRecordHelper.updateANR(pbr, contact.recordId, anr, null, onsuccess, onerror); + ICCRecordHelper.updateANR(pbr, contact.recordId, anr, null, + (updatedANR) => { + // ANR could have multiple files. If we support more than one anr, + // we will save it as anr0, anr1,...etc. + onsuccess((updatedANR) ? {anr: [updatedANR]} : null); + }, onerror); } else { if (DEBUG) { this.context.debug("Unsupported field :" + field); @@ -15197,10 +14747,18 @@ ICCContactHelperObject.prototype = { // Case 2. if (field === USIM_PBR_EMAIL) { - ICCRecordHelper.updateEmail(pbr, recordId, contact.email, contact.recordId, onsuccess, onerror); + ICCRecordHelper.updateEmail(pbr, recordId, contact.email, contact.recordId, + (updatedEmail) => { + onsuccess({email: updatedEmail}); + }, onerror); } else if (field === USIM_PBR_ANR0) { let anr = Array.isArray(contact.anr) ? contact.anr[0] : null; - ICCRecordHelper.updateANR(pbr, recordId, anr, contact.recordId, onsuccess, onerror); + ICCRecordHelper.updateANR(pbr, recordId, anr, contact.recordId, + (updatedANR) => { + // ANR could have multiple files. If we support more than one anr, + // we will save it as anr0, anr1,...etc. + onsuccess((updatedANR) ? {anr: [updatedANR]} : null); + }, onerror); } else { if (DEBUG) { this.context.debug("Unsupported field :" + field); @@ -15224,16 +14782,26 @@ ICCContactHelperObject.prototype = { */ addContactFieldType2: function(pbr, contact, field, onsuccess, onerror) { let ICCRecordHelper = this.context.ICCRecordHelper; - let successCb = function successCb(recordId) { - let updateCb = function updateCb() { - this.updateContactFieldIndexInIAP(pbr, contact.recordId, field, recordId, onsuccess, onerror); + + let updateCb = function updateCb(contactField) { + this.updateContactFieldIndexInIAP(pbr, contact.recordId, field, recordId, () => { + onsuccess(contactField); + }, onerror); }.bind(this); if (field === USIM_PBR_EMAIL) { - ICCRecordHelper.updateEmail(pbr, recordId, contact.email, contact.recordId, updateCb, onerror); + ICCRecordHelper.updateEmail(pbr, recordId, contact.email, contact.recordId, + (updatedEmail) => { + updateCb({email: updatedEmail}); + }, onerror); } else if (field === USIM_PBR_ANR0) { - ICCRecordHelper.updateANR(pbr, recordId, contact.anr[0], contact.recordId, updateCb, onerror); + ICCRecordHelper.updateANR(pbr, recordId, contact.anr[0], contact.recordId, + (updatedANR) => { + // ANR could have multiple files. If we support more than one anr, + // we will save it as anr0, anr1,...etc. + updateCb((updatedANR) ? {anr: [updatedANR]} : null); + }, onerror); } }.bind(this); @@ -15267,6 +14835,74 @@ ICCContactHelperObject.prototype = { }.bind(this); ICCRecordHelper.readIAP(pbr.iap.fileId, recordNumber, gotIAPCb, onerror); }, + + /** + * Update ICC ADN like EFs with Extension, like EF_ADN, EF_FDN. + * + * @param fileId EF id of the ADN or FDN. + * @param extFileId EF id of the EXT. + * @param contact The contact will be updated. (Shall have recordId property) + * @param pin2 PIN2 is required when updating ICC_EF_FDN. + * @param onsuccess Callback to be called when success. + * @param onerror Callback to be called when error. + */ + updateADNLikeWithExtension: function(fileId, extFileId, contact, pin2, onsuccess, onerror) { + let ICCRecordHelper = this.context.ICCRecordHelper; + let extNumber; + + if (contact.number) { + let numStart = contact.number[0] == "+" ? 1 : 0; + let number = contact.number.substring(0, numStart) + + this.context.GsmPDUHelper.stringToExtendedBcd( + contact.number.substring(numStart)); + extNumber = number.substr(numStart + ADN_MAX_NUMBER_DIGITS, + EXT_MAX_NUMBER_DIGITS); + } + + ICCRecordHelper.getADNLikeExtensionRecordNumber(fileId, contact.recordId, + (extRecordNumber) => { + let updateADNLike = (extRecordNumber) => { + ICCRecordHelper.updateADNLike(fileId, extRecordNumber, contact, + pin2, (updatedContact) => { + if (extNumber && extRecordNumber != 0xff) { + updatedContact.number = updatedContact.number.concat(extNumber); + } + onsuccess(updatedContact); + }, onerror); + }; + + let updateExtension = (extRecordNumber) => { + ICCRecordHelper.updateExtension(extFileId, extRecordNumber, extNumber, + () => updateADNLike(extRecordNumber), + () => updateADNLike(0xff)); + }; + + if (extNumber) { + if (extRecordNumber != 0xff) { + updateExtension(extRecordNumber); + return; + } + + ICCRecordHelper.findFreeRecordId(extFileId, + (extRecordNumber) => updateExtension(extRecordNumber), + (errorMsg) => { + if (DEBUG) { + this.context.debug("Couldn't find free extension record Id for " + extFileId + ": " + errorMsg); + } + updateADNLike(0xff); + }); + return; + } + + if (extRecordNumber != 0xff) { + ICCRecordHelper.cleanEFRecord(extFileId, extRecordNumber, + () => updateADNLike(0xff), onerror); + return; + } + + updateADNLike(0xff); + }, onerror); + }, }; function IconLoaderObject(aContext) { @@ -15485,7 +15121,6 @@ let ContextPool = { let quirks = aOptions.quirks; RILQUIRKS_CALLSTATE_EXTRA_UINT32 = quirks.callstateExtraUint32; - RILQUIRKS_V5_LEGACY = quirks.v5Legacy; RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL = quirks.requestUseDialEmergencyCall; RILQUIRKS_SIM_APP_STATE_EXTRA_FIELDS = quirks.simAppStateExtraFields; RILQUIRKS_EXTRA_UINT32_2ND_CALL = quirks.extraUint2ndCall; @@ -15494,6 +15129,7 @@ let ContextPool = { RILQUIRKS_DATA_REGISTRATION_ON_DEMAND = quirks.dataRegistrationOnDemand; RILQUIRKS_SUBSCRIPTION_CONTROL = quirks.subscriptionControl; RILQUIRKS_SIGNAL_EXTRA_INT32 = quirks.signalExtraInt; + RILQUIRKS_AVAILABLE_NETWORKS_EXTRA_STRING = quirks.availableNetworkExtraStr; RILQUIRKS_SMSC_ADDRESS_FORMAT = quirks.smscAddressFormat; }, diff --git a/dom/system/gonk/tests/test_ril_worker_cellbroadcast_config.js b/dom/system/gonk/tests/test_ril_worker_cellbroadcast_config.js index aa1a5616bb..92b3639ccf 100644 --- a/dom/system/gonk/tests/test_ril_worker_cellbroadcast_config.js +++ b/dom/system/gonk/tests/test_ril_worker_cellbroadcast_config.js @@ -156,7 +156,7 @@ add_test(function test_ril_worker_cellbroadcast_set_search_list() { context.RIL.setCellBroadcastSearchList(options); // Enforce the MMI result to string for comparison. equal("" + context.RIL.cellBroadcastConfigs.MMI, aExpected); - equal(options.success, true); + do_check_eq(options.errorMsg, undefined); } let searchListStr = "1,2,3,4"; diff --git a/dom/system/gonk/tests/test_ril_worker_cf.js b/dom/system/gonk/tests/test_ril_worker_cf.js index 83d45b4453..b8db716b7c 100644 --- a/dom/system/gonk/tests/test_ril_worker_cf.js +++ b/dom/system/gonk/tests/test_ril_worker_cf.js @@ -71,8 +71,7 @@ add_test(function test_setCallForward_unconditional() { let postedMessage = workerHelper.postedMessage; - equal(postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); + equal(postedMessage.errorMsg, undefined); run_next_test(); }); @@ -116,8 +115,7 @@ add_test(function test_queryCallForwardStatus_unconditional() { let postedMessage = workerHelper.postedMessage; - equal(postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); + equal(postedMessage.errorMsg, undefined); ok(Array.isArray(postedMessage.rules)); do_print(postedMessage.rules.length); equal(postedMessage.rules.length, 1); diff --git a/dom/system/gonk/tests/test_ril_worker_clip.js b/dom/system/gonk/tests/test_ril_worker_clip.js index 9393b808a4..d1ce5f6179 100644 --- a/dom/system/gonk/tests/test_ril_worker_clip.js +++ b/dom/system/gonk/tests/test_ril_worker_clip.js @@ -29,7 +29,6 @@ add_test(function test_queryCLIP_provisioned() { let postedMessage = workerHelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); equal(postedMessage.provisioned, 1); run_next_test(); }); @@ -55,7 +54,6 @@ add_test(function test_getCLIP_error_generic_failure_invalid_length() { let postedMessage = workerHelper.postedMessage; - equal(postedMessage.errorMsg, "GenericFailure"); - ok(!postedMessage.success); + equal(postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); run_next_test(); }); diff --git a/dom/system/gonk/tests/test_ril_worker_clir.js b/dom/system/gonk/tests/test_ril_worker_clir.js index 2c679e4071..5882a3c4cc 100644 --- a/dom/system/gonk/tests/test_ril_worker_clir.js +++ b/dom/system/gonk/tests/test_ril_worker_clir.js @@ -34,7 +34,6 @@ add_test(function test_setCLIR_success() { let postedMessage = workerHelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); run_next_test(); }); @@ -57,8 +56,7 @@ add_test(function test_setCLIR_generic_failure() { let postedMessage = workerHelper.postedMessage; - equal(postedMessage.errorMsg, "GenericFailure"); - ok(!postedMessage.success); + equal(postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); run_next_test(); }); @@ -89,7 +87,6 @@ add_test(function test_getCLIR_n0_m1() { let postedMessage = workerHelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); equal(postedMessage.n, 0); equal(postedMessage.m, 1); run_next_test(); @@ -120,7 +117,6 @@ add_test(function test_getCLIR_error_generic_failure_invalid_length() { let postedMessage = workerHelper.postedMessage; - equal(postedMessage.errorMsg, "GenericFailure"); - ok(!postedMessage.success); + equal(postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); run_next_test(); }); diff --git a/dom/system/gonk/tests/test_ril_worker_cw.js b/dom/system/gonk/tests/test_ril_worker_cw.js index d62d93a22e..efa8b5c21d 100644 --- a/dom/system/gonk/tests/test_ril_worker_cw.js +++ b/dom/system/gonk/tests/test_ril_worker_cw.js @@ -23,7 +23,6 @@ add_test(function test_setCallWaiting_success() { let postedMessage = workerHelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); run_next_test(); }); @@ -45,8 +44,7 @@ add_test(function test_setCallWaiting_generic_failure() { let postedMessage = workerHelper.postedMessage; - equal(postedMessage.errorMsg, "GenericFailure"); - ok(!postedMessage.success); + equal(postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); run_next_test(); }); @@ -64,7 +62,7 @@ add_test(function test_queryCallWaiting_success_enabled_true() { context.Buf.int32Array = [ 1, // serviceClass 1, // enabled - 1 // length + 2 // length ]; context.RIL[REQUEST_QUERY_CALL_WAITING](1, {}); }; @@ -74,9 +72,7 @@ add_test(function test_queryCallWaiting_success_enabled_true() { let postedMessage = workerHelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); - equal(postedMessage.length, 1); - ok(postedMessage.enabled); + equal(postedMessage.serviceClass, 1); run_next_test(); }); @@ -93,7 +89,7 @@ add_test(function test_queryCallWaiting_success_enabled_false() { context.Buf.int32Array = [ 1, // serviceClass 0, // enabled - 1 // length + 2 // length ]; context.RIL[REQUEST_QUERY_CALL_WAITING](1, {}); }; @@ -103,8 +99,6 @@ add_test(function test_queryCallWaiting_success_enabled_false() { let postedMessage = workerHelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); - equal(postedMessage.length, 1); - ok(!postedMessage.enabled); + equal(postedMessage.serviceClass, ICC_SERVICE_CLASS_NONE); run_next_test(); }); diff --git a/dom/system/gonk/tests/test_ril_worker_icc_CardLock.js b/dom/system/gonk/tests/test_ril_worker_icc_CardLock.js index c0eb0d919b..dc7eb93b9d 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc_CardLock.js +++ b/dom/system/gonk/tests/test_ril_worker_icc_CardLock.js @@ -16,7 +16,6 @@ add_test(function test_icc_get_card_lock_enabled() { let buf = context.Buf; let ril = context.RIL; ril.aid = "123456789"; - ril.v5Legacy = false; function do_test(aLock) { const serviceClass = ICC_SERVICE_CLASS_VOICE | @@ -32,13 +31,11 @@ add_test(function test_icc_get_card_lock_enabled() { // Data let parcel = this.readStringList(); - equal(parcel.length, ril.v5Legacy ? 3 : 4); + equal(parcel.length, 4); equal(parcel[0], GECKO_CARDLOCK_TO_FACILITY[aLock]); equal(parcel[1], ""); equal(parcel[2], serviceClass.toString()); - if (!ril.v5Legacy) { - equal(parcel[3], ril.aid); - } + equal(parcel[3], ril.aid); }; ril.iccGetCardLockEnabled({lockType: aLock}); @@ -87,7 +84,6 @@ add_test(function test_icc_set_card_lock_enabled() { let buf = context.Buf; let ril = context.RIL; ril.aid = "123456789"; - ril.v5Legacy = false; function do_test(aLock, aPassword, aEnabled) { const serviceClass = ICC_SERVICE_CLASS_VOICE | @@ -103,14 +99,12 @@ add_test(function test_icc_set_card_lock_enabled() { // Data let parcel = this.readStringList(); - equal(parcel.length, ril.v5Legacy ? 4 : 5); + equal(parcel.length, 5); equal(parcel[0], GECKO_CARDLOCK_TO_FACILITY[aLock]); equal(parcel[1], aEnabled ? "1" : "0"); equal(parcel[2], aPassword); equal(parcel[3], serviceClass.toString()); - if (!ril.v5Legacy) { - equal(parcel[4], ril.aid); - } + equal(parcel[4], ril.aid); }; ril.iccSetCardLockEnabled({ @@ -151,12 +145,10 @@ add_test(function test_icc_change_card_lock_password() { // Data let parcel = this.readStringList(); - equal(parcel.length, ril.v5Legacy ? 2 : 3); + equal(parcel.length, 3); equal(parcel[0], aPassword); equal(parcel[1], aNewPassword); - if (!ril.v5Legacy) { - equal(parcel[2], ril.aid); - } + equal(parcel[2], ril.aid); }; ril.iccChangeCardLockPassword({ @@ -180,7 +172,6 @@ add_test(function test_icc_unlock_card_lock_pin() { let ril = context.RIL; let buf = context.Buf; ril.aid = "123456789"; - ril.v5Legacy = false; function do_test(aLock, aPassword) { let GECKO_CARDLOCK_TO_REQUEST = {}; @@ -196,11 +187,9 @@ add_test(function test_icc_unlock_card_lock_pin() { // Data let parcel = this.readStringList(); - equal(parcel.length, ril.v5Legacy ? 1 : 2); + equal(parcel.length, 2); equal(parcel[0], aPassword); - if (!ril.v5Legacy) { - equal(parcel[1], ril.aid); - } + equal(parcel[1], ril.aid); }; ril.iccUnlockCardLock({ @@ -224,7 +213,6 @@ add_test(function test_icc_unlock_card_lock_puk() { let ril = context.RIL; let buf = context.Buf; ril.aid = "123456789"; - ril.v5Legacy = false; function do_test(aLock, aPassword, aNewPin) { let GECKO_CARDLOCK_TO_REQUEST = {}; @@ -240,12 +228,10 @@ add_test(function test_icc_unlock_card_lock_puk() { // Data let parcel = this.readStringList(); - equal(parcel.length, ril.v5Legacy ? 2 : 3); + equal(parcel.length, 3); equal(parcel[0], aPassword); equal(parcel[1], aNewPin); - if (!ril.v5Legacy) { - equal(parcel[2], ril.aid); - } + equal(parcel[2], ril.aid); }; ril.iccUnlockCardLock({ diff --git a/dom/system/gonk/tests/test_ril_worker_icc_ICCContactHelper.js b/dom/system/gonk/tests/test_ril_worker_icc_ICCContactHelper.js index ec8a68d5a9..29b83b76a8 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc_ICCContactHelper.js +++ b/dom/system/gonk/tests/test_ril_worker_icc_ICCContactHelper.js @@ -352,7 +352,7 @@ add_test(function test_read_icc_contacts() { onsuccess(JSON.parse(JSON.stringify(aTestData.pbrs))); }; - record.readADNLike = function readADNLike(fileId, onsuccess, onerror) { + record.readADNLike = function readADNLike(fileId, extFileId, onsuccess, onerror) { onsuccess(JSON.parse(JSON.stringify(aTestData.adnLike))); }; @@ -398,6 +398,7 @@ add_test(function test_update_icc_contact() { const EMAIL_RECORD_ID = 20; const ANR0_FILE_ID = 0x4f11; const ANR0_RECORD_ID = 30; + const EXT_RECORD_ID = 0x01; let worker = newUint8Worker(); let context = worker.ContextPool._contexts[0]; @@ -408,11 +409,11 @@ add_test(function test_update_icc_contact() { function do_test(aSimType, aContactType, aContact, aPin2, aFileType, aHaveIapIndex, aEnhancedPhoneBook) { ril.appType = aSimType; ril._isCdma = (aSimType === CARD_APPTYPE_RUIM); - ril.iccInfoPrivate.cst = (aEnhancedPhoneBook) ? [0x20, 0x0C, 0x0, 0x0, 0x0] - : [0x20, 0x00, 0x0, 0x0, 0x0]; + ril.iccInfoPrivate.cst = (aEnhancedPhoneBook) ? [0x20, 0x0C, 0x28, 0x0, 0x20] + : [0x20, 0x0, 0x28, 0x0, 0x20]; ril.iccInfoPrivate.sst = (aSimType === CARD_APPTYPE_SIM)? - [0x20, 0x0, 0x0, 0x0, 0x0]: - [0x2, 0x0, 0x0, 0x0, 0x0]; + [0x20, 0x0, 0x28, 0x0, 0x20]: + [0x16, 0x0, 0x0, 0x0, 0x0]; recordHelper.readPBR = function(onsuccess, onerror) { if (aFileType === ICC_USIM_TYPE1_TAG) { @@ -421,7 +422,9 @@ add_test(function test_update_icc_contact() { email: {fileId: EMAIL_FILE_ID, fileType: ICC_USIM_TYPE1_TAG}, anr0: {fileId: ANR0_FILE_ID, - fileType: ICC_USIM_TYPE1_TAG} + fileType: ICC_USIM_TYPE1_TAG}, + ext1: {fileId: ICC_EF_EXT1} + }]); } else if (aFileType === ICC_USIM_TYPE2_TAG) { onsuccess([{ @@ -433,23 +436,48 @@ add_test(function test_update_icc_contact() { indexInIAP: 0}, anr0: {fileId: ANR0_FILE_ID, fileType: ICC_USIM_TYPE2_TAG, - indexInIAP: 1} + indexInIAP: 1}, + ext1: {fileId: ICC_EF_EXT1} }]); } }; - recordHelper.updateADNLike = function(fileId, contact, pin2, onsuccess, onerror) { + recordHelper.updateADNLike = function(fileId, extRecordNumber, contact, pin2, onsuccess, onerror) { if (aContactType === GECKO_CARDCONTACT_TYPE_FDN) { equal(fileId, ICC_EF_FDN); } else if (aContactType === GECKO_CARDCONTACT_TYPE_ADN) { equal(fileId, ICC_EF_ADN); } + + if (aContact.number.length > ADN_MAX_NUMBER_DIGITS) { + equal(extRecordNumber, EXT_RECORD_ID); + } else { + equal(extRecordNumber, 0xff); + } + equal(pin2, aPin2); equal(contact.alphaId, aContact.alphaId); equal(contact.number, aContact.number); + onsuccess({alphaId: contact.alphaId, + number: contact.number.substring(0, ADN_MAX_NUMBER_DIGITS)}); + }; + + recordHelper.getADNLikeExtensionRecordNumber = function(fileId, recordNumber, onsuccess, onerror) { + onsuccess(EXT_RECORD_ID); + }; + + recordHelper.updateExtension = function(fileId, recordNumber, number, onsuccess, onerror) { onsuccess(); }; + recordHelper.findFreeRecordId = function(fileId, onsuccess, onerror) { + onsuccess(EXT_RECORD_ID); + }; + + recordHelper.cleanEFRecord = function(fileId, recordNumber, onsuccess, onerror) { + onsuccess(); + } + recordHelper.readIAP = function(fileId, recordNumber, onsuccess, onerror) { equal(fileId, IAP_FILE_ID); equal(recordNumber, ADN_RECORD_ID); @@ -471,7 +499,7 @@ add_test(function test_update_icc_contact() { equal(recordNumber, EMAIL_RECORD_ID); } equal(email, aContact.email); - onsuccess(); + onsuccess(email); }; recordHelper.updateANR = function(pbr, recordNumber, number, adnRecordId, onsuccess, onerror) { @@ -484,7 +512,7 @@ add_test(function test_update_icc_contact() { if (Array.isArray(aContact.anr)) { equal(number, aContact.anr[0]); } - onsuccess(); + onsuccess(number); }; recordHelper.findFreeRecordId = function(fileId, onsuccess, onerror) { @@ -498,7 +526,25 @@ add_test(function test_update_icc_contact() { }; let isSuccess = false; - let onsuccess = function onsuccess() { + let onsuccess = function onsuccess(updatedContact) { + equal(ADN_RECORD_ID, updatedContact.recordId); + equal(aContact.alphaId, updatedContact.alphaId); + equal(aContact.number.substring(0, ADN_MAX_NUMBER_DIGITS + EXT_MAX_NUMBER_DIGITS), + updatedContact.number); + if ((aSimType == CARD_APPTYPE_USIM || aSimType == CARD_APPTYPE_RUIM) && + (aFileType == ICC_USIM_TYPE1_TAG || aFileType == ICC_USIM_TYPE2_TAG)) { + if (aContact.hasOwnProperty('email')) { + equal(aContact.email, updatedContact.email); + } + + if (aContact.hasOwnProperty('anr')) { + equal(aContact.anr[0], updatedContact.anr[0]); + } + } else { + equal(updatedContact.email, null); + equal(updatedContact.anr, null); + } + do_print("updateICCContact success"); isSuccess = true; }; @@ -542,6 +588,22 @@ add_test(function test_update_icc_contact() { alphaId: "test4", number: "123456", anr: ["+654321"] + }, + // a contact number over 20 digits. + { + pbrIndex: 0, + recordId: ADN_RECORD_ID, + alphaId: "test4", + number: "0123456789012345678901234567890123456789", + anr: ["+654321"] + }, + // a contact number over 40 digits. + { + pbrIndex: 0, + recordId: ADN_RECORD_ID, + alphaId: "test5", + number: "01234567890123456789012345678901234567890123456789", + anr: ["+654321"] }]; for (let i = 0; i < contacts.length; i++) { @@ -589,6 +651,111 @@ add_test(function test_update_icc_contact() { run_next_test(); }); +/** + * Verify ICCContactHelper.updateICCContact with appType is CARD_APPTYPE_USIM and + * insufficient space to store Type 2 USIM contact fields. + */ +add_test(function test_update_icc_contact_full_email_and_anr_field() { + const ADN_RECORD_ID = 100; + const ADN_SFI = 1; + const IAP_FILE_ID = 0x4f17; + const EMAIL_FILE_ID = 0x4f50; + const EMAIL_RECORD_ID = 20; + const ANR0_FILE_ID = 0x4f11; + const ANR0_RECORD_ID = 30; + + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let recordHelper = context.ICCRecordHelper; + let contactHelper = context.ICCContactHelper; + let ril = context.RIL; + + function do_test(aSimType, aContactType, aContact, aPin2) { + ril.appType = CARD_APPTYPE_USIM; + ril.iccInfoPrivate.sst = [0x2, 0x0, 0x0, 0x0, 0x0]; + + recordHelper.readPBR = function(onsuccess, onerror) { + onsuccess([{ + adn: {fileId: ICC_EF_ADN, + sfi: ADN_SFI}, + iap: {fileId: IAP_FILE_ID}, + email: {fileId: EMAIL_FILE_ID, + fileType: ICC_USIM_TYPE2_TAG, + indexInIAP: 0}, + anr0: {fileId: ANR0_FILE_ID, + fileType: ICC_USIM_TYPE2_TAG, + indexInIAP: 1} + }]); + }; + + recordHelper.updateADNLike = function(fileId, extRecordNumber, contact, pin2, onsuccess, onerror) { + if (aContactType === GECKO_CARDCONTACT_TYPE_ADN) { + equal(fileId, ICC_EF_ADN); + } + equal(pin2, aPin2); + equal(contact.alphaId, aContact.alphaId); + equal(contact.number, aContact.number); + onsuccess({alphaId: contact.alphaId, + number: contact.number}); + }; + + recordHelper.readIAP = function(fileId, recordNumber, onsuccess, onerror) { + equal(fileId, IAP_FILE_ID); + equal(recordNumber, ADN_RECORD_ID); + onsuccess([0xff, 0xff]); + }; + + recordHelper.updateIAP = function(fileId, recordNumber, iap, onsuccess, onerror) { + equal(fileId, IAP_FILE_ID); + equal(recordNumber, ADN_RECORD_ID); + onsuccess(); + }; + + recordHelper.findFreeRecordId = function(fileId, onsuccess, onerror) { + let recordId = 0; + // emulate email and anr don't have free record. + if (fileId === EMAIL_FILE_ID || fileId === ANR0_FILE_ID) { + onerror(CONTACT_ERR_NO_FREE_RECORD_FOUND); + } else { + onsuccess(recordId); + } + }; + + let isSuccess = false; + let onsuccess = function onsuccess(updatedContact) { + equal(ADN_RECORD_ID, updatedContact.recordId); + equal(aContact.alphaId, updatedContact.alphaId); + equal(updatedContact.email, null); + equal(updatedContact.anr, null); + + do_print("updateICCContact success"); + isSuccess = true; + }; + + let onerror = function onerror(errorMsg) { + do_print("updateICCContact failed: " + errorMsg); + }; + + contactHelper.updateICCContact(aSimType, aContactType, aContact, aPin2, onsuccess, onerror); + ok(isSuccess); + } + + let contact = { + pbrIndex: 0, + recordId: ADN_RECORD_ID, + alphaId: "test", + number: "123456", + email: "test@mail.com", + anr: ["+654321"] + }; + + // USIM + do_print("Test update USIM adn contacts"); + do_test(CARD_APPTYPE_USIM, GECKO_CARDCONTACT_TYPE_ADN, contact, null); + + run_next_test(); +}); + /** * Verify updateICCContact with removal of anr and email with File Type 1. */ @@ -605,8 +772,9 @@ add_test(function test_update_icc_contact_with_remove_type1_attr() { let recordHelper = context.ICCRecordHelper; let contactHelper = context.ICCContactHelper; - recordHelper.updateADNLike = function(fileId, contact, pin2, onsuccess, onerror) { - onsuccess(); + recordHelper.updateADNLike = function(fileId, extRecordNumber, contact, pin2, onsuccess, onerror) { + onsuccess({alphaId: contact.alphaId, + number: contact.number}); }; let contact = { @@ -622,12 +790,12 @@ add_test(function test_update_icc_contact_with_remove_type1_attr() { recordHelper.updateEmail = function(pbr, recordNumber, email, adnRecordId, onsuccess, onerror) { ok(email == null); - onsuccess(); + onsuccess(email); }; recordHelper.updateANR = function(pbr, recordNumber, number, adnRecordId, onsuccess, onerror) { ok(number == null); - onsuccess(); + onsuccess(number); }; function do_test(type) { @@ -652,7 +820,9 @@ add_test(function test_update_icc_contact_with_remove_type1_attr() { } }; - let successCb = function() { + let successCb = function(updatedContact) { + equal(updatedContact.email, null); + equal(updatedContact.anr, null); ok(true); }; @@ -782,3 +952,91 @@ add_test(function test_find_free_icc_contact_usim() { run_next_test(); }); + +/** + * Verify ICCContactHelper.updateADNLikeWithExtension + */ +add_test(function test_update_adn_like_with_extension() { + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let ril = context.RIL; + let record = context.ICCRecordHelper; + let contactHelper = context.ICCContactHelper; + ril.appType = CARD_APPTYPE_SIM; + // Correct record Id starts from 1, so put a null element at index 0. + // ext_records contains data at index 1, and it only has 1 free record at index 2. + let notFree = 0x01; + let ext_records = [null, notFree, null]; + + function do_test(contact, extRecordNumber, expectedExtRecordNumber, expectedNumber, expectedCleanEFRecord) { + // Override some functions to test. + record.getADNLikeExtensionRecordNumber = function(fileId, recordNumber, onsuccess, onerror) { + onsuccess(extRecordNumber); + } + + record.updateADNLike = function(fileId, extRecordNumber, contact, pin2, onsuccess, onerror) { + equal(extRecordNumber, expectedExtRecordNumber); + onsuccess({alphaId: contact.alphaId, + number: contact.number.substring(0, ADN_MAX_NUMBER_DIGITS)}); + } + + record.updateExtension = function(fileId, recordNumber, number, onsuccess, onerror) { + if (recordNumber > ext_records.length) { + onerror("updateExtension failed."); + return; + } + ext_records[recordNumber] = number; + onsuccess(); + } + + record.findFreeRecordId = function(fileId, onsuccess, onerror) { + for (let i = 1; i < ext_records.length; i++) { + if (!ext_records[i]) { + onsuccess(i); + return; + } + } + + onerror("No free record found."); + } + + let isCleanEFRecord = false; + record.cleanEFRecord = function(fileId, recordNumber, onsuccess, onerror) { + if (recordNumber > ext_records.length) { + onerror("cleanEFRecord failed."); + return; + } + ext_records[recordNumber] = null; + isCleanEFRecord = true; + onsuccess(); + } + + let successCb = function successCb(updatedContact) { + equal(updatedContact.number, expectedNumber); + }; + + let errorCb = function errorCb(errorMsg) { + do_print("updateADNLikeWithExtension failed, msg = " + errorMsg); + ok(false); + }; + + contactHelper.updateADNLikeWithExtension(ICC_EF_ADN, ICC_EF_EXT1, contact, null, successCb, errorCb); + + if (expectedCleanEFRecord) { + ok(isCleanEFRecord); + } + } + + // Update extension record with previous extension record number. + do_test({recordId: 1, alphaId: "test", number: "001122334455667788991234"}, 0x01, 0x01, "001122334455667788991234"); + // Update extension record and find a free record. + do_test({recordId: 1, alphaId: "test", number: "001122334455667788995678"}, 0xff, 0x02, "001122334455667788995678"); + // Update extension record with no free extension record. + do_test({recordId: 1, alphaId: "test", number: "001122334455667788994321"}, 0xff, 0xff, "00112233445566778899"); + // Update extension record with clean previous extension record. + do_test({recordId: 1, alphaId: "test", number: "00112233445566778899"}, 0x01, 0xff, "00112233445566778899", true); + // Update extension record with no extension record and previous extension record. + do_test({recordId: 1, alphaId: "test", number: "00112233445566778899"}, 0xff, 0xff, "00112233445566778899"); + + run_next_test(); +}); \ No newline at end of file diff --git a/dom/system/gonk/tests/test_ril_worker_icc_ICCPDUHelper.js b/dom/system/gonk/tests/test_ril_worker_icc_ICCPDUHelper.js index f35bb55376..49855c3962 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc_ICCPDUHelper.js +++ b/dom/system/gonk/tests/test_ril_worker_icc_ICCPDUHelper.js @@ -47,6 +47,84 @@ add_test(function test_read_icc_ucs2_string() { run_next_test(); }); +/** + * Verify ICCPDUHelper#writeICCUCS2String() + */ +add_test(function test_write_icc_ucs2_string() { + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let helper = context.GsmPDUHelper; + let iccHelper = context.ICCPDUHelper; + let alphaLen = 18; + let test_data = [ + { + encode: 0x80, + // string only contain one character. + data: "\u82b3" + }, { + encode: 0x80, + // 2 UCS2 character not located in the same half-page. + data: "Fire \u82b3\u8233" + }, { + encode: 0x80, + // 2 UCS2 character not located in the same half-page. + data: "\u694a\u704a" + }, { + encode: 0x81, + // 2 UCS2 character within same half-page. + data: "Fire \u6901\u697f" + }, { + encode: 0x81, + // 2 UCS2 character within same half-page. + data: "Fire \u6980\u69ff" + }, { + encode: 0x82, + // 2 UCS2 character within same half-page, but bit 8 is different. + data: "Fire \u0514\u0593" + }, { + encode: 0x82, + // 2 UCS2 character over 0x81 can encode range. + data: "Fire \u8000\u8001" + }, { + encode: 0x82, + // 2 UCS2 character over 0x81 can encode range. + data: "Fire \ufffd\ufffe" + }]; + + for (let i = 0; i < test_data.length; i++) { + let test = test_data[i]; + let writtenStr = iccHelper.writeICCUCS2String(alphaLen, test.data); + equal(writtenStr, test.data); + equal(helper.readHexOctet(), test.encode); + equal(iccHelper.readICCUCS2String(test.encode, alphaLen - 1), test.data); + } + + // This string use 0x80 encoded and the maximum capacity is 17 octets. + // Each alphabet takes 2 octets, thus the first 8 alphabets can be written. + let str = "Mozilla \u82b3\u8233 On Fire"; + let writtenStr = iccHelper.writeICCUCS2String(alphaLen, str); + equal(writtenStr, str.substring(0, 8)); + equal(helper.readHexOctet(), 0x80); + equal(iccHelper.readICCUCS2String(0x80, alphaLen - 1), str.substring(0, 8)); + + // This string use 0x81 encoded and the maximum capacity is 15 octets. + // Each alphabet takes 1 octets, thus the first 15 alphabets can be written. + str = "Mozilla \u6901\u697f On Fire"; + writtenStr = iccHelper.writeICCUCS2String(alphaLen, str); + equal(writtenStr, str.substring(0, 15)); + equal(helper.readHexOctet(), 0x81); + equal(iccHelper.readICCUCS2String(0x81, alphaLen - 1), str.substring(0, 15)); + + // This string use 0x82 encoded and the maximum capacity is 14 octets. + // Each alphabet takes 1 octets, thus the first 14 alphabets can be written. + str = "Mozilla \u0514\u0593 On Fire"; + writtenStr = iccHelper.writeICCUCS2String(alphaLen, str); + equal(writtenStr, str.substring(0, 14)); + equal(helper.readHexOctet(), 0x82); + equal(iccHelper.readICCUCS2String(0x82, alphaLen - 1), str.substring(0, 14)); + + run_next_test(); +}); /** * Verify ICCPDUHelper#readDiallingNumber */ @@ -154,7 +232,8 @@ add_test(function test_write_string_to_8bit_unpacked() { let str; // Test 1, write GSM alphabets. - iccHelper.writeStringTo8BitUnpacked(langTable.length + ffLen, langTable); + let writtenStr = iccHelper.writeStringTo8BitUnpacked(langTable.length + ffLen, langTable); + equal(writtenStr, langTable); for (let i = 0; i < langTable.length; i++) { equal(helper.readHexOctet(), i); @@ -166,8 +245,8 @@ add_test(function test_write_string_to_8bit_unpacked() { // Test 2, write GSM extended alphabets. str = "\u000c\u20ac"; - iccHelper.writeStringTo8BitUnpacked(4, str); - + writtenStr = iccHelper.writeStringTo8BitUnpacked(4, str); + equal(writtenStr, str); equal(iccHelper.read8BitUnpackedToString(4), str); // Test 3, write GSM and GSM extended alphabets. @@ -179,8 +258,8 @@ add_test(function test_write_string_to_8bit_unpacked() { // 1 octet for 1 gsm alphabet, // 2 octes for trailing 0xff. // "Totally 7 octets are to be written." - iccHelper.writeStringTo8BitUnpacked(7, str); - + writtenStr = iccHelper.writeStringTo8BitUnpacked(7, str); + equal(writtenStr, str); equal(iccHelper.read8BitUnpackedToString(7), str); run_next_test(); @@ -199,7 +278,8 @@ add_test(function test_write_string_to_8bit_unpacked_with_max_octets_written() { // The maximum of the number of octets that can be written is 3. // Only 3 characters shall be written even the length of the string is 4. - iccHelper.writeStringTo8BitUnpacked(3, langTable.substring(0, 4)); + let writtenStr = iccHelper.writeStringTo8BitUnpacked(3, langTable.substring(0, 4)); + equal(writtenStr, langTable.substring(0, 3)); helper.writeHexOctet(0xff); // dummy octet. for (let i = 0; i < 3; i++) { equal(helper.readHexOctet(), i); @@ -209,18 +289,21 @@ add_test(function test_write_string_to_8bit_unpacked_with_max_octets_written() { // \u000c is GSM extended alphabet, 2 octets. // \u00a3 is GSM alphabet, 1 octet. let str = "\u000c\u00a3"; - iccHelper.writeStringTo8BitUnpacked(3, str); + writtenStr = iccHelper.writeStringTo8BitUnpacked(3, str); + equal(writtenStr, str.substring(0, 2)); equal(iccHelper.read8BitUnpackedToString(3), str); str = "\u00a3\u000c"; - iccHelper.writeStringTo8BitUnpacked(3, str); + writtenStr = iccHelper.writeStringTo8BitUnpacked(3, str); + equal(writtenStr, str.substring(0, 2)); equal(iccHelper.read8BitUnpackedToString(3), str); // 2 GSM extended alphabets cost 4 octets, but maximum is 3, so only the 1st // alphabet can be written. str = "\u000c\u000c"; - iccHelper.writeStringTo8BitUnpacked(3, str); + writtenStr = iccHelper.writeStringTo8BitUnpacked(3, str); helper.writeHexOctet(0xff); // dummy octet. + equal(writtenStr, str.substring(0, 1)); equal(iccHelper.read8BitUnpackedToString(4), str.substring(0, 1)); run_next_test(); @@ -283,36 +366,42 @@ add_test(function test_write_alpha_identifier() { let ffLen = 2; // Removal - iccHelper.writeAlphaIdentifier(10, null); + let writenAlphaId = iccHelper.writeAlphaIdentifier(10, null); + equal(writenAlphaId, null); equal(iccHelper.readAlphaIdentifier(10), ""); // GSM 8 bit let str = "Mozilla"; - iccHelper.writeAlphaIdentifier(str.length + ffLen, str); + writenAlphaId = iccHelper.writeAlphaIdentifier(str.length + ffLen, str); + equal(writenAlphaId , str); equal(iccHelper.readAlphaIdentifier(str.length + ffLen), str); // UCS2 - str = "Mozilla\u694a"; - iccHelper.writeAlphaIdentifier(str.length * 2 + ffLen, str); + str = "Mozilla\u8000"; + writenAlphaId = iccHelper.writeAlphaIdentifier(str.length * 2 + ffLen, str); + equal(writenAlphaId , str); // * 2 for each character will be encoded to UCS2 alphabets. equal(iccHelper.readAlphaIdentifier(str.length * 2 + ffLen), str); // Test with maximum octets written. // 1 coding scheme (0x80) and 1 UCS2 character, total 3 octets. str = "\u694a"; - iccHelper.writeAlphaIdentifier(3, str); + writenAlphaId = iccHelper.writeAlphaIdentifier(3, str); + equal(writenAlphaId , str); equal(iccHelper.readAlphaIdentifier(3), str); // 1 coding scheme (0x80) and 2 UCS2 characters, total 5 octets. // numOctets is limited to 4, so only 1 UCS2 character can be written. - str = "\u694a\u694a"; - iccHelper.writeAlphaIdentifier(4, str); + str = "\u694a\u69ca"; + writenAlphaId = iccHelper.writeAlphaIdentifier(4, str); helper.writeHexOctet(0xff); // dummy octet. + equal(writenAlphaId , str.substring(0, 1)); equal(iccHelper.readAlphaIdentifier(5), str.substring(0, 1)); // Write 0 octet. - iccHelper.writeAlphaIdentifier(0, "1"); + writenAlphaId = iccHelper.writeAlphaIdentifier(0, "1"); helper.writeHexOctet(0xff); // dummy octet. + equal(writenAlphaId, null); equal(iccHelper.readAlphaIdentifier(1), ""); run_next_test(); @@ -373,28 +462,36 @@ add_test(function test_write_alpha_id_dialling_number() { alphaId: "Mozilla", number: "1234567890" }; - helper.writeAlphaIdDiallingNumber(recordSize, contactW.alphaId, - contactW.number); + + let writtenContact = helper.writeAlphaIdDiallingNumber(recordSize, + contactW.alphaId, + contactW.number, 0xff); let contactR = helper.readAlphaIdDiallingNumber(recordSize); - equal(contactW.alphaId, contactR.alphaId); - equal(contactW.number, contactR.number); + equal(writtenContact.alphaId, contactR.alphaId); + equal(writtenContact.number, contactR.number); + equal(0xff, contactR.extRecordNumber); // Write a contact with alphaId encoded in UCS2 and number has '+'. let contactUCS2 = { alphaId: "火狐", number: "+1234567890" }; - helper.writeAlphaIdDiallingNumber(recordSize, contactUCS2.alphaId, - contactUCS2.number); + + writtenContact = helper.writeAlphaIdDiallingNumber(recordSize, + contactUCS2.alphaId, + contactUCS2.number, 0xff); contactR = helper.readAlphaIdDiallingNumber(recordSize); - equal(contactUCS2.alphaId, contactR.alphaId); - equal(contactUCS2.number, contactR.number); + equal(writtenContact.alphaId, contactR.alphaId); + equal(writtenContact.number, contactR.number); + equal(0xff, contactR.extRecordNumber); // Write a null contact (Removal). - helper.writeAlphaIdDiallingNumber(recordSize); + writtenContact = helper.writeAlphaIdDiallingNumber(recordSize); contactR = helper.readAlphaIdDiallingNumber(recordSize); equal(contactR, null); + equal(writtenContact.alphaId, null); + equal(writtenContact.number, null); // Write a longer alphaId/dialling number // Dialling Number : Maximum 20 digits(10 octets). @@ -405,19 +502,24 @@ add_test(function test_write_alpha_id_dialling_number() { alphaId: "AAAAAAAAABBBBBBBBBCCCCCCCCC", number: "123456789012345678901234567890", }; - helper.writeAlphaIdDiallingNumber(recordSize, longContact.alphaId, - longContact.number); + + writtenContact = helper.writeAlphaIdDiallingNumber(recordSize, + longContact.alphaId, + longContact.number, 0xff); contactR = helper.readAlphaIdDiallingNumber(recordSize); - equal(contactR.alphaId, "AAAAAAAAABBBBBBBBB"); - equal(contactR.number, "12345678901234567890"); + equal(writtenContact.alphaId, contactR.alphaId); + equal(writtenContact.number, contactR.number); + equal(0xff, contactR.extRecordNumber); // Add '+' to number and test again. longContact.number = "+123456789012345678901234567890"; - helper.writeAlphaIdDiallingNumber(recordSize, longContact.alphaId, - longContact.number); + writtenContact = helper.writeAlphaIdDiallingNumber(recordSize, + longContact.alphaId, + longContact.number, 0xff); contactR = helper.readAlphaIdDiallingNumber(recordSize); - equal(contactR.alphaId, "AAAAAAAAABBBBBBBBB"); - equal(contactR.number, "+12345678901234567890"); + equal(writtenContact.alphaId, contactR.alphaId); + equal(writtenContact.number, contactR.number); + equal(0xff, contactR.extRecordNumber); run_next_test(); }); @@ -511,7 +613,8 @@ add_test(function test_write_number_with_length() { function test(number, expectedNumber) { expectedNumber = expectedNumber || number; - iccHelper.writeNumberWithLength(number); + let writeNumber = iccHelper.writeNumberWithLength(number); + equal(writeNumber, expectedNumber); let numLen = helper.readHexOctet(); equal(expectedNumber, iccHelper.readDiallingNumber(numLen)); for (let i = 0; i < (ADN_MAX_BCD_NUMBER_BYTES - numLen); i++) { @@ -536,6 +639,9 @@ add_test(function test_write_number_with_length() { test("++(01)2*3-4#5,6+7(8)9*0#1,", "+012*34#5,6789*0#1,"); + // over maximum 20 digits should be truncated. + test("012345678901234567890123456789", "01234567890123456789"); + // null iccHelper.writeNumberWithLength(null); for (let i = 0; i < (ADN_MAX_BCD_NUMBER_BYTES + 1); i++) { diff --git a/dom/system/gonk/tests/test_ril_worker_icc_ICCRecordHelper.js b/dom/system/gonk/tests/test_ril_worker_icc_ICCRecordHelper.js index 9f90d9201d..00c55873de 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc_ICCRecordHelper.js +++ b/dom/system/gonk/tests/test_ril_worker_icc_ICCRecordHelper.js @@ -204,10 +204,8 @@ add_test(function test_update_email() { // pin2. equal(this.readString(), null); - if (!ril.v5Legacy) { - // AID. Ignore because it's from modem. - this.readInt32(); - } + // AID. Ignore because it's from modem. + this.readInt32(); if (count == NUM_TESTS) { run_next_test(); @@ -350,10 +348,8 @@ add_test(function test_update_anr() { // pin2. equal(this.readString(), null); - if (!ril.v5Legacy) { - // AID. Ignore because it's from modem. - this.readInt32(); - } + // AID. Ignore because it's from modem. + this.readInt32(); if (count == NUM_TESTS) { run_next_test(); @@ -487,10 +483,8 @@ add_test(function test_update_iap() { // pin2. equal(this.readString(), null); - if (!ril.v5Legacy) { - // AID. Ignore because it's from modem. - this.readInt32(); - } + // AID. Ignore because it's from modem. + this.readInt32(); run_next_test(); }; @@ -500,6 +494,74 @@ add_test(function test_update_iap() { do_test([1, 2]); }); +/** + * Verify ICCRecordHelper.readADNLike. + */ +add_test(function test_read_adn_like() { + const RECORD_SIZE = 0x20; + + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let helper = context.GsmPDUHelper; + let record = context.ICCRecordHelper; + let buf = context.Buf; + let io = context.ICCIOHelper; + let ril = context.RIL; + + function do_test(extFileId, rawEF, expectedExtRecordNumber, expectedNumber) { + io.loadLinearFixedEF = function fakeLoadLinearFixedEF(options) { + // Write data size + buf.writeInt32(rawEF.length * 2); + + // Write adn + for (let i = 0; i < rawEF.length; i += 2) { + helper.writeHexOctet(parseInt(rawEF.substr(i, 2), 16)); + } + + // Write string delimiter + buf.writeStringDelimiter(rawEF.length * 2); + + options.p1 = 1; + options.recordSize = RECORD_SIZE; + options.totalRecords = 1; + if (options.callback) { + options.callback(options); + } + }; + + record.readExtension = function(fileId, recordNumber, onsuccess, onerror) { + onsuccess("1234"); + } + + let successCb = function successCb(contacts) { + ok(contacts[0].number == expectedNumber); + }; + + let errorCb = function errorCb(errorMsg) { + do_print("Reading ADNLike failed, msg = " + errorMsg); + ok(false); + }; + + record.readADNLike(ICC_EF_ADN, extFileId, successCb, errorCb); + } + + ril.appType = CARD_APPTYPE_SIM; + // Valid extension + do_test(ICC_EF_EXT1,"436f6e74616374303031ffffffffffffffff0b8199887766554433221100ff01", + 0x01,"998877665544332211001234"); + // Empty extension + do_test(ICC_EF_EXT1,"436f6e74616374303031ffffffffffffffff0b8199887766554433221100ffff", + 0xff, "99887766554433221100"); + // Unsupport extension + do_test(null,"436f6e74616374303031ffffffffffffffff0b8199887766554433221100ffff", + 0xff, "99887766554433221100"); + // Empty dialling number contact + do_test(null,"436f6e74616374303031ffffffffffffffffffffffffffffffffffffffffffff", + 0xff, ""); + + run_next_test(); +}); + /** * Verify ICCRecordHelper.updateADNLike. */ @@ -555,6 +617,7 @@ add_test(function test_update_adn_like() { let contact = pdu.readAlphaIdDiallingNumber(0x20); equal(contact.alphaId, "test"); equal(contact.number, "123456"); + equal(contact.extRecordNumber, "0xff"); // pin2. if (fileId == ICC_EF_ADN) { @@ -563,10 +626,8 @@ add_test(function test_update_adn_like() { equal(this.readString(), "1111"); } - if (!ril.v5Legacy) { - // AID. Ignore because it's from modem. - this.readInt32(); - } + // AID. Ignore because it's from modem. + this.readInt32(); if (fileId == ICC_EF_FDN) { run_next_test(); @@ -574,11 +635,11 @@ add_test(function test_update_adn_like() { }; fileId = ICC_EF_ADN; - record.updateADNLike(fileId, + record.updateADNLike(fileId, 0xff, {recordId: 1, alphaId: "test", number: "123456"}); fileId = ICC_EF_FDN; - record.updateADNLike(fileId, + record.updateADNLike(fileId, 0xff, {recordId: 1, alphaId: "test", number: "123456"}, "1111"); }); @@ -729,3 +790,291 @@ add_test(function test_handling_iccid() { run_next_test(); }); + +/** + * Verify ICCRecordHelper.readExtension + */ +add_test(function test_read_extension() { + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let helper = context.GsmPDUHelper; + let record = context.ICCRecordHelper; + let buf = context.Buf; + let io = context.ICCIOHelper; + + function do_test(rawExtension, expectedExtensionNumber) { + io.loadLinearFixedEF = function fakeLoadLinearFixedEF(options) { + // Write data size + buf.writeInt32(rawExtension.length * 2); + + // Write ext + for (let i = 0; i < rawExtension.length; i += 2) { + helper.writeHexOctet(parseInt(rawExtension.substr(i, 2), 16)); + } + + // Write string delimiter + buf.writeStringDelimiter(rawExtension.length); + + if (options.callback) { + options.callback(options); + } + }; + + let successCb = function successCb(number) { + do_print("extension number:" + number); + equal(number, expectedExtensionNumber); + }; + + let errorCb = function errorCb() { + ok(expectedExtensionNumber == null); + }; + + record.readExtension(0x6f4a, 1, successCb, errorCb); + } + + // Test unsupported record type 0x01 + do_test("010a10325476981032547698ff", ""); + // Test invalid length 0xc1 + do_test("020c10325476981032547698ff", null); + // Test extension chain which we don't support + do_test("020a1032547698103254769802", "01234567890123456789"); + // Test valid Extension + do_test("020a10325476981032547698ff", "01234567890123456789"); + // Test valid Extension + do_test("0209103254769810325476ffff", "012345678901234567"); + // Test empty Extension + do_test("02ffffffffffffffffffffffff", ""); + + run_next_test(); +}); + +/** + * Verify ICCRecordHelper.updateExtension + */ +add_test(function test_update_extension() { + const RECORD_SIZE = 13; + const RECORD_NUMBER = 1; + + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let pduHelper = context.GsmPDUHelper; + let ril = context.RIL; + let recordHelper = context.ICCRecordHelper; + let buf = context.Buf; + let ioHelper = context.ICCIOHelper; + + // Override. + ioHelper.updateLinearFixedEF = function(options) { + options.pathId = context.ICCFileHelper.getEFPath(options.fileId); + options.command = ICC_COMMAND_UPDATE_RECORD; + options.p1 = options.recordNumber; + options.p2 = READ_RECORD_ABSOLUTE_MODE; + options.p3 = RECORD_SIZE; + ril.iccIO(options); + }; + + function do_test(fileId, number, expectedNumber) { + buf.sendParcel = function() { + // Request Type. + equal(this.readInt32(), REQUEST_SIM_IO); + + // Token : we don't care + this.readInt32(); + + // command. + equal(this.readInt32(), ICC_COMMAND_UPDATE_RECORD); + + // fileId. + equal(this.readInt32(), fileId); + + // pathId. + if (ril.appType == CARD_APPTYPE_SIM || ril.appType == CARD_APPTYPE_RUIM) { + equal(this.readString(), + EF_PATH_MF_SIM + EF_PATH_DF_TELECOM); + } else{ + equal(this.readString(), + EF_PATH_MF_SIM + EF_PATH_DF_TELECOM + EF_PATH_DF_PHONEBOOK); + } + + // p1. + equal(this.readInt32(), RECORD_NUMBER); + + // p2. + equal(this.readInt32(), READ_RECORD_ABSOLUTE_MODE); + + // p3. + equal(this.readInt32(), RECORD_SIZE); + + // data. + let strLen = this.readInt32(); + // Extension record + let recordType = pduHelper.readHexOctet(); + + equal(recordType, 0x02); + equal(pduHelper.readHexOctet(), 10); + equal( + pduHelper.readSwappedNibbleExtendedBcdString(EXT_MAX_NUMBER_DIGITS - 1), + expectedNumber); + + this.readStringDelimiter(strLen); + + // pin2. + equal(this.readString(), null); + + // AID. Ignore because it's from modem. + this.readInt32(); + }; + + recordHelper.updateExtension(fileId, RECORD_NUMBER, number); + } + + ril.appType = CARD_APPTYPE_SIM; + do_test(ICC_EF_EXT1, "01234567890123456789", "01234567890123456789"); + // We don't support extension chain. + do_test(ICC_EF_EXT1, "012345678901234567891234", "01234567890123456789"); + + ril.appType = CARD_APPTYPE_USIM; + do_test(ICC_EF_EXT1, "01234567890123456789", "01234567890123456789"); + + ril.appType = CARD_APPTYPE_RUIM; + do_test(ICC_EF_EXT1, "01234567890123456789", "01234567890123456789"); + + run_next_test(); +}); + +/** + * Verify ICCRecordHelper.cleanEFRecord + */ +add_test(function test_clean_ef_record() { + const RECORD_SIZE = 13; + const RECORD_NUMBER = 1; + + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let pduHelper = context.GsmPDUHelper; + let ril = context.RIL; + let recordHelper = context.ICCRecordHelper; + let buf = context.Buf; + let ioHelper = context.ICCIOHelper; + + // Override. + ioHelper.updateLinearFixedEF = function(options) { + options.pathId = context.ICCFileHelper.getEFPath(options.fileId); + options.command = ICC_COMMAND_UPDATE_RECORD; + options.p1 = options.recordNumber; + options.p2 = READ_RECORD_ABSOLUTE_MODE; + options.p3 = RECORD_SIZE; + ril.iccIO(options); + }; + + function do_test(fileId) { + buf.sendParcel = function() { + // Request Type. + equal(this.readInt32(), REQUEST_SIM_IO); + + // Token : we don't care + this.readInt32(); + + // command. + equal(this.readInt32(), ICC_COMMAND_UPDATE_RECORD); + + // fileId. + equal(this.readInt32(), fileId); + + // pathId. + if (ril.appType == CARD_APPTYPE_SIM || ril.appType == CARD_APPTYPE_RUIM) { + equal(this.readString(), + EF_PATH_MF_SIM + EF_PATH_DF_TELECOM); + } else{ + equal(this.readString(), + EF_PATH_MF_SIM + EF_PATH_DF_TELECOM + EF_PATH_DF_PHONEBOOK); + } + + // p1. + equal(this.readInt32(), RECORD_NUMBER); + + // p2. + equal(this.readInt32(), READ_RECORD_ABSOLUTE_MODE); + + // p3. + equal(this.readInt32(), RECORD_SIZE); + + // data. + let strLen = this.readInt32(); + // Extension record + for (let i = 0; i < RECORD_SIZE; i++) { + equal(pduHelper.readHexOctet(), 0xff); + } + + this.readStringDelimiter(strLen); + + // pin2. + equal(this.readString(), null); + + // AID. Ignore because it's from modem. + this.readInt32(); + }; + + recordHelper.cleanEFRecord(fileId, RECORD_NUMBER); + } + + ril.appType = CARD_APPTYPE_SIM; + do_test(ICC_EF_EXT1); + + run_next_test(); +}); + +/** + * Verify ICCRecordHelper.getADNLikeExtensionRecordNumber + */ +add_test(function test_get_adn_like_extension_record_number() { + const RECORD_SIZE = 0x20; + + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + let helper = context.GsmPDUHelper; + let record = context.ICCRecordHelper; + let buf = context.Buf; + let io = context.ICCIOHelper; + + function do_test(rawEF, expectedRecordNumber) { + io.loadLinearFixedEF = function fakeLoadLinearFixedEF(options) { + // Write data size + buf.writeInt32(rawEF.length * 2); + + // Write ext + for (let i = 0; i < rawEF.length; i += 2) { + helper.writeHexOctet(parseInt(rawEF.substr(i, 2), 16)); + } + + // Write string delimiter + buf.writeStringDelimiter(rawEF.length); + options.recordSize = RECORD_SIZE; + if (options.callback) { + options.callback(options); + } + }; + + let isSuccess = false; + let successCb = function successCb(number) { + equal(number, expectedRecordNumber); + isSuccess = true; + }; + + let errorCb = function errorCb(errorMsg) { + do_print("Reading ADNLike failed, msg = " + errorMsg); + ok(false); + }; + + record.getADNLikeExtensionRecordNumber(ICC_EF_ADN, 1, successCb, errorCb); + ok(isSuccess); + } + + // Valid Extension, Alpha Id(Encoded with GSM 8 bit): "Contact001", + // Dialling Number: 99887766554433221100, Ext1: 0x01 + do_test("436f6e74616374303031ffffffffffffffff0b8199887766554433221100ff01", 0x01); + // Empty Extension, Alpha Id(Encoded with GSM 8 bit): "Contact001", Ext1: 0xff + do_test("436f6e74616374303031ffffffffffffffffffffffffffffffffffffffffffff", 0xff); + + run_next_test(); +}); diff --git a/dom/system/gonk/tests/test_ril_worker_icc_SimRecordHelper.js b/dom/system/gonk/tests/test_ril_worker_icc_SimRecordHelper.js index adb6eb7eef..cd6e88f94e 100644 --- a/dom/system/gonk/tests/test_ril_worker_icc_SimRecordHelper.js +++ b/dom/system/gonk/tests/test_ril_worker_icc_SimRecordHelper.js @@ -355,10 +355,8 @@ add_test(function test_update_mwis() { // pin2. equal(this.readString(), null); - if (!ril.v5Legacy) { - // AID. Ignore because it's from modem. - this.readInt32(); - } + // AID. Ignore because it's from modem. + this.readInt32(); }; ok(!isUpdated); diff --git a/dom/system/gonk/tests/test_ril_worker_mmi.js b/dom/system/gonk/tests/test_ril_worker_mmi.js deleted file mode 100644 index e3b24a6957..0000000000 --- a/dom/system/gonk/tests/test_ril_worker_mmi.js +++ /dev/null @@ -1,525 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this); - -function run_test() { - run_next_test(); -} - -function createMMIOptions(procedure, serviceCode, sia, sib, sic) { - let mmi = { - fullMMI: Array.slice(arguments).join("*") + "#", - procedure: procedure, - serviceCode: serviceCode, - sia: sia, - sib: sib, - sic: sic - }; - - return mmi; -} - -function testSendMMI(mmi, error) { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - do_print("worker.postMessage " + worker.postMessage); - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({rilMessageType: "sendMMI", mmi: mmi}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.rilMessageType, "sendMMI"); - equal(postedMessage.errorMsg, error); -} - -/** - * sendMMI tests. - */ - -add_test(function test_sendMMI_null() { - testSendMMI(null, MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_short_code() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - let ussdOptions; - - context.RIL.sendUSSD = function fakeSendUSSD(options){ - ussdOptions = options; - context.RIL[REQUEST_SEND_USSD](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: {fullMMI: "**"}}); - - let postedMessage = workerhelper.postedMessage; - equal(ussdOptions.ussd, "**"); - equal (postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - ok(context.RIL._ussdSession); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.changeICCPIN = function fakeChangeICCPIN(options) { - context.RIL[REQUEST_ENTER_SIM_PIN](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "04", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal (postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_no_new_PIN() { - testSendMMI(createMMIOptions("**", "04", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_no_old_PIN() { - testSendMMI(createMMIOptions("**", "04", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_wrong_procedure() { - testSendMMI(createMMIOptions("*", "04", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN_new_PIN_mismatch() { - testSendMMI(createMMIOptions("**", "04", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.changeICCPIN2 = function fakeChangeICCPIN2(options) { - context.RIL[REQUEST_ENTER_SIM_PIN2](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "042", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal (postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_no_new_PIN2() { - testSendMMI(createMMIOptions("**", "042", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_no_old_PIN2() { - testSendMMI(createMMIOptions("**", "042", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_wrong_procedure() { - testSendMMI(createMMIOptions("*", "042", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_change_PIN2_new_PIN2_mismatch() { - testSendMMI(createMMIOptions("**", "042", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.enterICCPUK = function fakeEnterICCPUK(options) { - context.RIL[REQUEST_ENTER_SIM_PUK](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "05", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal (postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_no_new_PIN() { - testSendMMI(createMMIOptions("**", "05", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_no_PUK() { - testSendMMI(createMMIOptions("**", "05", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_wrong_procedure() { - testSendMMI(createMMIOptions("*", "05", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN_new_PIN_mismatch() { - testSendMMI(createMMIOptions("**", "05", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.enterICCPUK2 = function fakeEnterICCPUK2(options) { - context.RIL[REQUEST_ENTER_SIM_PUK2](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("**", "052", "1234", "4567", - "4567")}); - - let postedMessage = workerhelper.postedMessage; - - equal (postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_no_new_PIN2() { - testSendMMI(createMMIOptions("**", "052", "1234", "", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_no_PUK2() { - testSendMMI(createMMIOptions("**", "052", "", "1234", "4567"), - MMI_ERROR_KS_ERROR); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_wrong_procedure() { - testSendMMI(createMMIOptions("*", "052", "1234", "4567", "4567"), - MMI_ERROR_KS_INVALID_ACTION); - - run_next_test(); -}); - -add_test(function test_sendMMI_unblock_PIN2_new_PIN_mismatch() { - testSendMMI(createMMIOptions("**", "052", "4567", "1234", "4567"), - MMI_ERROR_KS_MISMATCH_PIN); - - run_next_test(); -}); - -add_test(function test_sendMMI_get_IMEI() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.getIMEI = function getIMEI(options) { - mmiOptions = options; - context.RIL[REQUEST_SEND_USSD](0, {}); - }; - - context.RIL.sendMMI({mmi: createMMIOptions("*#", "06")}); - - let postedMessage = workerhelper.postedMessage; - - notEqual(mmiOptions.mmi, null); - equal (postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - - run_next_test(); -}); - -add_test(function test_sendMMI_get_IMEI_error() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.getIMEI = function getIMEI(options){ - mmiOptions = options; - context.RIL[REQUEST_SEND_USSD](0, { - errorMsg: GECKO_ERROR_RADIO_NOT_AVAILABLE - }); - }; - - context.RIL.sendMMI({mmi: createMMIOptions("*#", "06")}); - - let postedMessage = workerhelper.postedMessage; - - notEqual(mmiOptions.mmi, null); - equal (postedMessage.errorMsg, GECKO_ERROR_RADIO_NOT_AVAILABLE); - ok(!postedMessage.success); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_interrogation_voice() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.Buf.readInt32List = function fakeReadUint32List() { - return [1]; - }; - - context.RIL.queryICCFacilityLock = - function fakeQueryICCFacilityLock(options) { - context.RIL[REQUEST_QUERY_FACILITY_LOCK](1, { - rilMessageType: "sendMMI" - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*#", "33")}); - - let postedMessage = workerhelper.postedMessage; - - ok(postedMessage.success); - ok(postedMessage.enabled); - equal(postedMessage.statusMessage, MMI_SM_KS_SERVICE_ENABLED_FOR); - ok(Array.isArray(postedMessage.additionalInformation)); - equal(postedMessage.additionalInformation[0], "serviceClassVoice"); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_activation() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.setICCFacilityLock = - function fakeSetICCFacilityLock(options) { - mmiOptions = options; - context.RIL[REQUEST_SET_FACILITY_LOCK](0, { - rilMessageType: "sendMMI", - procedure: MMI_PROCEDURE_ACTIVATION - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*", "33")}); - - let postedMessage = workerhelper.postedMessage; - - equal(mmiOptions.procedure, MMI_PROCEDURE_ACTIVATION); - ok(postedMessage.success); - equal(postedMessage.statusMessage, MMI_SM_KS_SERVICE_ENABLED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_deactivation() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let mmiOptions; - - context.RIL.setICCFacilityLock = - function fakeSetICCFacilityLock(options) { - mmiOptions = options; - context.RIL[REQUEST_SET_FACILITY_LOCK](0, { - rilMessageType: "sendMMI", - procedure: MMI_PROCEDURE_DEACTIVATION - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("#", "33")}); - - let postedMessage = workerhelper.postedMessage; - - equal(mmiOptions.procedure, MMI_PROCEDURE_DEACTIVATION); - ok(postedMessage.success); - equal(postedMessage.statusMessage, MMI_SM_KS_SERVICE_DISABLED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_barring_BAIC_procedure_not_supported() { - testSendMMI(createMMIOptions("**", "33", "0000"), MMI_ERROR_KS_NOT_SUPPORTED); - - run_next_test(); -}); - -add_test(function test_sendMMI_USSD() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let ussdOptions; - - context.RIL.sendUSSD = function fakeSendUSSD(options) { - ussdOptions = options; - context.RIL[REQUEST_SEND_USSD](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*", "123")}); - - let postedMessage = workerhelper.postedMessage; - - equal(ussdOptions.ussd, "**123#"); - equal (postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - ok(context.RIL._ussdSession); - - run_next_test(); -}); - -add_test(function test_sendMMI_USSD_error() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - let ussdOptions; - - context.RIL.sendUSSD = function fakeSendUSSD(options){ - ussdOptions = options; - context.RIL[REQUEST_SEND_USSD](0, { - errorMsg: GECKO_ERROR_GENERIC_FAILURE - }); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*", "123")}); - - let postedMessage = workerhelper.postedMessage; - - equal(ussdOptions.ussd, "**123#"); - equal (postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); - ok(!postedMessage.success); - ok(!context.RIL._ussdSession); - - run_next_test(); -}); - -function setCallWaitingSuccess(mmi) { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.RIL.setCallWaiting = function fakeSetCallWaiting(options) { - context.RIL[REQUEST_SET_CALL_WAITING](0, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: mmi}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); -} - -add_test(function test_sendMMI_call_waiting_activation() { - setCallWaitingSuccess(createMMIOptions("*", "43", "10")); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_deactivation() { - setCallWaitingSuccess(createMMIOptions("#", "43")); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_registration() { - testSendMMI(createMMIOptions("**", "43"), MMI_ERROR_KS_NOT_SUPPORTED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_erasure() { - testSendMMI(createMMIOptions("##", "43"), MMI_ERROR_KS_NOT_SUPPORTED); - - run_next_test(); -}); - -add_test(function test_sendMMI_call_waiting_interrogation() { - let workerhelper = newInterceptWorker(); - let worker = workerhelper.worker; - let context = worker.ContextPool._contexts[0]; - - context.Buf.readInt32 = function fakeReadUint32() { - return context.Buf.int32Array.pop(); - }; - - context.RIL.queryCallWaiting = function fakeQueryCallWaiting(options) { - context.Buf.int32Array = [ - 7, // serviceClass - 1, // enabled - 2 // length - ]; - context.RIL[REQUEST_QUERY_CALL_WAITING](1, {}); - }; - - context.RIL.radioState = GECKO_RADIOSTATE_ENABLED; - context.RIL.sendMMI({mmi: createMMIOptions("*#", "43")}); - - let postedMessage = workerhelper.postedMessage; - - equal(postedMessage.errorMsg, GECKO_ERROR_SUCCESS); - ok(postedMessage.success); - equal(postedMessage.length, 2); - ok(postedMessage.enabled); - run_next_test(); -}); diff --git a/dom/system/gonk/tests/test_ril_worker_mmi_cf.js b/dom/system/gonk/tests/test_ril_worker_mmi_cf.js index 08cd9b683c..1362b313ef 100644 --- a/dom/system/gonk/tests/test_ril_worker_mmi_cf.js +++ b/dom/system/gonk/tests/test_ril_worker_mmi_cf.js @@ -36,7 +36,6 @@ function setCallForwardSuccess(procedure, serviceCode, sia, sib, sic) { let postedMessage = workerhelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); } add_test(function test_sendMMI_call_forwarding_activation() { @@ -82,7 +81,6 @@ add_test(function test_sendMMI_call_forwarding_interrogation() { let postedMessage = workerhelper.postedMessage; equal(postedMessage.errorMsg, undefined); - ok(postedMessage.success); ok(Array.isArray(postedMessage.rules)); equal(postedMessage.rules.length, 1); ok(postedMessage.rules[0].active); @@ -110,7 +108,6 @@ add_test(function test_sendMMI_call_forwarding_interrogation_no_rules() { let postedMessage = workerhelper.postedMessage; equal(postedMessage.errorMsg, GECKO_ERROR_GENERIC_FAILURE); - ok(!postedMessage.success); run_next_test(); }); diff --git a/dom/system/gonk/tests/test_ril_worker_smsc_address.js b/dom/system/gonk/tests/test_ril_worker_smsc_address.js index c016a273b9..c8c283b7c4 100644 --- a/dom/system/gonk/tests/test_ril_worker_smsc_address.js +++ b/dom/system/gonk/tests/test_ril_worker_smsc_address.js @@ -6,20 +6,26 @@ subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this); const SMSC_ATT = '+13123149810'; const SMSC_ATT_TYPO = '+++1312@@@314$$$9,8,1,0'; const SMSC_ATT_TEXT = '"+13123149810",145'; +const SMSC_ATT_TEXT_INCORRECT_TOA = '"+13123149810",129'; const SMSC_ATT_PDU = '07913121139418F0'; const SMSC_O2 = '+447802000332'; const SMSC_O2_TEXT = '"+447802000332",145'; const SMSC_O2_PDU = '0791448720003023'; +const SMSC_EMPTY = ''; const SMSC_TON_UNKNOWN = '0407485455' const SMSC_TON_UNKNOWN_TEXT = '"0407485455",129'; +const SMSC_TON_UNKNOWN_TEXT_NO_TOA = '"0407485455"'; +const SMSC_TON_UNKNOWN_TEXT_INVALID_TOA = '"0407485455",abc'; const SMSC_TON_UNKNOWN_PDU = '06814070844555'; +const SMSC_EMPTY_PDU = 'FFFFFFFFFFFFFFFFFFFFFFFF'; +const SMSC_EMPTY_TEXT = ''; function run_test() { run_next_test(); } function setSmsc(context, smsc, ton, npi, expected) { - context.Buf.sendParcel = function() { + context.Buf.postRILMessage = function() { equal(this.readString(), expected); }; @@ -30,6 +36,17 @@ function setSmsc(context, smsc, ton, npi, expected) { }); } +function getSmsc(worker, context, raw, smsc, ton, npi) { + worker.postMessage = function(message) { + equal(message.smscAddress, smsc); + equal(message.typeOfNumber, ton); + equal(message.numberPlanIdentification, npi); + } + + context.Buf.writeString(raw); + context.RIL[REQUEST_GET_SMSC_ADDRESS](0, { rilMessageType: "getSmscAddress"}); +} + add_test(function test_setSmscAddress() { let worker = newUint8Worker(); let context = worker.ContextPool._contexts[0]; @@ -69,3 +86,27 @@ add_test(function test_setSmscAddress() { run_next_test(); }); +add_test(function test_getSmscAddress() { + let worker = newUint8Worker(); + let context = worker.ContextPool._contexts[0]; + + // Test text mode. + worker.RILQUIRKS_SMSC_ADDRESS_FORMAT = "text"; + getSmsc(worker, context, SMSC_ATT_TEXT, SMSC_ATT, 1, 1); + getSmsc(worker, context, SMSC_ATT_TEXT_INCORRECT_TOA, SMSC_ATT, 1, 1); + getSmsc(worker, context, SMSC_O2_TEXT, SMSC_O2, 1, 1); + getSmsc(worker, context, SMSC_TON_UNKNOWN_TEXT, SMSC_TON_UNKNOWN, 0, 1); + getSmsc(worker, context, SMSC_TON_UNKNOWN_TEXT_NO_TOA, SMSC_TON_UNKNOWN, 0, 1); + getSmsc(worker, context, SMSC_TON_UNKNOWN_TEXT_INVALID_TOA, SMSC_TON_UNKNOWN, + 0, 1); + getSmsc(worker, context, SMSC_EMPTY_TEXT, SMSC_EMPTY, 0, 1); + + // Test pdu mode. + worker.RILQUIRKS_SMSC_ADDRESS_FORMAT = "pdu"; + getSmsc(worker, context, SMSC_ATT_PDU, SMSC_ATT, 1, 1); + getSmsc(worker, context, SMSC_O2_PDU, SMSC_O2, 1, 1); + getSmsc(worker, context, SMSC_TON_UNKNOWN_PDU, SMSC_TON_UNKNOWN, 0, 1); + getSmsc(worker, context, SMSC_EMPTY_PDU, SMSC_EMPTY, 0, 1); + + run_next_test(); +}); diff --git a/dom/system/gonk/tests/xpcshell.ini b/dom/system/gonk/tests/xpcshell.ini index 97acee85db..6af3d7437f 100644 --- a/dom/system/gonk/tests/xpcshell.ini +++ b/dom/system/gonk/tests/xpcshell.ini @@ -23,8 +23,6 @@ skip-if = true [test_ril_worker_sms_gsmpduhelper.js] [test_ril_worker_sms_segment_info.js] [test_ril_worker_smsc_address.js] -[test_ril_worker_mmi.js] -[test_ril_worker_mmi_cf.js] [test_ril_worker_cf.js] [test_ril_worker_cellbroadcast_config.js] [test_ril_worker_cellbroadcast.js] diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index f27f39e948..61b3be55b0 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -165,38 +165,27 @@ Telephony::IsValidServiceId(uint32_t aServiceId) return aServiceId < GetNumServices(); } -// static -bool -Telephony::IsActiveState(uint16_t aCallState) { - return aCallState == nsITelephonyService::CALL_STATE_DIALING || - aCallState == nsITelephonyService::CALL_STATE_ALERTING || - aCallState == nsITelephonyService::CALL_STATE_CONNECTED; -} - uint32_t -Telephony::ProvidedOrDefaultServiceId(const Optional& aServiceId) +Telephony::GetServiceId(const Optional& aServiceId, + bool aGetIfActiveCall) { if (aServiceId.WasPassed()) { return aServiceId.Value(); - } else { - uint32_t serviceId = 0; - mService->GetDefaultServiceId(&serviceId); - return serviceId; - } -} - -bool -Telephony::HasDialingCall() -{ - for (uint32_t i = 0; i < mCalls.Length(); i++) { - const RefPtr& call = mCalls[i]; - if (call->CallState() > nsITelephonyService::CALL_STATE_UNKNOWN && - call->CallState() < nsITelephonyService::CALL_STATE_CONNECTED) { - return true; + } else if (aGetIfActiveCall) { + nsTArray > &calls = mCalls; + if (mGroup->IsActive()) { + calls = mGroup->CallsArray(); + } + for (uint32_t i = 0; i < calls.Length(); i++) { + if (calls[i]->IsActive()) { + return calls[i]->mServiceId; + } } } - return false; + uint32_t serviceId = 0; + mService->GetDefaultServiceId(&serviceId); + return serviceId; } already_AddRefed @@ -218,12 +207,6 @@ Telephony::DialInternal(uint32_t aServiceId, const nsAString& aNumber, return promise.forget(); } - // We only support one outgoing call at a time. - if (HasDialingCall()) { - promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); - return promise.forget(); - } - nsCOMPtr callback = new TelephonyDialCallback(GetOwner(), this, promise); @@ -265,17 +248,17 @@ Telephony::CreateCallId(const nsAString& aNumber, uint16_t aNumberPresentation, already_AddRefed Telephony::CreateCall(TelephonyCallId* aId, uint32_t aServiceId, - uint32_t aCallIndex, uint16_t aCallState, + uint32_t aCallIndex, TelephonyCallState aState, bool aEmergency, bool aConference, bool aSwitchable, bool aMergeable) { // We don't have to create an already ended call. - if (aCallState == nsITelephonyService::CALL_STATE_DISCONNECTED) { + if (aState == TelephonyCallState::Disconnected) { return nullptr; } RefPtr call = - TelephonyCall::Create(this, aId, aServiceId, aCallIndex, aCallState, + TelephonyCall::Create(this, aId, aServiceId, aCallIndex, aState, aEmergency, aConference, aSwitchable, aMergeable); NS_ASSERTION(call, "This should never fail!"); @@ -346,57 +329,66 @@ Telephony::HandleCallInfo(nsITelephonyCallInfo* aInfo) aInfo->GetIsSwitchable(&isSwitchable); aInfo->GetIsMergeable(&isMergeable); - RefPtr call = GetCallFromEverywhere(serviceId, callIndex); + TelephonyCallState state = TelephonyCall::ConvertToTelephonyCallState(callState); + RefPtr call = GetCallFromEverywhere(serviceId, callIndex); + // Handle a newly created call. if (!call) { RefPtr id = CreateCallId(aInfo); - call = CreateCall(id, serviceId, callIndex, callState, isEmergency, + call = CreateCall(id, serviceId, callIndex, state, isEmergency, isConference, isSwitchable, isMergeable); - - if (call && callState == nsITelephonyService::CALL_STATE_INCOMING) { + // The newly created call is an incoming call. + if (call && + state == TelephonyCallState::Incoming) { nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("incoming"), call); NS_ENSURE_SUCCESS(rv, rv); } - } else { - call->UpdateEmergency(isEmergency); - call->UpdateSwitchable(isSwitchable); - call->UpdateMergeable(isMergeable); + return NS_OK; + } - nsAutoString number; - aInfo->GetNumber(number); - RefPtr id = call->Id(); - id->UpdateNumber(number); + // Update an existing call + call->UpdateEmergency(isEmergency); + call->UpdateSwitchable(isSwitchable); + call->UpdateMergeable(isMergeable); - // State changed. - if (call->CallState() != callState) { - if (callState == nsITelephonyService::CALL_STATE_DISCONNECTED) { - call->ChangeStateInternal(callState, true); - return NS_OK; - } + nsAutoString number; + aInfo->GetNumber(number); + RefPtr id = call->Id(); + id->UpdateNumber(number); - // We don't fire the statechange event on a call in conference here. - // Instead, the event will be fired later in - // TelephonyCallGroup::ChangeState(). Thus the sequence of firing the - // statechange events is guaranteed: first on TelephonyCallGroup then on - // individual TelephonyCall objects. - bool fireEvent = !isConference; - call->ChangeStateInternal(callState, fireEvent); + nsAutoString disconnectedReason; + aInfo->GetDisconnectedReason(disconnectedReason); + + // State changed. + if (call->State() != state) { + if (state == TelephonyCallState::Disconnected) { + call->UpdateDisconnectedReason(disconnectedReason); + call->ChangeState(TelephonyCallState::Disconnected); + return NS_OK; } - // Group changed. - RefPtr group = call->GetGroup(); + // We don't fire the statechange event on a call in conference here. + // Instead, the event will be fired later in + // TelephonyCallGroup::ChangeState(). Thus the sequence of firing the + // statechange events is guaranteed: first on TelephonyCallGroup then on + // individual TelephonyCall objects. + bool fireEvent = !isConference; + call->ChangeStateInternal(state, fireEvent); + } - if (!group && isConference) { - // Add to conference. - NS_ASSERTION(mCalls.Contains(call), "Should in mCalls"); - mGroup->AddCall(call); - RemoveCall(call); - } else if (group && !isConference) { - // Remove from conference. - NS_ASSERTION(mGroup->CallsArray().Contains(call), "Should in mGroup"); - mGroup->RemoveCall(call); - AddCall(call); - } + // Group changed. + RefPtr group = call->GetGroup(); + + if (!group && isConference) { + // Add to conference. + NS_ASSERTION(mCalls.Contains(call), "Should in mCalls"); + mGroup->AddCall(call); + RemoveCall(call); + } else if (group && !isConference) { + // Remove from conference. + NS_ASSERTION(mGroup->CallsArray().Contains(call), "Should in mGroup"); + mGroup->RemoveCall(call); + AddCall(call); } return NS_OK; @@ -437,7 +429,7 @@ already_AddRefed Telephony::Dial(const nsAString& aNumber, const Optional& aServiceId, ErrorResult& aRv) { - uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); + uint32_t serviceId = GetServiceId(aServiceId); RefPtr promise = DialInternal(serviceId, aNumber, false, aRv); return promise.forget(); } @@ -447,7 +439,7 @@ Telephony::DialEmergency(const nsAString& aNumber, const Optional& aServiceId, ErrorResult& aRv) { - uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); + uint32_t serviceId = GetServiceId(aServiceId); RefPtr promise = DialInternal(serviceId, aNumber, true, aRv); return promise.forget(); } @@ -459,7 +451,8 @@ Telephony::SendTones(const nsAString& aDTMFChars, const Optional& aServiceId, ErrorResult& aRv) { - uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); + uint32_t serviceId = GetServiceId(aServiceId, + true /* aGetIfActiveCall */); nsCOMPtr global = do_QueryInterface(GetOwner()); if (!global) { @@ -496,7 +489,8 @@ Telephony::StartTone(const nsAString& aDTMFChar, const Optional& aServiceId, ErrorResult& aRv) { - uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); + uint32_t serviceId = GetServiceId(aServiceId, + true /* aGetIfActiveCall */); if (aDTMFChar.IsEmpty()) { NS_WARNING("Empty tone string will be ignored"); @@ -514,7 +508,8 @@ Telephony::StartTone(const nsAString& aDTMFChar, void Telephony::StopTone(const Optional& aServiceId, ErrorResult& aRv) { - uint32_t serviceId = ProvidedOrDefaultServiceId(aServiceId); + uint32_t serviceId = GetServiceId(aServiceId, + true /* aGetIfActiveCall */); if (!IsValidServiceId(serviceId)) { aRv.Throw(NS_ERROR_INVALID_ARG); @@ -625,18 +620,21 @@ Telephony::SetSpeakerEnabled(bool aEnabled, ErrorResult& aRv) void Telephony::GetActive(Nullable& aValue) { - if (mGroup->CallState() == nsITelephonyService::CALL_STATE_CONNECTED) { + if (mGroup->IsActive()) { aValue.SetValue().SetAsTelephonyCallGroup() = mGroup; - } else { - // Search the first active call. - for (uint32_t i = 0; i < mCalls.Length(); i++) { - if (IsActiveState(mCalls[i]->CallState())) { - aValue.SetValue().SetAsTelephonyCall() = mCalls[i]; - return; - } - } - aValue.SetNull(); + return; } + + // Search for the active call. + for (uint32_t i = 0; i < mCalls.Length(); i++) { + if (mCalls[i]->IsActive()) { + aValue.SetValue().SetAsTelephonyCall() = mCalls[i]; + return; + } + } + + // Nothing active found. + aValue.SetNull(); } already_AddRefed @@ -725,6 +723,7 @@ Telephony::WindowAudioCaptureChanged() NS_IMETHODIMP Telephony::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllInfo) { + // Update call state nsresult rv; for (uint32_t i = 0; i < aLength; ++i) { rv = HandleCallInfo(aAllInfo[i]); @@ -733,6 +732,9 @@ Telephony::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllInfo) } } + // Update conference state + mGroup->ChangeState(); + rv = HandleAudioAgentState(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -746,30 +748,11 @@ Telephony::EnumerateCallState(nsITelephonyCallInfo* aInfo) return HandleCallInfo(aInfo); } -NS_IMETHODIMP -Telephony::ConferenceCallStateChanged(uint16_t aCallState) -{ - mGroup->ChangeState(aCallState); - return NS_OK; -} - NS_IMETHODIMP Telephony::EnumerateCallStateComplete() { // Set conference state. - if (mGroup->CallsArray().Length() >= 2) { - const nsTArray > &calls = mGroup->CallsArray(); - - uint16_t callState = calls[0]->CallState(); - for (uint32_t i = 1; i < calls.Length(); i++) { - if (calls[i]->CallState() != callState) { - callState = nsITelephonyService::CALL_STATE_UNKNOWN; - break; - } - } - - mGroup->ChangeState(callState); - } + mGroup->ChangeState(); HandleAudioAgentState(); if (mReadyPromise) { @@ -809,26 +792,6 @@ Telephony::SupplementaryServiceNotification(uint32_t aServiceId, return NS_OK; } -NS_IMETHODIMP -Telephony::NotifyError(uint32_t aServiceId, - int32_t aCallIndex, - const nsAString& aError) -{ - RefPtr callToNotify = - GetCallFromEverywhere(aServiceId, aCallIndex); - if (!callToNotify) { - NS_ERROR("Don't call me with a bad call index!"); - return NS_ERROR_UNEXPECTED; - } - - // Set the call state to 'disconnected' and remove it from the calls list. - callToNotify->UpdateDisconnectedReason(aError); - callToNotify->NotifyError(aError); - callToNotify->ChangeState(nsITelephonyService::CALL_STATE_DISCONNECTED); - - return NS_OK; -} - NS_IMETHODIMP Telephony::NotifyCdmaCallWaiting(uint32_t aServiceId, const nsAString& aNumber, uint16_t aNumberPresentation, diff --git a/dom/telephony/Telephony.h b/dom/telephony/Telephony.h index d1820c4e54..0ae8623fc2 100644 --- a/dom/telephony/Telephony.h +++ b/dom/telephony/Telephony.h @@ -189,14 +189,9 @@ private: static bool IsValidServiceId(uint32_t aServiceId); - static bool - IsActiveState(uint16_t aCallState); - uint32_t - ProvidedOrDefaultServiceId(const Optional& aServiceId); - - bool - HasDialingCall(); + GetServiceId(const Optional& aServiceId, + bool aGetIfActiveCall = false); already_AddRefed DialInternal(uint32_t aServiceId, const nsAString& aNumber, bool aEmergency, @@ -213,9 +208,13 @@ private: already_AddRefed CreateCall(TelephonyCallId* aId, - uint32_t aServiceId, uint32_t aCallIndex, uint16_t aCallState, - bool aEmergency = false, bool aConference = false, - bool aSwitchable = true, bool aMergeable = true); + uint32_t aServiceId, + uint32_t aCallIndex, + TelephonyCallState aState, + bool aEmergency = false, + bool aConference = false, + bool aSwitchable = true, + bool aMergeable = true); nsresult NotifyEvent(const nsAString& aType); diff --git a/dom/telephony/TelephonyCall.cpp b/dom/telephony/TelephonyCall.cpp index 813aae3914..aea7fad5d7 100644 --- a/dom/telephony/TelephonyCall.cpp +++ b/dom/telephony/TelephonyCall.cpp @@ -34,16 +34,51 @@ } \ } +#ifdef TELEPHONY_CALL_STATE +#undef TELEPHONY_CALL_STATE +#endif + +#define TELEPHONY_CALL_STATE(_state) \ + (TelephonyCallStateValues::strings[static_cast(_state)].value) + using namespace mozilla::dom; using namespace mozilla::dom::telephony; using mozilla::ErrorResult; +// static +TelephonyCallState +TelephonyCall::ConvertToTelephonyCallState(uint32_t aCallState) +{ + switch (aCallState) { + case nsITelephonyService::CALL_STATE_DIALING: + return TelephonyCallState::Dialing; + case nsITelephonyService::CALL_STATE_ALERTING: + return TelephonyCallState::Alerting; + case nsITelephonyService::CALL_STATE_CONNECTED: + return TelephonyCallState::Connected; + case nsITelephonyService::CALL_STATE_HELD: + return TelephonyCallState::Held; + case nsITelephonyService::CALL_STATE_DISCONNECTED: + return TelephonyCallState::Disconnected; + case nsITelephonyService::CALL_STATE_INCOMING: + return TelephonyCallState::Incoming; + } + + NS_NOTREACHED("Unknown state!"); + return TelephonyCallState::Disconnected; +} + // static already_AddRefed -TelephonyCall::Create(Telephony* aTelephony, TelephonyCallId* aId, - uint32_t aServiceId, uint32_t aCallIndex, - uint16_t aCallState, bool aEmergency, bool aConference, - bool aSwitchable, bool aMergeable) +TelephonyCall::Create(Telephony* aTelephony, + TelephonyCallId* aId, + uint32_t aServiceId, + uint32_t aCallIndex, + TelephonyCallState aState, + bool aEmergency, + bool aConference, + bool aSwitchable, + bool aMergeable) { NS_ASSERTION(aTelephony, "Null aTelephony pointer!"); NS_ASSERTION(aId, "Null aId pointer!"); @@ -61,8 +96,7 @@ TelephonyCall::Create(Telephony* aTelephony, TelephonyCallId* aId, call->mMergeable = aMergeable; call->mError = nullptr; - call->ChangeStateInternal(aCallState, false); - + call->ChangeStateInternal(aState, false); return call.forget(); } @@ -83,35 +117,15 @@ TelephonyCall::WrapObject(JSContext* aCx, JS::Handle aGivenProto) } void -TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents) +TelephonyCall::ChangeStateInternal(TelephonyCallState aState, bool aFireEvents) { RefPtr kungFuDeathGrip(this); - mCallState = aCallState; - switch (aCallState) { - case nsITelephonyService::CALL_STATE_DIALING: - mState.AssignLiteral("dialing"); - break; - case nsITelephonyService::CALL_STATE_ALERTING: - mState.AssignLiteral("alerting"); - break; - case nsITelephonyService::CALL_STATE_CONNECTED: - mState.AssignLiteral("connected"); - break; - case nsITelephonyService::CALL_STATE_HELD: - mState.AssignLiteral("held"); - break; - case nsITelephonyService::CALL_STATE_DISCONNECTED: - mState.AssignLiteral("disconnected"); - break; - case nsITelephonyService::CALL_STATE_INCOMING: - mState.AssignLiteral("incoming"); - break; - default: - NS_NOTREACHED("Unknown state!"); - } + // Update current state + mState = aState; - if (aCallState == nsITelephonyService::CALL_STATE_DISCONNECTED) { + // Handle disconnected calls + if (mState == TelephonyCallState::Disconnected) { NS_ASSERTION(mLive, "Should be live!"); mLive = false; if (mGroup) { @@ -119,8 +133,7 @@ TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents) } else { mTelephony->RemoveCall(this); } - UpdateDisconnectedReason(NS_LITERAL_STRING("NormalCallClearingError")); - } else if (!mLive) { + } else if (!mLive) { // Handle newly added calls mLive = true; if (mGroup) { mGroup->AddCall(this); @@ -129,23 +142,39 @@ TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents) } } + // Dispatch call state changed and call state event if (aFireEvents) { - nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), this); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch specific event!"); - } - - // This can change if the statechange handler called back here... Need to - // figure out something smarter. - if (mCallState == aCallState) { - rv = DispatchCallEvent(mState, this); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch specific event!"); - } - } + NotifyStateChanged(); } } +nsresult +TelephonyCall::NotifyStateChanged() +{ + // Since |mState| can be changed after statechange handler called back here, + // we must save current state. Maybe we should figure out something smarter. + TelephonyCallState prevState = mState; + + nsresult res = DispatchCallEvent(NS_LITERAL_STRING("statechange"), this); + if (NS_FAILED(res)) { + NS_WARNING("Failed to dispatch specific event!"); + } + + // Check whether |mState| remains the same after the statechange handler. + if (mState != prevState) { + NS_WARNING("Call State has changed by statechange handler!"); + return res; + } + + res = DispatchCallEvent(NS_ConvertASCIItoUTF16(TELEPHONY_CALL_STATE(mState)), + this); + if (NS_FAILED(res)) { + NS_WARNING("Failed to dispatch a specific event!"); + } + + return res; +} + nsresult TelephonyCall::DispatchCallEvent(const nsAString& aType, TelephonyCall* aCall) @@ -196,16 +225,23 @@ TelephonyCall::NotifyError(const nsAString& aError) void TelephonyCall::UpdateDisconnectedReason(const nsAString& aDisconnectedReason) { - NS_ASSERTION(Substring(aDisconnectedReason, aDisconnectedReason.Length() - 5).EqualsLiteral("Error"), + NS_ASSERTION(Substring(aDisconnectedReason, + aDisconnectedReason.Length() - 5).EqualsLiteral("Error"), "Disconnected reason should end with 'Error'"); - if (mDisconnectedReason.IsNull()) { - // There is no 'Error' suffix in the corresponding enum. We should skip - // that part for comparison. - CONVERT_STRING_TO_NULLABLE_ENUM( - Substring(aDisconnectedReason, 0, aDisconnectedReason.Length() - 5), - TelephonyCallDisconnectedReason, - mDisconnectedReason); + if (!mDisconnectedReason.IsNull()) { + return; + } + + // There is no 'Error' suffix in the corresponding enum. We should skip + // that part for comparison. + CONVERT_STRING_TO_NULLABLE_ENUM( + Substring(aDisconnectedReason, 0, aDisconnectedReason.Length() - 5), + TelephonyCallDisconnectedReason, + mDisconnectedReason); + + if (!aDisconnectedReason.EqualsLiteral("NormalCallClearingError")) { + NotifyError(aDisconnectedReason); } } @@ -272,9 +308,10 @@ TelephonyCall::Answer(ErrorResult& aRv) return nullptr; } - if (mCallState != nsITelephonyService::CALL_STATE_INCOMING) { + if (mState != TelephonyCallState::Incoming) { NS_WARNING(nsPrintfCString("Answer on non-incoming call is rejected!" - " (State: %u)", mCallState).get()); + " (State: %s)", + TELEPHONY_CALL_STATE(mState)).get()); promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); return promise.forget(); } @@ -294,15 +331,16 @@ TelephonyCall::HangUp(ErrorResult& aRv) return nullptr; } - if (mCallState == nsITelephonyService::CALL_STATE_DISCONNECTED) { - NS_WARNING(nsPrintfCString("HangUp on previously disconnected call" - " is rejected! (State: %u)", mCallState).get()); + if (mState == TelephonyCallState::Disconnected) { + NS_WARNING(nsPrintfCString("HangUp on a disconnected call is rejected!" + " (State: %s)", + TELEPHONY_CALL_STATE(mState)).get()); promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); return promise.forget(); } nsCOMPtr callback = new TelephonyCallback(promise); - aRv = mCallState == nsITelephonyService::CALL_STATE_INCOMING ? + aRv = mState == TelephonyCallState::Incoming ? mTelephony->Service()->RejectCall(mServiceId, mCallIndex, callback) : mTelephony->Service()->HangUpCall(mServiceId, mCallIndex, callback); NS_ENSURE_TRUE(!aRv.Failed(), nullptr); @@ -328,30 +366,13 @@ TelephonyCall::Hold(ErrorResult& aRv) return promise.forget(); } -already_AddRefed -TelephonyCall::Resume(ErrorResult& aRv) -{ - RefPtr promise = CreatePromise(aRv); - if (!promise) { - return nullptr; - } - - nsCOMPtr callback = new TelephonyCallback(promise); - aRv = Resume(callback); - if (NS_WARN_IF(aRv.Failed() && - !aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR))) { - return nullptr; - } - - return promise.forget(); -} - nsresult TelephonyCall::Hold(nsITelephonyCallback* aCallback) { - if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) { + if (mState != TelephonyCallState::Connected) { NS_WARNING(nsPrintfCString("Hold non-connected call is rejected!" - " (State: %u)", mCallState).get()); + " (State: %s)", + TELEPHONY_CALL_STATE(mState)).get()); aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError")); return NS_ERROR_DOM_INVALID_STATE_ERR; } @@ -383,11 +404,31 @@ TelephonyCall::Hold(nsITelephonyCallback* aCallback) return NS_OK; } +already_AddRefed +TelephonyCall::Resume(ErrorResult& aRv) +{ + RefPtr promise = CreatePromise(aRv); + if (!promise) { + return nullptr; + } + + nsCOMPtr callback = new TelephonyCallback(promise); + aRv = Resume(callback); + if (NS_WARN_IF(aRv.Failed() && + !aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR))) { + return nullptr; + } + + return promise.forget(); +} + nsresult TelephonyCall::Resume(nsITelephonyCallback* aCallback) { - if (mCallState != nsITelephonyService::CALL_STATE_HELD) { - NS_WARNING("Resume non-held call is rejected!"); + if (mState != TelephonyCallState::Held) { + NS_WARNING(nsPrintfCString("Resume non-held call is rejected!" + " (State: %s)", + TELEPHONY_CALL_STATE(mState)).get()); aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError")); return NS_ERROR_DOM_INVALID_STATE_ERR; } diff --git a/dom/telephony/TelephonyCall.h b/dom/telephony/TelephonyCall.h index 729fd159ba..3ecf39a1fe 100644 --- a/dom/telephony/TelephonyCall.h +++ b/dom/telephony/TelephonyCall.h @@ -29,7 +29,7 @@ class TelephonyCall final : public DOMEventTargetHelper RefPtr mSecondId; uint32_t mServiceId; - nsString mState; + TelephonyCallState mState; bool mEmergency; RefPtr mError; Nullable mDisconnectedReason; @@ -38,7 +38,6 @@ class TelephonyCall final : public DOMEventTargetHelper bool mMergeable; uint32_t mCallIndex; - uint16_t mCallState; bool mLive; public: @@ -65,10 +64,10 @@ public: already_AddRefed GetSecondId() const; - void - GetState(nsString& aState) const + TelephonyCallState + State() const { - aState.Assign(mState); + return mState; } bool @@ -89,6 +88,14 @@ public: return mMergeable; } + bool + IsActive() const + { + return mState == TelephonyCallState::Dialing || + mState == TelephonyCallState::Alerting || + mState == TelephonyCallState::Connected; + } + already_AddRefed GetError() const; @@ -122,18 +129,24 @@ public: IMPL_EVENT_HANDLER(error) IMPL_EVENT_HANDLER(groupchange) + static TelephonyCallState + ConvertToTelephonyCallState(uint32_t aCallState); + static already_AddRefed Create(Telephony* aTelephony, TelephonyCallId* aId, - uint32_t aServiceId, uint32_t aCallIndex, uint16_t aCallState, + uint32_t aServiceId, uint32_t aCallIndex, TelephonyCallState aState, bool aEmergency = false, bool aConference = false, bool aSwitchable = true, bool aMergeable = true); void - ChangeState(uint16_t aCallState) + ChangeState(TelephonyCallState aState) { - ChangeStateInternal(aCallState, true); + ChangeStateInternal(aState, true); } + nsresult + NotifyStateChanged(); + uint32_t ServiceId() const { @@ -146,12 +159,6 @@ public: return mCallIndex; } - uint16_t - CallState() const - { - return mCallState; - } - void UpdateEmergency(bool aEmergency) { @@ -194,7 +201,7 @@ private: Resume(nsITelephonyCallback* aCallback); void - ChangeStateInternal(uint16_t aCallState, bool aFireEvents); + ChangeStateInternal(TelephonyCallState aState, bool aFireEvents); nsresult DispatchCallEvent(const nsAString& aType, diff --git a/dom/telephony/TelephonyCallGroup.cpp b/dom/telephony/TelephonyCallGroup.cpp index 7af565e6d3..e79fe276f8 100644 --- a/dom/telephony/TelephonyCallGroup.cpp +++ b/dom/telephony/TelephonyCallGroup.cpp @@ -10,16 +10,23 @@ #include "Telephony.h" #include "mozilla/dom/CallEvent.h" #include "mozilla/dom/CallGroupErrorEvent.h" -#include "mozilla/dom/TelephonyCallGroupBinding.h" #include "mozilla/dom/telephony/TelephonyCallback.h" +#include "nsPrintfCString.h" + +#ifdef TELEPHONY_GROUP_STATE +#undef TELEPHONY_GROUP_STATE +#endif + +#define TELEPHONY_GROUP_STATE(_state) \ + (TelephonyCallGroupStateValues::strings[static_cast(_state)].value) + using namespace mozilla::dom; using namespace mozilla::dom::telephony; using mozilla::ErrorResult; TelephonyCallGroup::TelephonyCallGroup(nsPIDOMWindow* aOwner) : DOMEventTargetHelper(aOwner) - , mCallState(nsITelephonyService::CALL_STATE_UNKNOWN) { } @@ -37,6 +44,7 @@ TelephonyCallGroup::Create(Telephony* aTelephony) new TelephonyCallGroup(aTelephony->GetOwner()); group->mTelephony = aTelephony; + group->mState = TelephonyCallGroupState::_empty; group->mCallsList = new CallsList(aTelephony, group); return group.forget(); @@ -82,48 +90,84 @@ TelephonyCallGroup::NotifyError(const nsAString& aName, const nsAString& aMessag } void -TelephonyCallGroup::ChangeState(uint16_t aCallState) +TelephonyCallGroup::ChangeState() { - if (mCallState == aCallState) { + MOZ_ASSERT(mCalls.Length() != 1); + if (mCalls.Length() == 0) { + ChangeStateInternal(TelephonyCallGroupState::_empty); return; } - mCallState = aCallState; - switch (aCallState) { - case nsITelephonyService::CALL_STATE_UNKNOWN: - mState.AssignLiteral(""); - break; - case nsITelephonyService::CALL_STATE_CONNECTED: - mState.AssignLiteral("connected"); - break; - case nsITelephonyService::CALL_STATE_HELD: - mState.AssignLiteral("held"); - break; - default: - NS_NOTREACHED("Unknown state!"); - } - - nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), nullptr); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch specific event!"); - } - if (!mState.IsEmpty()) { - // This can change if the statechange handler called back here... Need to - // figure out something smarter. - if (mCallState == aCallState) { - rv = DispatchCallEvent(mState, nullptr); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch specific event!"); - } + TelephonyCallState state = mCalls[0]->State(); + for (uint32_t i = 1; i < mCalls.Length(); i++) { + if (mCalls[i]->State() != state) { + MOZ_ASSERT(false, "Various call states are found in a call group!"); + ChangeStateInternal(TelephonyCallGroupState::_empty); + return; } } - for (uint32_t index = 0; index < mCalls.Length(); index++) { - RefPtr call = mCalls[index]; - call->ChangeState(aCallState); - - MOZ_ASSERT(call->CallState() == aCallState); + TelephonyCallGroupState groupState = TelephonyCallGroupState::_empty; + switch (state) { + case TelephonyCallState::Connected: + groupState = TelephonyCallGroupState::Connected; + break; + case TelephonyCallState::Held: + groupState = TelephonyCallGroupState::Held; + break; + default: + NS_NOTREACHED(nsPrintfCString("Invavild call state for a call group(%s)!", + TELEPHONY_CALL_STATE(state)).get()); } + + ChangeStateInternal(groupState); +} + +void +TelephonyCallGroup::ChangeStateInternal(TelephonyCallGroupState aState) +{ + if (mState == aState) { + return; + } + + // Update Current State + mState = aState; + + // Dispatch related events + NotifyStateChanged(); +} + +nsresult +TelephonyCallGroup::NotifyStateChanged() +{ + // Since |mState| can be changed after statechange handler called back here, + // we must save current state. Maybe we should figure out something smarter. + TelephonyCallGroupState prevState = mState; + + nsresult res = DispatchCallEvent(NS_LITERAL_STRING("statechange"), nullptr); + if (NS_FAILED(res)) { + NS_WARNING("Failed to dispatch specific event!"); + } + + // Check whether |mState| remains the same after the statechange handler. + // Besides, If there is no conference call at all, then we dont't have to + // dispatch the state evnet. + if (mState == prevState) { + res = DispatchCallEvent(NS_ConvertASCIItoUTF16(TELEPHONY_GROUP_STATE(mState)), + nullptr); + if (NS_FAILED(res)) { + NS_WARNING("Failed to dispatch specific event!"); + } + } + + // Notify each call within to dispatch call state change event + for (uint32_t index = 0; index < mCalls.Length(); index++) { + if (NS_FAILED(mCalls[index]->NotifyStateChanged())){ + res = NS_ERROR_FAILURE; + } + } + + return res; } nsresult @@ -172,14 +216,13 @@ TelephonyCallGroup::CanConference(const TelephonyCall& aCall, if (!aSecondCall) { MOZ_ASSERT(!mCalls.IsEmpty()); - - return (mCallState == nsITelephonyService::CALL_STATE_CONNECTED && - aCall.CallState() == nsITelephonyService::CALL_STATE_HELD) || - (mCallState == nsITelephonyService::CALL_STATE_HELD && - aCall.CallState() == nsITelephonyService::CALL_STATE_CONNECTED); + return (mState == TelephonyCallGroupState::Connected && + aCall.State() == TelephonyCallState::Held) || + (mState == TelephonyCallGroupState::Held && + aCall.State() == TelephonyCallState::Connected); } - MOZ_ASSERT(mCallState == nsITelephonyService::CALL_STATE_UNKNOWN); + MOZ_ASSERT(mState != TelephonyCallGroupState::_empty); if (aCall.ServiceId() != aSecondCall->ServiceId()) { return false; @@ -189,10 +232,10 @@ TelephonyCallGroup::CanConference(const TelephonyCall& aCall, return false; } - return (aCall.CallState() == nsITelephonyService::CALL_STATE_CONNECTED && - aSecondCall->CallState() == nsITelephonyService::CALL_STATE_HELD) || - (aCall.CallState() == nsITelephonyService::CALL_STATE_HELD && - aSecondCall->CallState() == nsITelephonyService::CALL_STATE_CONNECTED); + return (aCall.State() == TelephonyCallState::Connected && + aSecondCall->State() == TelephonyCallState::Held) || + (aCall.State() == TelephonyCallState::Held && + aSecondCall->State() == TelephonyCallState::Connected); } already_AddRefed @@ -297,7 +340,7 @@ TelephonyCallGroup::Remove(TelephonyCall& aCall, ErrorResult& aRv) return nullptr; } - if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) { + if (mState != TelephonyCallGroupState::Connected) { NS_WARNING("Remove call from a non-connected call group. Ignore!"); promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); return promise.forget(); @@ -330,6 +373,14 @@ TelephonyCallGroup::HangUp(ErrorResult& aRv) return nullptr; } + if (mState == TelephonyCallGroupState::_empty) { + NS_WARNING(nsPrintfCString("We don't have a call group now!" + " (State: %s)", + TELEPHONY_GROUP_STATE(mState)).get()); + promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); + return promise.forget(); + } + nsCOMPtr callback = new TelephonyCallback(promise); aRv = mTelephony->Service()->HangUpConference(mCalls[0]->ServiceId(), callback); @@ -357,6 +408,26 @@ TelephonyCallGroup::Hold(ErrorResult& aRv) return promise.forget(); } +nsresult +TelephonyCallGroup::Hold(nsITelephonyCallback* aCallback) +{ + if (mState != TelephonyCallGroupState::Connected) { + NS_WARNING(nsPrintfCString("Resume non-connected call group is rejected!" + " (State: %s)", + TELEPHONY_GROUP_STATE(mState)).get()); + aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError")); + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + + nsresult rv = mTelephony->Service()->HoldConference(mCalls[0]->ServiceId(), + aCallback); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + already_AddRefed TelephonyCallGroup::Resume(ErrorResult& aRv) { @@ -377,29 +448,13 @@ TelephonyCallGroup::Resume(ErrorResult& aRv) return promise.forget(); } -nsresult -TelephonyCallGroup::Hold(nsITelephonyCallback* aCallback) -{ - if (mCallState != nsITelephonyService::CALL_STATE_CONNECTED) { - NS_WARNING("Holding a non-connected call is rejected!"); - aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError")); - return NS_ERROR_DOM_INVALID_STATE_ERR; - } - - nsresult rv = mTelephony->Service()->HoldConference(mCalls[0]->ServiceId(), - aCallback); - if (NS_WARN_IF(NS_FAILED(rv))) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - nsresult TelephonyCallGroup::Resume(nsITelephonyCallback* aCallback) { - if (mCallState != nsITelephonyService::CALL_STATE_HELD) { - NS_WARNING("Resuming a non-held call is rejected!"); + if (mState != TelephonyCallGroupState::Held) { + NS_WARNING(nsPrintfCString("Resume non-held call group is rejected!" + " (State: %s)", + TELEPHONY_GROUP_STATE(mState)).get()); aCallback->NotifyError(NS_LITERAL_STRING("InvalidStateError")); return NS_ERROR_DOM_INVALID_STATE_ERR; } diff --git a/dom/telephony/TelephonyCallGroup.h b/dom/telephony/TelephonyCallGroup.h index 57113530d8..8d1d90f605 100644 --- a/dom/telephony/TelephonyCallGroup.h +++ b/dom/telephony/TelephonyCallGroup.h @@ -8,6 +8,7 @@ #define mozilla_dom_telephony_telephonycallgroup_h__ #include "mozilla/dom/Promise.h" +#include "mozilla/dom/TelephonyCallGroupBinding.h" #include "mozilla/dom/telephony/TelephonyCommon.h" namespace mozilla { @@ -21,9 +22,7 @@ class TelephonyCallGroup final : public DOMEventTargetHelper RefPtr mCallsList; - nsString mState; - - uint16_t mCallState; + TelephonyCallGroupState mState; public: NS_DECL_ISUPPORTS_INHERITED @@ -64,10 +63,15 @@ public: already_AddRefed Resume(ErrorResult& aRv); - void - GetState(nsString& aState) const + TelephonyCallGroupState + State() const { - aState = mState; + return mState; + } + + bool + IsActive() { + return mState == TelephonyCallGroupState::Connected; } IMPL_EVENT_HANDLER(statechange) @@ -94,14 +98,9 @@ public: return mCalls; } + // Update its call state according to the calls wihtin itself. void - ChangeState(uint16_t aCallState); - - uint16_t - CallState() const - { - return mCallState; - } + ChangeState(); nsresult NotifyError(const nsAString& aName, const nsAString& aMessage); @@ -116,9 +115,15 @@ private: nsresult Resume(nsITelephonyCallback* aCallback); + nsresult + NotifyStateChanged(); + nsresult NotifyCallsChanged(TelephonyCall* aCall); + void + ChangeStateInternal(TelephonyCallGroupState aState); + nsresult DispatchCallEvent(const nsAString& aType, TelephonyCall* aCall); diff --git a/dom/telephony/TelephonyCallInfo.cpp b/dom/telephony/TelephonyCallInfo.cpp index fe45afc523..8ad6923f8c 100644 --- a/dom/telephony/TelephonyCallInfo.cpp +++ b/dom/telephony/TelephonyCallInfo.cpp @@ -15,10 +15,13 @@ NS_IMPL_ISUPPORTS(TelephonyCallInfo, nsITelephonyCallInfo) TelephonyCallInfo::TelephonyCallInfo(uint32_t aClientId, uint32_t aCallIndex, uint16_t aCallState, + const nsAString& aDisconnectedReason, + const nsAString& aNumber, uint16_t aNumberPresentation, const nsAString& aName, uint16_t aNamePresentation, + bool aIsOutgoing, bool aIsEmergency, bool aIsConference, @@ -27,10 +30,13 @@ TelephonyCallInfo::TelephonyCallInfo(uint32_t aClientId, : mClientId(aClientId), mCallIndex(aCallIndex), mCallState(aCallState), + mDisconnectedReason(aDisconnectedReason), + mNumber(aNumber), mNumberPresentation(aNumberPresentation), mName(aName), mNamePresentation(aNamePresentation), + mIsOutgoing(aIsOutgoing), mIsEmergency(aIsEmergency), mIsConference(aIsConference), @@ -60,6 +66,13 @@ TelephonyCallInfo::GetCallState(uint16_t* aCallState) return NS_OK; } +NS_IMETHODIMP +TelephonyCallInfo::GetDisconnectedReason(nsAString& aDisconnectedReason) +{ + aDisconnectedReason = mDisconnectedReason; + return NS_OK; +} + NS_IMETHODIMP TelephonyCallInfo::GetNumber(nsAString& aNumber) { diff --git a/dom/telephony/TelephonyCallInfo.h b/dom/telephony/TelephonyCallInfo.h index 689cca4457..81be49aa5b 100644 --- a/dom/telephony/TelephonyCallInfo.h +++ b/dom/telephony/TelephonyCallInfo.h @@ -21,12 +21,21 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSITELEPHONYCALLINFO - TelephonyCallInfo(uint32_t aClientId, uint32_t aCallIndex, - uint16_t aCallState, const nsAString& aNumber, - uint16_t aNumberPresentation, const nsAString& aName, - uint16_t aNamePresentation, bool aIsOutgoing, - bool aIsEmergency, bool aIsConference, - bool aIsSwitchable, bool aIsMergeable); + TelephonyCallInfo(uint32_t aClientId, + uint32_t aCallIndex, + uint16_t aCallState, + const nsAString& aDisconnectedReason, + + const nsAString& aNumber, + uint16_t aNumberPresentation, + const nsAString& aName, + uint16_t aNamePresentation, + + bool aIsOutgoing, + bool aIsEmergency, + bool aIsConference, + bool aIsSwitchable, + bool aIsMergeable); private: // Don't try to use the default constructor. @@ -37,10 +46,13 @@ private: uint32_t mClientId; uint32_t mCallIndex; uint16_t mCallState; + nsString mDisconnectedReason; + nsString mNumber; uint16_t mNumberPresentation; nsString mName; uint16_t mNamePresentation; + bool mIsOutgoing; bool mIsEmergency; bool mIsConference; diff --git a/dom/telephony/TelephonyDialCallback.cpp b/dom/telephony/TelephonyDialCallback.cpp index 1a28117ca1..d3a632944a 100644 --- a/dom/telephony/TelephonyDialCallback.cpp +++ b/dom/telephony/TelephonyDialCallback.cpp @@ -59,7 +59,7 @@ TelephonyDialCallback::NotifyDialCallSuccess(uint32_t aClientId, RefPtr id = mTelephony->CreateCallId(aNumber); RefPtr call = mTelephony->CreateCall(id, aClientId, aCallIndex, - nsITelephonyService::CALL_STATE_DIALING); + TelephonyCallState::Dialing); mPromise->MaybeResolve(call); return NS_OK; diff --git a/dom/telephony/gonk/TelephonyService.js b/dom/telephony/gonk/TelephonyService.js index 2c5b06c159..c43c8535d4 100644 --- a/dom/telephony/gonk/TelephonyService.js +++ b/dom/telephony/gonk/TelephonyService.js @@ -11,6 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); +/* global RIL */ XPCOMUtils.defineLazyGetter(this, "RIL", function () { let obj = {}; Cu.import("resource://gre/modules/ril_consts.js", obj); @@ -22,8 +23,6 @@ const GONK_TELEPHONYSERVICE_CONTRACTID = const GONK_TELEPHONYSERVICE_CID = Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}"); -const MOBILECALLFORWARDINGOPTIONS_CID = - Components.ID("{79b5988b-9436-48d8-a652-88fa033f146c}"); const TELEPHONYCALLINFO_CID = Components.ID("{d9e8b358-a02c-4cf3-9fc7-816c2e8d46e4}"); @@ -53,11 +52,190 @@ const DIAL_ERROR_RADIO_NOT_AVAILABLE = RIL.GECKO_ERROR_RADIO_NOT_AVAILABLE; const TONES_GAP_DURATION = 70; +// Consts for MMI. +// MMI procedure as defined in TS.22.030 6.5.2 +const MMI_PROCEDURE_ACTIVATION = "*"; +const MMI_PROCEDURE_DEACTIVATION = "#"; +const MMI_PROCEDURE_INTERROGATION = "*#"; +const MMI_PROCEDURE_REGISTRATION = "**"; +const MMI_PROCEDURE_ERASURE = "##"; + +XPCOMUtils.defineConstant(this, "MMI_PROCEDURE_ACTIVATION", MMI_PROCEDURE_ACTIVATION); +XPCOMUtils.defineConstant(this, "MMI_PROCEDURE_DEACTIVATION", MMI_PROCEDURE_DEACTIVATION); +XPCOMUtils.defineConstant(this, "MMI_PROCEDURE_INTERROGATION", MMI_PROCEDURE_INTERROGATION); +XPCOMUtils.defineConstant(this, "MMI_PROCEDURE_REGISTRATION", MMI_PROCEDURE_REGISTRATION); +XPCOMUtils.defineConstant(this, "MMI_PROCEDURE_ERASURE", MMI_PROCEDURE_ERASURE); + +// MMI call forwarding service codes as defined in TS.22.030 Annex B +const MMI_SC_CFU = "21"; +const MMI_SC_CF_BUSY = "67"; +const MMI_SC_CF_NO_REPLY = "61"; +const MMI_SC_CF_NOT_REACHABLE = "62"; +const MMI_SC_CF_ALL = "002"; +const MMI_SC_CF_ALL_CONDITIONAL = "004"; + +// MMI service codes for PIN/PIN2/PUK/PUK2 management as defined in TS.22.030 +// sec 6.6 +const MMI_SC_PIN = "04"; +const MMI_SC_PIN2 = "042"; +const MMI_SC_PUK = "05"; +const MMI_SC_PUK2 = "052"; + +// MMI service code for IMEI presentation as defined in TS.22.030 sec 6.7 +const MMI_SC_IMEI = "06"; + +// MMI call waiting service code +const MMI_SC_CALL_WAITING = "43"; + +// MMI service code for registration new password as defined in TS 22.030 6.5.4 +const MMI_SC_CHANGE_PASSWORD = "03"; +const MMI_ZZ_BARRING_SERVICE = "330"; + +// MMI call barring service codes +const MMI_SC_BAOC = "33"; +const MMI_SC_BAOIC = "331"; +const MMI_SC_BAOICxH = "332"; +const MMI_SC_BAIC = "35"; +const MMI_SC_BAICr = "351"; +const MMI_SC_BA_ALL = "330"; +const MMI_SC_BA_MO = "333"; +const MMI_SC_BA_MT = "353"; + +// MMI called line presentation service codes +const MMI_SC_CLIP = "30"; +const MMI_SC_CLIR = "31"; + +// MMI service code key strings. +const MMI_KS_SC_CALL_BARRING = "scCallBarring"; +const MMI_KS_SC_CALL_FORWARDING = "scCallForwarding"; +const MMI_KS_SC_CLIP = "scClip"; +const MMI_KS_SC_CLIR = "scClir"; +const MMI_KS_SC_PWD = "scPwd"; +const MMI_KS_SC_CALL_WAITING = "scCallWaiting"; +const MMI_KS_SC_PIN = "scPin"; +const MMI_KS_SC_PIN2 = "scPin2"; +const MMI_KS_SC_PUK = "scPuk"; +const MMI_KS_SC_PUK2 = "scPuk2"; +const MMI_KS_SC_CHANGE_PASSWORD = "scChangePassword"; +const MMI_KS_SC_IMEI = "scImei"; +const MMI_KS_SC_USSD = "scUssd"; +const MMI_KS_SC_CALL = "scCall"; + +// MMI error messages key strings. +const MMI_ERROR_KS_ERROR = "emMmiError"; +const MMI_ERROR_KS_NOT_SUPPORTED = "emMmiErrorNotSupported"; +const MMI_ERROR_KS_INVALID_ACTION = "emMmiErrorInvalidAction"; +const MMI_ERROR_KS_MISMATCH_PIN = "emMmiErrorMismatchPin"; +const MMI_ERROR_KS_MISMATCH_PASSWORD = "emMmiErrorMismatchPassword"; +const MMI_ERROR_KS_BAD_PIN = "emMmiErrorBadPin"; +const MMI_ERROR_KS_BAD_PUK = "emMmiErrorBadPuk"; +const MMI_ERROR_KS_INVALID_PIN = "emMmiErrorInvalidPin"; +const MMI_ERROR_KS_INVALID_PASSWORD = "emMmiErrorInvalidPassword"; +const MMI_ERROR_KS_NEEDS_PUK = "emMmiErrorNeedsPuk"; +const MMI_ERROR_KS_SIM_BLOCKED = "emMmiErrorSimBlocked"; + +// MMI status message. +const MMI_SM_KS_PASSWORD_CHANGED = "smPasswordChanged"; +const MMI_SM_KS_PIN_CHANGED = "smPinChanged"; +const MMI_SM_KS_PIN2_CHANGED = "smPin2Changed"; +const MMI_SM_KS_PIN_UNBLOCKED = "smPinUnblocked"; +const MMI_SM_KS_PIN2_UNBLOCKED = "smPin2Unblocked"; +const MMI_SM_KS_SERVICE_ENABLED = "smServiceEnabled"; +const MMI_SM_KS_SERVICE_ENABLED_FOR = "smServiceEnabledFor"; +const MMI_SM_KS_SERVICE_DISABLED = "smServiceDisabled"; +const MMI_SM_KS_SERVICE_REGISTERED = "smServiceRegistered"; +const MMI_SM_KS_SERVICE_ERASED = "smServiceErased"; +const MMI_SM_KS_SERVICE_INTERROGATED = "smServiceInterrogated"; +const MMI_SM_KS_SERVICE_NOT_PROVISIONED = "smServiceNotProvisioned"; +const MMI_SM_KS_CLIR_PERMANENT = "smClirPermanent"; +const MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON = "smClirDefaultOnNextCallOn"; +const MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF = "smClirDefaultOnNextCallOff"; +const MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON = "smClirDefaultOffNextCallOn"; +const MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF = "smClirDefaultOffNextCallOff"; +const MMI_SM_KS_CALL_CONTROL = "smCallControl"; + +// MMI Service class +const MMI_KS_SERVICE_CLASS_VOICE = "serviceClassVoice"; +const MMI_KS_SERVICE_CLASS_DATA = "serviceClassData"; +const MMI_KS_SERVICE_CLASS_FAX = "serviceClassFax"; +const MMI_KS_SERVICE_CLASS_SMS = "serviceClassSms"; +const MMI_KS_SERVICE_CLASS_DATA_SYNC = "serviceClassDataSync"; +const MMI_KS_SERVICE_CLASS_DATA_ASYNC = "serviceClassDataAsync"; +const MMI_KS_SERVICE_CLASS_PACKET = "serviceClassPacket"; +const MMI_KS_SERVICE_CLASS_PAD = "serviceClassPad"; + +// States of USSD Session : DONE -> ONGOING [-> CANCELLING] -> DONE +const USSD_SESSION_DONE = "DONE"; +const USSD_SESSION_ONGOING = "ONGOING"; +const USSD_SESSION_CANCELLING = "CANCELLING"; + +const MMI_PROC_TO_CF_ACTION = {}; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ACTIVATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ENABLE; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_DEACTIVATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_DISABLE; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_INTERROGATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_QUERY_STATUS; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_REGISTRATION] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_REGISTRATION; +MMI_PROC_TO_CF_ACTION[MMI_PROCEDURE_ERASURE] = Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ERASURE; + +const MMI_SC_TO_CF_REASON = {}; +MMI_SC_TO_CF_REASON[MMI_SC_CFU] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL; +MMI_SC_TO_CF_REASON[MMI_SC_CF_BUSY] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_MOBILE_BUSY; +MMI_SC_TO_CF_REASON[MMI_SC_CF_NO_REPLY] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_NO_REPLY; +MMI_SC_TO_CF_REASON[MMI_SC_CF_NOT_REACHABLE] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_NOT_REACHABLE; +MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_ALL_CALL_FORWARDING; +MMI_SC_TO_CF_REASON[MMI_SC_CF_ALL_CONDITIONAL] = Ci.nsIMobileConnection.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING; + +const MMI_SC_TO_LOCK_TYPE = {}; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PIN] = Ci.nsIIcc.CARD_LOCK_TYPE_PIN; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PIN2] = Ci.nsIIcc.CARD_LOCK_TYPE_PIN2; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PUK] = Ci.nsIIcc.CARD_LOCK_TYPE_PUK; +MMI_SC_TO_LOCK_TYPE[MMI_SC_PUK2] = Ci.nsIIcc.CARD_LOCK_TYPE_PUK2; + +const MMI_PROC_TO_CLIR_ACTION = {}; +MMI_PROC_TO_CLIR_ACTION[MMI_PROCEDURE_ACTIVATION] = Ci.nsIMobileConnection.CLIR_INVOCATION; +MMI_PROC_TO_CLIR_ACTION[MMI_PROCEDURE_DEACTIVATION] = Ci.nsIMobileConnection.CLIR_SUPPRESSION; + +const MMI_SC_TO_CB_PROGRAM = {}; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAOC] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAOIC] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAOICxH] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAIC] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_INCOMING; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BAICr] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_INCOMING_ROAMING; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BA_ALL] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_ALL_SERVICE; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BA_MO] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_OUTGOING_SERVICE; +MMI_SC_TO_CB_PROGRAM[MMI_SC_BA_MT] = Ci.nsIMobileConnection.CALL_BARRING_PROGRAM_INCOMING_SERVICE; + +const CF_ACTION_TO_STATUS_MESSAGE = {}; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ENABLE] = MMI_SM_KS_SERVICE_ENABLED; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_DISABLE] = MMI_SM_KS_SERVICE_DISABLED; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_REGISTRATION] = MMI_SM_KS_SERVICE_REGISTERED; +CF_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CALL_FORWARD_ACTION_ERASURE] = MMI_SM_KS_SERVICE_ERASED; + +const LOCK_TYPE_TO_STATUS_MESSAGE = {}; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PIN] = MMI_SM_KS_PIN_CHANGED; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PIN2] = MMI_SM_KS_PIN2_CHANGED; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PUK] = MMI_SM_KS_PIN_UNBLOCKED; +LOCK_TYPE_TO_STATUS_MESSAGE[Ci.nsIIcc.CARD_LOCK_TYPE_PUK2] = MMI_SM_KS_PIN2_UNBLOCKED; + +const CLIR_ACTION_TO_STATUS_MESSAGE = {}; +CLIR_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CLIR_INVOCATION] = MMI_SM_KS_SERVICE_ENABLED; +CLIR_ACTION_TO_STATUS_MESSAGE[Ci.nsIMobileConnection.CLIR_SUPPRESSION] = MMI_SM_KS_SERVICE_DISABLED; + +const MMI_KS_SERVICE_CLASS_MAPPING = {}; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE] = MMI_KS_SERVICE_CLASS_VOICE; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA] = MMI_KS_SERVICE_CLASS_DATA; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX] = MMI_KS_SERVICE_CLASS_FAX; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS] = MMI_KS_SERVICE_CLASS_SMS; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_SYNC] = MMI_KS_SERVICE_CLASS_DATA_SYNC; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_ASYNC] = MMI_KS_SERVICE_CLASS_DATA_ASYNC; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PACKET] = MMI_KS_SERVICE_CLASS_PACKET; +MMI_KS_SERVICE_CLASS_MAPPING[Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PAD] = MMI_KS_SERVICE_CLASS_PAD; + var DEBUG; function debug(s) { dump("TelephonyService: " + s + "\n"); } +/* global gRadioInterfaceLayer */ XPCOMUtils.defineLazyGetter(this, "gRadioInterfaceLayer", function() { let ril = { numRadioInterfaces: 0 }; try { @@ -66,66 +244,51 @@ XPCOMUtils.defineLazyGetter(this, "gRadioInterfaceLayer", function() { return ril; }); +/* global gPowerManagerService */ XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService", "@mozilla.org/power/powermanagerservice;1", "nsIPowerManagerService"); +/* global gTelephonyMessenger */ XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyMessenger", "@mozilla.org/ril/system-messenger-helper;1", "nsITelephonyMessenger"); +/* global gAudioService */ XPCOMUtils.defineLazyServiceGetter(this, "gAudioService", "@mozilla.org/telephony/audio-service;1", "nsITelephonyAudioService"); +/* global gGonkMobileConnectionService */ XPCOMUtils.defineLazyServiceGetter(this, "gGonkMobileConnectionService", "@mozilla.org/mobileconnection/mobileconnectionservice;1", "nsIGonkMobileConnectionService"); -XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function() { - let ns = {}; - Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns); - return ns.PhoneNumberUtils; -}); +/* global gIccService */ +XPCOMUtils.defineLazyServiceGetter(this, "gIccService", + "@mozilla.org/icc/iccservice;1", + "nsIIccService"); -XPCOMUtils.defineLazyGetter(this, "gDialNumberUtils", function() { - let ns = {}; - Cu.import("resource://gre/modules/DialNumberUtils.jsm", ns); - return ns.DialNumberUtils; -}); -function MobileCallForwardingOptions(aOptions) { - for (let key in aOptions) { - this[key] = aOptions[key]; - } -} -MobileCallForwardingOptions.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileCallForwardingOptions]), - classID: MOBILECALLFORWARDINGOPTIONS_CID, - classInfo: XPCOMUtils.generateCI({ - classID: MOBILECALLFORWARDINGOPTIONS_CID, - classDescription: "MobileCallForwardingOptions", - interfaces: [Ci.nsIMobileCallForwardingOptions] - }), +/* global PhoneNumberUtils */ +XPCOMUtils.defineLazyModuleGetter(this, "PhoneNumberUtils", + "resource://gre/modules/PhoneNumberUtils.jsm"); - // nsIMobileForwardingOptions - - active: false, - action: nsIMobileConnection.CALL_FORWARD_ACTION_UNKNOWN, - reason: nsIMobileConnection.CALL_FORWARD_REASON_UNKNOWN, - number: null, - timeSeconds: -1, - serviceClass: nsIMobileConnection.ICC_SERVICE_CLASS_NONE -}; +/* global DialNumberUtils */ +XPCOMUtils.defineLazyModuleGetter(this, "DialNumberUtils", + "resource://gre/modules/DialNumberUtils.jsm"); function TelephonyCallInfo(aCall) { this.clientId = aCall.clientId; this.callIndex = aCall.callIndex; this.callState = aCall.state; + this.disconnectedReason = aCall.disconnectedReason || ""; + this.number = aCall.number; this.numberPresentation = aCall.numberPresentation; this.name = aCall.name; this.namePresentation = aCall.namePresentation; + this.isOutgoing = aCall.isOutgoing; this.isEmergency = aCall.isEmergency; this.isConference = aCall.isConference; @@ -146,10 +309,13 @@ TelephonyCallInfo.prototype = { clientId: 0, callIndex: 0, callState: nsITelephonyService.CALL_STATE_UNKNOWN, + disconnectedReason: "", + number: "", numberPresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED, name: "", namePresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED, + isOutgoing: true, isEmergency: false, isConference: false, @@ -193,7 +359,8 @@ MobileConnectionListener.prototype = { notifyClirModeChanged: function(mode) {}, notifyLastKnownNetworkChanged: function() {}, notifyLastKnownHomeNetworkChanged: function() {}, - notifyNetworkSelectionModeChanged: function() {} + notifyNetworkSelectionModeChanged: function() {}, + notifyDeviceIdentitiesChanged: function() {} }; function TelephonyService() { @@ -203,8 +370,8 @@ function TelephonyService() { this._isDialing = false; this._cachedDialRequest = null; this._currentCalls = {}; - this._currentConferenceState = nsITelephonyService.CALL_STATE_UNKNOWN; this._audioStates = []; + this._ussdSessions = []; this._cdmaCallWaitingNumber = null; @@ -218,6 +385,7 @@ function TelephonyService() { for (let i = 0; i < this._numClients; ++i) { this._audioStates[i] = nsITelephonyAudioService.PHONE_STATE_NORMAL; + this._ussdSessions[i] = USSD_SESSION_DONE; this._currentCalls[i] = {}; this._enumerateCallsForClient(i); } @@ -240,6 +408,15 @@ TelephonyService.prototype = { _callRingWakeLock: null, _callRingWakeLockTimer: null, + /** + * USSD session flags. + * Only one USSD session may exist at a time, and the session is assumed + * to exist until: + * a) There's a call to cancelUSSD() + * b) Receiving a session end unsolicited event. + */ + _ussdSessions: null, + _acquireCallRingWakeLock: function() { if (!this._callRingWakeLock) { if (DEBUG) debug("Acquiring a CPU wake lock for handling incoming call."); @@ -395,10 +572,6 @@ TelephonyService.prototype = { } }, - _rulesToCallForwardingOptions: function(aRules) { - return aRules.map(rule => new MobileCallForwardingOptions(rule)); - }, - _updateDebugFlag: function() { try { DEBUG = RIL.DEBUG_RIL || @@ -443,7 +616,7 @@ TelephonyService.prototype = { * MMI full object. */ _isTemporaryCLIR: function(aMmi) { - return (aMmi && aMmi.serviceCode === RIL.MMI_SC_CLIR) && aMmi.dialNumber; + return (aMmi && aMmi.serviceCode === MMI_SC_CLIR) && aMmi.dialNumber; }, /** @@ -456,12 +629,12 @@ TelephonyService.prototype = { // In temporary mode, MMI_PROCEDURE_ACTIVATION means allowing CLI // presentation, i.e. CLIR_SUPPRESSION. See TS 22.030, Annex B. switch (aProcedure) { - case RIL.MMI_PROCEDURE_ACTIVATION: - return RIL.CLIR_SUPPRESSION; - case RIL.MMI_PROCEDURE_DEACTIVATION: - return RIL.CLIR_INVOCATION; + case MMI_PROCEDURE_ACTIVATION: + return Ci.nsIMobileConnection.CLIR_SUPPRESSION; + case MMI_PROCEDURE_DEACTIVATION: + return Ci.nsIMobileConnection.CLIR_INVOCATION; default: - return RIL.CLIR_DEFAULT; + return Ci.nsIMobileConnection.CLIR_DEFAULT; } }, @@ -560,18 +733,18 @@ TelephonyService.prototype = { // We don't try to be too clever here, as the phone is probably in the // locked state. Let's just check if it's a number without normalizing if (!aIsDialEmergency) { - aNumber = gPhoneNumberUtils.normalize(aNumber); + aNumber = PhoneNumberUtils.normalize(aNumber); } // Validate the number. // Note: isPlainPhoneNumber also accepts USSD and SS numbers - if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) { + if (!PhoneNumberUtils.isPlainPhoneNumber(aNumber)) { if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop."); aCallback.notifyError(DIAL_ERROR_BAD_NUMBER); return; } - let isEmergencyNumber = gDialNumberUtils.isEmergency(aNumber); + let isEmergencyNumber = DialNumberUtils.isEmergency(aNumber); // DialEmergency accepts only emergency number. if (aIsDialEmergency && !isEmergencyNumber) { @@ -597,7 +770,7 @@ TelephonyService.prototype = { return; } - let mmi = gDialNumberUtils.parseMMI(aNumber); + let mmi = DialNumberUtils.parseMMI(aNumber); if (mmi) { if (this._isTemporaryCLIR(mmi)) { this._dialCall(aClientId, mmi.dialNumber, @@ -625,27 +798,27 @@ TelephonyService.prototype = { // Handling of supplementary services within a call as 3GPP TS 22.030 6.5.5 _dialInCallMMI: function(aClientId, aNumber, aCallback) { let mmiCallback = { - notifyError: () => aCallback.notifyDialMMIError(RIL.MMI_ERROR_KS_ERROR), - notifySuccess: () => aCallback.notifyDialMMISuccess(RIL.MMI_SM_KS_CALL_CONTROL) + notifyError: () => aCallback.notifyDialMMIError(MMI_ERROR_KS_ERROR), + notifySuccess: () => aCallback.notifyDialMMISuccess(MMI_SM_KS_CALL_CONTROL) }; if (aNumber === "0") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._hangUpBackground(aClientId, mmiCallback); } else if (aNumber === "1") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._hangUpForeground(aClientId, mmiCallback); } else if (aNumber[0] === "1" && aNumber.length === 2) { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this.hangUpCall(aClientId, parseInt(aNumber[1]), mmiCallback); } else if (aNumber === "2") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._switchActiveCall(aClientId, mmiCallback); } else if (aNumber[0] === "2" && aNumber.length === 2) { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._separateCallGsm(aClientId, parseInt(aNumber[1]), mmiCallback); } else if (aNumber === "3") { - aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL); + aCallback.notifyDialMMI(MMI_KS_SC_CALL); this._conferenceCallGsm(aClientId, mmiCallback); } else { this._dialCall(aClientId, aNumber, undefined, aCallback); @@ -675,7 +848,7 @@ TelephonyService.prototype = { return; } - let isEmergency = gDialNumberUtils.isEmergency(aNumber); + let isEmergency = DialNumberUtils.isEmergency(aNumber); if (!isEmergency) { if (!this._isRadioOn(aClientId)) { @@ -775,7 +948,7 @@ TelephonyService.prototype = { _dialCdmaThreeWayCall: function(aClientId, aNumber, aCallback) { this._sendToRilWorker(aClientId, "cdmaFlash", { featureStr: aNumber }, response => { - if (!response.success) { + if (response.errorMsg) { aCallback.notifyError(response.errorMsg); return; } @@ -791,7 +964,7 @@ TelephonyService.prototype = { childCall.state = nsITelephonyService.CALL_STATE_DIALING; childCall.number = aNumber; childCall.isOutgoing = true; - childCall.isEmergency = gDialNumberUtils.isEmergency(aNumber); + childCall.isEmergency = DialNumberUtils.isEmergency(aNumber); childCall.isConference = false; childCall.isSwitchable = false; childCall.isMergeable = true; @@ -816,7 +989,7 @@ TelephonyService.prototype = { this._sendToRilWorker(aClientId, "dial", aOptions, response => { this._isDialing = false; - if (!response.success) { + if (response.errorMsg) { this._sendToRilWorker(aClientId, "getFailCause", null, response => { aCallback.notifyError(response.failCause); }); @@ -836,126 +1009,669 @@ TelephonyService.prototype = { * Parsed MMI structure. * @param aCallback * A nsITelephonyDialCallback object. - * @param aStartNewSession - * True to start a new session for ussd request. */ _dialMMI: function(aClientId, aMmi, aCallback) { let mmiServiceCode = aMmi ? - this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD; + this._serviceCodeToKeyString(aMmi.serviceCode) : MMI_KS_SC_USSD; aCallback.notifyDialMMI(mmiServiceCode); - this._sendToRilWorker(aClientId, "sendMMI", - { mmi: aMmi }, response => { - if (DEBUG) debug("MMI response: " + JSON.stringify(response)); + if (mmiServiceCode !== MMI_KS_SC_IMEI && !this._isRadioOn(aClientId)) { + aCallback.notifyDialMMIError(DIAL_ERROR_RADIO_NOT_AVAILABLE); + return; + } - if (!response.success) { - if (response.additionalInformation != null) { - aCallback.notifyDialMMIErrorWithInfo(response.errorMsg, - response.additionalInformation); - } else { - aCallback.notifyDialMMIError(response.errorMsg); - } - return; - } + // We check if the MMI service code is supported and in that case we + // trigger the appropriate RIL request if possible. + switch (mmiServiceCode) { + // Call Forwarding + case MMI_KS_SC_CALL_FORWARDING: + this._callForwardingMMI(aClientId, aMmi, aCallback); + break; - // We expect to have an IMEI at this point if the request was supposed - // to query for the IMEI, so getting a successful reply from the RIL - // without containing an actual IMEI number is considered an error. - if (mmiServiceCode === RIL.MMI_KS_SC_IMEI && - !response.statusMessage) { - aCallback.notifyDialMMIError(RIL.GECKO_ERROR_GENERIC_FAILURE); - return; - } + // Change the current ICC PIN number. + case MMI_KS_SC_PIN: + // Change the current ICC PIN2 number. + case MMI_KS_SC_PIN2: + this._iccChangeLockMMI(aClientId, aMmi, aCallback); + break; - // MMI query call forwarding options request returns a set of rules that - // will be exposed in the form of an array of MozCallForwardingOptions - // instances. - if (mmiServiceCode === RIL.MMI_KS_SC_CALL_FORWARDING) { - if (response.isSetCallForward) { - gGonkMobileConnectionService.notifyCFStateChanged(aClientId, - response.action, - response.reason, - response.number, - response.timeSeconds, - response.serviceClass); - } + // Unblock ICC PUK. + case MMI_KS_SC_PUK: + // Unblock ICC PUN2. + case MMI_KS_SC_PUK2: + this._iccUnlockMMI(aClientId, aMmi, aCallback); + break; - if (response.additionalInformation != null) { - let callForwardingOptions = - this._rulesToCallForwardingOptions(response.additionalInformation); - aCallback.notifyDialMMISuccessWithCallForwardingOptions( - response.statusMessage, callForwardingOptions.length, callForwardingOptions); + // IMEI + case MMI_KS_SC_IMEI: + this._getImeiMMI(aClientId, aMmi, aCallback); + break; + + // CLIP + case MMI_KS_SC_CLIP: + this._clipMMI(aClientId, aMmi, aCallback); + break; + + // CLIR (non-temporary ones) + case MMI_KS_SC_CLIR: + this._clirMMI(aClientId, aMmi, aCallback); + break; + + // Change call barring password + case MMI_KS_SC_CHANGE_PASSWORD: + this._callBarringPasswordMMI(aClientId, aMmi, aCallback); + break; + + // Call barring + case MMI_KS_SC_CALL_BARRING: + this._callBarringMMI(aClientId, aMmi, aCallback); + break; + + // Call waiting + case MMI_KS_SC_CALL_WAITING: + this._callWaitingMMI(aClientId, aMmi, aCallback); + break; + + // Handle unknown MMI code as USSD. + default: + if (this._ussdSessions[aClientId] == USSD_SESSION_ONGOING) { + // Cancel the previous ussd session first. + this._cancelUSSDInternal(aClientId, aResponse => { + // Fail to cancel ussd session, report error instead of sending ussd + // request. + if (aResponse.errorMsg) { + aCallback.notifyDialMMIError(aResponse.errorMsg); + return; + } + this._sendUSSDInternal(aClientId, aMmi.fullMMI, + this._defaultMMICallbackHandler.bind(this, aCallback)); + }); return; } - } + this._sendUSSDInternal(aClientId, aMmi.fullMMI, + this._defaultMMICallbackHandler.bind(this, aCallback)); + break; + } + }, - // No additional information - if (response.additionalInformation === undefined) { - aCallback.notifyDialMMISuccess(response.statusMessage); - return; - } + /** + * Handle call forwarding MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callForwardingMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + let action = MMI_PROC_TO_CF_ACTION[aMmi.procedure]; + let reason = MMI_SC_TO_CF_REASON[aMmi.serviceCode]; - // Additional information is an integer. - if (!isNaN(parseInt(response.additionalInformation, 10))) { - aCallback.notifyDialMMISuccessWithInteger( - response.statusMessage, response.additionalInformation); - return; - } + if (action === Ci.nsIMobileConnection.CALL_FORWARD_ACTION_QUERY_STATUS) { + connection.getCallForwarding(reason, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetCallForwardingSuccess: function(aCount, aResults) { + aCallback.notifyDialMMISuccessWithCallForwardingOptions( + MMI_SM_KS_SERVICE_INTERROGATED, aCount, aResults); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + } else { + let number = aMmi.sia; + let serviceClass = this._siToServiceClass(aMmi.sib); + let timeSeconds = aMmi.sic; + connection.setCallForwarding(action, reason, number, timeSeconds, + serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(CF_ACTION_TO_STATUS_MESSAGE[action]); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + } + }, - // Additional information is an array of strings. - let array = response.additionalInformation; - if (Array.isArray(array) && array.length > 0 && typeof array[0] === "string") { - aCallback.notifyDialMMISuccessWithStrings(response.statusMessage, - array.length, array); - return; - } + /** + * Handle icc change lock MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _iccChangeLockMMI: function(aClientId, aMmi, aCallback) { + let errorMsg = this._getIccLockMMIError(aMmi); + if (errorMsg) { + aCallback.notifyDialMMIError(errorMsg); + return; + } - aCallback.notifyDialMMISuccess(response.statusMessage); + let icc = gIccService.getIccByServiceId(aClientId); + let lockType = MMI_SC_TO_LOCK_TYPE[aMmi.serviceCode]; + + icc.changeCardLockPassword(lockType, aMmi.sia, aMmi.sib, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(LOCK_TYPE_TO_STATUS_MESSAGE[lockType]); + }, + notifyCardLockError: function(aErrorMsg, aRetryCount) { + if (aRetryCount <= 0) { + if (lockType === Ci.nsIIcc.CARD_LOCK_TYPE_PIN) { + aErrorMsg = MMI_ERROR_KS_NEEDS_PUK; + } + + aCallback.notifyDialMMIError(aErrorMsg); + return; + } + + aCallback.notifyDialMMIErrorWithInfo(MMI_ERROR_KS_BAD_PIN, + aRetryCount); + }, }); }, + /** + * Handle icc unlock lock MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _iccUnlockMMI: function(aClientId, aMmi, aCallback) { + let errorMsg = this._getIccLockMMIError(aMmi); + if (errorMsg) { + aCallback.notifyDialMMIError(errorMsg); + return; + } + + let icc = gIccService.getIccByServiceId(aClientId); + let lockType = MMI_SC_TO_LOCK_TYPE[aMmi.serviceCode]; + + icc.unlockCardLock(lockType, aMmi.sia, aMmi.sib, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(LOCK_TYPE_TO_STATUS_MESSAGE[lockType]); + }, + notifyCardLockError: function(aErrorMsg, aRetryCount) { + if (aRetryCount <= 0) { + if (lockType === Ci.nsIIcc.CARD_LOCK_TYPE_PUK) { + aErrorMsg = MMI_ERROR_KS_SIM_BLOCKED; + } + + aCallback.notifyDialMMIError(aErrorMsg); + return; + } + + aCallback.notifyDialMMIErrorWithInfo(MMI_ERROR_KS_BAD_PUK, + aRetryCount); + }, + }); + }, + + /** + * Handle IMEI MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _getImeiMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + if (!connection.deviceIdentities || !connection.deviceIdentities.imei) { + aCallback.notifyDialMMIError(RIL.GECKO_ERROR_GENERIC_FAILURE); + return; + } + + aCallback.notifyDialMMISuccess(connection.deviceIdentities.imei); + }, + + /** + * Handle CLIP MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _clipMMI: function(aClientId, aMmi, aCallback) { + if (aMmi.procedure !== MMI_PROCEDURE_INTERROGATION) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + return; + } + + this._sendToRilWorker(aClientId, "queryCLIP", {}, aResponse => { + if (aResponse.errorMsg) { + aCallback.notifyDialMMIError(aResponse.errorMsg); + return; + } + + // aResponse.provisioned informs about the called party receives the + // calling party's address information: + // 0 for CLIP not provisioned + // 1 for CLIP provisioned + // 2 for unknown + switch (aResponse.provisioned) { + case 0: + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_DISABLED); + break; + case 1: + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_ENABLED); + break; + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_ERROR); + break; + } + }); + }, + + /** + * Handle CLIR MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _clirMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + switch (aMmi.procedure) { + case MMI_PROCEDURE_INTERROGATION: + connection.getCallingLineIdRestriction({ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetClirStatusSuccess: function(aN, aM) { + let errorMsg; + let statusMessage; + // TS 27.007 +CLIR parameter 'm'. + switch (aM) { + // CLIR not provisioned. + case 0: + statusMessage = MMI_SM_KS_SERVICE_NOT_PROVISIONED; + break; + // CLIR provisioned in permanent mode. + case 1: + statusMessage = MMI_SM_KS_CLIR_PERMANENT; + break; + // Unknown (e.g. no network, etc.). + case 2: + errorMsg = MMI_ERROR_KS_ERROR; + break; + // CLIR temporary mode presentation restricted. + case 3: + // TS 27.007 +CLIR parameter 'n'. + switch (aN) { + // Default. + case 0: + // CLIR invocation. + case 1: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_ON; + break; + // CLIR suppression. + case 2: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_ON_NEXT_CALL_OFF; + break; + default: + errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; + break; + } + break; + // CLIR temporary mode presentation allowed. + case 4: + // TS 27.007 +CLIR parameter 'n'. + switch (aN) { + // Default. + case 0: + // CLIR suppression. + case 2: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_OFF; + break; + // CLIR invocation. + case 1: + statusMessage = MMI_SM_KS_CLIR_DEFAULT_OFF_NEXT_CALL_ON; + break; + default: + errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; + break; + } + break; + default: + errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; + break; + } + + if (errorMsg) { + aCallback.notifyDialMMIError(errorMsg); + return; + } + + aCallback.notifyDialMMISuccess(statusMessage); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + case MMI_PROCEDURE_ACTIVATION: + case MMI_PROCEDURE_DEACTIVATION: { + let clirMode = MMI_PROC_TO_CLIR_ACTION[aMmi.procedure]; + connection.setCallingLineIdRestriction(clirMode, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(CLIR_ACTION_TO_STATUS_MESSAGE[clirMode]); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + } + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + break; + } + }, + + /** + * Handle change call barring password MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callBarringPasswordMMI: function(aClientId, aMmi, aCallback) { + if (aMmi.procedure !== MMI_PROCEDURE_REGISTRATION && + aMmi.procedure !== MMI_PROCEDURE_ACTIVATION) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_INVALID_ACTION); + return; + } + + if (aMmi.sia !== "" && aMmi.sia !== MMI_ZZ_BARRING_SERVICE) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + return; + } + + let validPassword = aSi => /^[0-9]{4}$/.test(aSi); + if (!validPassword(aMmi.sib) || !validPassword(aMmi.sic) || + !validPassword(aMmi.pwd)) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_INVALID_PASSWORD); + return; + } + + if (aMmi.sic !== aMmi.pwd) { + aCallback.notifyDialMMIError(MMI_ERROR_KS_MISMATCH_PASSWORD); + return; + } + + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + connection.changeCallBarringPassword(aMmi.sib, aMmi.sic, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess(MMI_SM_KS_PASSWORD_CHANGED); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + }, + + /** + * Handle call barring MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callBarringMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + let program = MMI_SC_TO_CB_PROGRAM[aMmi.serviceCode]; + let password = aMmi.sia || ""; + let serviceClass = this._siToServiceClass(aMmi.sib); + + switch (aMmi.procedure) { + case MMI_PROCEDURE_INTERROGATION: + connection.getCallBarring(program, password, serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetCallBarringSuccess: function(aProgram, aEnabled, aServiceClass) { + if (!aEnabled) { + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_DISABLED); + return; + } + + let services = this._serviceClassToStringArray(aServiceClass); + aCallback.notifyDialMMISuccessWithStrings(MMI_SM_KS_SERVICE_ENABLED_FOR, + services.length, services); + }.bind(this), + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + case MMI_PROCEDURE_ACTIVATION: + case MMI_PROCEDURE_DEACTIVATION: { + let enabled = (aMmi.procedure === MMI_PROCEDURE_ACTIVATION); + connection.setCallBarring(program, enabled, password, serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess( + enabled ? MMI_SM_KS_SERVICE_ENABLED + : MMI_SM_KS_SERVICE_DISABLED + ); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + } + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + break; + } + }, + + /** + * Handle call waiting MMI code. + * + * @param aClientId + * Client id. + * @param aMmi + * Parsed MMI structure. + * @param aCallback + * A nsITelephonyDialCallback object. + */ + _callWaitingMMI: function(aClientId, aMmi, aCallback) { + let connection = gGonkMobileConnectionService.getItemByServiceId(aClientId); + + switch (aMmi.procedure) { + case MMI_PROCEDURE_INTERROGATION: + connection.getCallWaiting({ + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifyGetCallWaitingSuccess: function(aServiceClass) { + if (aServiceClass === Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE) { + aCallback.notifyDialMMISuccess(MMI_SM_KS_SERVICE_DISABLED); + return; + } + + let services = this._serviceClassToStringArray(aServiceClass); + aCallback.notifyDialMMISuccessWithStrings(MMI_SM_KS_SERVICE_ENABLED_FOR, + services.length, services); + }.bind(this), + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + case MMI_PROCEDURE_ACTIVATION: + case MMI_PROCEDURE_DEACTIVATION: { + let enabled = (aMmi.procedure === MMI_PROCEDURE_ACTIVATION); + let serviceClass = this._siToServiceClass(aMmi.sia); + connection.setCallWaiting(enabled, serviceClass, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionCallback]), + notifySuccess: function() { + aCallback.notifyDialMMISuccess( + enabled ? MMI_SM_KS_SERVICE_ENABLED + : MMI_SM_KS_SERVICE_DISABLED + ); + }, + notifyError: function(aErrorMsg) { + aCallback.notifyDialMMIError(aErrorMsg); + }, + }); + break; + } + default: + aCallback.notifyDialMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + break; + } + }, + _serviceCodeToKeyString: function(aServiceCode) { switch (aServiceCode) { - case RIL.MMI_SC_CFU: - case RIL.MMI_SC_CF_BUSY: - case RIL.MMI_SC_CF_NO_REPLY: - case RIL.MMI_SC_CF_NOT_REACHABLE: - case RIL.MMI_SC_CF_ALL: - case RIL.MMI_SC_CF_ALL_CONDITIONAL: - return RIL.MMI_KS_SC_CALL_FORWARDING; - case RIL.MMI_SC_PIN: - return RIL.MMI_KS_SC_PIN; - case RIL.MMI_SC_PIN2: - return RIL.MMI_KS_SC_PIN2; - case RIL.MMI_SC_PUK: - return RIL.MMI_KS_SC_PUK; - case RIL.MMI_SC_PUK2: - return RIL.MMI_KS_SC_PUK2; - case RIL.MMI_SC_IMEI: - return RIL.MMI_KS_SC_IMEI; - case RIL.MMI_SC_CLIP: - return RIL.MMI_KS_SC_CLIP; - case RIL.MMI_SC_CLIR: - return RIL.MMI_KS_SC_CLIR; - case RIL.MMI_SC_BAOC: - case RIL.MMI_SC_BAOIC: - case RIL.MMI_SC_BAOICxH: - case RIL.MMI_SC_BAIC: - case RIL.MMI_SC_BAICr: - case RIL.MMI_SC_BA_ALL: - case RIL.MMI_SC_BA_MO: - case RIL.MMI_SC_BA_MT: - return RIL.MMI_KS_SC_CALL_BARRING; - case RIL.MMI_SC_CALL_WAITING: - return RIL.MMI_KS_SC_CALL_WAITING; - case RIL.MMI_SC_CHANGE_PASSWORD: - return RIL.MMI_KS_SC_CHANGE_PASSWORD; + case MMI_SC_CFU: + case MMI_SC_CF_BUSY: + case MMI_SC_CF_NO_REPLY: + case MMI_SC_CF_NOT_REACHABLE: + case MMI_SC_CF_ALL: + case MMI_SC_CF_ALL_CONDITIONAL: + return MMI_KS_SC_CALL_FORWARDING; + case MMI_SC_PIN: + return MMI_KS_SC_PIN; + case MMI_SC_PIN2: + return MMI_KS_SC_PIN2; + case MMI_SC_PUK: + return MMI_KS_SC_PUK; + case MMI_SC_PUK2: + return MMI_KS_SC_PUK2; + case MMI_SC_IMEI: + return MMI_KS_SC_IMEI; + case MMI_SC_CLIP: + return MMI_KS_SC_CLIP; + case MMI_SC_CLIR: + return MMI_KS_SC_CLIR; + case MMI_SC_BAOC: + case MMI_SC_BAOIC: + case MMI_SC_BAOICxH: + case MMI_SC_BAIC: + case MMI_SC_BAICr: + case MMI_SC_BA_ALL: + case MMI_SC_BA_MO: + case MMI_SC_BA_MT: + return MMI_KS_SC_CALL_BARRING; + case MMI_SC_CALL_WAITING: + return MMI_KS_SC_CALL_WAITING; + case MMI_SC_CHANGE_PASSWORD: + return MMI_KS_SC_CHANGE_PASSWORD; default: - return RIL.MMI_KS_SC_USSD; + return MMI_KS_SC_USSD; } }, + /** + * Helper for translating basic service group to service class parameter. + */ + _siToServiceClass: function(aSi) { + if (!aSi) { + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE; + } + + let serviceCode = parseInt(aSi, 10); + switch (serviceCode) { + case 10: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 11: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 12: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX; + case 13: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX; + case 16: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_SMS; + case 19: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_FAX + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 21: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PAD + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_ASYNC; + case 22: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PACKET + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_SYNC; + case 25: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_ASYNC; + case 26: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_DATA_SYNC + + Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + case 99: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_PACKET; + default: + return Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE; + } + }, + + _serviceClassToStringArray: function(aServiceClass) { + let services = []; + for (let mask = Ci.nsIMobileConnection.ICC_SERVICE_CLASS_VOICE; + mask <= Ci.nsIMobileConnection.ICC_SERVICE_CLASS_MAX; + mask <<= 1) { + if (mask & aServiceClass) { + services.push(MMI_KS_SERVICE_CLASS_MAPPING[mask]); + } + } + return services; + }, + + _getIccLockMMIError: function(aMmi) { + // As defined in TS.122.030 6.6.2 to change the ICC PIN we should expect + // an MMI code of the form **04*OLD_PIN*NEW_PIN*NEW_PIN#, where old PIN + // should be entered as the SIA parameter and the new PIN as SIB and + // SIC. + if (aMmi.procedure !== MMI_PROCEDURE_REGISTRATION) { + return MMI_ERROR_KS_INVALID_ACTION; + } + + if (!aMmi.sia || !aMmi.sib || !aMmi.sic) { + return MMI_ERROR_KS_ERROR; + } + + if (aMmi.sia.length < 4 || aMmi.sia.length > 8 || + aMmi.sib.length < 4 || aMmi.sib.length > 8 || + aMmi.sic.length < 4 || aMmi.sic.length > 8) { + return MMI_ERROR_KS_INVALID_PIN; + } + + if (aMmi.sib != aMmi.sic) { + return MMI_ERROR_KS_MISMATCH_PIN; + } + + return null; + }, + /** * The default callback handler for call operations. * @@ -965,13 +1681,21 @@ TelephonyService.prototype = { * The response from ril_worker. */ _defaultCallbackHandler: function(aCallback, aResponse) { - if (!aResponse.success) { + if (aResponse.errorMsg) { aCallback.notifyError(aResponse.errorMsg); } else { aCallback.notifySuccess(); } }, + _defaultMMICallbackHandler: function(aCallback, aResponse) { + if (aResponse.errorMsg) { + aCallback.notifyDialMMIError(aResponse.errorMsg); + } else { + aCallback.notifyDialMMISuccess(""); + } + }, + _getCallsWithState: function(aClientId, aState) { let calls = []; for (let i in this._currentCalls[aClientId]) { @@ -1005,7 +1729,7 @@ TelephonyService.prototype = { } aCall.isOutgoing = !aRilCall.isMT; - aCall.isEmergency = gDialNumberUtils.isEmergency(aCall.number); + aCall.isEmergency = DialNumberUtils.isEmergency(aCall.number); if (!aCall.started && aCall.state == nsITelephonyService.CALL_STATE_CONNECTED) { @@ -1072,7 +1796,7 @@ TelephonyService.prototype = { let tones = aDtmfChars; let playTone = (tone) => { this._sendToRilWorker(aClientId, "startTone", { dtmfChar: tone }, response => { - if (!response.success) { + if (response.errorMsg) { aCallback.notifyError(response.errorMsg); return; } @@ -1177,6 +1901,41 @@ TelephonyService.prototype = { return; } + // After hangup a single call, gecko has to resume the held call or conference. + if (!call.isConference) { + let heldCalls = this._getCallsWithState(aClientId, nsITelephonyService.CALL_STATE_HELD); + + if (heldCalls.length) { + if (call.state === nsITelephonyService.CALL_STATE_CONNECTED) { + // For a foreground call, ril has a request to do two actions together. + this._hangUpForeground(aClientId, aCallback); + } else { + // Otherwise, gecko should send out two consecutive requests by itself. + this._sendToRilWorker(aClientId, "hangUpCall", { callIndex: aCallIndex }, response => { + if (response.errorMsg) { + aCallback.notifyError(response.errorMsg); + } else { + aCallback.notifySuccess(); + + let emptyCallback = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallback]), + notifySuccess: () => {}, + notifyError: () => {} + }; + + if (heldCalls.length === 1) { + this.resumeCall(aClientId, heldCalls[0].callIndex, emptyCallback); + } else { + this.resumeConference(aClientId, emptyCallback); + } + } + }); + } + + return; + } + } + call.hangUpLocal = true; this._sendToRilWorker(aClientId, "hangUpCall", { callIndex: aCallIndex }, this._defaultCallbackHandler.bind(this, aCallback)); @@ -1234,7 +1993,7 @@ TelephonyService.prototype = { _conferenceCallGsm: function(aClientId, aCallback) { this._sendToRilWorker(aClientId, "conferenceCall", null, response => { - if (!response.success) { + if (response.errorMsg) { aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE); // TODO: Bug 1124993. Deprecate it. Use callback response is enough. this._notifyAllListeners("notifyConferenceError", @@ -1256,7 +2015,7 @@ TelephonyService.prototype = { } this._sendToRilWorker(aClientId, "cdmaFlash", null, response => { - if (!response.success) { + if (response.errorMsg) { aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE); // TODO: Bug 1124993. Deprecate it. Use callback response is enough. this._notifyAllListeners("notifyConferenceError", @@ -1272,7 +2031,6 @@ TelephonyService.prototype = { calls.push(call); } this._handleCallStateChanged(aClientId, calls); - this._handleConferenceCallStateChanged(nsITelephonyService.CALL_STATE_CONNECTED); aCallback.notifySuccess(); }); @@ -1294,7 +2052,7 @@ TelephonyService.prototype = { _separateCallGsm: function(aClientId, aCallIndex, aCallback) { this._sendToRilWorker(aClientId, "separateCall", { callIndex: aCallIndex }, response => { - if (!response.success) { + if (response.errorMsg) { aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE); // TODO: Bug 1124993. Deprecate it. Use callback response is enough. this._notifyAllListeners("notifyConferenceError", @@ -1316,7 +2074,6 @@ TelephonyService.prototype = { parentCall.isSwitchable = true; parentCall.isMergeable = true; this._handleCallStateChanged(aClientId, [childCall, parentCall]); - this._handleConferenceCallStateChanged(nsITelephonyService.CALL_STATE_UNKNOWN); }, // See 3gpp2, S.R0006-522-A v1.0. Table 4, XID 6S. @@ -1324,7 +2081,7 @@ TelephonyService.prototype = { // controlling subscriber and the second party. Go to the 2-way state. _separateCallCdma: function(aClientId, aCallIndex, aCallback) { this._sendToRilWorker(aClientId, "cdmaFlash", null, response => { - if (!response.success) { + if (response.errorMsg) { aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE); // TODO: Bug 1124993. Deprecate it. Use callback response is enough. this._notifyAllListeners("notifyConferenceError", @@ -1360,11 +2117,24 @@ TelephonyService.prototype = { return; } - let foreground = this._currentConferenceState == nsITelephonyService.CALL_STATE_CONNECTED; - this._sendToRilWorker(aClientId, - foreground ? "hangUpForeground" : "hangUpBackground", - null, - this._defaultCallbackHandler.bind(this, aCallback)); + // Find a conference call, and send the corresponding request to RIL worker. + for (let index in this._currentCalls[aClientId]) { + let call = this._currentCalls[aClientId][index]; + if (!call.isConference) { + continue; + } + + let command = call.state === nsITelephonyService.CALL_STATE_CONNECTED ? + "hangUpForeground" : "hangUpBackground"; + this._sendToRilWorker(aClientId, command, null, + this._defaultCallbackHandler.bind(this, aCallback)); + return; + } + + // There is no conference call. + if (DEBUG) debug("hangUpConference: " + + "No conference call in modem[" + aClientId + "]."); + aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE); }, _switchConference: function(aClientId, aCallback) { @@ -1386,13 +2156,33 @@ TelephonyService.prototype = { }, sendUSSD: function(aClientId, aUssd, aCallback) { - this._sendToRilWorker(aClientId, "sendUSSD", { ussd: aUssd }, - this._defaultCallbackHandler.bind(this, aCallback)); + this._sendUSSDInternal(aClientId, aUssd, + this._defaultCallbackHandler.bind(this, aCallback)); + }, + + _sendUSSDInternal: function(aClientId, aUssd, aCallback) { + this._ussdSessions[aClientId] = USSD_SESSION_ONGOING; + this._sendToRilWorker(aClientId, "sendUSSD", { ussd: aUssd }, aResponse => { + if (aResponse.errorMsg) { + this._ussdSessions[aClientId] = USSD_SESSION_DONE; + } + aCallback(aResponse); + }); }, cancelUSSD: function(aClientId, aCallback) { - this._sendToRilWorker(aClientId, "cancelUSSD", {}, - this._defaultCallbackHandler.bind(this, aCallback)); + this._cancelUSSDInternal(aClientId, + this._defaultCallbackHandler.bind(this, aCallback)); + }, + + _cancelUSSDInternal: function(aClientId, aCallback) { + this._ussdSessions[aClientId] = USSD_SESSION_CANCELLING; + this._sendToRilWorker(aClientId, "cancelUSSD", {}, aResponse => { + if (aResponse.errorMsg) { + this._ussdSessions[aClientId] = USSD_SESSION_ONGOING; + } + aCallback(aResponse); + }); }, get microphoneMuted() { @@ -1436,19 +2226,14 @@ TelephonyService.prototype = { * calls being disconnected as well. * * @return Array a list of calls we need to fire callStateChange - * - * TODO: The list currently doesn't contain calls that we fire notifyError - * for them. However, after Bug 1147736, notifyError is replaced by - * callStateChanged and those calls should be included in the list. */ _disconnectCalls: function(aClientId, aCalls, aFailCause = RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) { if (DEBUG) debug("_disconnectCalls: " + JSON.stringify(aCalls)); - // Child cannot live without parent. Let's find all the calls that need to - // be disconnected. + // In addition to the disconnected call itself, its decedent calls should be + // treated as disconnected calls as well. let disconnectedCalls = aCalls.slice(); - for (let call in aCalls) { while (call.childId) { call = this._currentCalls[aClientId][call.childId]; @@ -1457,28 +2242,22 @@ TelephonyService.prototype = { } // Store unique value in the list. - disconnectedCalls = [...Set(disconnectedCalls)]; + disconnectedCalls = [...new Set(disconnectedCalls)]; let callsForStateChanged = []; disconnectedCalls.forEach(call => { call.state = nsITelephonyService.CALL_STATE_DISCONNECTED; - call.failCause = aFailCause; + call.disconnectedReason = aFailCause; - if (call.parentId) { - let parentCall = this._currentCalls[aClientId][call.parentId]; + let parentCall = this._currentCalls[aClientId][call.parentId]; + if (parentCall) { delete parentCall.childId; } this._notifyCallEnded(call); - if (call.hangUpLocal || !call.failCause || - call.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) { - callsForStateChanged.push(call); - } else { - this._notifyAllListeners("notifyError", - [aClientId, call.callIndex, call.failCause]); - } + callsForStateChanged.push(call); delete this._currentCalls[aClientId][call.callIndex]; }); @@ -1577,12 +2356,6 @@ TelephonyService.prototype = { this._handleCallStateChanged(aClientId, [...changedCalls]); - // Should handle conferenceCallStateChange after callStateChanged and - // callDisconnected. - if (newConferenceState != this._currentConferenceState) { - this._handleConferenceCallStateChanged(newConferenceState); - } - this._updateAudioState(aClientId); // Handle cached dial request. @@ -1602,6 +2375,10 @@ TelephonyService.prototype = { _handleCallStateChanged: function(aClientId, aCalls) { if (DEBUG) debug("handleCallStateChanged: " + JSON.stringify(aCalls)); + if (aCalls.length === 0) { + return; + } + if (aCalls.some(call => call.state == nsITelephonyService.CALL_STATE_DIALING)) { gTelephonyMessenger.notifyNewCall(); } @@ -1658,18 +2435,33 @@ TelephonyService.prototype = { [aClientId, callIndex, notification]); }, - _handleConferenceCallStateChanged: function(aState) { - if (DEBUG) debug("handleConferenceCallStateChanged: " + aState); - this._currentConferenceState = aState; - this._notifyAllListeners("conferenceCallStateChanged", [aState]); - }, - notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) { if (DEBUG) { debug("notifyUssdReceived for " + aClientId + ": " + aMessage + " (sessionEnded : " + aSessionEnded + ")"); } + let oldSession = this._ussdSessions[aClientId]; + this._ussdSessions[aClientId] = + aSessionEnded ? USSD_SESSION_DONE : USSD_SESSION_ONGOING; + + // We suppress the empty message only when the session is not changed and + // is not alive. See Bug 1057455, 1198676. + // Moreover, we should allow a notification initiated by network + // in which further response is not required. + // See |5.2.2 Actions at the UE| in 3GPP TS 22.090: + // " + // The network may explicitly indicate to the UE that a response + // from the user is required. ... + // If the network does not indicate that a response is required, + // then the normal MMI procedures on the UE continue to apply. + // " + if (oldSession != USSD_SESSION_ONGOING && + this._ussdSessions[aClientId] != USSD_SESSION_ONGOING && + !aMessage) { + return; + } + gTelephonyMessenger.notifyUssdReceived(aClientId, aMessage, aSessionEnded); }, @@ -1697,37 +2489,4 @@ TelephonyService.prototype = { } }; -/** - * This implements nsISystemMessagesWrapper.wrapMessage(), which provides a - * plugable way to wrap a "ussd-received" type system message. - * - * Please see SystemMessageManager.js to know how it customizes the wrapper. - */ -function USSDReceivedWrapper() { - if (DEBUG) debug("USSDReceivedWrapper()"); -} -USSDReceivedWrapper.prototype = { - // nsISystemMessagesWrapper implementation. - wrapMessage: function(aMessage, aWindow) { - if (DEBUG) debug("wrapMessage: " + JSON.stringify(aMessage)); - - let session = aMessage.sessionEnded ? null : - new aWindow.USSDSession(aMessage.serviceId); - - let event = new aWindow.USSDReceivedEvent("ussdreceived", { - serviceId: aMessage.serviceId, - message: aMessage.message, - session: session - }); - - return event; - }, - - classDescription: "USSDReceivedWrapper", - classID: Components.ID("{d03684ed-ede4-4210-8206-f4f32772d9f5}"), - contractID: "@mozilla.org/dom/system-messages/wrapper/ussd-received;1", - QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyService, - USSDReceivedWrapper]); +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyService]); diff --git a/dom/telephony/gonk/TelephonyService.manifest b/dom/telephony/gonk/TelephonyService.manifest index 28c8d8633f..8a15cdf8dc 100644 --- a/dom/telephony/gonk/TelephonyService.manifest +++ b/dom/telephony/gonk/TelephonyService.manifest @@ -1,5 +1,3 @@ component {67d26434-d063-4d28-9f48-5b3189788155} TelephonyService.js contract @mozilla.org/telephony/gonktelephonyservice;1 {67d26434-d063-4d28-9f48-5b3189788155} -component {d03684ed-ede4-4210-8206-f4f32772d9f5} TelephonyService.js -contract @mozilla.org/dom/system-messages/wrapper/ussd-received;1 {d03684ed-ede4-4210-8206-f4f32772d9f5} diff --git a/dom/telephony/gonk/TelephonyUtils.jsm b/dom/telephony/gonk/TelephonyUtils.jsm new file mode 100644 index 0000000000..4406656ba4 --- /dev/null +++ b/dom/telephony/gonk/TelephonyUtils.jsm @@ -0,0 +1,109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["TelephonyUtils"]; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); + +/* global TelephonyService */ +XPCOMUtils.defineLazyServiceGetter(this, + "TelephonyService", + "@mozilla.org/telephony/telephonyservice;1", + "nsITelephonyService"); + +function getCurrentCalls(aFilter) { + if (aFilter === undefined) { + aFilter = call => true; + } + + let calls = []; + + // nsITelephonyService.enumerateCalls is synchronous. + TelephonyService.enumerateCalls({ + QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyListener]), + enumerateCallStateComplete: function() {}, + enumerateCallState: function(call) { + if (aFilter(call)) { + calls.push(call); + } + }, + }); + + return calls; +} + +this.TelephonyUtils = { + /** + * Check whether there are any calls. + * + * @param aClientId [optional] If provided, only check on aClientId + * @return boolean + */ + hasAnyCalls: function(aClientId) { + let calls = getCurrentCalls(call => { + if (aClientId !== undefined && call.clientId !== aClientId) { + return false; + } + return true; + }); + + return calls.length !== 0; + }, + + /** + * Check whether there are any connected calls. + * + * @param aClientId [optional] If provided, only check on aClientId + * @return boolean + */ + hasConnectedCalls: function(aClientId) { + let calls = getCurrentCalls(call => { + if (aClientId !== undefined && call.clientId !== aClientId) { + return false; + } + return call.callState === Ci.nsITelephonyService.CALL_STATE_CONNECTED; + }); + + return calls.length !== 0; + }, + + /** + * Return a promise which will be resolved when there are no calls. + * + * @param aClientId [optional] only check on aClientId if provided + * @return Promise + */ + waitForNoCalls: function(aClientId) { + if (!this.hasAnyCalls(aClientId)) { + return Promise.resolve(); + } + + let self = this; + return new Promise(resolve => { + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyListener]), + + enumerateCallStateComplete: function() {}, + enumerateCallState: function() {}, + callStateChanged: function() { + if (!self.hasAnyCalls(aClientId)) { + TelephonyService.unregisterListener(this); + resolve(); + } + }, + supplementaryServiceNotification: function() {}, + notifyError: function() {}, + notifyCdmaCallWaiting: function() {}, + notifyConferenceError: function() {} + }; + + TelephonyService.registerListener(listener); + }); + } +}; diff --git a/dom/telephony/gonk/USSDReceivedWrapper.js b/dom/telephony/gonk/USSDReceivedWrapper.js new file mode 100644 index 0000000000..54ead325f3 --- /dev/null +++ b/dom/telephony/gonk/USSDReceivedWrapper.js @@ -0,0 +1,82 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; +const kPrefRilDebuggingEnabled = "ril.debugging.enabled"; + +let DEBUG; +function debug(s) { + dump("USSDReceivedWrapper: " + s + "\n"); +} + +XPCOMUtils.defineLazyGetter(this, "RIL", function () { + let obj = {}; + Cu.import("resource://gre/modules/ril_consts.js", obj); + return obj; +}); + +/** + * This implements nsISystemMessagesWrapper.wrapMessage(), which provides a + * plugable way to wrap a "ussd-received" type system message. + * + * Please see SystemMessageManager.js to know how it customizes the wrapper. + */ +function USSDReceivedWrapper() { + this._updateDebugFlag(); + Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false); + if (DEBUG) debug("USSDReceivedWrapper()"); +} +USSDReceivedWrapper.prototype = { + _updateDebugFlag: function() { + try { + DEBUG = RIL.DEBUG_RIL || + Services.prefs.getBoolPref(kPrefRilDebuggingEnabled); + } catch (e) {} + }, + + /** + * nsIObserver interface. + */ + observe: function(aSubject, aTopic, aData) { + switch (aTopic) { + case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID: + if (aData === kPrefRilDebuggingEnabled) { + this._updateDebugFlag(); + } + break; + } + }, + + // nsISystemMessagesWrapper implementation. + wrapMessage: function(aMessage, aWindow) { + if (DEBUG) debug("wrapMessage: " + JSON.stringify(aMessage)); + + let session = aMessage.sessionEnded ? null : + new aWindow.USSDSession(aMessage.serviceId); + + let event = new aWindow.USSDReceivedEvent("ussdreceived", { + serviceId: aMessage.serviceId, + message: aMessage.message, + session: session + }); + + return event; + }, + + classDescription: "USSDReceivedWrapper", + classID: Components.ID("{d03684ed-ede4-4210-8206-f4f32772d9f5}"), + contractID: "@mozilla.org/dom/system-messages/wrapper/ussd-received;1", + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, + Ci.nsISystemMessagesWrapper]) +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([USSDReceivedWrapper]); diff --git a/dom/telephony/gonk/USSDReceivedWrapper.manifest b/dom/telephony/gonk/USSDReceivedWrapper.manifest new file mode 100644 index 0000000000..039498828c --- /dev/null +++ b/dom/telephony/gonk/USSDReceivedWrapper.manifest @@ -0,0 +1,2 @@ +component {d03684ed-ede4-4210-8206-f4f32772d9f5} USSDReceivedWrapper.js +contract @mozilla.org/dom/system-messages/wrapper/ussd-received;1 {d03684ed-ede4-4210-8206-f4f32772d9f5} diff --git a/dom/telephony/ipc/PTelephony.ipdl b/dom/telephony/ipc/PTelephony.ipdl index 60a83537f5..57f559df1a 100644 --- a/dom/telephony/ipc/PTelephony.ipdl +++ b/dom/telephony/ipc/PTelephony.ipdl @@ -125,14 +125,10 @@ sync protocol PTelephony { manages PTelephonyRequest; child: - NotifyCallError(uint32_t aClientId, int32_t aCallIndex, nsString aError); - NotifyCallStateChanged(nsTelephonyCallInfo[] aAllInfo); NotifyCdmaCallWaiting(uint32_t aClientId, IPCCdmaWaitingCallData aData); - NotifyConferenceCallStateChanged(uint16_t aCallState); - NotifyConferenceError(nsString aName, nsString aMessage); NotifySupplementaryService(uint32_t aClientId, int32_t aCallIndex, diff --git a/dom/telephony/ipc/TelephonyChild.cpp b/dom/telephony/ipc/TelephonyChild.cpp index ecbe0b0d48..593387f6e5 100644 --- a/dom/telephony/ipc/TelephonyChild.cpp +++ b/dom/telephony/ipc/TelephonyChild.cpp @@ -47,17 +47,6 @@ TelephonyChild::DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor) return true; } -bool -TelephonyChild::RecvNotifyCallError(const uint32_t& aClientId, - const int32_t& aCallIndex, - const nsString& aError) -{ - MOZ_ASSERT(mService); - - mService->NotifyError(aClientId, aCallIndex, aError); - return true; -} - bool TelephonyChild::RecvNotifyCallStateChanged(nsTArray&& aAllInfo) { @@ -91,15 +80,6 @@ TelephonyChild::RecvNotifyCdmaCallWaiting(const uint32_t& aClientId, return true; } -bool -TelephonyChild::RecvNotifyConferenceCallStateChanged(const uint16_t& aCallState) -{ - MOZ_ASSERT(mService); - - mService->ConferenceCallStateChanged(aCallState); - return true; -} - bool TelephonyChild::RecvNotifyConferenceError(const nsString& aName, const nsString& aMessage) diff --git a/dom/telephony/ipc/TelephonyChild.h b/dom/telephony/ipc/TelephonyChild.h index d58260c862..e26bb310de 100644 --- a/dom/telephony/ipc/TelephonyChild.h +++ b/dom/telephony/ipc/TelephonyChild.h @@ -34,10 +34,6 @@ protected: virtual bool DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor) override; - virtual bool - RecvNotifyCallError(const uint32_t& aClientId, const int32_t& aCallIndex, - const nsString& aError) override; - virtual bool RecvNotifyCallStateChanged(nsTArray&& aAllInfo) override; @@ -45,9 +41,6 @@ protected: RecvNotifyCdmaCallWaiting(const uint32_t& aClientId, const IPCCdmaWaitingCallData& aData) override; - virtual bool - RecvNotifyConferenceCallStateChanged(const uint16_t& aCallState) override; - virtual bool RecvNotifyConferenceError(const nsString& aName, const nsString& aMessage) override; diff --git a/dom/telephony/ipc/TelephonyIPCSerializer.h b/dom/telephony/ipc/TelephonyIPCSerializer.h index 54a439aa2f..a0f22d7418 100644 --- a/dom/telephony/ipc/TelephonyIPCSerializer.h +++ b/dom/telephony/ipc/TelephonyIPCSerializer.h @@ -37,10 +37,13 @@ struct ParamTraits uint32_t clientId; uint32_t callIndex; uint16_t callState; + nsString disconnectedReason; + nsString number; uint16_t numberPresentation; nsString name; uint16_t namePresentation; + bool isOutgoing; bool isEmergency; bool isConference; @@ -50,10 +53,13 @@ struct ParamTraits aParam->GetClientId(&clientId); aParam->GetCallIndex(&callIndex); aParam->GetCallState(&callState); + aParam->GetDisconnectedReason(disconnectedReason); + aParam->GetNumber(number); aParam->GetNumberPresentation(&numberPresentation); aParam->GetName(name); aParam->GetNamePresentation(&namePresentation); + aParam->GetIsOutgoing(&isOutgoing); aParam->GetIsEmergency(&isEmergency); aParam->GetIsConference(&isConference); @@ -63,10 +69,13 @@ struct ParamTraits WriteParam(aMsg, clientId); WriteParam(aMsg, callIndex); WriteParam(aMsg, callState); + WriteParam(aMsg, disconnectedReason); + WriteParam(aMsg, number); WriteParam(aMsg, numberPresentation); WriteParam(aMsg, name); WriteParam(aMsg, namePresentation); + WriteParam(aMsg, isOutgoing); WriteParam(aMsg, isEmergency); WriteParam(aMsg, isConference); @@ -90,10 +99,13 @@ struct ParamTraits uint32_t clientId; uint32_t callIndex; uint16_t callState; + nsString disconnectedReason; + nsString number; uint16_t numberPresentation; nsString name; uint16_t namePresentation; + bool isOutgoing; bool isEmergency; bool isConference; @@ -104,10 +116,13 @@ struct ParamTraits if (!(ReadParam(aMsg, aIter, &clientId) && ReadParam(aMsg, aIter, &callIndex) && ReadParam(aMsg, aIter, &callState) && + ReadParam(aMsg, aIter, &disconnectedReason) && + ReadParam(aMsg, aIter, &number) && ReadParam(aMsg, aIter, &numberPresentation) && ReadParam(aMsg, aIter, &name) && ReadParam(aMsg, aIter, &namePresentation) && + ReadParam(aMsg, aIter, &isOutgoing) && ReadParam(aMsg, aIter, &isEmergency) && ReadParam(aMsg, aIter, &isConference) && @@ -117,10 +132,21 @@ struct ParamTraits } nsCOMPtr info = - new TelephonyCallInfo(clientId, callIndex, callState, number, - numberPresentation, name, namePresentation, - isOutgoing, isEmergency, isConference, - isSwitchable, isMergeable); + new TelephonyCallInfo(clientId, + callIndex, + callState, + disconnectedReason, + + number, + numberPresentation, + name, + namePresentation, + + isOutgoing, + isEmergency, + isConference, + isSwitchable, + isMergeable); info.forget(aResult); diff --git a/dom/telephony/ipc/TelephonyIPCService.cpp b/dom/telephony/ipc/TelephonyIPCService.cpp index 75f2d83fd3..862fa6b7a4 100644 --- a/dom/telephony/ipc/TelephonyIPCService.cpp +++ b/dom/telephony/ipc/TelephonyIPCService.cpp @@ -376,15 +376,6 @@ TelephonyIPCService::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** a return NS_OK; } -NS_IMETHODIMP -TelephonyIPCService::ConferenceCallStateChanged(uint16_t aCallState) -{ - for (uint32_t i = 0; i < mListeners.Length(); i++) { - mListeners[i]->ConferenceCallStateChanged(aCallState); - } - return NS_OK; -} - NS_IMETHODIMP TelephonyIPCService::EnumerateCallStateComplete() { @@ -421,16 +412,6 @@ TelephonyIPCService::NotifyConferenceError(const nsAString& aName, return NS_OK; } -NS_IMETHODIMP -TelephonyIPCService::NotifyError(uint32_t aClientId, int32_t aCallIndex, - const nsAString& aError) -{ - for (uint32_t i = 0; i < mListeners.Length(); i++) { - mListeners[i]->NotifyError(aClientId, aCallIndex, aError); - } - return NS_OK; -} - NS_IMETHODIMP TelephonyIPCService::SupplementaryServiceNotification(uint32_t aClientId, int32_t aCallIndex, diff --git a/dom/telephony/ipc/TelephonyParent.cpp b/dom/telephony/ipc/TelephonyParent.cpp index d9af1b837c..68015c23b8 100644 --- a/dom/telephony/ipc/TelephonyParent.cpp +++ b/dom/telephony/ipc/TelephonyParent.cpp @@ -286,15 +286,6 @@ TelephonyParent::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo** aAllI return SendNotifyCallStateChanged(allInfo) ? NS_OK : NS_ERROR_FAILURE; } -NS_IMETHODIMP -TelephonyParent::ConferenceCallStateChanged(uint16_t aCallState) -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - return SendNotifyConferenceCallStateChanged(aCallState) ? NS_OK - : NS_ERROR_FAILURE; -} - NS_IMETHODIMP TelephonyParent::EnumerateCallStateComplete() { @@ -331,17 +322,6 @@ TelephonyParent::NotifyConferenceError(const nsAString& aName, : NS_ERROR_FAILURE; } -NS_IMETHODIMP -TelephonyParent::NotifyError(uint32_t aClientId, - int32_t aCallIndex, - const nsAString& aError) -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - return SendNotifyCallError(aClientId, aCallIndex, nsString(aError)) - ? NS_OK : NS_ERROR_FAILURE; -} - NS_IMETHODIMP TelephonyParent::SupplementaryServiceNotification(uint32_t aClientId, int32_t aCallIndex, @@ -392,12 +372,6 @@ TelephonyRequestParent::CallStateChanged(uint32_t aLength, nsITelephonyCallInfo* MOZ_CRASH("Not a TelephonyParent!"); } -NS_IMETHODIMP -TelephonyRequestParent::ConferenceCallStateChanged(uint16_t aCallState) -{ - MOZ_CRASH("Not a TelephonyParent!"); -} - NS_IMETHODIMP TelephonyRequestParent::EnumerateCallStateComplete() { @@ -431,14 +405,6 @@ TelephonyRequestParent::NotifyConferenceError(const nsAString& aName, MOZ_CRASH("Not a TelephonyParent!"); } -NS_IMETHODIMP -TelephonyRequestParent::NotifyError(uint32_t aClientId, - int32_t aCallIndex, - const nsAString& aError) -{ - MOZ_CRASH("Not a TelephonyParent!"); -} - NS_IMETHODIMP TelephonyRequestParent::SupplementaryServiceNotification(uint32_t aClientId, int32_t aCallIndex, diff --git a/dom/telephony/moz.build b/dom/telephony/moz.build index 27eb963ad2..4b6eef1fc7 100644 --- a/dom/telephony/moz.build +++ b/dom/telephony/moz.build @@ -63,6 +63,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']: EXTRA_COMPONENTS += [ 'gonk/TelephonyAudioService.js', 'gonk/TelephonyAudioService.manifest', + 'gonk/USSDReceivedWrapper.js', + 'gonk/USSDReceivedWrapper.manifest', ] if not CONFIG['DISABLE_MOZ_RIL_GEOLOC']: EXTRA_COMPONENTS += [ @@ -70,9 +72,13 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']: 'gonk/TelephonyService.manifest', ] EXTRA_JS_MODULES += [ - 'gonk/DialNumberUtils.jsm' + 'gonk/DialNumberUtils.jsm', + 'gonk/TelephonyUtils.jsm', ] include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/dom/telephony/nsITelephonyCallInfo.idl b/dom/telephony/nsITelephonyCallInfo.idl index 3561ec4e06..e179b70060 100644 --- a/dom/telephony/nsITelephonyCallInfo.idl +++ b/dom/telephony/nsITelephonyCallInfo.idl @@ -4,7 +4,7 @@ #include "nsISupports.idl" -[scriptable, uuid(3ea2d155-8ea2-42be-85d7-bd8ede8afc40)] +[scriptable, uuid(e5e1be26-a3d4-49b3-8d9f-c1df5192b364)] interface nsITelephonyCallInfo : nsISupports { /** @@ -22,6 +22,14 @@ interface nsITelephonyCallInfo : nsISupports */ readonly attribute unsigned short callState; + /** + * The disconnectedReason of a call is defualt to an empty string when the + * call is "not disconnected", but once the call becomes "disconnected" the + * disconnectedReason should be a non-empty string no matter the call is + * disconnected for a noraml reason or an error. + */ + readonly attribute DOMString disconnectedReason; + /** * Number of the other party. */ diff --git a/dom/telephony/nsITelephonyService.idl b/dom/telephony/nsITelephonyService.idl index 48d29e65ce..f4fe40a135 100644 --- a/dom/telephony/nsITelephonyService.idl +++ b/dom/telephony/nsITelephonyService.idl @@ -7,7 +7,7 @@ interface nsIMobileCallForwardingOptions; interface nsITelephonyCallInfo; -[scriptable, uuid(37fb45bb-ae10-4cfd-b24e-d656a9787a0a)] +[scriptable, uuid(baa9f5f3-5cab-40e0-81e9-ae0abd917907)] interface nsITelephonyListener : nsISupports { /** @@ -29,17 +29,6 @@ interface nsITelephonyListener : nsISupports void callStateChanged(in unsigned long length, [array, size_is(length)] in nsITelephonyCallInfo allInfo); - /** - * Called when participants of a conference call have been updated, and the - * conference call state changes. - * - * @param callState - * Possible values are: nsITelephonyService::CALL_STATE_UNKNOWN, - * nsITelephonyService::CALL_STATE_HELD, - * nsITelephonyService::CALL_STATE_CONNECTED. - */ - void conferenceCallStateChanged(in unsigned short callState); - /** * Notify when RIL receives supplementary service notification. * @@ -54,20 +43,6 @@ interface nsITelephonyListener : nsISupports in long callIndex, in unsigned short notification); - /** - * Called when RIL error occurs. - * - * @param clientId - Indicate the RIL client, 0 ~ (number of client - 1). - * @param callIndex - * Call identifier assigned by the RIL. -1 if no connection - * @param error - * Error from RIL. - */ - void notifyError(in unsigned long clientId, - in long callIndex, - in AString error); - /** * Called when a waiting call comes in CDMA networks. * diff --git a/dom/telephony/test/marionette/head.js b/dom/telephony/test/marionette/head.js index 0bf7972c46..95de5568d6 100644 --- a/dom/telephony/test/marionette/head.js +++ b/dom/telephony/test/marionette/head.js @@ -1022,6 +1022,11 @@ let emulator = (function() { }); } + function sendTone(tone, pause, serviceId) { + log("Send DTMF " + tone + " serviceId " + serviceId); + return telephony.sendTones(tone, pause, null, serviceId); + } + /** * Config radio. * @@ -1085,6 +1090,7 @@ let emulator = (function() { this.gRemoveCallInConference = removeCallInConference; this.gHangUpCallInConference = hangUpCallInConference; this.gHangUpConference = hangUpConference; + this.gSendTone = sendTone; this.gSetupConference = setupConference; this.gSetRadioEnabled = setRadioEnabled; }()); diff --git a/dom/telephony/test/marionette/manifest.ini b/dom/telephony/test/marionette/manifest.ini index b188715686..028c35264d 100644 --- a/dom/telephony/test/marionette/manifest.ini +++ b/dom/telephony/test/marionette/manifest.ini @@ -21,6 +21,7 @@ qemu = true [test_dsds_connection_conflict.js] [test_dsds_default_service_id.js] [test_dsds_normal_call.js] +[test_dtmf.js] [test_emergency.js] [test_emergency_label.js] [test_incall_mmi_call_hold.js] @@ -32,7 +33,6 @@ qemu = true [test_incoming_answer_hangup_oncallschanged.js] [test_incoming_basic_operations.js] [test_incoming_onstatechange.js] -[test_mmi.js] [test_mmi_call_barring.js] [test_mmi_call_forwarding.js] [test_mmi_call_waiting.js] @@ -41,6 +41,7 @@ qemu = true [test_mmi_change_pin2.js] [test_mmi_clip.js] [test_mmi_clir.js] +[test_mmi_imei.js] [test_mmi_unlock_puk.js] [test_mmi_unlock_puk2.js] [test_mmi_ussd.js] @@ -58,4 +59,5 @@ qemu = true [test_ready.js] [test_redundant_operations.js] [test_swap_held_and_active.js] +[test_TelephonyUtils.js] [test_temporary_clir.js] diff --git a/dom/telephony/test/marionette/test_TelephonyUtils.js b/dom/telephony/test/marionette/test_TelephonyUtils.js new file mode 100644 index 0000000000..b3a155718a --- /dev/null +++ b/dom/telephony/test/marionette/test_TelephonyUtils.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_CONTEXT = "chrome"; +MARIONETTE_HEAD_JS = 'head.js'; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Promise.jsm"); + +XPCOMUtils.defineLazyServiceGetter(this, + "TelephonyService", + "@mozilla.org/telephony/telephonyservice;1", + "nsIGonkTelephonyService"); + +XPCOMUtils.defineLazyModuleGetter(this, "TelephonyUtils", + "resource://gre/modules/TelephonyUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "RIL", function () { + let ns = {}; + Cu.import("resource://gre/modules/ril_consts.js", ns); + return ns; +}); + +const number = "0912345678"; + +function dial() { + return new Promise(resolve => { + TelephonyService.dial(0, number, false, { + QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyDialCallback]), + notifyDialCallSuccess: function() { resolve(); } + }); + }); +} + +function waitForStateChanged() { + return new Promise(resolve => { + let listener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyListener]), + + callStateChanged: function(length, allInfo) { + resolve(allInfo); + TelephonyService.unregisterListener(listener); + }, + conferenceCallStateChanged: function() {}, + supplementaryServiceNotification: function() {}, + notifyError: function() {}, + notifyCdmaCallWaiting: function() {}, + notifyConferenceError: function() {} + }; + + TelephonyService.registerListener(listener); + }); +} + +function test_noCall() { + log("== test_noCall =="); + is(TelephonyUtils.hasAnyCalls(), false, "hasAnyCalls"); + is(TelephonyUtils.hasConnectedCalls(), false, "hasConnectedCalls"); + return TelephonyUtils.waitForNoCalls(); +} + +function test_oneCall() { + log("== test_oneCall =="); + + return dial() + .then(() => { + is(TelephonyUtils.hasAnyCalls(), true, "hasAnyCalls"); + is(TelephonyUtils.hasConnectedCalls(), false, "hasConnectedCalls"); + }) + .then(() => { + let p = waitForStateChanged(); + emulator.runCmd("gsm accept " + number); + return p; + }) + .then(allInfo => { + is(allInfo[0].callState, Ci.nsITelephonyService.CALL_STATE_CONNECTED); + is(TelephonyUtils.hasAnyCalls(), true, "hasAnyCalls"); + is(TelephonyUtils.hasConnectedCalls(), true, "hasConnectedCalls"); + }) + .then(() => { + let p = TelephonyUtils.waitForNoCalls(); + emulator.runCmd("gsm cancel " + number); + return p; + }); +} + +test_noCall() + .then(test_oneCall) + .catch(error => ok(false, "Promise reject: " + error)) + .then(finish); diff --git a/dom/telephony/test/marionette/test_dtmf.js b/dom/telephony/test/marionette/test_dtmf.js new file mode 100644 index 0000000000..6446094405 --- /dev/null +++ b/dom/telephony/test/marionette/test_dtmf.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = 'head.js'; + +function muxModem(id) { + return new Promise((resolve, reject) => { + emulator.runCmdWithCallback("mux modem " + id, resolve); + }); +} + +function testDtmfNoActiveCall() { + log("= testDtmfNoActiveCall ="); + return new Promise((resolve, reject) => { + gSendTone('1', 5, 0).then(() => { + log("Unexpected success. We cannot send a DTMF without an active call"); + reject(); + }, resolve); + }); +} + +function testDtmfDsds() { + log("= testDtmfDsds ="); + + let outCall; + let number = "0912345000"; + let serviceId = 0; + let otherServiceId = 1; + + return Promise.resolve() + .then(() => muxModem(serviceId)) + .then(() => gDial(number, serviceId)) + .then(call => { + outCall = call; + is(outCall.serviceId, serviceId); + }) + .then(() => gRemoteAnswer(outCall)) + // Send tone with correct serviceId. + .then(() => gSendTone('1', 5, serviceId)) + .then(() => emulator.runCmd("modem dtmf")) + .then(tone => { + is(tone, '1,OK', 'Sent tone is 1'); + }) + // Send tone without serviceId. + .then(() => gSendTone('2', 5)) + .then(() => emulator.runCmd("modem dtmf")) + .then(tone => { + is(tone, '2,OK', 'Sent tone is 2'); + }) + // Send tone with incorrect serviceId. + .then(gSendTone('1', 5, otherServiceId).catch((e) => { + log('Expected Error ' + e); + gRemoteHangUp(outCall); + }) + ) + .catch((e) => { + log('Unexpected Error ' + e); + ok(false); + }); +} + +startDSDSTest(function() { + testDtmfNoActiveCall() + .then(testDtmfDsds) + .then(emulator.runCmd("modem dtmf reset")) + .catch(error => ok(false, "Promise reject: " + error)) + .then(finish); +}); diff --git a/dom/telephony/test/marionette/test_mmi.js b/dom/telephony/test/marionette/test_mmi_imei.js similarity index 100% rename from dom/telephony/test/marionette/test_mmi.js rename to dom/telephony/test/marionette/test_mmi_imei.js diff --git a/dom/telephony/test/marionette/test_multiple_hold.js b/dom/telephony/test/marionette/test_multiple_hold.js index 356b11df87..58194aeed5 100644 --- a/dom/telephony/test/marionette/test_multiple_hold.js +++ b/dom/telephony/test/marionette/test_multiple_hold.js @@ -46,9 +46,13 @@ startTest(function() { .then(() => gCheckAll(inCall, [inCall, outCall], "", [], [inInfo.active, outInfo.held])) - .then(() => gHangUp(inCall)) - .then(() => gCheckAll(null, [outCall], "", [], [outInfo.held])) - + // Hangup the active call will automatically resume the held call. + .then(() => { + let p1 = gWaitForNamedStateEvent(outCall, "connected"); + let p2 = gHangUp(inCall); + return Promise.all([p1, p2]); + }) + .then(() => gCheckAll(outCall, [outCall], "", [], [outInfo.active])) .then(() => gHangUp(outCall)) .then(() => gCheckAll(null, [], "", [], [])) diff --git a/dom/telephony/test/xpcshell/test_parseMMI.js b/dom/telephony/test/xpcshell/test_parseMMI.js index 566bf80053..74ffed2546 100644 --- a/dom/telephony/test/xpcshell/test_parseMMI.js +++ b/dom/telephony/test/xpcshell/test_parseMMI.js @@ -1,7 +1,8 @@ /* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ + * http://creativecommons.org/publicdomain/zero/1.0/ */ -subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this); +let TS = {}; +subscriptLoader.loadSubScript("resource://gre/components/TelephonyService.js", TS); let NS = {}; subscriptLoader.loadSubScript("resource://gre/modules/DialNumberUtils.jsm", NS); @@ -50,7 +51,7 @@ add_test(function test_parseMMI_USSD() { let mmi = parseMMI("*123#"); equal(mmi.fullMMI, "*123#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, undefined); equal(mmi.sib, undefined); @@ -73,7 +74,7 @@ add_test(function test_parseMMI_sia() { let mmi = parseMMI("*123*1#"); equal(mmi.fullMMI, "*123*1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, "1"); equal(mmi.sib, undefined); @@ -88,7 +89,7 @@ add_test(function test_parseMMI_sib() { let mmi = parseMMI("*123**1#"); equal(mmi.fullMMI, "*123**1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, "1"); @@ -103,7 +104,7 @@ add_test(function test_parseMMI_sic() { let mmi = parseMMI("*123***1#"); equal(mmi.fullMMI, "*123***1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, ""); @@ -118,7 +119,7 @@ add_test(function test_parseMMI_sia_sib() { let mmi = parseMMI("*123*1*1#"); equal(mmi.fullMMI, "*123*1*1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, "1"); equal(mmi.sib, "1"); @@ -133,7 +134,7 @@ add_test(function test_parseMMI_sia_sic() { let mmi = parseMMI("*123*1**1#"); equal(mmi.fullMMI, "*123*1**1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, "1"); equal(mmi.sib, ""); @@ -148,7 +149,7 @@ add_test(function test_parseMMI_sib_sic() { let mmi = parseMMI("*123**1*1#"); equal(mmi.fullMMI, "*123**1*1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, "1"); @@ -163,7 +164,7 @@ add_test(function test_parseMMI_pwd() { let mmi = parseMMI("*123****1#"); equal(mmi.fullMMI, "*123****1#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, ""); equal(mmi.sib, ""); @@ -178,7 +179,7 @@ add_test(function test_parseMMI_dial_number() { let mmi = parseMMI("*123#345"); equal(mmi.fullMMI, "*123#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "123"); equal(mmi.sia, undefined); equal(mmi.sib, undefined); @@ -198,7 +199,7 @@ add_test(function test_parseMMI_activation() { let mmi = parseMMI("*00*12*34*56#"); equal(mmi.fullMMI, "*00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_ACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_ACTIVATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -213,7 +214,7 @@ add_test(function test_parseMMI_deactivation() { let mmi = parseMMI("#00*12*34*56#"); equal(mmi.fullMMI, "#00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_DEACTIVATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_DEACTIVATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -228,7 +229,7 @@ add_test(function test_parseMMI_interrogation() { let mmi = parseMMI("*#00*12*34*56#"); equal(mmi.fullMMI, "*#00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_INTERROGATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_INTERROGATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -243,7 +244,7 @@ add_test(function test_parseMMI_registration() { let mmi = parseMMI("**00*12*34*56#"); equal(mmi.fullMMI, "**00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_REGISTRATION); + equal(mmi.procedure, TS.MMI_PROCEDURE_REGISTRATION); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); @@ -258,7 +259,7 @@ add_test(function test_parseMMI_erasure() { let mmi = parseMMI("##00*12*34*56#"); equal(mmi.fullMMI, "##00*12*34*56#"); - equal(mmi.procedure, MMI_PROCEDURE_ERASURE); + equal(mmi.procedure, TS.MMI_PROCEDURE_ERASURE); equal(mmi.serviceCode, "00"); equal(mmi.sia, "12"); equal(mmi.sib, "34"); diff --git a/dom/webidl/MozMobileConnection.webidl b/dom/webidl/MozMobileConnection.webidl index 4b38b5ab1d..1d9c9d4047 100644 --- a/dom/webidl/MozMobileConnection.webidl +++ b/dom/webidl/MozMobileConnection.webidl @@ -56,6 +56,9 @@ interface MozMobileConnection : EventTarget const long CALL_BARRING_PROGRAM_OUTGOING_INTERNATIONAL_EXCEPT_HOME = 2; const long CALL_BARRING_PROGRAM_ALL_INCOMING = 3; const long CALL_BARRING_PROGRAM_INCOMING_ROAMING = 4; + const long CALL_BARRING_PROGRAM_ALL_SERVICE = 5; + const long CALL_BARRING_PROGRAM_OUTGOING_SERVICE = 6; + const long CALL_BARRING_PROGRAM_INCOMING_SERVICE = 7; /** * Calling line identification restriction constants. diff --git a/dom/webidl/MozPaymentProvider.webidl b/dom/webidl/MozPaymentProvider.webidl index 30137c3b24..5450713a2e 100644 --- a/dom/webidl/MozPaymentProvider.webidl +++ b/dom/webidl/MozPaymentProvider.webidl @@ -4,8 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -callback SilentSmsCallback = void (optional MozSmsMessage message); - dictionary PaymentIccInfo { DOMString mcc; DOMString mnc; @@ -13,6 +11,16 @@ dictionary PaymentIccInfo { boolean dataPrimary; }; +dictionary PaymentSmsMessage { + DOMString iccId; + DOMString sender; + DOMString body; + DOMTimeStamp timestamp; + DOMTimeStamp sentTimestamp; +}; + +callback SilentSmsCallback = void (optional PaymentSmsMessage message); + [NavigatorProperty="mozPaymentProvider", NoInterfaceObject, HeaderFile="mozilla/dom/PaymentProviderUtils.h", diff --git a/dom/webidl/TelephonyCall.webidl b/dom/webidl/TelephonyCall.webidl index 9a529c1297..7a44866ed5 100644 --- a/dom/webidl/TelephonyCall.webidl +++ b/dom/webidl/TelephonyCall.webidl @@ -15,7 +15,7 @@ interface TelephonyCall : EventTarget { // call. We need an additional attribute for the CDMA waiting call. readonly attribute TelephonyCallId? secondId; - readonly attribute DOMString state; + readonly attribute TelephonyCallState state; // The property "emergency" indicates whether the call number is an emergency // number. Only the outgoing call could have a value with true and it is @@ -56,6 +56,15 @@ interface TelephonyCall : EventTarget { attribute EventHandler ongroupchange; }; +enum TelephonyCallState { + "dialing", + "alerting", + "connected", + "held", + "disconnected", + "incoming", +}; + enum TelephonyCallDisconnectedReason { "BadNumber", "NoRouteToDestination", diff --git a/dom/webidl/TelephonyCallGroup.webidl b/dom/webidl/TelephonyCallGroup.webidl index fbb8b9d126..e5af408309 100644 --- a/dom/webidl/TelephonyCallGroup.webidl +++ b/dom/webidl/TelephonyCallGroup.webidl @@ -26,7 +26,7 @@ interface TelephonyCallGroup : EventTarget { [NewObject, Throws] Promise resume(); - readonly attribute DOMString state; + readonly attribute TelephonyCallGroupState state; attribute EventHandler onstatechange; attribute EventHandler onconnected; @@ -34,3 +34,9 @@ interface TelephonyCallGroup : EventTarget { attribute EventHandler oncallschanged; attribute EventHandler onerror; }; + +enum TelephonyCallGroupState { + "", + "connected", + "held", +}; diff --git a/embedding/browser/nsCTooltipTextProvider.h b/embedding/browser/nsCTooltipTextProvider.h index 4829cdf0ba..c3e24372ef 100644 --- a/embedding/browser/nsCTooltipTextProvider.h +++ b/embedding/browser/nsCTooltipTextProvider.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsCommandHandler.cpp b/embedding/browser/nsCommandHandler.cpp index dedb32ad6e..6110889104 100644 --- a/embedding/browser/nsCommandHandler.cpp +++ b/embedding/browser/nsCommandHandler.cpp @@ -1,6 +1,6 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsCommandHandler.h b/embedding/browser/nsCommandHandler.h index 778362dadb..bad9d6fcba 100644 --- a/embedding/browser/nsCommandHandler.h +++ b/embedding/browser/nsCommandHandler.h @@ -1,6 +1,6 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsContextMenuInfo.h b/embedding/browser/nsContextMenuInfo.h index 5f29373b72..998045f973 100644 --- a/embedding/browser/nsContextMenuInfo.h +++ b/embedding/browser/nsContextMenuInfo.h @@ -1,6 +1,6 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsDocShellTreeOwner.cpp b/embedding/browser/nsDocShellTreeOwner.cpp index 0d3a0381e4..43c988487c 100644 --- a/embedding/browser/nsDocShellTreeOwner.cpp +++ b/embedding/browser/nsDocShellTreeOwner.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsDocShellTreeOwner.h b/embedding/browser/nsDocShellTreeOwner.h index 7186500d15..4ba6e52bcb 100644 --- a/embedding/browser/nsDocShellTreeOwner.h +++ b/embedding/browser/nsDocShellTreeOwner.h @@ -1,6 +1,6 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsEmbedStream.cpp b/embedding/browser/nsEmbedStream.cpp index 78ebd18f19..a20feb3172 100644 --- a/embedding/browser/nsEmbedStream.cpp +++ b/embedding/browser/nsEmbedStream.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsEmbedStream.h b/embedding/browser/nsEmbedStream.h index 1ecac1f00b..6b9098fbb2 100644 --- a/embedding/browser/nsEmbedStream.h +++ b/embedding/browser/nsEmbedStream.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsWebBrowserContentPolicy.cpp b/embedding/browser/nsWebBrowserContentPolicy.cpp index 0b681fa498..f6b17a1976 100644 --- a/embedding/browser/nsWebBrowserContentPolicy.cpp +++ b/embedding/browser/nsWebBrowserContentPolicy.cpp @@ -1,7 +1,6 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: ft=cpp tw=78 sw=4 et ts=8 - * - * This Source Code Form is subject to the terms of the Mozilla Public +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/browser/nsWebBrowserContentPolicy.h b/embedding/browser/nsWebBrowserContentPolicy.h index 9da5909805..e360e21101 100644 --- a/embedding/browser/nsWebBrowserContentPolicy.h +++ b/embedding/browser/nsWebBrowserContentPolicy.h @@ -1,6 +1,6 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * This Source Code Form is subject to the terms of the Mozilla Public +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/commandhandler/nsBaseCommandController.cpp b/embedding/components/commandhandler/nsBaseCommandController.cpp index 4ae3d443fb..2bd87b6f56 100644 --- a/embedding/components/commandhandler/nsBaseCommandController.cpp +++ b/embedding/components/commandhandler/nsBaseCommandController.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/commandhandler/nsBaseCommandController.h b/embedding/components/commandhandler/nsBaseCommandController.h index dbabfa1093..83976faacc 100644 --- a/embedding/components/commandhandler/nsBaseCommandController.h +++ b/embedding/components/commandhandler/nsBaseCommandController.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/commandhandler/nsCommandGroup.cpp b/embedding/components/commandhandler/nsCommandGroup.cpp index 8e69fe372e..69a4757986 100644 --- a/embedding/components/commandhandler/nsCommandGroup.cpp +++ b/embedding/components/commandhandler/nsCommandGroup.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/commandhandler/nsCommandGroup.h b/embedding/components/commandhandler/nsCommandGroup.h index 61fdcdb63b..b06d725b35 100644 --- a/embedding/components/commandhandler/nsCommandGroup.h +++ b/embedding/components/commandhandler/nsCommandGroup.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/commandhandler/nsControllerCommandTable.cpp b/embedding/components/commandhandler/nsControllerCommandTable.cpp index cf9c81a77d..7ea7922938 100644 --- a/embedding/components/commandhandler/nsControllerCommandTable.cpp +++ b/embedding/components/commandhandler/nsControllerCommandTable.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/commandhandler/nsControllerCommandTable.h b/embedding/components/commandhandler/nsControllerCommandTable.h index b3bce09f7b..353850bc05 100644 --- a/embedding/components/commandhandler/nsControllerCommandTable.h +++ b/embedding/components/commandhandler/nsControllerCommandTable.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/find/nsFind.cpp b/embedding/components/find/nsFind.cpp index 5a8ec1b2e3..520b675c39 100644 --- a/embedding/components/find/nsFind.cpp +++ b/embedding/components/find/nsFind.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/find/nsFind.h b/embedding/components/find/nsFind.h index 0e3cb7dde9..1609545a6e 100644 --- a/embedding/components/find/nsFind.h +++ b/embedding/components/find/nsFind.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/windowwatcher/nsAutoWindowStateHelper.cpp b/embedding/components/windowwatcher/nsAutoWindowStateHelper.cpp index 919e9d0aa4..2f81b26a1b 100644 --- a/embedding/components/windowwatcher/nsAutoWindowStateHelper.cpp +++ b/embedding/components/windowwatcher/nsAutoWindowStateHelper.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/windowwatcher/nsAutoWindowStateHelper.h b/embedding/components/windowwatcher/nsAutoWindowStateHelper.h index 822571520b..537b10dfa3 100644 --- a/embedding/components/windowwatcher/nsAutoWindowStateHelper.h +++ b/embedding/components/windowwatcher/nsAutoWindowStateHelper.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/windowwatcher/nsDialogParamBlock.cpp b/embedding/components/windowwatcher/nsDialogParamBlock.cpp index 065d4a04cb..f6f7af9677 100644 --- a/embedding/components/windowwatcher/nsDialogParamBlock.cpp +++ b/embedding/components/windowwatcher/nsDialogParamBlock.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/windowwatcher/nsDialogParamBlock.h b/embedding/components/windowwatcher/nsDialogParamBlock.h index a610ffa9d4..c1b06ea53a 100644 --- a/embedding/components/windowwatcher/nsDialogParamBlock.h +++ b/embedding/components/windowwatcher/nsDialogParamBlock.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/embedding/components/windowwatcher/nsPromptUtils.h b/embedding/components/windowwatcher/nsPromptUtils.h index 0b91fd86d2..bd1267bef6 100644 --- a/embedding/components/windowwatcher/nsPromptUtils.h +++ b/embedding/components/windowwatcher/nsPromptUtils.h @@ -1,3 +1,5 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/xpcom/glue/nsCOMPtr.h b/xpcom/glue/nsCOMPtr.h index 2927419733..2d5583fccb 100644 --- a/xpcom/glue/nsCOMPtr.h +++ b/xpcom/glue/nsCOMPtr.h @@ -339,6 +339,12 @@ protected: // template class nsGetterAddRefs; +// Helper for assert_validity method +template +char (&TestForIID(decltype(&NS_GET_TEMPLATE_IID(T))))[2]; +template +char TestForIID(...); + template class nsCOMPtr final #ifdef NSCAP_FEATURE_USE_BASE @@ -379,6 +385,15 @@ private: T* MOZ_OWNING_REF mRawPtr; #endif + void assert_validity() + { + static_assert(1 < sizeof(TestForIID(nullptr)), "nsCOMPtr only works " + "for types with IIDs. Either use RefPtr; add an IID to " + "your type with NS_DECLARE_STATIC_IID_ACCESSOR/" + "NS_DEFINE_STATIC_IID_ACCESSOR; or make the nsCOMPtr point " + "to a base class with an IID."); + } + public: typedef T element_type; @@ -412,12 +427,14 @@ public: nsCOMPtr() : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); } nsCOMPtr(const nsCOMPtr& aSmartPtr) : NSCAP_CTOR_BASE(aSmartPtr.mRawPtr) { + assert_validity(); if (mRawPtr) { NSCAP_ADDREF(this, mRawPtr); } @@ -427,6 +444,7 @@ public: MOZ_IMPLICIT nsCOMPtr(T* aRawPtr) : NSCAP_CTOR_BASE(aRawPtr) { + assert_validity(); if (mRawPtr) { NSCAP_ADDREF(this, mRawPtr); } @@ -437,6 +455,7 @@ public: MOZ_IMPLICIT nsCOMPtr(already_AddRefed& aSmartPtr) : NSCAP_CTOR_BASE(aSmartPtr.take()) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, mRawPtr); NSCAP_ASSERT_NO_QUERY_NEEDED(); } @@ -445,6 +464,7 @@ public: MOZ_IMPLICIT nsCOMPtr(already_AddRefed&& aSmartPtr) : NSCAP_CTOR_BASE(aSmartPtr.take()) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, mRawPtr); NSCAP_ASSERT_NO_QUERY_NEEDED(); } @@ -454,6 +474,7 @@ public: MOZ_IMPLICIT nsCOMPtr(already_AddRefed& aSmartPtr) : NSCAP_CTOR_BASE(static_cast(aSmartPtr.take())) { + assert_validity(); // But make sure that U actually inherits from T. static_assert(mozilla::IsBaseOf::value, "U is not a subclass of T"); @@ -466,6 +487,7 @@ public: MOZ_IMPLICIT nsCOMPtr(already_AddRefed&& aSmartPtr) : NSCAP_CTOR_BASE(static_cast(aSmartPtr.take())) { + assert_validity(); // But make sure that U actually inherits from T. static_assert(mozilla::IsBaseOf::value, "U is not a subclass of T"); @@ -477,6 +499,7 @@ public: MOZ_IMPLICIT nsCOMPtr(const nsQueryInterface aQI) : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); assign_from_qi(aQI, NS_GET_TEMPLATE_IID(T)); } @@ -485,6 +508,7 @@ public: MOZ_IMPLICIT nsCOMPtr(const nsQueryInterfaceWithError& aQI) : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); assign_from_qi_with_error(aQI, NS_GET_TEMPLATE_IID(T)); } @@ -493,6 +517,7 @@ public: MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCID aGS) : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); assign_from_gs_cid(aGS, NS_GET_TEMPLATE_IID(T)); } @@ -501,6 +526,7 @@ public: MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByCIDWithError& aGS) : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); assign_from_gs_cid_with_error(aGS, NS_GET_TEMPLATE_IID(T)); } @@ -509,6 +535,7 @@ public: MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractID aGS) : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); assign_from_gs_contractid(aGS, NS_GET_TEMPLATE_IID(T)); } @@ -517,6 +544,7 @@ public: MOZ_IMPLICIT nsCOMPtr(const nsGetServiceByContractIDWithError& aGS) : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); assign_from_gs_contractid_with_error(aGS, NS_GET_TEMPLATE_IID(T)); } @@ -526,6 +554,7 @@ public: MOZ_IMPLICIT nsCOMPtr(const nsCOMPtr_helper& aHelper) : NSCAP_CTOR_BASE(0) { + assert_validity(); NSCAP_LOG_ASSIGNMENT(this, 0); assign_from_helper(aHelper, NS_GET_TEMPLATE_IID(T)); NSCAP_ASSERT_NO_QUERY_NEEDED();