author | Ryan VanderMeulen <ryanvm@gmail.com> |
Thu, 21 Nov 2013 15:22:03 -0500 | |
changeset 172456 | 7225e584d6c754a54e4787e312b0927b315cce99 |
parent 172419 | cdbe5f3adeff24194060f31ee1509b20b9e8b0c6 (current diff) |
parent 172455 | b36a09f3ae680c083a02773286be4d769156a4d4 (diff) |
child 172457 | 121ce5b3fc114a8f1437722a553b21885a0494ed |
child 172481 | 85abacb35e236f5b7f94e8aa11a3ac7618b18180 |
child 172482 | ee5dbd3936a43256ff7684772546a6c5ef4b3ea5 |
child 172502 | 2330857264c4d2068183fa7dfb41eab3cb70c955 |
child 172512 | 1971c22c6e7839c312c2a96c5432d5c8bf12561c |
push id | 445 |
push user | ffxbld |
push date | Mon, 10 Mar 2014 22:05:19 +0000 |
treeherder | mozilla-release@dc38b741b04e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 28.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
CLOBBER | file | annotate | diff | comparison | revisions | |
configure.in | file | annotate | diff | comparison | revisions |
--- a/CLOBBER +++ b/CLOBBER @@ -13,9 +13,9 @@ # | | # O <-- Clobber O <-- Clobber # # Note: The description below will be part of the error message shown to users. # # Modifying this file will now automatically clobber the buildbot machines \o/ # -More Windows WebIDL changes. +More Windows WebIDL changes.
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -37,16 +37,22 @@ pref("browser.offline-apps.notify", fals pref("browser.cache.offline.enable", true); pref("offline-apps.allow_by_default", true); /* protocol warning prefs */ pref("network.protocol-handler.warn-external.tel", false); pref("network.protocol-handler.warn-external.mailto", false); pref("network.protocol-handler.warn-external.vnd.youtube", false); +/* protocol expose prefs */ +// By default, all protocol handlers are exposed. This means that the browser +// will response to openURL commands for all URL types. It will also try to open +// link clicks inside the browser before failing over to the system handlers. +pref("network.protocol-handler.expose.rtsp", false); + /* http prefs */ pref("network.http.pipelining", true); pref("network.http.pipelining.ssl", true); pref("network.http.proxy.pipelining", true); pref("network.http.pipelining.maxrequests" , 6); pref("network.http.keep-alive.timeout", 600); pref("network.http.max-connections", 20); pref("network.http.max-persistent-connections-per-server", 6);
--- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "89c57a31cebe0d8d94cbce4bcb47788b18f4fe84", + "revision": "94113691294813ec22930d07a5806921056dd856", "repo_path": "/integration/gaia-central" }
--- a/configure.in +++ b/configure.in @@ -7339,17 +7339,17 @@ if test -n "$MOZ_B2G_BT"; then fi AC_SUBST(MOZ_B2G_BT) AC_SUBST(MOZ_B2G_BT_BLUEZ) AC_SUBST(MOZ_B2G_BT_BLUEDROID) dnl ======================================================== dnl = Enable NFC Interface for B2G (Gonk usually) dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(b2g-nfc, +MOZ_ARG_ENABLE_BOOL(nfc, [ --enable-nfc Set compile flags necessary for compiling NFC API ], MOZ_NFC=1, MOZ_NFC= ) if test -n "$MOZ_NFC"; then AC_DEFINE(MOZ_NFC) fi AC_SUBST(MOZ_NFC)
--- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -765,16 +765,17 @@ GK_ATOM(onpagehide, "onpagehide") GK_ATOM(onpageshow, "onpageshow") GK_ATOM(onpaint, "onpaint") GK_ATOM(onpairedstatuschanged, "onpairedstatuschanged") GK_ATOM(onpaste, "onpaste") GK_ATOM(onpopuphidden, "onpopuphidden") GK_ATOM(onpopuphiding, "onpopuphiding") GK_ATOM(onpopupshowing, "onpopupshowing") GK_ATOM(onpopupshown, "onpopupshown") +GK_ATOM(onradiostatechange, "onradiostatechange") GK_ATOM(onreadystatechange, "onreadystatechange") GK_ATOM(onreceived, "onreceived") GK_ATOM(onremoteheld, "onremoteheld") GK_ATOM(onremoteresumed, "onremoteresumed") GK_ATOM(onretrieving, "onretrieving") GK_ATOM(onRequest, "onRequest") GK_ATOM(onrequestmediaplaystatus, "onrequestmediaplaystatus") GK_ATOM(onreset, "onreset")
--- a/dom/bluetooth/BluetoothRilListener.cpp +++ b/dom/bluetooth/BluetoothRilListener.cpp @@ -126,16 +126,22 @@ MobileConnectionListener::NotifyOtaStatu } NS_IMETHODIMP MobileConnectionListener::NotifyIccChanged() { return NS_OK; } +NS_IMETHODIMP +MobileConnectionListener::NotifyRadioStateChanged() +{ + return NS_OK; +} + /** * TelephonyListener Implementation * * TODO: Bug 921991 - B2G BT: support multiple sim cards */ class TelephonyListener : public nsITelephonyListener { public:
--- a/dom/bluetooth/bluedroid/BluetoothHfpManager.cpp +++ b/dom/bluetooth/bluedroid/BluetoothHfpManager.cpp @@ -507,23 +507,23 @@ void BluetoothHfpManager::ProcessConnectionState(bthf_connection_state_t aState, bt_bdaddr_t* aBdAddress) { BT_LOGR("%s: state %d", __FUNCTION__, aState); mConnectionState = aState; if (aState == BTHF_CONNECTION_STATE_CONNECTED) { + BdAddressTypeToString(aBdAddress, mDeviceAddress); BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED, NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID)); } else if (aState == BTHF_CONNECTION_STATE_DISCONNECTED) { DisconnectSco(); BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::NOTIFY_CONN_STATE_CHANGED, NS_LITERAL_STRING(BLUETOOTH_HFP_STATUS_CHANGED_ID)); - Reset(); } } void BluetoothHfpManager::ProcessAudioState(bthf_audio_state_t aState, bt_bdaddr_t* aBdAddress) { BT_LOGR("%s: state %d", __FUNCTION__, aState); @@ -736,16 +736,18 @@ BluetoothHfpManager::NotifyConnectionSta // Enumerate current calls mListener->EnumerateCalls(); OnConnect(EmptyString()); } else if (mConnectionState == BTHF_CONNECTION_STATE_DISCONNECTED) { mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE); OnDisconnect(EmptyString()); + + Reset(); } } } void BluetoothHfpManager::NotifyDialer(const nsAString& aCommand) { BluetoothValue v;
--- a/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp +++ b/dom/bluetooth/bluedroid/gonk/BluetoothServiceBluedroid.cpp @@ -70,17 +70,17 @@ static bool sIsBtEnabled = false; static nsString sAdapterBdAddress; static nsString sAdapterBdName; static uint32_t sAdapterDiscoverableTimeout; static InfallibleTArray<nsString> sAdapterBondedAddressArray; static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack; static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray; -static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetPairedDeviceRunnableArray; +static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray; static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray; static nsTArray<int> sRequestedDeviceCountArray; static StaticAutoPtr<Monitor> sToggleBtMonitor; /** * Static callback functions */ @@ -351,17 +351,17 @@ static void RemoteDevicePropertiesChangeCallback(bt_status_t aStatus, bt_bdaddr_t *aBdAddress, int aNumProperties, bt_property_t *aProperties) { MOZ_ASSERT(!NS_IsMainThread()); if (sRequestedDeviceCountArray.IsEmpty()) { - MOZ_ASSERT(sGetPairedDeviceRunnableArray.IsEmpty()); + MOZ_ASSERT(sGetDeviceRunnableArray.IsEmpty()); return; } sRequestedDeviceCountArray[0]--; InfallibleTArray<BluetoothNamedValue> props; nsString remoteDeviceBdAddress; @@ -390,31 +390,31 @@ RemoteDevicePropertiesChangeCallback(bt_ } } // Use address as the index sRemoteDevicesPack.AppendElement( BluetoothNamedValue(remoteDeviceBdAddress, props)); if (sRequestedDeviceCountArray[0] == 0) { - MOZ_ASSERT(!sGetPairedDeviceRunnableArray.IsEmpty()); + MOZ_ASSERT(!sGetDeviceRunnableArray.IsEmpty()); - if (sGetPairedDeviceRunnableArray.IsEmpty()) { + if (sGetDeviceRunnableArray.IsEmpty()) { BT_LOGR("No runnable to return"); return; } - DispatchBluetoothReply(sGetPairedDeviceRunnableArray[0], + DispatchBluetoothReply(sGetDeviceRunnableArray[0], sRemoteDevicesPack, EmptyString()); // After firing it, clean up cache sRemoteDevicesPack.Clear(); sRequestedDeviceCountArray.RemoveElementAt(0); - sGetPairedDeviceRunnableArray.RemoveElementAt(0); + sGetDeviceRunnableArray.RemoveElementAt(0); } } static void DeviceFoundCallback(int aNumProperties, bt_property_t *aProperties) { MOZ_ASSERT(!NS_IsMainThread()); @@ -783,22 +783,64 @@ BluetoothServiceBluedroid::GetDefaultAda runnable.forget(); return NS_OK; } nsresult BluetoothServiceBluedroid::GetConnectedDevicePropertiesInternal( - uint16_t aProfileId, BluetoothReplyRunnable* aRunnable) + uint16_t aServiceUuid, BluetoothReplyRunnable* aRunnable) { MOZ_ASSERT(NS_IsMainThread()); - //FIXME: This will be implemented in later patches - DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString()); + if (!IsReady()) { + NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); + DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); + return NS_OK; + } + + BluetoothProfileManagerBase* profile = + BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid); + if (!profile) { + InfallibleTArray<BluetoothNamedValue> emptyArr; + DispatchBluetoothReply(aRunnable, emptyArr, + NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE)); + return NS_OK; + } + + nsTArray<nsString> deviceAddresses; + if (profile->IsConnected()) { + nsString address; + profile->GetAddress(address); + deviceAddresses.AppendElement(address); + } + + int requestedDeviceCount = deviceAddresses.Length(); + if (requestedDeviceCount == 0) { + InfallibleTArray<BluetoothNamedValue> emptyArr; + DispatchBluetoothReply(aRunnable, emptyArr, EmptyString()); + return NS_OK; + } + + for (int i = 0; i < requestedDeviceCount; i++) { + // Retrieve all properties of devices + bt_bdaddr_t addressType; + StringToBdAddressType(deviceAddresses[i], &addressType); + + int ret = sBtInterface->get_remote_device_properties(&addressType); + if (ret != BT_STATUS_SUCCESS) { + DispatchBluetoothReply(aRunnable, BluetoothValue(true), + NS_LITERAL_STRING("GetConnectedDeviceFailed")); + return NS_OK; + } + } + + sRequestedDeviceCountArray.AppendElement(requestedDeviceCount); + sGetDeviceRunnableArray.AppendElement(aRunnable); return NS_OK; } nsresult BluetoothServiceBluedroid::GetPairedDevicePropertiesInternal( const nsTArray<nsString>& aDeviceAddress, BluetoothReplyRunnable* aRunnable) { @@ -825,17 +867,17 @@ BluetoothServiceBluedroid::GetPairedDevi if (ret != BT_STATUS_SUCCESS) { DispatchBluetoothReply(aRunnable, BluetoothValue(true), NS_LITERAL_STRING("GetPairedDeviceFailed")); return NS_OK; } } sRequestedDeviceCountArray.AppendElement(requestedDeviceCount); - sGetPairedDeviceRunnableArray.AppendElement(aRunnable); + sGetDeviceRunnableArray.AppendElement(aRunnable); return NS_OK; } nsresult BluetoothServiceBluedroid::StartDiscoveryInternal( BluetoothReplyRunnable* aRunnable) { @@ -864,23 +906,25 @@ BluetoothServiceBluedroid::StopDiscovery { MOZ_ASSERT(NS_IsMainThread()); if (!IsReady()) { NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!"); DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr); return NS_OK; } + int ret = sBtInterface->cancel_discovery(); if (ret != BT_STATUS_SUCCESS) { ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StopDiscovery")); return NS_OK; } sChangeDiscoveryRunnableArray.AppendElement(aRunnable); + return NS_OK; } nsresult BluetoothServiceBluedroid::GetDevicePropertiesInternal( const BluetoothSignal& aSignal) { return NS_OK; @@ -1143,22 +1187,24 @@ nsresult BluetoothServiceBluedroid::PrepareAdapterInternal() { return NS_OK; } static void NextBluetoothProfileController() { - sControllerArray[0] = nullptr; - sControllerArray.RemoveElementAt(0); + MOZ_ASSERT(NS_IsMainThread()); - if (!sControllerArray.IsEmpty()) { - sControllerArray[0]->Start(); - } + // First, remove the task at the front which has been already done. + NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); + sControllerArray.RemoveElementAt(0); + // Re-check if the task array is empty, if it's not, the next task will begin. + NS_ENSURE_FALSE_VOID(sControllerArray.IsEmpty()); + sControllerArray[0]->Start(); } static void ConnectDisconnect(bool aConnect, const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable, uint16_t aServiceUuid, uint32_t aCod = 0) { MOZ_ASSERT(NS_IsMainThread());
--- a/dom/icc/tests/marionette/test_icc_card_state.js +++ b/dom/icc/tests/marionette/test_icc_card_state.js @@ -1,34 +1,27 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 30000; MARIONETTE_HEAD_JS = "icc_header.js"; function setRadioEnabled(enabled) { - SpecialPowers.addPermission("settings-write", true, document); + let connection = navigator.mozMobileConnections[0]; + ok(connection); - // TODO: Bug 856553 - [B2G] RIL: need an API to enable/disable radio - let settings = navigator.mozSettings; - let setLock = settings.createLock(); - let obj = { - "ril.radio.disabled": !enabled - }; - let setReq = setLock.set(obj); + let request = connection.setRadioEnabled(enabled); - setReq.addEventListener("success", function onSetSuccess() { - log("set 'ril.radio.disabled' to " + enabled); - }); + request.onsuccess = function onsuccess() { + log('setRadioEnabled: ' + enabled); + }; - setReq.addEventListener("error", function onSetError() { - ok(false, "cannot set 'ril.radio.disabled' to " + enabled); - }); - - SpecialPowers.removePermission("settings-write", document); + request.onerror = function onerror() { + ok(false, "setRadioEnabled should be ok"); + }; } /* Basic test */ taskHelper.push(function basicTest() { is(icc.cardState, "ready", "card state is " + icc.cardState); taskHelper.runNext(); });
--- a/dom/icc/tests/marionette/test_icc_info.js +++ b/dom/icc/tests/marionette/test_icc_info.js @@ -1,34 +1,27 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 30000; MARIONETTE_HEAD_JS = "icc_header.js"; function setRadioEnabled(enabled) { - SpecialPowers.addPermission("settings-write", true, document); + let connection = navigator.mozMobileConnections[0]; + ok(connection); - // TODO: Bug 856553 - [B2G] RIL: need an API to enable/disable radio - let settings = navigator.mozSettings; - let setLock = settings.createLock(); - let obj = { - "ril.radio.disabled": !enabled - }; - let setReq = setLock.set(obj); + let request = connection.setRadioEnabled(enabled); - setReq.addEventListener("success", function onSetSuccess() { - log("set 'ril.radio.disabled' to " + enabled); - }); + request.onsuccess = function onsuccess() { + log('setRadioEnabled: ' + enabled); + }; - setReq.addEventListener("error", function onSetError() { - ok(false, "cannot set 'ril.radio.disabled' to " + enabled); - }); - - SpecialPowers.removePermission("settings-write", document); + request.onerror = function onerror() { + ok(false, "setRadioEnabled should be ok"); + }; } function setEmulatorMccMnc(mcc, mnc) { let cmd = "operator set 0 Android,Android," + mcc + mnc; emulatorHelper.sendCommand(cmd, function (result) { let re = new RegExp("" + mcc + mnc + "$"); ok(result[0].match(re), "MCC/MNC should be changed."); });
--- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -98,17 +98,18 @@ this.SystemMessagePermissionsTable = { "nfc-manager-tech-discovered": { "nfc-manager": [] }, "nfc-manager-tech-lost": { "nfc-manager": [] }, "nfc-powerlevel-change": { "settings": ["read", "write"] - } + }, + "rtsp-open-video": {}, }; this.SystemMessagePermissionsChecker = { /** * Return all the needed permission names for the given system message. * @param string aSysMsgName * The system messsage name. * @returns object
--- a/dom/network/interfaces/nsIDOMMobileConnection.idl +++ b/dom/network/interfaces/nsIDOMMobileConnection.idl @@ -6,17 +6,17 @@ interface nsIDOMEventListener; interface nsIDOMDOMRequest; interface nsIDOMMozMobileConnectionInfo; interface nsIDOMMozMobileNetworkInfo; interface nsIDOMMozMobileCellInfo; interface nsIDOMMozMobileCFInfo; -[scriptable, builtinclass, uuid(052550e3-7466-4941-80d7-405c169652f9)] +[scriptable, builtinclass, uuid(4c8331f9-45f3-479d-ac3f-acb60fcc0583)] interface nsIDOMMozMobileConnection : nsIDOMEventTarget { const long ICC_SERVICE_CLASS_VOICE = (1 << 0); const long ICC_SERVICE_CLASS_DATA = (1 << 1); const long ICC_SERVICE_CLASS_FAX = (1 << 2); const long ICC_SERVICE_CLASS_SMS = (1 << 3); const long ICC_SERVICE_CLASS_DATA_SYNC = (1 << 4); const long ICC_SERVICE_CLASS_DATA_ASYNC = (1 << 5); @@ -78,16 +78,24 @@ interface nsIDOMMozMobileConnection : ns /** * The selection mode of the voice and data networks. * * Possible values: null (unknown), 'automatic', 'manual' */ readonly attribute DOMString networkSelectionMode; /** + * The current radio state. + * + * Possible values: null (unknown), 'enabling', 'enabled', 'disabling', + * 'disabled' + */ + readonly attribute DOMString radioState; + + /** * Search for available networks. * * If successful, the request's onsuccess will be called, and the request's * result will be an array of nsIDOMMozMobileNetworkInfo. * * Otherwise, the request's onerror will be called, and the request's error * will be either 'RadioNotAvailable', 'RequestNotSupported', * or 'GenericFailure'. @@ -344,16 +352,34 @@ interface nsIDOMMozMobileConnection : ns * If successful, the request's onsuccess will be called. * * Otherwise, the request's onerror will be called, and the request's error * will be either 'RequestNotSupported' or 'GenericFailure'. */ nsIDOMDOMRequest exitEmergencyCbMode(); /** + * Set radio enabled/disabled. + * + * @param enabled + * True to enable the radio. + * + * If successful, the request's onsuccess will be called. + * + * Otherwise, the request's onerror will be called, and the request's error + * will be either 'InvalidStateError', 'RadioNotAvailable', or + * 'GenericFailure'. + * + * Note: Request is not available when radioState is null, 'enabling', or + * 'disabling'. Calling the function in above conditions will receive + * 'InvalidStateError' error. + */ + nsIDOMDOMRequest setRadioEnabled(in boolean enabled); + + /** * The 'voicechange' event is notified whenever the voice connection object * changes. */ [implicit_jscontext] attribute jsval onvoicechange; /** * The 'datachange' event is notified whenever the data connection object * changes values. @@ -390,16 +416,22 @@ interface nsIDOMMozMobileConnection : ns */ [implicit_jscontext] attribute jsval onotastatuschange; /** * The 'oniccchange' event is notified whenever the iccid value * changes. */ [implicit_jscontext] attribute jsval oniccchange; + + /** + * The 'onradiostatechange' event is notified whenever the radio state + * changes. + */ + [implicit_jscontext] attribute jsval onradiostatechange; }; [scriptable, uuid(49706beb-a160-40b7-b745-50f62e389a2c)] interface nsIDOMMozMobileConnectionInfo : nsISupports { /** * State of the connection. *
--- a/dom/network/interfaces/nsIMobileConnectionProvider.idl +++ b/dom/network/interfaces/nsIMobileConnectionProvider.idl @@ -5,17 +5,17 @@ #include "nsISupports.idl" interface nsIDOMDOMRequest; interface nsIDOMMozMobileCFInfo; interface nsIDOMMozMobileConnectionInfo; interface nsIDOMMozMobileNetworkInfo; interface nsIDOMWindow; -[scriptable, uuid(f02c50d5-9d34-4f24-80eb-527a280e31fa)] +[scriptable, uuid(5013f5cc-24f9-45dc-ba03-f5dc031a3a6b)] interface nsIMobileConnectionListener : nsISupports { void notifyVoiceChanged(); void notifyDataChanged(); void notifyUssdReceived(in DOMString message, in boolean sessionEnded); void notifyDataError(in DOMString message); void notifyCFStateChange(in boolean success, @@ -23,39 +23,41 @@ interface nsIMobileConnectionListener : in unsigned short reason, in DOMString number, in unsigned short timeSeconds, in unsigned short serviceClass); void notifyEmergencyCbModeChanged(in boolean active, in unsigned long timeoutMs); void notifyOtaStatusChanged(in DOMString status); void notifyIccChanged(); + void notifyRadioStateChanged(); }; /** * XPCOM component (in the content process) that provides the mobile * network information. */ -[scriptable, uuid(84278a49-0f05-4585-b3f4-c74882ae5719)] +[scriptable, uuid(9a804dc4-6900-46af-8c38-3d0f424672b5)] interface nsIMobileConnectionProvider : nsISupports { /** * Called when a content process registers receiving unsolicited messages from * RadioInterfaceLayer in the chrome process. Only a content process that has * the 'mobileconnection' permission is allowed to register. */ void registerMobileConnectionMsg(in unsigned long clientId, in nsIMobileConnectionListener listener); void unregisterMobileConnectionMsg(in unsigned long clientId, in nsIMobileConnectionListener listener); nsIDOMMozMobileConnectionInfo getVoiceConnectionInfo(in unsigned long clientId); nsIDOMMozMobileConnectionInfo getDataConnectionInfo(in unsigned long clientId); DOMString getIccId(in unsigned long clientId); DOMString getNetworkSelectionMode(in unsigned long clientId); + DOMString getRadioState(in unsigned long clientId); nsIDOMDOMRequest getNetworks(in unsigned long clientId, in nsIDOMWindow window); nsIDOMDOMRequest selectNetwork(in unsigned long clientId, in nsIDOMWindow window, in nsIDOMMozMobileNetworkInfo network); nsIDOMDOMRequest selectNetworkAutomatically(in unsigned long clientId, in nsIDOMWindow window); @@ -104,9 +106,11 @@ interface nsIMobileConnectionProvider : nsIDOMDOMRequest setCallingLineIdRestriction(in unsigned long clientId, in nsIDOMWindow window, in unsigned short clirMode); nsIDOMDOMRequest getCallingLineIdRestriction(in unsigned long clientId, in nsIDOMWindow window); nsIDOMDOMRequest exitEmergencyCbMode(in unsigned long clientId, in nsIDOMWindow window); + + nsIDOMDOMRequest setRadioEnabled(in unsigned long clientId, in nsIDOMWindow window, in bool enabled); };
--- a/dom/network/src/MobileConnection.cpp +++ b/dom/network/src/MobileConnection.cpp @@ -73,16 +73,17 @@ NS_IMPL_RELEASE_INHERITED(MobileConnecti NS_IMPL_EVENT_HANDLER(MobileConnection, voicechange) NS_IMPL_EVENT_HANDLER(MobileConnection, datachange) NS_IMPL_EVENT_HANDLER(MobileConnection, ussdreceived) NS_IMPL_EVENT_HANDLER(MobileConnection, dataerror) NS_IMPL_EVENT_HANDLER(MobileConnection, cfstatechange) NS_IMPL_EVENT_HANDLER(MobileConnection, emergencycbmodechange) NS_IMPL_EVENT_HANDLER(MobileConnection, otastatuschange) NS_IMPL_EVENT_HANDLER(MobileConnection, iccchange) +NS_IMPL_EVENT_HANDLER(MobileConnection, radiostatechange) MobileConnection::MobileConnection(uint32_t aClientId) : mClientId(aClientId) { mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID); mWindow = nullptr; // Not being able to acquire the provider isn't fatal since we check @@ -207,16 +208,27 @@ MobileConnection::GetNetworkSelectionMod if (!mProvider || !CheckPermission("mobileconnection")) { return NS_OK; } return mProvider->GetNetworkSelectionMode(mClientId, aNetworkSelectionMode); } NS_IMETHODIMP +MobileConnection::GetRadioState(nsAString& aRadioState) +{ + aRadioState.SetIsVoid(true); + + if (!mProvider || !CheckPermission("mobileconnection")) { + return NS_OK; + } + return mProvider->GetRadioState(mClientId, aRadioState); +} + +NS_IMETHODIMP MobileConnection::GetNetworks(nsIDOMDOMRequest** aRequest) { *aRequest = nullptr; if (!CheckPermission("mobileconnection")) { return NS_OK; } @@ -514,16 +526,33 @@ MobileConnection::ExitEmergencyCbMode(ns if (!mProvider) { return NS_ERROR_FAILURE; } return mProvider->ExitEmergencyCbMode(mClientId, GetOwner(), aRequest); } +NS_IMETHODIMP +MobileConnection::SetRadioEnabled(bool aEnabled, + nsIDOMDOMRequest** aRequest) +{ + *aRequest = nullptr; + + if (!CheckPermission("mobileconnection")) { + return NS_OK; + } + + if (!mProvider) { + return NS_ERROR_FAILURE; + } + + return mProvider->SetRadioEnabled(mClientId, GetOwner(), aEnabled, aRequest); +} + // nsIMobileConnectionListener NS_IMETHODIMP MobileConnection::NotifyVoiceChanged() { if (!CheckPermission("mobileconnection")) { return NS_OK; } @@ -649,8 +678,18 @@ NS_IMETHODIMP MobileConnection::NotifyIccChanged() { if (!CheckPermission("mobileconnection")) { return NS_OK; } return DispatchTrustedEvent(NS_LITERAL_STRING("iccchange")); } + +NS_IMETHODIMP +MobileConnection::NotifyRadioStateChanged() +{ + if (!CheckPermission("mobileconnection")) { + return NS_OK; + } + + return DispatchTrustedEvent(NS_LITERAL_STRING("radiostatechange")); +} \ No newline at end of file
--- a/dom/network/tests/marionette/manifest.ini +++ b/dom/network/tests/marionette/manifest.ini @@ -13,8 +13,9 @@ disabled = Bug 808783 [test_mobile_data_connection.js] [test_mobile_data_location.js] [test_mobile_data_state.js] [test_mobile_mmi.js] [test_mobile_roaming_preference.js] [test_call_barring_get_option.js] [test_call_barring_set_error.js] [test_call_barring_change_password.js] +[test_mobile_set_radio.js]
new file mode 100644 --- /dev/null +++ b/dom/network/tests/marionette/test_mobile_set_radio.js @@ -0,0 +1,166 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; + +const DATA_KEY = "ril.data.enabled"; +const APN_KEY = "ril.data.apnSettings"; + +let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise; + +SpecialPowers.setBoolPref("dom.mozSettings.enabled", true); +SpecialPowers.addPermission("mobileconnection", true, document); +SpecialPowers.addPermission("settings-read", true, document); +SpecialPowers.addPermission("settings-write", true, document); + +let settings = window.navigator.mozSettings; +let connection = window.navigator.mozMobileConnections[0]; +ok(connection instanceof MozMobileConnection, + "connection is instanceof " + connection.constructor); + +function setSetting(key, value) { + let deferred = Promise.defer(); + + let setLock = settings.createLock(); + let obj = {}; + obj[key] = value; + + let setReq = setLock.set(obj); + setReq.addEventListener("success", function onSetSuccess() { + ok(true, "set '" + key + "' to " + obj[key]); + deferred.resolve(); + }); + setReq.addEventListener("error", function onSetError() { + ok(false, "cannot set '" + key + "'"); + deferred.reject(); + }); + + return deferred.promise; +} + +function setEmulatorAPN() { + let apn = + [ + [ + {"carrier":"T-Mobile US", + "apn":"epc.tmobile.com", + "mmsc":"http://mms.msg.eng.t-mobile.com/mms/wapenc", + "types":["default","supl","mms"]} + ] + ]; + return setSetting(APN_KEY, apn); +} + +function enableData() { + log("Turn data on."); + + let deferred = Promise.defer(); + + connection.addEventListener("datachange", function ondatachange() { + if (connection.data.connected === true) { + connection.removeEventListener("datachange", ondatachange); + log("mobileConnection.data.connected is now '" + + connection.data.connected + "'."); + deferred.resolve(); + } + }); + + setEmulatorAPN() + .then(() => setSetting(DATA_KEY, true)); + + return deferred.promise; +} + +function receivedPending(received, pending, nextAction) { + let index = pending.indexOf(received); + if (index != -1) { + pending.splice(index, 1); + } + if (pending.length === 0) { + nextAction(); + } +} + +function waitRadioState(state) { + let deferred = Promise.defer(); + + waitFor(function() { + deferred.resolve(); + }, function() { + return connection.radioState == state; + }); + + return deferred.promise; +} + +function setRadioEnabled(enabled, transientState, finalState) { + log("setRadioEnabled to " + enabled); + + let deferred = Promise.defer(); + let done = function() { + deferred.resolve(); + }; + + let pending = ["onradiostatechange", "onsuccess"]; + + let receivedTransient = false; + connection.onradiostatechange = function() { + let state = connection.radioState; + log("Received 'radiostatechange' event, radioState: " + state); + + if (state == transientState) { + receivedTransient = true; + } else if (state == finalState) { + ok(receivedTransient); + receivedPending("onradiostatechange", pending, done); + } + }; + + let req = connection.setRadioEnabled(enabled); + + req.onsuccess = function() { + log("setRadioEnabled success"); + receivedPending("onsuccess", pending, done); + }; + + req.onerror = function() { + ok(false, "setRadioEnabled should not fail"); + deferred.reject(); + }; + + return deferred.promise; +} + +function testSwitchRadio() { + log("= testSwitchRadio ="); + return waitRadioState("enabled") + .then(setRadioEnabled.bind(null, false, "disabling", "disabled")) + .then(setRadioEnabled.bind(null, true, "enabling", "enabled")); +} + +function testDisableRadioWhenDataConnected() { + log("= testDisableRadioWhenDataConnected ="); + return waitRadioState("enabled") + .then(enableData) + .then(setRadioEnabled.bind(null, false, "disabling", "disabled")) + .then(() => { + // Data should be disconnected. + is(connection.data.connected, false); + }) + .then(setRadioEnabled.bind(null, true, "enabling", "enabled")); +} + +function cleanUp() { + SpecialPowers.removePermission("mobileconnection", document); + SpecialPowers.removePermission("settings-write", document); + SpecialPowers.removePermission("settings-read", document); + SpecialPowers.clearUserPref("dom.mozSettings.enabled"); + finish(); +} + +testSwitchRadio() + .then(testDisableRadioWhenDataConnected) + .then(null, () => { + ok(false, "promise reject somewhere"); + }) + .then(cleanUp);
--- a/dom/system/gonk/RILContentHelper.js +++ b/dom/system/gonk/RILContentHelper.js @@ -96,16 +96,18 @@ const RIL_IPC_MSG_NAMES = [ "RIL:IccOpenChannel", "RIL:IccCloseChannel", "RIL:IccExchangeAPDU", "RIL:ReadIccContacts", "RIL:UpdateIccContact", "RIL:SetRoamingPreference", "RIL:GetRoamingPreference", "RIL:ExitEmergencyCbMode", + "RIL:SetRadioEnabled", + "RIL:RadioStateChanged", "RIL:SetVoicePrivacyMode", "RIL:GetVoicePrivacyMode", "RIL:OtaStatusChanged" ]; XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", "nsISyncMessageSender"); @@ -454,16 +456,17 @@ function RILContentHelper() { this.rilContexts = []; this.voicemailInfos = []; this.voicemailStatuses = []; for (let clientId = 0; clientId < this.numClients; clientId++) { this.rilContexts[clientId] = { cardState: RIL.GECKO_CARDSTATE_UNKNOWN, networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN, + radioState: null, iccInfo: null, voiceConnectionInfo: new MobileConnectionInfo(), dataConnectionInfo: new MobileConnectionInfo() }; this.voicemailInfos[clientId] = new VoicemailInfo(); } @@ -606,16 +609,17 @@ RILContentHelper.prototype = { let rilContext = cpmm.sendSyncMessage("RIL:GetRilContext", {clientId: cId})[0]; if (!rilContext) { if (DEBUG) debug("Received null rilContext from chrome process."); continue; } this.rilContexts[cId].cardState = rilContext.cardState; this.rilContexts[cId].networkSelectionMode = rilContext.networkSelectionMode; + this.rilContexts[cId].radioState = rilContext.detailedRadioState; this.updateIccInfo(cId, rilContext.iccInfo); this.updateConnectionInfo(rilContext.voice, this.rilContexts[cId].voiceConnectionInfo); this.updateConnectionInfo(rilContext.data, this.rilContexts[cId].dataConnectionInfo); } return this.rilContexts[clientId]; }, @@ -652,16 +656,21 @@ RILContentHelper.prototype = { return context && context.iccInfo && context.iccInfo.iccid; }, getNetworkSelectionMode: function getNetworkSelectionMode(clientId) { let context = this.getRilContext(clientId); return context && context.networkSelectionMode; }, + getRadioState: function getRadioState(clientId) { + let context = this.getRilContext(clientId); + return context && context.radioState; + }, + /** * The networks that are currently trying to be selected (or "automatic"). * This helps ensure that only one network per client is selected at a time. */ _selectingNetworks: null, getNetworks: function getNetworks(clientId, window) { if (window == null) { @@ -1357,16 +1366,35 @@ RILContentHelper.prototype = { data: { requestId: requestId, } }); return request; }, + setRadioEnabled: function setRadioEnabled(clientId, window, enabled) { + if (window == null) { + throw Components.Exception("Can't get window object", + Cr.NS_ERROR_UNEXPECTED); + } + let request = Services.DOMRequest.createRequest(window); + let requestId = this.getRequestId(request); + + cpmm.sendAsyncMessage("RIL:SetRadioEnabled", { + clientId: clientId, + data: { + requestId: requestId, + enabled: enabled, + } + }); + + return request; + }, + _mobileConnectionListeners: null, _cellBroadcastListeners: null, _voicemailListeners: null, _iccListeners: null, voicemailInfos: null, voicemailStatuses: null, @@ -1762,16 +1790,26 @@ RILContentHelper.prototype = { this.handleExitEmergencyCbMode(data); break; case "RIL:EmergencyCbModeChanged": this._deliverEvent(clientId, "_mobileConnectionListeners", "notifyEmergencyCbModeChanged", [data.active, data.timeoutMs]); break; + case "RIL:SetRadioEnabled": + this.handleSimpleRequest(data.requestId, data.errorMsg, null); + break; + case "RIL:RadioStateChanged": + this.rilContexts[clientId].radioState = data; + this._deliverEvent(clientId, + "_mobileConnectionListeners", + "notifyRadioStateChanged", + null); + break; case "RIL:SetVoicePrivacyMode": this.handleSimpleRequest(data.requestId, data.errorMsg, null); break; case "RIL:GetVoicePrivacyMode": this.handleSimpleRequest(data.requestId, data.errorMsg, data.enabled); break; } @@ -2089,9 +2127,8 @@ RILContentHelper.prototype = { return true; } }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RILContentHelper, DOMMMIError, IccCardLockError]); -
--- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -16,16 +16,17 @@ "use strict"; const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Sntp.jsm"); Cu.import("resource://gre/modules/systemlibs.js"); +Cu.import("resource://gre/modules/Promise.jsm"); var RIL = {}; Cu.import("resource://gre/modules/ril_consts.js", RIL); // set to true in ril_consts.js to see debug messages var DEBUG = RIL.DEBUG_RIL; // Read debug setting from pref @@ -101,16 +102,17 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES "RIL:ChangeCallBarringPassword", "RIL:SetCallWaitingOptions", "RIL:GetCallWaitingOptions", "RIL:SetCallingLineIdRestriction", "RIL:GetCallingLineIdRestriction", "RIL:SetRoamingPreference", "RIL:GetRoamingPreference", "RIL:ExitEmergencyCbMode", + "RIL:SetRadioEnabled", "RIL:SetVoicePrivacyMode", "RIL:GetVoicePrivacyMode" ]; const RIL_IPC_ICCMANAGER_MSG_NAMES = [ "RIL:SendStkResponse", "RIL:SendStkMenuSelection", "RIL:SendStkTimerExpiration", @@ -412,16 +414,21 @@ XPCOMUtils.defineLazyGetter(this, "gMess let clientId = msg.json.clientId || 0; let radioInterface = this.ril.getRadioInterface(clientId); if (!radioInterface) { if (DEBUG) debug("No such radio interface: " + clientId); return null; } + if (msg.name === "RIL:SetRadioEnabled") { + // Special handler for SetRadioEnabled. + return gRadioEnabledController.receiveMessage(msg); + } + return radioInterface.receiveMessage(msg); }, /** * nsIObserver interface methods. */ observe: function observe(subject, topic, data) { @@ -461,17 +468,141 @@ XPCOMUtils.defineLazyGetter(this, "gMess this._sendTargetMessage("icc", message, { clientId: clientId, data: data }); } }; }); -// Initialize shared preference 'ril.numRadioInterfaces' according to system +XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function () { + return { + ril: null, + pendingMessages: [], // For queueing "RIL:SetRadioEnabled" messages. + timer: null, + request: null, + deactivatingDeferred: {}, + + init: function init(ril) { + this.ril = ril; + }, + + receiveMessage: function(msg) { + if (DEBUG) debug("setRadioEnabled: receiveMessage: " + JSON.stringify(msg)); + this.pendingMessages.push(msg); + if (this.pendingMessages.length === 1) { + this._processNextMessage(); + } + }, + + isDeactivatingDataCalls: function() { + return this.request !== null; + }, + + finishDeactivatingDataCalls: function(clientId) { + if (DEBUG) debug("setRadioEnabled: finishDeactivatingDataCalls: " + clientId); + let deferred = this.deactivatingDeferred[clientId]; + if (deferred) { + deferred.resolve(); + } + }, + + _processNextMessage: function() { + if (this.pendingMessages.length === 0) { + return; + } + + let msg = this.pendingMessages.shift(); + this._handleMessage(msg); + }, + + _handleMessage: function(msg) { + if (DEBUG) debug("setRadioEnabled: handleMessage: " + JSON.stringify(msg)); + let radioInterface = this.ril.getRadioInterface(msg.json.clientId || 0); + + if (!radioInterface.isValidStateForSetRadioEnabled()) { + radioInterface.setRadioEnabledResponse(msg.target, msg.json.data, + "InvalidStateError"); + this._processNextMessage(); + return; + } + + if (radioInterface.isDummyForSetRadioEnabled(msg.json.data)) { + radioInterface.setRadioEnabledResponse(msg.target, msg.json.data); + this._processNextMessage(); + return; + } + + if (msg.json.data.enabled) { + radioInterface.receiveMessage(msg); + this._processNextMessage(); + } else { + this.request = (function() { + radioInterface.receiveMessage(msg); + this._processNextMessage(); + }).bind(this); + + // In some DSDS architecture with only one modem, toggling one radio may + // toggle both. Therefore, for safely turning off, we should first + // explicitly deactivate all data calls from all clients. + this._deactivateDataCalls().then(() => { + if (DEBUG) debug("setRadioEnabled: deactivation done"); + this._executeRequest(); + }); + + this._createTimer(); + } + }, + + _deactivateDataCalls: function() { + if (DEBUG) debug("setRadioEnabled: deactivating data calls..."); + this.deactivatingDeferred = {}; + + let promise = Promise.resolve(); + for (let i = 0, N = this.ril.numRadioInterfaces; i < N; ++i) { + promise = promise.then(this._deactivateDataCallsForClient(i)); + } + + return promise; + }, + + _deactivateDataCallsForClient: function(clientId) { + return (function() { + let deferred = this.deactivatingDeferred[clientId] = Promise.defer(); + this.ril.getRadioInterface(clientId).deactivateDataCalls(); + return deferred.promise; + }).bind(this); + }, + + _createTimer: function() { + if (!this.timer) { + this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + } + this.timer.initWithCallback(this._executeRequest, RADIO_POWER_OFF_TIMEOUT, + Ci.nsITimer.TYPE_ONE_SHOT); + }, + + _cancelTimer: function() { + if (this.timer) { + this.timer.cancel(); + } + }, + + _executeRequest: function() { + if (typeof this.request === "function") { + if (DEBUG) debug("setRadioEnabled: executeRequest"); + this._cancelTimer(); + this.request(); + this.request = null; + } + } + }; +}); + +// Initialize shared preference "ril.numRadioInterfaces" according to system // property. try { Services.prefs.setIntPref(kPrefRilNumRadioInterfaces, (function () { // When Gonk property "ro.moz.ril.numclients" is not set, return 1; if // explicitly set to any number larger-equal than 0, return num; else, return // 1 for compatibility. try { let numString = libcutils.property_get("ro.moz.ril.numclients", "1"); @@ -527,16 +658,17 @@ CdmaIccInfo.prototype = { // nsIDOMMozCdmaIccInfo mdn: null }; function RadioInterfaceLayer() { gMessageManager.init(this); + gRadioEnabledController.init(this); let options = { debug: debugPref, cellBroadcastDisabled: false, clirMode: RIL.CLIR_DEFAULT }; try { @@ -680,17 +812,17 @@ WorkerMessenger.prototype = { * Send arbitrary message to worker. * * @param rilMessageType * A text message type. * @param message [optional] * An optional message object to send. * @param callback [optional] * An optional callback function which is called when worker replies - * with an message containing a 'rilMessageToken' attribute of the + * with an message containing a "rilMessageToken" attribute of the * same value we passed. This callback function accepts only one * parameter -- the reply from worker. It also returns a boolean * value true to keep current token-callback mapping and wait for * another worker reply, or false to remove the mapping. */ send: function send(rilMessageType, message, callback) { message = message || {}; @@ -715,17 +847,17 @@ WorkerMessenger.prototype = { /** * Send message to worker and return worker reply to RILContentHelper. * * @param msg * A message object from ppmm. * @param rilMessageType * A text string for worker message type. * @param ipcType [optinal] - * A text string for ipc message type. 'msg.name' if omitted. + * A text string for ipc message type. "msg.name" if omitted. * * @TODO: Bug 815526 - deprecate RILContentHelper. */ sendWithIPCMessage: function sendWithIPCMessage(msg, rilMessageType, ipcType) { this.send(rilMessageType, msg.json.data, (function(reply) { ipcType = ipcType || msg.name; msg.target.sendAsyncMessage(ipcType, { clientId: this.radioInterface.clientId, @@ -753,16 +885,17 @@ function RadioInterface(options) { // via a given APN type. this.apnSettings = { byType: {}, byApn: {} }; this.rilContext = { radioState: RIL.GECKO_RADIOSTATE_UNAVAILABLE, + detailedRadioState: null, cardState: RIL.GECKO_CARDSTATE_UNKNOWN, networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN, iccInfo: null, imsi: null, // These objects implement the nsIDOMMozMobileConnectionInfo interface, // although the actual implementation lives in the content process. So are // the child attributes `network` and `cell`, which implement @@ -787,34 +920,31 @@ function RadioInterface(options) { this.voicemailInfo = { number: null, displayName: null }; this.operatorInfo = {}; - // Read the 'ril.radio.disabled' setting in order to start with a known - // value at boot time. let lock = gSettingsService.createLock(); - lock.get("ril.radio.disabled", this); // Read preferred network type from the setting DB. lock.get("ril.radio.preferredNetworkType", this); // Read the APN data from the settings DB. lock.get("ril.data.roaming_enabled", this); lock.get("ril.data.enabled", this); lock.get("ril.data.apnSettings", this); - // Read the 'time.clock.automatic-update.enabled' setting to see if + // Read the "time.clock.automatic-update.enabled" setting to see if // we need to adjust the system clock time by NITZ or SNTP. lock.get(kSettingsClockAutoUpdateEnabled, this); - // Read the 'time.timezone.automatic-update.enabled' setting to see if + // Read the "time.timezone.automatic-update.enabled" setting to see if // we need to adjust the system timezone by NITZ. lock.get(kSettingsTimezoneAutoUpdateEnabled, this); // Set "time.clock.automatic-update.available" to false when starting up. this.setClockAutoUpdateAvailable(false); // Set "time.timezone.automatic-update.available" to false when starting up. this.setTimezoneAutoUpdateAvailable(false); @@ -831,21 +961,21 @@ function RadioInterface(options) { Services.obs.addObserver(this, kNetworkInterfaceStateChangedTopic, false); Services.prefs.addObserver(kPrefCellBroadcastDisabled, this, false); this.portAddressedSmsApps = {}; this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this); this._sntp = new Sntp(this.setClockBySntp.bind(this), - Services.prefs.getIntPref('network.sntp.maxRetryCount'), - Services.prefs.getIntPref('network.sntp.refreshPeriod'), - Services.prefs.getIntPref('network.sntp.timeout'), - Services.prefs.getCharPref('network.sntp.pools').split(';'), - Services.prefs.getIntPref('network.sntp.port')); + Services.prefs.getIntPref("network.sntp.maxRetryCount"), + Services.prefs.getIntPref("network.sntp.refreshPeriod"), + Services.prefs.getIntPref("network.sntp.timeout"), + Services.prefs.getCharPref("network.sntp.pools").split(";"), + Services.prefs.getIntPref("network.sntp.port")); } RadioInterface.prototype = { classID: RADIOINTERFACE_CID, classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACE_CID, classDescription: "RadioInterface", interfaces: [Ci.nsIRadioInterface]}), @@ -858,38 +988,38 @@ RadioInterface.prototype = { workerMessenger: null, debug: function debug(s) { dump("-*- RadioInterface[" + this.clientId + "]: " + s + "\n"); }, /** * A utility function to copy objects. The srcInfo may contain - * 'rilMessageType', should ignore it. + * "rilMessageType", should ignore it. */ updateInfo: function updateInfo(srcInfo, destInfo) { for (let key in srcInfo) { - if (key === 'rilMessageType') { + if (key === "rilMessageType") { continue; } destInfo[key] = srcInfo[key]; } }, /** * A utility function to compare objects. The srcInfo may contain - * 'rilMessageType', should ignore it. + * "rilMessageType", should ignore it. */ isInfoChanged: function isInfoChanged(srcInfo, destInfo) { if (!destInfo) { return true; } for (let key in srcInfo) { - if (key === 'rilMessageType') { + if (key === "rilMessageType") { continue; } if (srcInfo[key] !== destInfo[key]) { return true; } } return false; @@ -986,16 +1116,19 @@ RadioInterface.prototype = { this.setCallingLineIdRestriction(msg.target, msg.json.data); break; case "RIL:GetCallingLineIdRestriction": this.workerMessenger.sendWithIPCMessage(msg, "getCLIR"); break; case "RIL:ExitEmergencyCbMode": this.workerMessenger.sendWithIPCMessage(msg, "exitEmergencyCbMode"); break; + case "RIL:SetRadioEnabled": + this.setRadioEnabled(msg.target, msg.json.data); + break; case "RIL:GetVoicemailInfo": // This message is sync. return this.voicemailInfo; case "RIL:SetRoamingPreference": this.workerMessenger.sendWithIPCMessage(msg, "setRoamingPreference"); break; case "RIL:GetRoamingPreference": this.workerMessenger.sendWithIPCMessage(msg, "queryRoamingPreference"); @@ -1110,20 +1243,16 @@ RadioInterface.prototype = { this.handleUSSDReceived(message); break; case "stkcommand": this.handleStkProactiveCommand(message); break; case "stksessionend": gMessageManager.sendIccMessage("RIL:StkSessionEnd", this.clientId, null); break; - case "setRadioEnabled": - let lock = gSettingsService.createLock(); - lock.set("ril.radio.disabled", !message.on, null, null); - break; case "exitEmergencyCbMode": this.handleExitEmergencyCbMode(message); break; case "cdma-info-rec-received": if (DEBUG) this.debug("cdma-info-rec-received: " + JSON.stringify(message)); gSystemMessenger.broadcastMessage("cdma-info-rec-received", message); break; default: @@ -1476,101 +1605,70 @@ RadioInterface.prototype = { } let status = RIL.CDMA_OTA_PROVISION_STATUS_TO_GECKO[message.status]; gMessageManager.sendMobileConnectionMessage("RIL:OtaStatusChanged", this.clientId, status); }, + _isRadioChanging: function _isRadioChanging() { + let state = this.rilContext.detailedRadioState; + return state == RIL.GECKO_DETAILED_RADIOSTATE_ENABLING || + state == RIL.GECKO_DETAILED_RADIOSTATE_DISABLING; + }, + + _convertRadioState: function _converRadioState(state) { + switch (state) { + case RIL.GECKO_RADIOSTATE_OFF: + return RIL.GECKO_DETAILED_RADIOSTATE_DISABLED; + case RIL.GECKO_RADIOSTATE_READY: + return RIL.GECKO_DETAILED_RADIOSTATE_ENABLED; + default: + return RIL.GECKO_DETAILED_RADIOSTATE_UNKNOWN; + } + }, + handleRadioStateChange: function handleRadioStateChange(message) { - this._changingRadioPower = false; - let newState = message.radioState; if (this.rilContext.radioState == newState) { return; } this.rilContext.radioState = newState; + this.handleDetailedRadioStateChanged(this._convertRadioState(newState)); + //TODO Should we notify this change as a card state change? - - this._ensureRadioState(); }, - _ensureRadioState: function _ensureRadioState() { - if (DEBUG) { - this.debug("Reported radio state is " + this.rilContext.radioState + - ", desired radio enabled state is " + this._radioEnabled); - } - if (this._radioEnabled == null) { - // We haven't read the initial value from the settings DB yet. - // Wait for that. - return; - } - if (!this._sysMsgListenerReady) { - // The UI's system app isn't ready yet for us to receive any - // events (e.g. incoming SMS, etc.). Wait for that. - return; - } - if (this.rilContext.radioState == RIL.GECKO_RADIOSTATE_UNKNOWN) { - // We haven't received a radio state notification from the RIL - // yet. Wait for that. - return; - } - if (this._changingRadioPower) { - // We're changing the radio power currently, ignore any changes. + handleDetailedRadioStateChanged: function handleDetailedRadioStateChanged(state) { + if (this.rilContext.detailedRadioState == state) { return; } - - if (this.rilContext.radioState == RIL.GECKO_RADIOSTATE_OFF && - this._radioEnabled) { - this._changingRadioPower = true; - this.setRadioEnabled(true); - } - if (this.rilContext.radioState == RIL.GECKO_RADIOSTATE_READY && - !this._radioEnabled) { - this._changingRadioPower = true; - this.powerOffRadioSafely(); - } + this.rilContext.detailedRadioState = state; + gMessageManager.sendMobileConnectionMessage("RIL:RadioStateChanged", + this.clientId, state); }, - _radioOffTimer: null, - _cancelRadioOffTimer: function _cancelRadioOffTimer() { - if (this._radioOffTimer) { - this._radioOffTimer.cancel(); - } - }, - _fireRadioOffTimer: function _fireRadioOffTimer() { - if (DEBUG) this.debug("Radio off timer expired, set radio power off right away."); - this.setRadioEnabled(false); - }, - - /** - * Clean up all existing data calls before turning radio off. - */ - powerOffRadioSafely: function powerOffRadioSafely() { + deactivateDataCalls: function deactivateDataCalls() { let dataDisconnecting = false; for each (let apnSetting in this.apnSettings.byApn) { for each (let type in apnSetting.types) { if (this.getDataCallStateByType(type) == RIL.GECKO_NETWORK_STATE_CONNECTED) { this.deactivateDataCallByType(type); dataDisconnecting = true; } } } - if (dataDisconnecting) { - if (this._radioOffTimer == null) { - this._radioOffTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - } - this._radioOffTimer.initWithCallback(this._fireRadioOffTimer.bind(this), - RADIO_POWER_OFF_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT); - this._radioOffAfterDataDisconnected = true; - return; + + // No data calls exist. It's safe to proceed the pending radio power off + // request. + if (gRadioEnabledController.isDeactivatingDataCalls() && !dataDisconnecting) { + gRadioEnabledController.finishDeactivatingDataCalls(this.clientId); } - this.setRadioEnabled(false); }, /** * This function will do the following steps: * 1. Clear the cached APN settings in the RIL. * 2. Combine APN, user name, and password as the key of |byApn| object to * refer to the corresponding APN setting. * 3. Use APN type as the index of |byType| object to refer to the @@ -1606,18 +1704,18 @@ RadioInterface.prototype = { let inputApnSetting = simApnSettings[i]; if (!this.validateApnSetting(inputApnSetting)) { continue; } // Combine APN, user name, and password as the key of |byApn| object to // refer to the corresponding APN setting. let apnKey = inputApnSetting.apn + - (inputApnSetting.user || '') + - (inputApnSetting.password || ''); + (inputApnSetting.user || "") + + (inputApnSetting.password || ""); if (!this.apnSettings.byApn[apnKey]) { this.apnSettings.byApn[apnKey] = inputApnSetting; } else { this.apnSettings.byApn[apnKey].types = this.apnSettings.byApn[apnKey].types.concat(inputApnSetting.types); } @@ -1724,17 +1822,17 @@ RadioInterface.prototype = { if (dataInfo.roaming && !this.dataCallSettings.roamingEnabled) { if (DEBUG) this.debug("We're roaming, but data roaming is disabled."); return; } if (wifi_active) { if (DEBUG) this.debug("Don't connect data call when Wifi is connected."); return; } - if (this._changingRadioPower) { + if (this._isRadioChanging()) { // We're changing the radio power currently, ignore any changes. return; } if (DEBUG) this.debug("Data call settings: connect data call."); this.setupDataCallByType("default"); }, @@ -1931,28 +2029,28 @@ RadioInterface.prototype = { result: (success ? RIL.PDU_FCS_OK : RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED) }); if (!success) { // At this point we could send a message to content to notify the user // that storing an incoming SMS failed, most likely due to a full disk. if (DEBUG) { - this.debug("Could not store SMS, error code " + rv); + this.debug("Could not store SMS " + message.id + ", error code " + rv); } return; } this.broadcastSmsSystemMessage(kSmsReceivedObserverTopic, domMessage); Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null); }.bind(this); if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) { - gMobileMessageDatabaseService.saveReceivedMessage(message, - notifyReceived); + message.id = gMobileMessageDatabaseService.saveReceivedMessage(message, + notifyReceived); } else { message.id = -1; message.threadId = 0; message.delivery = DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED; message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS; message.read = false; let domMessage = @@ -2000,34 +2098,32 @@ RadioInterface.prototype = { } this._deliverDataCallCallback("dataCallStateChanged", [datacall]); // Process pending radio power off request after all data calls // are disconnected. if (datacall.state == RIL.GECKO_NETWORK_STATE_UNKNOWN && - this._radioOffAfterDataDisconnected) { + gRadioEnabledController.isDeactivatingDataCalls()) { let anyDataConnected = false; for each (let apnSetting in this.apnSettings.byApn) { for each (let type in apnSetting.types) { if (this.getDataCallStateByType(type) == RIL.GECKO_NETWORK_STATE_CONNECTED) { anyDataConnected = true; break; } } if (anyDataConnected) { break; } } if (!anyDataConnected) { - if (DEBUG) this.debug("All data connections are disconnected, set radio off."); - this._radioOffAfterDataDisconnected = false; - this._cancelRadioOffTimer(); - this.setRadioEnabled(false); + if (DEBUG) this.debug("All data connections are disconnected."); + gRadioEnabledController.finishDeactivatingDataCalls(this.clientId); } } }, /** * Handle data call list. */ handleDataCallList: function handleDataCallList(message) { @@ -2210,19 +2306,17 @@ RadioInterface.prototype = { gMessageManager.sendRequestResults("RIL:ExitEmergencyCbMode", message); }, // nsIObserver observe: function observe(subject, topic, data) { switch (topic) { case kSysMsgListenerReadyObserverTopic: - Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic); - this._sysMsgListenerReady = true; - this._ensureRadioState(); + this.setRadioEnabledInternal({enabled: true}, null); break; case kMozSettingsChangedObserverTopic: let setting = JSON.parse(data); this.handleSettingsChange(setting.key, setting.value, setting.message); break; case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID: if (data === kPrefCellBroadcastDisabled) { let value = false; @@ -2267,43 +2361,27 @@ RadioInterface.prototype = { } break; case kScreenStateChangedTopic: this.workerMessenger.send("setScreenState", { on: (data === "on") }); break; } }, - // Flag to determine whether the UI's system app is ready to receive - // events yet. - _sysMsgListenerReady: false, - - // Flag to determine the radio state to start with when we boot up. It - // corresponds to the 'ril.radio.disabled' setting from the UI. - _radioEnabled: null, - - // Flag to ignore any radio power change requests during We're changing - // the radio power. - _changingRadioPower: false, - - // Flag to determine if we need to set radio off when we are notified a data - // call has been disconnected. - _radioOffAfterDataDisconnected: false, - // Data calls setting. dataCallSettings: null, apnSettings: null, // Flag to determine whether to update system clock automatically. It - // corresponds to the 'time.clock.automatic-update.enabled' setting. + // corresponds to the "time.clock.automatic-update.enabled" setting. _clockAutoUpdateEnabled: null, // Flag to determine whether to update system timezone automatically. It - // corresponds to the 'time.clock.automatic-update.enabled' setting. + // corresponds to the "time.clock.automatic-update.enabled" setting. _timezoneAutoUpdateEnabled: null, // Remember the last NITZ message so that we can set the time based on // the network immediately when users enable network-based time. _lastNitzMessage: null, // Object that handles SNTP. _sntp: null, @@ -2341,21 +2419,16 @@ RadioInterface.prototype = { } this.handle(aName, aResult); }, // nsISettingsServiceCallback handle: function handle(aName, aResult) { switch(aName) { - case "ril.radio.disabled": - if (DEBUG) this.debug("'ril.radio.disabled' is now " + aResult); - this._radioEnabled = !aResult; - this._ensureRadioState(); - break; case "ril.radio.preferredNetworkType": if (DEBUG) this.debug("'ril.radio.preferredNetworkType' is now " + aResult); this.setPreferredNetworkType(aResult); break; case "ril.data.enabled": if (DEBUG) this.debug("'ril.data.enabled' is now " + aResult); let enabled; if (Array.isArray(aResult)) { @@ -2417,37 +2490,28 @@ RadioInterface.prototype = { this.setCellBroadcastSearchList(aResult); break; } }, handleError: function handleError(aErrorMessage) { if (DEBUG) this.debug("There was an error while reading RIL settings."); - // Default radio to on. - this._radioEnabled = true; - this._ensureRadioState(); - // Clean data call setting. this.dataCallSettings.oldEnabled = false; this.dataCallSettings.enabled = false; this.dataCallSettings.roamingEnabled = false; this.apnSettings = { byType: {}, byApn: {}, }; }, // nsIRadioInterface - setRadioEnabled: function setRadioEnabled(value) { - if (DEBUG) this.debug("Setting radio power to " + value); - this.workerMessenger.send("setRadioPower", { on: value }); - }, - rilContext: null, // Handle phone functions of nsIRILContentHelper _sendCfStateChanged: function _sendCfStateChanged(message) { gMessageManager.sendMobileConnectionMessage("RIL:CfStateChanged", this.clientId, message); }, @@ -2505,16 +2569,72 @@ RadioInterface.prototype = { target.sendAsyncMessage("RIL:SetCallingLineIdRestriction", { clientId: this.clientId, data: response }); return false; }).bind(this)); }, + isValidStateForSetRadioEnabled: function() { + let state = this.rilContext.radioState; + + return !this._isRadioChanging() && + (state == RIL.GECKO_RADIOSTATE_READY || + state == RIL.GECKO_RADIOSTATE_OFF); + }, + + isDummyForSetRadioEnabled: function(message) { + let state = this.rilContext.radioState; + + return (state == RIL.GECKO_RADIOSTATE_READY && message.enabled) || + (state == RIL.GECKO_RADIOSTATE_OFF && !message.enabled); + }, + + setRadioEnabledResponse: function(target, message, errorMsg) { + if (errorMsg) { + message.errorMsg = errorMsg; + } + + target.sendAsyncMessage("RIL:SetRadioEnabled", { + clientId: this.clientId, + data: message + }); + }, + + setRadioEnabled: function setRadioEnabled(target, message) { + if (DEBUG) { + this.debug("setRadioEnabled: " + JSON.stringify(message)); + } + + if (!this.isValidStateForSetRadioEnabled()) { + this.setRadioEnabledResponse(target, message, "InvalidStateError"); + return; + } + + if (this.isDummyForSetRadioEnabled(message)) { + this.setRadioEnabledResponse(target, message); + return; + } + + let callback = (function(response) { + this.setRadioEnabledResponse(target, response); + return false; + }).bind(this); + + this.setRadioEnabledInternal(message, callback); + }, + + setRadioEnabledInternal: function setRadioEnabledInternal(message, callback) { + let state = message.enabled ? RIL.GECKO_DETAILED_RADIOSTATE_ENABLING + : RIL.GECKO_DETAILED_RADIOSTATE_DISABLING; + this.handleDetailedRadioStateChanged(state); + this.workerMessenger.send("setRadioEnabled", message, callback); + }, + /** * List of tuples of national language identifier pairs. * * TODO: Support static/runtime settings, see bug 733331. */ enabledGsmTableTuples: [ [RIL.PDU_NL_IDENTIFIER_DEFAULT, RIL.PDU_NL_IDENTIFIER_DEFAULT], ], @@ -2581,17 +2701,17 @@ RadioInterface.prototype = { septet = langShiftTable.indexOf(c); if (septet < 0) { if (!strict7BitEncoding) { return -1; } // Bug 816082, when strict7BitEncoding is enabled, we should replace // characters that can't be encoded with GSM 7-Bit alphabets with '*'. - c = '*'; + c = "*"; if (langTable.indexOf(c) >= 0) { length++; } else if (langShiftTable.indexOf(c) >= 0) { length += 2; } else { // We can't even encode a '*' character with current configuration. return -1; } @@ -2803,17 +2923,17 @@ RadioInterface.prototype = { inc = 2; if (septet < 0) { if (!strict7BitEncoding) { throw new Error("Given text cannot be encoded with GSM 7-bit Alphabet!"); } // Bug 816082, when strict7BitEncoding is enabled, we should replace // characters that can't be encoded with GSM 7-Bit alphabets with '*'. - c = '*'; + c = "*"; if (langTable.indexOf(c) >= 0) { inc = 1; } } } if ((len + inc) > segmentSeptets) { ret.push({ @@ -2973,17 +3093,18 @@ RadioInterface.prototype = { // If the radio is disabled or the SIM card is not ready, just directly // return with the corresponding error code. let errorCode; if (!PhoneNumberUtils.isPlainPhoneNumber(options.number)) { if (DEBUG) this.debug("Error! Address is invalid when sending SMS: " + options.number); errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR; - } else if (!this._radioEnabled) { + } else if (this.rilContext.detailedRadioState == + RIL.GECKO_DETAILED_RADIOSTATE_DISABLED) { if (DEBUG) this.debug("Error! Radio is disabled when sending SMS."); errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR; } else if (this.rilContext.cardState != "ready") { if (DEBUG) this.debug("Error! SIM card is not ready when sending SMS."); errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR; } if (errorCode) { if (silent) { @@ -3058,17 +3179,17 @@ RadioInterface.prototype = { (function notifyResult(rv, domMessage) { // TODO bug 832140 handle !Components.isSuccessCode(rv) let topic = (response.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS) ? kSmsDeliverySuccessObserverTopic : kSmsDeliveryErrorObserverTopic; - // Broadcasting a 'sms-delivery-success' system message to open apps. + // Broadcasting a "sms-delivery-success" system message to open apps. if (topic == kSmsDeliverySuccessObserverTopic) { this.broadcastSmsSystemMessage(topic, domMessage); } // Notifying observers the delivery status is updated. Services.obs.notifyObservers(domMessage, topic, null); }).bind(this)); @@ -3147,18 +3268,18 @@ RadioInterface.prototype = { "normal", // message class sendingMessage.timestamp, 0, false); notifyResult(Cr.NS_OK, domMessage); return; } - gMobileMessageDatabaseService.saveSendingMessage(sendingMessage, - notifyResult); + let id = gMobileMessageDatabaseService.saveSendingMessage( + sendingMessage, notifyResult); }, registerDataCallCallback: function registerDataCallCallback(callback) { if (this._datacall_callbacks) { if (this._datacall_callbacks.indexOf(callback) != -1) { throw new Error("Already registered this callback!"); } } else { @@ -3388,21 +3509,21 @@ RILNetworkInterface.prototype = { broadcast: null, dns1: null, dns2: null, get httpProxyHost() { - return this.apnSetting.proxy || ''; + return this.apnSetting.proxy || ""; }, get httpProxyPort() { - return this.apnSetting.port || ''; + return this.apnSetting.port || ""; }, /** * nsIRilNetworkInterface Implementation */ get serviceId() { return this.radioInterface.clientId; @@ -3520,17 +3641,17 @@ RILNetworkInterface.prototype = { // In current design, we don't update status of secondary APN if it shares // same APN name with the default APN. In this condition, this.cid will // not be set and we don't want to update its status. if (this.cid == null) { return; } if (this.state == datacall.state) { - if (datacall.state != GECKO_NETWORK_STATE_CONNECTED) { + if (datacall.state != RIL.GECKO_NETWORK_STATE_CONNECTED) { return; } // State remains connected, check for minor changes. let changed = false; if (this.gateway != datacall.gw) { this.gateway = datacall.gw; changed = true; }
--- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -2374,16 +2374,22 @@ this.CALL_FAIL_IMSI_UNKNOWN_IN_VLR = 242 this.CALL_FAIL_IMEI_NOT_ACCEPTED = 243; this.CALL_FAIL_ERROR_UNSPECIFIED = 0xffff; // Other Gecko-specific constants this.GECKO_RADIOSTATE_UNAVAILABLE = null; this.GECKO_RADIOSTATE_OFF = "off"; this.GECKO_RADIOSTATE_READY = "ready"; +this.GECKO_DETAILED_RADIOSTATE_UNKNOWN = null; +this.GECKO_DETAILED_RADIOSTATE_ENABLING = "enabling"; +this.GECKO_DETAILED_RADIOSTATE_ENABLED = "enabled"; +this.GECKO_DETAILED_RADIOSTATE_DISABLING = "disabling"; +this.GECKO_DETAILED_RADIOSTATE_DISABLED = "disabled"; + this.GECKO_CARDSTATE_UNDETECTED = null; this.GECKO_CARDSTATE_ILLEGAL = "illegal"; this.GECKO_CARDSTATE_UNKNOWN = "unknown"; this.GECKO_CARDSTATE_PIN_REQUIRED = "pinRequired"; this.GECKO_CARDSTATE_PUK_REQUIRED = "pukRequired"; this.GECKO_CARDSTATE_PERSONALIZATION_IN_PROGRESS = "personalizationInProgress"; this.GECKO_CARDSTATE_PERSONALIZATION_READY = "personalizationReady"; this.GECKO_CARDSTATE_NETWORK_LOCKED = "networkLocked";
--- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -939,25 +939,25 @@ let RIL = { this.appType, options.contactType, contact, options.pin2, onsuccess, onerror); } else { ICCContactHelper.addICCContact( this.appType, options.contactType, contact, options.pin2, onsuccess, onerror); } }, /** - * Request the phone's radio power to be switched on or off. - * - * @param on - * Boolean indicating the desired power state. - */ - setRadioPower: function setRadioPower(options) { + * Request the phone's radio to be enabled or disabled. + * + * @param enabled + * Boolean indicating the desired state. + */ + setRadioEnabled: function setRadioEnabled(options) { Buf.newParcel(REQUEST_RADIO_POWER, options); Buf.writeInt32(1); - Buf.writeInt32(options.on ? 1 : 0); + Buf.writeInt32(options.enabled ? 1 : 0); Buf.sendParcel(); }, /** * Query call waiting status via MMI. */ _handleQueryMMICallWaiting: function _handleQueryMMICallWaiting(options) { function callback(options) { @@ -1408,19 +1408,17 @@ let RIL = { if (this.radioState == GECKO_RADIOSTATE_OFF) { if (DEBUG) debug("Automatically enable radio for an emergency call."); if (!this.cachedDialRequest) { this.cachedDialRequest = {}; } this.cachedDialRequest.onerror = onerror; this.cachedDialRequest.callback = this.sendDialRequest.bind(this, options); - - // Change radio setting value in settings DB to enable radio. - this.sendChromeMessage({rilMessageType: "setRadioEnabled", on: true}); + this.setRadioEnabled({enabled: true}); return; } this.sendDialRequest(options); }, sendDialRequest: function sendDialRequest(options) { Buf.newParcel(options.request); @@ -5179,28 +5177,35 @@ RIL[REQUEST_OPERATOR] = function REQUEST return; } let operatorData = Buf.readStringList(); if (DEBUG) debug("Operator: " + operatorData); this._processOperator(operatorData); }; RIL[REQUEST_RADIO_POWER] = function REQUEST_RADIO_POWER(length, options) { - if (options.rilRequestError) { - if (this.cachedDialRequest && options.on) { - // Turning on radio fails. Notify the error of making an emergency call. - this.cachedDialRequest.onerror(GECKO_ERROR_RADIO_NOT_AVAILABLE); - this.cachedDialRequest = null; - } + if (options.rilMessageType == null) { + // The request was made by ril_worker itself. + if (options.rilRequestError) { + if (this.cachedDialRequest && options.enabled) { + // Turning on radio fails. Notify the error of making an emergency call. + this.cachedDialRequest.onerror(GECKO_ERROR_RADIO_NOT_AVAILABLE); + this.cachedDialRequest = null; + } + return; + } + + if (this._isInitialRadioState) { + this._isInitialRadioState = false; + } + return; } - if (this._isInitialRadioState) { - this._isInitialRadioState = false; - } + this.sendChromeMessage(options); }; RIL[REQUEST_DTMF] = null; RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS(length, options) { this._processSmsSendResult(length, options); }; RIL[REQUEST_SEND_SMS_EXPECT_MORE] = null; RIL.readSetupDataCall_v5 = function readSetupDataCall_v5(options) { @@ -6015,17 +6020,17 @@ RIL[REQUEST_GET_UNLOCK_RETRY_COUNT] = fu }; RIL[UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED] = function UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED() { let radioState = Buf.readInt32(); // Ensure radio state at boot time. if (this._isInitialRadioState) { // Even radioState is RADIO_STATE_OFF, we still have to maually turn radio off, // otherwise REQUEST_GET_SIM_STATUS will still report CARD_STATE_PRESENT. - this.setRadioPower({on: false}); + this.setRadioEnabled({enabled: false}); } let newState; if (radioState == RADIO_STATE_UNAVAILABLE) { newState = GECKO_RADIOSTATE_UNAVAILABLE; } else if (radioState == RADIO_STATE_OFF) { newState = GECKO_RADIOSTATE_OFF; } else {
--- a/dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js +++ b/dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js @@ -1,42 +1,51 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 60000; MARIONETTE_HEAD_JS = 'head.js'; -let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise; - -const KEY = "ril.radio.disabled"; - -let settings; +let connection; let number = "112"; let outgoing; -function setAirplaneMode() { - log("Turning on airplane mode"); +function receivedPending(received, pending, nextAction) { + let index = pending.indexOf(received); + if (index != -1) { + pending.splice(index, 1); + } + if (pending.length === 0) { + nextAction(); + } +} - let deferred = Promise.defer(); +function setRadioEnabled(enabled, callback) { + let request = connection.setRadioEnabled(enabled); + let desiredRadioState = enabled ? 'enabled' : 'disabled'; - let setLock = settings.createLock(); - let obj = {}; - obj[KEY] = false; + let pending = ['onradiostatechange', 'onsuccess']; + let done = callback; - let setReq = setLock.set(obj); - setReq.addEventListener("success", function onSetSuccess() { - ok(true, "set '" + KEY + "' to " + obj[KEY]); - deferred.resolve(); - }); - setReq.addEventListener("error", function onSetError() { - ok(false, "cannot set '" + KEY + "'"); - deferred.reject(); - }); + connection.onradiostatechange = function() { + let state = connection.radioState; + log("Received 'radiostatechange' event, radioState: " + state); - return deferred.promise; + if (state == desiredRadioState) { + receivedPending('onradiostatechange', pending, done); + } + }; + + request.onsuccess = function onsuccess() { + receivedPending('onsuccess', pending, done); + }; + + request.onerror = function onerror() { + ok(false, "setRadioEnabled should be ok"); + }; } function dial() { log("Make an outgoing call."); outgoing = telephony.dial(number); ok(outgoing); is(outgoing.number, number); @@ -103,15 +112,17 @@ function hangUp() { }; emulator.run("gsm cancel " + number); } function cleanUp() { finish(); } -startTestWithPermissions(['settings-write'], function() { - settings = window.navigator.mozSettings; - ok(settings); - setAirplaneMode() - .then(dial) - .then(null, cleanUp); +startTestWithPermissions(['mobileconnection'], function() { + connection = navigator.mozMobileConnections[0]; + ok(connection instanceof MozMobileConnection, + "connection is instanceof " + connection.constructor); + + setRadioEnabled(false, function() { + dial(); + }); });
--- a/dom/telephony/test/marionette/test_outgoing_radio_off.js +++ b/dom/telephony/test/marionette/test_outgoing_radio_off.js @@ -1,45 +1,50 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ MARIONETTE_TIMEOUT = 60000; MARIONETTE_HEAD_JS = 'head.js'; -let icc; let connection; let outgoing; -function changeSetting(key, value, callback) { - let obj = {}; - obj[key] = value; - - let setReq = navigator.mozSettings.createLock().set(obj); - setReq.addEventListener("success", function onSetSuccess() { - ok(true, "set '" + key + "' to " + obj[key]); - setReq.removeEventListener("success", onSetSuccess); - callback(); - }); - setReq.addEventListener("error", function onSetError() { - ok(false, "cannot set '" + key + "'"); - cleanUp(); - }); +function receivedPending(received, pending, nextAction) { + let index = pending.indexOf(received); + if (index != -1) { + pending.splice(index, 1); + } + if (pending.length === 0) { + nextAction(); + } } function setRadioEnabled(enabled, callback) { - changeSetting("ril.radio.disabled", !enabled, function() { - // Wait for iccdetected event after turning on radio. - // Wait for iccundetected event after turning off radio. - let event = (enabled) ? "iccdetected" : "iccundetected"; - icc.addEventListener(event, function handler(evt) { - log(event + ": " + evt.iccId); - icc.removeEventListener(event, handler); - callback(); - }); - }); + let request = connection.setRadioEnabled(enabled); + let desiredRadioState = enabled ? 'enabled' : 'disabled'; + + let pending = ['onradiostatechange', 'onsuccess']; + let done = callback; + + connection.onradiostatechange = function() { + let state = connection.radioState; + log("Received 'radiostatechange' event, radioState: " + state); + + if (state == desiredRadioState) { + receivedPending('onradiostatechange', pending, done); + } + }; + + request.onsuccess = function onsuccess() { + receivedPending('onsuccess', pending, done); + }; + + request.onerror = function onerror() { + ok(false, "setRadioEnabled should be ok"); + }; } function dial(number) { // Verify initial state before dial. ok(telephony); is(telephony.active, null); ok(telephony.calls); is(telephony.calls.length, 0); @@ -69,25 +74,17 @@ function dial(number) { }); }; } function cleanUp() { finish(); } -let permissions = [ - "mobileconnection", - "settings-write" -]; - -startTestWithPermissions(permissions, function() { +startTestWithPermissions(['mobileconnection'], function() { connection = navigator.mozMobileConnections[0]; ok(connection instanceof MozMobileConnection, "connection is instanceof " + connection.constructor); - icc = navigator.mozIccManager; - ok(icc instanceof MozIccManager, "icc is instanceof " + icc.constructor); - setRadioEnabled(false, function() { dial("0912345678"); }); });
--- a/gfx/layers/composite/APZCTreeManager.cpp +++ b/gfx/layers/composite/APZCTreeManager.cpp @@ -208,17 +208,19 @@ APZCTreeManager::UpdatePanZoomController if (apzc) { aTransform = gfx3DMatrix(); } else { // Multiply child layer transforms on the left so they get applied first aTransform = aLayer->GetTransform() * aTransform; } uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId); - AsyncPanZoomController* next = nullptr; + // If there's no APZC at this level, any APZCs for our child layers will + // have our siblings as siblings. + AsyncPanZoomController* next = apzc ? nullptr : aNextSibling; for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { next = UpdatePanZoomControllerTree(aCompositor, child, childLayersId, aTransform, aParent, next, aIsFirstPaint, aFirstPaintLayersId, aApzcsToDestroy); } // Return the APZC that should be the sibling of other APZCs as we continue // moving towards the first child at this depth in the layer tree. // If this layer doesn't have an APZC, we promote any APZCs in the subtree
--- a/ipc/nfc/Nfc.cpp +++ b/ipc/nfc/Nfc.cpp @@ -8,21 +8,21 @@ #include "mozilla/ipc/Nfc.h" #include <fcntl.h> #include <sys/socket.h> #include <sys/un.h> #undef LOG -#if defined(MOZ_WIDGET_GONK) +#if (defined(MOZ_WIDGET_GONK) && defined(DEBUG)) #include <android/log.h> #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) #else -#define LOG(args...) printf(args); +#define LOG(args...) #endif #include "jsfriendapi.h" #include "nsThreadUtils.h" // For NS_IsMainThread. USING_WORKERS_NAMESPACE using namespace mozilla::ipc;
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -115,16 +115,20 @@ #include "mozilla/Preferences.h" #include "mozilla/ipc/URIUtils.h" #ifdef MOZ_WIDGET_GONK #include "nsDeviceStorage.h" #endif +#ifdef NECKO_PROTOCOL_rtsp +#include "nsISystemMessagesInternal.h" +#endif + using namespace mozilla; using namespace mozilla::ipc; // Buffer file writes in 32kb chunks #define BUFFERED_OUTPUT_SIZE (1024 * 32) // Download Folder location constants #define NS_PREF_DOWNLOAD_DIR "browser.download.dir" @@ -577,16 +581,92 @@ nsresult nsExternalHelperAppService::Ini NS_ENSURE_SUCCESS(rv, rv); return obs->AddObserver(this, "last-pb-context-exited", true); } nsExternalHelperAppService::~nsExternalHelperAppService() { } +#ifdef NECKO_PROTOCOL_rtsp +namespace { +/** + * A stack helper to clear the currently pending exception in a JS context. + */ +class AutoClearPendingException { +public: + AutoClearPendingException(JSContext* aCx) : + mCx(aCx) { + } + ~AutoClearPendingException() { + JS_ClearPendingException(mCx); + } +private: + JSContext *mCx; +}; +} // anonymous namespace + +/** + * This function broadcasts a system message in order to launch video app for + * rtsp scheme. This is Gonk-specific behavior. + */ +void nsExternalHelperAppService::LaunchVideoAppForRtsp(nsIURI* aURI) +{ + NS_NAMED_LITERAL_STRING(msgType, "rtsp-open-video"); + + // Make the url is rtsp. + bool isRTSP = false; + aURI->SchemeIs("rtsp", &isRTSP); + NS_ASSERTION(isRTSP, "Not rtsp protocol! Something goes wrong here"); + + // Construct jsval for system message. + AutoSafeJSContext cx; + AutoClearPendingException helper(cx); + JS::Rooted<JSObject*> msgObj(cx, JS_NewObject(cx, nullptr, nullptr, nullptr)); + NS_ENSURE_TRUE_VOID(msgObj); + JS::Rooted<JS::Value> jsVal(cx); + bool rv; + + // Set the "url" and "title" properties of the message. + // In the case of RTSP streaming, the title is the same as the url. + { + nsAutoCString spec; + aURI->GetAsciiSpec(spec); + JSString *urlStr = JS_NewStringCopyN(cx, spec.get(), spec.Length()); + NS_ENSURE_TRUE_VOID(urlStr); + jsVal.setString(urlStr); + + rv = JS_SetProperty(cx, msgObj, "url", jsVal); + NS_ENSURE_TRUE_VOID(rv); + + rv = JS_SetProperty(cx, msgObj, "title", jsVal); + NS_ENSURE_TRUE_VOID(rv); + } + + // Set the "type" property of the message. This is a fake MIME type. + { + NS_NAMED_LITERAL_CSTRING(mimeType, "video/rtsp"); + JSString *typeStr = JS_NewStringCopyN(cx, mimeType.get(), mimeType.Length()); + NS_ENSURE_TRUE_VOID(typeStr); + jsVal.setString(typeStr); + } + rv = JS_SetProperty(cx, msgObj, "type", jsVal); + NS_ENSURE_TRUE_VOID(rv); + + // Broadcast system message. + nsCOMPtr<nsISystemMessagesInternal> systemMessenger = + do_GetService("@mozilla.org/system-message-internal;1"); + NS_ENSURE_TRUE_VOID(systemMessenger); + jsVal.setObject(*msgObj); + systemMessenger->BroadcastMessage(msgType, jsVal, JS::UndefinedValue()); + + return; +} +#endif + NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeContentType, nsIRequest *aRequest, nsIInterfaceRequestor *aWindowContext, bool aForceSave, nsIStreamListener ** aStreamListener) { nsAutoString fileName; nsAutoCString fileExtension; @@ -917,17 +997,28 @@ nsExternalHelperAppService::LoadURI(nsIU return NS_OK; // missing default pref } } if (!allowLoad) { return NS_OK; // explicitly denied } - +#ifdef NECKO_PROTOCOL_rtsp + // Handle rtsp protocol. + { + bool isRTSP = false; + rv = aURI->SchemeIs("rtsp", &isRTSP); + if (NS_SUCCEEDED(rv) && isRTSP) { + LaunchVideoAppForRtsp(aURI); + return NS_OK; + } + } +#endif + nsCOMPtr<nsIHandlerInfo> handler; rv = GetProtocolHandlerInfo(scheme, getter_AddRefs(handler)); NS_ENSURE_SUCCESS(rv, rv); nsHandlerInfoAction preferredAction; handler->GetPreferredAction(&preferredAction); bool alwaysAsk = true; handler->GetAlwaysAskBeforeHandling(&alwaysAsk);
--- a/uriloader/exthandler/nsExternalHelperAppService.h +++ b/uriloader/exthandler/nsExternalHelperAppService.h @@ -33,16 +33,17 @@ #include "nsIHandlerService.h" #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsCOMArray.h" #include "nsWeakReference.h" #include "nsIPrompt.h" #include "nsAutoPtr.h" #include "mozilla/Attributes.h" +#include "necko-config.h" class nsExternalAppHandler; class nsIMIMEInfo; class nsITransfer; class nsIDOMWindow; /** * The helper app service. Responsible for handling content that Mozilla @@ -170,16 +171,25 @@ protected: */ void ExpungeTemporaryFiles(); /** * Functions related to the tempory file cleanup service provided by * nsExternalHelperAppService (for the temporary files added during * the private browsing mode) */ void ExpungeTemporaryPrivateFiles(); + +#ifdef NECKO_PROTOCOL_rtsp + /** + * Launch video app for rtsp protocol. This function is supported only on Gonk + * for now. + */ + static void LaunchVideoAppForRtsp(nsIURI* aURI); +#endif + /** * Array for the files that should be deleted */ nsCOMArray<nsIFile> mTemporaryFilesList; /** * Array for the files that should be deleted (for the temporary files * added during the private browsing mode) */
--- a/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp +++ b/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp @@ -210,17 +210,17 @@ int GonkNativeWindowClient::dequeueBuffe if (result != NO_ERROR) { ALOGE("dequeueBuffer: requestBuffer failed: %d", result); return result; } } - if (fence->isValid()) { + if (fence.get() && fence->isValid()) { *fenceFd = fence->dup(); if (*fenceFd == -1) { ALOGE("dequeueBuffer: error duping fence: %d", errno); // dup() should never fail; something is badly wrong. Soldier on // and hope for the best; the worst that should happen is some // visible corruption that lasts until the next frame. } } else {